it-swarm-es.tech

¿Cómo puedo usar ffmpeg para dividir video MPEG en fragmentos de 10 minutos?

A menudo existe una necesidad en la comunidad de desarrolladores de código abierto o activa para publicar grandes segmentos de video en línea. (Videos de encuentros, campamentos, charlas tecnológicas ...) Siendo que soy un desarrollador y no un camarógrafo, no deseo desembolsar el rasguño adicional en una cuenta premium de Vimeo. ¿Cómo puedo tomar un video de charla técnica MPEG de 12.5 GB (1:20:00) y dividirlo en segmentos de 00:10:00 para cargarlo fácilmente en sitios para compartir videos?

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

Envolver esto en un script para hacerlo en un bucle no sería difícil.

Tenga en cuenta que si intenta calcular el número de iteraciones en función de la salida de duración de una llamada ffprobe, esto es estimado del promedio velocidad de bits al comienzo del clip y el tamaño del archivo del clip a menos que proporcione el argumento -count_frames, lo que ralentiza considerablemente su funcionamiento.

Otra cosa a tener en cuenta es que la posición de la opción -ss En la línea de comando importa . Donde lo tengo ahora es lento pero preciso. La primera versión de esta respuesta le dio la alternativa rápida pero imprecisa . El artículo vinculado también describe una alternativa en su mayoría rápida pero precisa, que paga con un poco de complejidad.

Aparte de eso, no creo que realmente quieras cortar exactamente 10 minutos para cada clip. Eso pondrá cortes justo en el medio de las oraciones, incluso las palabras. Creo que deberías usar un editor o reproductor de video para encontrar puntos de corte naturales a solo 10 minutos de diferencia.

Suponiendo que su archivo está en un formato que YouTube puede aceptar directamente, no tiene que volver a codificar para obtener segmentos. Simplemente pase las compensaciones del punto de corte natural a ffmpeg, diciéndole que pase el A/V codificado sin tocarlo usando el códec "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
...

El argumento -c copy Le dice que copie todas las secuencias de entrada (audio, video y potencialmente otras, como subtítulos) en la salida tal como está. Para programas de A/V simples, es equivalente a las banderas más detalladas -c:v copy -c:a copy O las banderas de estilo antiguo -vcodec copy -acodec copy. Usaría el estilo más detallado cuando quiera copiar solo una de las secuencias, pero vuelva a codificar la otra. Por ejemplo, hace muchos años había una práctica común con los archivos QuickTime para comprimir el video con video H.264 pero dejar el audio como PCM sin comprimir ; si se encontró con un archivo de este tipo hoy, podría modernizarlo con -c:v copy -c:a aac para reprocesar solo la secuencia de audio, dejando el video intacto.

El punto de inicio para cada comando anterior después del primero es el punto de inicio del comando anterior más la duración del comando anterior.

72
Warren Young

Aquí está la solución de una línea :

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

Tenga en cuenta que esto no le ofrece divisiones precisas, pero debe adaptarse a sus necesidades. En su lugar, se cortará en el primer fotograma después del tiempo especificado después de segment_time, en el código anterior sería después de la marca de 20 minutos.

Si encuentra que solo se puede reproducir el primer fragmento, intente agregar -reset_timestamps 1 como se menciona en los comentarios.

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

Enfrenté el mismo problema antes y armé un script simple Python para hacer eso (usando FFMpeg). Disponible aquí: https://github.com/c0decracker/video-splitter , y pegado a continuación:

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

Una alternativa más legible sería

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

Aquí está la fuente y la lista de comandos FFmpeg usados ​​comúnmente.

4
Niket Pathak

Si desea crear realmente los mismos fragmentos, debe forzar ffmpeg para crear i-frame en el primer cuadro de cada fragmento para que pueda usar este comando para crear un fragmento de 0.5 segundos.

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

Tenga en cuenta que la puntuación exacta del formato alternativo es -ss mm:ss.xxx. Luché durante horas tratando de usar el intuitivo pero equivocado mm:ss:xx en vano.

$ man ffmpeg | grep -C1 position

-ss posición
Busque la posición de tiempo dada en segundos. La sintaxis "hh: mm: ss [.xxx]" también es compatible.

Referencias aquí y aquí .

3
Mark Hudson

Simplemente use lo que está integrado en ffmpeg para hacer exactamente esto.

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

Esto lo dividirá en mandriles de aproximadamente 2 segundos, se dividirá en los fotogramas clave relevantes y generará los archivos 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