Automatisation de la conversion des enregistrements de la Freebox pour N900

Ce billet montre la chaine complète de conversion des enregistrements des chaines de la TNT par une Freebox afin d’être lus par le N900. Il s’appuie sur les articles précédents en y ajoutant l’automatisation de la récupération des fichiers. La chaine de conversion se compose des étapes suivantes:

  1. rapatriement des enregistrements sur le PC
  2. conversion des enregistrement en H264
  3. rapatriement des fichiers convertis sur le N900

Chacune des étapes sera automatisée et s’effectuera par étage c’est-à-dire qu’une étape devra être complète avant de passer à la suivante. La procédure pourra être interrompue à tout moment pour se poursuivre plus tard. L’ensemble de la procédure doit minimiser l’espace disque utilisé et les étapes doivent s’exécuter de manière concurrente pour le mieux (1 en même temps que 3 par ex)

Rapatriement des enregistrements sur le PC

L’accès à la freebox se fait en FTP. La méthode utilisée est celle du miroitage différentiel à l’aide de l’outil lftp. Lftp permet de miroiter un répertoire entier ce qui correspond à notre problématique. Pour cela nous utilisons la commande mirror de lftp. L’aspect différentiel ou incrémental nous permet de reprendre un rapatriement interrompu. C’est l’option -c de la commande mirror. Mais ce n’est pas suffisant. Pour minimiser l’espace disque, une fois le rapatriement fait, les fichiers sont déplacés vers le répertoire de travail de l’étape 2. Il ne faut alors pas que la prochaine exécution de l’étape 1 ramène de nouveau les fichiers déjà traités ( la suppression des enregistrements est volontairement exclue de la procédure). En solution nous conservons la date du dernier mirroitage complet et utilisons l’option –newer-than=aaaammdd de la commande mirror qui parle d’elle-même. Pour finir le script de miroitage ne travaille que si aucun enregistrement n’est en cours. Pour cela nous comparons 2 fois de suite le contenu du répertoire des enregistrements de la Freebox.

En plus des spécificités précédente, ce script comme les autres, utilise la présence d’un fichier .lock pour s’assurer qu’aucune autre instance de lui-même est en train de travailler et rattrape les signaux SIGTERM SIGINT et SIGHUP pour faire le nettoyage qui se limite ici à supprimer les fichiers temporaires et le fichier de verrouillage.

Voici le script de téléchargement des enregistrements:

#!/bin/bash
echo "***********************************"
echo "*                                 *"
echo "*   Freebox record downloader     *"
echo "*                                 *"
echo "***********************************"

INPUT_DIR=.
FINAL_DIR=..
# Change 0000 by the password
CREDENTIAL=freebox,0000
LAST_MIRROR_FILE="$INPUT_DIR/.last_mirror"

# do not allow concurrency
if [ -e "$INPUT_DIR/.lock" ]; then
 exit 1
else
 touch "$INPUT_DIR/.lock"
fi

function cleanup_action {
 rm -f "$INPUT_DIR/.listing1.txt"
 rm -f "$INPUT_DIR/.listing2.txt"
 rm -f "$INPUT_DIR/.lock"
}

function clean_shutdown {
 echo "shutdown sequence..." 
 cleanup_action
 exit 1
}

trap 'clean_shutdown' SIGTERM SIGINT SIGHUP

# do not start download if the freebox is recording
lftp -u $CREDENTIAL -e "open; ls /Disque dur/Enregistrements/ ; quit" hd1.freebox.fr > "$INPUT_DIR/.listing1.txt"
sleep 5
lftp -u $CREDENTIAL -e "open; ls /Disque dur/Enregistrements/ ; quit" hd1.freebox.fr > "$INPUT_DIR/.listing2.txt"
cmp -s "$INPUT_DIR/.listing1.txt" "$INPUT_DIR/.listing2.txt" > /dev/null
if [ $? -eq 0 ]; then
 if [ -e "$LAST_MIRROR_FILE" ]; then 
 LAST_MIRROR=`cat "$LAST_MIRROR_FILE"`
 else
 LAST_MIRROR=0
 fi
 LAST_MIRROR_DATE=`date +%Y%m%d`
 lftp -u freebox,4085 -e "mirror -c --newer-than=$LAST_MIRROR /Disque dur/Enregistrements/ .; quit" hd1.freebox.fr
 # mirroring finished, save the current date because
 # we don't want to download these files again
 echo $LAST_MIRROR_DATE > "$LAST_MIRROR_FILE"
 # move files to processing dir if not buzy
 if [ ! -e "$FINAL_DIR/.lock" ]; then
 mv -u -f *.ts "$FINAL_DIR"
 fi
fi

cleanup_action

Voici le script de tâche cron à mettre dans /etc/cron.hourly

#!/bin/sh
N900_DIR=/home/thierry/Vidéos/n900
cd $N900_DIR/backup
./download.sh
#change thierry by your user
chown  thierry:thierry  $N900_DIR/*.ts

Conversion des enregistrements en H264

Une fois que les fichiers sont déplacés dans le répertoire /home/thierry/Vidéos/n900 un script se charge de les convertir en h264. Ce script commence par s’assurer qu’une autre instance de lui-même n’est pas déjà en train de travailler. La conversion elle même se base sur l’origine du fichier pour déterminer la résolution optimale (voir ce billet). Notons que la commande ffmpeg doit être lancé en arrière-plan car elle tue le shell quand elle se termine (étrange, peut-être spécifique à ma configuration?). Par ailleurs comme la conversion est un processus long, le script n’écrase pas le fichier de destination si il existe déjà. Pour finir le script rattrape les signaux d’interruptions pour faire le ménage. Dans son cas, le ménage consiste surtout à ne pas laisser un fichier converti incomplet. Donc on supprime le fichier en cours de création. Le prochain lancement le récréera. Enfin notons que le script supprime le fichier original une fois sa conversion terminée avec succès.

Voici le fichier de conversion:

#!/bin/bash

echo "***********************************"
echo "*                                 *"
echo "*    video converter for N900     *"
echo "*                                 *"
echo "***********************************"

INPUT_DIR=.
OUTPUT_DIR=$INPUT_DIR/converted

# do not allow concurrency
if [ -e "$INPUT_DIR/.lock" ]; then
 exit 1
else
 touch "$INPUT_DIR/.lock"
fi

function convert_file {
 original_def=$2
 original_file=$1
 converted_file="$OUTPUT_DIR/$original_file.mp4"

 if [ -e "$converted_file" ]; then
 return 1
 fi

 if [ $original_def == "mpeg" ]; then
 SIZE="400x480"
 elif [ $original_def == "dvd" ]; then
 SIZE="592x480"
 elif [ $original_def == "hd" ]; then
 SIZE="640x480"
 elif [ $original_def == "other" ]; then
 SIZE="320x240"
 else
 echo "format $original_def inconnu"
 return 1
 fi

 echo $converted_file > .work_in_progress
 (ffmpeg -i "$original_file" -acodec libfaac -vcodec libx264  -vpre hq -vpre baseline -crf 25 -maxrate 1000k -bufsize 2000k -threads 0 -flags2 -fastpskip  -s "$SIZE" "$converted_file" ) &
 echo $! > .work_pid
 wait       
 if [ $? -eq 0 ]; then
 rm "$original_file"
 fi
 rm .work_pid
 rm .work_in_progress

}

function clean_shutdown {
 echo "shutdown sequence..."
 if [ -e ".work_pid" ]; then
 kill `cat .work_pid`
 rm .work_pid
 fi
 if [ -e .work_in_progress ]; then
 rm "`cat .work_in_progress`"
 rm .work_in_progress
 fi
 rm "$INPUT_DIR/.lock"
 exit 1
}

trap 'clean_shutdown' SIGTERM SIGINT SIGHUP

find $INPUT_DIR -maxdepth 1 -type f ( -name "France 3*.ts" -o -name "France 4*.ts" -o -name "France 5*.ts" -o -name "La Chaîne*.ts" -o -name "NT1*.ts" -o -name "TMC*.ts" ) -print | ( while read i; do convert_file "$i" mpeg; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "DirectStar*.ts" -o -name "RTL9*.ts" -o -name "AB*.ts" ) -print | ( while read i; do convert_file "$i" dvd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "Arte*.ts" -o -name "Direct 8*.ts" -o -name "France 2*.ts" -o -name "NRJ*.ts" ) -print | ( while read i; do convert_file "$i" hd; done )

# this just for any extra video i could put in the process directory
find $INPUT_DIR -maxdepth 1 -type f ( -name "*.mpeg" -o -name "*.vob" -o -name "*.mpg" -o -name "*.avi" ) | ( while read i; do convert_file "$i" other; done )

rm "$INPUT_DIR/.lock"

Voici le script cron à mettre dans /etc/cron.hourly

#!/bin/sh
N900_DIR=/home/thierry/Vidéos/n900
cd $N900_DIR
./convert_all.sh
chown -R thierry:thierry  ./converted

rapatriement des fichiers convertis sur le N900

Le rapatriement des fichiers est assuré par un script qui s’exécute sur le N900 et qui s’appuie sur la présence d’un deamon rsync sur le PC. Le script commence par s’assurer qu’il n’y a pas de conversion en cours sinon il rapatrierait un fichier incomplet. Puis il fait un rsync en spcifiant les options –remove-sent-files et –ignore-existing afin d’une part de libérer du disque sur le PC et d’autre part de limiter le temps de synchronisation en prevenant le rapatriement de fichiers qui seraient convertis en plusieurs fois (possible si on ne supprime pas les enregistrements sur la Freebox).
Voici le fichier de script à placer sur le N900:

#!/bin/sh
LOCAL_REPLAY_DIR=/home/user/MyDocs/.videos/replay
LOCK_FILE=$LOCAL_REPLAY_DIR/.lock
if [ -e $LOCK_FILE ]; then
	rm $LOCK_FILE
fi
rsync 192.168.0.12::"videos/n900/.lock"  $LOCAL_REPLAY_DIR/
if [ -e  $LOCK_FILE ]; then
  exit 1
fi
rsync -v --remove-sent-files --ignore-existing 192.168.0.12::"videos/n900/converted/*.mp4" $LOCAL_REPLAY_DIR

La planification de ce script est dévolue au l’outil Alarmed. On peut par exemple le programmer tous les jours à 7h00.

Cette chaine de conversion permet de libérer son utilisateur des contraintes de copie et de lancement de commande de conversion. Il n’a qu’à programmer les enregistrements pour que quelques heures plus tard ils soient disponibles pour un visionnage en mobilité !

Nettoyage en cas de terminaison brusque d’un script Bash

Lors de la réalisation de l’automatisation de conversion vidéo dans le billet précédent, nous avons déja vu comment empêcher la réentrance avec la création d’un fichier “.lock”. Ce fichier est un artéfact de travail qui doit être supprimé quand le script se termine. Or le script peut être interrompu (tué) avant qu’il n’arrive à son terme.

Dans ce cas il faut quand même essayer de faire le ménage afin de permettre à nouveau l’execution du script. Bash contient une instruction trap qui permet de répondre aux signaux systèmes (SIGXXX) sauf SIGKILL (kill -9). Cela laisse en fait toute la lattidute de réagir au CTR-C ou à la commande kill de base (sans le -9).

Dans l’exemple du billet précédent l’interception des signaux peut être utilisée pour d’une part, supprimer le fichier qui était en train d’être encodé car il sera incomplet et d’autre part pour supprimer le fichier “.lock”. Le code suivant en illustre la mise en oeuvre:

#!/bin/bash

echo "***********************************"
echo "*                                 *"
echo "*    video converter for N900     *"
echo "*                                 *"
echo "***********************************"



INPUT_DIR=.
OUTPUT_DIR=$INPUT_DIR/converted



# do not allow concurrency 
if [ -e "$INPUT_DIR/.lock" ]; then
  exit 1
else
  touch "$INPUT_DIR/.lock"
fi

function convert_file {
	original_def=$2
	original_file=$1
	converted_file="$OUTPUT_DIR/$original_file.mp4"

	if [ -e "$converted_file" ]; then
		return 1
	fi
	
 	if [ $original_def == "mpeg" ]; then
		SIZE="400x480"
	elif [ $original_def == "dvd" ]; then
		SIZE="592x480"
	elif [ $original_def == "hd" ]; then
		SIZE="640x480"
	elif [ $original_def == "other" ]; then
		SIZE="320x240"
	else
		echo "format $original_def inconnu"
		return 1
	fi

	echo $converted_file > .work_in_progress
	(ffmpeg -i "$original_file" -acodec libfaac -vcodec libx264  -vpre hq -vpre baseline -crf 25 -maxrate 1000k -bufsize 2000k -threads 0 -flags2 -fastpskip  -s "$SIZE" "$converted_file" ) &
	echo $! > .work_pid
	wait        
	if [ $? -eq 0 ]; then
		rm "$original_file"
	fi
	rm .work_pid
	rm .work_in_progress


}

function clean_shutdown {
  echo "shutdown sequence..."
  if [ -e ".work_pid" ]; then
	kill `cat .work_pid`
	rm .work_pid
  fi
  if [ -e .work_in_progress ]; then
        rm "`cat .work_in_progress`"
	rm .work_in_progress
  fi
  rm .lock
  exit 1
}

trap 'clean_shutdown' SIGTERM SIGINT



find $INPUT_DIR -maxdepth 1 -type f ( -name "France 3*.ts" -o -name "France 4*.ts" -o -name "France 5*.ts" -o -name "La Chaîne*.ts" -o -name "NT1*.ts" -o -name "TMC*.ts" ) -print | ( while read i; do convert_file "$i" mpeg; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "DirectStar*.ts" -o -name "RTL9*.ts" -o -name "AB*.ts" ) -print | ( while read i; do convert_file "$i" dvd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "Arte*.ts" -o -name "Direct 8*.ts" -o -name "France 2*.ts" -o -name "NRJ*.ts" ) -print | ( while read i; do convert_file "$i" hd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "*.mpeg" -o -name "*.vob" -o -name "*.mpg" -o -name "*.avi" ) | ( while read i; do convert_file "$i" other; done )

rm "$INPUT_DIR/.lock"

Automatisation encodage vidéo pour N900

L’objectif de ce billet et de mettre en œuvre un processus de transfert de vidéo depuis un répertoire d’un PC vers le N900 avec un ré-encodage des fichiers afin d’en assurer une lecture optimale sur le terminal. Ce cas d’utilisation s’avère utile quand on veut visionner des programmes TV enregistrés par exemple.

Nous disposons sur le PC d’un répertoire dans lequel on va mettre les vidéos à encoder pour transfert sur le N900. Disons que ce répertoire est le suivant:

/home/thierry/Vidéos/n900

L’idée est de passer au crible le répertoire ci-dessus à la recherche de fichier à convertir. Comme les fichiers ont une résolution d’image variable, nous allons utiliser un script qui sélectionne le bon encodage en fonction du fichier. Le billet précédent montre une correspondance entre chaine de la TNT par ADSL et la taille du fichier encodé.

Le fichier /home/thierry/Vidéos/n900/convert_all.sh suivant contient le script d’encodage:

#!/bin/bash

echo "***********************************"
echo "*                                 *"
echo "*    video converter for N900     *"
echo "*                                 *"
echo "***********************************"



INPUT_DIR=.
OUTPUT_DIR=$INPUT_DIR/converted

# do not allow concurrency
if [ -e "$INPUT_DIR/.lock" ]; then
 exit 1
else
 touch "$INPUT_DIR/.lock"
fi

function convert_file {
 original_def=$2
 original_file=$1
 converted_file="$OUTPUT_DIR/$original_file.mp4"

 if [ -e "$converted_file" ]; then
 return 1
 fi

 if [ $original_def == "mpeg" ]; then
 SIZE="400x480"
 elif [ $original_def == "dvd" ]; then
 SIZE="592x480"
 elif [ $original_def == "hd" ]; then
 SIZE="640x480"
 else
 echo "format $original_def inconnu"
 return 1
 fi

 (ffmpeg -i "$original_file" -acodec libfaac -vcodec libx264  -vpre hq -vpre baseline -crf 25 -maxrate 1000k -bufsize 2000k -threads 0 -flags2 -fastpskip  -s "$SIZE" "$converted_file" ) &
 wait
 if [ $? -eq 0 ]; then
 rm "$original_file"
 fi

}

find $INPUT_DIR -maxdepth 1 -type f ( -name "France 3*.ts" -o -name "France 4*.ts" -o -name "France 5*.ts" -o -name "La Chaîne*.ts" -o -name "NT1*.ts" -o -name "TMC*.ts" ) -print | ( while read i; do convert_file "$i" mpeg; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "DirectStar*.ts" -o -name "RTL9*.ts" -o -name "AB*.ts" ) -print | ( while read i; do convert_file "$i" dvd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "Arte*.ts" -o -name "Direct 8*.ts" -o -name "France 2*.ts" -o -name "NRJ*.ts" ) -print | ( while read i; do convert_file "$i" hd; done )

rm "$INPUT_DIR/.lock"

Ce script encode les fichiers vers le sous répertoire /home/thierry/Vidéos/n900/converted en supprimant les fichiers originaux après leur conversion.

L’automatisation de la conversion se fait par un script que l’on va mettre dans /etc/crond.hourly/convertReplayN900 dont voici le contenu:

#!/bin/sh
N900_DIR=/home/thierry/Vidéos/n900
cd $N900_DIR
./convert_all.sh
chown -R thierry:thierry  ./converted

Ce script sera lancé toutes les heures. Il exécutera le premier script vu plus haut puis changera le propriétaire des fichiers encodés (ici avec l’utilisateur thierry) car sinon les fichiers encodés appartiendront à root.

Il ne reste plus qu’à plannifier le transfert des fichiers encodés vers le N900. Comme d’habitude, ce sera au terminal d’initier le transfert car il est plus facile d’avoir un deamon rsync sur le PC que sur le N900. On suppose donc que le répertoire contenant les fichiers encodés est accessible par le module videos du serveur rsync.

Voici le script à utiliser depuis le terminal pour y rapatrier les fichiers convertis:

#!/bin/sh
rsync -v --remove-sent-files --ignore-existing 192.168.0.12::"videos/n900/converted/*.mp4" /home/user/MyDocs/.videos/replay

Le lecteur prendra soin de modifier l’adresse 192.168.0.12 par celle du PC où se trouve le serveur rsync.