Publi

Cómo extraer duración, fotogramas, bitrate y fps de un vídeo para nuestros scripts

1200px-Film_strip

Seguramente en nuestros scripts, si tratamos con archivos de vídeo, nos interese conocer información sobre el mismo. Tal vez podamos extraer más información de un archivo de vídeo con un software especializado como VLC; pero para hacer unos cálculos rápidos o una conversión de formato nos va a venir muy bien.

Programas como avconv o ffmpeg son capaces de extraer información básica del vídeo. Es más, programas como identify (de imagemagick) entre otros, en realidad llaman por detrás a uno de los dos primeros para realizar la identificación del archivo.

Pero claro, imaginemos que queremos automatizar algunos procesos y hay archivos de vídeo involucrados. ¿Cómo extraemos la información de los mismos? Con herramientas como sed o awk. Al final del post tendréis el código fuente completo del script.

¿avconv o ffmpeg?

avconv es un fork de ffmpeg. Por eso, tienen algunas cosas en común. El caso es que algunas distribuciones incluyen ffmpeg y otras avconv. Por lo que lo primero que tenemos que tener claro es qué programa tenemos nosotros instalado.
Ambos programas sólo tienen que especificar el archivo de entrada para tener como salida información del archivo de vídeo. En este caso el argumento -i nos devuelve algo como:

avconv version 9.18-6:9.18-0ubuntu0.14.04.1, Copyright (c) 2000-2014 the Libav developers
built on Mar 16 2015 13:19:10 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
Guessed Channel Layout for Input Stream #0.1 : stereo
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from ‘/tmp/video.mp4’:
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf54.20.4
Duration: 00:26:25.36, start: 0.000000, bitrate: 1105 kb/s
Stream #0.0(und): Video: h264 (High), yuv420p, 720×576 [PAR 16:15 DAR 4:3], 974 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc
Stream #0.1(und): Audio: mp3, 48000 Hz, stereo, s16p, 128 kb/s
At least one output file must be specified

Nos da más información de la que vamos a extraer, por lo que podríamos sacar más cosas en claro, por otro lado, no nos enseña los frames totales del vídeo, y eso puede resultarnos de ayuda ya que esta herramienta expresa el progreso en número de frames procesados, por lo que si sabemos cuántos frames hay en total, podemos saber qué porcentaje de progreso llevamos. Pero lo veremos más adelante.

Desgranando el script

Para eso he utilizado una función que busca los dos ejecutables en el sistema, y coge el que exista.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function checkSoftware()
{
    SW="`which avconv; which ffmpeg`"

    if [ -n "`echo $SW | grep avconv`" ]
    then
    CONVERTSOFTWARE="avconv"
    elif [ -n "`echo $SW | grep ffmpeg`" ]
    then
    CONVERTSOFTWARE="ffmpeg"
    else
    echo "No hay ningún conversor disponible" >&2
        exit 1
    fi
}

Con esto, sólo tendremos que llamar a checkSoftware y a partir de ahí se establecerá la variable CONVERTSOFTWARE con el nombre de nuestro ejecutable.

Tras esto, sólo tendremos que llamar a:

1
$CONVERTSOFTWARE -i "$ARCHIVO"

Esto, yo prefiero meterlo en una función que he llamado getVideoInfo(), por si algún día decido cambiar algo o introducir algo más:

1
2
3
4
function getVideoInfo()
{
    $CONVERTSOFTWARE -i "$1" 2>&1
}

Al final, como avconv devuelve los mensajes por la salida de error, redirijo ésta a la salida estándar (2>&1) para no tener problemas.

Obtención de datos

Los datos los obtendremos a partir de la salida de avconv (o ffmpeg), filtrando las zonas de texto. Puede que haya archivos que por su naturaleza no nos devuelvan bien la información (porque tengan varios streams, o porque no tengan los streams bien formados, o no tengan stream de vídeo…), aunque por lo general sí que funcionará bien:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getAllData()
{
    FILE="$1"
    if [ ! -r "$FILE" ]
    then
    panic "File $FILE not found"
    fi
    FILEINFO="`getVideoInfo "$FILE"`"
    DURATION=$(echo "$FILEINFO" | sed -n "s/.* Duration: \([^,]*\), start: .*/\1/p")
    BITRATE=$(echo "$FILEINFO" | sed -n "s/.* bitrate: \([^,]*\) kb\/s/\1/p")
    FPS=$(echo "$FILEINFO" | sed -n "s/.*, \(.*\) fps.*/\1/p")
    FRAMES=$(echo $DURATION | awk -F':' "{ FRAMES=(\$1*3600+\$2*60+\$3)*$FPS; print FRAMES }")
}

Esto creará algunas variables:

  • FILEINFO: Contendrá la información en bruto devuelta por avconv
  • DURATION: Busco la palabra Duration y start y extraigo el valor que hay entre ellos, que es la duración en horas:minutos:segundos (segundos puede tener decimales)
  • BITRATE: Busco la palabra bitrate y kb/s y extraigo el número entre ellos, así consigo los kilobits por segundo.
  • FPS: Los fotogramas por segundo son un número seguido de la palabra fps
  • FRAMES: Los fotogramas, ya que no vienen en esta salida los calculo extrayendo el número total de segundos de duración y multiplicándolos por los FPS. Es decir: (horas*3600 + minutos*60 + segundos)*fps. En este caso, awk tomará como separador ‘:’ y cogerá el elemento $1, como hora; $2, como minutos; $3, como segundos. La barra delante del $ es un escapado, ya que hemos de diferenciar entre una variable de awk (\$1) y una variable de bash ($FPS).

El script completo

Aquí lo tenéis para copiar y pegar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/bin/bash
#
# Author: Gaspar Fernandez (blakeyed@totaki.com)
# Extracted from https://poesiabinaria.net
#
# Do whatever you want with this code.

function panic()
{
    echo "$1" 1>&2
    exit 1
}

function checkSoftware()
{
    SW="`which avconv; which ffmpeg`"

    if [ -n "`echo $SW | grep avconv`" ]
    then
    CONVERTSOFTWARE="avconv"
    elif [ -n "`echo $SW | grep ffmpeg`" ]
    then
    CONVERTSOFTWARE="ffmpeg"
    else
    panic "Failed to search conversion software in remote server"
    fi
}

function getVideoInfo()
{
    $CONVERTSOFTWARE -i "$1" 2>&1
}

function getAllData()
{
    FILE="$1"
    if [ ! -r "$FILE" ]
    then
    panic "File $FILE not found"
    fi
    FILEINFO="`getVideoInfo "$FILE"`"
    DURATION=$(echo "$FILEINFO" | sed -n "s/.* Duration: \([^,]*\), start: .*/\1/p")
    BITRATE=$(echo "$FILEINFO" | sed -n "s/.* bitrate: \([^,]*\) kb\/s/\1/p")
    FPS=$(echo "$FILEINFO" | sed -n "s/.*, \(.*\) fps.*/\1/p")
    FRAMES=$(echo $DURATION | awk -F':' "{ FRAMES=(\$1*3600+\$2*60+\$3)*$FPS; print FRAMES }")
}

checkSoftware

if [ -z "$1" ]
then
    panic "No input file"
fi

if [ -z "$2" ]
then
    FILE="$1"
    DATA="all"
else
    FILE="$2"
    DATA="$1"
fi

case "$DATA" in
    "all")
    getAllData "$FILE"
    echo "Duration: $DURATION"
    echo "FPS: $FPS"
    echo "Bitrate: $BITRATE kb/s"
    echo "Total frames: $FRAMES"
    ;;
    "duration")
    getAllData "$FILE"
    echo "$DURATION"
    ;;
    "seconds")
    getAllData "$FILE"
    SECS=$(($FRAMES/$FPS+$(($FRAMES%$FPS!=0))))
    echo $SECS
    ;;
    "fps")
    getAllData "$FILE"
    echo "$FPS"
    ;;
    "bitrate")
    getAllData "$FILE"
    echo "$BITRATE"
    ;;
    "frames")
    getAllData "$FILE"
    echo "$FRAMES"
    ;;
    *)
    panic "Element to extract not recognized: $DATA"
esac

El ejecutable tiene algunas opciones, si lo llamamos vinfo y ejecutamos:

$ vinfo mivideo.avi

Nos devolverá información general sobre el vídeo.

Duration: 00:26:25.36
FPS: 25
Bitrate: 1105 kb/s
Total frames: 39634

En cambio si especificamos una opción de las posibles (duration, fps, bitrate, frames) sólo devolverá el contenido del dato, sin ningún texto introductorio, para que podamos usar ese dato directamente en scripts.

$ vinfo frames mivideo.avi
39634

Actualización 11/07/2016: Actualización mínima del script (añade la duración en segundos)

Foto: “Film strip” by Bart from New Orleans, Louisiana, USA – Strip. Licensed under CC BY 2.0 via Commons

También podría interesarte...

There are 3 comments left Ir a comentario

  1. Pingback: Cómo extraer duración, fotogramas, bitrate y fps de un vídeo para nuestros scripts | PlanetaLibre /

  2. erro /
    Usando Mozilla Firefox Mozilla Firefox 55.0 en Ubuntu Linux Ubuntu Linux

    Hola: Ando buscando cómo obtener las dimensiones de un vídeo por script. Tienes algún ejemplo, por favor. Desde ya, muchas gracias.

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 56.0 en Ubuntu Linux Ubuntu Linux

      Hola!
      Puedes utilizar lo siguiente:
      $ ffprobe -v quiet -print_format flat -select_streams v:0 -show_entries stream=height,width video.mp4

      Sólo coge el primer stream del vídeo (v:0) puedes quitar “-select_streams v:0” y recoges la información de todos los streams.

      Saludos!

Leave a Reply