FFmpeg mp4 to webm, faça uma compilação de 10 segundos com base no tamanho

1

Vamos dizer que eu tenho input.mp4 que tem 12 minutos de duração. Eu gostaria de levar 4 vezes 2,5 segundos a partir da entrada para um arquivo, output.webm.

Assim, os primeiros 2,5 segundos na marca de 2,5 minutos, o segundo 2,5 segundos na marca de 5 minutos, depois o mesmo na marca de 7,5 segundos e finalmente os últimos 2,5 segundos na marca de 10 minutos. Em seguida, imprima tudo para o mesmo output.webm Arquivo.

E se isso é possível, isso também é possível para que, se o input.mp4 mudanças de comprimento, eu teria essas 4 partes com base no comprimento em vez de sempre ser padrão 2,5, 5, 7,5 e 10?

EmJeiEn
fonte

Respostas:

1

Criando um clipe de compilação baseado em cortes em um vídeo usando o ffmpeg

Por favor, note o seguinte é baseado em resposta de ptQa com modificações, por isso merece um upvote se esta resposta for útil.

Os seguintes fffmpeg O encantamento lhe dará um webm VP8 de 10 segundos com cortes na marca 2,5,5 7,5 e 10 minutos:

ffmpeg -hide_banner -i "input.mp4" -filter_complex " \
[0:v]trim=start=150:duration=2.5,setpts=PTS-STARTPTS[av];\
[0:a]atrim=start=150:duration=2.5,asetpts=PTS-STARTPTS[aa];\
[0:v]trim=start=300:duration=2.5,setpts=PTS-STARTPTS[bv];\
[0:a]atrim=start=300:duration=2.5,asetpts=PTS-STARTPTS[ba];\
[0:v]trim=start=450:duration=2.5,setpts=PTS-STARTPTS[cv];\
[0:a]atrim=start=450:duration=2.5,asetpts=PTS-STARTPTS[ca];\
[0:v]trim=start=900:duration=2.5,setpts=PTS-STARTPTS[dv];\
[0:a]atrim=start=900:duration=2.5,asetpts=PTS-STARTPTS[da];\
[av][aa][bv][ba][cv][ca][dv][da]concat=n=4:v=1:a=1[outv][outa]" \
-map [outv] -map [outa] -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis  out.webm

Isso faz quatro usos cada um aparar e atrim filtros e, finalmente, combina os subclipes usando o concat filtro.

As opções de codificação são as sugeridas pelo libvpx guia de codificação do wiki do FFmpeg .

Bônus: trims variáveis ​​com base no comprimento da entrada

Isso é factível com um pouco de script, usando ffprobe para obter a duração, por exemplo:

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4

Isso retorna o comprimento do clipe em segundos, que pode ser dividido por 5 e depois multiplicado por 1, 2 3 e 4 para dar posições de quatro cortes igualmente espaçados, respectivamente.

Nota sobre desempenho

Embora a geração do clipe desta forma seja legal, ele excisa exatamente os subclipes do vídeo de entrada, é lento .

Uma abordagem mais rápida seria usar busca de entrada e uma cópia de fluxo para criar os subclipes, antes de uni-los e codificá-los.

No entanto, se você precisar de durações exatas, ou o desempenho não for um problema, use um monte de trim filtros fará o trabalho muito bem.

Script para gerar o clipe usando trim para qualquer comprimento de entrada e qualquer número de cortes / subclipes

#!/bin/bash
# clip.sh - create a clip of short sequences of a passed-in media file
# takes one argument, the input file
# can customise number of cuts, total length of clip
# clips will be evenly-spaced in input file

SUBCLIPS=4 # number of clips to make up clip
OUTPUTLENGTH=10 # output file length, in seconds
OUTPUTFILE=out.webm # name

cliplength=$(echo "$OUTPUTLENGTH/$SUBCLIPS"| bc -l)

if [ -e "$1" ]; then
        videofile="$1"
else
        echo "file not found: $1"
        exit 1
fi

ffmpeg_preamble="ffmpeg -hide_banner -i '$videofile' -filter_complex \""

duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$videofile")

cutfilters=""
concatfilter=""

for cutno in $( seq 1 $SUBCLIPS ); do
    cut=$(echo "(1 / ($SUBCLIPS+1)) * $cutno" | bc -l)
    let cutchar=$cutno+96 # ascii value so we get max 26 cuts rather than 10
    cutletter=$(printf "\x$(printf %x $cutchar)")
    cutpos=$(echo "$duration * $cut" |bc)
    cutfilters=$(printf "%s[0:v]trim=start=%f:duration=%f,setpts=PTS-STARTPTS[%sv];" "$cutfilters" "$cutpos" "$cliplength" "$cutletter")
    cutfilters=$(printf "%s[0:a]atrim=start=%f:duration=%f,asetpts=PTS-STARTPTS[%sa];" "$cutfilters" "$cutpos" "$cliplength" "$cutletter")
    concatfilter=$(printf "%s[%sv][%sa]" "$concatfilter" "$cutletter" "$cutletter")
done

# concat the cuts together

concatfilter=$(printf "%sconcat=n=%s:v=1:a=1[outv][outa]" "$concatfilter" "$SUBCLIPS")

ffmpeg_webmopts=" -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis "
ffmpeg_postscript="\" -map [outv] -map [outa] $ffmpeg_webmopts '$OUTPUTFILE'"

# Chance to cancel before we start

printf "***(hit ctrl+c to cancel)***\n\nExecuting: %s%s%s%s\n" "$ffmpeg_preamble" "$cutfilters" "$concatfilter" "$ffmpeg_postscript"
sleep 3
eval $(echo "$ffmpeg_preamble $cutfilters $concatfilter $ffmpeg_postscript")
bertieb
fonte
Eu escrevi um script para automatizar a parte do bônus, uma vez que tenha sido arrumado, eu vou editar essa resposta para incluir
bertieb
Obrigado! Entendi com isso :). Tomou a duração com ffprobe, em seguida, apenas acrescentou variáveis ​​mais calculadas as vezes (fazendo isso com php). Funciona muito bem, muito obrigado novamente!
EmJeiEn
Feliz por ajudar, foi um exercício interessante para mim - eu não escrevi um script para gerar um filtro! Se você está fazendo isso em php e desempenho é mais crítico do que durações exatas, você pode querer considerar o uso de buscas e arquivos temporários para cortar + participar (mas isso é provavelmente uma questão separada)
bertieb
Eu definitivamente vou olhar para isso nesse caso, sim, de fato, o desempenho é fundamental para mim. Isso funciona como um sonho, mas é bom que ele seja executado mesmo em servidores poderosos.
EmJeiEn
Concordo, demora bastante tempo. Nesse caso, posso testar a procura + fluxo copiando os subclipes e transcodificando. Se resultar em uma diferença notável, postarei uma pergunta de auto-resposta e ligarei a partir daqui :)
bertieb