У меня есть видео 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
.
2а. (Необязательно) Измените шкалу времени на удобную, чтобы упростить расчеты.
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
Если вы выполните этот шаг, новая шкала времени должна быть равна или кратна исходной частоте кадров.
2б. Рассчитайте необходимую временную шкалу, так чтобы для target framerate x
, PTS кадра # x
в источнике имел то же значение, что и new tbn
. Если вы выполнили шаг 2a, это очень просто, и это просто новая частота кадров. Таким образом, для target fps 45
new 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для подробностей