it-swarm.com.de

Wie kann ich ffmpeg verwenden, um MPEG-Videos in 10-Minuten-Blöcke aufzuteilen?

In der Open Source- oder aktiven Entwickler-Community besteht häufig die Notwendigkeit, große Videosegmente online zu veröffentlichen. (Meet-up-Videos, Campouts, technische Gespräche ...) Da ich Entwickler und kein Videofilmer bin, habe ich keine Lust, den zusätzlichen Kratzer auf einem Premium-Vimeo-Konto zu beseitigen. Wie nehme ich dann ein 12,5 GB (1:20:00) MPEG Tech Talk-Video und schneide es in 00:10:00 Segmente, um es einfach auf Video-Sharing-Sites hochzuladen?

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

Es wäre nicht schwer, dies in ein Skript zu packen, um es in einer Schleife zu tun.

Beachten Sie, dass, wenn Sie versuchen, die Anzahl der Iterationen basierend auf der von einem ffprobe -Aufruf ausgegebenen Dauer zu berechnen, dies aus dem Durchschnitt geschätzt wird Bitrate am Anfang des Clips und Dateigröße des Clips, es sei denn, Sie geben das Argument -count_frames an, wodurch die Operation erheblich verlangsamt wird.

Beachten Sie auch, dass die Position der Option -ss In der Befehlszeile matter ist. Wo ich es jetzt habe, ist langsam aber genau. Die erste Version dieser Antwort gab die Alternative schnell, aber ungena . Der verlinkte Artikel beschreibt auch eine meist schnelle, aber dennoch genaue Alternative, für die Sie mit ein wenig Komplexität bezahlen.

Abgesehen davon denke ich nicht, dass Sie wirklich genau 10 Minuten für jeden Clip schneiden möchten. Dadurch werden Schnitte genau in die Mitte von Sätzen gesetzt, sogar in Wörter. Ich denke, Sie sollten einen Video-Editor oder -Player verwenden, um natürliche Schnittpunkte in einem Abstand von knapp 10 Minuten zu finden.

Angenommen, deine Datei hat ein Format, das YouTube direkt akzeptieren kann, musst du nicht neu codieren, um Segmente zu erhalten. Übergeben Sie einfach die natürlichen Schnittpunkt-Offsets an ffmpeg und teilen Sie ihm mit, dass er das codierte A/V mit dem Codec "copy" unberührt durchlaufen soll:

$ 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
...

Das Argument -c copy Weist es an, alle Eingabestreams (Audio, Video und möglicherweise andere, z. B. Untertitel) unverändert in die Ausgabe zu kopieren. Bei einfachen A/V-Programmen entspricht dies den ausführlicheren Flags -c:v copy -c:a copy Oder den Flags im alten Stil -vcodec copy -acodec copy. Sie würden den ausführlicheren Stil verwenden, wenn Sie nur einen der Streams kopieren, den anderen jedoch neu codieren möchten. Vor vielen Jahren gab es beispielsweise bei QuickTime-Dateien eine gängige Praxis, das Video mit H.264-Video zu komprimieren, das Audio jedoch als nkomprimiertes PCM zu belassen. Wenn Sie heute auf eine solche Datei gestoßen sind, können Sie sie mit -c:v copy -c:a aac modernisieren, um nur den Audiostream erneut zu verarbeiten und das Video unberührt zu lassen.

Der Startpunkt für jeden Befehl oben nach dem ersten ist der Startpunkt des vorherigen Befehls plus die Dauer des vorherigen Befehls.

72
Warren Young

Hier ist die einzeilige Lösung :

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

Bitte beachten Sie, dass Sie dadurch keine genauen Aufteilungen erhalten, sondern Ihren Anforderungen entsprechen sollten. Es wird stattdessen beim ersten Frame nach der nach segment_time Angegebenen Zeit geschnitten, im obigen Code nach der 20-Minuten-Marke.

Wenn Sie feststellen, dass nur der erste Block spielbar ist, fügen Sie -reset_timestamps 1 Wie in den Kommentaren erwähnt hinzu.

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

Konnte das gleiche Problem früher und stellte ein einfaches Python Skript, um genau das zu tun (mit FFMpeg). Verfügbar hier: https://github.com/c0decracker/video-splitter und unten eingefügt:

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

Ein Alternate more readable Weg wäre

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
*/

Here's die Quelle und Liste der häufig verwendeten FFmpeg-Befehle.

4
Niket Pathak

Wenn Sie wirklich dieselben Chunks erstellen möchten, müssen Sie ffmpeg zwingen, einen i-Frame für den ersten Frame jedes Chunks zu erstellen, damit Sie diesen Befehl zum Erstellen eines 0,5-Sekunden-Chunks verwenden können.

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

Beachten Sie, dass die genaue Interpunktion des alternativen Formats -ss mm:ss.xxx Ist. Ich hatte stundenlang Mühe, den intuitiven, aber falschen mm:ss:xx Vergeblich zu verwenden.

$ man ffmpeg | grep -C1 position

-ss Position
Suchen Sie in Sekunden nach der angegebenen Zeitposition. Die Syntax "hh: mm: ss [.xxx]" wird ebenfalls unterstützt.

Referenzen hier und hier .

3
Mark Hudson

Verwenden Sie einfach das, was in ffmpeg integriert ist, um genau dies zu tun.

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

Dadurch wird es in ungefähr 2 Sekunden lange Spannfutter aufgeteilt, an den entsprechenden Keyframes aufgeteilt und in die Dateien cam_out_h2640001.mp4, cam_out_h2640002.mp4 usw. ausgegeben.

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