วิธีสร้างภาพขนาดย่อจากกลางวิดีโอด้วย FFmpeg


8

มีวิธีในการคว้าภาพขนาดย่อของวิดีโอใน FFmpeg หรือไม่?

ฉันต้องการคว้าเฟรมที่อยู่ตรงกลางที่สุดเป็นวิดีโอและใช้เป็นภาพขนาดย่อ ไม่ทราบระยะเวลาของวิดีโอ

ความสามารถในการระบุขนาดของภาพขนาดย่อก็จะมีประโยชน์เช่นกัน

คำตอบ:


9

คำสั่งหนึ่งบรรทัด:

ffmpeg -i input.mp4 -vcodec mjpeg -vframes 1 -an -f rawvideo -ss `ffmpeg -i input.mp4 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F ':' '{print ($3+$2*60+$1*3600)/2}'` output.jpg

คำสั่งย่อยรับช่วงเวลาทั้งหมดของอินพุตวิดีโอ

ffmpeg -i input.mp4 2>&1 | grep Duration | awk '{print $2}' | tr -d ,

และไปป์ที่ awk เพื่อคำนวณระยะเวลา / 2 แบบนี้

echo '00:01:30.000' | awk -F ':' '{print ($3+$2*60+$1*3600)/2}'

ดังนั้นให้สิ่งนี้ระหว่างวินาทีเพื่อ -ss ตัวเลือกและมันเสร็จแล้ว

หากคุณต้องการระบุขนาดให้เลือกตัวเลือก -s เช่น WxH whet W และ H เป็นค่าจำนวนเต็ม (เช่น: -s 800x600 หรือ -s svga) หรืออ่านหน้า man ffmpeg เพื่อดูตัวเลือกหรือรูปแบบอื่น ๆ ที่มีอยู่


1
-vcodec mjpeg -an -f rawvideoไม่จำเป็นเมื่อเอาต์พุตของคุณเป็นเพียง.jpgไฟล์ ฉันอยากจะแนะนำให้ส่งออกไปยัง PNG แทนและทำการบีบอัดในภายหลัง
slhck

2
ffmpegเอาท์พุทเป็นเพียงเพื่อวัตถุประสงค์ในการให้ข้อมูลเท่านั้นและไม่ได้มีไว้สำหรับการแยกดังนั้นจึงประมวลผลก็อาจได้รับการพิจารณาที่เปราะบาง ใช้ffprobeเพื่อรับช่วงเวลาแทน ดูFFmpeg วิกิพีเดีย: เคล็ดลับ
llogan

5

ก่อนอื่นใช้ FFmpeg เวอร์ชันล่าสุดเสมอ

หากคุณมีสิทธิ์เข้าถึง PHP คำถามของคุณจะได้รับคำตอบที่สมบูรณ์แบบใน Stack Overflow: ใช้ FFMpeg เพื่อรับเฟรมกลางของวิดีโอ

$output = shell_exec("/usr/local/bin/ffmpeg -i {$path}");
preg_match('/Duration: ([0-9]{2}):([0-9]{2}):([^ ,])+/', $output, $matches);
$time = str_replace("Duration: ", "", $matches[0]);
$time_breakdown = explode(":", $time);
$total_seconds = round(($time_breakdown[0]*60*60) + ($time_breakdown[1]*60) + $time_breakdown[2]);
shell_exec("/usr/local/bin/ffmpeg -y  -i {$path} -f mjpeg -vframes 1 -ss " . ($total_seconds / 2) . " -s {$w}x{$h} {$output_filename}"; 

สิ่งที่มันจะทำคือเพียงดึงระยะเวลาจากเอาต์พุตของ FFmpeg และใช้สิ่งนั้นเพื่อกำหนดไทม์โคดของเฟรมกลาง

คุณสามารถปรับให้เข้ากับเปลือกหอยอื่น ๆ ได้อย่างง่ายดายและเพียงแค่แทรกคำสั่งต่อไปนี้โดยที่เฟรมกลางอยู่ที่ประมาณ534วินาที:

ffmpeg -y  -i input.mp4 -f mjpeg -vframes 1 -ss 534 thumbnail.jpg

คุณสามารถเปลี่ยนขนาดด้วย-s 480x320หรือคล้ายกันได้ตลอดเวลาทั้งนี้ขึ้นอยู่กับขนาดที่คุณต้องการแทรกไว้ที่ไหนสักแห่งหลังจาก-i input.mp4ตัวเลือก


การแก้ปัญหาข้างต้นไม่ถูกต้องเล็กน้อย มันจะให้ผลลัพธ์ทันที แต่คุณไม่สามารถระบุเฟรมที่คุณต้องการได้ หากคุณทราบเฟรมที่แน่นอนที่คุณต้องการแตกแล้วให้ใช้selectตัวกรองเช่นกับหมายเลขเฟรม12345:

ffmpeg -i input.mp4 -filter:v select="eq(n\,12345)" -vframes 1 thumbnail.jpg

โปรดทราบว่าคำสั่งนี้อาจใช้เวลาสักครู่เนื่องจากต้องข้ามไปยังเฟรมที่ระบุก่อนจึงจะสามารถแตกออกได้


อีกทางเลือกหนึ่งในการระบุมิติข้อมูลขนาดย่อ - ตัวอย่างนี้ตัดไปเป็นอัตราส่วน 4: 3:ffmpeg -y -i input.mp4 -vf "crop=(ih*4/3):ih" -f mjpeg -vframes 1 -ss 534 thumbnail.jpg
rymo

มีวิธีข้ามส่วน "รอ" หรือไม่? กรอไปข้างหน้าสู่เฟรม / เวลาที่ระบุ?
แซมสัน

@Samson คุณกำลังพูดถึงทางออกใดที่แน่นอน หากคุณระบุไว้-ssก่อนหน้าการ-iค้นหาควรเร็วกว่านี้มาก
slhck

1

นี่คือวิธีการที่ใช้:

  • ffprobe เพื่อรับระยะเวลา
  • bc เพื่อคำนวณครึ่งระยะเวลา
  • ffmpeg เพื่อสร้างภาพขนาดย่อ

ตัวอย่าง:

input=video.mp4
duration=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$input")
ffmpeg -ss "$(echo "$duration"/2 | bc)" -i "$input" -q:v 2 -vframes 1 output.jpg
  • คุณสามารถแทนที่การใช้ขี้เกียจของฉันbcด้วยทางเลือกที่คุณชื่นชอบเช่นawkหรือprintfถ้าคุณต้องการ

  • คุณสามารถปรับขนาด / ปรับขนาดภาพโดยการเพิ่มตัวกรองขนาดเป็นตัวเลือกในการส่งออกของคุณคำสั่งเช่นffmpeg-vf scale=320:-1

  • ระยะเวลาที่ให้มาffprobeอาจไม่แม่นยำขึ้นอยู่กับอินพุต ในกรณีที่ไม่น่าเป็นไปได้ที่คุณมีปัญหาเช่นนี้คุณจะต้องถอดรหัสอินพุตทั้งหมดเช่นด้วยffmpeg -i input -f null -และแยกวิเคราะห์เอาต์พุตเพื่อรับค่าระยะเวลาที่ถูกต้อง


0
$currentVideoDuration =  shell_exec("$ffmpeg -i $videoPath 2>&1 | grep Duration");
  $actualDuration = substr($currentVideoDuration, 11, 12);
  $arrayWithHoursMinsAndSecs = explode(":", $actualDuration);
  $thisVideoDurationInSeconds = $arrayWithHoursMinsAndSecs[2] + $arrayWithHoursMinsAndSecs[1]*60 + $arrayWithHoursMinsAndSecs[0]*3600;
  $halfVideoDuration = $thisVideoDurationInSeconds/2;
  echo 'midpoint of this video: '. $halfVideoDuration;
  echo shell_exec("$ffmpeg -i $videoPath -y -ss 00:00:$halfVideoDuration -vframes 1 thumbnail.png 2>&1");

แทนที่ $ videopath ด้วยพา ธ ไปยังวิดีโอของคุณ


0

วิธีง่ายๆคือ:

 ffmpeg -i l1.mp4 -ss 00:00:14.435 -vframes 1 out.png

1
นี่ดูเหมือนคำตอบของ PrometeeและคำตอบของLordNeckbeardยกเว้นว่าคุณคิดว่าผู้ใช้รู้ความยาวของวิดีโออยู่แล้ว (คำถามบอกว่าไม่เข้าใจ) และคุณไม่อธิบายอะไรเลย
สกอตต์

ฉันให้แค่ตัวอย่างถ้าไม่ต้องการเวลาลอง ffmpeg -i l1.mp4 -vframes 1 out11.jpg นี้
Pankaj Chauhan

-1

คำสั่ง bash นี้ทำงานได้เหมือนเครื่องราง:

avconv -i 'in.mpg' -vcodec mjpeg -vframes 1 -an -f rawvideo \
-s 420x300 -ss `avconv -i in.mpg 2>&1 | grep Duration \
| awk '{print $2}' | tr -d , \ 
| awk -F ':' '{print ($3+$2*60+$1*3600)/2}'` out.jpg

1
พระเจ้าของฉันมีความซับซ้อน
Sridhar Sarnobat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.