Como posso usar o ffmpeg para dividir o vídeo MPEG em pedaços de 10 minutos?

54

Geralmente, há necessidade de uma comunidade de software livre ou de desenvolvedores ativos publicar grandes segmentos de vídeo on-line. (Meet-up videos, campouts, tech talks ...) Sendo que eu sou um desenvolvedor e não um videomaker, não tenho o desejo de desembolsar o extra em uma conta premium do Vimeo. Como, então, eu faço um vídeo sobre tecnologia MPEG de 12,5 GB (1:20:00) e faço cortes em segmentos de 00:10:00 para facilitar o upload para sites de compartilhamento de vídeo?

    
por Gabriel 06.09.2010 / 18:16

8 respostas

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

Resolver isso em um script para fazer isso em um loop não seria difícil.

Tenha em atenção que, se tentar calcular o número de iterações com base na duração da saída de uma chamada ffprobe , isto é estimado a partir da taxa média de bits no início do clipe e tamanho do arquivo, a menos que você forneça o argumento -count_frames , o que atrasa consideravelmente sua operação.

Outra coisa que deve estar ciente é que a posição da opção -ss na linha de comando é importante . Onde eu tenho agora é lento, mas preciso. A primeira versão dessa resposta deu a alternativa rápida, mas imprecisa . O artigo vinculado também descreve uma alternativa mais rápida, mas ainda precisa, pela qual você paga com um pouco de complexidade.

Tudo isso de lado, eu não acho que você realmente queira cortar exatamente 10 minutos para cada clipe. Isso vai colocar cortes bem no meio de frases, até mesmo palavras. Acho que você deveria estar usando um editor de vídeo ou player para encontrar pontos de corte naturais com apenas 10 minutos de intervalo.

Supondo que seu arquivo esteja em um formato que o YouTube possa aceitar diretamente, não é necessário reencodificá-lo para obter segmentos. Basta passar as compensações do ponto de corte natural para ffmpeg , informando para passar o A / V codificado por inalterado usando o 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
...

O argumento -c copy diz para copiar todos os fluxos de entrada (áudio, vídeo e potencialmente outros, como legendas) para a saída como está. Para programas A / V simples, é equivalente aos sinalizadores mais detalhados -c:v copy -c:a copy ou aos sinalizadores de estilo antigo -vcodec copy -acodec copy . Você usaria o estilo mais detalhado quando quiser copiar apenas um dos fluxos, mas recodificar o outro. Por exemplo, muitos anos atrás, havia uma prática comum com arquivos QuickTime para compactar o vídeo com vídeo H.264, mas deixar o áudio como PCM não comprimido ; se você encontrasse esse arquivo hoje, poderia modernizá-lo com -c:v copy -c:a aac para reprocessar apenas o fluxo de áudio, deixando o vídeo intacto.

O ponto inicial para cada comando acima após o primeiro é o ponto inicial do comando anterior mais a duração do comando anterior.

    
por 06.09.2010 / 19:49
44

Aqui está a solução de uma linha :

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

Por favor, note que isso não lhe dá divisões precisas, mas deve atender às suas necessidades. Em vez disso, ele cortará no primeiro quadro após o tempo especificado após segment_time , no código acima, após a marca de 20 minutos.

Se você achar que apenas o primeiro pedaço pode ser reproduzido, tente adicionar -reset_timestamps 1 como mencionado nos comentários.

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4
    
por 27.06.2015 / 03:39
6

Encarou o mesmo problema anteriormente e montou um script Python simples para fazer exatamente isso (usando o FFMpeg). Disponível aqui: link e colado abaixo:

#!/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)
    
por 21.11.2014 / 00:28
2

Observe que a pontuação exata do formato alternativo é -ss mm:ss.xxx . Eu lutei por horas tentando usar o intuitivo-mas-errado mm:ss:xx sem sucesso.

$ man ffmpeg | grep -C1 position

-ss position
Seek to given time position in seconds. "hh:mm:ss[.xxx]" syntax is also supported.

Referências aqui e aqui .

    
por 11.06.2011 / 04:43
2

Se você quiser criar realmente os mesmos Chunks, deve forçar o ffmpeg a criar o i-frame no primeiro quadro de cada chunk, para que você possa usar esse comando para criar um bloco de 0.5 segundo.

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
    
por 23.08.2018 / 09:27
0

Uma maneira Alternativa mais legível seria

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

Aqui está a fonte e a lista de comandos FFmpeg comumente usados.

    
por 22.03.2019 / 16:03
0
#!/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.*$//' | 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
    
por 21.04.2019 / 18:56
-2

Você não deveria estar realmente seguindo nenhuma das respostas neste tópico, em vez disso, basta usar o que está embutido no ffmpeg para fazer exatamente isso.

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

Isso dividirá em aproximadamente 2 segundos, dividido nos quadros-chave relevantes e será enviado para os arquivos cam_out_h2640001.mp4, cam_out_h2640002.mp4, etc.

    
por 22.03.2016 / 06:48