27

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

6 ответов6

26

Лучший способ сделать это в настоящее время - использовать демоверсию concat. Сначала создайте файл с именем 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 FAQ.


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

Это может не сработать, если ваши входные файлы отличаются - я считаю, что различия в битрейте в порядке, но размер кадра, частота кадров и т.д. Должны совпадать.

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

На Mac (macOS 10.13 High Sierra) я успешно использовал следующее:

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

При необходимости измените порядок так же, как указано выше (обратите внимание, что ./ был удален - путь заставлял ffmpeg выдавать ошибку небезопасного имени файла).

Затем мне пришлось удалить кодек и битрейт, указанные для fPmpeg MacPorts по умолчанию, чтобы он мне понравился:

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

Спасибо за сценарий dvo. Это была отличная отправная точка для меня. У меня не было проблем с использованием именованных каналов, поэтому я адаптировал скрипт для их использования. Плюс я исправил это, чтобы работать с именами файлов с пробелами в них. Вывод в именованные каналы не "идет" до тех пор, пока вы не начнете читать из них, поэтому необходимо задавать фоновые задачи начального remux с помощью & перед последним вызовом avconv. Кроме того, не забывайте использовать -c copy, иначе вы перекодируете всю партию.

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

Спасибо зла Вот небольшой скрипт для автоматизации процесса с использованием более поздней avconv .
Кстати, именованные каналы у меня не сработали (вывод на них застрял).

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .