web-dev-qa-db-fra.com

Comment puis-je utiliser ffmpeg pour diviser une vidéo MPEG en morceaux de 10 minutes?

Il est souvent nécessaire dans la communauté open source ou les développeurs actifs de publier de gros segments vidéo en ligne. (Meet-up vidéos, campouts, discussions techniques ...) Étant donné que je suis développeur et non vidéaste, je n'ai aucune envie de débourser le scratch supplémentaire sur un compte Vimeo premium. Comment puis-je prendre une vidéo de conversation technique MPEG de 12,5 Go (1:20:00) et la découper en segments 00:10:00 pour un téléchargement facile sur des sites de partage vidéo?

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

Envelopper cela dans un script pour le faire en boucle ne serait pas difficile.

Sachez que si vous essayez de calculer le nombre d'itérations en fonction de la durée de sortie d'un appel ffprobe qu'il s'agit estimé de la moyenne le débit binaire au début du clip et la taille du fichier du clip, sauf si vous donnez le -count_frames argument, ce qui ralentit considérablement son fonctionnement.

Une autre chose à savoir est que la position du -ss option sur la ligne de commande importe . Là où je l'ai maintenant, c'est lent mais précis. La première version de cette réponse a donné l'alternative rapide mais inexacte . L'article lié décrit également une alternative principalement rapide mais toujours précise, que vous payez avec un peu de complexité.

Tout cela mis à part, je ne pense pas que vous vouliez vraiment couper à exactement 10 minutes pour chaque clip. Cela mettra des coupes au milieu des phrases, même des mots. Je pense que vous devriez utiliser un éditeur ou un lecteur vidéo pour trouver des points de coupure naturels juste à moins de 10 minutes d'intervalle.

En supposant que votre fichier est dans un format que YouTube peut accepter directement, vous n'avez pas besoin de recoder pour obtenir des segments. Passez simplement les décalages de point de coupure naturels à ffmpeg, en lui disant de passer l'A/V encodé sans les toucher en utilisant le codec "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
...

Le -c copy L'argument lui dit de copier tous les flux d'entrée (audio, vidéo et potentiellement d'autres, tels que les sous-titres) dans la sortie telle quelle. Pour les programmes A/V simples, cela équivaut aux indicateurs plus verbeux -c:v copy -c:a copy ou les drapeaux à l'ancienne -vcodec copy -acodec copy. Vous utiliseriez le style plus détaillé lorsque vous souhaitez copier un seul des flux, mais réencoder l'autre. Par exemple, il y a de nombreuses années, il y avait une pratique courante avec les fichiers QuickTime pour compresser la vidéo avec la vidéo H.264 mais laisser l'audio comme PCM non compressé ; si vous rencontriez un tel fichier aujourd'hui, vous pouvez le moderniser avec -c:v copy -c:a aac pour ne retraiter que le flux audio, sans toucher à la vidéo.

Le point de départ de chaque commande ci-dessus après la première est le point de départ de la commande précédente plus la durée de la commande précédente.

72
Warren Young

Voici la solution à une ligne :

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

Veuillez noter que cela ne vous donne pas de fractionnement précis, mais devrait répondre à vos besoins. Il sera plutôt coupé à la première image après le temps spécifié après segment_time, dans le code ci-dessus, ce serait après la marque des 20 minutes.

Si vous trouvez que seul le premier morceau est jouable, essayez d'ajouter -reset_timestamps 1 comme mentionné dans les commentaires.

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

Face au même problème plus tôt et mis en place un simple script Python pour faire exactement cela (en utilisant FFMpeg). Disponible ici: https://github.com/c0decracker/video-splitter , et collé ci-dessous:

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

Une alternative plus façon lisible serait

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

Voici la source et la liste des commandes FFmpeg couramment utilisées.

4
Niket Pathak

Si vous voulez créer vraiment les mêmes morceaux, vous devez forcer ffmpeg à créer un i-frame sur la première image de chaque morceau afin que vous puissiez utiliser cette commande pour créer un bloc de 0,5 seconde.

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

Notez que la ponctuation exacte du format alternatif est -ss mm:ss.xxx. J'ai eu du mal pendant des heures à essayer d'utiliser le _ intuitif mais faux mm:ss:xx en vain.

$ man ffmpeg | grep -C1 position

-ss position
Cherchez la position temporelle donnée en secondes. La syntaxe "hh: mm: ss [.xxx]" est également prise en charge.

Références ici et ici .

3
Mark Hudson

Utilisez simplement ce qui est intégré à ffmpeg pour faire exactement cela.

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

Cela le divisera en approximativement 2 mandrins de seconde, fractionné au niveau des images clés pertinentes et sera généré dans les fichiers cam_out_h2640001.mp4, cam_out_h2640002.mp4, etc.

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