Вопрос или проблема
По сути, я хочу принудительно изменить частоту кадров файла, который не закодирован с использованием H.264. С H.264 я могу сделать следующее:
# Предположим, я хочу взять файл с частотой 30FPS и сделать его 60FPS, фактически удвоив скорость.
# Извлеките H264 поток из исходного файла в сырой H264 поток
ffmpeg -i source.mp4 -map 0:v -vcodec copy -bsf:v h264_mp4toannexb source-video.h264
# Принудительно заставить FFMPEG регенерировать значения PTS для потока H264
ffmpeg -fflags +genpts -r 60 -i source-video.h264 -vcodec copy output.mp4
Эта техника задокументирована самим FFmpeg и уже объяснялась здесь на Super User, а также является тем, что вы найдете, если поищете это в Google.
Однако, предположим, мой исходный поток не в формате H.264 или H.265, и поэтому я не могу использовать эту последовательность команд. Например, предположим, у меня есть поток FFV1 (из архивного источника) или поток MPEG2 (с DVD) или даже поток MPEG1 (с VideoCD и т.д.). Предположим, я хочу сохранить видео в его оригинальном кодеке, таким образом, кодирование в H.264, выполнение преобразования частоты кадров, а затем повторное преобразование в оригинальный формат – это два лишних поколения сжатия с потерями, которые определенно не соответствуют “без перекодирования”.
Я пробовал использовать vsync-drop и setpts на других кодеках (например, FFV1), и это, похоже, не работает:
# source - это FFV1 на 30fps
# это создает выходной файл длиной 1,5 секунды для 60 секунд видео
# выходной файл содержит только несколько первых кадров источника, все еще на 30fps
# ffprobe также показывает 30fps для выходного файла
ffmpeg -vsync drop -fflags +genpts -i ffv1video.mkv -map 0:v -c copy ffv1fastvideo.mkv
# удаление -vsync drop приводит к идентичному выходному файлу
ffmpeg -fflags +genpts -i ffv1video.mkv -map 0:v -c copy -r 60 ffv1fastvideo.mkv
# добавление частоты кадров ко входу ничего не меняет
ffmpeg -r 60 -vsync drop -fflags +genpts -i ffv1video.mkv -map 0:v -c copy -r 60 ffv1fastvideo.mkv
# вы не можете использовать -vf setpts=..., если используете -c copy
Все вышеперечисленное применимо и в случае использования таких кодеков, как mpeg2 или иные. По сути, я просто хочу регенерировать PTS для каждого кадра без перекодирования всего потока.
Кажется, что это то, что FFmpeg должен делать для потоков других, кроме h.264 и h.265 – можно ли это сделать?
Вы можете это сделать с использованием битового фильтра setts
. Это работает потому, что вы не можете использовать обычные фильтры с копированием кодека (-c:v copy
), так как они работают с декодированным видеопотоком, но вы можете использовать битовые фильтры, которые работают с кодированным потоком.
У меня есть более подробный пост об этом здесь: Изменение частоты кадров в ffmpeg без перекодирования
.
Ответ или решение
Конечно, решение задачи изменения частоты кадров без повторного кодирования в FFmpeg для кодеков, отличных от H.264 и H.265, вызывает интерес. Давайте подробно рассмотрим, как это можно сделать с использованием инструментов FFmpeg и теории работы с временными метками (PTS) и фильтрами битового потока. Применяя подход TEA (Theory, Example, Application), разберем данную тему максимально подробно.
Теория
Когда мы говорим о необходимости изменения частоты кадров без повторного кодирования, речь идет о том, чтобы изменить порядок декодирования и показа кадров видеофайла, сохраняя его исходное качество. В FFmpeg для таких операций используется изменение временных меток PTS (Presentation Timestamp). Временные метки указывают, когда каждый кадр должен быть показан, и манипуляции с ними позволяют изменять воспринимаемую скорость видео.
Ключевой теоретический аспект здесь заключается в различии между фильтрами видеопотока и фильтрами битового потока. Видеофильтры, такие как setpts
, требуют декодирования видеопотока, так как они работают на уровне пиксельных данных, что требует повторного кодирования. В свою очередь, фильтры битового потока, как bitstream filter
, применяются напрямую к кодированным данным и не требуют их декодирования. Это позволяет сохранять видеоданные в их изначальном формате и избежать потерь качества при повторном кодировании.
Пример
Для иллюстрации рассмотрим пример использования фильтра битового потока setts
для изменения временных меток. Предположим, что у нас есть видео в формате FFV1, и мы хотим изменить частоту кадров с 30 FPS до 60 FPS, ускоряя воспроизведение без рекодирования.
Вот как это можно сделать:
ffmpeg -i ffv1video.mkv -c:v copy -bsf:v setts=ts=N/(60*TB) ffv1fastvideo.mkv
В этом примере:
-c:v copy
обозначает копирование видеопотока без повторного кодирования.-bsf:v setts=ts=N/(60*TB)
изменяет временные метки кадров, заставляя видео проигрываться быстрее.
Применение
Практическое применение данного подхода заключается в возможности работать с различными форматами видео, такими как FFV1, MPEG2, MPEG1 и другими, без ущерба для их качества. Это особенно полезно в архивации, реставрации и трансформации мультимедийных данных, где каждая потеря качества может быть критичной.
Если мы используем setts
, мы можем воздействовать на любую потоковую структуру, где поддерживаются временные метки. Применение этого фильтра позволяет создавать адаптивные решения для манипуляции временной структурой без качества изначальных медиа данных.
Заключение
Изменение частоты кадров видео без повторного кодирования с использованием FFmpeg требует понимания и правильного использования фильтров битового потока. Подход с использованием фильтра setts
успешно решает эту задачу для кодеков, отличных от H.264 и H.265, минимизируя потери данных и сохраняя исходный формат. Это решение является идеальным вариантом для работы с видеоархивами и любыми медиа, где важно сохранить исходное качество и структуру потока. Такие знания значительно расширяют возможности работы с видеоданными, предлагая более глубокий контроль и гибкость в мультимедийной обработке.