Convierta un video a un tamaño de pantalla fijo recortando y redimensionando

22

He tratado de resolver esto por mí mismo, pero la gran cantidad de opciones me desconcierta.

Quiero usar idealmente ffmpego mencoder(o algo más, pero esos dos que sé que tengo trabajando) para convertir cualquier video entrante a un tamaño de pantalla fijo.

Si el video es más ancho o demasiado corto para él, entonces recorte el video en el centro. Si no es el tamaño correcto, cambie el tamaño hacia arriba o hacia abajo para que sea exactamente el tamaño de pantalla fijo.

Lo último que necesito es 720x480 en un XVid AVI con una pista de audio MP3.

He encontrado muchas páginas que muestran cómo cambiar el tamaño a una resolución máxima, pero necesito que el video sea exactamente esa resolución (con partes adicionales recortadas, sin barras negras).

¿Alguien puede decirme la línea de comando para ejecutar, o al menos conseguirme la mayor parte del camino? Si necesita ser varias líneas de comando (ejecute X para obtener la resolución, haga este cálculo y luego ejecute Y con la salida de ese cálculo), puedo escribir eso.

Andy Jeffries
fuente
Aparentemente, el dispositivo de video funciona con Mpeg4 y XVid AVI, si eso es más fácil / mejor.
Andy Jeffries

Respuestas:

19

No soy un gurú ffmpeg, pero esto debería funcionar.

En primer lugar, puede obtener el tamaño del video de entrada como este:

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

Con un ffmpeg razonablemente reciente, puede cambiar el tamaño de su video con estas opciones:

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

Puede establecer el ancho o la altura -1para permitir ffmpegcambiar el tamaño del video manteniendo la relación de aspecto. En realidad, -2es una mejor opción ya que el valor calculado debería ser par. Entonces podrías escribir:

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

Una vez que obtenga el video, puede ser más grande de lo esperado, 720x480ya que deja ffmpegcalcular la altura, por lo que tendrá que recortarlo. Esto se puede hacer así:

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

Finalmente, podría escribir un script como este (se puede optimizar fácilmente, pero lo mantuve simple para la legibilidad):

#!/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
fuente
Tuve la extraña sensación de que pasaría por alto un escenario, pero lo he intentado con muchos archivos de video y parece recortar y cambiar el tamaño de los videos al tamaño correcto absoluto, ¡bien hecho!
Andy Jeffries
Parece que hay algún error con la decisión CROP_SIDE.
Sutra
`# Obtenga la tasa entre el tamaño real y el deseado 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}' # Tomemos el lado más corto, para que el video sea al menos tan grande # como el tamaño deseado, y recorte el lado más largo: crop_side = 'n' if [$ {w_rate} -gt $ {h_rate}]; luego scale = "- 2: $ {out_height}" crop_side = 'w' sino 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

Reemplace "input.file" con el nombre de su archivo de entrada.

Brian
fuente
Esta debería ser la respuesta principal, ya que no requiere una llamada a ffprobe. Tenga en cuenta las secuencias de comas de barra diagonal inversa en el comando. Tendrá que escapar de las barras invertidas si coloca el comando en una cadena C / JavaScript / PHP.
cleong
Bien Brian. La parte del filtro de video es exactamente lo que necesitaba también. (es decir, para convertir videos antiguos de 4: 3 a 16: 9)!
Dave
1
¡genio! todo hecho en uno.
Soli
2
Mucho más limpio que el guión anterior. Tenga en cuenta que si no desea XVID / MP3, puede cambiar las etiquetas después de la segunda comilla a algo así-c:a copy scaled_result.mp4
Nick
7

Si desea recortar y escalar de una vez, puede hacer una cadena de filtro de recortar y luego escalar de la siguiente manera:

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

Lo anterior primero recorta un video a 16: 9 vertical, luego escala a 400 px de alto x el ancho apropiado (número par).

Chris
fuente
1

Soy nuevo en ffmpeg, pero ahora tengo un pequeño y agradable convertidor en VB.NET que crea películas en bruto de varios formatos de salida para usar en dispositivos pequeños que tienen acceso a la tarjeta SD pero no tienen suficiente potencia para decodificar video complejo.

Debido a la forma en que funciona al cambiar el tamaño y el recorte, tuve que codificarlo manualmente y construir los parámetros. Básicamente necesito saber el ancho y la altura resultantes.

Tengo 2 casillas de verificación para las opciones: -

Conservar relación de aspecto (X) Rellenar todos los píxeles (X)

Solo se necesitan 4 variables como entrada (1) Ancho de video original, (2) Altura de video original, (3) Ancho del dispositivo, (4) Altura del dispositivo.

Aunque para VB.NET podría adaptarse bastante fácil para cualquier 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
fuente