Bagaimana cara mendapatkan cap waktu keyframe terdekat sebelum cap waktu tertentu dengan FFmpeg?

18

Saya ingin mencari FFmpeg perintah yang cepat dan akurat. Saya menemukan ini .

Solusinya adalah kita menerapkan -ssinput (pencarian cepat) dan output (pencarian akurat). Tetapi: Jika pencarian input tidak akurat, bagaimana kita bisa yakin bahwa posisi pencarian itu akurat?


Sebagai contoh: Jika kita ingin mencari ke 00:03:00, perintahnya adalah seperti:

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

Yang pertama -ssakan mencari ke tempat lain, bukan 00:02:30, katakan 00:02:31. Dan setelah menerapkan pencarian kedua, hasil akhirnya akan 00:03:01- bukan yang kita inginkan. Apakah itu benar?

Di mana -ssmencari pertama ? Apakah itu mencari keyframe yang paling dekat 00:02:30?

Jika demikian, inilah pemikiran saya — perbaiki saya jika saya salah: setelah pencarian pertama, kami mendapatkan cap waktu hasilnya (dalam contoh ini:) 00:02:31, maka kami menerapkan pencarian kedua dengan waktu yang tepat, dalam hal ini 00:00:29.

Pertanyaannya adalah: Bagaimana cara mendapatkan cap waktu dari hasil pencarian pertama?

jackode
sumber

Jawaban:

18

Untuk benar-benar menjawab pertanyaan judul Anda: Anda bisa mendapatkan daftar bingkai-I

ffprobe -select_streams v -show_frames <INPUT> 

Anda selanjutnya dapat membatasi ini untuk output yang diperlukan dengan menambahkan -show_entries frame=pkt_pts_time,pict_type.

Untuk melihat bingkai mana yang paling dekat (setelah) cap waktu tertentu, Anda harus terlebih dahulu mencari tahu semua cap waktu dari kerangka kunci, misalnya dengan awk.

Pertama, tentukan waktu yang ingin Anda cari, misalnya, 2: 30m yang sama dengan 150-an.

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

Sebagai contoh, ini akan kembali 150.400000.


Perhatikan bahwa ketika menggunakan -sssebelumnya -i, FFmpeg akan menemukan keyframe sebelumnya ke titik pencarian, kemudian menetapkan nilai PTS negatif untuk semua frame berikut hingga mencapai titik pencarian. Seorang pemain harus memecahkan kode tetapi tidak menampilkan bingkai dengan PTS negatif, dan video harus mulai secara akurat.

Beberapa pemain tidak menghargai ini dengan benar dan akan menampilkan video hitam atau sampah. Dalam hal ini, skrip di atas dapat digunakan untuk menemukan PTS dari keyframe setelah titik pencarian Anda, dan menggunakannya untuk mulai mencari dari keyframe. Namun, ini tidak akan akurat.

Perhatikan bahwa jika Anda ingin menjadi super akurat saat mencari — dan mempertahankan kompatibilitas dengan banyak pemain — Anda mungkin harus mengonversi video ke format intra-saja tanpa kehilangan apa pun, tempat Anda dapat memotong pada titik mana pun, dan kemudian menyandikan ulang. Tapi ini tidak akan cepat.

slhck
sumber
1
terima kasih, saya tidak membuat editor video, tetapi saya ingin memiliki pencarian video yang tepat di mana jaraknya kurang dari 0,5 detik.
jackode
1
Anda mungkin bisa bermain-main dengan PTS dari ffprobe. Jika tidak, format perantara apa pun dapat dilakukan, misalnya ProRes 422, DNxHD, yang secara visual lossless dan intra-frame saja. Atau Anda menggunakan sesuatu seperti HuffYUV, dll. Tetapi kemudian Anda akan kehilangan aspek "cepat" lagi, tentu saja.
slhck
apa versi ffprobe yang Anda gunakan untuk perintah, karena kata sayaUnrecognized option 'select_streams'
jackode
2
Anda sudah dekat, select_streamsopsi ditambahkan pada Oktober 2012 . :) Anda bisa melakukannya tanpa itu tetapi kemudian Anda akan mendapatkan informasi untuk frame audio juga, dicampur di antaranya.
slhck
2
Catatan Anda dapat menambahkan baris ini ffmpeg untuk membiarkannya hanya output diperlukan 2 bidang, bukan banyak hal yang akan dibuang oleh awk: -show_entries bingkai = pkt_pts_time, pict_type
Jannes
7

Saya mengerti pertanyaan ini sudah berumur beberapa tahun, tetapi versi terbaru dari ffprobe memiliki kemampuan untuk melewati bingkai . Anda dapat masuk -skip_frame nokeyuntuk melaporkan info hanya pada frame kunci (I-frame). Ini dapat menghemat banyak waktu! Pada file MP4 2GB 1080p, dibutuhkan waktu 4 menit tanpa bingkai loncatan. Menambahkan paramter lewati hanya membutuhkan waktu 20 detik.

Perintah:

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

Hasil:

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

Jadi hasilnya hanya akan berisi info tentang bingkai kunci.

Hind-D
sumber
1

Membangun jawaban slhck , inilah fungsi bash yang akan mengembalikan keyframe terdekat yang terjadi SEBELUM Ndetik.

Ini juga digunakan -read_intervalsuntuk memastikan bahwa ffprobe hanya mulai mencari frame kunci Anda 25 detik sebelum Ndetik. Trik ini dan memiliki awk keluar ketika stempel waktu ditemukan sangat mempercepat.

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
}

contoh penggunaan:

➜ ffnearest input.mkv 30
23.941000

Saya menggunakan ini untuk memotong file video tanpa kembali encoding. Karena Anda tidak dapat menambahkan kerangka kunci baru tanpa pengodean ulang, saya menggunakan ffnearestuntuk mencari kerangka kunci sebelum saya ingin memotong. Ini sebuah contoh:

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

Perhatikan bahwa untuk contoh itu Anda mungkin perlu mengubah format apa yang dilewatkan di -ssparam jika Anda mencari lebih dari 60 detik pertama.

(Secara menjengkelkan, memberitahu ffmpeg untuk mencari tepat ke cap waktu keyframe tampaknya membuat ffmpeg mengecualikan keyframe dalam output, tetapi mengurangi 0,5 detik dari cap waktu aktual keyframe melakukan trik. Untuk bash, Anda perlu menggunakan bcuntuk mengevaluasi ekspresi dengan desimal. , tetapi dalam -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]karya zsh .)

Chris
sumber
Ini akan memberikan waktu bingkai berikutnya setelah saya membingkai.
Ehsan Chavoshi
0

jika Anda ingin mendapatkan informasi dari frame I, Anda dapat menggunakan

ffprobe -i input.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type|grep -B 1 'pict_type=I'
shuaihanhungry
sumber