ffmpeg неточно ищет ключевые кадры с H265 vcopy

У меня есть видео H265 4K MP4 29,97 кадров в секунду с размером GOP ровно 30 кадров. Когда я пытаюсь вырезать с самого начала, используя:

ffmpeg -ss 1 -i INPUT.MP4 -vcodec copy OUTPUT_1SEC.MP4
ffmpeg -ss 2 -i INPUT.MP4 -vcodec copy OUTPUT_2SEC.MP4
ffmpeg -ss 3 -i INPUT.MP4 -vcodec copy OUTPUT_3SEC.MP4
ffmpeg -ss 4 -i INPUT.MP4 -vcodec copy OUTPUT_4SEC.MP4
ffmpeg -ss 5 -i INPUT.MP4 -vcodec copy OUTPUT_5SEC.MP4
ffmpeg -ss 6 -i INPUT.MP4 -vcodec copy OUTPUT_6SEC.MP4
ffmpeg -ss 7 -i INPUT.MP4 -vcodec copy OUTPUT_7SEC.MP4
ffmpeg -ss 8 -i INPUT.MP4 -vcodec copy OUTPUT_8SEC.MP4
ffmpeg -ss 9 -i INPUT.MP4 -vcodec copy OUTPUT_9SEC.MP4

Выходные видео начинаются с 0 (-ss 1~4), 4 (-ss 5~8) или 8 секунд (-ss 9), как показано ниже:

Хронология Премьер Про

Так что кажется, что ffmpeg каким-то образом обнаруживает GOP 4 секунды вместо 1 секунды. Это нормально?

Кроме того, как я могу записать правильный тайм-код в выходном видео? Например, я пробовал много комбинаций, таких как:

ffmpeg -ss 5 -i INPUT.MP4 -vcodec copy -timecode 00:00:05:00 OUTPUT_5SEC.MP4
ffmpeg -ss 5 -i INPUT.MP4 -vcodec copy -copyts OUTPUT_5SEC.MP4
ffmpeg -start_at_zero -ss 5 -i INPUT.MP4 -vcodec copy -copyts OUTPUT_5SEC.MP4

Но он либо дает мне точный тайм-код, который я ввел (первая строка), либо начинается с нуля (две последние строки).

Первоначально я думал о поиске именно в секунду (или через несколько кадров после), поэтому я знал, что получу ключевой кадр, чтобы я мог угадать точный тайм-код, с которого начнется вывод, но кажется, что ffmpeg -ss не совсем основан на ключевых кадрах ? Может быть, я что-то здесь упускаю? Спасибо за помощь.

Дополнительная информация

Я хотел бы написать сценарий процесса вырезания, поэтому я хочу знать, откуда взялся этот 4-секундный «интервал ключевого кадра».

Вот вывод моего ввода ffprobe:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'INPUT.MP4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2mp41
    encoder         : Lavf57.72.101
    comment         : DE=None, Mode=M, DSW=0001
    location-{    : +XX.4914-0XX.5164+XX.000000/
    location        : +XX.4914-0XX.5164+XX.000000/
  Duration: 00:01:45.31, start: 0.000000, bitrate: 100065 kb/s
    Stream #0:0(eng): Video: hevc (Main) (hev1 / 0x31766568), yuv420p(tv, bt709), 4096x2160 [SAR 1:1 DAR 256:135], 100062 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 29.97 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(eng): Subtitle: mov_text (tx3g / 0x67337874), 2 kb/s (default)
    Metadata:
      handler_name    : SubtitleHandler

Вот команда, которую я использовал для проверки размера группы (введите «I» на 1,31,61,... и «P» между ними):

ffprobe -i INPUT.MP4 -select_streams v -show_frames -show_entries frame=pict_type -of csv > OUTPUT.CSV

версия ffmpeg N-86330-gbd1179e и версия ffmpeg N-86330-gbd1179e

Изменить: пример видео здесь


person dsagilles    schedule 19.08.2017    source источник
comment
Каков результат ffprobe -i INPUT.MP4 -select_streams v -show_entries frame=key_frame -of csv > OUTPUT.CSV   -  person Gyan    schedule 19.08.2017
comment
frame,I в строке 1,31,61,... и frame,P во всех остальных строках   -  person dsagilles    schedule 19.08.2017
comment
значение key_frame не pict_type. Не все I-кадры являются IDR-кадрами.   -  person Gyan    schedule 19.08.2017
comment
Ах да, извините. Тот же шаблон, но с frame,1 в строке 1,31,61... и frame,0 во всех остальных строках.   -  person dsagilles    schedule 19.08.2017
comment
Хорошо, попробуйте скопировать только видеодорожку (-map 0:v). Временной код представляет собой одну закодированную строку, а не метаданные хронометража.   -  person Gyan    schedule 19.08.2017
comment
Тот же результат с ffmpeg -ss 7 -i INPUT.MP4 -map 0:v -vcodec copy OUTPUT_7SEC.MP4. Спасибо за объяснение таймкода.   -  person dsagilles    schedule 19.08.2017
comment
Вы можете поделиться файлом целиком или его частью?   -  person Gyan    schedule 19.08.2017
comment
Конечно. Я только что добавил ссылку внизу моего сообщения. Спасибо, что заглянули :)   -  person dsagilles    schedule 19.08.2017


Ответы (1)


Хотя каждую секунду есть ключевой кадр, в поле MOOV только три кадра установлены как выборки синхронизации.

/moov/trak/mdia/minf/stbl/stss                              @ 0x77e8515
  Box size: 0x1c    version: 0x0    flags: 0x0
  entry_count:              0x3
    sample_number:
    0x1    0x79    0xf1

(это 1-й, 121-й и 241-й кадры.)

FFmpeg полагается на эту информацию при поиске.


Обходной путь - мультиплексировать в TS, а затем повторно мультиплексировать в MP4.

ffmpeg -i input.mp4 -c copy input.ts

а потом

ffmpeg -i input.ts -c copy newinput.mp4

Или в одной команде

ffmpeg -i input.mp4 -c copy -f mpegts - | ffmpeg -f mpegts -i - -c copy newinput.mp4

Файлы MPEG-TS не имеют индекса, поэтому, если вы хотите использовать этот файл для извлечения, укажите точку поиска перед ключевым кадром, из которого вы хотите вырезать.

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

person Gyan    schedule 19.08.2017
comment
Спасибо за эту полезную информацию. Теперь я могу разбивать newinput.mp4 каждую секунду, но результирующие клипы содержат скачки, черные и зеленые кадры + блоки сжатия. В итоге я преобразовал весь свой h265 в DnxHR, и я храню полный неотредактированный h265 для архива. Я очень ценю время, которое вы потратили на изучение этого, теперь я понимаю больше о ключевых кадрах. - person dsagilles; 02.09.2017