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