it-swarm-pt.com

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

Geralmente, é necessário que a comunidade de desenvolvedores de código aberto ou ativos publique on-line grandes segmentos de vídeo. (Vídeos de reunião, acampamentos, palestras sobre tecnologia ...) Sendo que sou desenvolvedor e não cinegrafista, não tenho nenhum desejo de fazer um arranhão extra em uma conta premium do Vimeo. Como, então, pego um vídeo de conversa técnica MPEG de 12,5 GB (1:20:00) e o divido em segmentos 00:10:00 para facilitar o upload para sites de compartilhamento de vídeo?

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

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

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

Outra coisa a ter em atenção é que a posição do -ss opção na linha de comando assuntos . Onde eu tenho agora é lento, mas preciso. A primeira versão desta resposta deu a alternativa rápida mas imprecisa . O artigo vinculado também descreve uma alternativa mais rápida, mas ainda precisa, que 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 colocará cortes bem no meio das frases, até das palavras. Eu acho que você deveria usar 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, você não precisará recodificar para obter segmentos. Apenas passe as compensações naturais do ponto de corte para ffmpeg, dizendo-lhe para passar o A/V codificado intocado 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 -c copy argumento diz para copiar todos os fluxos de entrada (áudio, vídeo e potencialmente outros, como legendas) na saída como estão. Para programas simples de A/V, é equivalente aos sinalizadores mais detalhados -c:v copy -c:a copy ou os sinalizadores de estilo antigo -vcodec copy -acodec copy. Você usaria o estilo mais detalhado quando desejar copiar apenas um dos fluxos, mas recodifique o outro. Por exemplo, há muitos anos, havia uma prática comum com os arquivos do QuickTime para compactar o vídeo com o vídeo H.264, mas deixar o áudio como PCM descompactado ; se você encontrar um arquivo assim hoje, poderá modernizá-lo com -c:v copy -c:a aac para reprocessar apenas o fluxo de áudio, deixando o vídeo intocado.

O ponto inicial de todos os comandos acima após o primeiro é o ponto inicial do comando anterior mais a duração do comando anterior.

72
Warren Young

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

Observe que isso não fornece divisões precisas, mas deve atender às suas necessidades. Em vez disso, ele será cortado no primeiro quadro após o tempo especificado após segment_time, no código acima, seria após a marca de 20 minutos.

Se você achar que apenas o primeiro pedaço é reproduzível, 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
68
Jon

Enfrentou o mesmo problema anteriormente e montou um script simples Python para fazer exatamente isso (usando FFMpeg). Disponível aqui: https://github.com/c0decracker/video-splitter e colados 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)
7
c0decracker

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 a fonte e a lista dos comandos FFmpeg comumente usados.

4
Niket Pathak

Se você deseja criar realmente os mesmos pedaços, deve forçar o ffmpeg a criar o i-frame no primeiro quadro de todos os pedaços, para que você possa usar este comando para criar um pedaço 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
4
alireza akbaribayat

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 para nenhum proveito.

$ man ffmpeg | grep -C1 position

posição -ss
Procure a posição de tempo especificada em segundos. A sintaxe "hh: mm: ss [.xxx]" também é suportada.

Referências aqui e aqui .

3
Mark Hudson

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 o dividirá em mandris de aproximadamente 2 segundos, nos quadros-chave relevantes e será gerado nos arquivos 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