问题
I'm trying to concatenate two mp4 files using ffmpeg. I need this to be an automatic process hence why I chose ffmpeg. I'm converting the two files into .ts files and then concatenating them and then trying to encode that concated .ts file. The files are h264 and aac encoded and I'm hoping to keep the quality the same or as close to original as possible.
ffmpeg -i part1.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part1.ts
ffmpeg -i part2.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part2.ts
cat part1.ts part2.ts > parts.ts
ffmpeg -y -i parts.ts -acodec copy -ar 44100 -ab 96k -coder ac -vbsf h264_mp4toannexb parts.mp4
Unfortunately I'm getting the following error message coming back from ffmpeg during encoding:
[h264 @ 0x1012600]sps_id out of range
[h264 @ 0x1012600]non-existing SPS 0 referenced in buffering period
[h264 @ 0x1012600]sps_id out of range
[h264 @ 0x1012600]non-existing SPS 0 referenced in buffering period
[NULL @ 0x101d600]error, non monotone timestamps 13779431 >= 13779431kbits/s
av_interleaved_write_frame(): Error while opening file
This happens about half way through encoding which makes me think that you can't concat two .ts files together and have it work.
回答1:
FFmpeg has three concatenation methods:
1. concat video filter
ffmpeg -i opening.mkv -i episode.mkv -i ending.mkv \
-filter_complex "[0:v] [0:a] [1:v] [1:a] [2:v] [2:a] concat=n=3:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" output.mkv
Note that this method performs a re-encode.
2. concat demuxer
$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'
$ ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4
For Windows:
(echo file 'first file.mp4' & echo file 'second file.mp4' )>list.txt
ffmpeg -safe 0 -f concat -i list.txt -c copy output.mp4
3. concat protocol
ffmpeg -i "concat:input1|input2" -codec copy output.mkv
This method does not work for many formats, including MP4, due to the nature of these formats and the simplistic concatenation performed by this method.
Which one to use
concat filter: Use if your inputs do not have the same parameters (width, height, etc), or are not the same formats/codecs, or if you want to perform any filtering. (You could re-encode just the inputs that don't match so they share the same codec and other parameters, then use the concat demuxer to avoid re-encoding the other inputs).
concat demuxer: Use when you want to avoid a re-encode and your format does not support file level concatenation (most files used by general users do not support file level concatenation).
concat protocol: Use with formats that support file level concatenation (MPEG-1, MPEG-2 PS, DV). Do not use with MP4.
If in doubt try the concat demuxer.
Also see
- FFmpeg FAQ: How can I join video files?
- FFmpeg Wiki: How to concatenate (join, merge) media files
回答2:
FOR MP4 FILES
For .mp4 files (which I obtained from DailyMotion.com: a 50 minute tv episode, downloadable only in three parts, as three .mp4 video files) the following was an effective solution for Windows 7, and does NOT involve re-encoding the files.
I renamed the files (as file1.mp4, file2.mp4, file3.mp4) such that the parts were in the correct order for viewing the complete tv episode.
Then I created a simple batch file (concat.bat), with the following contents:
:: Create File List
echo file file1.mp4 > mylist.txt
echo file file2.mp4 >> mylist.txt
echo file file3.mp4 >> mylist.txt
:: Concatenate Files
ffmpeg -f concat -i mylist.txt -c copy output.mp4
The batch file, and ffmpeg.exe, must both be put in the same folder as the .mp4 files to be joined. Then run the batch file. It will typically take less than ten seconds to run.
.
Addendum (2018/10/21) -
If what you were looking for is a method for specifying all the mp4 files in the current folder without a lot of retyping, try this in your Windows batch file instead (MUST include the option -safe 0):
:: Create File List
for %%i in (*.mp4) do echo file '%%i'>> mylist.txt
:: Concatenate Files
ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4
This works on Windows 7, in a batch file. Don't try using it on the command line, because it only works in a batch file!
回答3:
Here's a fast (takes less than 1 minute) and lossless way to do this without needing intermediate files:
ls Movie_Part_1.mp4 Movie_Part_2.mp4 | perl -ne 'print "file $_"' | ffmpeg -f concat -i - -c copy Movie_Joined.mp4
The "ls" contains the files to join The "perl" creates the concatenation file on-the-fly into a pipe The "-i -" part tells ffmpeg to read from the pipe
(note - my files had no spaces or weird stuff in them - you'll need appropriate shell-escaping if you want to do this idea with "hard" files).
回答4:
for MP4 files:
If they are not exactly same (100% same codec, same resolution, same type) MP4 files, then you have to trans-code them into intermediate streams at first:
ffmpeg -i myfile1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp1.ts
ffmpeg -i myfile2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp2.ts
// now join
ffmpeg -i "concat:temp1.ts|temp2.ts" -c copy -bsf:a aac_adtstoasc output.mp4
NOTE!: Output will be like first file ( and not a second one)
回答5:
I was trying to concatenate three .mp3
audio files into one .m4a
file and this ffmpeg command works.
Input command:
ffmpeg -i input1.mp3 -i input2.mp3 -i input3.mp3 -filter_complex concat=n=3:v=0:a=1 -f MOV -vn -y input.m4a
Meanings of :
" -filter_complex concat=n=3:v=0:a=1" :
concat means use the media concatenate (joining) function.
n means confirm total count of input files.
v means has video? use 0 = no video, 1 = contains video.
a means has audio? use 0 = no audio, 1 = contain audio.
-f means force set file format (to see all supported formats, useffmpeg -formats
)
-vn means disable video (and also-an
would disable audio if not wanted)
-y means overwrite output files (if the output file already exists).
For more info: use ffmpeg -h full
print all options (including all format and codec specific options, very long)
回答6:
I ended up using mpg as the intermediate format and it worked (NOTE this is a dangerous example, -qscale 0 will re-encode the video...)
ffmpeg -i 1.mp4 -qscale 0 1.mpg
ffmpeg -i 2.mp4 -qscale 0 2.mpg
cat 1.mpg 2.mpg | ffmpeg -f mpeg -i - -qscale 0 -vcodec mpeg4 output.mp4
回答7:
Detailed documentation on various ways of concatenation in ffmpeg can be found here.
You can use 'Concat filter' for quick concatenation.
It performs a re-encode. This option is best when inputs have different video/audio formats.
For Concatenating 2 files:
ffmpeg -i input1.mp4 -i input2.webm \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0] concat=n=2:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" output.mp4
For Concatenating 3 files:
ffmpeg -i input1.mp4 -i input2.webm -i input3.mp4 \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0] [2:v:0] [2:a:0] concat=n=3:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" output.mp4
This works for same as well as multiple input file types.
回答8:
this worked for me (on windows)
ffmpeg -i "concat:input1|input2" -codec copy output
an example...
ffmpeg -i "concat:01.mp4|02.mp4" -codec copy output.mp4
Python
Using some python code to do it with as many mp4 there are in a folder (install python from python.org, copy and paste and save this code into a file called mp4.py and run it from the cmd opened in the folder with python mp4.py and all the mp4 in the folder will be concatenated)
import glob
import os
stringa = ""
for f in glob.glob("*.mp4"):
stringa += f + "|"
os.system("ffmpeg -i \"concat:" + stringa + "\" -codec copy output.mp4")
Version 2 with Python
Taken from my post on my blog, this is how I do it in python:
import os
import glob
def concatenate():
stringa = "ffmpeg -i \"concat:"
elenco_video = glob.glob("*.mp4")
elenco_file_temp = []
for f in elenco_video:
file = "temp" + str(elenco_video.index(f) + 1) + ".ts"
os.system("ffmpeg -i " + f + " -c copy -bsf:v h264_mp4toannexb -f mpegts " + file)
elenco_file_temp.append(file)
print(elenco_file_temp)
for f in elenco_file_temp:
stringa += f
if elenco_file_temp.index(f) != len(elenco_file_temp)-1:
stringa += "|"
else:
stringa += "\" -c copy -bsf:a aac_adtstoasc output.mp4"
print(stringa)
os.system(stringa)
concatenate()
回答9:
based on rogerdpack's and Ed999's responses, I've created my .sh version
#!/bin/bash
[ -e list.txt ] && rm list.txt
for f in *.mp4
do
echo "file $f" >> list.txt
done
ffmpeg -f concat -i list.txt -c copy joined-out.mp4 && rm list.txt
it joins all the *.mp4
files in current folder into joined-out.mp4
tested on mac.
resulting filesize is exact sum of my 60 tested files. Should not be any loss. Just what I needed
回答10:
I found the pipe operator did not work for me when using option 3 to concat several MP4s on a Mac in the accepted answer.
The following one-liner works on a Mac (High Sierra) to concatenate mp4s, with no intermediary file creation required.
ffmpeg -f concat -safe 0 -i <(for f in ./*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4
回答11:
Here is a script I made to concatenate several GoPro mp4's into a 720p mp4. Hope it's of help.
#!/bin/sh
cmd="( "
for i; do
cmd="${cmd}ffmpeg -i $i -ab 256000 -vb 10000000 -mbd rd -trellis 2 -cmp 2 -subcmp 2 -g 100 -f mpeg -; "
done
cmd="${cmd} ) | ffmpeg -i - -vb 10000000 -ab 256000 -s 1280x720 -y out-`date +%F-%H%M.%S`.mp4"
echo "${cmd}"
eval ${cmd}
回答12:
From the documentation here: https://trac.ffmpeg.org/wiki/Concatenate
If you have MP4 files, these could be losslessly concatenated by first transcoding them to MPEG-2 transport streams. With H.264 video and AAC audio, the following can be used:
ffmpeg -i input1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
ffmpeg -i input2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc output.mp4
This approach works on all platforms.
I needed the ability to encapsulate this in a cross platform script, so I used fluent-ffmpeg
and came up with the following solution:
const unlink = path =>
new Promise((resolve, reject) =>
fs.unlink(path, err => (err ? reject(err) : resolve()))
)
const createIntermediate = file =>
new Promise((resolve, reject) => {
const out = `${Math.random()
.toString(13)
.slice(2)}.ts`
ffmpeg(file)
.outputOptions('-c', 'copy', '-bsf:v', 'h264_mp4toannexb', '-f', 'mpegts')
.output(out)
.on('end', () => resolve(out))
.on('error', reject)
.run()
})
const concat = async (files, output) => {
const names = await Promise.all(files.map(createIntermediate))
const namesString = names.join('|')
await new Promise((resolve, reject) =>
ffmpeg(`concat:${namesString}`)
.outputOptions('-c', 'copy', '-bsf:a', 'aac_adtstoasc')
.output(output)
.on('end', resolve)
.on('error', reject)
.run()
)
names.map(unlink)
}
concat(['file1.mp4', 'file2.mp4', 'file3.mp4'], 'output.mp4').then(() =>
console.log('done!')
)
回答13:
For .mp4
files, I found it works better and faster to use the opensource command line tool: mp4box
. Then You can use it this way:
mp4box.exe -add video1.mp4 -cat video2.mp4 destvideo.mp4
Download it here for most platforms: https://gpac.wp.imt.fr/mp4box/
回答14:
ffmpeg \
-i input_1.mp4 \
-i input_2.mp4 \
-filter_complex '[0:v]pad=iw*2:ih[int];[int][1:v]overlay=W/2:0[vid]' \
-map [vid] \
-c:v libx264 \
-crf 23 \
-preset veryfast \
output.mp4
回答15:
Merging all mp4 files from current directory
I personnaly like not creating external file that I have to delete afterwards, so my solution was following which includes files numbering listing (like file_1_name, file_2_name, file_10_name, file_20_name, file_100_name
, ...)
#!/bin/bash
filesList=""
for file in $(ls -1v *.mp4);do #lists even numbered file
filesList="${filesList}${file}|"
done
filesList=${filesList%?} # removes trailing pipe
ffmpeg -i "concat:$filesList" -c copy $(date +%Y%m%d_%H%M%S)_merged.mp4
回答16:
After various tries below script worked for me on windows 10 powershell.
$files=Get-ChildItem -path e:\ -Filter *.mp4
$files| ForEach-Object {"file '$($_.FullName)'"}| Out-File -FilePath e:\temp.txt -Encoding ASCII
if (-not (test-path "e:\ffmpeg\bin\ffmpeg.exe")) {throw "e:\ffmpeg\bin\ffmpeg.exe needed"}
E:\ffmpeg\bin\ffmpeg.exe -safe 0 -f concat -i "e:\temp.txt" -c copy -bsf:v hevc_mp4toannexb -an e:\joined.mp4
# Conversion Cleanup
Remove-Item e:\temp.txt
Here first two lines create a text file temp.txt which has following content
file 'e:\first.mp4'
file 'e:\second.mp4'
3rd, 4th lines checks if ffmpeg is available at path and create the "joined.mp4"
The key differences from other answers are as below
usage of -bsf:v hevc_mp4toannexb -an
for my mp4 file above worked, you may need to use other alternatives like below depending on your video encoding.
h264_mp4toannexb
All such possible Bitstream filters can be found at https://ffmpeg.org/ffmpeg-bitstream-filters.html
回答17:
Here's my method for joining a directory full of MP4 files using command substitution and the concat video filter (this will re-encode) - figured someone else will get some use out of this one-liner, especially if you have many files (I just joined 17 files in one fell swoop):
ffmpeg $(for f in *.mp4 ; do echo -n "-i $f "; done) -filter_complex \
"$(i=0 ; for f in *.mp4 ; do echo -n "[$i:v] [$i:a] " ; i=$((i+1)) ; done \
&& echo "concat=n=$i:v=1:a=1 [v] [a]")" -map "[v]" -map "[a]" output.mp4
N.B. this command joins your files in the order in which they're named (i.e. the same order as they're presented if you run ls *.mp4
) - in my case, they each had a track number, so it worked great.
回答18:
The concat protocol described here; https://trac.ffmpeg.org/wiki/Concatenate#protocol
When implemented using named pipes to avoid intermediate files
Is very fast (read: instant), has no frames dropped, and works well.
Remember to delete the named pipe files and remember to check if the video is H264 and AAC which you can do with just ffmpeg -i filename.mp4
(check for h264 and aac mentions)
来源:https://stackoverflow.com/questions/48993190/can-i-add-any-independent-two-mp4-files-in-java-without-any-corruptness-or-data