Serveur(s) d'encodage

SAdmins ›› Installation ››
Parent Previous Next

Monter un serveur d'encodage externe à Médiacad


Médiacad dispose de 4 webServices permettant d'obtenir le nombre de tâches d'encodage en cours ou une nouvelle tâche, et d'indiquer un succès ou une erreur d'encodage.


C'est via ces webServices que le serveur d'encodage va interagir avec Médiacad.


La répartition des rôles est très claire et doit être respectée à la lettre :



Un script python tournant sur le serveur d'encodage est disponible. Il gère toutes les opérations d'encodage externes.


Changer la configuration


Dans /local/config/application.cfg.array.php, modifier les entrées suivantes :

       ['medias']['isAllowedEncoding'] = false => Médiacad ne réalisera plus d'encodage

       ['medias']['maxParallelEncoding'] = XX => nombre maxi d'encodages en parallèle sur le serveur d'encodage

       tous les paths de la partie ['medias']['encode'] concernent le serveur web de Médiacad, pas le serveur d'encodage

       dans ['auth']['adapters']['salt']['config']['passwords'], ajouter une entrée pour le serveur d'encodage différente de Médiacad (ce sera le login), par exemple "encodeServer", et indiquer son mot de passe.


Dans /local/config/wsAcl.cfg.array.php, modifier les entrées suivantes :

       dans ['ws']['encode']['getinprogresstaskcount'], ['getnexttask'], ['setencodesuccess'] et ['setencodeerror'], ajouter le login du serveur d'encodage ("encodeServer" dans notre exemple).


La documentation ci-dessous n'est utile que si vous ne souhaitez pas utiliser le script python fourni.

Requêter les webServices de Médiacad


Les webServices utilisent par défaut l'adaptateur d'authentification salt (= "grain de sel").


Le principe :


  1. le serveur d'encodage essaie de se connecter au ws ; l'accès est refusé et un salt (chaine aléatoire de 30 caractères) est délivré.
  2. le serveur d'encodage concatène son password au salt et hash le tout en sha256.
  3. le serveur d'encodage envoie à l'url demandée le login, le hash obtenu et les données éventuelles (en POST).


La connexion n'est valable que pour une seule action (la session est réinitialisée ensuite), tout nouvel appel au ws doit recommencer la procédure.


Pour information, voici les deux méthodes de l'adaptateur salt :


   public function isAuth()

   {

       if (!isset($_POST['login']) or !isset($_POST['password'])

                or !isset($this->_adapterOptions['passwords'][(string) $_POST['login']])

                or is_null(App_Lib_Session::get('serviceAuth', 'salt'))) {

           return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, array());

       }

       

       $saltedPassword = hash('sha256', $this->_adapterOptions['passwords'][(string) $_POST['login']] . App_Lib_Session::get('serviceAuth', 'salt'));

       App_Lib_Session::namespaceUnset('serviceAuth');

       

       return (string) $_POST['password'] == $saltedPassword ? new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, array('uid' => (string) $_POST['login']))

                                                             : new Zend_Auth_Result(Zend_Auth_Result::FAILURE, array());

   }


   public function authenticate()

   {

       $_SESSION = array();

       // renvoi du grain de sel en Json

       $appLibControllerActionHelperRandomString = new App_Lib_Controller_Action_Helper_RandomString();

       $salt = $appLibControllerActionHelperRandomString->direct(30);

       App_Lib_Session::set('serviceAuth', 'salt', $salt);

       echo Zend_Json::encode(array('isSuccess' => true, 'data' => array('salt' => $salt)));

       exit;

   }


Médiacad utilise une classe (trunk/application/library/WebService.php) qui permet de requêter ses webServices.


Le code principal est le suivant :


   public function requestWS($uri, $params = array())

   {

       // initialisation

       $ch = curl_init($uri);

       curl_setopt($ch, CURLOPT_POST, true);

       curl_setopt($ch, CURLOPT_POSTFIELDS, array('data' => 'data'));

       curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

       curl_setopt($ch, CURLOPT_COOKIESESSION, true);

       curl_setopt($ch, CURLOPT_COOKIEJAR, 'mediacadWSSession');

       

       // récupération du salt

       $result = curl_exec($ch);

       if ($result === false) {

           return array('isSuccess' => false, 'errorCode' => curl_error($ch), 'errorMessage' => curl_error($ch), 'data' => array());

       }

       

       $result = Zend_Json::decode($result, Zend_Json::TYPE_ARRAY);

       if (!isset($result['data']['salt'])) {

           $translate = Zend_Registry::get('Zend_Translate');

           return array('isSuccess' => false, 'errorCode' => 'appLibWebServiceMissingSaltError', 'errorMessage' => $translate->_('appLibWebServiceMissingSaltError'), 'data' => array());

       }

       

       // login et requête

       $params['login'] = $this->_login;

       $params['password'] = hash('sha256', $this->_password . $result['data']['salt']);

       

       curl_setopt($ch, CURLOPT_COOKIEFILE, 'mediacadWSSession');

       curl_setopt($ch, CURLOPT_POSTFIELDS, $params);

       curl_setopt($ch, CURLOPT_URL, $uri);

       

       $result = curl_exec($ch);

       

       if ($result === false) {

           $message = array('isSuccess' => false, 'errorCode' => curl_error($ch), 'errorMessage' => curl_error($ch), 'data' => array());

       } else {

           $message = Zend_Json::decode($result, Zend_Json::TYPE_ARRAY);

       }

       curl_close($ch);

       

       return $message;

   }



Chaque webService renvoie un json de la forme suivante :


   message de succès :


   {"isSuccess":true,"data":{"key":"value"}}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array('key' => 'value'));


   message d'erreur :


   {"isSuccess":false,"errorCode":"wsEncodeError404","errorMessage":"Action introuvable.","data":[]}


ce qui, une fois décodé, donne en php :


array('isSuccess' => false,

     'errorCode' => 'wsEncodeError404',

     'errorMessage' => 'Action introuvable.',

     'data' => array());


Messages d'erreurs possibles


Les messages d'erreurs possibles à la connexion sont :


errorCode

errorMessage

solution

wsEncodeError401

Accès à cette action interdit.

problème d'ACL => revoir les configs

wsEncodeError404

Action introuvable

revoir l'uri demandée

               

       

       .        

Demander une nouvelle tâche

webService


   webService : getnexttask (path_de_mediacad/ws/encode/gestnexttask)

   données à transmettre : aucune.


pas de tâche disponible


Médiacad renvoie un message de succès mais avec 'hasNewEncodingTask' == false.


Message de succès :


{"isSuccess":true,"data":{"hasNewEncodingTask":false}}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array(

         'hasNewEncodingTask' => false));


une tâche est disponible


Médiacad renvoie les données nécessaires à l'accomplissement de la tâche.


Il y a 3 types de tâches possibles : copy, delete et encode.

tâche copy


Message de succès type :


{"isSuccess":true,"data":{"hasNewEncodingTask":true,"encodingTaskId":"68","task":"copy","sourceLocation":"##sourcePath##\/video\/17.mp4","destLocation":"##encodedPath##\/video\/17.mp4"}}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array(

         'hasNewEncodingTask' => true,

         'encodingTaskId' => '68',

         'task' => 'copy',

         'sourceLocation' => '##sourcePath##/video/17.mp4',

         'destLocation' => '##encodedPath##/video/17.mp4'));


tâche delete


Message de succès type :


{"isSuccess":true,"data":{"hasNewEncodingTask":true,"encodingTaskId":"69","task":"delete","location":"##sourcePath##\/book\/18.odt"}}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array(

         'hasNewEncodingTask' => true,

         'encodingTaskId' => '69',

         'task' => 'delete',

         'location' => '##sourcePath##/book/18.odt'));


tâche encode


Message de succès type :


{"isSuccess":true,"data":{"hasNewEncodingTask":true,"encodingTaskId":"67","task":"encode","application":"ffmpeg","exe":"ffmpeg","command":"##applicationPath## -i \"##sourcePath##\/video\/17.mp4\" -vcodec libvpx -vb 700k -maxrate 8000k -bufsize 1000K -minrate 10k -qmin 3 -qmax 30 -rc_lookahead 16 -keyint_min 0 -g 360 -skip_threshold 0 -level 116 -preset slow -ab 192k \"##encodedPath##\/video\/17.webm\""}}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array(

         'hasNewEncodingTask' => true,

         'encodingTaskId' => '67',

         'task' => 'encode',

         'application' => 'ffmpeg',

         'exe' => 'ffmpeg',

         'command' => '##applicationPath## -i "##sourcePath##/video/17.mp4" -vcodec libvpx -vb 700k -maxrate 8000k -bufsize 1000K -minrate 10k -qmin 3 -qmax 30 -rc_lookahead 16 -keyint_min 0 -g 360 -skip_threshold 0 -level 116 -preset slow -ab 192k "##encodedPath##/video/17.webm"'));


réaliser la tâche


Avant de réaliser la tâche, il faut remplacer les ##xxxPath## des données renvoyées.


   ##applicationPath## : sera remplacé par le path de l'application sur le serveur d'encodage, en fonction des clefs 'application' et 'exe' (voir plus haut l'équivalent dans Médiacad (trunk/local/config/application.cfg.array.php), partie ['medias']['encode']).

   ##tmpPath##, ##sourcePath## et ##encodedPath## : sera remplacé par les paths correspondants (voir plus haut l'équivalent dans Médiacad (trunk/local/config/application.cfg.array.php), partie ['medias']).


Une fois la tâche réalisée, le serveur d'encodage doit requêter selon le cas "setencodesuccess" ou "setencodeerror".


Pour information, la méthode utilisée par Médiacad pour l'encodage (trunk/application/library/Media.php) est la suivante :


   public function runNextEncodingTask()

   {

       $appModelEncodingTask = $this->_getAppModelEncodingTask();

       

       // récupération des tâches inProgress avec timeout dépassé depuis plus de 5 min

       $exceededTimeTaskRowset = $appModelEncodingTask->getExceededTimeTasks(5);

       if (!is_null($exceededTimeTaskRowset)) {

           // met les tâches en erreur

           foreach ($exceededTimeTaskRowset as $exceededTimeTaskRow) {

               $this->setEncodeError($exceededTimeTaskRow['encodingTaskId'], 'appLibMediaEncodingTimeout');

           }

       }

       

       // récupération du nombre de tâches inProgress

       $inProgressTaskCount = $appModelEncodingTask->getInProgressTasksCount();


       // si ce nombre est < au nombre maxi => récupération de la tâche suivante en wait + passage à inProgress

       $encodingTaskRow = null;

       if ($inProgressTaskCount < $this->_localConfig['maxParallelEncoding']) {

           $encodingTaskRow = $appModelEncodingTask->setInProgressForNextTask();

       }

       // pas de nouvelle tâche disponible => fin de la méthode

       if (is_null($encodingTaskRow)) {

           return null;

       }

       

       // envoi d un nouveau ping s il reste des tâches et que le max n est pas atteint

       if (($inProgressTaskCount + 1) < $this->_localConfig['maxParallelEncoding']) {

           $this->pingEncode();

       }

       

       

       // traitement de la tâche

       // config php

       ini_set('memory_limit', $this->_localConfig['encodingTaskMemoryLimit']);

       set_time_limit($encodingTaskRow['maxInProgressDuration'] * 60);

       

       // lancement de la tâche

       $encodeCommand = Zend_Json::decode($encodingTaskRow['params']);

       

       $beginTime = microtime(true);

       

       

       // copy

       if ($encodeCommand['task'] == 'copy') {

           $errorMessage = 'appLibMediaFileCopyError';


           $sourceLocation = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand['sourceLocation']);

           $sourceLocation = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $sourceLocation);

           $sourceLocation = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $sourceLocation);

           

           $destLocation = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand['destLocation']);

           $destLocation = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $destLocation);

           $destLocation = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $destLocation);

           

           $result = copy($sourceLocation, $destLocation);

           

       // delete

       } else if ($encodeCommand['task'] == 'delete') {

           $errorMessage = 'appLibMediaFileDeleteError';


           $location = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand['location']);

           $location = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $location);

           $location = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $location);

           

           $result = unlink($location);

           

       // encode

       } else {

           $errorMessage = 'appLibMediaEncodingTaskError';

           

               $encodeCommand = str_replace('##applicationPath##', $this->_localConfig['encode']['applications'][$encodeCommand['application']][$encodeCommand['exe']]['applicationPath'], $encodeCommand['command']);

               $encodeCommand = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand);

               $encodeCommand = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $encodeCommand);

               $encodeCommand = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $encodeCommand) . ' 2>&1';

               

               $output = array();

               $result = 0;

               exec($encodeCommand, $output, $result);

               

               $result > 0 ? $result = false : $result = true;

               

               if (!$result) {

                   if (count($output) > 0) {

                       isset($encodingTaskRow['fileId']) ? $fileId = $encodingTaskRow['fileId'] : $fileId = 'xx';

                       $errorLog = 'appLibMediaEncodingTaskError - fileId=' . $fileId . ' - errorMessage=';

                       foreach ($output as $line) {

                               $errorLog .= $line;

                       }

                       App_Lib_Log::log($errorLog, Zend_Log::ERR);

                   }

               }

       }

       $endTime = microtime(true);    

       

       // fin de l encodage

       $result ? $this->setEncodeSuccess($encodingTaskRow['encodingTaskId'], $endTime - $beginTime) : $this->setEncodeError($encodingTaskRow['encodingTaskId'], $errorMessage);

       

       // envoi d un nouveau ping

       $this->pingEncode();

   }


Messages d'erreurs possibles


Pas d'erreurs spécifiques au webService.


Le webService peut renvoyer les exceptions de l'application.

Signaler un encodage réussi

webService


   webService : setencodesuccess(path_de_mediacad/ws/encode/setencodesuccess)

   données à transmettre : (string) ['encodingTaskId'] et (float) ['encodeDuration'] (en secondes).


Message de succès


{"isSuccess":true,"data":[]}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array());


Messages d'erreurs possibles

errorCode

errorMessage

solution

wsEncodeSetEncodeSuccessRequestMustBePost

Les données doivent être envoyées en POST


wsEncodeSetEncodeSuccessMissingPostFieldEncodingTaskId

Champs POST 'encodingTaskId' manquant.

renvoyer l'encodingTaskId reçu par gestnexttask

wsEncodeSetEncodeSuccessUnknownEncodingTaskId

EncodingTaskId inconnu.

soit l'encodingTaskId a été modifié par le serveur d'encodage, soit le webService a déjà été appelé pour ce même encodingTaskId

wsEncodeSetEncodeSuccessMissingPostFieldEncodeDuration

Champs POST 'encodeDuration' manquant.

renvoyer la durée d'encodage (float en secondes)

               

Le webService peut aussi renvoyer les exceptions de l'application.

Signaler une erreur d'encodage

webService


   webService : setencodeerror(path_de_mediacad/ws/encode/setencodeerror)

   données à transmettre : (string) ['encodingTaskId'] et (string) ['errorMessage'].


Message de succès


{"isSuccess":true,"data":[]}


ce qui, une fois décodé, donne en php :


array('isSuccess' => true,

     'data' => array());


Messages d'erreurs possibles

errorCode

errorMessage

solution

wsEncodeSetEncodeErrorRequestMustBePost

Les données doivent être envoyées en POST.        


wsEncodeSetEncodeErrorMissingPostFieldEncodingTaskId

Champs POST 'encodingTaskId' manquant.

renvoyer l'encodingTaskId reçu par gestnexttask

wsEncodeSetEncodeErrorUnknownEncodingTaskId

EncodingTaskId inconnu.

soit l'encodingTaskId a été modifié par le serveur d'encodage, soit le webService a déjà été appelé pour ce même encodingTaskId

wsEncodeSetEncodeErrorMissingPostFieldErrorMessage

Champs POST 'errorMessage' manquant.        

renvoyer le message d'erreur

               

Le webService peut aussi renvoyer les exceptions de l'application.



Créé avec HelpNDoc Personal Edition: Créer des documents d'aide PDF facilement