Изменить частоту кадров в ffmpeg без перекодирования

Изменить частоту кадров в ffmpeg без перекодирования

У меня есть видео mkv (h264) с частотой 23,976 кадров в секунду (24000/1001), но я хочу преобразовать его в 25 кадров в секунду без перекодирования и потери качества. Я знаю, что mkvmerge может это сделать (с опцией --default-duration '0:25fps'), но я бы хотел сделать это напрямую из ffmpeg, если это возможно. Согласно документации, это должно работать:

ffmpeg -i input.mkv -r 25 -vcodec copy output.mkv

но когда я его запускаю, я получаю только тот же видео fps. Какой правильный метод сделать это (если существует) в ffmpeg?

решение1

Вот метод, использующий текущие версии FFmpeg. Он основан на том, что concat demuxer не масштабирует PTS входов после первого файла, а просто применяет фиксированное смещение. Допустим, у вас есть поток 30 кадров в секунду с временной шкалой 15360(типично для выходных данных FFmpeg). Это означает, что у кадра 0есть PTS 0, а у кадра 30есть PTS 15360. Это стало бы потоком 45 кадров в секунду, если бы мы могли изменить временную шкалу на , 23040не влияя на значения PTS.

По сути, именно это и делает представленный ниже метод.

1. Определите исходные свойства.

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s,
       30 fps, 30 tbr, 15360 tbn (default)

Необходимо отметить свойства источника, особенно разрешение и tbn.


. (Необязательно) Измените шкалу времени на удобную, чтобы упростить расчеты.

ffmpeg -i in.mp4 -c copy -an -video_track_timescale 30 in-v30.mp4

Это дает нам

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s, \
       30 fps, 30 tbr, 30 tbn (default

Если вы выполните этот шаг, новая шкала времени должна быть равна или кратна исходной частоте кадров.

. Рассчитайте необходимую временную шкалу, так чтобы для target framerate x, PTS кадра # xв источнике имел то же значение, что и new tbn. Если вы выполнили шаг 2a, это очень просто, и это просто новая частота кадров. Таким образом, для target fps 45new tbnдолжно быть 45.


3. Сгенерировать фиктивное видео.

ffmpeg -f lavfi -i color=s=1280x720:r=45:d=1 -profile:v main -video_track_timescale 45 0.mp4

Для достижения наилучших результатов все свойства должны быть одинаковыми, например разрешение, профиль H.264, формат пикселей, количество ссылок и т. д.


4Объедините видео.

Сначала создайте текстовый файл.

file '0.mp4'
file 'in-v30.mp4'

Затем, конкат

ffmpeg -f concat -i list.txt -c copy -video_track_timescale 45 45fps.mp4

Выходной файл будет содержать второе видео, воспроизводимое со скоростью 45 кадров в секунду.

5. Теперь отколите фиктивный преролл.

ffmpeg -ss 1.1 -i 45fps.mp4 -c copy -avoid_negative_ts make_zero in45.mp4

и у вас есть

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1757 kb/s, \
       45 fps, 45 tbr, 11520 tbn (default)

Я же говорил, что это запутанно!

решение2

ffmpeg -itsscale 1.0427083 -i input.mp4 -codec copy output.mp4

Это правильно замедляет mp4 с частотой 25 кадров в секунду, созданный Handbrake из источника PAL DVD, до 23,974 кадров в секунду. Оригинальное шоу — NTSC. Звук остается синхронизированным на протяжении всего времени показа, которое теперь составляет 47 минут. Он очень быстрый, поскольку не выполняется декодирование/кодирование. Однако на протяжении всего фильма наблюдаются сбои (пропадание) звука с интервалом примерно в 3 секунды. Тот же результат при vcodecзамене кодека, за исключением того, что хотя видео не перекодируется, звук перекодируется с половиной исходного битрейта и все еще имеет сбои с пропаданием.

ffmpeg -itsscale 1.0427083 -i input.mp4 -vcodec copy -filter:a "atempo=0.959041" output.mp4

Это устраняет выпадения звука, но перекодирует звук. Это намного быстрее, чем перекодирование видео. Оставшийся недостаток в том, что по умолчанию используется половина исходного битрейта звука. Нужно выяснить, как установить битрейт звука для перекодирования.

решение3

Использовать-itsscaleна входном видео для достижения эффективного изменения частоты кадров. Он отлично работает с -vcodec copy.

решение4

Это можно сделать с помощью фильтра bitstream setts. Это также позволяет избежать хлопот с записью необработанного потока в файл, а затем его повторным муксированием. Это работает, потому что, хотя вы не можете использовать обычные фильтры с codec copy ( -c:v copy), поскольку они работают с декодированным видеопотоком, вы можете использовать фильтры bitstream, которые работают с кодированным потоком.

Например, чтобы изменить частоту кадров на 60 кадров в секунду, вставьте

-bsf:v setts=ts=STARTPTS+N/TB_OUT/60

Это должно установить оба ptsи dtsбез декодирования потока. Если у вас видео с переменной частотой кадров, вам может понадобиться что-то вроде этого

-bsf:v setts=ts='if(PREV_OUTPTS+9223372036854775808\,PREV_OUTPTS\,STARTPTS)+PREV_OUTDURATION*2'

Это изменяет длительность кадра вместо того, чтобы предполагать, что каждый кадр имеет постоянную длительность. Странно выглядящее ifвыражение должно гарантировать, что первый кадр смещен правильно, так как PREV_OUTPTSустановлено на -9223372036854775808(минимальное значение 64-битного int) во время первого кадра, и нам нужно заменить его на ноль.

Если у вас есть B-кадры, это может испортить декодирование, так как dtsможет потребоваться меньше, чем pts, но, похоже, у меня все равно работает нормально. Вы можете попробовать заменить setts=tsна setts=pts, если у вас возникнут проблемы. Смотритеhttps://ffmpeg.org/ffmpeg-bitstream-filters.html#settsдля подробностей

Связанный контент