01 septiembre 2011

Cambiando el tamaño de tus vídeos con Avidemux

Siguiendo en la línea de la entrada anterior, voy a seguir en esta entrada explicando cómo transformar diversos vídeos (en alta definición) en vídeos más pequeños pero con una calidad aceptable; en mi caso, y en mi percepción, se trata de un resultado más que aceptable.

Para ello, he utilizado Avidemux; sí, sé que podría haber usado MEencoder (de MPlayer) o FFmpeg, pero aunque ya los he utilizado alguna vez, me resulta difícil configurar todas las opciones del codificador mediante la línea de comandos. Entonces os preguntaréis: ¿cómo piensas hacerlo con Avidemux? Pues la respuesta es a medias.

Pero primero, vayamos por pasos. Antes de nada, debemos tener instalado avidemux (junto a su interfaz de línea de comandos, CLI):

# aptitude install avidemux-cli avidemux

Ahora, abrimos uno de los vídeos que queramos recodificar con él. Una vez hecho esto, configuramos todos los parámetros de vídeo y audio que querremos que tengan nuestros vídeos; en mi caso, escalar los vídeos a la mitad de su tamaño mediante el filtro dimensionar y ponerle una tasa de bits constante de 1500 kbps (que ya está puesto por defecto):

Tras ello, vamos a Archivo -> Guardar Proyecto como... y lo guardamos con la extensión .js, pues se guardarán los parámetros de nuestro proyecto como un archivo en javascript. Una vez hecho esto, abrimos el archivo con un editor de texto y eliminamos las siguientes líneas:

// 01 videos source 
app.load("/ruta/al/video.ext");
//01 segments
app.clearSegments();
app.addSegment(0,0,3017);
app.markerA=0;
app.markerB=3016;

Eliminar estas líneas es necesario ya que son configuraciones dependientes del vídeo que hemos abierto para guardar el script. Los marker son las marcas que definen el segmento de vídeo que vamos a transformar, y como comprenderéis, son diferentes para cada vídeo, motivo por el cual no se deben establecer aquí; lo mismo para la ruta.

Una vez creado y modificado el script de configuración, es hora de utilizarlo; para ello, debemos ejecutar la siguiente línea:

avidemux2_cli --force-alt-h264 --load video.ext --run script.js --save video.res.ext --quit

Los parámetros utilizados son:

--force-alt-h264
Para que en los vídeos que estén codificados con H.264 se habran correctamente sin necesidad de preguntar al usuario; lo pongo porque la mayoría de las cámaras gravan en MOV, que no deja de ser, en la gran mayoría de veces, un contenedor de este codec.
--load
Para cargar el vídeo que deseamos.
--run
Para ejecutar el script que hemos guardado anteriormente.
--save
Para indicar dónde debe guardar el vídeo recodificado.
--quit
Para finalizar el programa en cuanto haya acabado.

Una vez contamos con el script y conociendo cuál es el comando a ejecutar, para recodificar diversos vídeos deberíamos ejecutar el siguiente comando (en bash):

for i in `ls *.ext`; do
 output=$(echo $i | sed s/.ext/sca.ext/);
 avidemux2_cli --force-alt-h264 --load $i --run script.js --save $output --quit;
done

De esta forma, se transformarán todos los archivos con la extensión ext utilizando la configuración almacenada en script.js.

Si queréis realizar algo un tanto más complejo, podéis modificar parte del javascript, como yo he hecho a la hora de establecer el escalado, haciendo que el vídeo se escale en la proporción que hayas establecido en la variable scale:

//** Scaling **
var scale=1/2;
var width=app.video.width*scale;
var height=app.video.height*scale;

displayInfo("Video will be scaled to "+width+"x"+height+" pixels.");

app.video.addFilter("resize","w="+width,"h="+height,"algo=1");

Si aún así queréis hacerlo todo más compacto, podéis incluir el propio javascript dentro de otro script en bash. Os presento como ejemplo el que he realizado para convertir mis vídeos (atención a las comillas de las variables y los \' que he puesto en la versión del xml):

#!/bin/bash

DIR=/tmp/sca
SCRIPT='//AD  <- Needed to identify//
//--automatically built--

var app = new Avidemux();

//** Video **
//** Postproc **
app.video.setPostProc(3,3,0);

app.video.fps1000 = 25000;

//** Filters **

//** Scaling **
var scale=1/2;
var width=app.video.width*scale;
var height=app.video.height*scale;

displayInfo("Video will be scaled to "+width+"x"+height+" pixels.");

app.video.addFilter("resize","w="+width,"h="+height,"algo=1");

//** Video Codec conf **
app.video.codecPlugin("32BCB447-21C9-4210-AE9A-4FCE6C8588AE", "x264", "2PASSBITRATE=1500", "<?xml version='\'1.0\''?><x264Config><presetConfiguration><name>&lt;default&gt;</name><type>default</type></presetConfiguration><x264Options><fastFirstPass>true</fastFirstPass><threads>0</threads><deterministic>true</deterministic><sliceThreading>false</sliceThreading><threadedLookahead>-1</threadedLookahead><idcLevel>-1</idcLevel><vui><sarAsInput>true</sarAsInput><sarHeight>1</sarHeight><sarWidth>1</sarWidth><overscan>undefined</overscan><videoFormat>undefined</videoFormat><fullRangeSamples>true</fullRangeSamples><colorPrimaries>undefined</colorPrimaries><transfer>undefined</transfer><colorMatrix>undefined</colorMatrix><chromaSampleLocation>0</chromaSampleLocation></vui><referenceFrames>3</referenceFrames><gopMaximumSize>250</gopMaximumSize><gopMinimumSize>0</gopMinimumSize><scenecutThreshold>40</scenecutThreshold><periodicIntraRefresh>false</periodicIntraRefresh><bFrames>3</bFrames><adaptiveBframeDecision>1</adaptiveBframeDecision><bFrameBias>0</bFrameBias><bFrameReferences>strict</bFrameReferences><loopFilter>true</loopFilter><loopFilterAlphaC0>0</loopFilterAlphaC0><loopFilterBeta>0</loopFilterBeta><cabac>true</cabac><openGop>disabled</openGop><interlaced>disabled</interlaced><constrainedIntraPrediction>false</constrainedIntraPrediction><cqmPreset>flat</cqmPreset><intra4x4Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></intra4x4Luma><intraChroma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></intraChroma><inter4x4Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></inter4x4Luma><interChroma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></interChroma><intra8x8Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></intra8x8Luma><inter8x8Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></inter8x8Luma><analyse><partitionI4x4>true</partitionI4x4><partitionI8x8>true</partitionI8x8><partitionP8x8>true</partitionP8x8><partitionP4x4>false</partitionP4x4><partitionB8x8>true</partitionB8x8><dct8x8>true</dct8x8><weightedPredictionPframes>smart</weightedPredictionPframes><weightedPrediction>true</weightedPrediction><directPredictionMode>spatial</directPredictionMode><chromaLumaQuantiserDifference>0</chromaLumaQuantiserDifference><motionEstimationMethod>hexagonal</motionEstimationMethod><motionVectorSearchRange>16</motionVectorSearchRange><motionVectorLength>-1</motionVectorLength><motionVectorThreadBuffer>-1</motionVectorThreadBuffer><subpixelRefinement>7</subpixelRefinement><chromaMotionEstimation>true</chromaMotionEstimation><mixedReferences>true</mixedReferences><trellis>finalMacroblock</trellis><fastPSkip>true</fastPSkip><dctDecimate>true</dctDecimate><psychoRdo>1</psychoRdo><psychoTrellis>0</psychoTrellis><noiseReduction>0</noiseReduction><interLumaDeadzone>21</interLumaDeadzone><intraLumaDeadzone>11</intraLumaDeadzone></analyse><rateControl><quantiserMinimum>10</quantiserMinimum><quantiserMaximum>51</quantiserMaximum><quantiserStep>4</quantiserStep><maximumConstantRateFactor>0</maximumConstantRateFactor><averageBitrateTolerance>1</averageBitrateTolerance><vbvMaximumBitrate>0</vbvMaximumBitrate><vbvBufferSize>0</vbvBufferSize><vbvInitialOccupancy>0.9</vbvInitialOccupancy><ipFrameQuantiser>1.4</ipFrameQuantiser><pbFrameQuantiser>1.3</pbFrameQuantiser><adaptiveQuantiserMode>variance</adaptiveQuantiserMode><adaptiveQuantiserStrength>1</adaptiveQuantiserStrength><mbTree>true</mbTree><frametypeLookahead>40</frametypeLookahead><quantiserCurveCompression>0.6</quantiserCurveCompression><reduceFluxBeforeCurveCompression>20</reduceFluxBeforeCurveCompression><reduceFluxAfterCurveCompression>0.5</reduceFluxAfterCurveCompression></rateControl><accessUnitDelimiters>false</accessUnitDelimiters><spsIdentifier>0</spsIdentifier><sliceMaxSize>0</sliceMaxSize><sliceMaxMacroblocks>0</sliceMaxMacroblocks><sliceCount>0</sliceCount><hrd>none</hrd></x264Options></x264Config>");

//** Audio **
app.audio.reset();
app.audio.codec("Faac",128,4,"80 00 00 00 ");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer="NONE";
app.setContainer("MP4");
setSuccess(1);
//app.Exit();

//End of script';

SCRNAME="script.js";

echo "$SCRIPT" > $SCRNAME;

mkdir -p $DIR;
for i in `ls *.MOV`; do
 output=$(echo $i | sed s/.MOV/sca.mp4/);
 avidemux --nogui --force-alt-h264 --load $i --run $SCRNAME --save $DIR/$output --quit;
 rm $DIR/$output.stat*;
done

rm $SCRNAME;

Fuentes utilizadas:

Pues eso es todo. Espero que este pequeño tutorial y el de la entrada anterior os hayan sido de ayuda; desde luego, para mí lo han sido, reduciendo unos 7,9 GB de imágenes y vídeos en tan solo 2,6 GB.

Espero no haber sido muy cargante con estas últimas entradas, pero quería conservar como apuntes este pequeño trabajo, que estoy bien seguro que me serán de utilidad en el futuro.

Un saludo,
Morpheus