VVdeC позволяет декодировать видеопотоки H.266/VVC в несжатые кадры YUV или Y4M, проверять корректность декодирования, управлять многопоточной обработкой и встраивать VVC-декодирование в собственные приложения через библиотеку C, FFmpeg, GStreamer или WebAssembly.
В повседневной работе с VVdeC важно сразу разделить три задачи. Для проверки элементарного VVC-потока подходит консольная утилита vvdecapp; для обработки файлов-контейнеров удобнее вызывать декодер через FFmpeg или GStreamer; для собственной программы предназначен API библиотеки. Такой выбор избавляет от попыток передать в vvdecapp MP4 как будто это поток .266 и делает цепочку обработки предсказуемой.
Декодер рассчитан на профиль VVC Main10 и работает с особенностями стандарта, которые требуют заметно больше вычислений, чем старые видеокодеки. Поэтому результат зависит не только от правильности командной строки, но и от числа потоков, задержки разбора кадров, формата вывода и того, каким способом извлечён исходный VVC. Ниже эти параметры рассматриваются как части одного рабочего процесса, а не как изолированные переключатели.
Скачать VVdeC
- Конвертация видео
- Сжатие файлов
- Просто для новичков
- Нет графического интерфейса
- CLI ждёт raw VVC-поток
- YUV требует много места
Что именно делает VVdeC
VVdeC — программный декодер H.266/VVC. Его задача противоположна кодированию: на вход поступают сжатые NAL-единицы VVC, а на выходе появляются реконструированные кадры. Самый простой сценарий — преобразовать элементарный поток sample.266 в последовательность сырых кадров YUV. Именно этот режим удобен при проверке совместимости кодера, анализе тестовых потоков и подготовке кадров для инструментов, которые не умеют декодировать VVC самостоятельно.
В состав исходного проекта входит библиотека с C API и пример консольного приложения vvdecapp. В текущей документации Fraunhofer HHI примерное приложение помечено как deprecated, поэтому его разумно рассматривать прежде всего как диагностический инструмент и наглядный пример использования библиотеки. Это не означает, что команда декодирования перестала быть полезной: она остаётся удобным способом проверить поток, получить YUV/Y4M и воспроизвести ошибку без дополнительного контейнерного слоя.
Если исходное видео находится в MP4, MPEG-TS или другом контейнере, сам контейнер сначала должен быть разобран. Для этого подходит FFmpeg, где libvvdec можно выбрать как VVC-декодер. В GStreamer ту же роль выполняет элемент vvdec. В обоих случаях демультиплексор и парсер берут на себя извлечение VVC-потока, а VVdeC занимается непосредственно восстановлением изображения.
- vvdecapp — быстрое декодирование raw VVC в YUV/Y4M, проверка хэшей и параметров многопоточности.
- libvvdec — встраивание декодера в собственную программу с контролем буферов, кадров и жизненного цикла.
- FFmpeg — удобная работа с контейнерами, преобразование пиксельных форматов и передача кадров дальше по фильтрам.
- GStreamer — потоковые конвейеры с автоматическим согласованием caps и подключением вывода или последующей обработки.
- WebAssembly — компиляция ядра VVdeC для использования из JavaScript в браузерном проигрывателе или другом веб-приложении.
Какие входные данные ожидать от vvdecapp
Ключевая особенность vvdecapp состоит в том, что параметр --bitstream ожидает raw VVC bitstream, то есть элементарный поток с NAL-единицами, а не произвольный медиаконтейнер. Расширение .266, .h266 или .vvc само по себе не гарантирует корректность содержимого, но обычно используется именно для такого сырого потока. Если передать MP4 напрямую, приложение не получает структуру контейнера и не обязано находить в нём нужную видеодорожку.
Для файла MP4 практический путь — сначала демультиплексировать VVC без перекодирования. В FFmpeg это делается копированием видеопотока и применением битстрим-фильтра vvc_mp4toannexb. Результирующий файл .266 уже подходит для vvdecapp. Такой порядок особенно полезен при диагностике: он отделяет возможную проблему контейнера от проблемы самого VVC-потока.
ffmpeg -i input.mp4 -an -vcodec copy -bsf vvc_mp4toannexb output.266
vvdecapp -b output.266 -o decoded.yuv
Если поток получен непосредственно от VVC-кодера, дополнительная операция обычно не нужна. Однако перед разбором ошибки стоит убедиться, что файл начинается с корректной последовательности NAL-единиц и содержит нужные наборы параметров. Поток, обрезанный посередине GOP или лишённый VPS/SPS/PPS, может выглядеть как неработающий декодер, хотя причина находится на стороне подготовленного входа.
Когда лучше не извлекать raw VVC
Если цель — просто декодировать видеодорожку из MP4 в другой формат, промежуточный .266 не обязателен. FFmpeg умеет сам демультиплексировать контейнер и вызвать libvvdec. Это уменьшает число временных файлов, сохраняет временные метки в обработке и облегчает работу с несколькими дорожками. Raw-поток имеет смысл создавать, когда требуется воспроизводимый тест для vvdecapp, проверка conformance или анализ битстрима отдельными инструментами.
Потоковый сценарий тоже не требует временного файла: контейнер можно разобрать в медиаконвейере и передавать NAL-единицы декодеру по мере поступления. В библиотечном API эту роль выполняют структуры access unit. Важно лишь соблюдать границы данных, которые ожидает API, и не считать произвольный фрагмент файла полноценной NAL-единицей.
Первый запуск vvdecapp
Минимальная команда содержит входной битстрим и имя файла с декодированными кадрами. Короткие ключи -b и -o эквивалентны полным --bitstream и --output. Для первичной проверки лучше не добавлять экспериментальные опции: если базовый вызов работает, остальные параметры можно менять по одному и точно понимать, что повлияло на результат.
vvdecapp -b str.266 -o raw_yuv.yuv
Файл YUV после этой команды не содержит привычного контейнерного описания. Проигрывателю или анализатору обычно нужны ширина, высота и пиксельный формат. Для 10-битного материала документация VVdeC сопоставляет вывод с yuv420p10le в терминологии FFmpeg, для 8-битного — с yuv420p. Если открыть 10-битный файл как 8-битный либо указать неверное разрешение, изображение будет выглядеть испорченным, хотя само декодирование могло пройти корректно.
Для проверки доступных параметров служит vvdecapp --help. Набор ключей может отличаться между сборками, поэтому справка конкретного исполняемого файла важнее команды из старой статьи или чужого скрипта. Особенно это касается диагностических и экспериментальных переключателей; базовые параметры ввода, вывода, числа потоков, parse delay и проверки хэшей документированы в официальном руководстве.
Основные параметры командной строки
| Параметр | Назначение | Когда менять |
|---|---|---|
--bitstream, -b | Raw VVC bitstream | Всегда указывает исходный элементарный поток |
--output, -o | Имя YUV-файла или - для stdout | Когда кадры нужно сохранить либо передать в pipe |
--y4m | Принудительный вывод Y4M | Удобно для pipe; для расширения .y4m включается автоматически |
--threads, -t | Размер пула потоков | Для управления загрузкой CPU и воспроизводимых тестов |
--verbosity, -v | Подробность сообщений | При диагностике ошибок и в автоматизированных сценариях |
--parsedelay, -p | Число разобранных кадров, ожидающих реконструкции | Для компромисса между задержкой и масштабированием по потокам |
--SEIDecodedPictureHash, -dph | Проверка Decoded Picture Hash SEI | Для conformance и обнаружения несовпадения реконструкции |
--loops, -L | Повторное декодирование потока | Для тестов без многократного запуска процесса |
--CheckYuvMD5, -md5 | Сравнение декодированного YUV с заданным MD5 | Для автоматической проверки эталонного результата |
--help, -h | Справка | Для сверки опций конкретной сборки |
Значение -1 для --threads означает автоматический выбор с выделением потока на доступное ядро в логике sample app, а 0 переводит декодирование в однопоточный режим. Однопоточный запуск полезен не только на слабом компьютере: он упрощает сравнение логов, воспроизведение редких ошибок синхронизации и профилирование конкретных участков кода.
--parsedelay не является обычной настройкой качества. Он управляет тем, сколько уже разобранных кадров может ждать реконструкции. Увеличение этого окна повышает потенциальный параллелизм, но одновременно добавляет задержку и требует больше буферизации. Автоматическое значение -1 привязывает задержку разбора к выделенным потокам, что обычно разумнее случайного большого числа.
YUV и Y4M: какой вывод выбрать
Raw YUV — самый прямой результат декодирования. В нём подряд записываются значения плоскостей кадров, без служебной оболочки привычного видеоконтейнера. Это удобно для алгоритмов анализа и кодеров, которые уже знают геометрию и формат данных, но неудобно для ручного просмотра: размер кадра, цветовая субдискретизация, разрядность и частота кадров должны быть известны отдельно.
Y4M добавляет текстовый заголовок и служебные разделители кадров. Он значительно удобнее в pipe-цепочках, потому что следующая программа может получить часть параметров изображения из самого потока. В VVdeC формат можно запросить ключом --y4m; при выводе в файл с расширением .y4m sample app включает его автоматически.
vvdecapp -b str.266 -o raw_yuv.y4m
vvdecapp -b str.266 --y4m -o - | some_consumer
Выбор Y4M не превращает результат в сжатое видео: кадры остаются практически несжатыми, поэтому объём данных быстро растёт. Если задача состоит в перекодировании, промежуточный YUV-файл часто вообще не нужен. Лучше связать декодер и следующий этап pipe или воспользоваться FFmpeg, где кадры передаются между компонентами внутри процесса.
Почему YUV-файл выглядит сломано
Самая частая причина — неверные параметры просмотра. Для raw-файла проигрыватель не может определить ширину и высоту по имени. Если декодировано 1920×1080, а просмотрщик считает кадр 1280×720, границы строк съезжают и изображение превращается в диагональные полосы. Аналогичный эффект появляется при несоответствии yuv420p и yuv420p10le.
Вторая причина — попытка считать маленький endian 10-битного вывода как обычный 8-битный поток. На каждый компонент приходится другая организация данных, поэтому ошибка видна не как чуть неправильные цвета, а как полностью испорченная структура изображения. При сомнениях проще сначала вывести Y4M либо проверить параметры через FFmpeg, а уже затем возвращаться к сырому YUV.
Передача декодированных кадров через stdout
Значение - у --output направляет вывод в стандартный поток. Для бинарных кадров это требует дисциплины: нельзя смешивать информационные сообщения декодера с самим видеопотоком. Приложения обычно разводят служебный вывод и stdout, но при написании оболочек следует помнить, что любое добавленное скриптом echo в тот же канал повредит данные.
Официальный пример показывает pipe из VVdeC в VVenC через Y4M. Такая связка полезна не потому, что декодирование и повторное кодирование обычно желательно, а как демонстрация корректной передачи геометрии и пиксельного формата между процессами без огромного временного файла.
vvdecapp -b str.266 --y4m -o - | vvencapp -i - -o newstr.266 --y4m
В реальном конвейере получателем может быть анализатор, конвертер или пользовательское приложение. Если downstream-процесс завершился раньше, источник получит закрытый pipe. Это следует отличать от ошибки VVC-декодирования: журнал при повышенной verbosity обычно показывает, дошёл ли декодер до проблемной NAL-единицы или запись оборвалась уже на стадии вывода.
Настройка многопоточности
Декодирование VVC хорошо распараллеливается не во всех местах одинаково. Параметр --threads задаёт размер пула потоков, но максимальное число логических процессоров не всегда означает лучший режим для конкретной системы. На машине, где параллельно выполняются другие задачи, ограничение пула может дать более предсказуемое время отклика и избежать конкуренции за память.
Для диагностики полезно сравнивать три режима: -t 0 как однопоточный контроль, умеренное фиксированное число потоков и автоматическое -t -1. Сравнение здесь не обязано сводиться к скорости. Если ошибка появляется только при высокой параллельности, такое различие помогает сузить область поиска; если результат декодирования одинаков, можно дальше настраивать производительность отдельно от корректности.
--parsedelay тесно связан с пулом. При большем количестве разобранных заранее кадров планировщик получает больше готовой работы, однако возрастает задержка до появления соответствующих кадров на выходе. Для офлайн-декодирования это зачастую приемлемо, а для интерактивного воспроизведения или низколатентной обработки — уже нет. Поэтому универсального лучшего числа не существует.
SIMD и архитектура процессора
VVdeC содержит оптимизированные SIMD-реализации. В текущей системе сборки предусмотрены x86 intrinsics, а для ARM — NEON и дополнительные варианты RDM, SVE и SVE2 при наличии поддержки компилятором и целевой платформой. На не-x86 часть x86-интринсиков может транслироваться через SIMDe. Пользователю обычной готовой сборки эти детали чаще всего не нужно менять, но при самостоятельной компиляции они объясняют, почему две сборки на одном и том же потоке могут заметно отличаться по нагрузке.
Опции CMake VVDEC_ENABLE_X86_SIMD, VVDEC_ENABLE_ARM_SIMD, VVDEC_ENABLE_ARM_SIMD_RDM, VVDEC_ENABLE_ARM_SIMD_SVE и VVDEC_ENABLE_ARM_SIMD_SVE2 предназначены именно для формирования бинарника. Это не параметры качества изображения. Отключение SIMD полезно при поиске проблем оптимизированного кода или для специализированной переносимости, но не является обычным способом сделать декодирование точнее.
Уровень сообщений и диагностический журнал
--verbosity определяет, сколько информации выводит vvdecapp. В нормальном пакетном сценарии избыточный журнал мешает, а при разборе повреждённого VVC-потока — наоборот, помогает увидеть точку отказа, тип NAL и контекст сообщения. Практический подход состоит в том, чтобы сначала сохранить минимальный воспроизводимый вход, затем повторить запуск с повышенной подробностью и приложить точную команду к отчёту.
Сообщение об ошибке не всегда означает, что декодер не поддерживает кодек. H.266/VVC включает сложную структуру параметров и ссылочных кадров, поэтому отсутствие нужного parameter set, неверная длина NAL, обрыв данных или несогласованная последовательность могут приводить к исключению намного позже фактического места повреждения. Чем меньше входной файл, воспроизводящий проблему, тем легче отделить ошибку источника от ошибки реализации.
При работе с недоверенными потоками журнал особенно полезен, но сам по себе не заменяет изоляцию процесса. Декодер обрабатывает сложный бинарный формат; повреждённые последовательности должны считаться потенциально опасным входом. Для серверной обработки разумно ограничивать ресурсы процесса, обновлять библиотеку и не давать процессу лишних прав независимо от того, насколько подробные сообщения он печатает.
Проверка Decoded Picture Hash
VVC-поток может содержать SEI-сообщение Decoded Picture Hash. Ключ --SEIDecodedPictureHash или короткий -dph просит VVdeC сравнивать хэш, переданный в потоке, с реконструированным изображением. Это удобный механизм conformance: совпадение говорит о том, что декодер получил ожидаемые данные кадра, а несовпадение позволяет быстро обнаружить отклонение.
Проверка имеет смысл только тогда, когда соответствующий SEI действительно присутствует. Отсутствие DPH нельзя интерпретировать как ошибку: поток может быть корректным и без этого дополнительного сообщения. Если задача — автоматическая лабораторная проверка, сначала стоит убедиться, что тестовый набор предусматривает DPH, а затем включать -dph как обязательное условие.
В цепочках, где VVC предварительно перепаковывают, важно не путать проверку битового файла с проверкой реконструкции. Контейнеризация может изменить расположение или представление NAL-единиц, не меняя декодированные пиксели. DPH относится именно к реконструированному изображению, поэтому помогает отделить косметические различия контейнера от реального изменения результата декодирования.
Проверка YUV по MD5
Ключ --CheckYuvMD5 предназначен для тестов, где заранее известен MD5 декодированного YUV. Это более внешний контроль, чем DPH: сравнивается ожидаемый результат всего вывода в выбранном представлении. Он удобен в CI, когда одно и то же тестовое видео декодируется на разных архитектурах и сборках, а результат должен оставаться идентичным.
Перед использованием такого контроля важно зафиксировать формат. MD5 от 8-битного yuv420p и от 10-битного yuv420p10le не совпадёт даже при визуально одинаковом содержимом. То же относится к любому преобразованию диапазона, матрицы цвета или расположения плоскостей. Хэш должен относиться непосредственно к тому байтовому потоку, который создаёт выбранная конфигурация.
--loops дополняет тестовый сценарий, позволяя повторить декодирование одного входного файла несколько раз внутри запуска. Это удобно для воспроизводимости и длительных проверок, но при измерениях нужно понимать, что повторный запуск может взаимодействовать с файловым кэшем и прогревом CPU. Сам параметр не является бенчмарком и не формирует статистику автоматически.
Работа через C API
Для встраивания VVdeC не требуется запускать vvdecapp как внешний процесс. Библиотека предоставляет C API, через который приложение создаёт конфигурацию, открывает экземпляр декодера, выделяет контейнер для access unit, подаёт NAL-единицы и получает указатели на декодированные кадры. Такой подход даёт контроль над памятью и избавляет от сериализации YUV на диск между этапами.
Типичная инициализация начинается со структуры vvdecParams. Функция vvdec_params_default заполняет безопасные значения по умолчанию, после чего приложение изменяет только необходимые поля — например, уровень логирования или параметры потоков. Затем vvdec_decoder_open возвращает дескриптор декодера. Если открыть экземпляр не удалось, переходить к подаче данных нельзя: ошибка инициализации должна завершить текущий сценарий.
vvdecParams params;
vvdec_params_default(¶ms);
params.logLevel = VVDEC_INFO;
vvdecDecoder* decoder = vvdec_decoder_open(¶ms);
Следующий объект — vvdecAccessUnit. Документация показывает последовательность vvdec_accessUnit_alloc, vvdec_accessUnit_default и vvdec_accessUnit_alloc_payload. Размер payload должен быть достаточным для максимальной NAL-единицы, которую приложение собирается передать. Ошибка здесь опасна: нельзя копировать NAL без проверки ёмкости буфера и затем надеяться, что декодер сам исправит переполнение.
Подача NAL-единиц
После чтения очередной NAL её байты помещаются в payload, а фактическая длина задаётся через соответствующее поле access unit. Затем вызывается vvdec_decode. Вызов может вернуть кадр не на каждую поданную NAL: внутреннее упорядочивание и буферизация кадров являются нормальной частью видеодекодирования. Поэтому код должен отдельно обрабатывать код возврата и наличие результата.
Полученный vvdecFrame принадлежит жизненному циклу декодера. После обработки кадр освобождается через vvdec_frame_unref, а не обычным free. Это правило критично в долгоживущем приложении: пропуск unref постепенно удерживает внутренние буферы, а преждевременное освобождение или сохранение указателя после unref ведёт к обращению к недействительной памяти.
Приложение может передать кадр в рендерер, фильтр, конвертер или энкодер непосредственно из памяти. При этом нужно учитывать шаг строки и структуру плоскостей, а не предполагать, что вся картинка всегда лежит одним плотным блоком. Разрядность также определяет, интерпретируются элементы как восьми- или шестнадцатибитные значения.
Flush в конце потока
После последней входной NAL декодер может ещё хранить кадры, которые должны быть выданы в правильном порядке. Поэтому завершить цикл сразу после конца файла недостаточно. Документация предписывает вызывать flush до состояния EOF, обрабатывая все возвращаемые кадры тем же способом и освобождая их после использования.
Это особенно заметно на потоках с переупорядочиванием: отсутствие flush проявляется как потеря последних кадров. Ошибку легко ошибочно списать на повреждённый файл, хотя фактически программа просто не дочитала внутренний буфер декодера. Правильная последовательность завершения — drain, освобождение access unit, закрытие декодера и только затем уничтожение связанных ресурсов приложения.
Управление памятью и сроком жизни объектов
При использовании API полезно заранее определить владельца каждого ресурса. Буфер input access unit контролируется приложением через функции VVdeC; выходной кадр выдаёт декодер и принимает обратно через vvdec_frame_unref; сам экземпляр закрывается библиотечной функцией. Явная модель владения избавляет от двойного освобождения и от попытки использовать указатель на плоскость после возврата кадра декодеру.
Если приложение передаёт полученные плоскости другому потоку, нужно решить, кто и когда удерживает кадр. Простая передача только адреса без синхронизации почти всегда ошибочна: декодер может переиспользовать внутреннюю память после unref. Безопасный вариант — закончить потребление до unref или скопировать данные в буфер, жизненный цикл которого принадлежит следующему этапу.
Размер входного payload тоже не следует фиксировать на глаз для всех файлов. NAL-единицы могут отличаться очень существенно. Парсер должен проверять длину до копирования и расширять буфер контролируемо. Для сетевого источника дополнительно требуется максимальный разумный предел, чтобы искусственно объявленная длина не заставила процесс выделить неограниченную память.
Получение информации о кадре
Выходной кадр содержит характеристики, необходимые для дальнейшей обработки: геометрию, формат плоскостей, разрядность и данные изображения. Не следует подменять их параметрами из имени файла. Если на вход подаются разные последовательности, сведения из самого декодированного кадра являются надёжной точкой для настройки конвертера или рендерера.
Приложение, которое пишет raw YUV, должно решить, в каком формате сохранять компоненты и как описать их пользователю. Именно поэтому sample app и Y4M полезны как эталон поведения: Y4M добавляет часть метаданных, а raw YUV оставляет их вне файла. Для API-потребителя те же параметры должны жить в структуре состояния сеанса или передаваться вместе с каждым изменением последовательности.
При смене разрешения внутри потока нельзя безусловно продолжать писать кадры как будто размеры постоянны. Для сырого YUV такой файл становится неудобен: внешнему инструменту неоткуда узнать границу изменения. В интерактивном приложении рендерер должен быть готов перестроить поверхность вывода после получения новой информации последовательности.
VVdeC и FFmpeg
FFmpeg удобен, когда нужно декодировать VVC из контейнера, преобразовать пиксельный формат, сохранить кадры или включить декодирование в большой транскодирующий конвейер. В сборке с поддержкой VVdeC декодер выбирается именем libvvdec. Наличие можно проверить списком кодеков, а список доступных параметров — справкой конкретного декодера.
ffmpeg -hide_banner -codecs | grep vvc
ffmpeg -h decoder=libvvdec
Официальная страница интеграции указывает, что собственный VVC-декодер FFmpeg является вариантом по умолчанию, поэтому для осознанного выбора VVdeC нужно явно указать -c:v libvvdec. Это важно при сравнении: без параметра можно считать, что тестируется VVdeC, хотя фактически кадры декодирует другой backend.
ffmpeg -c:v libvvdec -i input.mp4 output.yuv
ffmpeg -c:v libvvdec -i input.mp4 -strict -1 output.y4m
Поддерживаемые форматы вывода, перечисленные для интеграции, включают gray, gray10le, yuv420p, yuv422p, yuv444p и соответствующие 10-битные варианты yuv420p10le, yuv422p10le, yuv444p10le. Конкретный формат, который получится без принудительного -pix_fmt, зависит от входа и согласования фильтров.
Film grain SEI в FFmpeg-интеграции
Для libvvdec в FFmpeg документирована опция -filmgrain, управляющая использованием Film Grain SEI; значение по умолчанию включено. Если сравниваются пиксельные хэши или два декодера, это существенная деталь: применение синтеза зерна меняет итоговые кадры, поэтому обе стороны теста должны работать с одинаковой политикой.
При обычном просмотре выключать film grain только ради скорости любой ценой без понимания содержимого не стоит: SEI является частью задуманного представления потока. Для лабораторного сравнения, напротив, параметр следует фиксировать явно и записывать в команду, чтобы результат можно было повторить.
Измерение декодирования в FFmpeg
Для технической проверки можно направить декодированные кадры в null muxer и включить встроенный -benchmark. Такой запуск убирает запись огромного YUV-файла и позволяет оценивать именно путь чтения, демультиплексирования и декодирования. Результаты нельзя переносить между разными компьютерами как универсальную характеристику VVdeC, но для сравнения двух конфигураций на одной системе метод удобен.
ffmpeg -c:v libvvdec -benchmark -i input.mp4 -f null NULL
Чтобы сравнение имело смысл, нужно одинаково обращаться с контейнером, потоками и выводом. Одна команда, записывающая YUV на медленный диск, и другая, отправляющая кадры в null, измеряют разные нагрузки. Аналогично, аппаратный декодер другого кодека не является корректной точкой сравнения с программным VVC-декодером.
Просмотр VVC через ffplay
FFplay использует инфраструктуру FFmpeg и поэтому удобен для быстрой визуальной проверки. Он может работать как с raw VVC-файлами, так и с контейнерами, которые поддерживает соответствующая сборка. Если требуется именно VVdeC, декодер следует указать явно, иначе будет выбран стандартный VVC backend FFmpeg.
ffplay -codec:v:1 libvvdec input.mp4
Для элементарных потоков обычно встречаются расширения .266 и .vvc; для контейнеров — например MP4 или MPEG-TS. Возможность открыть контейнер не означает, что vvdecapp тоже должен его понимать: FFplay перед декодером использует демультиплексоры и парсеры, которых в минимальной sample app нет.
Если картинка воспроизводится через FFplay, но не через собственное приложение на libvvdec, полезно сравнить именно подготовку NAL-единиц. Часто различие находится в парсере Annex B, обработке parameter sets или финальном flush, а не в самом ядре VVdeC.
Декодирование в GStreamer
В GStreamer VVdeC доступен как элемент vvdec. Его место в конвейере — после демультиплексора и H.266-парсера, перед преобразованием формата и видеовыводом. Официальная документация приводит цепочку с MP4: файл читает filesrc, qtdemux извлекает дорожку, h266parse формирует поток, vvdec декодирует его, а videoconvert и autovideosink отвечают за согласование и показ.
gst-launch-1.0 filesrc location=vvc.mp4 ! qtdemux ! h266parse ! vvdec ! videoconvert ! autovideosink
Такой конвейер наглядно показывает границы ответственности. Если qtdemux не находит дорожку или h266parse не согласует caps, проблема возникает до VVdeC. Если декодер создаёт raw video, но sink не принимает формат, нужно исследовать согласование после VVdeC. Разделение этапов делает диагностику точнее, чем запуск большого приложения, где все ошибки сводятся к сообщению не удалось открыть видео.
Sink pad элемента принимает video/x-h266 с byte-stream и alignment по access unit. На выходе объявляются raw-форматы, среди которых GRAY8, I420, Y42B, Y444 и 10-битные варианты. Это позволяет медиаконвейеру автоматически подобрать videoconvert или другой потребитель, но не отменяет необходимости проверять caps, если цепочка содержит специализированный фильтр.
Потоки в GStreamer-элементе
Для GStreamer задокументированы свойства n-threads и n-parser-threads. Первое связано с рабочими потоками декодера, второе — с потоками разбора. Настраивать их следует в контексте всего pipeline: чрезмерное количество потоков в каждом элементе может создать конкуренцию между декодером, конвертером, фильтрами и sink.
Для живого конвейера важна не только средняя производительность, но и стабильность времени обработки. Если кадры приходят рывками, стоит сначала проверить очереди и timestamp, затем загрузку CPU, и лишь после этого увеличивать число потоков VVdeC. Дополнительный параллелизм не компенсирует неверно настроенный источник или блокирующий downstream.
При построении приложения на GStreamer полезно слушать bus и сохранять сообщения ERROR/WARNING вместе с caps вокруг vvdec. Это гораздо информативнее, чем фиксировать только факт остановки pipeline. По caps можно понять, пришёл ли на декодер действительно H.266 byte-stream и в каком raw-формате он попытался отдавать кадры.
Сборка VVdeC из исходников
Сборка проекта основана на CMake. Для обычной конфигурации достаточно создать отдельный каталог build, выбрать Release и запустить компиляцию. Отдельный каталог важен не из эстетических соображений: он позволяет быстро удалить результаты и повторить конфигурацию с другими флагами, не смешивая созданные файлы с исходным деревом.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
На генераторах с несколькими конфигурациями значение --config Release выбирается во время сборки; на типичной Unix-конфигурации CMAKE_BUILD_TYPE=Release действует при генерации. Если декодер нужен для измерения производительности, случайная Debug-сборка даст вводящий в заблуждение результат и может существенно изменить поведение проверок.
Homebrew-формула для VVdeC включает BUILD_SHARED_LIBS=1 и VVDEC_INSTALL_VVDECAPP=1, после чего выполняет CMake install. Это хороший ориентир для системной интеграции: разделяемая библиотека устанавливается вместе с заголовками и sample app, а downstream-проект может находить их штатными механизмами вместо жёстко прописанных путей в каталоге сборки.
Статическая и разделяемая библиотека
Статическое связывание упрощает перенос одного исполняемого файла, но переносит ответственность за лицензионные уведомления, повторную сборку и обновление библиотеки на владельца приложения. Разделяемая библиотека удобнее для системного пакета и нескольких программ, использующих один libvvdec, однако требует контролировать ABI и путь поиска shared library.
Нельзя считать, что двоичная совместимость сохраняется между любыми крупными ветками. При изменении API/ABI dependent-приложение нужно пересобрать. Поэтому при поставке собственного продукта разумно фиксировать совместимую версию в системе сборки и обновлять её осознанно, прогоняя тестовые VVC-потоки после смены зависимости.
Для разработки полезно иметь два профиля: Release для реальной скорости и RelWithDebInfo либо отдельную диагностическую сборку для стека вызовов. Полный Debug нужен, когда исследуется логика, но он не должен становиться случайной эталонной конфигурацией для пользовательского декодирования.
Настройки CMake, которые действительно относятся к VVdeC
В текущем CMake-файле проекта доступны флаги, управляющие SIMD, трассировкой и диагностикой входного потока. Их следует отличать от параметров vvdecapp: CMake-опция изменяет создаваемый бинарник, а ключ командной строки действует уже во время запуска. Если сборка получена из пакета, переключить CMake-флаг без пересборки нельзя.
| Опция CMake | Что меняет | Практическое назначение |
|---|---|---|
VVDEC_ENABLE_X86_SIMD | Включает x86 intrinsics | Обычная оптимизированная сборка и диагностика SIMD |
VVDEC_ENABLE_ARM_SIMD | Включает ARM intrinsics | NEON-путь на ARM |
VVDEC_ENABLE_ARM_SIMD_RDM | Разрешает ARM RDM | Используется при поддержке компилятором и CPU |
VVDEC_ENABLE_ARM_SIMD_SVE | Разрешает SVE | Специализированная ARM-сборка |
VVDEC_ENABLE_ARM_SIMD_SVE2 | Разрешает SVE2 | Специализированная ARM-сборка |
VVDEC_ENABLE_TRACING | Компилирует трассировку | Разработка и анализ внутренних операций |
VVDEC_WRITE_INPUT_BITSTREAM | Добавляет запись входного bitstream для отладки | Фиксация реально поданных декодеру данных |
VVDEC_ENABLE_UNSTABLE_API | Открывает нестабильные расширения API | Только если приложение готово к несовместимым изменениям |
Опцию unstable API не следует включать на всякий случай. Её смысл именно в доступе к интерфейсам, для которых не обещается та же стабильность, что у основной поверхности API. Если приложение не использует конкретную экспериментальную функцию, включение такого режима только усложняет поддержку и тестирование.
Установка библиотеки и поиск зависимостей
После успешной сборки можно выполнить CMake install в системный или пользовательский prefix. Если права администратора не нужны, удобнее выбрать каталог внутри домашней директории и передать его downstream-проекту через CMAKE_PREFIX_PATH или механизмы pkg-config. Это позволяет держать тестовую сборку отдельно от системной.
Ошибка library not found после успешной компиляции чаще относится не к VVdeC как декодеру, а к динамическому загрузчику. Нужно проверить, куда установлен libvvdec, присутствует ли каталог в ожидаемых путях, и с каким RPATH собран потребитель. Копирование библиотеки в случайный системный каталог решает симптом, но создаёт труднообъяснимую конфигурацию для следующего обновления.
При статическом связывании проблема выглядит иначе: линкер должен получить архив и все необходимые системные зависимости в корректном порядке. Если собственный CMake-проект использует экспортируемую цель VVdeC, лучше опираться на неё, чем вручную перечислять каждый флаг компоновщика. Так настройки из пакета переносятся вместе с целью.
Сборка для Windows
Проект поддерживает Windows на x86/x64 и ARM64 в заявленной матрице архитектур, но практический способ сборки зависит от выбранного компилятора. С Visual Studio удобнее генерировать CMake-проект и собирать Release-конфигурацию. Если используется MinGW, важно не смешивать библиотеки, собранные для другого ABI, только потому что имена файлов похожи.
Для командной утилиты после сборки следует найти именно vvdecapp.exe соответствующей конфигурации. В многоконфигурационном генераторе Release и Debug могут лежать в разных подкаталогах. Ошибка, при которой из PATH запускается старый vvdecapp.exe, встречается чаще, чем кажется: перед сравнением параметров полезно вывести версию или полный путь найденного исполняемого файла.
Если приложение на Windows использует shared DLL, рядом с ним должны быть доступные runtime-библиотеки нужного toolchain и сам libvvdec. Сообщение о невозможности запуска до появления окна или до обработки командной строки обычно указывает именно на загрузчик DLL, а не на повреждение VVC-потока.
Сборка на Linux
На Linux типичный путь состоит из CMake, компилятора C++ и make/ninja. Для интеграции с FFmpeg часто удобнее собрать shared library и установить её в известный prefix, а затем сконфигурировать FFmpeg с поддержкой libvvdec. После установки имеет смысл проверить, что pkg-config и динамический загрузчик видят одну и ту же версию библиотеки.
Пакетные системы упрощают эту часть, но разделяют runtime, приложение и development-файлы. Например, RPM-пакет с vvdecapp может зависеть от отдельного пакета vvdec-libs. Для запуска утилиты достаточно runtime-зависимостей, а для компиляции собственного проекта нужны заголовки и файлы разработки.
Если после обновления пакета FFmpeg перестал загружать libvvdec, проверьте не только наличие файла, но и SONAME, против которого собран FFmpeg. Копирование символической ссылки с прежним номером ABI опасно: успешная загрузка несовместимой библиотеки может дать гораздо более трудную ошибку позже.
Сборка на macOS
На macOS VVdeC можно собирать CMake напрямую или установить через Homebrew. Формула Homebrew собирает shared library и vvdecapp, поэтому после установки команда должна находиться через обычный prefix brew. На Apple Silicon важно не смешивать arm64-библиотеку с процессом x86_64, запущенным через Rosetta, если только вся цепочка осознанно собрана под одну архитектуру.
При ручной сборке Xcode/Clang выбор архитектуры задаётся на уровне CMake. Ошибка компоновки wrong architecture обычно означает, что один из объектов или зависимостей создан для другого target. Это не исправляется параметрами самого декодера; необходимо привести toolchain и зависимости к единой архитектуре.
Homebrew bottle удобен как готовый бинарный пакет, но его внутренний layout рассчитан на инфраструктуру Homebrew. Простое извлечение tar.gz в произвольную папку не равно полноценной установке, если библиотека содержит ожидаемые относительные пути. Для редакционного дистрибутива такой пакет следует помечать как кандидат и проверять его на целевой системе, а не называть универсальным portable.
WebAssembly: VVdeC в браузерном коде
VVdeC поддерживает WebAssembly как цель сборки через Emscripten. Это не превращает основную программу в онлайн-сервис: речь идёт о компиляции той же библиотеки для среды браузера. Разработчик может включить полученные vvdecapp.wasm, JavaScript-обвязку и worker в собственный web player, сохранив управление загрузкой VVC-потока и выводом кадров на стороне приложения.
emcmake cmake -B build/wasm
cmake --build build/wasm
В CMake для WASM включаются pthreads, модульная загрузка и экспорт функций, а для SIMD-сборки используется -msimd128. Поэтому окружение должно поддерживать соответствующие WebAssembly-возможности. Если браузер блокирует многопоточность из-за политики изоляции страницы, проблема находится в окружении веб-приложения, а не в содержимом VVC.
Память WebAssembly ограничена иначе, чем память обычного нативного процесса. Большие кадры, несколько буферов и высокий parse delay могут быстро занять выделенное пространство. Нельзя переносить число потоков и буферизацию из серверной x86-сборки в браузер без измерения фактического потребления.
JavaScript-интерфейс
WASM-обвязка предоставляет объекты Decoder, AccessUnit и FrameHandle. Параметры создаются через new VVdeC.Params(), затем передаются конструктору декодера. После использования объекты, созданные в Emscripten binding, нужно удалять методом .delete(), иначе JavaScript-сборщик мусора не освобождает нативную память автоматически в тот момент, когда это ожидает приложение.
Payload access unit представлен типизированным массивом. Приложение копирует очередную NAL в au.payload, задаёт payloadUsedSize и вызывает decode с FrameHandle. Плоскости кадра доступны как Uint8Array или Uint16Array в зависимости от bit depth. Это позволяет загружать данные в WebGL/WebGPU или другой обработчик без предварительного текстового преобразования.
Логика flush в WASM та же по смыслу, что в C API: после последнего access unit нужно извлечь отложенные кадры до EOF. Затем кадры возвращаются декодеру, а Decoder, AccessUnit и FrameHandle удаляются. Если забыть этот этап, browser player может терять конец последовательности и одновременно удерживать память после переключения файла.
Встраивание в собственный проигрыватель
Проигрыватель на libvvdec должен решать больше задач, чем сам декодер. Нужно демультиплексировать контейнер, восстановить границы access unit, получить кадры, преобразовать или отобразить их, синхронизировать с аудио и обрабатывать timestamp. VVdeC отвечает за H.266/VVC-декодирование, но не заменяет весь медиаплеер.
Если источник — raw .266, временные метки могут отсутствовать полностью. Частоту кадров тогда приходится брать из параметров потока, внешних метаданных или пользовательской настройки. Если источник — MP4, timing находится в контейнере и должен пройти через демультиплексор. Потеря timestamp при переходе к raw-потоку — одна из причин, по которой vvdecapp удобен для декодирования, но не является универсальным проигрывателем.
Для вывода на экран также требуется преобразование из YUV в подходящее представление GPU. Делать полный YUV→RGB на CPU необязательно: современный рендерер может загрузить плоскости отдельно и выполнить матрицу в шейдере. Но параметры матрицы, диапазона и transfer characteristics должны приходить из надёжных метаданных, иначе технически корректно декодированный кадр будет показан с неверными цветами.
Использование в транскодере
В транскодере VVdeC стоит между демультиплексированием VVC и кодером целевого формата. Самый неэффективный вариант — сохранить весь фильм как raw YUV на диск, затем отдельно запустить кодирование. Правильнее передавать кадры в памяти или pipe, если выбранные программы поддерживают такой режим. Это уменьшает временное место и исключает лишнюю операцию чтения огромного промежуточного файла.
Через FFmpeg внутренний frame pipeline обычно проще, потому что контейнер, декодер, фильтры и энкодер работают в одном процессе. При написании собственного приложения библиотечный API даёт аналогичную возможность: после vvdec_decode кадр можно сразу передавать в преобразователь формата и кодер, соблюдая срок жизни буфера.
Нельзя предполагать, что VVC-вход всегда 4:2:0 только потому, что большинство потребительских файлов такие. Интеграция должна читать фактический формат кадра и заранее понимать, какие варианты принимает выбранный энкодер. Если требуется конвертация 4:2:2/4:4:4 в 4:2:0, это отдельная операция, которая влияет на данные и должна быть осознанной.
Проверка результатов декодирования
Корректность можно проверять на нескольких уровнях. Самый сильный сигнал для conformance — DPH SEI, если он присутствует. Для фиксированного тестового набора подходит MD5 raw YUV. Визуальная проверка полезна для артефактов, но не обнаруживает каждое отличие пикселей и зависит от правильности самого просмотрщика.
При сравнении двух декодеров сначала убедитесь, что они выдают одинаковое представление: одинаковая bit depth, subsampling, crop, порядок кадров и применение film grain. Только после этого имеет смысл считать хэш. Иначе несовпадение показывает различие в настройке вывода, а не ошибку VVC-реконструкции.
Для автоматических тестов хороший набор включает короткие потоки с разными разрешениями и инструментами стандарта, а также повреждённые файлы, на которых проверяется контролируемое завершение. Цель негативного теста не в том, чтобы получить картинку из испорченного потока, а в том, чтобы приложение не выходило за границы памяти и возвращало понятную ошибку.
Работа с повреждёнными VVC-потоками
Повреждение может проявляться как синтаксическая ошибка, отсутствие ссылочного кадра, некорректный parameter set или невозможная длина структуры. Декодер вправе остановить обработку такого потока. Приложение вокруг библиотеки не должно продолжать использовать последний указатель на кадр как будто декодирование прошло успешно; код возврата и наличие нового frame должны проверяться независимо.
Если проблема воспроизводится, полезно сохранить точный raw bitstream, команду и сборку декодера. Контейнер лучше исключить на первом этапе, потому что он добавляет собственный парсер. Затем можно проверить тот же .266 через vvdecapp: если ошибка повторяется, минимальный тест уже не зависит от проигрывателя или FFmpeg-фильтров.
Современные релизы VVdeC содержат дополнительные проверки соответствия bitstream и исправления обработки сломанных потоков. Поэтому для недоверенного контента не стоит держаться за старую сборку только ради бинарной совместимости. Обновление, однако, должно сопровождаться повторным тестом API и зависимого приложения, особенно если между версиями менялся ABI.
Как локализовать ошибку между контейнером и декодером
Первый вопрос — получает ли VVdeC именно VVC byte-stream. Если MP4 открывается одним проигрывателем, но собственный pipeline падает до первого кадра, извлеките raw VVC с vvc_mp4toannexb и запустите vvdecapp. Успешное декодирование raw-файла указывает, что ядро VVdeC и видеоданные в целом работоспособны, а расследование стоит перенести на демультиплексор и формирование access unit.
Если raw-поток тоже не декодируется, следующий шаг — включить более подробный журнал и найти первую ошибку, а не последнюю. Последующие сообщения часто являются каскадом после исходного нарушения синтаксиса. Для сильно повреждённого файла десятки предупреждений в конце не обязательно содержат первопричину.
Если vvdecapp декодирует поток, а libvvdec в вашем коде нет, сравните границы NAL, payloadUsedSize, порядок подачи parameter sets и flush. Эта проверка обычно эффективнее, чем сразу пересобирать библиотеку с другими оптимизациями.
Ситуация: декодирование завершается без выходного файла
Сначала проверьте, указан ли -o и доступен ли каталог для записи. Затем отделите ошибку файловой системы от декодирования: выведите Y4M в stdout и направьте его в другой файл средствами shell. Если данные появляются, проблема связана с путём или правами; если нет — смотрите журнал VVdeC и состояние входного потока.
Нулевой размер выхода возможен и тогда, когда вход не содержит полного кадра. Например, вырезан фрагмент, начинающийся после нужных parameter sets, либо тестовый файл состоит из повреждённой NAL. Расширение .266 не делает такой фрагмент самодостаточным.
В библиотечном коде аналогичная ситуация часто возникает из-за пропущенного flush. Пока приложение подаёт access units, декодер может не вернуть часть кадров; после конца входа их нужно явно извлечь. Если количество пропавших кадров стабильно связано с концом файла, drain — один из первых пунктов проверки.
Ситуация: изображение имеет неверные цвета
VVdeC возвращает YUV-плоскости, а окончательный RGB зависит от преобразования. Неверная матрица YUV→RGB, диапазон full/limited или chroma siting способны изменить изображение без единой ошибки VVC-декодирования. Поэтому сравнивать цвета нужно в цепочке, где метаданные известны и правильно переданы рендереру.
Для raw YUV часто теряются именно метаданные. Если открыть файл в стороннем просмотрщике с настройками по умолчанию, тот может выбрать неподходящую матрицу. Y4M хранит больше описания потока, но тоже не заменяет полноценные цветовые метаданные контейнера. Для точной обработки лучше сохранять соответствующую информацию отдельно или не выходить из медиаконвейера.
Если сравниваются два декодера, выводите их в одинаковый raw pixel format и считайте хэш до RGB-конвертации. Так становится понятно, различаются ли сами реконструированные компоненты или только последующая цветовая обработка.
Ситуация: 10-битный материал выглядит как шум
Для 10-битных кадров документация sample app указывает представление yuv420p10le в терминах FFmpeg. Просмотр такого файла как yuv420p нарушает границы компонентов и создаёт картину, похожую на серьёзное повреждение. Перед поиском бага в декодере нужно проверить bit depth и настройки viewer.
В библиотечном коде аналогичная ошибка возникает, если плоскость 16-битных элементов интерпретируется как массив 8-битных значений. WASM-интерфейс прямо отражает это различие, предоставляя Uint8Array или Uint16Array. Нативное приложение должно быть столь же аккуратным.
Если downstream-энкодер принимает только 8 бит, преобразование 10→8 является отдельной операцией с потерей точности. VVdeC не должен неявно делать её только ради совместимости: лучше использовать явный pixel-format conversion и зафиксировать это в команде или графе обработки.
Ситуация: CPU загружен, а скорость недостаточна
Сначала убедитесь, что используется Release-сборка с подходящими SIMD-оптимизациями. Затем проверьте --threads и --parsedelay. Если поток маленького разрешения или имеет мало параллелизма, добавление потоков может почти не помочь. Если поток тяжёлый, ограничение частотой памяти и взаимодействием потоков может появиться раньше полного использования всех логических CPU.
Не измеряйте производительность во время записи огромного YUV на медленный накопитель, если целью является именно декодер. Используйте FFmpeg null output или другой sink, который не становится узким местом. И наоборот, если реальная задача обязана писать YUV, игнорировать дисковую подсистему в итоговой оценке тоже нельзя.
В виртуальной машине или контейнере число доступных ядер может не соответствовать реальной квоте CPU. Автоматическое -t -1 ориентируется на доступную информацию о системе, поэтому при жёстких лимитах разумнее задать число потоков явно и проверить фактическое время.
Ситуация: больше потоков делает хуже
Параллелизм имеет накладные расходы. Потоки синхронизируются, конкурируют за кеш и память, а parse delay увеличивает рабочий набор. На CPU с большим количеством логических потоков оптимум может находиться ниже максимума. Именно поэтому --threads — управляемый параметр, а не переключатель быстрее.
Для интерактивного воспроизведения важна задержка. Конфигурация, которая повышает суммарный throughput за счёт большего parsedelay, может ухудшить время от поступления данных до готового кадра. Выбор должен соответствовать сценарию: офлайн-пакет и live player оптимизируются по разным метрикам.
Если ухудшение резкое и воспроизводится только на конкретной архитектуре, проверьте, что бинарник действительно собран для неё и не использует неожиданный слой эмуляции. Сравнение с однопоточным режимом и отключением отдельных SIMD путей в диагностической сборке помогает отделить проблему планирования от проблемы оптимизированного кода.
Ситуация: приложение падает на одном файле
Один воспроизводимый вход — хороший материал для диагностики. Скопируйте его, вычислите минимальный фрагмент без изменения структуры, запишите точную команду и сохраните stderr. Не пытайтесь починить файл перед воспроизведением, иначе исчезнет условие, вызвавшее ошибку.
Затем повторите запуск на сборке без внешних оболочек: vvdecapp против raw VVC. Если падение остаётся, стек становится короче. Если исчезает, возвращайте этапы по одному — контейнер, FFmpeg/GStreamer, фильтры, собственный код. Такой двоичный поиск быстрее случайного изменения десяти настроек.
Для разработческой сборки AddressSanitizer и UndefinedBehaviorSanitizer могут дать место выхода за границы или use-after-free. Снимки терминала, использованные в этом материале, происходят именно из реальных задач VVdeC с ASan и поэтому показывают настоящую диагностическую среду, а не нарисованный интерфейс.
Ситуация: FFmpeg не показывает libvvdec
Команда ffmpeg -hide_banner -codecs | grep vvc помогает понять, с какими декодерами собран конкретный FFmpeg. Если libvvdec отсутствует, одной установки vvdecapp недостаточно: FFmpeg должен быть собран с соответствующей библиотекой и обнаружить development-файлы на этапе configure.
Если декодер был доступен при сборке, но исчез после перемещения приложения, проверьте динамические зависимости бинарника. FFmpeg может быть собран с libvvdec, однако загрузчик не находит нужный SONAME в runtime. В этом случае повторная установка только CLI-пакета без vvdec-libs проблему не решит.
Когда FFmpeg содержит и native VVC decoder, и libvvdec, не делайте вывод по слову vvc в списке. Запросите ffmpeg -h decoder=libvvdec и в рабочих командах указывайте -c:v libvvdec. Это исключает неоднозначность при сравнении.
Ситуация: GStreamer не соединяет h266parse и vvdec
Ошибка линковки элементов почти всегда сопровождается caps. Убедитесь, что после parser получается video/x-h266 с byte-stream и подходящим alignment. Если источник отдаёт другой stream-format, нужен корректный parser/демультиплексор, а не принудительное объявление caps без преобразования данных.
Проверьте, что плагин с элементом vvdec действительно установлен в том registry, который использует запускаемый GStreamer. Несколько установок GStreamer на одной машине могут видеть разные каталоги плагинов. gst-inspect-1.0 vvdec должен показать свойства элемента и pad templates именно в текущем окружении.
Если caps согласованы, но pipeline останавливается после старта, включите диагностику GStreamer по категориям parser и decoder. Это полезнее, чем повышать только verbosity внутри VVdeC, потому что проблема может быть в границах buffers до вызова библиотеки.
Ситуация: приложение теряет последние кадры
На уровне libvvdec первой проверкой должен быть flush. Видеодекодер хранит кадры для переупорядочивания, поэтому вход закончился и все кадры выданы — разные события. Drain нужно продолжать до соответствующего EOF, освобождая каждый полученный frame.
На уровне контейнера дополнительно проверьте, что демультиплексор передал последнюю access unit полностью. Обрезка нескольких байтов в конце может выглядеть как неправильный flush, хотя на самом деле декодеру не дали полный NAL. Сравнение с raw-потоком помогает разделить эти случаи.
Если потеря возникает только при остановке пользователем, архитектура должна различать graceful end-of-stream и немедленную отмену. При отмене можно осознанно отбросить буферизированные кадры; при нормальном EOS их нужно вывести. Смешение этих путей приводит к редким иногда не хватает конца без явной ошибки.
Ситуация: процесс потребляет всё больше памяти
В библиотечном приложении первым делом проверьте парность vvdec_frame_unref для каждого полученного кадра. Затем убедитесь, что access unit и decoder освобождаются при переключении файла и при ошибке, а не только в успешной ветке. Долгоживущий сервис быстро выявляет ресурсы, которые короткая CLI-команда маскирует завершением процесса.
В WASM дополнительно требуется .delete() для объектов, созданных через Emscripten bindings. Удаление JavaScript-ссылки само по себе не обязательно освобождает соответствующий C++ объект тогда, когда это необходимо. При последовательном открытии десятков потоков эта разница становится заметна.
Большой parsedelay и большое число рабочих потоков также закономерно увеличивают число одновременно используемых буферов. Это не утечка, если память стабилизируется на определённом уровне и освобождается после закрытия decoder. Поэтому диагностика должна отличать растущий без границы график от постоянного рабочего набора.
Ситуация: pipe зависает
Pipe создаёт обратное давление: если потребитель перестал читать, vvdecapp блокируется на записи stdout. CPU при этом может быть почти свободен, что похоже на deadlock внутри декодера. Проверьте состояние downstream-процесса и не ждёт ли он, в свою очередь, дополнительных метаданных или неправильного формата.
Для Y4M потребитель должен понимать заголовок и frame markers. Если он ожидает raw YUV фиксированного размера, ключ --y4m делает вход несовместимым. И наоборот, программа, ожидающая Y4M, не сможет автоматически угадать размер из чистого raw потока. Формат между двумя концами pipe должен быть согласован явно.
В shell важно также правильно обрабатывать stderr. Если скрипт объединяет stderr с stdout через 2>&1, текстовые сообщения VVdeC попадают прямо между байтами видео. Такой поток будет повреждён независимо от правильности декодера.
Ситуация: нужен только один кадр для анализа
Самый простой способ — декодировать последовательность до интересующего кадра и остановить downstream после получения результата. Однако из-за межкадровых зависимостей нельзя взять произвольный байтовый диапазон вокруг нужного кадра и ожидать независимого декодирования. Декодеру нужны parameter sets и необходимые reference pictures.
Если контейнер имеет индекс и точки random access, удобнее искать до подходящей точки через FFmpeg, затем декодировать до нужного timestamp. Raw .266 сам по себе не предоставляет такой удобной временной навигации. Для исследовательских тестов иногда проще подготовить короткий VVC-клип заранее.
Сохранять весь YUV только ради одного кадра необязательно. Библиотечный API позволяет обработать frame в памяти и закончить сеанс. Это особенно полезно для генерации миниатюр, метрик или анализа заголовков, хотя само масштабирование/PNG-кодирование выполняется уже другими компонентами.
Ситуация: нужно проверить поток в CI
Для CI полезна короткая команда, которая декодирует фиксированный вход и сравнивает результат по DPH или MD5. Выходной файл после проверки можно удалить. Тест должен завершаться ненулевым кодом при ошибке, а скрипт — действительно проверять этот код, а не считать наличия любой строки в логе доказательством успеха.
Набор тестов стоит разделить на позитивные и негативные. Позитивные подтверждают декодирование корректных Main10-потоков и стабильный результат; негативные подают заранее повреждённые файлы и требуют контролируемого отказа без падения процесса. Это защищает не только качество декодирования, но и обработку внешнего недоверенного контента.
Если тест запускается на нескольких архитектурах, фиксируйте фактическую сборку и набор SIMD. Пиксельный результат должен быть одинаков при эквивалентной конфигурации, но производительность сравнивать между разными CI runners бессмысленно без контроля аппаратуры.
Ситуация: нужен воспроизводимый тест производительности
Выберите один и тот же VVC-файл, одинаковую сборку Release, фиксированное число потоков и одинаковый sink. Перед запуском запишите CPU, частоту, ограничения контейнера и команду. Не меняйте одновременно threads, parsedelay и формат вывода: иначе будет невозможно понять источник изменения.
Для декодерного throughput полезно исключить запись YUV, но для end-to-end сценария нужно оставить реальный следующий этап. Эти два теста отвечают на разные вопросы. Первый показывает предел декодирования, второй — фактическую пропускную способность приложения.
Несколько повторов необходимы из-за планировщика и фоновой нагрузки, но --loops не заменяет полноценную методику. Один процесс с повторением может иначе использовать кеш, чем несколько независимых запусков. Результаты следует описывать как данные конкретного окружения, а не как обещанную скорость VVdeC.
Ограничения vvdecapp
У sample app нет графического интерфейса и встроенного медиабраузера. Она ориентирована на raw VVC и технический вывод YUV/Y4M. Это делает инструмент прозрачным для разработчика, но неудобным для пользователя, которому нужно просто открыть MP4 двойным щелчком. Для такого сценария VVdeC используется через медиаплеер или framework.
Raw YUV занимает много места и лишён контейнерных метаданных. Если сохранять длительное видео, свободное пространство может закончиться значительно раньше, чем ожидает пользователь, привыкший к сжатым файлам. Pipe или внутренний frame pipeline почти всегда предпочтительнее, если кадры сразу идут на следующий этап.
Наконец, sample app не является универсальным демультиплексором. MP4, TS и сетевые протоколы требуют внешней инфраструктуры. Это сознательное разделение обязанностей: библиотека фокусируется на H.266/VVC, а контейнеры и доставку берут FFmpeg, GStreamer или код приложения.
Что VVdeC не делает
VVdeC не кодирует видео в VVC. Для кодирования в экосистеме Fraunhofer существует отдельный проект VVenC, и смешивать его параметры с vvdecapp нельзя. Ключи качества, QP, bitrate или preset относятся к энкодеру и не появляются в декодере только потому, что оба инструмента работают с VVC.
Декодер также не является нелинейным видеоредактором, конвертером эффектов или авторинговой программой. Он не предлагает монтажную шкалу, титры, обрезку клипов или меню экспорта. Такие операции выполняются над декодированными кадрами другими программами; VVdeC обеспечивает один фундаментальный этап — восстановление изображения из VVC.
Наконец, наличие WebAssembly-цели не означает, что существует официальный облачный сайт, куда нужно загружать видео. WASM — способ компиляции библиотеки для браузера. Конкретный web player должен предоставить интерфейс, загрузку файлов, рендеринг и политику безопасности сам.
Практический сценарий: проверить VVC после кодирования
После создания .266 кодером не обязательно сразу помещать его в MP4. Сначала полезно декодировать элементарный поток VVdeC в Y4M или YUV и проверить, что реконструкция завершается без ошибок. Если энкодер записал Decoded Picture Hash SEI, включение -dph даёт формальный контроль вместо одной визуальной оценки.
Дальше можно сравнить число ожидаемых кадров, разрешение и bit depth с исходной задачей. Несоответствие количества кадров не всегда связано с VVdeC: ошибочная обрезка потока, неверно сформированный GOP или пропущенный flush на стороне кодера также способны изменить результат. Проверка сразу после кодирования сокращает число этапов, между которыми приходится искать причину.
Только после этого поток имеет смысл мультиплексировать и отдельно проверять контейнер. Такой порядок даёт две контрольные точки: raw VVC подтверждает корректность кодирования/декодирования, а MP4 или TS — корректность упаковки и временных меток. Если второй тест ломается при успешном первом, VVdeC уже не является первым подозреваемым.
Практический сценарий: преобразовать VVC в кадры для анализа
Алгоритму компьютерного зрения или измерителю качества часто нужны несжатые кадры, а не возможность воспроизвести контейнер. Если исходник уже является raw VVC, vvdecapp даёт прямой путь в YUV/Y4M. Для MP4 удобнее FFmpeg с libvvdec, поскольку он сам извлекает дорожку и может сразу преобразовать pixel format, масштабировать или выбирать диапазон кадров.
При массовой обработке избегайте создания одного огромного YUV на каждое видео без необходимости. Подавайте Y4M по pipe в анализатор либо используйте frame API. Это снижает требования к диску и устраняет отдельный этап очистки временных файлов. Если анализатор требует произвольный доступ, тогда сохранённый YUV оправдан, но его метаданные — размер, format, frame rate — нужно хранить рядом.
Для точного измерителя качества любое преобразование пиксельного формата должно быть частью методики. Например, сравнение 10-битного VVC после принудительного 8-битного вывода оценивает уже не только декодер, но и квантование. Лучше сохранять исходную разрядность до момента, когда инструмент действительно требует другое представление.
Практический сценарий: встроить декодер в сервер
Сервис, который принимает пользовательские VVC-файлы, должен отделять парсинг контейнера, декодирование и последующую обработку. Libvvdec можно держать внутри worker-процесса, которому выданы ограничения CPU, памяти и времени. Это особенно разумно для сложного медиаформата: повреждённый файл не должен получать возможность бесконечно удерживать ресурсы всего сервиса.
Декодер лучше создавать на задачу или на строго ограниченную сессию, а не делить один экземпляр между независимыми потоками запросов без документированной модели синхронизации. Access units конкретного видео должны идти в правильном порядке; смешивание данных двух файлов в одном decoder разрушает состояние ссылочных кадров.
Журнал сервера должен сохранять версию библиотеки, код возврата, минимальное описание входа и этап, на котором произошёл отказ. Сами пользовательские видеоданные не обязательно писать в общий log. Для воспроизведения редкого бага допустимо сохранять отдельный тестовый файл в защищённом хранилище с понятной политикой доступа.
Практический сценарий: собственный GUI поверх VVdeC
Отсутствие графического интерфейса в vvdecapp не мешает создать его поверх библиотеки. GUI обычно отвечает за выбор файла и отображение прогресса, демультиплексор — за контейнер, libvvdec — за VVC, а рендерер — за вывод YUV. Важно не пытаться превратить UI-поток в поток декодирования: тяжёлая работа должна выполняться асинхронно, иначе окно будет зависать даже при корректном decoder.
Прогресс для raw VVC удобно оценивать по позиции чтения файла, но это не точное число декодированных кадров. Для контейнера лучше использовать timestamp и длительность. При отмене пользовательской операции UI должен сигнализировать worker, затем дождаться безопасного завершения или контролируемо отбросить оставшиеся кадры, а не уничтожать decoder из другого потока в произвольный момент.
Сообщение об ошибке в GUI следует строить из собственного контекста и текста VVdeC. Пользователю полезно знать, что не прочитан VVC-поток или не поддержан контейнер, а разработчику — получить исходный error code и log. Один текст не удалось открыть файл скрывает слишком много разных причин.
Практический сценарий: регрессионные тесты библиотеки
Набор коротких VVC-потоков позволяет быстро проверить новую сборку на нескольких CPU. Для каждого теста фиксируются ожидаемый успех/ошибка и, где возможно, хэш реконструкции. Такой набор лучше случайных больших фильмов: короткий файл даёт более быструю локализацию и может быть включён в CI без гигантского хранилища.
Отдельно стоит тестировать многопоточность. Один и тот же корректный поток запускается с -t 0, небольшим фиксированным пулом и автоматическим режимом. Результат должен оставаться эквивалентным при одинаковой политике вывода. Различия в времени допустимы, различия в реконструкции требуют расследования.
Негативные образцы должны быть документированы как намеренно повреждённые. Иначе через несколько месяцев кто-то исправит тест, заменив его валидным файлом и тем самым удалив важную проверку обработки ошибок. Для каждой категории полезно хранить короткое описание: обрыв NAL, неверный parameter set, некорректная ссылка и так далее.
Как читать сообщения о parameter sets
VVC использует наборы параметров, которые описывают свойства последовательности и изображения. Если декодер сообщает, что нужная структура отсутствует или недоступна, первым делом проверьте, не начинается ли тестовый файл слишком поздно. Копирование нескольких мегабайт из середины raw-потока не гарантирует наличие начальной конфигурации.
При извлечении из контейнера bitstream-фильтр должен сформировать представление, пригодное для Annex B. Если вручную копировать sample payload из MP4 без учёта структуры NAL, получившийся файл не обязан быть корректным raw VVC. Использование штатного vvc_mp4toannexb снижает риск такого несоответствия.
В собственном демультиплексоре важно передавать parameter sets до тех картинок, которые на них ссылаются. Оптимизация, выкидывающая повторяющиеся NAL только по типу без анализа идентификаторов, способна сделать поток недекодируемым при смене параметров.
Как отличить предупреждение от фатальной ошибки
Не каждое сообщение повышенной verbosity требует остановки. Приложение должно ориентироваться прежде всего на документированный код возврата API и состояние кадра. Текстовый log предназначен человеку и может меняться формулировками, поэтому парсить его регулярным выражением как единственный машинный интерфейс ненадёжно.
В shell-скрипте полезно проверять exit status vvdecapp и существование ожидаемого результата. Если нужен строгий тест реконструкции, добавляется DPH или MD5. Такая комбинация устойчивее, чем правило если в stdout встретилось слово error, считать тест неуспешным.
При создании GUI текст библиотеки можно показать в подробностях, а пользователю дать короткое действие: проверить вход, извлечь VVC из контейнера, убедиться в свободном месте или обновить сборку. Технический log при этом сохраняется без перевода, чтобы разработчик мог сопоставить его с исходным кодом.
Выбор между raw YUV, Y4M и контейнером
| Форма данных | Преимущество | Ограничение | Типичная задача |
|---|---|---|---|
| Raw YUV | Минимальная оболочка, прямые пиксели | Нет размера, fps и большинства метаданных | Лабораторный анализ, хэши, эталонные кадры |
| Y4M | Самоописание основных параметров и удобный pipe | Данные всё ещё несжатые | Связка CLI-инструментов |
| MP4/TS через FFmpeg | Сохраняются дорожки и timing | Нужен демультиплексор/framework | Транскодирование и просмотр |
| Frame API | Нет промежуточной сериализации | Нужно писать код и управлять памятью | Собственные приложения и сервисы |
Правильный выбор снижает количество лишних преобразований. Raw YUV не качественнее кадра, который уже находится в памяти libvvdec; это всего лишь способ сериализовать тот же результат. Y4M не добавляет сжатия, а контейнер не обязан менять пиксели, если используется только демультиплексирование.
Параметры, которые стоит фиксировать в техническом задании
Если VVdeC используется в повторяемом процессе, запишите не только имя программы. Нужны тип входа (raw или контейнер через framework), требуемая bit depth, формат выхода, число потоков или политика auto, parse delay, film grain для FFmpeg-интеграции и способ проверки результата. Это делает задачу воспроизводимой на другой машине.
Для библиотечного проекта добавьте способ связывания, ABI/версию, compiler target и включённые нестабильные API. Для WASM — Emscripten toolchain, поддержку threads/SIMD и лимит памяти. Эти параметры описывают реальную среду намного точнее, чем фраза использовать VVdeC.
Если важна задержка, отдельно укажите её как требование. Настройки, оптимальные для максимального throughput, могут увеличивать очередь разобранных кадров. Без такой метрики разработчик естественно выберет пропускную способность и получит технически быстрый, но неудобный live pipeline.
Лицензия и встраивание
Код VVdeC распространяется по лицензии BSD-3-Clause-Clear. Для разработчика это означает разрешительный режим, однако текст лицензии и copyright notices всё равно должны учитываться при распространении. Конкретный способ включения уведомлений зависит от продукта: исходная поставка, пакет, документация или раздел лицензий приложения.
Лицензия библиотеки не распространяется автоматически на все окружающие компоненты. FFmpeg, GStreamer, кодеки следующего этапа и контейнерные библиотеки имеют собственные лицензии. Если создаётся единый дистрибутив, проверять нужно совокупность зависимостей, а не только файл LICENSE.txt VVdeC.
Само наличие открытого исходного кода также не отменяет вопросов патентов стандарта VVC. Лицензия на программный код и права на использование запатентованных технологий — разные предметы. Для коммерческого продукта юридическую оценку VVC следует проводить отдельно от условий BSD исходников.
Безопасная обработка недоверенного видео
VVC — сложный бинарный формат, поэтому файл из внешнего источника следует считать недоверенным. Запускайте декодирование с минимальными правами, ограничивайте доступ к файловой системе и сетям, а в сервисе задавайте предел времени и памяти. Даже если конкретный поток проходит conformance, контейнер и другие дорожки обрабатываются дополнительным кодом.
Не используйте crash как способ определить поддерживается файл или нет. API должен вернуть контролируемую ошибку, а оболочка — завершить задачу. Если встречен воспроизводимый crash, вход и стек полезно сохранить для разработчиков, но рабочий сервис должен изолировать такой сбой от остальных запросов.
Проверка размеров особенно важна перед выделением payload. Значения из входа нельзя использовать для неограниченного malloc без верхнего предела. То же относится к разрешению кадра: приложение, принимающее пользовательские данные, должно иметь разумные лимиты независимо от теоретических возможностей стандарта.
Диагностическая последовательность при любой проблеме
- Определить, что находится на входе: raw VVC или контейнер.
- Если это контейнер, извлечь VVC штатным demux/bitstream filter и повторить тест.
- Запустить
vvdecappс минимальными параметрами и сохранить stderr. - Проверить YUV/Y4M с правильными размером и bit depth.
- Повторить с
-t 0, если подозревается проблема многопоточности. - Включить более подробную verbosity и найти первое существенное сообщение.
- Если используется API, проверить границы NAL, payloadUsedSize, unref и flush.
- Если используется FFmpeg/GStreamer, проверить, что явно выбран libvvdec/VVdeC и согласованы caps.
- Сократить вход до минимального воспроизводимого файла без разрушения нужных parameter sets.
- Только после воспроизводимого минимального теста менять CMake-флаги или toolchain.
Эта последовательность ценна тем, что постепенно удаляет слои. Она не гарантирует, что причина находится в самом битстриме или в самом декодере, но быстро определяет границу, на которой поведение меняется. В медиастеке такая граница почти всегда информативнее общего сообщения приложения.
Сравнение VVdeC с аналогами
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| VVdeC | Быстрого программного H.266/VVC-декодирования, интеграции через C API, FFmpeg и GStreamer | Нет собственного GUI и контейнерного интерфейса в sample app |
| VTM Decoder | Эталонной проверки инструментов стандарта, исследований и conformance | Референсная реализация не ориентирована на практическую скорость воспроизведения |
| OpenVVC | Открытой альтернативной библиотечной реализации VVC и исследовательской интеграции | Меньшая экосистема готовых интеграций и пакетов |
| FFmpeg native VVC decoder | Работы с VVC внутри обычных FFmpeg-команд без внешнего libvvdec | Меньше специализированных настроек именно VVdeC |
| MainConcept VVC Decoder SDK | Коммерческих продуктов с готовым проприетарным VVC SDK и поддержкой поставщика | Коммерческая проприетарная модель вместо открытой библиотеки |
Практический выбор зависит от задачи. Для разработки, где нужна производительная открытая библиотека с прямым C API и готовыми связками с FFmpeg/GStreamer, VVdeC является естественным кандидатом. VTM разумнее держать как референс для проверки стандарта; native VVC decoder FFmpeg удобен, если дополнительная зависимость нежелательна; MainConcept выбирают, когда важен коммерческий SDK и договорная поддержка. OpenVVC полезен как независимая открытая реализация для сравнения и исследований.
ВидеоМАСТЕР в эту таблицу не включён, потому что его пользовательская задача — конвертирование и обработка видео как готового медиа, а VVdeC и перечисленные аналоги сравниваются именно как реализации H.266/VVC-декодера. Совпадение слова видео не делает их прямыми конкурентами на уровне codec library.
Кому подходит VVdeC
В первую очередь — разработчикам медиаплееров, транскодеров и исследовательских инструментов, которым нужен программный VVC-декодер. Вторая группа — инженеры тестирования: vvdecapp, DPH, MD5 и управляемые потоки позволяют строить воспроизводимые проверки. Третья — специалисты, которым нужно извлечь raw-кадры из H.266 для анализа.
Для пользователя, который хочет просто смотреть обычные видеофайлы через графическое окно, сама sample app неудобна. Рациональнее выбрать плеер или FFmpeg/GStreamer-приложение, где VVdeC уже подключён как backend. Тогда контейнер, звук, субтитры и навигация решаются на соответствующем уровне, а декодер остаётся незаметной частью цепочки.
Если задача связана с кодированием в VVC, требуется другой инструмент. VVdeC может проверить результат энкодера, но не заменяет его. Чёткое разделение encode/decode экономит время уже на этапе поиска документации: параметры VVenC нельзя переносить в vvdecapp.
Краткий рабочий чек-лист
- Для
vvdecappподготовить raw VVC, а не MP4. - Начать с
vvdecapp -b input.266 -o output.yuv. - Для pipe предпочитать Y4M, когда следующему этапу нужны параметры кадра.
- При просмотре raw YUV указать правильное разрешение и bit depth.
- Не повышать
threadsиparsedelayодновременно при диагностике. - Использовать DPH или MD5, если требуется формальная проверка.
- В API освобождать кадры через
vvdec_frame_unrefи выполнять flush. - В FFmpeg указывать
-c:v libvvdec, если нужно тестировать именно VVdeC. - В GStreamer проверять caps до и после
vvdec. - Для недоверенного контента ограничивать ресурсы и права процесса.
При соблюдении этих правил VVdeC становится не консольной программой с множеством непонятных ключей, а хорошо разделённым VVC-декодером: raw-поток можно проверить sample app, контейнер — передать через media framework, а собственный продукт — связать с библиотекой напрямую. Главная практическая сложность заключается не в количестве переключателей, а в корректном разграничении контейнера, VVC-битстрима, реконструированных кадров и последующей обработки.
Частые вопросы по VVdeC
Можно ли передать vvdecapp обычный MP4?
Для sample app правильным входом является raw VVC bitstream. Если видеодорожка лежит в MP4, контейнер нужно сначала разобрать. Самый понятный диагностический путь — извлечь VVC без перекодирования через FFmpeg с vvc_mp4toannexb, а затем передать полученный .266 в vvdecapp. Если промежуточный файл не нужен, используйте FFmpeg с -c:v libvvdec: тогда демультиплексирование и декодирование происходят в одном процессе.
Почему программа создаёт YUV, который не открывается как обычное видео?
Raw YUV не содержит полноценного контейнерного заголовка. Просмотрщику нужно отдельно сообщить ширину, высоту и пиксельный формат. Для 10-битного входа особенно важно выбрать 10-битное little-endian представление. Если хочется получить файл, который проще передавать между CLI-инструментами, выберите Y4M: он остаётся несжатым, но несёт базовую информацию о кадрах и их границах.
Нужно ли всегда ставить максимальное число потоков?
Нет. Автоматический режим удобен как стартовая точка, но число потоков выбирается по реальному сценарию. Для low-latency важна задержка, для пакетной обработки — throughput, а в многозадачной системе нужно оставлять CPU другим процессам. Фиксированное число потоков также полезно для повторяемых тестов. Если максимум логических CPU работает медленнее, это не противоречие: синхронизация и давление на память тоже имеют стоимость.
Можно ли использовать VVdeC только как библиотеку без vvdecapp?
Да. C API и является основной поверхностью интеграции. Приложение создаёт vvdecParams, открывает decoder, подаёт access units, принимает vvdecFrame, возвращает кадры через unref и выполняет flush в конце. Такой режим предпочтителен, когда кадры сразу идут в renderer, фильтр или encoder, потому что нет промежуточной записи YUV и отдельного процесса.
Чем libvvdec в FFmpeg отличается от native VVC decoder?
Это разные реализации декодера, доступные через один framework. Native VVC находится в кодовой базе FFmpeg, а libvvdec вызывает библиотеку Fraunhofer. Если важно тестировать именно VVdeC, декодер нужно выбрать явно. Для пользователя итоговая команда может выглядеть почти одинаково, но производительность, поддерживаемые внутренние опции и путь выполнения различаются.
Что делать, если декодирование работает в vvdecapp, но не в собственном коде?
Сравните подготовку входа. Проверьте границы NAL, payloadUsedSize, порядок parameter sets, размер payload и финальный flush. Затем проверьте срок жизни кадров: после vvdec_frame_unref нельзя продолжать читать плоскости. Если raw-файл один и тот же, а sample app успешно его проходит, случайные изменения SIMD и числа потоков обычно менее полезны, чем аудит собственного glue-кода.
Подходит ли VVdeC для браузера?
Проект можно собрать в WebAssembly и вызывать через JavaScript bindings, поэтому технически VVdeC подходит как ядро web player. Но готовая библиотека не предоставляет сайт, контролы проигрывателя, загрузку контейнера и синхронизацию звука. Всё это строит приложение вокруг WASM. Нужно также учитывать ограничения памяти, поддержку WebAssembly threads/SIMD и корректно удалять Emscripten-объекты.
Как убедиться, что кадры декодированы правильно?
Для conformance используйте Decoded Picture Hash SEI, когда поток его содержит. Для фиксированного набора подойдёт MD5 raw YUV. При сравнении двух декодеров обязательно унифицируйте pixel format, bit depth, crop и политику film grain. Визуальный просмотр полезен как дополнительная проверка, но сам по себе не доказывает побитовое совпадение реконструкции.
Итоговая схема применения
Если на входе raw VVC и нужно быстро проверить декодирование, выбирайте vvdecapp. Если на входе контейнер и требуется трансформация или просмотр, подключайте VVdeC через FFmpeg/GStreamer. Если кадры должны оставаться внутри вашего процесса, используйте C API. Если декодирование нужно в браузерном приложении, собирайте WebAssembly и проектируйте вокруг него собственный pipeline.
На всех четырёх путях сохраняется одна модель: сначала корректно подготовить VVC access units, затем декодировать, обработать каждый возвращённый frame, вывести отложенные кадры при завершении и освободить ресурсы библиотечными функциями. Ошибки чаще всего возникают на границах этой модели — контейнер выдают за raw-поток, забывают flush, неверно читают 10-битный YUV или передают указатель на кадр после unref.
Поэтому VVdeC лучше использовать как специализированный H.266/VVC-компонент, а не пытаться заставить его решать задачи контейнера, монтажа или кодирования. В таком месте он даёт наиболее прозрачный контроль: sample app помогает воспроизвести поток, API — встроить декодер, а интеграции — подключить его к зрелой инфраструктуре обработки медиа.