FFmpegInteropX позволяет встроить декодирование видео, аудио и потоковых источников на базе FFmpeg в приложения Windows, передать результат в MediaPlayer, работать с несколькими дорожками и субтитрами, применять аудио- и видеофильтры, управлять аппаратным декодированием и извлекать отдельные кадры через FrameGrabber.
Главный сценарий FFmpegInteropX — дать приложению источник медиаданных там, где возможностей стандартного набора декодеров Windows недостаточно или нужен единый путь через FFmpeg. Библиотека создаёт объект FFmpegMediaSource из файла, потока или URI, разбирает контейнер, подготавливает аудио-, видео- и субтитровые дорожки, после чего отдаёт MediaPlaybackItem для стандартного MediaPlayer. При этом разработчик остаётся внутри привычной модели Windows Media: управление воспроизведением, транспортные кнопки, выбор дорожек и позиция принадлежат платформенному плееру, а FFmpegInteropX берёт на себя чтение, демультиплексирование и декодирование тех данных, которые должны попасть в эту цепочку.
Это не универсальный интерфейс к любым операциям FFmpeg и не средство экспорта готового ролика. FFmpegInteropX ориентирован на воспроизведение и получение декодированных кадров: библиотека не предоставляет кодирование и транскодирование. Поэтому её имеет смысл выбирать для медиаплеера, просмотра камер и сетевых потоков, приложения с расширенной поддержкой контейнеров и кодеков, анализа кадров или собственного интерфейса поверх MediaPlayer, но не для задачи перекодировать файл из одного формата в другой.
Скачать FFmpegInteropX
- Конвертация видео
- Сжатие файлов
- Просто для новичков
- Нет кодирования
- Субтитры требуют PlaybackItem
- Видеофильтры нагружают CPU
Что именно делает FFmpegInteropX
В рабочей цепочке FFmpegInteropX находится между источником данных и мультимедийным стеком Windows. На вход библиотека получает IRandomAccessStream, IInputStream, путь к файлу в подходящих desktop-сценариях либо строку URI. Дальше FFmpeg открывает контейнер и определяет потоки, библиотека формирует сведения о дорожках и создаёт источник, который можно связать с MediaPlayer. Такой подход важен: FFmpegInteropX не рисует собственное окно плеера и не навязывает набор кнопок. Видимый интерфейс создаёт приложение, а образцы из репозитория показывают, как подключить боковую панель настроек, стандартный MediaPlayerElement и системные transport controls.
Через один FFmpegMediaSource доступны сведения о контейнере и дорожках, главы, встроенная миниатюра, субтитры, задержки отдельных потоков, фильтры и настройки декодеров. Свойства VideoStreams, AudioStreams и SubtitleStreams возвращают перечни доступных дорожек. Для текущих дорожек есть CurrentVideoStream и CurrentAudioStream. Объект FormatInfo содержит название формата, длительность и битрейт, а MetadataTags позволяет получить теги контейнера без отдельного вызова внешней утилиты.
У каждой дорожки есть базовые поля, полезные для собственного меню выбора: имя, язык, имя кодека, disposition-флаги, битрейт, признак дорожки по умолчанию и индекс потока. Для видео дополнительно доступны ширина и высота, частота кадров, aspect ratio, глубина представления, сведения о выбранном движке декодирования и HDR. Для аудио — число каналов, channel layout, частота дискретизации и битность. Эти данные можно показать пользователю до или во время воспроизведения либо использовать для автоматического выбора предпочтительной дорожки.
Почему FFmpegInteropX не следует воспринимать как готовый плеер
В репозитории есть sample-приложения, поэтому при первом знакомстве библиотека может выглядеть как отдельный медиаплеер. На практике sample нужен только как демонстрация API. В его XAML видимая область воспроизведения — обычный MediaPlayerElement с включёнными transport controls, а боковая панель вызывает методы FFmpegInteropX и меняет MediaSourceConfig. Если перенести библиотеку в собственный проект, эти кнопки, переключатели и слайдеры автоматически не появятся: разработчик решает, какие элементы управления нужны продукту.
Такое разделение удобно в приложениях, где дизайн плеера уже существует. Можно оставить собственную панель, горячие клавиши и представление списка файлов, заменив только механизм открытия источника. Можно, наоборот, использовать стандартные элементы Windows и добавить одну-две функции FFmpegInteropX — например поддержку внешних субтитров или декодирование формата, который штатный путь не открывает. В обоих случаях библиотека выступает медиадвижком, а не оболочкой.
Подключение библиотеки к проекту
Для обычного проекта точкой входа служит пакет FFmpegInteropX: он подтягивает библиотеку и подходящую сборку FFmpeg в зависимости от типа проекта. Для UWP существует отдельный верхнеуровневый пакет FFmpegInteropX.UWP. В более низком уровне пакетная схема разделена на библиотеку и сборку FFmpeg: desktop-варианты FFmpegInteropX.Desktop.Lib и FFmpegInteropX.Desktop.FFmpeg, а также FFmpegInteropX.UWP.Lib и FFmpegInteropX.UWP.FFmpeg. Такое разделение имеет практический смысл для тех, кто хочет оставить interop-библиотеку, но подставить собственную сборку FFmpeg.
При выборе пакета ориентируются не на название UI-фреймворка само по себе, а на модель приложения и среду выполнения. Для WinUI 3 и обычных desktop-проектов используется desktop-путь. Для UWP пакет UWP избавляет от неоднозначности с тем, какие двоичные компоненты попадут в приложение. В UWP с современным .NET это особенно важно на Xbox: официальный README прямо указывает использовать UWP-пакет для такой конфигурации. Это тот случай, когда совместимость влияет на конкретное действие — выбор зависимостей перед сборкой.
Если требуется отладка внутри самой FFmpegInteropX, пакет можно заменить ссылкой на проект Source\FFmpegInteropX.vcxproj из исходников. Тогда C++/WinRT-код библиотеки находится в той же solution и доступен для пошаговой отладки. Более глубокая схема — собственная сборка FFmpeg вместо поставляемой зависимости. Она нужна только тогда, когда стандартный набор библиотек и параметров не подходит: например, требуется иной compile-time набор компонентов FFmpeg или особая политика лицензирования в продукте.
В прикладном коде после подключения пространства имён главным типом становится FFmpegMediaSource. Настройки собираются в MediaSourceConfig, где параметры сгруппированы по четырём областям: General, Video, Audio и Subtitles. Дополнительные опции FFmpeg, которые не вынесены в отдельные свойства, передаются через FFmpegOptions. Благодаря такой структуре типичный проект не обязан работать с низкоуровневым C API FFmpeg напрямую.
Открытие локального файла
Базовый путь в UWP-подобном коде начинается с получения IRandomAccessStream. Например, приложение выбирает StorageFile, открывает его на чтение и передаёт поток в FFmpegMediaSource.CreateFromStreamAsync. Вторым аргументом можно передать MediaSourceConfig; если специальных настроек нет, существует перегрузка без конфигурации. После успешного открытия библиотека уже знает формат и набор дорожек, поэтому перед запуском плеера можно прочитать метаданные или скорректировать настройки, которые должны быть применены до создания playback item.
Для входа, который представлен не random-access потоком, существует CreateFromInputStreamAsync. В desktop-сборке добавлен CreateFromFileAsync, позволяющий открыть файл по имени без промежуточного StorageFile. В Win32-вариантах методов создания также есть перегрузки с windowId; они нужны в тех случаях, когда библиотеке необходимо связать операции, зависящие от окна, с правильным окном приложения. Использовать такую перегрузку на всякий случай не требуется — она решает конкретную задачу desktop/WinUI-интеграции.
После создания источника есть два основных варианта. Первый — вызвать CreateMediaPlaybackItem() и назначить результат в источник MediaPlayer. Второй — воспользоваться OpenWithMediaPlayerAsync(mediaPlayer): метод создаёт playback item, назначает его плееру, ждёт события открытия или ошибки и участвует в освобождении ресурсов, когда источник плеера меняется. Для нового кода второй путь удобен тем, что сокращает количество служебных шагов и снижает вероятность забыть часть жизненного цикла.
Если нужен фрагмент, а не весь файл, CreateMediaPlaybackItem имеет перегрузки со временем начала и с ограничением длительности. Это не монтаж: библиотека не создаёт новый файл и не перекодирует диапазон. Она формирует объект воспроизведения с заданным временным окном, что удобно для предпросмотра эпизода, запуска с отметки или UI, в котором один медиафайл представлен несколькими логическими сегментами.
Критически важный жизненный цикл FFmpegMediaSource
Ссылку на FFmpegMediaSource нужно хранить всё время воспроизведения. Нельзя создать объект в локальной переменной метода, передать playback item плееру и считать, что дальше источник больше не нужен. Если сборщик мусора освободит interop-объект, воспроизведение может остановиться с ошибкой. В sample-приложении для этого есть поле класса, а не временная переменная. Для собственного проекта практическое правило простое: объект плеера и соответствующий ему FFmpegMediaSource должны иметь сопоставимый срок жизни.
При смене файла старый источник лучше закрывать или заменять управляемо, а подписки на события — снимать вместе с ним. Если используется OpenWithMediaPlayerAsync, часть очистки при смене MediaPlayer.Source библиотека выполняет сама. Если цепочка собрана вручную через CreateMediaPlaybackItem, контроль за собственными ссылками и событиями полностью остаётся у приложения.
MediaPlaybackItem и MediaStreamSource
FFmpegMediaSource.GetMediaStreamSource() возвращает более низкоуровневый MediaStreamSource. Он полезен для сценариев, где приложение строит нестандартную медиапайплайн-логику, но у него есть важное ограничение FFmpegInteropX: при таком пути субтитры не работают. Для субтитров библиотеке нужен MediaPlaybackItem, потому что именно в нём Windows предоставляет коллекции timed metadata tracks и механизм выбора представления.
Поэтому обычный плеер с субтитрами должен строиться вокруг CreateMediaPlaybackItem или OpenWithMediaPlayerAsync. GetMediaStreamSource имеет смысл выбирать осознанно — например, когда приложение использует MediaPlayer как frame server и копирует кадры в собственную поверхность, либо когда субтитры вообще не входят в требования. Попытка исправлять пропавшие субтитры настройками кодировки при использовании чистого MediaStreamSource не даст результата: проблема находится уровнем выше.
Панель sample-приложения и реальные элементы управления
Официальный C# sample наглядно показывает набор функций, которые авторы считают основными. Вверху панели находятся кнопка Open Local File, поле для URI и переключатель Auto create playback item. Отдельная кнопка ведёт к примеру списка воспроизведения, а Start playback позволяет разделить создание FFmpegMediaSource и запуск. Такая схема полезна при отладке: можно открыть контейнер, посмотреть дорожки и изменить часть параметров до того, как будет создан playback item.
Далее sample выводит переключатели аппаратного декодирования и системных декодеров, параметры IYUV, отдельные флаги для MP3 и AAC, fast seeking и read-ahead buffering. Ниже находятся FrameGrabber, выбор кодировки субтитров, загрузка внешних субтитров, регулировка задержки и длительности cues, синхронизация аудио/видео, строки FFmpeg-фильтров и блок GPU postprocessing. Это не декоративная тестовая форма: названия контролов напрямую соответствуют свойствам конфигурации и методам библиотеки, поэтому sample удобно использовать как карту API.
Основная область sample — MediaPlayerElement со стандартными transport controls. Боковая панель реализована через SplitView и может скрываться. После успешного открытия файла пример закрывает панель, оставляя видео и системные элементы управления. Такое поведение показывает рекомендуемую архитектуру интерфейса: сложные параметры размещаются отдельно, а ежедневные операции — пауза, перемотка, позиция и выбор дорожек — остаются у платформенного плеера.
MediaSourceConfig: как устроены настройки
MediaSourceConfig — не плоский мешок параметров. В текущем API общие настройки находятся в GeneralConfig, видео — в VideoConfig, аудио — в AudioConfig, субтитры — в SubtitlesConfig. Это важно при чтении старых примеров из обсуждений: свойства, которые раньше могли встречаться прямо у конфигурации, в актуальном интерфейсе сгруппированы. При переносе фрагмента кода сначала следует сверяться с IDL и IntelliSense, а не механически копировать старые имена.
Пятое важное поле — FFmpegOptions, набор дополнительных параметров, которые передаются при создании AVFormatContext. Через него задают, например, сетевые таймауты, параметры RTSP, reconnect-опции, probe size и другие параметры форматов/протоколов FFmpeg. Это гибкий механизм, но он не превращает любой параметр командной строки ffmpeg.exe в рабочую опцию: нужно понимать, к какому уровню FFmpeg относится ключ и принимается ли он контекстом открытия входа.
Настройки, влияющие на создание декодера или описание потока, лучше выставлять до CreateFromStreamAsync/CreateFromUriAsync. Некоторые свойства допускают изменение в процессе — например параметры read-ahead buffer, задержки потоков и фильтры имеют соответствующие runtime-методы. Если свойство отвечает за выходной пиксельный формат или выбор decoder mode, рассчитывать на безопасное мгновенное переключение после запуска не стоит: такие решения относятся к построению медиапайплайна.
GeneralConfig
GeneralConfig.AutoExtendDuration разрешает расширять длительность media source, если после первоначального расчёта обнаруживаются данные за пределами ожидаемого конца. SkipErrors задаёт число повреждённых кадров, которые допускается пропустить до остановки декодирования. Это не режим чинить любой битый файл: большой допуск может помочь пережить отдельные ошибки, но не делает некорректный поток гарантированно воспроизводимым.
MaxSupportedPlaybackRate сообщает media stream source максимальную поддерживаемую скорость воспроизведения. Само значение не перестраивает UI transport controls и не добавляет кнопки в произвольный интерфейс. Если приложение показывает собственный список скоростей, его нужно синхронизировать с заданным максимумом. Поддержка значения на уровне MediaStreamSource важна, чтобы системный MediaTransportControl мог корректно разрешить выбор скорости там, где такая функция включена.
FileStreamReadSize ограничивает размер одного чтения для источников IRandomAccessStream. KeepMetadataOnMediaSourceClosed определяет, останутся ли метаданные доступны после закрытия media source; отключение имеет смысл там, где после окончания воспроизведения сведения о файле больше не нужны и предпочтительнее раньше освободить память. AttachmentCacheFolderName задаёт имя каталога во временной области приложения, куда могут помещаться вложения вроде шрифтов субтитров.
FastSeek и быстрый переход по ключевым кадрам
General.FastSeek меняет стратегию позиционирования: FFmpegMediaSource старается перейти к ближайшему ключевому видеокадру. Для корректной работы этой функции нужно связать MediaPlayer.PlaybackSession со свойством FFmpegMediaSource.PlaybackSession после запуска. В sample эта зависимость прямо вынесена в подпись переключателя. Если включить fast seek, но не назначить playback session там, где это требуется, результат не будет соответствовать ожидаемой логике быстрого позиционирования.
FastSeekCleanAudio помогает избежать слышимых артефактов аудио после быстрого перехода; цена — небольшое снижение скорости самого seek. FastSeekSmartStreamSwitching предназначен для ускорения смены потоков в сочетании с fast seek. Полезно разделять эти параметры: первый относится к чистоте звука после перехода, второй — к поведению при переключении дорожек. В интерфейсе пользователю обычно достаточно одного режима быстрая перемотка, а дополнительные флаги можно оставить внутренней настройкой.
Read-ahead buffer для сетевых источников
Read-ahead buffer читает данные вперёд и удерживает ограниченный объём по размеру и длительности. Управляют им ReadAheadBufferEnabled, ReadAheadBufferSize и ReadAheadBufferDuration. В sample для размера и времени есть отдельные слайдеры. Ограничения работают вместе: приложение может, например, не позволять буферу бесконтрольно расти ни по мегабайтам, ни по секундам.
Буфер полезен при нестабильной сети и при сценариях, где кратковременный провал скорости не должен сразу остановить воспроизведение. Если нужная позиция уже попала в буфер, перемещение вперёд может обойтись без нового сетевого открытия. При этом увеличение буфера не является бесплатным улучшением: растёт потребление памяти и задержка между получением данных и их фактическим просмотром может стать менее очевидной для live-сценария. Для камеры с требованием минимальной задержки логика обычно противоположна логике длинного VOD-потока.
Метод StartBuffering() позволяет явно запустить заполнение, если функция включена в конфигурации. Параметры буфера допускают изменение во время воспроизведения, что удобно для адаптивного UI: приложение может уменьшать накопление для live-режима и увеличивать его для обычного интернет-видео. Но FFmpegInteropX не принимает решение за продукт, какая задержка правильная — это зависит от назначения приложения и характера источника.
VideoConfig: выбор декодера и формата видеовыхода
Видео-настройки сосредоточены в MediaSourceConfig.Video. Главный параметр здесь — VideoDecoderMode. Он определяет, кто будет декодировать видеопоток: FFmpeg с D3D11, программный декодер FFmpeg или системный декодер Windows. Это не косметический переключатель. От выбора движка зависят доступные аппаратные пути, поведение HDR, нагрузка на процессор и возможность использовать некоторые программные FFmpeg-фильтры.
Режим Automatic сначала пытается задействовать аппаратное декодирование FFmpeg через D3D11, а если оно недоступно для конкретного сочетания кодека и устройства, переходит к программному декодеру FFmpeg. AutomaticSystemDecoder строит стратегию вокруг системных декодеров Windows и проверки их аппаратных возможностей. ForceSystemDecoder заставляет использовать системный декодер, а ForceFFmpegSoftwareDecoder — программный путь FFmpeg. Отдельного режима принудительно D3D11 любой ценой в публичном перечислении нет: автоматический FFmpeg-путь сам выбирает D3D11, когда он доступен.
Для приложения это означает, что не стоит назначать один режим всем источникам без причины. Например, программный декодер полезен, если нужно гарантированно пропустить кадры через программный FFmpeg video filter. Системный декодер может быть уместен, когда важна интеграция с медиастеком Windows. Автоматический режим удобен как базовое значение, если приложение не располагает дополнительными знаниями о кодеке, графическом адаптере и дальнейшей обработке кадра.
D3D11 и аппаратное декодирование
FFmpegInteropX умеет использовать нативное D3D11-декодирование FFmpeg для H.264, HEVC, AV1, VC-1, VP8, VP9, WMV3 и MPEG-2. Это именно декодирующий путь: наличие аппаратного декодера не превращает библиотеку в видеокодер. Информация о реально выбранном движке доступна через VideoStreamInfo.DecoderEngine, а HardwareDecoderStatus показывает состояние аппаратной поддержки. Важно читать эти свойства после начала воспроизведения там, где результат зависит от фактически построенного пути.
В пользовательском интерфейсе полезнее показывать нейтральную диагностику вроде Системный декодер, FFmpeg software или FFmpeg D3D11, чем обещать GPU включён только на основании переключателя. Аппаратное ускорение зависит от формата, профиля, уровня, установленного кодека, графического драйвера и выбранного режима. Даже при общем наличии видеодекодера конкретный файл может пойти другим путём.
CodecChecker помогает работать с системными расширениями для MPEG-2, VP9 и HEVC. Класс умеет проверить наличие соответствующего расширения, открыть страницу расширения и сообщить через событие CodecRequired, что установка кодека способна улучшить воспроизведение. После изменения набора кодеков можно вызвать RefreshAsync(), чтобы обновить состояние. Это полезно именно для режима, который опирается на системные декодеры; для программного FFmpeg-декодирования такой запрос не является условием открытия файла.
Профиль и уровень системных H.264 и HEVC декодеров
Для системного декодера предусмотрены ограничения SystemDecoderH264MaxProfile, SystemDecoderH264MaxLevel, SystemDecoderHEVCMaxProfile и SystemDecoderHEVCMaxLevel. Их назначение — не изменить параметры потока, а не направлять в системный декодер материал, выходящий за заданные границы. Например, значение уровня H.264 можно ограничить в соответствии с возможностями оборудования, а проверку уровня HEVC при необходимости отключить специальным значением.
Эти параметры особенно важны, когда приложение разворачивается на разном железе. Сам факт наличия аппаратного блока H.264 или HEVC ещё не означает поддержку любого профиля и уровня. Если поток не укладывается в ограничения, разумнее позволить FFmpegInteropX выбрать программный путь, чем получать нестабильное воспроизведение только ради формального использования системного декодера.
Выходные форматы IYUV, NV12, BGRA и 10 бит
VideoOutputAllowIyuv, VideoOutputAllowNv12, VideoOutputAllowBgra8 и VideoOutputAllow10bit задают допустимые типы выходных кадров. Это не список форматов файлов. Речь о представлении декодированного изображения на границе с медиапайплайном. BGRA нужен, в частности, для сценариев с прозрачностью; NV12 типичен для видеопайплайнов Windows; 10-битный выход необходим, когда приложение хочет сохранить соответствующую глубину цвета в поддерживаемом пути.
Если приложению не требуется вручную обрабатывать пиксели, обычно нет смысла включать все варианты только для совместимости. Чем точнее известен последующий потребитель кадра, тем осмысленнее выбор. В самописном видеорендерере требования определяет формат создаваемой поверхности, а в обычном MediaPlayer большую часть согласования выполняет медиастек.
HDR: что предоставляет библиотека
Video.HdrSupport имеет режимы Automatic, Enabled и Disabled. У VideoStreamInfo есть свойства HasHdrMetadata и IsHdrActive. Первое сообщает о наличии HDR-метаданных для потока, второе — о том, активен ли HDR в выбранном декодирующем пути. Для системного декодера эти свойства по контракту не используются как источник HDR-состояния и возвращают ложное значение, поэтому их нельзя трактовать как универсальный анализатор любого системного воспроизведения.
При штатном воспроизведении через MediaPlayer библиотека может сохранить HDR-путь при подходящем сочетании декодера, формата и вывода. Но отдельный вопрос возникает, если разработчик хочет забрать кадр в свой Direct3D-рендерер. Публичный FrameGrabber отдаёт объект VideoFrame с буфером пикселей; API прямого получения декодерной ID3D11Texture2D, номера slice или исходной P010-поверхности в этом классе нет. Поэтому custom renderer, которому принципиален собственный zero-copy HDR10-путь, нельзя проектировать так, будто FrameGrabber уже экспортирует внутреннюю D3D11-поверхность.
Практическое разделение такое: для обычного просмотра используйте медиапайплайн, который умеет принять выход FFmpegMediaSource; для анализа и миниатюр подходит FrameGrabber; для специализированного рендерера, которому требуется доступ к GPU-текстуре конкретного формата и HDR-метаданным на каждом кадре, понадобится отдельная архитектура. Это один из случаев, когда сходство названий аппаратное декодирование и доступ к аппаратно декодированному texture вводит в заблуждение: первое поддерживается, второе не является публичной функцией FrameGrabber.
AudioConfig: декодирование звука и downmix
Аудио-параметры находятся в MediaSourceConfig.Audio. MaxDecoderThreads ограничивает число потоков декодирования, причём нулевое значение означает выбор по количеству логических процессоров. DownmixAudioStreamsToStereo позволяет свести многоканальный звук в стерео, если приложение ориентировано на двухканальный выход или хочет унифицировать формат дальнейшей обработки.
Для MP3 и AAC есть флаги SystemDecoderMP3 и SystemDecoderAAC. Они переключают эти форматы на системный декодер. Документация API отдельно отмечает, что такой путь может быть полезен на отдельных устройствах, например там, где системный декодер даёт аппаратное преимущество, но не рассматривается как обязательная настройка для обычного ПК. Поэтому переносить оба переключателя в пользовательские настройки без объяснения обычно не стоит: это прежде всего инженерный выбор медиапайплайна.
DefaultStreamName задаёт имя для аудиодорожек, когда контейнер не даёт достаточно осмысленной подписи. FFmpegAudioFilters позволяет назначить начальную цепочку фильтров ещё до создания источника. Позднее её можно заменить через методы самого FFmpegMediaSource. Такой двухуровневый подход удобен: общий профиль приложения хранится в конфигурации, а изменения конкретного воспроизведения выполняются на активном источнике.
Несколько видео- и аудиодорожек
FFmpegInteropX предоставляет списки VideoStreams, AudioStreams и SubtitleStreams. Базовый интерфейс IStreamInfo содержит имя, язык, имя кодека, disposition, битрейт, признак дорожки по умолчанию и индекс потока. Дополнительные классы расширяют эти сведения характеристиками своего типа: видео сообщает размеры, кадровую частоту и движок декодирования, аудио — число каналов, раскладку, частоту дискретизации и разрядность, субтитры — внешний ли это поток и является ли он forced.
В контейнере Matroska, MPEG-TS или другом многодорожечном источнике эти сведения позволяют построить нормальное меню выбора: показывать пользователю не Stream 1, а, например, язык, кодек и роль дорожки. StreamDisposition различает default, original, dub, commentary, lyrics, karaoke, forced, hearing impaired, visual impaired, captions и другие назначения. Если приложение предназначено для просмотра фильмов или вещательных потоков, эти флаги полезнее, чем выбор исключительно по порядковому номеру.
На стороне MediaPlaybackItem Windows предоставляет коллекции дорожек и выбор активной дорожки. В sample клавиша V циклически переключает видеодорожки, а с Shift направление меняется. Это демонстрация того, что библиотека не фиксирует первый видеопоток навсегда. Аналогичным образом приложение может построить собственное меню аудио и субтитров, ориентируясь на реальные коллекции.
FramesPerSecondOverride
VideoStreamInfo.FramesPerSecondOverride позволяет переопределить кадровую частоту до вызова CreateMediaPlaybackItem(). Это узкоспециализированный инструмент для источников с некорректно объявленной частотой или для сценария, где разработчик осознанно меняет временную модель видео. Документация предупреждает, что переопределение затрагивает только этот поток и может привести к рассинхронизации аудио и видео.
Поэтому использовать этот параметр как средство ускорить видео неправильно. Для пользовательской скорости воспроизведения есть свойства MediaPlayer и поддерживаемая скорость MediaStreamSource; изменение FPS-паспорта потока решает другую задачу. Если проблема проявляется как дрейф синхронизации, сначала следует проверить временные метки и исходные метаданные, а не маскировать её новым FPS.
Задержка отдельных потоков и синхронизация
Методы SetStreamDelay() и GetStreamDelay() применяются к любому IStreamInfo. Положительная задержка заставляет соответствующие семплы отображаться позже, отрицательная — раньше. Это прямой механизм коррекции синхронизации аудио, видео или субтитров без перепаковки файла. В sample есть общий слайдер от отрицательных к положительным значениям и список потока, к которому применяется поправка.
Для субтитров есть также SetSubtitleDelay(), который устанавливает задержку сразу для всех subtitle streams. Если задача — поправить только одну внешнюю дорожку, удобнее работать с конкретным потоком или SubtitleParser. Если же все субтитры данного видео отстают одинаково, общий вызов проще и меньше зависит от того, сколько дорожек подключено.
Нужно учитывать, что задержка исправляет положение уже существующих временных меток, а не анализирует синхронизацию автоматически. FFmpegInteropX не измеряет губную синхронизацию и не определяет правильный offset. Значение задаёт приложение или пользователь. В медиаплеере удобно хранить поправку отдельно для каждого файла, а в системе видеонаблюдения чаще требуется диагностировать источник, если смещение постоянно растёт.
Информация о контейнере, главах и встроенной обложке
FormatInfo возвращает название, формат, длительность и битрейт источника. Через MetadataTags доступны метаданные в виде отображения ключ — набор значений. Это позволяет показать название, комментарии и другие теги без отдельного прохода сторонним анализатором, если FFmpeg уже прочитал их при открытии.
ChapterInfos содержит главы с названием, временем начала и длительностью. На этой основе можно построить меню глав, навигацию по сценам или шкалу с маркерами. В отличие от вычисленных каждые пять минут отметок, здесь используются реальные данные контейнера. Если глав нет, коллекция не создаёт их автоматически.
Если контейнер содержит вложенную картинку, HasThumbnail сообщает о её наличии, а ExtractThumbnail() возвращает MediaThumbnailData с буфером и расширением. Это полезно для библиотеки мультимедиа: не нужно декодировать случайный видеокадр, если автор файла уже вложил постер. При отсутствии вложенной картинки можно перейти к FrameGrabber и получить кадр из выбранной позиции.
Субтитры: встроенные и внешние дорожки
Поддержка субтитров в FFmpegInteropX связана с MediaPlaybackItem и Windows timed metadata API. Встроенные subtitle streams обнаруживаются вместе с другими потоками контейнера и представлены объектами SubtitleStreamInfo. Для текстовых и графических форматов FFmpeg выполняет разбор, а результат передаётся в подходящую модель Windows. Именно поэтому использование GetMediaStreamSource() лишает приложение обычного механизма субтитров: этот низкоуровневый путь не несёт той же интеграции с MediaPlaybackItem.TimedMetadataTracks.
Если субтитры нужны пользователю, предпочтительный путь — создать MediaPlaybackItem либо вызвать OpenWithMediaPlayerAsync(). Тогда дорожки можно выбирать средствами MediaPlayerElement или собственным интерфейсом. Приложение при этом может учитывать язык, forced-флаг, default disposition и признак внешнего файла, а не показывать безликий список индексов.
AutoSelectForcedSubtitles позволяет автоматически активировать дорожки с forced disposition. Это удобно для фильмов, где отдельная дорожка содержит только перевод иностранных реплик или надписей. Однако forced не означает показывать любые субтитры всегда: приложение должно сохранять различие между forced-треком и обычной полной дорожкой.
Добавление внешнего файла
Метод AddExternalSubtitleAsync() принимает IRandomAccessStream и, при необходимости, имя дорожки. Его можно вызвать после открытия видео, в том числе во время воспроизведения. Результатом является набор SubtitleStreamInfo, поэтому один внешний источник не обязательно сводится к одной строке текста в UI — приложение работает с тем, что реально распознал FFmpeg.
Официальная схема поддерживает форматы внешних субтитров, которые способен разобрать FFmpeg, за исключением двухфайловой DVD-схемы SUB/IDX. Это ограничение важно проговаривать именно как ограничение внешней загрузки пары файлов, а не как утверждение, будто VobSub вообще невозможно встретить во встроенном потоке контейнера. Внутренний subtitle stream и два самостоятельных файла — разные сценарии ввода.
Sample показывает три подхода рядом: Load External Subtitle File (ffmpeg), Read External Subtitle File (ffmpeg) и Load External Subtitle File (winAPI). Первый добавляет дорожку в активный источник, второй демонстрирует отдельный парсер, третий использует Windows TimedTextSource. Такое соседство полезно для разработчика: FFmpegInteropX не заставляет использовать FFmpeg для каждого SRT, если штатного Windows-пути достаточно, но даёт единый FFmpeg-парсер там, где формат выходит за рамки простого timed text.
SubtitleParser отдельно от воспроизведения
SubtitleParser нужен, когда требуется прочитать дорожку без обязательного создания полноценного медиаплеера. ReadSubtitleAsync() принимает поток или URI; в расширенном варианте ему передают имя, MediaSourceConfig и VideoStreamDescriptor. Последний нужен, в частности, чтобы корректно рассчитать геометрию графических субтитров относительно видео.
Полученный SubtitleTrack можно использовать в собственном сценарии, а GetStreamDelay() и SetStreamDelay() дают независимую временную поправку. Такой API полезен редактору таймингов, инструменту проверки субтитров или медиакаталогу, которому нужно проанализировать файл без обычного окна воспроизведения.
Кодировки текста
Subtitles.ExternalSubtitleEncoding управляет кодировкой внешнего текста. Значение null включает автоматическое определение и является штатным вариантом. Если обнаружен ANSI-текст, применяется ExternalSubtitleAnsiEncoding. В распоряжении приложения есть класс CharacterEncoding со списком известных кодировок, кодовой страницей Windows, системной локалью, ASCII и UTF-8.
На практике это решает типичную проблему старых SRT и похожих файлов: байты не являются UTF-8, а пользователь видит кракозябры. Вместо ручного перекодирования до вызова библиотеки можно позволить FFmpegInteropX определить формат или дать пользователю выбор кодовой страницы. В sample для этого предусмотрен список Select ANSI subtitle encoding.
При ошибочном отображении кириллицы сначала следует проверить сам файл и явно выбранную кодировку. Не стоит компенсировать проблему шрифтом: шрифт и character encoding отвечают за разные этапы. Если текст уже неправильно декодирован в Unicode, смена FontFamily не восстановит исходные байты.
Стиль, регион и встроенные шрифты
SubtitleStyle и SubtitleRegion задают базовое оформление и область размещения timed text. OverrideSubtitleStyles определяет, нужно ли применять эти значения даже тогда, когда в самой дорожке присутствует собственное оформление. Для простых SRT это может быть способом унифицировать внешний вид; для ASS/SSA с авторским позиционированием и стилями принудительное переопределение способно уничтожить часть замысла.
UseEmbeddedSubtitleFonts разрешает использовать шрифты, вложенные в медиаконтейнер. Для сложных ASS/SSA это существенно, поскольку авторские стили часто рассчитывают на конкретную гарнитуру. Вложения извлекаются во временное хранилище, а имя каталога регулируется общей настройкой AttachmentCacheFolderName.
Нужно помнить, что окончательный рендер субтитров зависит не только от FFmpegInteropX, но и от Windows media presentation layer. Текстовая дорожка, ImageCue и сложная ASS-анимация проходят разными путями. Поэтому FFmpeg распознал дорожку и все эффекты исходного рендера воспроизвелись пиксель-в-пиксель — не одно и то же утверждение. Для приложения, где стилизованные караоке-субтитры являются ключевой функцией, этот сценарий нужно проектировать отдельно, а не экстраполировать поведение обычного SRT.
Минимальная длительность и устранение наложений
MinimumSubtitleDuration задаёт минимальное время показа cue, AdditionalSubtitleDuration добавляет время к исходной длительности, PreventModifiedSubtitleDurationOverlap старается не допускать пересечения после такой модификации, а ModifiedSubtitleDurationGap может сохранить небольшой промежуток между соседними репликами. Эти настройки относятся к читаемости, а не к общей A/V-синхронизации.
Например, очень короткая реплика может быть технически корректной по таймкодам, но практически нечитаемой. Увеличение минимальной длительности помогает, однако простое растягивание каждого cue способно наложить одну строку на следующую. Поэтому параметры предусмотрены как группа. В sample для минимальной и дополнительной длительности есть отдельные слайдеры и переключатель предотвращения overlap.
Bitmap-субтитры и особенности отображения
Графические дорожки принципиально отличаются от текста: cue содержит изображение, а не строку, которую система свободно перерисовывает любым шрифтом. Масштабирование, размер области видео, альфа-канал и поведение конкретного UI-стека становятся частью результата. В официальном issue-трекере встречаются примеры проблем с ImageCue в отдельных сочетаниях UWP/.NET и WinUI. Их не следует превращать в утверждение bitmap subtitles не работают, но они показывают, почему графические субтитры нужно проверять именно в целевой оболочке приложения.
Если текстовые дорожки отображаются, а PGS/VobSub ведут себя иначе, полезно разделить диагностику на три слоя: распознан ли subtitle stream, создаются ли ImageCue и правильно ли их выводит текущий presentation layer. Переключение кодировки в таком случае не поможет, потому что графическая дорожка не декодируется как текст.
FFmpeg audio filters
Аудиофильтры задаются строкой в синтаксисе FFmpeg. Начальное значение можно положить в Audio.FFmpegAudioFilters, а для уже созданного источника вызвать SetFFmpegAudioFilters(). Есть вариант для всех аудиодорожек и вариант для конкретного AudioStreamInfo. Текущую строку можно получить через GetFFmpegAudioFilters(), а очистить — через соответствующий ClearFFmpegAudioFilters().
Sample по умолчанию демонстрирует цепочку volume и aecho. Это не означает, что библиотека ограничена двумя эффектами: применяется обычный filter graph FFmpeg, насколько используемая сборка поддерживает выбранные фильтры. Можно строить эквалайзер, менять громкость, выполнять ресэмплинг или другую допустимую обработку. Но строка фильтра остаётся техническим интерфейсом: FFmpegInteropX не превращает её автоматически в набор подписанных ползунков.
Если приложение показывает фильтры обычному пользователю, лучше держать шаблоны на своей стороне и генерировать корректную строку из безопасных параметров. Передавать произвольный текст без проверки удобно для инженерной утилиты, но неудобно для медиаплеера: синтаксическая ошибка приведёт к отказу построения графа, а имена и допустимые значения отличаются между фильтрами.
FFmpeg video filters и цена программной обработки
Видео использует симметричные методы SetFFmpegVideoFilters(), GetFFmpegVideoFilters() и ClearFFmpegVideoFilters(). Можно назначить цепочку всем видеопотокам либо конкретному VideoStreamInfo. В sample в поле стоит пример colorchannelmixer, показывающий, что строка применяется во время воспроизведения, когда пользователь подтверждает ввод.
Ключевое ограничение зафиксировано прямо в API: FFmpeg video filters выполняются на CPU и могут ухудшать производительность воспроизведения. Это особенно заметно на 4K, высоком FPS или сложных фильтрах. Если исходный видеопуть находился на GPU, необходимость выполнить программный фильтр может привести к дополнительным преобразованиям и копированию. Поэтому есть аппаратное декодирование не означает, что произвольная FFmpeg-цепочка тоже выполняется аппаратно.
Для deinterlace, цветокоррекции, crop, scale и других операций необходимо выбирать путь исходя из задачи. Если нужен один регулируемый параметр изображения во время просмотра, GPU postprocessing из sample может быть выгоднее программного filter graph. Если же нужен конкретный фильтр FFmpeg, которого нет в GPU-effect, придётся принять его вычислительную стоимость.
Команды фильтрам во время воспроизведения
FFmpegInteropX умеет менять некоторые параметры без пересоздания filter graph. Для этого есть SendFFmpegAudioFilterCommand() и SendFFmpegVideoFilterCommand(). Вызов получает target, command и arguments, а при необходимости ещё конкретный stream. Результат представлен FilterCommandResult с признаками успеха и строкой ответа.
Преимущество команд состоит в непрерывности: некоторые фильтры накапливают внутреннее состояние, поэтому разрушение графа и создание нового может вызвать пропуск пакетов, щелчок звука или проблемы временных меток. Поддерживаемая команда меняет параметр внутри существующего фильтра. Например, можно изменить громкость у volume или gain у эквалайзера, если конкретное свойство разрешает runtime-команду.
Не все параметры подходят для такого изменения. Для фильтра FFmpeg нужно смотреть свойства, помеченные флагом T, и отдельные команды, если они определены самим фильтром. Кроме того, отправлять команды можно только после начала воспроизведения потока. Если приложение вызывает их сразу после создания FFmpegMediaSource, когда фильтр ещё фактически не работает, ожидаемого эффекта не будет.
GPU Video Postprocessing в sample-приложении
В демонстрационном плеере есть отдельный блок GPU Video Postprocessing. Его переключатель включает GPU-обработку для следующего открываемого файла, а VideoAdjustmentsConfiguration связывается со слайдерами Contrast, Brightness, Saturation, Temperature, Tint, Sharpness и SharpnessThreshold. Это отдельный механизм от FFmpeg video filters.
Разделение важно для архитектуры интерфейса. Пользователь может ожидать, что яркость и строка eq=brightness=... — одно и то же, но внутри они проходят разными путями. GPU postprocessing ориентирован на быстрые визуальные корректировки, а FFmpeg filter graph — на универсальность фильтров. Нельзя автоматически переносить ограничения одного механизма на другой.
Если требуется стандартная панель коррекции изображения, разумно использовать GPU-настройки, а поле FFmpeg filter оставить расширенным режимом. Так пользователь не обязан знать синтаксис FFmpeg для обычной регулировки контраста, а специалист сохраняет возможность построить сложную цепочку.
FrameGrabber: извлечение отдельных кадров
FrameGrabber предназначен для получения кадров без обязательного воспроизведения всего ролика. Его можно создать из IRandomAccessStream, IInputStream, URI, а в desktop-варианте — непосредственно из имени файла. После открытия доступны длительность и сведения о текущем видеопотоке. Для уменьшения кадра перед декодированием предусмотрены DecodePixelWidth и DecodePixelHeight.
Основной метод ExtractVideoFrameAsync() принимает позицию. Если exactSeek=false, можно получить ближайший предыдущий ключевой кадр: такой запрос быстрее, но временно менее точен. При exactSeek=true библиотека декодирует от подходящей точки до требуемого кадра. Параметр maxFrameSkip ограничивает, сколько кадров разрешено пройти после ключевого кадра, чтобы дорогой запрос не продолжался бесконтрольно.
Асинхронную операцию точного поиска можно отменить. Это важно для hover-thumbnails или быстро движущегося scrubber: если пользователь уже перевёл указатель на другую позицию, нет смысла тратить ресурсы на кадр, который интерфейс больше не покажет. Хороший UI отменяет старый запрос и оставляет актуальный, вместо того чтобы выстраивать очередь из десятков точных seeks.
Последовательное чтение кадров
ExtractNextVideoFrameAsync() получает следующий кадр последовательно и возвращает null в конце потока. Если нужно проанализировать много соседних кадров, этот путь принципиально эффективнее, чем многократно выполнять точный seek на каждую временную позицию. В задачах компьютерного зрения, генерации контактного листа или покадровой статистики следует выбирать последовательное декодирование, когда порядок обработки позволяет.
Обе группы методов имеют варианты с целевым IBuffer. Это позволяет приложению повторно использовать память вместо постоянного создания новых буферов. Для длительного покадрового анализа такой контроль может заметно уменьшить давление на сборщик мусора и количество выделений, хотя конкретный эффект зависит от остальной части приложения.
VideoFrame и сохранение JPEG, PNG, BMP
Результат FrameGrabber — VideoFrame с PixelData, шириной, высотой, pixel aspect ratio и timestamp. Дополнительно вычисляются display width, display height и display aspect ratio, чтобы приложение могло отличать физический размер пиксельного буфера от отображаемой геометрии.
Методы EncodeAsJpegAsync(), EncodeAsPngAsync() и EncodeAsBmpAsync() записывают кадр в поток. Sample открывает диалог сохранения и выбирает кодер по расширению. На этом удобно строить скриншот текущей позиции, генератор обложек или кеш превью. Кодирование здесь относится к изображениям, полученным из кадра, и не противоречит тому, что FFmpegInteropX не кодирует видеопотоки.
FrameGrabber не является интерфейсом к внутренней GPU-текстуре
Публичный VideoFrame FrameGrabber построен вокруг буфера пикселей. Если custom Direct3D renderer требует именно исходную ID3D11Texture2D аппаратного декодера, NV12/P010 surface и slice texture array, такой объект не выдаётся этим API. Наличие D3D11-декодирования внутри FFmpegMediaSource не делает внутреннюю поверхность автоматически доступной наружу.
Это особенно важно для HDR и низколатентных систем. Получить изображение через FrameGrabber, затем загрузить его в собственную GPU-текстуру — рабочая совместимая схема, но это уже не zero-copy. Если задача ставится как оставить каждый кадр на GPU от декодера до собственного шейдера, архитектуру нужно строить с учётом данного ограничения.
Сетевое воспроизведение через URI
Для сетевого источника используется FFmpegMediaSource.CreateFromUriAsync(). URI передаётся FFmpeg, поэтому доступные контейнеры и протоколы зависят от конкретной сборки и её библиотек. В поставляемом FFmpeg предусмотрены компоненты для HTTPS и RTMPS, а также XML-поддержка, используемая для DASH. Это даёт широкую основу для потокового воспроизведения, но не отменяет специфику каждого протокола.
Все дополнительные параметры открытия помещаются в MediaSourceConfig.FFmpegOptions. Это PropertySet, который передаётся при создании FFmpeg AVFormatContext. Через него задают протокольные опции, параметры probing и другие значения, которые FFmpeg принимает для конкретного demuxer или transport. Библиотека не переименовывает весь набор FFmpeg options в собственные свойства, поэтому редкие сценарии остаются доступны без расширения публичного API.
В sample для URI используются stimeout, timeout, reconnect, reconnect_streamed и reconnect_on_network_error. При этом в комментарии прямо отмечено, что смысл и единицы timeout зависят от протокола. Поэтому переносить одно значение из RTSP-конфигурации в HTTP, UDP и RTMP без проверки не следует.
Автоматическое переподключение
Для интернет-видео особенно полезны reconnect, reconnect_streamed и reconnect_on_network_error. Они позволяют FFmpeg повторить соединение после ряда сетевых ошибок и работать с источниками, для которых повторное открытие потока допустимо. Это не гарантирует бесшовное восстановление любого live-сервера: сервер может требовать новую авторизацию, менять последовательность сегментов или завершать сессию по собственной логике.
Хорошая обработка ошибок сочетает reconnect-опции с событиями MediaPlayer и журналом FFmpeg. Пользовательскому интерфейсу полезно различать краткий повтор соединения и окончательную ошибку, иначе любой сетевой провал выглядит как зависание. При долгом outage приложение может само решить, сколько попыток допустимо и когда показать кнопку повторного подключения.
RTSP и требование низкой задержки
В RTSP-сценариях часто требуется противоположное поведение по сравнению с VOD: минимальный буфер, быстрая выдача свежих кадров и контролируемые таймауты. Через FFmpegOptions можно передавать RTSP-параметры, включая предпочтение TCP, если это нужно конкретной сети. Но универсального набора нулевая задержка нет. Камера, прокси, Wi-Fi, GOP-структура и декодер участвуют в итоговой latency вместе.
Если поток периодически останавливается, первым делом нужно отделить транспорт от декодирования. Проверяют, приходит ли ошибка MediaPlayer, что пишет FFmpeg log, завершается ли источник по таймауту, не собран ли FFmpegMediaSource сборщиком мусора и не исчерпана ли логика reconnect. Уже после этого имеет смысл менять probesize, decoder mode и буферизацию.
Для нескольких одновременных камер нагрузка масштабируется не только количеством пикселей. Каждый источник имеет свои демультиплексирование, очереди, декодирование, timestamps и presentation. Если приложение падает только при сочетании RTSP и MJPEG или нескольких разных кодеков, диагностировать следует минимальную комбинацию потоков, а не делать вывод по одному успешному окну.
Проблемы определения формата
Если FFmpeg не успевает определить контейнер или потоки по небольшому начальному фрагменту, sample рекомендует рассмотреть увеличение probesize, max_probe_packets и analyzeduration. Цена такого решения — более долгий старт и больший объём анализируемых данных. Для live-системы нельзя просто ставить максимальные числа: это способно ухудшить время появления первого кадра.
Обратная ситуация — слишком большой анализ там, где формат известен заранее и важна скорость старта. Тогда параметры можно ограничить осознанно. Главное — не путать probing с network timeout: первый отвечает за анализ содержимого, второй — за ожидание операций транспорта.
DASH, HTTPS и защищённые источники
Сборка FFmpeg, рассчитанная на FFmpegInteropX, включает libxml2 для DASH и OpenSSL для защищённых протоколов вроде HTTPS и RTMPS. Это означает, что библиотека может открыть такие источники через FFmpeg-путь, если URI и серверная сторона совместимы. Приложение при этом получает всё тот же FFmpegMediaSource и не обязано иметь отдельный UI для каждого протокола.
Для адаптивных потоков важна смена внутренних streams. FFmpegInteropX умеет работать с несколькими видео- и аудиопотоками, но конкретная адаптивная логика зависит от источника и поведения FFmpeg. Если продукту необходима тесная интеграция именно с Windows AdaptiveMediaSource, это уже другой медиапуть: нельзя одновременно предполагать, что системная adaptive source автоматически сохраняет все функции FFmpegInteropX.
Для live-потока FFmpegMediaSource.Duration может быть нулевой: в контракте API это нормальное значение для streaming media. Поэтому UI не должен считать ноль ошибкой и делить позицию на duration для построения прогресса. Live-интерфейс лучше переключать в отдельный режим, где длительность не является обязательной конечной величиной.
Скорость воспроизведения
General.MaxSupportedPlaybackRate сообщает MediaStreamSource максимальную поддерживаемую скорость. Это ограничение уровня источника, а не команда воспроизводить сейчас с такой скоростью. Фактическую PlaybackRate меняют через MediaPlayer/PlaybackSession или собственные transport controls. Если max rate не объявлен, системный интерфейс может не предложить ожидаемый диапазон.
Важно отделять этот механизм от FramesPerSecondOverride. PlaybackRate меняет темп всей сессии, тогда как FPS override меняет интерпретацию конкретного видеопотока и способен нарушить синхронизацию. Для кнопок 0,5×, 1×, 1,5× и 2× нужен именно playback rate.
OpenWithMediaPlayerAsync и управление ресурсами
OpenWithMediaPlayerAsync() создаёт MediaPlaybackItem, назначает его в MediaPlayer.Source и ждёт MediaOpened либо MediaFailed. Это удобный сокращённый путь, когда приложение всё равно использует MediaPlayer. Метод также связывает очистку ресурсов со сменой source: если плеер переключится на другой источник или получит null, связанные ресурсы FFmpegMediaSource могут быть освобождены корректнее, чем при ручной разрозненной схеме.
Если приложению нужно предварительно изучить потоки, выбрать дорожки, ограничить диапазон воспроизведения или сформировать playback list, ручной CreateMediaPlaybackItem() остаётся более гибким. Есть перегрузки с startTime и парой startTime + durationLimit, что позволяет представить только нужный фрагмент источника без создания нового видеофайла.
MediaPlaybackList и последовательность роликов
Sample содержит отдельный пункт Media playback list sample. FFmpegInteropX не вводит собственный формат плейлиста: его источник можно оборачивать в MediaPlaybackItem и использовать штатные коллекции Windows. Это полезно для каталога, очереди просмотра или набора коротких клипов, где UI и правила перехода уже строятся вокруг MediaPlayer.
При таком сценарии особенно важно правильно хранить время жизни соответствующих FFmpegMediaSource. Если список содержит playback items, но исходные объекты преждевременно становятся недостижимыми, наличие элементов в очереди само по себе не гарантирует, что нативные ресурсы останутся живы. Архитектура коллекции должна хранить пару источник + playback item либо другой явный owner до конца использования.
Логирование FFmpegInteropX
FFmpegInteropLogging позволяет установить уровень сообщений и провайдер. LogLevel включает Panic, Fatal, Error, Warning, Info, Verbose, Debug и Trace. SetDefaultLogProvider() даёт готовый путь вывода, а SetLogProvider() позволяет подключить собственную реализацию ILogProvider.
В продакшене разумно собирать Error/Warning и, при необходимости, Info, а расширенный Debug/Trace включать для воспроизводимой проблемы. Слишком подробный лог многокамерной системы или высокочастотного потока может стать самостоятельной нагрузкой и затруднить поиск события. Полезно также записывать URI без секретных параметров, выбранный DecoderEngine, формат, размеры и момент MediaFailed.
Собственный log provider особенно удобен, если приложение уже использует централизованную телеметрию. Тогда сообщения FFmpeg можно коррелировать с сетевыми событиями, состоянием MediaPlayer и пользовательским действием. Это эффективнее, чем пытаться диагностировать отказ только по сообщению диалога.
Рабочий сценарий: универсальный медиаплеер
- Создать
MediaSourceConfigи оставить автоматический видеодекодер, если нет причины его фиксировать. - Для сетевых URI добавить осмысленные reconnect и timeout-параметры.
- Открыть поток через
CreateFromStreamAsync()или URI черезCreateFromUriAsync(). - Сохранить FFmpegMediaSource в поле или модели элемента.
- Создать
MediaPlaybackItemи передать его MediaPlayer либо использоватьOpenWithMediaPlayerAsync(). - После старта при FastSeek назначить
PlaybackSessionобратно в FFmpegMediaSource. - Построить меню дорожек по
VideoStreams,AudioStreamsиSubtitleStreams. - В обработчике ошибок выводить понятное состояние, а технические детали отправлять в лог.
Эта схема покрывает большинство обычных плееров. Дополнительные функции — FrameGrabber, фильтры, ручная задержка, read-ahead buffer — лучше добавлять по потребности. Если включить все флаги заранее, становится сложнее понять, какой из них изменил декодирующий путь или задержку.
Рабочий сценарий: камера или RTSP-монитор
Для камеры главным приоритетом обычно становятся устойчивое соединение и малая задержка. Начинают с URI, ограниченного buffer time на стороне MediaPlayer, подходящих RTSP/timeout options и RealTimePlayback у плеера, если приложение использует соответствующий frame-server сценарий. Read-ahead buffer в таком продукте включают только после оценки того, насколько допустима дополнительная задержка.
При обрыве соединения полезно не создавать новый FFmpegMediaSource бесконечно в tight loop. У приложения должен быть управляемый backoff, отмена при закрытии окна и очистка предыдущего источника. Если сервер отвечает, но первый кадр не появляется, проверяют probing и decoder mode; если URI вообще перестал отдавать данные — фильтры и субтитры к проблеме отношения не имеют.
Рабочий сценарий: генерация превью
Для единичной миниатюры создают FrameGrabber, устанавливают желаемый decode size и запрашивают кадр примерно из содержательной части ролика. Для шкалы предпросмотра можно выполнять запросы по нескольким позициям; при активном scrubber старые операции exact seek желательно отменять. Если нужна серия соседних кадров, переходят на ExtractNextVideoFrameAsync().
Полученные VideoFrame можно сразу сохранить в JPEG для компактного кеша, PNG для без потерь или BMP для простого несжатого обмена. Сам кеш, политику инвалидирования и выбор позиции библиотека не организует: это ответственность приложения.
Рабочий сценарий: плеер с внешними субтитрами
После открытия видео создаётся playback item, затем пользователь выбирает subtitle file. Поток передаётся в AddExternalSubtitleAsync(), после чего новая дорожка появляется среди timed metadata tracks. Если текст отображается неправильными символами, проверяется ExternalSubtitleEncoding/ExternalSubtitleAnsiEncoding. Если тайминг смещён одинаково, применяется SetSubtitleDelay().
Для плохо читаемых коротких реплик можно настроить minimum/additional duration и защиту от overlap. Для author-styled ASS/SSA нужно осторожно обращаться с OverrideSubtitleStyles: глобальный стиль полезен не всегда. Графические дорожки проверяются отдельно, поскольку у них нет текстовой кодировки и другая модель рендера.
Рабочий сценарий: фильтры с интерактивными параметрами
Если пользователю нужно менять громкость или эквалайзер во время просмотра, filter graph создаётся один раз, а совместимые параметры меняются командами. Это предпочтительнее постоянного вызова SetFFmpegAudioFilters() на каждое движение слайдера. Для цветокоррекции сначала стоит проверить GPU adjustments; если нужного эффекта там нет, использовать FFmpeg video filter с пониманием CPU-нагрузки.
Интерфейс должен валидировать числовые диапазоны до формирования команды и показывать отказ FilterCommandResult, а не молча принимать любое значение. Так техническая возможность FFmpeg превращается в предсказуемую пользовательскую функцию.
Ошибки и диагностика FFmpegInteropX
При сбое полезно сначала определить, на каком этапе он происходит: открытие входа, определение потоков, создание декодера, выдача семплов, построение MediaPlaybackItem или отображение MediaPlayer. FFmpegInteropX соединяет несколько слоёв, и одинаковый внешний симптом видео не играет может означать совершенно разные причины. Лог FFmpeg, событие MediaFailed, свойства потоков и выбранный DecoderEngine дают больше информации, чем последовательное переключение всех параметров наугад.
Воспроизведение внезапно останавливается без понятной причины
Первое, что нужно проверить, — жив ли объект FFmpegMediaSource. Его необходимо хранить в поле, модели текущего элемента или другом owner на всё время работы. Если сохранить только MediaPlaybackItem, а исходный объект станет недостижимым, сборщик мусора способен освободить его, после чего воспроизведение остановится. Это один из самых характерных для библиотеки жизненных циклов и потому должен быть заложен в архитектуру, а не исправляться отдельным таймером.
Если источник хранится правильно, следующим шагом проверяют MediaPlayer state и ExtendedErrorCode, затем FFmpeg log. Для сети отдельно смотрят reconnect и timeout, для файла — доступность потока и ошибки чтения. В цикле playlist важно, чтобы старый источник освобождался после переключения, а новый, наоборот, удерживался.
Файл открывается, но субтитров нет
Если использован GetMediaStreamSource(), отсутствие обычных subtitle tracks ожидаемо: для субтитров нужен MediaPlaybackItem. Поэтому сначала проверяют, каким методом построен источник для MediaPlayer. Затем смотрят SubtitleStreams и TimedMetadataTracks: если поток найден, но не выбран, проблема относится к presentation mode или пользовательскому выбору; если его нет уже на уровне FFmpegMediaSource, нужно исследовать сам контейнер и формат.
Для внешнего файла проверяют, завершился ли AddExternalSubtitleAsync(), что вернулось в списке дорожек и не используется ли двухфайловый SUB/IDX. Если текст есть, но символы испорчены, диагностируется кодировка. Если текст правильный, но позиция или вид отличаются от ожидаемых, переходят к SubtitleStyle, SubtitleRegion и особенностям конкретного subtitle format.
Кириллица или другой национальный текст отображается неправильно
Для старых ANSI-файлов сначала оставляют автоматическое определение. Если оно ошибается, выбирают конкретный ExternalSubtitleAnsiEncoding. Полезно протестировать исходный файл отдельно: иногда он содержит смешанную или ошибочно объявленную кодировку, которую невозможно восстановить одной системной code page.
Не нужно менять FFmpegOptions sub_charenc одновременно с высокоуровневой настройкой без понимания порядка применения. Чем меньше конкурирующих способов задать кодировку, тем легче понять результат. После исправления encoding уже имеет смысл заниматься font family и стилем.
Графические субтитры видны фрагментами или с артефактами
Bitmap subtitles проходят через ImageCue и зависят от Windows presentation layer. Если тот же контейнер с текстовым SRT работает, это ещё не доказывает исправность пути PGS/VobSub. Проверяют размеры video descriptor, фактический SoftwareBitmap, альфа-канал и поведение целевой WinUI/UWP-оболочки. Проблемы такого рода нельзя лечить сменой ANSI-кодировки.
Если приложение само перехватывает cue и рисует графику, оно принимает на себя больше ответственности: нужно вовремя обновлять изображение, очищать предыдущий cue и учитывать seek. Для сложных ASS/SSA с анимацией простой подход одна статичная bitmap на cue также может не сохранить все визуальные эффекты оригинального renderer.
После seek слышен щелчок или появляется грязный звук
При включённом FastSeek проверьте FastSeekCleanAudio. Эта настройка создана именно для более чистого восстановления звука после перехода и по умолчанию предпочитает корректность максимальной скорости. Также убедитесь, что PlaybackSession назначена источнику так, как требует fast seek.
Если проблема проявляется только с активным audio filter, временно очистите фильтры. Некоторые цепочки имеют буфер и собственную задержку; пересоздание графа на каждом seek может давать эффект, которого нет в чистом декодировании. Для интерактивного изменения параметра используйте filter command, когда фильтр её поддерживает.
Перемотка слишком медленная
Для обычного просмотра включают FastSeek и оценивают переход к ключевым кадрам. Точный поиск неизбежно зависит от расстояния от keyframe до целевой позиции и сложности декодирования. На длинном GOP в 4K программный точный seek может потребовать декодировать заметное число кадров.
В FrameGrabber при генерации миниатюры можно явно использовать exactSeek=false. Для кадрового анализатора, наоборот, если точность обязательна, следует не ждать скорости ключевого кадра. Если нужно получить много последовательных кадров, переход на ExtractNextVideoFrameAsync() обычно логичнее десятков независимых seeks.
FrameGrabber работает слишком медленно при извлечении каждого кадра
Распространённая ошибка — для каждого кадра рассчитывать timestamp и вызывать точный ExtractVideoFrameAsync(). Такой код снова и снова ищет позицию. Если анализ идёт от начала к концу, используйте последовательный метод. Дополнительно задайте DecodePixelWidth/DecodePixelHeight, если алгоритму не нужна исходная 4K-разрешающая способность.
Если после декодирования каждый frame ещё кодируется в PNG, время включает не только FFmpeg decoding, но и компрессию изображения и запись. Для вычислений по пикселям сохранять временный PNG вообще не обязательно — можно работать с PixelData.
Нет аппаратного декодирования
Проверьте VideoStreamInfo.HardwareDecoderStatus, DecoderEngine, формат, профиль и уровень. При ForceFFmpegSoftwareDecoder ожидать D3D11 бессмысленно. При выборе системного декодера учитывайте заданные max profile/level и наличие нужного codec extension. CodecChecker помогает обновить состояние после установки расширения.
Не следует считать программное декодирование ошибкой само по себе. Если аппаратный путь недоступен или не подходит потоку, software decoder является штатным fallback. Для фильтров на CPU он иногда даже упрощает pipeline, потому что не требуется сначала выводить кадр из видеопамяти.
Аппаратное декодирование есть, а CPU всё равно загружен
D3D11 decoder ускоряет конкретную стадию декодирования. Демультиплексирование, аудио, subtitle parsing, программные filters, копирование кадров и пользовательский renderer могут оставаться на CPU. Особенно дорого обходится цепочка, где аппаратно декодированный кадр выгружается в обычную память ради фильтра, а затем снова загружается на GPU.
Диагностику проводят отключением дополнительных стадий по одной: сначала чистое воспроизведение, затем subtitles, filters, frame-server copy и постобработку. Так становится видно, какой компонент действительно создаёт нагрузку.
Видео есть, но FFmpeg video filter не даёт ожидаемого результата
Проверьте, что строка фильтра синтаксически корректна и относится к video, а не audio filter graph. Если метод вызван для конкретного stream, убедитесь, что это активный VideoStreamInfo. Некоторые фильтры требуют определённого pixel format или изменяют геометрию, что может потребовать совместимого последующего output.
Для изменения параметра во время работы проверьте, поддерживает ли свойство runtime command. Вызов SendFFmpegVideoFilterCommand() до фактического старта потока не является заменой первоначальной настройки графа.
FilterCommandResult сообщает ошибку
Убедитесь, что target совпадает с именем фильтра в графе, command действительно существует, а arguments имеют правильный формат. Не каждый параметр, который можно указать при создании filter, разрешено менять после запуска. Если dynamic command не поддерживается, нужно изменить filter string и пересоздать цепочку с учётом возможного разрыва.
Поток по URI долго открывается
Разделите сетевое ожидание и анализ формата. Слишком большие timeout приводят к долгому ожиданию неотвечающего узла. Слишком большие analyzeduration или probesize увеличивают время определения потока. Но уменьшение этих величин ниже необходимого уровня может привести к тому, что FFmpeg вообще не обнаружит дорожку.
Для известного протокола тестируют параметры постепенно и фиксируют время каждого этапа в журнале. Не стоит одновременно менять транспорт, decoder mode, probing и read-ahead buffer: после этого невозможно понять, что именно улучшило старт.
Live-поток показывает длительность 0
Для streaming media это допустимое значение FFmpegMediaSource.Duration. Не нужно превращать его в ошибку не удалось определить длину. Если источник является HLS/DASH live и его окно со временем меняется, пользовательский интерфейс должен ориентироваться на доступную timeline конкретного MediaPlayer, а не ожидать статичную продолжительность файла.
Аудио и видео рассинхронизированы
Если смещение постоянно, можно применить SetStreamDelay() к нужной дорожке. Если оно растёт с течением времени, простая постоянная задержка проблему не решит: тогда надо проверять timestamps, фактический FPS, нестандартный FramesPerSecondOverride, источник и фильтры. Нарастающий drift обычно указывает на различие временных шкал или обработку, а не на фиксированный offset.
Ошибка появляется только после применения FramesPerSecondOverride
Верните исходный FPS и проверьте синхронизацию. Сам API предупреждает, что override затрагивает только video stream. Если аудио продолжает жить в исходной временной шкале, расхождение является ожидаемым риском. Использовать свойство стоит только при известной ошибке метаданных или в специально рассчитанном сценарии.
Проблема появляется при закрытии и повторном открытии многих файлов
Проверьте, освобождаются ли старые MediaPlayer source, FFmpegMediaSource, FrameGrabber и VideoFrame. Все эти объекты имеют нативные ресурсы или реализуют closable-семантику. Временные внешние subtitle streams, вложенные шрифты и собственные кеши приложения также могут удерживать память. KeepMetadataOnMediaSourceClosed=false позволяет очищать больше данных, если метаданные после окончания воспроизведения не нужны.
Если рост памяти связан только с конкретным файлом, важен минимальный воспроизводимый пример: контейнер, кодек, subtitle attachments, decoder engine. Общий вывод FFmpegInteropX течёт на любом видео без такого разделения не помогает найти слой, удерживающий память.
Чёрный экран в собственном frame-server renderer
Убедитесь, что MediaPlayer запущен, включён frame server, событие кадра приходит и destination surface имеет совместимый размер/формат. Если вы используете GetMediaStreamSource(), помните о его ограничениях по subtitle layer. Для custom renderer отдельно проверяется копирование кадра в поверхность; наличие живого FFmpegMediaSource лишь гарантирует существование источника, но не корректность пользовательского Direct3D-кода.
Что FFmpegInteropX не делает
| Задача | Что происходит на практике |
|---|---|
| Кодирование видео | Публичная библиотека ориентирована на декодирование; видеокодера для создания нового файла здесь нет. |
| Транскодирование | Нельзя передать MP4 и получить WebM, H.264 или HEVC-файл средствами FFmpegInteropX. |
| Монтаж таймлайна | Нет редакторского timeline, переходов, многослойного монтажа и экспорта проекта. |
| Автоматическая загрузка кодеков | CodecChecker может обнаружить системную потребность и открыть страницу расширения, но выбор и установка остаются отдельным действием. |
| Гарантированный zero-copy FrameGrabber | Публичный FrameGrabber выдаёт pixel buffer, а не внутреннюю D3D11 texture декодера. |
| Универсальный renderer ASS/SSA | Сложные стили и анимации зависят от subtitle path и presentation layer; это не отдельный Aegisub-совместимый движок. |
| Готовый каталог медиатеки | Метаданные и thumbnail доступны, но база файлов, поиск и UI каталога создаются приложением. |
Эти ограничения не являются недостатком выбора архитектуры, если задача совпадает с назначением библиотеки. FFmpegInteropX ценен именно как мост от FFmpeg-декодирования к Windows Media APIs. Если продукту нужен экспорт, монтаж или полный кроссплатформенный player framework, рациональнее выбрать инструмент другого уровня, чем пытаться достроить отсутствующий класс функций вокруг MediaSource.
Сравнение FFmpegInteropX с аналогами
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| FFmpegInteropX | Приложений Windows, которым нужен FFmpeg-декодер как источник для MediaPlayer, дорожки, субтитры, FrameGrabber и D3D11 | Нет кодирования и транскодирования |
| FFmpegInterop | Базового подключения FFmpeg к Windows MediaStreamSource в проектах, рассчитанных на исходную модель Microsoft | Меньше высокоуровневых возможностей, чем у FFmpegInteropX |
| LibVLCSharp | Кроссплатформенного .NET-плеера с широким мультимедийным API, воспроизведением и streaming-возможностями LibVLC | Использует стек LibVLC вместо нативной модели MediaPlayer Windows |
| FlyleafLib | Готового .NET media player framework для WinUI 3, WPF и WinForms с FFmpeg и DirectX | Более крупная player-архитектура, если нужен только источник MediaPlayer |
| FFME.Windows | WPF-приложений, которым нужен FFmpeg-бэкенд в форме расширенного MediaElement | Ориентирован на WPF, а не на UWP/MediaPlaybackItem |
| FFmpeg.AutoGen | Низкоуровневого доступа из .NET почти ко всему API FFmpeg и построения собственного медиапайплайна | Разработчик сам управляет FFmpeg API, памятью и интеграцией вывода |
Практический выбор определяется уровнем абстракции. Если уже используется MediaPlayer/MediaPlayerElement и требуется расширить набор декодируемых форматов, сохранить timed metadata, получить главы и добавить FrameGrabber, FFmpegInteropX попадает в задачу точнее большинства альтернатив. Он отдаёт Windows готовый media source, а не заставляет приложение самостоятельно собирать весь renderer.
LibVLCSharp разумнее, когда приложение строится вокруг LibVLC, должно работать на нескольких операционных системах или нуждается в его более широком playback/streaming API. FlyleafLib удобен, когда нужен готовый Windows player framework с собственным UI-ориентированным уровнем. FFME.Windows логичен для WPF, где требуется знакомая модель MediaElement. FFmpeg.AutoGen подходит разработчику, который сознательно хочет полный низкоуровневый контроль и готов сам решать lifetime, threading, timestamps, textures и presentation.
Оригинальный FFmpegInterop близок концептуально, однако FFmpegInteropX добавляет более развитые дорожки, subtitle API, FrameGrabber, D3D11-путь, фильтры и конфигурацию. При этом их нельзя смешивать в коде по названию классов: namespaces, API и набор возможностей различаются, поэтому пример для одного проекта не следует механически переносить в другой.
Когда FFmpegInteropX подходит лучше всего
Библиотека особенно уместна, если приложение уже использует Windows MediaPlayer, но штатных декодеров недостаточно или требуется единое поведение для более широкого набора контейнеров и кодеков. Она также подходит, когда нужны внешние FFmpeg-субтитры, программные filters, извлечение кадров, главы, metadata tags и выбор нескольких streams без создания собственного полного playback engine.
Второй сильный сценарий — специализированный Windows-плеер, где разработчик хочет оставить transport, playlist и presentation на стороне Windows, но контролировать decoding через FFmpeg. В этом случае FFmpegInteropX не дублирует MediaPlayer, а расширяет его источники.
Для инструмента, который должен перекодировать библиотеку файлов, сжимать ролики, менять контейнеры и экспортировать результат, выбор будет неудачным: этих функций в FFmpegInteropX нет. Для кроссплатформенного продукта также нужно оценивать альтернативы, потому что архитектура библиотеки тесно связана с Windows media APIs.
Практическая схема настройки без лишних параметров
Начинайте с минимальной конфигурации. Откройте один типичный файл в автоматическом decoder mode и без filters. Убедитесь, что дорожки, subtitles и seek работают. Затем добавьте только те параметры, которые решают конкретную задачу: reconnect для сети, read-ahead для нестабильного VOD, fast seek для длинных файлов, subtitle encoding для старых текстовых файлов.
Аппаратный decoder лучше не фиксировать без необходимости: автоматический путь уже умеет выбирать D3D11 и fallback. Системный decoder имеет смысл включать осознанно, особенно если приложение использует CodecChecker. Программный FFmpeg decoder стоит принудительно выбирать там, где важна предсказуемая CPU-обработка или отладка filter graph.
Для пользовательских настроек отделяйте понятные функции от инженерных. Задержка субтитров, стерео, яркость и точный кадр можно дать обычному пользователю. probesize, codec profile limits, FileStreamReadSize и произвольная FFmpeg filter string — скорее расширенные параметры или внутренний профиль.
Итог по возможностям FFmpegInteropX
FFmpegInteropX решает конкретную инженерную задачу: превращает возможности FFmpeg по чтению и декодированию мультимедиа в источник, совместимый с Windows MediaPlayer, и добавляет вокруг него управление потоками, субтитрами, фильтрами, аппаратным декодированием, HDR-состоянием, метаданными и извлечением кадров. Сильная сторона библиотеки не в отдельном пользовательском окне, а в том, что приложение получает FFmpeg-декодер без отказа от MediaPlaybackItem, playback session и других механизмов Windows.
Для устойчивой интеграции важны несколько правил: хранить FFmpegMediaSource всё время воспроизведения, использовать MediaPlaybackItem, если нужны субтитры, назначать PlaybackSession для FastSeek, не считать FFmpeg video filters GPU-эффектами и не ждать от FrameGrabber прямой D3D11 texture. При сетевой работе параметры probing, timeout, reconnect и buffering настраивают отдельно, потому что они решают разные проблемы.
Если эти границы соответствуют продукту, библиотека позволяет построить плеер, монитор потоков, каталог с превью или Windows-медиаинтерфейс с заметно более широким декодированием, чем даёт один системный набор кодеков. Если же основная задача — создание новых видеофайлов и транскодирование, следует выбирать FFmpeg API или приложение, предназначенное именно для кодирования.
Короткие ответы по настройке
Нужно ли всегда создавать MediaPlaybackItem?
Нет, FFmpegMediaSource позволяет получить и MediaStreamSource. Но если в продукте должны работать встроенные и внешние субтитры через timed metadata, используйте MediaPlaybackItem. Низкоуровневый MediaStreamSource подходит там, где приложение сознательно строит собственную схему и готово отказаться от этой части интеграции.
Можно ли открыть файл неизвестного расширения?
При работе со StorageFile sample добавляет в picker маску всех типов, а фактическое содержимое анализирует FFmpeg. Расширение само по себе не гарантирует поддержку и не является единственным критерием: важны контейнер, потоки и возможности сборки FFmpeg. Если содержимое распознаётся плохо, настраивают probing, а не переименовывают файл как способ добавить кодек.
Как узнать, какой декодер реально используется?
Читайте VideoStreamInfo.DecoderEngine после запуска воспроизведения. Возможны системный decoder, FFmpeg software decoder и FFmpeg D3D11 hardware decoder. Не определяйте это только по значению VideoDecoderMode: автоматический режим описывает стратегию выбора, а не гарантированный конечный движок для каждого файла.
Можно ли применить разные фильтры к двум аудиодорожкам?
Да. Перегрузки SetFFmpegAudioFilters() принимают конкретный AudioStreamInfo, а методы получения и очистки имеют такой же вариант. Это позволяет, например, корректировать одну дорожку, не заставляя вторую проходить через тот же filter graph.
Можно ли менять фильтр во время просмотра?
Полную строку можно заменить, но это пересоздаёт фильтрацию и может нарушить непрерывность. Если требуется изменить только параметр и соответствующий FFmpeg filter поддерживает runtime command, предпочтительнее SendFFmpegAudioFilterCommand() или SendFFmpegVideoFilterCommand(). Команда отправляется после старта потока.
Можно ли получить кадр без показа видео?
Да. FrameGrabber создаётся отдельно и не требует открытого окна MediaPlayerElement. Он подходит для server-side логики внутри Windows-приложения, генерации превью и анализа кадров. Для большого последовательного набора используйте ExtractNextVideoFrameAsync(), а не точный seek к каждому timestamp.
Можно ли ограничить разрешение кадра FrameGrabber?
Да, через DecodePixelWidth и DecodePixelHeight. Это полезно, когда исходник 4K, а миниатюра нужна 320 или 640 пикселей по ширине. Уменьшение до подходящего размера раньше в цепочке избавляет последующую обработку от лишнего объёма пикселей.
Как корректно сделать задержку только одной аудиодорожки?
Получите нужный AudioStreamInfo из AudioStreams и передайте его в SetStreamDelay(). Положительный TimeSpan сдвинет звук позже, отрицательный — раньше. Не меняйте subtitle delay, если проблема относится только к звуку.
Что использовать для простых изменений яркости и контраста?
В sample есть GPU video postprocessing с отдельными параметрами яркости, контраста, насыщенности, температуры, tint и резкости. Для таких интерактивных корректировок он логичнее универсального программного FFmpeg filter. FFmpeg video filters оставляют для операций, которых нет в GPU-наборе или которые должны быть выражены именно filter graph.
Что делать, если требуется экспорт обработанного видео?
FFmpegInteropX не предоставляет видеокодирование и транскодирование, поэтому playback filters сами по себе не образуют экспортный конвейер. Для создания нового файла потребуется отдельный encoder pipeline: непосредственная работа с FFmpeg, другая библиотека или готовый видеоконвертер. Не следует строить функцию экспорта на предположении, что MediaPlayer сможет сохранить декодированный источник обратно в контейнер.
Какие настройки важнее всего сохранить между запусками?
Для пользовательского плеера обычно полезны выбранные язык/дорожка, subtitle delay, режим forced subtitles и, возможно, значения визуальной коррекции. Decoder mode, probing и timeout лучше хранить как технический профиль только тогда, когда продукт действительно позволяет их менять. Иначе одна неудачная ручная настройка может сломать открытие всех следующих файлов, хотя автоматические значения работали.