Как получить отметку времени ближайшего ключевого кадра до заданной отметки времени с помощью FFmpeg?

18

Я хочу команду поиска FFmpeg, которая быстрая и точная. Я нашел это .

Решение состоит в том, что мы подаем заявку -ssкак на ввод (быстрый поиск), так и на выход (точный поиск). Но: Если поиск ввода не точен, как мы можем быть уверены, что позиция поиска точна?


Например: если мы хотим найти 00:03:00, команда будет выглядеть примерно так:

ffmpeg -ss 00:02:30 -i <INPUT> ... -ss 00:00:30 <OUTPUT>

Первый -ssбудет стремиться куда-то еще, а не 00:02:30, скажем 00:02:31. И после применения второго запроса, конечный результат будет 00:03:01- не то, что мы хотим. Это верно?

Куда первый -ssстремится? Ищет ли он ключевой кадр, который ближе всего к 00:02:30?

Если это так, вот моя мысль - поправьте меня, если я ошибаюсь: после первого поиска мы получаем временную метку результата (в этом примере:) 00:02:31, затем мы применяем второй поиск с соответствующим временем, в этом случае 00:00:29.

Вопрос в том, как получить отметку времени первого поиска.

jackode
источник

Ответы:

18

Чтобы буквально ответить на вопрос вашего заголовка: Вы можете получить список I-кадров с

ffprobe -select_streams v -show_frames <INPUT> 

Вы можете дополнительно ограничить это необходимым выводом, добавив -show_entries frame=pkt_pts_time,pict_type.

Чтобы увидеть, какой кадр является ближайшим (следует за) определенной временной меткой, вам сначала нужно узнать все временные метки ключевых кадров, например, с помощью awk.

Сначала определите время, которое вы хотите найти, например, 2: 30 м, что равно 150 с.

ffprobe -select_streams v -show_frames -show_entries frame=pkt_pts_time,pict_type -v quiet in.mp4 | 
awk -F= ' 
  /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } } 
  /pkt_pts_time/ { if (i && ($2 >= 150)) print $2; }  
' | head -n 1

Например, это вернется 150.400000.


Обратите внимание, что при использовании -ssbefore -iFFmpeg будет определять местонахождение ключевого кадра, предшествующего точке поиска, а затем присваивать отрицательные значения PTS всем последующим кадрам вплоть до достижения точки поиска. Плеер должен декодировать, но не отображать кадры с отрицательным PTS, и видео должно начинаться точно.

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

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

slhck
источник
1
спасибо, я не делаю видеоредактор, но я хочу иметь точный поиск видео, в котором промежуток должен быть меньше 0,5 секунды.
Джекод
1
Вы можете, вероятно, жонглировать с PTS от ffprobe. Если нет, то подойдет любой промежуточный формат, например, ProRes 422, DNxHD, которые не имеют визуальных потерь и только внутрикадровые. Или вы используете что-то вроде HuffYUV и т. Д. Но тогда вы, конечно, снова потеряете «быстрый» аспект.
Slhck
какую версию ffprobe вы использовали для команды, потому что мой сказалUnrecognized option 'select_streams'
jackode
2
Вы были близки, select_streamsопция была добавлена ​​в октябре 2012 года . :) Вы можете обойтись без этого, но тогда вы получите информацию для аудио кадров, смешанных между ними.
Slhck
2
Обратите внимание , вы можете добавить этот FFmpeg линию , чтобы она выводится только необходимые полями 2, вместо того , чтобы много вещей , которая получает брошенную AWK: -show_entries кадра = pkt_pts_time, pict_type
Ианние
7

Я понимаю, что этому вопросу уже несколько лет, но в последней версии ffprobe есть возможность пропустить кадры . Вы можете передать -skip_frame nokeyинформацию только по ключевым кадрам (I-кадрам). Это может сэкономить вам много времени! В MP4-файле размером 2 ГБ 1080p это занимало 4 минуты без пропуска кадров. Добавление параметра пропуска занимает всего 20 секунд.

Команда:

ffprobe -select_streams v -skip_frame nokey -show_frames -show_entries frame = pkt_pts_time, pict_type D: \ test.mp4

Результаты:

[FRAME]
pkt_pts_time=0.000000
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=3.753750
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=7.507500
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=11.261250
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=15.015000
pict_type=I
[/FRAME]

Таким образом, результаты будут содержать только информацию о ключевых кадрах.

Hind-D
источник
1

Основываясь на ответе slhck , вот функция bash, которая будет возвращать ближайший ключевой кадр, который происходит ДО Nсекунд.

Это также позволяет -read_intervalsгарантировать, что ffprobe только начнет искать ваш ключевой кадр за 25 секунд до Nсекунд. Этот трюк и выход из awk, когда найдена метка времени, значительно ускоряет процесс.

function ffnearest() {
  STIME=$2; export STIME;
  ffprobe -read_intervals $[$STIME-25]% -select_streams v -show_frames -show_entries frame=pkt_pts_time,pict_type -v quiet "$1" |
  awk -F= '
    /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } }
    /pkt_pts_time/ { if (i && ($2 <= ENVIRON["STIME"])) print $2; }
    /pkt_pts_time/ { if (i && ($2 > ENVIRON["STIME"])) exit 0; }
  ' | tail -n 1
}

пример использования:

➜ ffnearest input.mkv 30
23.941000

Я использую это для обрезки видео файлов без перекодирования. Поскольку вы не можете добавлять новые ключевые кадры без перекодировки, я использую их ffnearestдля поиска до того, как хочу вырезать. Вот пример:

ffmpeg  -i input.mkv -ss 00:00:$(echo "$(ffnearest input.mkv 30) - 0.5" | bc)  -c copy -y output.mkv;

Обратите внимание, что для этого примера вам может потребоваться изменить формат того, что передается в -ssпараметре, если вы ищете дальше, чем первые 60 секунд.

(досадно, что указание ffmpeg стремиться точно к временной метке ключевого кадра заставляет ffmpeg исключать этот ключевой кадр в выходных данных, но вычитая 0,5 секунды из фактической временной метки ключевого кадра, добивается цели. Для bash необходимо использовать bcдля вычисления выражений с десятичными знаками , но в зш -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]работает.)

Крис
источник
Это даст время следующего кадра после I кадра.
Эхсан Чавоши
0

если вы хотите получить информацию о I кадрах, вы можете использовать

ffprobe -i input.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type|grep -B 1 'pict_type=I'
shuaihanhungry
источник