Complete VIDEO-001 through VIDEO-004 software video studies

This commit is contained in:
LittleSam129
2026-09-16 18:27:35 +03:00
parent af7c05f2da
commit 95f664076a
403 changed files with 392009 additions and 0 deletions

252
docs/video/VIDEO_001.md Normal file
View File

@@ -0,0 +1,252 @@
# VIDEO-001: программный предел видеотракта
16 сентября 2026 года. Ветка `video-development`, исходный HEAD `af7c05f`.
`RF TX = 0`. SDR, камеры и экран не запускались. Основной `main`, старые результаты
и исходные IQ не менялись. Таблицы ниже относятся к этому компьютеру и одному
ролику; они не доказывают работу живого видео через радио.
## Что уже работает
| Узел | Файл и назначение | Зависимости и измеренное состояние |
|---|---|---|
| Подготовка JPEG | `experiments/lab047_jpeg_object_transport.py`, `experiments/lab050_video_stream.py`: уменьшение, RGB, JPEG | Pillow; Lab047: 320×240 q35, 8724 байта. Lab050: 128×96 q25, 1740–1893 байта на искусственных сдвигах фотографии. |
| Кодирование видео | `experiments/lab026_video_bitrate_benchmark.py` и `protocol/video_packet.py`: исследование сжатия и отдельный формат BASE/ROI | Это не подключённый к Lab045/Lab053 H.264 видеотракт. В VIDEO-001 добавлен автономный измеритель `tools/video_compare.py`, PyAV/libx264. |
| Фрагменты объекта | `experiments/lab046_bpsk_object_transport.py`, `protocol/image_fragments.py` | 12 байт метаданных и 244 байта JPEG на пакет. Сборка требует все фрагменты; отдельного замера CPU только для фрагментации нет. |
| Сигнал | `experiments/lab045_hackrf_bpsk_fec.py`, `experiments/lab049_bpsk_symbol_rate.py` | BPSK, свёрточный код 1/2, RRC; один пакет при 500 ксимв/с занимает 11,532 мс до паузы. Видео с H.264 в генератор не подаётся. |
| Приём потока | `experiments/lab053_streaming_receiver.py`, `experiments/lab053_pipeline.py` | NumPy, SciPy, Numba. Lab053: программный источник 6,044416 с, 240/240 CRC-пакетов, 30/30 JPEG, 5 свежих изображений/с в стенде. Камеры и экрана там нет. |
| Сборка и показ | `ImageSink` в `experiments/lab051_receiver_performance.py`; модель показа в `lab050_video_stream.py` | В Lab053 на 30 изображений сборка заняла 5,967 мс, JPEG decode 5,977 мс суммарно. Физический экран не реализован и не измерен. |
**MEASURED:** Lab047 передала по эфиру один JPEG 8724 байта за 10,3788 с на
20 ксимв/с. Первоначальный независимый приём остановился по вычислительному
пределу; повторный анализ той же RX-записи после сеанса восстановил 36/36
фрагментов и точный JPEG. Lab050–052 сначала не достигали реального времени:
например, Lab050 обработала 0,323745 с IQ за 55,157553 с при 500 ксимв/с.
Lab053 ускорила только программный конвейер: измеренные 5 кадров/с относятся к
заданному файлу IQ, не к аппаратному видео. Ориентир 5 кадров/с для полной
системы остаётся непроверенным.
**MEASURED:** Lab066 выполнила один аппаратно валидный сеанс 500 ксимв/с с
16 пакетами по 256 байт и 5 мс между ними. Строгие legacy и T2 получили
10/16 точных пакетов, шесть остановились на PHY-заголовке. Lab067 связала эти
отказы с локальным качеством принятого сигнала и не нашла основания для малой
правки приёмника. 10/16 не считается постоянной вероятностью доставки.
## Радиобюджет
**CALCULATED по генератору Lab045/Lab066:** 256 байт нагрузки содержат 12 байт
заголовка фрагмента и 244 байта изображения. Пакет протокола имеет ещё 8 байт
заголовка и 4 байта CRC, всего 268 байт. Код 1/2 с шестью хвостовыми битами
даёт `(268×8+6)×2 = 4300` кодированных бит. Преамбула PRBS7 занимает
1280 символов, троекратный PHY-заголовок 144, вместе с кодированным телом это
5724 BPSK-символа. Две защитные области и хвост RRC добавляют 42 интервала:
5766 интервалов, 92 256 IQ-отсчётов и 11,532 мс на пакет при 500 ксимв/с.
| Условие | Полезная скорость JPEG, бит/с | Смысл |
|---|---:|---|
| Без потерь и пауз | 169 268 | `244×8 / 0,011532`; верхняя граница формата. |
| Без потерь, с паузой 5 мс после пакета | 118 074 | `244×8 / 0,016532`; около 60,49 пакета/с. |
| Один сеанс Lab066, 10 принятых пакетов, с двумя 100-мс защитными интервалами | 42 480 | `10×244×8 / 0,459512`; видеоэквивалент, потому что Lab066 передавала не JPEG. Не измерение длительной видеодоставки. |
При 5/10/15 кадрах/с без потерь и с паузой помещаются соответственно не более
12/6/4 целых пакетов за период кадра: 2928/1464/976 байт изображения. Это
только длительность эфира; опорный H.264 кадр и потери могут заполнить очередь.
Текущий формат и сборщик понимают JPEG, поэтому H.264 требует отдельного
видеотранспорта. Его оценка в таблицах учитывает `ceil(размер/244)` пакетов на
кадр, но не означает готовую передачу H.264.
**MEASURED, сохранённый IQ Lab053:** 386 842 624 байта complex64 равны
6,044416 с при 8 Мвыб/с. Новый `tools/video_iq_replay.py` читает их блоками,
вызывает штатный независимый PHY/FEC/CRC-приёмник и сборщик, без графиков,
истории по блокам и сверки с TX во время замера. Первый проход: JIT-разогрев
0,496044 с, затем 4,099045 с обработки, или 1,475 с IQ/с обработки.
Повторный проход: разогрев 0,000135 с, обработка 4,238388 с, или 1,426.
Оба дали 240/240 пакетов и 30/30 точных JPEG. Первый и повторный проходы не
образуют статистической гарантии для другого ноутбука, а профиль всё ещё
содержит внутренние счётчики приёмника.
## Программное сравнение кодеков
**MEASURED:** один исходный файл `data/raw/video001/udacity_project_video.mp4`,
взятый из [открытого проекта Udacity по обнаружению автомобилей](https://github.com/udacity/CarND-Vehicle-Detection/blob/master/project_video.mp4).
Размер 25 268 015 байт, SHA-256
`651949922042c223375d40bf8929def97dbb62c6ade651d1f5b08eea47127d5f`;
1280×720, 25 кадров/с, 50,4 с. Это реальная съёмка с движущегося автомобиля.
В окне 7–13 с белая машина проходит справа, в окне 33–39 с появляются и
меняют положение чёрная и белая машины. В обоих окнах исходные выбранные кадры
различались. При изменении разрешения пропорции сохранены чёрными полями.
Предыдущая проба на пустой дороге сохранена отдельно в
`data/processed/video001/camera_only/` и не входит в таблицу ниже.
JPEG повторяет профиль проекта: q35, 4:2:0, независимые кадры. H.264 использует
PyAV 16.0.1 и доступный `libx264` из FFmpeg: `preset=veryfast`, `tune=zerolatency`,
без B-кадров и lookahead, один поток кодировщика, закрытый GOP с опорным кадром раз в секунду,
`crf=28`, VBV maxrate/buffer 160 или 320 кбит/с. Есть отдельные пробы с 40 кбит/с.
Настройка maxrate не считается фактическим битрейтом: ниже приведены размеры
полученных пакетов. Время кодирования и декодирования не включает чтение MP4.
Отдельный `end_to_end_file_pipeline_latency_s` измерен прямым проходом: чтение
очередного кадра MP4 с пропуском промежуточных, изменение размера, кодирование,
декодирование и получение RGB. Открытие файла, эфир, очередь показа и экран в
него не входят. Пик за 1 с рассчитан скользящим окном из `fps` кадров.
В таблице для каждого режима взято большее из двух окон значение среднего
и пикового потока, а также большее среднее время обработки кадра. Единицы:
кбит/с и мс/кадр. `FITS` сопоставляет пакетную пиковую нагрузку с 21,76
принятого пакета/с в одном сеансе Lab066. Для всех строк это `НЕТ`; длительный
рабочий предел радио остаётся **NOT_RUN**. По контрольным кадрам оценены
различимость машин и полос, направление и потеря деталей. Мелких препятствий
в этих двух участках нет; проверка с оператором ровера **NOT_RUN**.
| Разрешение | FPS | Кодек | Средний поток | Пик 1 с | Кодирование | Декодирование | Качество для оператора | FITS CURRENT RADIO BUDGET |
|---|---:|---|---:|---:|---:|---:|---|---|
| 320×240 | 5 | JPEG q35 | 245 | 280 | 0,6 | 0,5 | Ближние машины и полоса видны; дальние предметы мелки | НЕТ |
| 320×240 | 5 | H.264 160k | 117 | 172 | 1,2 | 0,3 | Ближние машины и направление видны; мелкие детали ограничены | НЕТ |
| 320×240 | 5 | H.264 320k | 117 | 172 | 1,2 | 0,3 | Почти тот же вид, что при 160k | НЕТ |
| 320×240 | 10 | JPEG q35 | 491 | 563 | 0,3 | 0,4 | Ближние машины и полоса видны; дальние предметы мелки | НЕТ |
| 320×240 | 10 | H.264 160k | 140 | 213 | 1,0 | 0,2 | Ближние машины и направление видны; мелкие детали ограничены | НЕТ |
| 320×240 | 10 | H.264 320k | 140 | 213 | 1,0 | 0,2 | Почти тот же вид, что при 160k | НЕТ |
| 320×240 | 15 | JPEG q35 | 738 | 850 | 0,2 | 0,3 | Ближние машины и полоса видны; дальние предметы мелки | НЕТ |
| 320×240 | 15 | H.264 160k | 151 | 238 | 0,9 | 0,2 | Ближние машины и направление видны; мелкие детали ограничены | НЕТ |
| 320×240 | 15 | H.264 320k | 151 | 238 | 0,9 | 0,2 | Почти тот же вид, что при 160k | НЕТ |
| 640×360 | 5 | JPEG q35 | 580 | 726 | 0,8 | 1,7 | Машины и разметка видны; крупный поток | НЕТ |
| 640×360 | 5 | H.264 160k | 174 | 230 | 3,2 | 0,7 | Машины видны; текстура дороги размыта при движении | НЕТ |
| 640×360 | 5 | H.264 320k | 342 | 437 | 3,8 | 1,0 | Машины, полоса и направление различимы | НЕТ |
| 640×360 | 10 | JPEG q35 | 1165 | 1470 | 0,6 | 0,8 | Машины и разметка видны; крупный поток | НЕТ |
| 640×360 | 10 | H.264 160k | 178 | 250 | 2,5 | 0,5 | Машины видны; текстура дороги размыта при движении | НЕТ |
| 640×360 | 10 | H.264 320k | 354 | 376 | 3,0 | 0,7 | Машины, полоса и направление различимы | НЕТ |
| 640×360 | 15 | JPEG q35 | 1752 | 2226 | 0,6 | 1,3 | Машины и разметка видны; крупный поток | НЕТ |
| 640×360 | 15 | H.264 160k | 180 | 250 | 2,3 | 0,4 | Машины видны; текстура дороги размыта при движении | НЕТ |
| 640×360 | 15 | H.264 320k | 355 | 375 | 2,6 | 0,5 | Машины, полоса и направление различимы | НЕТ |
| 640×480 | 5 | JPEG q35 | 699 | 841 | 1,3 | 1,4 | Машины видны; чёрные поля 60 пикселей | НЕТ |
| 640×480 | 5 | H.264 160k | 171 | 221 | 3,7 | 0,7 | Машины видны; поля и размытие деталей | НЕТ |
| 640×480 | 5 | H.264 320k | 347 | 412 | 4,1 | 1,0 | Машины видны; полезная область как 640×360 | НЕТ |
| 640×480 | 10 | JPEG q35 | 1402 | 1701 | 1,0 | 1,4 | Машины видны; чёрные поля 60 пикселей | НЕТ |
| 640×480 | 10 | H.264 160k | 178 | 252 | 2,9 | 0,5 | Машины видны; поля и размытие деталей | НЕТ |
| 640×480 | 10 | H.264 320k | 355 | 381 | 3,3 | 0,7 | Машины видны; полезная область как 640×360 | НЕТ |
| 854×480 | 5 | JPEG q35 | 911 | 1142 | 1,3 | 2,1 | Машины и разметка чётче; крупный поток | НЕТ |
| 854×480 | 5 | H.264 160k | 172 | 223 | 5,2 | 0,8 | Ближние машины видны; дальние детали размыты | НЕТ |
| 854×480 | 5 | H.264 320k | 347 | 378 | 6,3 | 1,3 | Машины, полосы и направление хорошо различимы | НЕТ |
| 854×480 | 10 | JPEG q35 | 1828 | 2318 | 1,4 | 2,2 | Машины и разметка чётче; крупный поток | НЕТ |
| 854×480 | 10 | H.264 160k | 179 | 265 | 4,3 | 0,6 | Ближние машины видны; дальние детали размыты | НЕТ |
| 854×480 | 10 | H.264 320k | 357 | 456 | 5,1 | 0,8 | Машины, полосы и направление хорошо различимы | НЕТ |
| 1280×720 | 5 | JPEG q35 | 1745 | 2247 | 2,7 | 4,9 | Машины и разметка подробны; поток велик | НЕТ |
| 1280×720 | 5 | H.264 160k | 173 | 217 | 9,8 | 1,2 | Машины видны; разрешение не даёт ожидаемой детализации | НЕТ |
| 1280×720 | 5 | H.264 320k | 346 | 441 | 11,5 | 1,7 | Машины и разметка видны; дальние надписи не проверены | НЕТ |
Полные размеры каждого кадра, включая опорные, фактическая скорость кодирования,
оба замера задержки, RSS всего процесса, PSNR и число уникальных кадров сохранены
в `data/processed/video001/comparison_traffic_160k.json` и
`comparison_traffic_320k.json`. RSS в общем процессе достиг 240,9 МиБ в первом
наборе и 241,7 МиБ во втором; это наблюдаемый пик при двух проходах на случай,
не отдельная память кодека. В сценарии заранее загружены все выбранные кадры,
поэтому RSS завышает память будущего поштучного видеотракта. У программных
кандидатов максимальная задержка
прямого файлового прохода составила 26,0 мс для HIGH QUALITY, 10,2 мс для
NORMAL и 11,5 мс для ROBUST на этом ПК.
Все 88 проходов этих двух наборов дали столько же уникальных декодированных
кадров, сколько входных. Повторного кадра под новым номером не было в
файловом декодировании; поведение экрана не проверено. Полученная частота
кодирования выше заданной FPS на этом ПК, но это не частота доставки через радио.
**MEASURED, проверка малого потока:** при настройке 40 кбит/с фактический
H.264 поток на `320×240 / 5` достигал 43,0 кбит/с в среднем и 56,0 кбит/с
в худшую секунду; опорный кадр до 2709 байт. Ближние автомобили узнаваемы,
но их края и дорожная текстура сильно размываются. На `640×360 / 10` получено
44,8/67,6 кбит/с. Проверены все 11 сочетаний при 40 кбит/с, оба участка;
во всех 44 проходах JPEG и H.264 число уникальных кадров равно входному.
Даже наименьший пик среди всех этих проходов составил 29 пакетных мест за
секунду, больше 21,76 принятого пакета/с в измеренном сеансе Lab066. Подробности:
`comparison_traffic_40k.json`; пик RSS этого набора — 240,5 МиБ.
**MEASURED, потеря данных в файле:** в каждом H.264 проходе удаляли первый
опорный пакет и, отдельно, первый промежуточный пакет. Точное совпадение с
неповреждённым декодированием возвращалось на следующем опорном кадре через
1 секунду исходного времени. Промежуточные изображения до него могут быть
испорчены или отсутствовать. У независимого JPEG потеря одного изображения
не мешает декодировать следующее. Это программная проба потери пакета кодека,
а не проверка нашего радиопротокола или реального показа без зависаний.
## Выбор режимов и граница вывода
Три **кандидата для следующей программной работы**, выбранные после замеров:
| Назначение | Режим | Причина | Радио сейчас |
|---|---|---|---|
| HIGH QUALITY | 854×480, 10 кадров/с, H.264 320k | Машины и полосы различимы; 357 кбит/с средний, 456 кбит/с пик, кодирование 5,1 мс/кадр | НЕТ |
| NORMAL | 640×360, 10 кадров/с, H.264 320k | Машины и направление различимы; 354/376 кбит/с, кодирование 3,0 мс/кадр | НЕТ |
| ROBUST | 320×240, 5 кадров/с, H.264 160k | Ближние машины видны при меньшей нагрузке; 117/172 кбит/с, кодирование 1,2 мс/кадр | НЕТ |
Названия описывают исследуемые программные профили, а не пригодность для
управления ровером. Вариант 40 кбит/с не выбран даже как ROBUST: потеря мелких
деталей и всплеск пакетов слишком велики. Для будущего видеотранспорта нужны
номер изображения, номер фрагмента, время создания и срок актуальности;
ожидающая очередь не больше одного нового изображения, сброс просроченного,
сброс зависимого H.264 потока после потери опорных данных до следующего IDR.
Имеющиеся PHY, FEC, CRC, заголовки и модуляция не менялись.
Для `1280×720 / 5` H.264 320k потребовал 346 кбит/с в среднем, 441 кбит/с
в худшую секунду и до 228 пакетных мест за секунду. Кодирование заняло в
среднем до 11,5 мс/кадр, декодирование до 1,7 мс/кадр, прямой файловый путь
до 32,0 мс для отдельного кадра; фактически кодировалось около 87 кадров/с.
Текущие 5-мс паузы ограничивают эфир примерно 60,49 пакета/с без потерь.
Крупнейший опорный кадр 12 593 байта требует 52 фрагмента и 0,860 с их
сериализации; наибольший кадр вообще — 14 550 байт, 60 фрагментов и 0,992 с.
Чтобы передать этот кадр в период 0,2 с, потребовалось бы не менее 300
пакетов/с без учёта управления, ошибок и защитного запаса. JPEG q35 при
том же режиме дал 1745/2247 кбит/с. Для 720p нужны иной радиобюджет или
существенно меньшие кадры, а также ограничение пиков, потерь и срока
актуальности; одной скорости кодировщика недостаточно.
## Очереди и наблюдаемые ограничения
В Lab053 очереди ограничены: 8 IQ-блоков, 4 строгих кандидата и 16 выходных
пакетов. В программном стенде переполнений не было. У экспериментального
`lab053_pipeline.py` растут списки истории блоков, пакетов и кадров: они нужны
для отчёта, но не должны попадать в рабочий процесс ноутбука. Новый повтор
IQ не собирает эту историю и не строит графики. Штатный приёмник всё же
сохраняет внутренние счётчики и двойное IQ-кольцо; в Lab053 выделение кольца
составило 6 797 824 байта. Это измеренная стоимость текущей реализации,
а не основание менять PHY в VIDEO-001.
`ImageSink` держит один собираемый JPEG. Недостающие фрагменты старого
изображения задерживают его завершение до появления следующего ID; в текущем
формате нет времени создания и срока годности. Отдельной H.264 очереди ещё
нет. В автономном сравнении создан один экземпляр libx264 на весь участок,
не новый процесс на каждый кадр. Основные измеренные ограничения для 10/15
кадров/с: пакетная и пиковая битовая нагрузка, потери Lab066, крупные опорные
изображения и отсутствие транспорта с правилами свежести и восстановления.
Одной достаточной скорости CPU-кодирования для решения этих вопросов мало.
**NOT_RUN:** независимые генераторы, передача H.264 или непрерывного видео по
эфиру, живой USB-приём, камера, экран, операторская проверка препятствий,
ноутбук и длительное подтверждение полезной скорости радиоканала. Следующая
практическая задача: сделать программный видеотранспорт с ограничением очереди,
дедлайном изображения и восстановлением по IDR, затем проверить его на
сохранённом IQ без доступа к SDR.
**Проверки:** `video_validate_results.py` сверил 132 прохода в трёх наборах:
матрицу режимов, SHA исходника, размеры кадров, поток, пакетные места,
уникальность и восстановление. Целевые тесты VIDEO-001: 4 пройдены. Общие
тесты: 1022 пройдены, один исторический тест исключён, поскольку в отдельном
рабочем дереве нет исходного `data/raw/lab064/tx_metadata.json`. Полный
`quick_gate.py` в этом дереве не завершён. На радио тесты не запускались.
## Воспроизведение
Из ветки `video-development` после установки `requirements-video.txt`. Исходный
ролик берётся по ссылке выше и сверяется с указанным SHA-256. Окна по умолчанию:
7–13 и 33–39 с; для другого ролика доступны повторяемые параметры `--window`.
```powershell
New-Item -ItemType Directory -Force data\raw\video001
Invoke-WebRequest https://raw.githubusercontent.com/udacity/CarND-Vehicle-Detection/master/project_video.mp4 -OutFile data\raw\video001\udacity_project_video.mp4
(Get-FileHash -Algorithm SHA256 data\raw\video001\udacity_project_video.mp4).Hash
python -B tools/video_compare.py --source data\raw\video001\udacity_project_video.mp4 --output data\processed\video001\comparison_traffic_160k.json --h264-maxrate-bps 160000
python -B tools/video_compare.py --source data\raw\video001\udacity_project_video.mp4 --output data\processed\video001\comparison_traffic_320k.json --h264-maxrate-bps 320000
python -B tools/video_compare.py --source data\raw\video001\udacity_project_video.mp4 --output data\processed\video001\comparison_traffic_40k.json --h264-maxrate-bps 40000
python -B tools/video_validate_results.py --source data\raw\video001\udacity_project_video.mp4 data\processed\video001\comparison_traffic_160k.json data\processed\video001\comparison_traffic_320k.json data\processed\video001\comparison_traffic_40k.json
python -B tools/video_report_table.py --comparison-160k data\processed\video001\comparison_traffic_160k.json --comparison-320k data\processed\video001\comparison_traffic_320k.json --report docs\video\VIDEO_001.md
python -B -m pytest tests\test_video_001_compare.py -q -p no:cacheprovider --basetemp data\processed\video001\pytest_temp_target
```
Повтор IQ описан в `SECOND_COMPUTER_SETUP.md`; он требует сохранённый файл
и исходную ведомость SHA только для послерезультатной сверки.