วิธีรับการประทับเวลาของคีย์เฟรมที่ใกล้ที่สุดก่อนเวลาประทับที่กำหนดด้วย 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-frames พร้อม

ffprobe -select_streams v -show_frames <INPUT> 

นอกจากนี้คุณยังสามารถ จำกัด -show_entries frame=pkt_pts_time,pict_typeการนี้เพื่อการส่งออกที่จำเป็นโดยการเพิ่ม

เพื่อดูว่ากรอบที่ใกล้เคียงที่สุด (มาหลังจาก) ประทับเวลาบางอย่างที่คุณจะต้องเป็นครั้งแรกที่จะหา timestamps awkทั้งหมดของคีย์เฟรมเช่นกับ

ก่อนอื่นให้กำหนดเวลาที่คุณต้องการค้นหาเช่น 2: 30m ซึ่งเท่ากับ 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, pict_type
Jannes

7

ผมเข้าใจคำถามนี้เป็นรุ่นเก่าหลายปี แต่รุ่นล่าสุดของ ffprobe มีความสามารถในการข้ามเฟรม คุณสามารถส่งผ่าน-skip_frame nokeyไปยังข้อมูลรายงานเฉพาะในคีย์เฟรม (I-frames) สิ่งนี้สามารถช่วยคุณประหยัดเวลาได้มาก! ในไฟล์ 2GB 1080p MP4 ใช้เวลา 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]

ดังนั้นผลลัพธ์จะมีข้อมูลเกี่ยวกับเฟรมหลักเท่านั้น


1

สร้างจากคำตอบของ slhck ต่อไปนี้เป็นฟังก์ชั่นทุบตีซึ่งจะส่งคืนคีย์เฟรมที่ใกล้เคียงที่สุดที่เกิดขึ้นก่อนหน้า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;

โปรดทราบว่าสำหรับตัวอย่างนั้นคุณอาจต้องเปลี่ยนรูปแบบของสิ่งที่ส่งผ่านใน-ssParam หากคุณกำลังมองหาไกลกว่า 60 วินาทีแรก

(น่ารำคาญที่บอกให้ ffmpeg ค้นหาเพื่อหาเวลาที่แน่นอนของ keyframe ดูเหมือนว่าจะทำให้ ffmpeg ไม่รวมคีย์เฟรมนั้นในผลลัพธ์ แต่การลบ 0.5 วินาทีจากการประทับเวลาจริงของ keyframe นั้นเป็นการหลอกลวงสำหรับการทุบตีคุณต้องใช้bcเพื่อประเมินนิพจน์ด้วยทศนิยม แต่ใน zsh -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]ทำงาน)


นี่จะให้เวลาเฟรมถัดไปหลังจากฉันเฟรม
Ehsan Chavoshi

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'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.