FFmpeg: Como dividir o vídeo de forma eficiente?

103

Desejo dividir um grande vídeo avi em dois vídeos menores consecutivos. Estou usando o ffmpeg.

Uma maneira é executar o ffmpeg duas vezes:

ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 output1.avi
ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:30:00 -t 00:30:00 output2.avi

Mas de acordo com a página de manual do ffmpeg, posso fazer mais de um arquivo de saída de um arquivo de entrada usando apenas uma linha:

ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 output1.avi \
   -vcodec copy -acodec copy -ss 00:30:00 -t 00:30:00 output2.avi

Minha pergunta é: a abordagem posterior economiza tempo de computação e memória?

Antony
fonte
3
ao usar a "cópia -vcodec -acodec cópia" é muito rápido :))
Savas Adar
@Antony, por que você não cronometra as duas versões (e olha para o monitor de memória, como htop) e nos diz qual é a resposta?
AlcubierreDrive
O primeiro exemplo faz isso sequencialmente, enquanto o segundo exemplo usa threads. Ambos farão a mesma coisa, nenhuma aceleração perceptível deve ocorrer. Mas para simplificar as coisas, você pode usar o segmentador de fluxo ffmpeg muxer: ffmpeg.org/…
Mladen B.
1
Este exemplo parece terrivelmente errado! A documentação do ffmpeg diz sobre a opção -ss: "Quando usado como uma opção de entrada (antes de -i), busca neste arquivo de entrada para posicionar. Quando usado como uma opção de saída (antes de um nome de arquivo de saída), decodifica, mas descarta a entrada até que o timestamps alcançam a posição. " Você o está usando na posição de saída, então sua segunda linha desperdiça esforço de decodificação e descarta os primeiros 30 minutos.
Lucian Wischik
1
@LucianWischik, mas usar -sscomo opção de entrada na segunda divisão pode não ser preciso, portanto, você pode não obter uma divisão limpa. IMO o exemplo da OP é mais correto.
jiggunjer

Respostas:

82

O wiki do ffmpeg leva de volta a esta página em referência a "Como dividir o vídeo de forma eficiente". Não estou convencido de que esta página responda a essa pergunta, então fiz como @AlcubierreDrive sugeriu ...

echo "Two commands" 
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 -sn test1.mkv
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:30:00 -t 01:00:00 -sn test2.mkv
echo "One command" 
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 \
  -sn test3.mkv -vcodec copy -acodec copy -ss 00:30:00 -t 01:00:00 -sn test4.mkv

Quais saídas ...

Two commands
real    0m16.201s
user    0m1.830s
sys 0m1.301s

real    0m43.621s
user    0m4.943s
sys 0m2.908s

One command
real    0m59.410s
user    0m5.577s
sys 0m3.939s

Testei um arquivo SD e HD, depois de algumas corridas e um pouco de matemática.

Two commands SD 0m53.94 #2 wins  
One command  SD 0m49.63  

Two commands SD 0m55.00  
One command  SD 0m52.26 #1 wins 

Two commands SD 0m58.60 #2 wins  
One command  SD 0m58.61 

Two commands SD 0m54.60  
One command  SD 0m50.51 #1 wins 

Two commands SD 0m53.94  
One command  SD 0m49.63 #1 wins  

Two commands SD 0m55.00  
One command  SD 0m52.26 #1 wins 

Two commands SD 0m58.71  
One command  SD 0m58.61 #1 wins

Two commands SD 0m54.63  
One command  SD 0m50.51 #1 wins  

Two commands SD 1m6.67s #2 wins  
One command  SD 1m20.18  

Two commands SD 1m7.67  
One command  SD 1m6.72 #1 wins

Two commands SD 1m4.92  
One command  SD 1m2.24 #1 wins

Two commands SD 1m1.73  
One command  SD 0m59.72 #1 wins

Two commands HD 4m23.20  
One command  HD 3m40.02 #1 wins

Two commands SD 1m1.30  
One command  SD 0m59.59 #1 wins  

Two commands HD 3m47.89  
One command  HD 3m29.59 #1 wins  

Two commands SD 0m59.82  
One command  SD 0m59.41 #1 wins  

Two commands HD 3m51.18  
One command  HD 3m30.79 #1 wins  

Arquivo SD = fluxo de transporte de 1,35 GB DVB
arquivo HD = fluxo de transporte de 3,14 GB DVB

Conclusão

O comando único é melhor se você estiver lidando com HD, ele concorda com os comentários do manual sobre o uso de -ss após o arquivo de entrada para fazer uma 'busca lenta'. Os arquivos SD têm uma diferença insignificante.

A versão de dois comandos deve ser mais rápida adicionando outro -ss antes do arquivo de entrada para uma 'busca rápida' seguida por uma busca lenta mais precisa.

user2867531
fonte
Se o TS contém vários programas (programas de TV capturados por DVB-T), como posso dividi-los? Por exemplo, eu tenho um arquivo TS que contém futebol + culinária + desenho animado. Como posso dividir esse arquivo TS em 3 arquivos mpg?
Dr.jacky de
Usei sua resposta na minha pergunta aqui ( stackoverflow.com/questions/41983940/… ), mas não funcionou, o que você acha que estou fazendo de errado?
utdev 01 de
1
O arquivo de resultado ficou sem vídeo (apenas áudio) no meu caso. Depois de alguns experimentos, encontrei a alma. É necessário especificar a chave para "não modificar a qualidade" como esta -q:v 0.
Oklas,
19

Aqui está um script útil, que ajuda você a dividir automaticamente: Um script para dividir vídeos usando ffmpeg

#!/bin/bash
 
# Written by Alexis Bezverkhyy <[email protected]> in 2011
# This is free and unencumbered software released into the public domain.
# For more information, please refer to <http://unlicense.org/>
 
function usage {
        echo "Usage : ffsplit.sh input.file chunk-duration [output-filename-format]"
        echo -e "\t - input file may be any kind of file reconginzed by ffmpeg"
        echo -e "\t - chunk duration must be in seconds"
        echo -e "\t - output filename format must be printf-like, for example myvideo-part-%04d.avi"
        echo -e "\t - if no output filename format is given, it will be computed\
 automatically from input filename"
}
 
IN_FILE="$1"
OUT_FILE_FORMAT="$3"
typeset -i CHUNK_LEN
CHUNK_LEN="$2"
 
DURATION_HMS=$(ffmpeg -i "$IN_FILE" 2>&1 | grep Duration | cut -f 4 -d ' ')
DURATION_H=$(echo "$DURATION_HMS" | cut -d ':' -f 1)
DURATION_M=$(echo "$DURATION_HMS" | cut -d ':' -f 2)
DURATION_S=$(echo "$DURATION_HMS" | cut -d ':' -f 3 | cut -d '.' -f 1)
let "DURATION = ( DURATION_H * 60 + DURATION_M ) * 60 + DURATION_S"
 
if [ "$DURATION" = '0' ] ; then
        echo "Invalid input video"
        usage
        exit 1
fi
 
if [ "$CHUNK_LEN" = "0" ] ; then
        echo "Invalid chunk size"
        usage
        exit 2
fi
 
if [ -z "$OUT_FILE_FORMAT" ] ; then
        FILE_EXT=$(echo "$IN_FILE" | sed 's/^.*\.\([a-zA-Z0-9]\+\)$/\1/')
        FILE_NAME=$(echo "$IN_FILE" | sed 's/^\(.*\)\.[a-zA-Z0-9]\+$/\1/')
        OUT_FILE_FORMAT="${FILE_NAME}-%03d.${FILE_EXT}"
        echo "Using default output file format : $OUT_FILE_FORMAT"
fi
 
N='1'
OFFSET='0'
let 'N_FILES = DURATION / CHUNK_LEN + 1'
 
while [ "$OFFSET" -lt "$DURATION" ] ; do
        OUT_FILE=$(printf "$OUT_FILE_FORMAT" "$N")
        echo "writing $OUT_FILE ($N/$N_FILES)..."
        ffmpeg -i "$IN_FILE" -vcodec copy -acodec copy -ss "$OFFSET" -t "$CHUNK_LEN" "$OUT_FILE"
        let "N = N + 1"
        let "OFFSET = OFFSET + CHUNK_LEN"
done
Marco C.
fonte
2
O mesmo comentário vale para você e para SEARAS. Gravar arquivos em lote é útil quando absolutamente necessário e quando você não tem nenhuma outra opção disponível, porque não é portátil. Você não conseguirá executar o mesmo script no Windows, por exemplo. Quando há formas mais genéricas / portáveis, scripts em lote devem ser evitados, para economizar o tempo necessário para portá-los. Como o ffmpeg tem várias maneiras de resolver esse problema, a maneira adequada seria usar o ffmpeg sozinho, sem a ajuda de scripts.
Mladen B.
Bom roteiro. Você escreveu isso? Está no GitHub? Eu gostaria de fazer algumas sugestões :)
vozes de
para mp4, as cenas são danificadas. é melhor substituir a cópia por: -vcodec libx264 -acodec aac
kishu
8

Na minha experiência, não use o ffmpeg para dividir / juntar. MP4Box, é mais rápido e leve que o ffmpeg. Por favor tente. Por exemplo, se você deseja dividir um arquivo MP4 de 1400 MB em duas partes de 700 MB, você pode usar o seguinte cmdl: MP4Box -splits 716800 input.mp4 por exemplo, para concatenar dois arquivos que você pode usar:

MP4Box -cat file1.mp4 -cat file2.mp4 output.mp4

Ou se precisar dividir por tempo, use -splitx StartTime:EndTime:

MP4Box -add input.mp4 -splitx 0:15 -new split.mp4

Classsic
fonte
Isso era exatamente o que eu precisava! Quando eu estava cortando com ffmpeg, os clipes não começavam em quadros-chave e apresentavam congelamentos estranhos nas extremidades. Usar o MP4Box -splitz resolveu o problema!
matt
1
como posso cortar o vídeo usando Mp4Box com vários números? por exemplo, preciso ter subclipes em segundos: 5 - 8, 21 - 30, 12 - 18, então preciso de três arquivos de saída de vídeo.
mhndev 01 de
E é muito mais fácil de usar do EndTimeque -t.
vmassuchetto
6

http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg também pode ser útil para você. Além disso, o ffmpeg tem um muxer de segmento que pode funcionar.

De qualquer forma, meu palpite é que combiná-los em um comando economizaria tempo.

rogerdpack
fonte
2

a abordagem posterior economiza tempo de computação e memória?

Não há grande diferença entre esses dois exemplos que você forneceu. O primeiro exemplo corta o vídeo sequencialmente, em 2 etapas, enquanto o segundo exemplo faz isso ao mesmo tempo (usando threads). Nenhuma aceleração particular será notada. Você pode ler mais sobre como criar várias saídas com FFmpeg

Além disso, o que você pode usar (no FFmpeg recente) é o muxer segmentador de fluxo que pode:

fluxos de saída para uma série de arquivos separados de duração quase fixa. O padrão de nome de arquivo de saída pode ser definido de maneira semelhante a image2 .

Mladen B.
fonte
2

Aqui está um arquivo bat simples do Windows para dividir o arquivo recebido em 50 partes. Cada parte tem duração de 1 minuto. Desculpe por esse script idiota. Espero que seja melhor ter um script do Windows idiota em vez de não tê-lo. Talvez ajude alguém. (Com base em "arquivo bat para loop" deste site.)

set var=0
@echo off
:start
set lz=
if %var% EQU 50 goto end
if %var% LEQ 9 set lz=0
echo part %lz%%var%
ffmpeg -ss 00:%lz%%var%:00 -t 00:01:00 -i %1 -acodec copy -vcodec copy %2_%lz%%var%.mp4
set /a var+=1
goto start

:end
echo var has reached %var%.
exit
Alex Bor
fonte
Este é essencialmente o primeiro fragmento de código na pergunta ( ffmpegcomandos consecutivos ) reescrito como um loop. Isso não responde à pergunta se essa abordagem ou um único comando "economizará tempo de computação e memória".
BACON
1

Não testei, mas parece promissor:

Segmentador de fluxo básico

Obviamente, está dividindo o AVI em segmentos do mesmo tamanho, o que implica que esses pedaços não perdem qualidade ou aumentam a memória ou devem ser recalculados.

Ele também usa a cópia do codec - isso significa que pode lidar com fluxos muito grandes? Porque este é meu problema, eu quero quebrar meu avi para que eu possa usar um filtro para me livrar da distorção. Mas um avi inteiro funciona por horas.

Mathias Weitz
fonte
-1

Esta é uma maneira perfeita de dividir o vídeo. Já fiz isso anteriormente e está funcionando bem para mim.

ffmpeg -i C:\xampp\htdocs\videoCutting\movie.mp4 -ss 00:00:00 -t 00:00:05 -async 1 C:\xampp\htdocs\videoCutting\SampleVideoNew.mp4 (Para cmd). shell_exec('ffmpeg -i C:\xampp\htdocs\videoCutting\movie.mp4 -ss 00:00:00 -t 00:00:05 -async 1 C:\xampp\htdocs\videoCutting\SampleVideoNew.mp4')(para php).

Siga isso e tenho certeza de que funcionará perfeitamente.

Atul Upadhyay
fonte
Por que essa é a "maneira perfeita" de dividir o vídeo? Como a linha de comando na pergunta, você está passando -iseguido por -sse -t. A questão é sobre como extrair com eficiência vários segmentos do mesmo arquivo de entrada, embora você esteja extraindo apenas um único segmento. Não vejo como isso responde à pergunta.
BACON
-2

uma maneira simples é usar a opção trim com API baseada em url publitio .

Depois de enviar seu vídeo, basta definir o deslocamento inicial e o deslocamento final no url (so_2, eo_2) assim:

    https://media.publit.io/file/so_2,eo_2/tummy.mp4

isso criará novos vídeos instantaneamente a partir do segundo segundo e com 2 segundos de duração. você pode dividir os vídeos assim como quiser.

ob1y2k
fonte
A questão é saber qual é a mais eficiente das duas abordagens para extrair vários segmentos de um arquivo de entrada ffmpeg. A menos que você tenha uma conexão de internet muito, muito rápida, acho que ter que carregar o vídeo inteiro para um serviço em nuvem e depois baixar os resultados aumentará significativamente o tempo de processamento. Mesmo assim, isso ainda não responde a pergunta de qual abordagem comffmpeg é mais eficiente.
BACON