it-swarm-ru.tech

Как я могу использовать ffmpeg для разделения видео MPEG на 10-минутные куски?

В сообществе разработчиков программного обеспечения с открытым исходным кодом или в активном сообществе разработчиков часто возникает необходимость публиковать большие видеофрагменты в Интернете. (Видео для встреч, лагеря, технические разговоры ...) Поскольку я являюсь разработчиком, а не видеографом, у меня нет желания раскошелиться на дополнительную премию в премиум-аккаунте Vimeo. Как мне тогда взять техническое видео MPEG 12,5 ГБ (1:20:00) и разделить его на сегменты 00:10:00 для удобной загрузки на сайты обмена видео?

73
Gabriel
$ ffmpeg -i source-file.foo -ss 0 -t 600 first-10-min.m4v
$ ffmpeg -i source-file.foo -ss 600 -t 600 second-10-min.m4v
$ ffmpeg -i source-file.foo -ss 1200 -t 600 third-10-min.m4v
...

Свернуть это в скрипт, чтобы сделать это в цикле, не составит труда.

Помните, что если вы попытаетесь рассчитать количество итераций на основе длительности, полученной при вызове ffprobe, это оценивается из среднего значения битрейт в начале клипа и размер файла клипа, если вы не указали -count_frames аргумент, который значительно замедляет работу.

Еще одна вещь, о которой следует знать, это то, что положение -ss опция в командной строке имеет значение . Где у меня это сейчас медленно, но точно. Первая версия этого ответа дала альтернативу быстро, но неточно . В связанной статье также описывается в основном быстрая, но все же точная альтернатива, за которую вы платите с небольшой сложностью.

Несмотря на это, я не думаю, что вы действительно хотите вырезать ровно 10 минут для каждого клипа. Это поместит сокращения прямо в середину предложений, даже слов. Я думаю, что вы должны использовать видеоредактор или проигрыватель, чтобы найти естественные точки среза, не дойдя до 10 минут.

Предполагая, что ваш файл находится в формате, который YouTube может принять напрямую, вам не нужно перекодировать, чтобы получить сегменты. Просто передайте естественное смещение точки вырезания ffmpeg, сказав ему пропустить закодированный A/V без изменений, используя кодек "copy":

$ ffmpeg -i source.m4v -ss 0 -t 593.3 -c copy part1.m4v
$ ffmpeg -i source.m4v -ss 593.3 -t 551.64 -c copy part2.m4v
$ ffmpeg -i source.m4v -ss 1144.94 -t 581.25 -c copy part3.m4v
...

-c copy Аргумент говорит ему скопировать все входные потоки (аудио, видео и, возможно, другие, такие как субтитры) в вывод как есть. Для простых A/V программ это эквивалентно более многословным флагам -c:v copy -c:a copy или флаги старого стиля -vcodec copy -acodec copy. Вы бы использовали более многословный стиль, когда хотите скопировать только один из потоков, но перекодировать другой. Например, много лет назад была распространена практика с файлами QuickTime сжимать видео с видео H.264, но оставлять аудио как несжатый PCM ; если вы натолкнулись на такой файл сегодня, вы можете обновить его с помощью -c:v copy -c:a aac для повторной обработки только аудиопотока, оставляя видео нетронутым.

Начальная точка для каждой команды выше после первой является начальной точкой предыдущей команды плюс продолжительность предыдущей команды.

72
Warren Young

Вот решение в одну строку :

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment output%03d.mp4

Обратите внимание, что это не дает точного разделения, но должно соответствовать вашим потребностям. Вместо этого он обрезает первый кадр по истечении времени, указанного после segment_time, в приведенном выше коде это будет после 20-минутной отметки.

Если вы обнаружите, что воспроизводится только первый блок, попробуйте добавить -reset_timestamps 1 как указано в комментариях.

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4
68
Jon

Столкнулся с той же проблемой ранее и собрал простой Python скрипт, чтобы сделать именно это (используя FFMpeg). Доступно здесь: https://github.com/c0decracker/video-splitter и вставлено ниже:

#!/usr/bin/env python
import subprocess
import re
import math
from optparse import OptionParser
length_regexp = 'Duration: (\d{2}):(\d{2}):(\d{2})\.\d+,'
re_length = re.compile(length_regexp)
def main():
    (filename, split_length) = parse_options()
    if split_length <= 0:
        print "Split length can't be 0"
        raise SystemExit
    output = subprocess.Popen("ffmpeg -i '"+filename+"' 2>&1 | grep 'Duration'",
                              Shell = True,
                              stdout = subprocess.PIPE
    ).stdout.read()
    print output
    matches = re_length.search(output)
    if matches:
        video_length = int(matches.group(1)) * 3600 + \
                       int(matches.group(2)) * 60 + \
                       int(matches.group(3))
        print "Video length in seconds: "+str(video_length)
    else:
        print "Can't determine video length."
        raise SystemExit
    split_count = int(math.ceil(video_length/float(split_length)))
    if(split_count == 1):
        print "Video length is less then the target split length."
        raise SystemExit
    split_cmd = "ffmpeg -i '"+filename+"' -vcodec copy "
    for n in range(0, split_count):
        split_str = ""
        if n == 0:
            split_start = 0
        else:
            split_start = split_length * n
            split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
                         " '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
                         "'"
    print "About to run: "+split_cmd+split_str
    output = subprocess.Popen(split_cmd+split_str, Shell = True, stdout =
                              subprocess.PIPE).stdout.read()
def parse_options():
    parser = OptionParser()
    parser.add_option("-f", "--file",
                      dest = "filename",
                      help = "file to split, for example sample.avi",
                      type = "string",
                      action = "store"
    )
    parser.add_option("-s", "--split-size",
                      dest = "split_size",
                      help = "split or chunk size in seconds, for example 10",
                      type = "int",
                      action = "store"
    )
    (options, args) = parser.parse_args()
    if options.filename and options.split_size:
        return (options.filename, options.split_size)
    else:
        parser.print_help()
        raise SystemExit
if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print "Exception occured running main():"
        print str(e)
7
c0decracker

Альтернативный более читаемый способ будет

ffmpeg -i input.mp4 -ss 00:00:00 -to 00:10:00 -c copy output1.mp4
ffmpeg -i input.mp4 -ss 00:10:00 -to 00:20:00 -c copy output2.mp4

/**
* -i  input file
* -ss start time in seconds or in hh:mm:ss
* -to end time in seconds or in hh:mm:ss
* -c codec to use
*/

Здесь источник и список часто используемых команд FFmpeg.

4
Niket Pathak

Если вы хотите создать действительно одинаковые чанки, необходимо заставить ffmpeg создавать i-кадр в первом кадре каждого чанка, чтобы вы могли использовать эту команду для создания 0,5-секундного чанка.

ffmpeg -hide_banner  -err_detect ignore_err -i input.mp4 -r 24 -codec:v libx264  -vsync 1  -codec:a aac  -ac 2  -ar 48k  -f segment   -preset fast  -segment_format mpegts  -segment_time 0.5 -force_key_frames  "expr: gte(t, n_forced * 0.5)" out%d.mkv
4
alireza akbaribayat

Обратите внимание, точная пунктуация альтернативного формата -ss mm:ss.xxx. Я боролся в течение нескольких часов, пытаясь использовать интуитивное, но неправильное mm:ss:xx но безрезультатно.

$ man ffmpeg | grep -C1 position

позиция
Поиск заданной временной позиции в секундах. Синтаксис "чч: мм: сс [.xxx]" также поддерживается.

Ссылки здесь и здесь .

3
Mark Hudson

Просто используйте то, что встроено в ffmpeg, чтобы сделать именно это.

ffmpeg -i invid.mp4 -threads 3 -vcodec copy -f segment -segment_time 2 cam_out_h264%04d.mp4

Это разделит его примерно на 2 секунды, разделит на соответствующие ключевые кадры и выведет в файлы cam_out_h2640001.mp4, cam_out_h2640002.mp4 и т.д.

1
John Allard
#!/bin/bash

if [ "X$1" == "X" ]; then
    echo "No file name for split, exiting ..."
    exit 1
fi

if [ ! -f "$1" ]; then
    echo "The file '$1' doesn't exist. exiting ..."
    exit 1
fi

duration=$(ffmpeg -i "$1" 2>&1 | grep Duration | sed 's/^.*Duration: \(.*\)\..., start.*$/\1/' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')      #'
split_time=${split_time:-55}
time=0
part=1

filename=${file%%.*}
postfix=${file##*.}

while [ ${time} -le ${duration} ]; do

echo    ffmpeg -i "$1" -vcodec copy -ss ${time} -t ${split_time} "${filename}-${part}.${postfix}"
    (( part++ ))
    (( time = time + split_time ))

done
0
Alex_Shilli