Як отримати штамп часу найближчого ключового кадру перед заданою часовою позначкою за допомогою 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.

Питання: Як ми можемо отримати позначку часу першого результату пошуку?

Відповіді:


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.


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

Деякі гравці не належним чином поважають це і відображатимуть чорне відео чи сміття. У цьому випадку вищевказаний скрипт може бути використаний для пошуку PTS ключового кадру після точки пошуку, і використовувати його для початку пошуку ключового кадру. Це, однак, не буде точним.

Зауважте, що якщо ви хочете бути дуже точними під час пошуку - і зберегти сумісність з багатьма програвачами - вам, ймовірно, слід перетворити відео у будь-який формат без втрат, який можна втратити в будь-який момент, а потім перекодувати його знову. Але це буде не швидко.


1
дякую, я не роблю редактор відео, але я хочу мати точне відео, яке шукає, у якому розрив повинен становити менше 0,5 секунд.
jackode

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 frame = pkt_pts_time, изображений_тип
Jannes

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, малюнок_типу 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]

Тому результати будуть містити лише інформацію про ключові кадри.


1

Спираючись на відповідь slhck , ось функція bash, яка поверне найближчий кадр ключових кадрів, який виникає ЗАЛИШЕ Nсекунд.

Це також використовує -read_intervalsдля того, щоб ffprobe тільки почав шукати ваш ключовий кадр за 25 секунд до Nсекунд. Цей трюк і неабиякий вихід, коли знайдено часову позначку, значно прискорює роботу.

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 секунди від фактичної часової позначки keyframe робить трюк. Для баш потрібно використовувати bcдля оцінки виразів з десятковими знаками , але в zsh -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'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.