Jak uzyskać znacznik czasu najbliższej klatki kluczowej przed danym znacznikiem czasu za pomocą FFmpeg?

18

Chcę komendy FFmpeg szukającej tak szybko i dokładnie. Znalazłem to .

Rozwiązaniem jest to, że ubiegamy się -sszarówno o dane wejściowe (szybkie wyszukiwanie), jak i wyniki (dokładne wyszukiwanie). Ale: Jeśli szukanie danych wejściowych nie jest dokładne, w jaki sposób możemy być pewni, że pozycja szukania jest dokładna?


Na przykład: jeśli chcemy szukać do 00:03:00, polecenie wygląda następująco:

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

Pierwszy -ssbędzie szukał czegoś innego, a nie 00:02:30powiedzą 00:02:31. A po zastosowaniu drugiego poszukiwania końcowy wynik byłby 00:03:01- nie taki, jaki chcemy. Czy to jest poprawne?

Gdzie -ssszuka tego pierwszy ? Czy chodzi o najbliższą klatkę kluczową 00:02:30?

Jeśli tak, oto moja myśl - popraw mnie, jeśli się mylę: po pierwszym wyszukiwaniu otrzymujemy znacznik czasu wyniku (w tym przykładzie 00:02:31:), a następnie stosujemy drugie wyszukiwanie z odpowiednim czasem, w tym przypadku 00:00:29.

Pytanie brzmi: Jak uzyskać znacznik czasu wyniku pierwszego wyszukiwania?

jackode
źródło

Odpowiedzi:

18

Aby dosłownie odpowiedzieć na pytanie w tytule: Możesz uzyskać listę I-ramek za pomocą

ffprobe -select_streams v -show_frames <INPUT> 

Możesz dodatkowo ograniczyć to do niezbędnego wyniku, dodając -show_entries frame=pkt_pts_time,pict_type.

Aby zobaczyć, która ramka jest najbliższa (następuje po) określonym znacznikiem czasu, najpierw musisz znaleźć wszystkie znaczniki czasu klatek kluczowych, na przykład za pomocą awk.

Najpierw określ czas, którego chcesz szukać, np. 2: 30m, co równa się 150s.

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

Na przykład to zwróci 150.400000.


Zauważ, że przy użyciu -sswcześniej -iFFmpeg zlokalizuje klatkę kluczową poprzedzającą punkt wyszukiwania, a następnie przypisze ujemne wartości PTS do wszystkich kolejnych ramek, aż osiągnie punkt wyszukiwania. Gracz powinien dekodować, ale nie wyświetlać klatek z ujemnym PTS, a wideo powinno rozpocząć się dokładnie.

Niektórzy gracze nie przestrzegają tego poprawnie i wyświetlają czarne wideo lub śmieci. W takim przypadku powyższego skryptu można użyć do znalezienia PTS klatki kluczowej po punkcie wyszukiwania i użyć go do rozpoczęcia wyszukiwania z klatki kluczowej. To jednak nie będzie dokładne.

Pamiętaj, że jeśli chcesz być bardzo dokładny podczas wyszukiwania - i zachować zgodność z wieloma odtwarzaczami - prawdopodobnie powinieneś przekonwertować wideo na dowolny bezstratny format tylko wewnątrz, w którym możesz wyciąć w dowolnym momencie, a następnie ponownie zakodować. Ale to nie będzie szybkie.

slhck
źródło
1
dzięki, nie tworzę edytora wideo, ale chcę mieć precyzyjne wyszukiwanie wideo, w którym przerwa powinna wynosić mniej niż 0,5 sekundy.
jackode
1
Prawdopodobnie możesz żonglować PTS z ffprobe. Jeśli nie, zrobiłby to każdy format pośredni, np. ProRes 422, DNxHD, które są wizualnie bezstratne i tylko wewnątrz klatki. Albo używasz czegoś takiego jak HuffYUV itp. Ale wtedy oczywiście stracisz aspekt „szybki”.
slhck
jakiej wersji programu ffprobe użyłeś do polecenia, ponieważ moja powiedziałaUnrecognized option 'select_streams'
jackode
2
Byłeś blisko, select_streamsopcja została dodana w październiku 2012 r . :) Mogłoby się bez tego obejść, ale wtedy dostaniesz również informacje dla ramek audio, pomieszanych pomiędzy nimi.
slhck
2
Pamiętaj, że możesz dodać tę linię ffmpeg, aby wyświetlała tylko niezbędne 2 pola, zamiast wielu rzeczy, które są wyrzucane przez awk: -show_entries frame = pkt_pts_time, pict_type
Jannes
7

Rozumiem, że to pytanie ma kilka lat, ale najnowsza wersja programu ffprobe ma możliwość pomijania ramek . Możesz przekazać, -skip_frame nokeyaby zgłosić informacje tylko o ramkach kluczowych (I-ramki). To pozwala zaoszczędzić dużo czasu! W przypadku pliku MP4 2 GB 1080p zajęło 4 minuty bez pomijania klatek. Dodanie parametru pominięcia zajmuje tylko 20 sekund.

Komenda:

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

Wyniki:

[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]

Zatem wyniki będą zawierać tylko informacje dotyczące klatek kluczowych.

Hind-D
źródło
1

Opierając się na odpowiedzi slhck , oto funkcja bash, która zwróci najbliższą klatkę kluczową występującą PRZED Nsekundami.

Pozwala to również -read_intervalsupewnić się, że ffprobe zacznie szukać twojej klatki kluczowej 25 sekund przed Nsekundami. Ta sztuczka i wyjście z awk po znalezieniu znacznika czasu znacznie przyspiesza.

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
}

przykładowe użycie:

➜ ffnearest input.mkv 30
23.941000

Używam tego do przycinania plików wideo bez ich ponownego kodowania. Ponieważ nie można dodawać nowych klatek kluczowych bez ponownego kodowania, ffnearestszukam w klatce kluczowej, zanim chcę ją wyciąć. Oto przykład:

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

Zauważ, że w tym przykładzie może być konieczna zmiana formatu tego, co przekazano w -ssparametrze, jeśli szukasz więcej niż pierwszych 60 sekund.

(irytujące, mówiąc ffmpeg, aby szukał dokładnie znacznika czasu klatki kluczowej, wydaje się, że ffmpeg wyklucza tę klatkę kluczową w danych wyjściowych, ale odejmowanie 0,5 sekundy od rzeczywistego znacznika czasu klatki kluczowej robi załamanie. W przypadku bash musisz użyć bcdo oceny wyrażeń dziesiętnych , ale w Zsh -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]działa.)

Chris
źródło
To da następny czas po klatce.
Ehsan Chavoshi
0

jeśli chcesz uzyskać informacje o ramkach I, możesz użyć

ffprobe -i input.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type|grep -B 1 'pict_type=I'
shuaihanhungry
źródło