เข้าร่วมไฟล์ mp4 ใน linux


31

ฉันต้องการเข้าร่วมไฟล์ mp4 สองไฟล์เพื่อสร้างไฟล์เดียว วิดีโอสตรีมจะถูกเข้ารหัสใน h264 และเสียงใน aac ฉันไม่สามารถเข้ารหัสวิดีโอเป็นรูปแบบอื่นได้อีกเนื่องจากเหตุผลด้านการคำนวณ นอกจากนี้ฉันไม่สามารถใช้โปรแกรม GUI ใด ๆ การประมวลผลทั้งหมดจะต้องดำเนินการด้วยยูทิลิตี้บรรทัดคำสั่ง Linux FFmpeg ไม่สามารถทำได้สำหรับไฟล์ mpeg4 ดังนั้นฉันจึงใช้ MP4Box แทน:

MP4Box -add video1.mp4 -cat video2.mp4 newvideo.mp4

น่าเสียดายที่เสียงทุกอย่างปะปนกัน ฉันคิดว่าปัญหาคือเสียงอยู่ใน aac ดังนั้นฉันจึงแปลงรหัสเป็น mp3 และใช้ MP4Box อีกครั้ง ในกรณีนี้เสียงดีสำหรับครึ่งแรกของnewvideo.mp4(สอดคล้องกับvideo1.mp4) แต่จากนั้นไม่มีเสียงและฉันไม่สามารถนำทางในวิดีโอได้เช่นกัน

ความคิดต่อไปของฉันคือสตรีมเสียงและวิดีโอมีความแตกต่างเล็ก ๆ น้อย ๆ ตามความยาวที่ฉันควรแก้ไข ดังนั้นสำหรับวิดีโออินพุตแต่ละอันฉันได้แยกวิดีโอและสตรีมเสียงแล้วเชื่อมต่อพวกเขาด้วยตัวเลือก -shortest ใน FFmpeg

ดังนั้นสำหรับวิดีโอแรกที่ฉันวิ่ง:

 avconv -y -i video1.mp4 -c copy -map 0:0 videostream1.mp4
 avconv -y -i video1.mp4 -c copy -map 0:1 audiostream1.m4a
 avconv -y -i videostream1.mp4 -i audiostream1.m4a  -c copy -shortest  video1_aligned.mp4

ในทำนองเดียวกันสำหรับวิดีโอที่สองแล้วใช้ MP4Box เหมือนก่อนหน้านี้ น่าเสียดายที่นี่ไม่ได้ผลเหมือนกัน ความสำเร็จเดียวที่ฉันมีคือเมื่อฉันเข้าร่วมสตรีมวิดีโอแยกต่างหาก (เช่น videostream1.mp4 และ videostream2.mp4) และสตรีมเสียง (เช่น audiostream1.m4a และ audiostream2.m4a) จากนั้นเข้าร่วมวิดีโอและเสียงในไฟล์สุดท้าย อย่างไรก็ตามการซิงโครไนซ์จะหายไปในช่วงครึ่งหลังของวิดีโอ ในทางกลับกันเสียงและวิดีโอจะล่าช้า 1 วินาที ข้อเสนอแนะใด ๆ ยินดีต้อนรับจริงๆ


"FFmpeg ไม่สามารถทำสิ่งนี้สำหรับไฟล์ mpeg4 ดังนั้นฉันจึงใช้ MP4Box แทน" คุณหมายถึงไฟล์ MP4 หรือไม่ ทั้งกระแสข้อมูลเสียงและวิดีโอใช้พารามิเตอร์การเข้ารหัสเดียวกัน (ความกว้างความสูง SPS PPS อัตราการสุ่มตัวอย่างจำนวนช่องสัญญาณหรือไม่)

ใช้ ffprobe ฉันเห็นว่ากระแสข้อมูลเสียงมีคุณลักษณะที่เหมือนกัน สตรีมวิดีโอมีขนาดเท่ากัน แต่มีอัตราต่างกัน สำหรับวิดีโอวิดีโอที่ 1: h264, 720x592, 277 kb / s, PAR 18944: 12915 DAR 512: 287, 24.97 fps, 45k tbr, 90k tbn, 90k tbc ในขณะที่วิดีโอที่สอง: h264, 720x592, 226 kb / s PAR 481: 330 DAR 39:22, 24.91 fps, 45k tbr, 90k tbn, 90k tbc คุณเชื่อหรือไม่ว่านี่คือสาเหตุของปัญหาของฉัน

MP4 รองรับการเข้าร่วมวิดีโอ / เสียงที่มีคุณสมบัติแตกต่างกันโดยใช้แทร็คที่แตกต่างกันสำหรับวิดีโอที่แตกต่างกัน แต่ผู้เล่นไม่ได้ฉลาด
rajneesh


คำถามของคุณได้รับคำตอบแล้วใน ffmpeg คำถามที่พบบ่อย: ffmpeg.org/faq.html#How-can-I-join-video-files_003f
Palec

คำตอบ:


31

วิธีที่ดีที่สุดในการทำเช่นนี้ในปัจจุบันคือการใช้demuxer ที่ต่อกัน ก่อนอื่นให้สร้างไฟล์ที่มีinputs.txtรูปแบบดังนี้:

file '/path/to/input1.mp4'
file '/path/to/input2.mp4'
file '/path/to/input3.mp4'

จากนั้นเพียงเรียกใช้คำสั่ง ffmpeg นี้:

ffmpeg -f concat -i inputs.txt -c copy output.mp4

ดูเพิ่มเติมเรียงต่อกันในffmpegคำถามที่พบบ่อย


ฉันเก็บสิ่งต่อไปนี้ไว้ที่นี่เพื่อประโยชน์ของทุกคนที่ใช้ ffmpeg รุ่นเก่ากว่า

เวอร์ชั่นล่าสุดของ ffmpeg สามารถทำได้: คุณจะต้องนำไฟล์เข้าสู่สตรีมการขนส่ง mpeg ก่อน (เป็นตัวประมวลผลที่ค่อนข้างเบาเพราะมันเปลี่ยนรูปแบบคอนเทนเนอร์เท่านั้น):

ffmpeg -i input.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb middle.ts

หากนั่นทำให้เกิดข้อผิดพลาดเกี่ยวกับ h264 คุณอาจต้องใช้:

ffmpeg -i input.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb middle.ts

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

ffmpeg -i "concat:middle1.ts|middle2.ts|middle3.ts" -c copy output.mp4

หากนั่นทำให้เกิดข้อผิดพลาดเกี่ยวกับ aac คุณอาจต้องใช้

ffmpeg -i "concat:middle1.ts|middle2.ts|middle3.ts" -c copy -absf aac_adtstoasc output.mp4

หากระบบของคุณรองรับการตั้งชื่อไปป์คุณสามารถทำได้โดยไม่ต้องสร้างไฟล์ระดับกลาง

mkfifo temp0 temp1

คุณจะต้องทำสิ่งต่อไปนี้ในเทอร์มินัลเสมือนสามแห่ง:

ffmpeg -i input0.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb -y temp0
ffmpeg -i input1.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb -y temp1
ffmpeg -f mpegts -i "concat:temp0|temp1" -c copy -absf aac_adtstoasc output.mp4

หาก output.mp4 มีอยู่แล้ว ffmpeg ตัวที่สามจะถามคุณว่าคุณต้องการเขียนทับมันหรือไม่ แต่จะทำเช่นนี้หลังจากที่ได้เข้าถึง FIFOแล้วและนี่จะทำให้ ffmpegs ปิดก่อน ดังนั้นตรวจสอบให้แน่ใจว่าคุณเลือกชื่อที่ไม่ได้ใช้สำหรับไฟล์เอาต์พุตของคุณ

สิ่งนี้อาจไม่ทำงานหากไฟล์อินพุตของคุณแตกต่างกัน - ฉันเชื่อว่าความแตกต่างของอัตราบิตนั้นโอเค แต่ขนาดเฟรมอัตราเฟรม ฯลฯ จะต้องตรงกัน


เพื่อนร่วมงานจำนวนมากทำงานอย่างมีเสน่ห์
Jose Armando

2
@DaveJarvis ไม่เช่นนั้น นั่นคือข้อความที่จงใจและแพร่กระจายโดยเจตนาโดยทีม libav ซึ่งเป็นกลุ่มนักพัฒนาที่แยกออกจากโครงการ ffmpeg เพื่อพัฒนา avconv (และเปลี่ยนชื่อเพราะพวกเขาไม่สามารถรักษาความปลอดภัยลิขสิทธิ์ในชื่อ ffmpeg) ดูว่าใครสามารถบอกความแตกต่างและความสัมพันธ์ระหว่าง ffmpeg, libav และ avconv ให้ฉันได้บ้าง สำหรับข้อมูลเพิ่มเติมอีกเล็กน้อย
evilsoup

2
*** THIS PROGRAM IS DEPRECATED *** This program is only provided for compatibility and will be removed in a future release. Please use avconv instead.เป็นข้อความที่ฉันเห็นในขณะนี้ทำงานffmpeg
ลอร์ด Loh

@LordLoh เห็นลิงค์ในความคิดเห็นก่อนหน้าของฉัน
evilsoup

1
คำตอบนั้นไม่ตอบ - วิธีเชื่อมต่อ mp4 กับavconv
Lord Loh

7

สำหรับ mp4 ทางออกเดียวที่ฉันพบคือ MP4Box จากแพ็คเกจ gpac

#!/bin/bash
filesList=""
for file in $(ls *.mp4|sort -n);do
    filesList="$filesList -cat $file"
done
MP4Box $filesList -new merged_files_$(date +%Y%m%d_%H%M%S).mp4

หรือคำสั่งคือ

MP4Box -cat file1.mp4 -cat file2.mp4 -new mergedFile.mp4

ด้วย mencoder และ avconv ฉันไม่สามารถทำงานได้ :-(


2

ขอบคุณสำหรับสคริปต์ dvo มันเป็นจุดเริ่มต้นที่ดีสำหรับฉัน ฉันไม่มีปัญหากับการใช้ pipes ชื่อดังนั้นฉันจึงปรับสคริปต์ให้ใช้แทน นอกจากนี้ฉันแก้ไขให้ทำงานกับชื่อไฟล์พร้อมช่องว่างในนั้น เอาต์พุตไปยังไพพ์ที่ระบุชื่อจะไม่ "ไป" จนกว่าคุณจะเริ่มอ่านจากมันดังนั้นจึงจำเป็นต้องแบ็คกราวน์งาน remux เริ่มต้นด้วย & ก่อนการเรียกขั้นสุดท้ายไปยัง avconv นอกจากนี้อย่าลืมใช้สำเนา -c หรือคุณจบการเข้ารหัสล็อตใหม่

#!/bin/bash
for FILE in "$@"; do
   TAG=$(echo "$FILE" | sed -e s/[^A-Za-z0-9.]/_/g)
   mkfifo $TAG.mp4concatpipe
   avconv -loglevel "quiet" -i "$FILE"  -f mpegts -c copy -bsf h264_mp4toannexb -y $TAG.mp4concatpipe &
   IN=$IN\|$TAG.fifo
done

IN=${IN#\|}
avconv -i concat:$IN -c copy out.mp4
rm *.mp4concatpipe

ด้วยเหตุผลนี้ทำให้ฉันมีข้อผิดพลาด "concat: first.m4v.fifo | second.m4v.fifo: ไม่มีไฟล์หรือไดเรกทอรี" ข้อผิดพลาด .. แม้จะมีเครื่องหมายคำพูด (-i concat: "$ IN" หรือ -i "concat: $ IN ") MP4Box แนะนำในคำตอบอื่น ๆ ด้านล่าง แต่ใช้งานได้ดี!
vorburger

2

บน Mac (macOS 10.13 High Sierra) ฉันใช้สิ่งต่อไปนี้กับความสำเร็จ:

for f in *.m4a; do echo "file '$f'" >> mylist.txt; done

จัดลำดับใหม่ตามด้านบนหากจำเป็น (โปรดทราบว่า. / ถูกลบออก - พา ธ ทำให้ ffmpeg โยนข้อผิดพลาดชื่อไฟล์ที่ไม่ปลอดภัย)

จากนั้นฉันต้องลบตัวแปลงสัญญาณและบิตเรตที่ระบุสำหรับ MacPorts ffmpeg เริ่มต้นที่ชอบ:

ffmpeg -f concat -i mylist.txt  output.m4a

ยินดีต้อนรับสู่ Super User! โปรดอ่านคำถามอีกครั้งอย่างระมัดระวัง คำตอบของคุณไม่ตอบคำถามเดิมซึ่งเกี่ยวกับ linux หากคุณเชื่อว่ามันจะทำงานบน linux โปรดเพิ่มเพื่อให้ผู้อ่านในอนาคตมีความเข้าใจที่ชัดเจนนอกเหนือจากนั้นยินดีต้อนรับสู่ superuser และขอบคุณสำหรับข้อมูลของคุณอีกครั้ง โปรดใช้เวลาไม่กี่นาทีและอ่าน: - superuser.com/help .Answering: superuser.com/help/how-to-answer
mic84

2

นี่คือหนึ่งซับที่ทำให้ง่ายขึ้นมาก มันทำทุกอย่างที่จำเป็นต้องทำเพื่อให้คุณมีไฟล์วิดีโอที่ได้ในที่สุด ไชโย!

find *.mp4 | sed 's:\ :\\\ :g'| sed 's/^/file /' > list.txt; ffmpeg -f concat -i list.txt -c copy output.mp4; rm list.txts

1

ขอบคุณ evilsoup; ที่นี่เป็นสคริปต์เล็ก ๆ น้อย ๆ avconvนี้เพื่อทำให้กระบวนการที่ใช้เมื่อเร็ว
BTW ชื่อท่อไม่ได้ผลสำหรับฉัน (ผลลัพธ์ที่ได้ติดค้างอยู่)

   #!/bin/bash

    for FILE in $@; do
      avconv -i $FILE -map 0 -f mpegts -bsf h264_mp4toannexb -c:v copy -y $FILE.tmp
      IN=$IN\|$FILE.tmp
    done

    IN=${IN#\|}
    avconv -i concat:"$IN" out.mp4

    for FILE in $@; do
      rm $FILE.tmp
    done

0

ได้ประสบความสำเร็จอย่างยิ่งใหญ่ด้วย

for f in ./*.m4a; do echo "file '$f'" >> mylist.txt; done

คุณอาจต้องเรียงลำดับ mylist.txt อีกเล็กน้อย

ffmpeg -f concat -i mylist.txt -c:a libfdk_aac -b:a 128k output.m4a

ffmpeg เวอร์ชั่น 2.2.3

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