08 septiembre 2011

Poniendo a punto el YP-CP3 en Lignux

Hace poco, mi antiguo reproductor portátil (aka MP3), un Sansa Clip+ de 4GB dejó de funcionar; por suerte, me entraba en garantía y Fnac me hizo una tarjeta regalo por el mismo precio que este. Por desgracia, fue una gran pérdida, pues ese Sansa tenía ampliación de memoria por tarjeta microSD y reproducía Flac y Ogg (Vorbis).

Tras recibir la tarjeta, me puse a buscar un reproductor que tuviese las mismas características o similares en el catálogo de Fnac; sin embargo, lo único que me aparecían eran Samsung's, pero anteriormente ya había tenido uno y me había dado ciertos problemillas, además de que al final el conector USB, que estaba ensamblado dentro del propio artefacto, como los reproductores que estaba encontrando en el catálogo, dejó de funcionar bien y varias veces casi lo rompo debido a un golpe accidental.

Visto que no podía encontrar ningún reproductor que se ajustase a mis necesidades, envie un correo electrónico a los 3 Fnac's que hay en Barcelona, y me consiguieron encontrar una buena alternativa que se ajustase a mis necesidades, que no se encontraba en el catálogo, pero con el que contaban, y seguramente cuenten aún, con existencias en el Fnac de l'Illa Diagonal: el Samsung YP-CP3 (especificaciones), un reproductor de vídeo y audio (aka MP4), que acepta Flac y Vorbis y además se conecta al PC mediante un cable, con lo cual no corre el peligro de estropearse como mi anterior reproductor Samsung. Como podréis imaginar, es el reproductor que finalmente he adquirido.

Este reproductor, a la par que interesante, cuenta con cierta falta de soporte en Lignux; ya que pese a que se conecta sin ningún problema, no acaba de sincronizarse bien con Rhythmbox, y el programa que cabría instalar para poder sacarle toda la potencia (Emodio) requiere de Internet Explorer y el reproductor Windows Media, por lo tanto no se puede instalar mediante Wine. Pues bien, este artículo va a tratar de los pequeños parches que he realizado para poder utilizar sin problema este, de momento, gran reproductor.

Sincronizando con Rhythmbox

En primer lugar, quería aprovechar la capacidad de las últimas versiones de Rhythmbox de sincronizarse con los reproductores multimedia, y para ello apliqué un viejo truco, que es añadir un archivo con nombre .is_audio_player en la raíz del sistema de ficheros del dispositivo; sin embargo, este archivo puede especificar más información sobre el reproductor que quizás, y en mi caso, en una Debian Testing, no se encuentren en el paquete media-player-info, quedando mi archivo .is_audio_player de la siguiente forma:

output_formats=audio/mpeg,audio/x-ms-wma,application/ogg,audio/ogg,audio/flac,audio/x-wav,image/jpeg,text/plain,video/x-ms-wmv,video/x-msvideo
input_formats=audio/mpeg
audio_folders=Music/

(Fuente: FAQ Rhythmbox)

Transfiriendo las listas de reproducción

Una vez que Rhythmbox reconoce bien el reproductor y les transfiere las pistas en los formatos que acepta (si no, primero las transformaría a mp3), sería interesante conseguir que las listas de reproducción definidas en Rhythmbox pudiesen reproducirse también en el reproductor; sin embargo, el CP3 sólo acepta listas en formato SPL (un formato propio de Samsung) y WPL, el formato de Windows Media, pero Rhythmbox deja las listas en la raíz del sistema en formato PLS.

Indagando un poco por internet, encontré, entre otros, que el proyecto libmtp ya había programado una utilidad para este tipo de listas; sin embargo, el reproductor no se conecta mediante MTP, así que no podía servirme de mucho. Pese a esto, también encontré cierta información en los foros de anythingbutipod (1 y 2), obteniendo jutno con un par de pruebas las características necesarias para preparar un archivo SPL que reconociese el reproductor:

  • Las primeras dos líneas deben ser la especificación de que se trata de un archivo SPL y la versión del formato (1.00 o 2.00 según el firmware), junto con un espacio en blanco:
    SPL PLAYLIST
    VERSION 1.00
  • Cada canción debe especificarse en una línea con su ruta siguiendo el siguiente formato: \Music\Ruta\Archivo 1.mp3
  • Un espacio en blanco y una línea indicando que se acabado la lista:

    END PLAYLIST
  • El archivo debe estar codificado en formato UTF-16
  • Los saltos de línea deben estar especificados en formato CR+LF (Windows)

Siguiendo estas reglas, obtenemos un archivo SPL válido; ahora lo que nos falta es convertir las listas en PLS a este formato. Para tal fin, yo he hecho un pequeño script en Shell y AWK, que he bautizado como pls2spl.sh y que, dada la ruta donde se encuentra el reproductor, lee todas las listas sincronizadas por Rhythmbox, que por defecto se guardan en la raíz, y una vez transformadas las mueve a la carpeta Playlists:

#!/bin/bash

# Script AWK que transforma un archivo PLS al formato SPL
awk='BEGIN {
FS="/";
OFS="\\";
print "SPL PLAYLIST\nVERSION 2.00\n";
}

/[Ff]ile([0-9]+)=/ { # Sólo nos interesan las rutas de los archivos
printf "%s", gensub("[Ff]ile([0-9]+)=", OFS, 1, $1);
for(i=2; i <= NF; i++) printf "\\%s", $i;
printf "\n";
}

END {
print "\nEND PLAYLIST";
}'

TMPFILE=/tmp/spl;

# Función para obtener el nombre del archivo en una ruta
function filename() {
echo echo $i | awk 'BEGIN{FS="/"} {print $NF}';
}

readonly DIR=Playlists; # Donde se guardan las listas de reproducción

#Comprobaciones iniciales
if (( $# < 1 )); then
echo "Por favor, especifica la ruta al dispositivo (ex: /media/ypcp3)";
exit 1;
elif (( $# > 1 )); then
echo "Sólo puedes especificar un dispositivo";
exit 1;
fi

# Preparamos el awk para transformar el archivo pls
awkfile=pls2spl.awk;
echo "$awk" > $awkfile;

# Nos aseguramos de que $DEVICE no tenga un barra al final
DEVICE=$(echo $1 | awk '{if ( substr($0, length($0),1) == "/") print substr($0, 0, length($0)-1); else print $0 }' );

IFS=$(echo -e "'\n'");
for i in $(ls -1 $DEVICE/*.pls); do
awk -f $awkfile $i > $TMPFILE;

# Transformamos a CRLF (Windows) y a Unicode-16
sed s/$/'\r'/ $TMPFILE | iconv -f UTF-8 -t UTF-16 -o $DEVICE/$DIR/$(filename $i | sed s/.pls/.spl/) -;

echo -n "Se ha convertido ";
filename $i;

rm $TMPFILE;
rm $i;
done

rm $awkfile;

He de hacer notar que en el archivo .is_audio_player se podría haber especificado en qué carpeta debe guardar Rhythmbox las listas. Si lo hiciésemos, Rhythmbox especificaría la ruta del archivo de la forma file:///media/cp3/Music/archivo%201.mp3, lo cual no es una ruta válida para el formato SPL; debido a esto, he preferido mantener el comportamiento por defecto de Rythmbox, lo cual facilita la conversión, evitando el texto escapado, es decir, las construcciones del tipo %20.

Convirtiendo los vídeos

Pese a que según las especificaciones de la página web el reproductor acepta WMV, MPEG4, AVI, MP4, ASF, según las especificaciones del manual de usuario el reproductor sólo reproduce los siguientes formatos de vídeo:

AVI/SVI: MPEG-4 Perfil simple de vídeo (640x480, 800kbps), MP3 Audio
RM/RMVB: Vídeo Real Media (640x480, 800kbps), Real Audio
WMV: Vídeo WMV9 (640x480, 500kbps), Audio WMA

Con tal de poder transformar los vídeos con comodidad al formato AVI que especifica, y sobre todo poder realizar el escalado de forma automática manteniendo la proporción, he realizado un pequeño script para Avidemux valiéndome de lo que he aprendido con la entrada anterior; además, este script lo he añadido como ajuste personalizado dentro de mi carpeta personal, tal y como explican en la wiki de Avidemux. En el script, que os muestro a continuación, podréis notar que he realizado ciertas operaciones para poder realizar el escalado; si es necesario, podéis encontrar una gran referencia y fuente de información sobre javascript en el w3schools.

//AD  <- Needed to identify//
//--automatically built--

var app = new Avidemux();

//** Video **
// ** Preparing
var maxw=640, maxh=480; // Max permited by device
var resx=400, resy=240, dratio=resx/resy; // dratio=15/9=5/3 (Display)

//** Postproc **
app.video.setPostProc(3,3,0);
app.video.fps1000 = 25000;

//** Scaling **
var width=app.video.width;
var height=app.video.height;
var vratio=width/height;

if (width >= height) {
width=resx=maxw; // Video will fit up to device
height=Math.round((width/vratio)/2)*2; // Values must be pair
resy=resx/dratio;

if (height > resy || height > maxh) {
height=resy; // To preserve display and video ratio
width=Math.round((height*vratio)/2)*2; // Values must be pair
resx=resy*dratio;
}
}
else { // Improbably
height=resy=maxh;
width=Math.round((height*vratio)/2)*2;
resx=resy*dratio;

if (width > resx || width > maxw) {
width=resx;
height=Math.round((width/vratio)/2)*2;
resy=resx/dratio;
}
}

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

// ** Filling with black to preserve aspect ratio (padding)

var padw=(resx-width)/2, padh=(resy-height)/2;

app.video.addFilter("addblack","left="+padw,"right="+padw,"top="+padh,"bottom="+padh);

//** Video Codec conf **
app.video.codecPlugin("92B544BE-59A3-4720-86F0-6AD5A2526FD2", "Xvid", "CBR=800", "?xml version='1.0'?><XvidConfig><presetConfiguration><name><default></name><type>default</type></presetConfiguration><XvidOptions><threads>0</threads><vui><sarAsInput>true</sarAsInput><sarHeight>1</sarHeight><sarWidth>1</sarWidth></vui><motionEstimation>high</motionEstimation><rdo>dct</rdo><bFrameRdo>false</bFrameRdo><chromaMotionEstimation>true</chromaMotionEstimation><qPel>false</qPel><gmc>false</gmc><turboMode>false</turboMode><chromaOptimiser>false</chromaOptimiser><fourMv>false</fourMv><cartoon>false</cartoon><greyscale>false</greyscale><interlaced>none</interlaced><frameDropRatio>0</frameDropRatio><maxIframeInterval>300</maxIframeInterval><maxBframes>2</maxBframes><bFrameSensitivity>0</bFrameSensitivity><closedGop>false</closedGop><packed>false</packed><quantImin>1</quantImin><quantPmin>1</quantPmin><quantBmin>1</quantBmin><quantImax>31</quantImax><quantPmax>31</quantPmax><quantBmax>31</quantBmax><quantBratio>150</quantBratio><quantBoffset>100</quantBoffset><quantType>mpeg</quantType><intraMatrix><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value></intraMatrix><interMatrix><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value></interMatrix><trellis>true</trellis><singlePass><reactionDelayFactor>16</reactionDelayFactor><averagingQuantiserPeriod>100</averagingQuantiserPeriod><smoother>100</smoother></singlePass><twoPass><keyFrameBoost>10</keyFrameBoost><maxKeyFrameReduceBitrate>20</maxKeyFrameReduceBitrate><keyFrameBitrateThreshold>1</keyFrameBitrateThreshold><overflowControlStrength>5</overflowControlStrength><maxOverflowImprovement>5</maxOverflowImprovement><maxOverflowDegradation>5</maxOverflowDegradation><aboveAverageCurveCompression>0</aboveAverageCurveCompression><belowAverageCurveCompression>0</belowAverageCurveCompression><vbvBufferSize>0</vbvBufferSize><maxVbvBitrate>0</maxVbvBitrate><vbvPeakBitrate>0</vbvPeakBitrate></twoPass></XvidOptions></XvidConfig>");

//** Audio **
app.audio.reset();
app.audio.codec("Lame",128,20,"80 00 00 00 01 00 00 00 01 00 00 00 09 00 00 00 00 00 00 00 ");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer="NONE";
app.setContainer("AVI");
setSuccess(1);
//app.Exit();

//End of script

Sin embargo, pese a haber realizado este script, y siendo Avidemux un gran programa, como mínimo de transcodificación de archivos; me he encontrado con vídeos que sufrían ciertos problemas al ser transcodificados, como por ejemplo un desfase en el audio respecto al vídeo, que han hecho que utilice también otro gran programa: ffmpeg (sí, sé que mencioné que lo encontraba un poco complicado, pero para usarlo habitualmente; una vez establecidos unos parámetros que no van a variar, no lo he encontrado tan complicado). Pues con tal de realizar el mismo trabajo que con Avidemux, he hecho otro script (sí, también sé que últimamente estoy algo pesado con ellos) que se encarga también de mantener la proporción del archivo a la hora de escalarlo:

#!/bin/bash

# Constantes
readonly resx=400;
readonly resy=240;
readonly maxw=640;
readonly maxh=480;

IFS=$(echo -e "\n");

# Comprobaciones
if (( $# < 1 )); then
echo "Debes especificar los vídeos a codificar";
exit 1;
fi


# Funciones

# Recoge información sobre el vídeo en $width y $height
# $1 -> Ruta al archivo de vídeo
function getInfo() {
local video=$(ffmpeg -i $1 2>&1 | grep -iw "video:")
local size=$(echo $video | egrep -o "[0-9]+x[0-9]+");

width=$(echo $size | cut -d"x" -f1);
height=$(echo $size | cut -d"x" -f2);
}

# $1 -> Elemento a dividir
# $2 -> Width
# $3 -> Height
function divPerRatio() {
echo $(( ($1 * $3) / $2 ));
}

# $1 -> Elemento a multiplicar
# $2 -> Width
# $3 -> Height
function mulPerRatio() {
echo $(( ($1 * $2) / $3 ));
}

# Pre: haber obtenido la información de $width y $height (con getInfo)
# Retorna el tamaño del vídeo ($x $y) y la resolución en pantalla ($rx $ry)
function getRes() {
if (($width >= $height)); then
x=$maxw;
rx=$x; # Para encajar el vídeo en el dispositivo
y=$(divPerRatio $x $width $height);
ry=$(divPerRatio $rx $resx $resy);

if (($y > $ry || $y > $maxh)); then
y=$ry; # Para conservar la proporción del dispositivo y el vídeo
x=$(mulPerRatio $y $width $height);
rx=$(mulPerRatio $ry $resx $resy);
fi
else # Improbable
y=$maxh;
ry=$y;
x=$(mulPerRatio $y $width $height);
rx=$(mulPerRatio $ry $resx $resy);

if (($x > $rx || $x > $maxw)); then
x=$rx;
y=$(divPerRatio $x $width $height);
ry=$(divPerRatio $rx $resx $resy);
fi
fi
}


# Main
for (( f=1 ; f <= $# ; f++ )); do
file=${!f};

# Añadimos extension si no la tiene
if ! echo $file | egrep "\..{1,3}$" > /dev/null; then
ln $file $file.ln;
file=$file.ln;
LKD=1;
fi

getInfo $file;
getRes;

posx=$(( ($rx - $x)/2 ));
posy=$(( ($ry - $y)/2 ));

output=$(echo $file | sed -r s/'\..{1,3}$'/.cp3.avi/);

# Ejecución
ffmpeg -i $file -ab 128k -acodec libmp3lame -vcodec libxvid -b 800k -r 25 -s ${x}x$y -vf pad="$rx:$ry:$posx:$posy" $output;

# Borramos archivo con extensión
if (( $LKD )); then
LKD=0;
rm $file;
fi
done

Como podréis comprobar, transforma todo aquellos vídeos que se le pasen como argumento al script; por ejemplo:

./ypcp3.ffmpeg.sh video1.mp4 video2 video3.wmv

Pues hasta aquí hemos llegado. Espero que este artículo os haya sido de ayuda, y si no, a mí me ha resultado útil para mantener estos scripts y esta información como apunte en mi blog.

Un saludo,
Morpheus

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