我正在尝试使用ffmpeg连接两个mp4文件。我需要这是一个自动过程,因此我选择了ffmpeg。我将这两个文件转换为.ts文件,然后将它们连接起来,然后尝试对连接的.ts文件进行编码。文件是h264和aac编码的,我希望保持质量相同或尽可能接近原始。
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
不幸的是,我在编码过程中从ffmpeg返回以下错误消息:
[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
这种情况大约发生在编码的一半,这让我认为不能将两个.ts文件合并在一起并使其正常工作。
根据此处的文档:https://trac.ffmpeg.org/wiki/Concatenate
如果您有MP4文件,可以通过首先将它们转码为MPEG-2传输流来无损地连接这些文件。对于H.264视频和AAC音频,可以使用以下内容:
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
这种方法适用于所有平台。
我需要将其封装在跨平台脚本中的能力,因此我使用了流利的ffmpeg并提出了以下解决方案:
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!')
)