Как объединить два файла MP4 с помощью FFmpeg?


Я пытаюсь объединить два файла mp4 с помощью ffmpeg. Мне нужно, чтобы это был автоматический процесс, поэтому я выбрал ffmpeg. Я конвертирую эти два файла .файлы 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 файлы вместе и заставить его работать.

13 200

13 ответов:

FFmpeg имеет три метода конкатенации.

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

отметьте, что этот метод выполняет повторное кодирование.

2. concat demuxer

$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'

$ ffmpeg -f concat -i mylist.txt -c copy output

для 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

ffmpeg -i "concat:input1|input2" -codec copy output

этот метод не работает для многих форматов, в том числе MP4, из-за природы этих форматов и упрощенного конкатенация выполняется этим методом.

какой из них использовать

  • concat filter: используйте, если ваши входные данные не имеют одинаковых параметров (ширина, высота и т. д.), Или не являются одинаковыми форматами/кодеками, или если вы хотите выполнить какую-либо фильтрацию. (Вы можете перекодировать только те входы, которые не совпадают, поэтому они используют один и тот же кодек и другие параметры, а затем использовать демуксер concat, чтобы избежать перекодирования другого входной.)

  • concat demuxer: используйте, когда вы хотите избежать повторного кодирования и формата не поддерживает конкатенацию на уровне файлов (большинство файлов, используемых обычными пользователями, не поддерживают конкатенацию на уровне файлов).

  • протоколом функция concat: используйте с форматами, которые поддерживают конкатенацию на уровне файлов (MPEG-1, MPEG-2 PS, DV). Не используйте с MP4.

если вы сомневаетесь, попробуйте конкат демультиплексора.

см. Также

вот быстрый (занимает менее 1 минуты) и без потерь способ сделать это без необходимости промежуточных файлов: -

ls Movie_Part_1.mp4 Movie_Part_2.mp4 | perl -ne 'print "file $_"' | ffmpeg -f concat -i - -c copy Movie_Joined.mp4

"ls" содержит файлы для присоединения Этот "перл" создает файл конкатенации на лету в трубу Часть "- i - " говорит ffmpeg читать из трубы

(Примечание-мои файлы не имели пробелов или странных вещей в них - вам понадобится соответствующая оболочка-экранирование, если вы хотите сделать эту идею с "жесткими" файлами).

в итоге я использовал mpg в качестве промежуточного формата, и он работал (обратите внимание, что это опасный пример,- qscale 0 перекодирует видео...)

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

для файлов MP4:

если они не совсем одинаковы (100% тот же кодек, то же разрешение, тот же тип) MP4 файлы, то вы должны сначала перекодировать их в промежуточные потоки:

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

результат: выход будет вести себя как первый файл ( а не второй)

Я пытался объединить три .mp3 аудиофайлы в один .m4a file и эта команда ffmpeg работает.

ввод команды:

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

значения : "- filter_complex concat=n=3: v=0: a=1":

функция concat означает использование функции Media concatenate (присоединение).
n означает подтверждение общего количества входных файлов.
v означает есть видео? используйте 0 = Нет видео, 1 = содержит видео.
a означает аудио? используйте 0 = Нет звука, 1 = содержит звук.
- f означает принудительно установить формат файла (чтобы увидеть все поддерживаемые форматы, используйте ffmpeg -formats)
- vn означает отключить видео (а также -an отключил бы звук, если бы не хотел)
- y означает перезапись выходных файлов (если выходной файл уже существует).

для получения дополнительной информации: используйте ffmpeg -h full печать всех параметров (включая все параметры формата и кодека, очень длинные)

ДЛЯ ФАЙЛОВ MP4

для . mp4 файлов (которые я получил от DailyMotion.com: 50-минутный телевизионный эпизод, загружаемый только в трех частях, как три видеофайла. mp4) следующее было эффективным решением для Windows 7, и не включить перекодирование файлов.

я переименовал файлы (как file1. mp4, file2. mp4, file3. mp4) такие, чтобы детали были в правильном порядке для просмотра всего телевизионного эпизода.

тогда я создал простой пакетным файлом (concat.bat), со следующим содержанием:

:: 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

на пакетным файлом и ffmpeg.exe, оба должны быть помещены в ту же папку, что и.mp4 файлы для присоединения. Затем запустите пакетный файл. Это, как правило, занимает менее десяти секунд, чтобы запустить.

подробная документация по различным способам конкатенации в ffmpeg может быть найти здесь.

можно использовать 'Concat filter' для быстрой конкатенации.

он выполняет повторное кодирование. Эта опция лучше всего подходит, когда входы имеют различные видео/аудио форматы.

для объединения 2-х файлов:

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

для объединения 3 файлы:

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

это работает для одной а также нескольких типов входных файлов.

вот сценарий, который я сделал, чтобы объединить несколько GoPro mp4 в 720p mp4. Надеюсь, это поможет.

#!/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}

основываясь на ответах rogerdpack и Ed999, я создал свою версию. sh

#!/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

он присоединяется ко всем *.mp4 файлы в текущей папке в joined-out.mp4

протестировано на mac.

результирующий размер файла-это точная сумма моих 60 проверенных файлов. Не должно быть никаких потерь. Только то, что мне нужно

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

объединение всех файлов mp4 из текущего каталога

мне лично нравится не создавать внешний файл, который я должен удалить впоследствии, поэтому мое решение было следующим, которое включает в себя список нумерации файлов (например,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

это сработало для меня (на windows)

ffmpeg -i "concat:input1|input2" -codec copy output

в частности

ffmpeg -i "concat:01.mp4|02.mp4" -codec copy output.mp4

Python

используя некоторый код python, чтобы сделать это с таким количеством mp4 есть в папке импорт Глоб импорт ОС

stringa = ""
for f in glob.glob("*.mp4"):
    stringa += f + "|"

os.system("ffmpeg -i \"concat:" + stringa + "\" -codec copy output.mp4")

из документации здесь: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

этот подход работает на всех платформах.

мне нужна была возможность инкапсулировать это в кросс-платформенный скрипт, поэтому я использовал fluent-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!')
)