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

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

Ответ 1

В итоге я использовал 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

Ответ 2

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 -safe 0 -i mylist.txt -c copy output.mp4

Для 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.mkv

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

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

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

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

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

Если сомневаетесь, попробуйте демоверсию concat.

Также смотрите

Ответ 3

ДЛЯ 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, которые нужно объединить. Затем запустите пакетный файл. Обычно это займет не более десяти секунд.
,

Приложение (2018/10/21) -

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

Это работает на Windows 7, в пакетном файле. Не пытайтесь использовать его в командной строке, потому что он работает только в командном файле!

Ответ 4

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

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

"Ls" содержит файлы для присоединения. "Perl" создает файл конкатенации "на лету" в трубу. "-i -" часть сообщает ffmpeg читать из трубы

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

Ответ 5

для файлов 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

НОТА! : Результат будет как первый файл (а не второй)

Ответ 6

Я пытался объединить три аудиофайла .mp3 в один файл .m4a, и эта команда 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 означает использование функции конкатенации (объединения) мультимедиа.
n означает подтверждение общего количества входных файлов.
v означает видео? используйте 0 = нет видео, 1 = содержит видео.
a означает аудио? используйте 0 = нет звука, 1 = содержать аудио.
-f означает форматированный файл формата файла (для просмотра всех поддерживаемых форматов используйте ffmpeg -formats)
-vn означает отключить видео (а также -an отключит звук, если он не нужен)
-y означает перезаписывать выходные файлы (если выходной файл уже существует).

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

Ответ 7

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

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

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

Для конкатенации 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

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

Ответ 8

на основе ответов 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.

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

Ответ 9

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

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

пример...

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

Python

Используя некоторый код Python, чтобы сделать это с таким количеством mp4, которое есть в папке (установите python с python.org, скопируйте и вставьте и сохраните этот код в файл с именем mp4.py и запустите его из cmd, открытого в папке с python mp4.py и все mp4 в папке будут объединены)

import glob
import os

stringa = ""
for f in glob.glob("*.mp4"):
    stringa += f + "|"
os.system("ffmpeg -i \"concat:" + stringa + "\" -codec copy output.mp4")

Версия 2 с Python

Взято из моего сообщения в моем блоге, вот как я делаю это в 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()

Ответ 10

Я обнаружил, что оператор трубы не работал для меня, когда использовал вариант 3 для согласования нескольких MP4 на Mac в принятом ответе.

Следующий однострочный лайнер работает на Mac (High Sierra) для конкатенации mp4s, без необходимости создания промежуточного файла.

ffmpeg -f concat -safe 0 -i <(for f in./*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4

Ответ 11

Вот script я сделал, чтобы объединить несколько 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}

Ответ 12

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

Ответ 13

Я обнаружил, что для файлов .mp4 лучше и быстрее использовать инструмент командной строки с открытым исходным кодом: mp4box. Тогда Вы можете использовать это следующим образом:

mp4box.exe -add video1.mp4 -cat video2.mp4 destvideo.mp4

Загрузите его здесь для большинства платформ: 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

Объединение всех файлов 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

Ответ 16

После различных попыток ниже скрипт работал на 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

Здесь первые две строки создают текстовый файл temp.txt, который имеет следующий контент

file 'e:\first.mp4'
file 'e:\second.mp4'

3-я, 4-я строки проверяют, доступен ли ffmpeg в path, и создают "join.mp4"

Основные отличия от других ответов приведены ниже

usage  of -bsf:v hevc_mp4toannexb -an

для моего файла mp4 выше, возможно, вам придется использовать другие альтернативы, как показано ниже, в зависимости от вашей кодировки видео.

h264_mp4toannexb

Все такие возможные фильтры битового потока можно найти по адресу https://ffmpeg.org/ffmpeg-bitstream-filters.html.

Ответ 17

Вот мой метод для соединения каталога, полного файлов MP4, с использованием подстановки команд и фильтра видео concat (это перекодирует) - подумал, что кому-то еще пригодится эта строка, особенно если у вас много файлов (я просто присоединились 17 файлов одним махом)

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

Обратите внимание, что эта команда объединяет ваши файлы в том порядке, в котором они названы (то есть в том же порядке, в котором они представлены, если вы запускаете ls *.mp4) - в моем случае у каждого из них был номер дорожки, поэтому она отлично работала.

Ответ 18

Протокол Concat описан здесь; https://trac.ffmpeg.org/wiki/Concatenate#protocol

Когда реализовано использование именованных каналов, чтобы избежать промежуточных файлов

Очень быстрый (читай: мгновенный), без пропущенных кадров и работает хорошо.

Не забудьте удалить файлы именованных каналов и не забудьте проверить, является ли видео H264 и AAC, что вы можете сделать только с помощью ffmpeg -i filename.mp4 (проверьте h264 и упоминания aac)