Converta um vídeo em um tamanho de tela fixo cortando e redimensionando

22

Eu tentei descobrir isso sozinho, mas a infinidade de opções me deixa perplexo.

Eu quero usar idealmente ffmpeg ou mencoder(ou qualquer outra coisa, mas os dois que eu sei que estou trabalhando) para converter qualquer vídeo recebido em um tamanho de tela fixo.

Se o vídeo for mais amplo ou muito curto, corte o vídeo no centro. Se não for o tamanho certo, redimensione para cima ou para baixo para torná-lo exatamente o tamanho fixo da tela.

A coisa final exata que preciso é de 720x480 em um XVid AVI com uma faixa de áudio MP3.

Encontrei muitas páginas mostrando como redimensionar para uma resolução máxima, mas preciso que o vídeo seja exatamente essa resolução (com partes extras cortadas, sem barras pretas).

Alguém pode me dizer a linha de comando para executar - ou pelo menos me levar um pouco / quase lá? Se precisar ser de várias linhas de comando (execute X para obter a resolução, faça esse cálculo e, em seguida, execute Y com a saída desse cálculo) eu posso criar um script.

Andy Jeffries
fonte
Aparentemente, o dispositivo de vídeo funciona com Mpeg4 e XVid AVI, se for mais fácil / melhor.
Andy Jeffries

Respostas:

19

Não sou um guru do ffmpeg, mas isso deve funcionar.

Primeiro de tudo, você pode obter o tamanho da entrada de vídeo como este:

ffprobe -v error -of flat=s=_ -select_streams v:0 -show_entries stream=height,width in.mp4

Com um ffmpeg razoavelmente recente, você pode redimensionar seu vídeo com estas opções:

ffmpeg -i in.mp4 -vf scale=720:480 out.mp4

Você pode definir a largura ou a altura -1para permitir ffmpegredimensionar o vídeo mantendo a proporção. Na verdade, -2é uma escolha melhor, pois o valor calculado deve ser uniforme. Então você pode digitar:

ffmpeg -i in.mp4 -vf scale=720:-2 out.mp4

Depois de obter o vídeo, ele pode ser maior do que o esperado, 720x480pois você ffmpegcalcula a altura e, portanto, precisa cortá-lo. Isso pode ser feito assim:

ffmpeg -i in.mp4 -filter:v "crop=in_w:480" out.mp4

Por fim, você pode escrever um script como este (pode ser facilmente otimizado, mas eu o mantive simples por legibilidade):

#!/bin/bash

FILE="/tmp/test.mp4"
TMP="/tmp/tmp.mp4"
OUT="/tmp/out.mp4"

OUT_WIDTH=720
OUT_HEIGHT=480

# Get the size of input video:
eval $(ffprobe -v error -of flat=s=_ -select_streams v:0 -show_entries stream=height,width ${FILE})
IN_WIDTH=${streams_stream_0_width}
IN_HEIGHT=${streams_stream_0_height}

# Get the difference between actual and desired size
W_DIFF=$[ ${OUT_WIDTH} - ${IN_WIDTH} ]
H_DIFF=$[ ${OUT_HEIGHT} - ${IN_HEIGHT} ]

# Let's take the shorter side, so the video will be at least as big
# as the desired size:
CROP_SIDE="n"
if [ ${W_DIFF} -lt ${H_DIFF} ] ; then
  SCALE="-2:${OUT_HEIGHT}"
  CROP_SIDE="w"
else
  SCALE="${OUT_WIDTH}:-2"
  CROP_SIDE="h"
fi

# Then perform a first resizing
ffmpeg -i ${FILE} -vf scale=${SCALE} ${TMP}

# Now get the temporary video size
eval $(ffprobe -v error -of flat=s=_ -select_streams v:0 -show_entries stream=height,width ${TMP})
IN_WIDTH=${streams_stream_0_width}
IN_HEIGHT=${streams_stream_0_height}

# Calculate how much we should crop
if [ "z${CROP_SIDE}" = "zh" ] ; then
  DIFF=$[ ${IN_HEIGHT} - ${OUT_HEIGHT} ]
  CROP="in_w:in_h-${DIFF}"
elif [ "z${CROP_SIDE}" = "zw" ] ; then
  DIFF=$[ ${IN_WIDTH} - ${OUT_WIDTH} ]
  CROP="in_w-${DIFF}:in_h"
fi

# Then crop...
ffmpeg -i ${TMP} -filter:v "crop=${CROP}" ${OUT}
apaul
fonte
Tive a sensação engraçada de que perderia um cenário, mas tentei com vários arquivos de vídeo e parece cortar e redimensionar corretamente os vídeos para o tamanho correto absoluto - muito bem!
Andy Jeffries
Parece que há algo errado com a decisão CROP_SIDE.
sutra
`# Obtenha a taxa entre o tamanho real e o desejado w_rate = echo "scale=3; ${in_width}/${out_width}*1000"| bc | awk '{printf "%d", $0}' h_rate = echo "scale=3; ${in_height}/${out_height}*1000"| bc | awk '{printf "%d", $0}' # Vamos usar o lado mais curto, para que o vídeo seja pelo menos tão grande quanto o tamanho desejado e recorte o lado mais longo: crop_side = 'n' if [$ {w_rate} -gt $ {h_rate}]; então scale = "- 2: $ {out_height}" crop_side = 'w' else scale = "$ {out_width}: - 2" crop_side = 'h' fi `
Sutra
23
ffmpeg -i input.file -vf "scale=(iw*sar)*max(720/(iw*sar)\,480/ih):ih*max(720/(iw*sar)\,480/ih), crop=720:480" -c:v mpeg4 -vtag XVID -q:v 4 -c:a libmp3lame -q:a 4 output.avi

Substitua "input.file" pelo nome do seu arquivo de entrada.

Brian
fonte
Essa deve ser a resposta principal, pois não requer uma chamada para ffprobe. Observe as seqüências de vírgula de barra invertida no comando Você precisará escapar das barras invertidas se estiver colocando o comando em uma string C / JavaScript / PHP.
cleong
Legal Brian. A parte do filtro de vídeo também é exatamente o que eu precisava. (ou seja, para converter vídeos antigos de 4: 3 para 16: 9)!
Dave
1
gênio! tudo feito em um.
Soli
2
Muito mais limpo que o script acima. Observação: Se você não quer XVID / MP3 você pode apenas mudar as tags após a segunda aspa para algo como-c:a copy scaled_result.mp4
Nick
7

Se você deseja cortar e escalar de uma só vez, é possível criar uma cadeia de filtros de cortar e escalar da seguinte maneira:

ffmpeg -i SomeInput.mp4 -vf "crop=in_h*9/16:in_h,scale=-2:400" -t 4 SomeOutput.mp4

O primeiro acima corta um vídeo para retrato 16: 9 e, em seguida, aumenta para 400 px x a largura apropriada (número par).

Chris
fonte
1

Eu sou novo no ffmpeg, mas agora tenho um pequeno conversor interessante no VB.NET que cria filmes em bruto de vários formatos de saída para uso em pequenos dispositivos com acesso ao cartão SD, mas sem energia suficiente para decodificar vídeos complexos.

Devido à maneira como funciona ao redimensionar e cortar, tive que codificá-lo manualmente e criar os parâmetros. Basicamente, preciso saber a largura e a altura resultantes.

Eu tenho 2 caixas de seleção para as opções: -

Preservar proporção (X) Preencher todos os pixels (X)

Somente 4 variáveis ​​são necessárias como entrada (1) Largura do vídeo original, (2) Altura do vídeo original, (3) Largura do dispositivo, (4) Altura do dispositivo.

Embora o VB.NET pudesse ser adaptado com bastante facilidade para qualquer idioma.

    Dim crop As String = "" ' will build this in ffmpeg format for -s or scale and crop

    If cbAspectRatio.Checked = False Then
        m_dest_output_w = sz._w
        m_dest_output_h = sz._h

        ' scale to w * h - simple
        crop = " -s " & m_dest_output_w & ":" & m_dest_output_h
    Else
        Dim ratio As Double = CDbl(m_video_width) / CDbl(m_video_height) ' this is aspect ratio of original unsized video

        If cbFillPixels.Checked = False Then ' shrink to fit
            If sz._w * ratio <= m_video_height Then
                m_dest_output_w = sz._w
                m_dest_output_h = sz._w / ratio
            Else
                m_dest_output_h = sz._h
                m_dest_output_w = sz._h / ratio
            End If

            ' no cropping just resizing to a non-filled area that fits

            crop = " -s " & m_dest_output_w & ":" & m_dest_output_h ' no cropping needed as letterboxed

        Else ' expand / fill --- cropping needed
            If sz._w * ratio >= m_video_height Then
                m_dest_output_w = sz._w
                m_dest_output_h = sz._w * ratio

                crop = ",crop=" & sz._w & ":" & sz._h & ":0:" & Math.Abs((m_dest_output_h - sz._h)) \ 2
            Else
                m_dest_output_h = sz._h
                m_dest_output_w = sz._h * ratio

                crop = ",crop=" & sz._w & ":" & sz._h & ":" & Math.Abs((m_dest_output_w - sz._w)) \ 2 & ":0"
            End If

            crop = " -vf scale=" & m_dest_output_w & ":" & m_dest_output_h & crop

            m_dest_output_w = sz._w
            m_dest_output_h = sz._h
        End If
    End If
TheWhitde
fonte