Streaming PHP pour video ogv

Tags:: ogg :: ogv :: php :: streaming :: video

Il est possible de streamer un flux avec PHP. En général on s'en sert pour la vidéo ou l'audio, mais c'est aussi très utile pour les clients qui savent gérer ce que l'on appelle un "resume download" (récupération d'un téléchargement coupé à certain endroit). Cela est aussi presque une obligation lorsque l'on veut utiliser la balise "video" en HTML5 et que le format n'est pas auto-streamé (les H264 par exemple n'ont pas ce souci) tel que le format libre Theora Ogg Vorbis.

Vous l'avez remarqué mais Youtube envoit les données vidéo au fur et à mesure que la lecture avance... et vous pouvez lire un film dés le milieur sans avoir à télécharger le début de la vidéo. C'est franchement intéressant mais pour cela il vous fallait un lecteur flash et soit un serveur de streaming, soit utiliser du H264 dont les entêtes du conteneur ont tout ce qu'il faut pour atteindre un point donné dans le fichier. Or j'aime utiliser le format Theora Ogg et il ne contient pas ces données. De plus, je n'aime pas flash et je voulais utiliser la balise video.

Si j'insert une balise vidéo directement, j'ai deux solutions:

  • laisser la balise de base, mais l'utilisateur ne pourra pas voir le film à partir d'un point donné sans passer par le chargement au moment du clique sur le bouton de lecture
  • ajouter le mot clef "preload" ou "autobuffer" pour charger le film à l'affichage de ma page, mais cela n'enlève pas qu'il faut charger toute la vidéo... donc même si l'utilisateur ne veut pas lire la vidéo, il aura le chargement depuis mon serveur...

Dans les deux cas, je ne stream pas, je charge le fichier sur le poste client. Mon serveur est donc forcé d'envoyer beaucoup de données, et le client voit sa bande passante diminuer le temps que tout se charge. En plus de cela, c'est peu pratique pour l'utilisateur...

Heureusement, le protocole HTTP est très souple et permet l'ajout d'entête. Les clients (je parle cette fois ci des logiciels, comme Firefox ou Chrome) peuvent alors envoyer des information pour dire "je veux commencer à tel endroit du fichier". Et c'est avec ce genre d'entête (ajouté d'après la norme HTTP 1.1) que nous allons travailler.

Avant d'attaquer la pratique, nous allons devoir nous pencher sur 2 points:

  • ce qu'est un streaming (dans le fonctionnement)
  • la lecture des entêtes HTTP et les réponses à apporter

Un streaming est une méthode d'envoit de fichier par blocs. C'est un résumé simple mais pourtant vrai. L'idée est de répondre à une demande selon plusieurs points:

  • bande passante
  • taille de données à envoyer
  • à partir de quel position du fichier
  • etc...

Faire un stream résulte donc simplement à répondre par un lot de données à une demande spécifique plutôt que d'envoyer un lots complet.

HTTP a une valeur d'entête utilisable par le client pour définir quel lot est demandé. La variable s'appelle "RANGE". Avec PHP, il est aisé de la lire:


$range = $_SERVER['HTTP_RANGE'];
 

Si cette valeur existe alors le client demande un rang de données et non pas le lot complet. Le serveur devra alors répondre quel lot il peut envoyer (multi byte ou non, taille du lot, positions possibles...)

Voici les 3 entêtes qui sont les plus significatives:

  • Accept-Ranges qui prendra une valeur "X-Y" ou X est le début du rang possible (en général 0) et Y la taille du lot.
  • Content-Range qui défini le rang envoyé ou, en cas de demande malformé, le rang que le client doit demander. La forme est "bytes octet_de_début-octet_de_fin/taille_possible". Attention, ce n'est pas une division mais bel et bien un format de réponse !
  • Content-Length déjà bien connu, c'est la taille du lots d'octets envoyé

Très bien. Maintenant il faut savoir lire le rang demandé.

  • si le rang est de la forme X-Y alors on nous demande d'envoyer les données depuis l'octet X à l'octet Y
  • si le rang est de la forme X- on nous demande d'envoyer des données depuis X jusqu'au dernier octet possible
  • si le rand est de la forme -Y alors on nous demande de partir de Y octets depuis la fin du fichier

Il reste donc à traiter cela. Voici une petite fonction que j'ai écrit pour gérer les demandes de rang:


sendRange($file) {
    $size = filesize($file);

    //le type du fichier
    header('Content-Type: '.mime_content_type($file));

    //on ne nous demande pas de rang, on envoit le fichier
    if(!isset($_SERVER['HTTP_RANGE'])){
        header('Content-Length: '.filesize($file));
        readfile($file);
        exit;
    }

    //on répond avec le rang possible
    header("Accept-Ranges: 0-$size");
   
    $range = $_SERVER['HTTP_RANGE'];
   
    //on a un format "bytes=X-Y", je pars donc du caractère 6 et je coupe au "-"
    $parts = explode('-', substr($range,6));
    $start = $parts[0];
   
    //le dernier octet se trouve à la taille du fichier - 1 octet (car on part de l'octet 0)
    $end = (isset($parts[1]) && is_numeric($parts[1])) ? $parts[1] : $size-1;

    //on ne m'a pas donné d'octet de début
    //on me demande de partir d'un octet particulier à partir de la fin du fichier
    if(strlen($start)<1){
        $start = $size-$end;
    }


    //tien le client s'est planté... je lui explique son cas
    if ($end < $start) {
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header('Content-Range: bytes '."$start-$end/$size");
        exit;
    }
   
    //maintenant, on peut envoyer les données
    //il faut prévenir le client qu'on sait gérer des données partielles
        header('HTTP/1.1 206 Partial Content');
       
    //et on lit le fichier (en mode byte)
    $fp = fopen($file,'rb');
   
    //je me déplace à l'octet demandé
    fseek($fp,$start);
   
    //je calcul la longueur des données que je vais envoyer
    $length = $end - $start + 1;
   
    //je fais des lots de 8ko
    $buffer = 1024*8;
   
        header("Content-Type: ".mime_content_type($file));
    header("Content-Range: bytes $start-$end/$size");
    header("Content-Length: ".$length);
   
    //si le fichier est gros, faut pas qu'on arrête le scipt !
    set_time_limit(0);       
   
    //et je lit le fichier
    //ftell me donne la position de mon pointeur de fichier
    while(!feof($fp) && ($pos = ftell($fp)) <= $end) {
        if ($pos + $buffer > $end) {
            //et oui, le prochain coup je vais déborder, je réduit mon buffer
                    $buffer = $end - $pos + 1;
            }
            //on envoit enfin les données
        echo fread($fp, $buffer);
        flush(); ob_flush();
    }             
    fclose($fp);
}
 

Résultat des courses, en utilisant cette fonction, je peux faire démarrer une vidéo à partir de n'importe quel endroit du fichier si le client le demande. Gain de bande passante, gain de convivialité... merci PHP :)

Utilisez le code barre pour ouvrir le ticket dans votre mobile:
This ticket on you mobile

Pas de commentaire pour le moment

Ajouter un commentaire
Veuillez répondre à la question suivante : Pim pam et tapez "poom" sans les guillemets

Trackbacks

Pour ajouter un trackback (retrolien) sur ce ticket, utilisez cette adresse: http://www.metal3d.org/index.php/trackback/default/tb?id=blog%2F263

Tags

Blog Copix Communication PC Config Fedora Vidéo Humour Autre Web Materiel Informatique PHP Développement Mootools HTML Linux WEB IE CSS Vista Microsoft Sympa XHTML Quizz Widget Klik CWE Voeux Bonne année Musique CCRMA Son Audio MAO Reconnaissance vocale Windows Mac Compiz MacOS Mysql Sun Base de données BD Screenlets Screenlet RPM compiz Ubuntu Live FireFox Derambarsh Facebook media informations Sécurité javascript SSH Commande Marketing concours ipod wii QT Trooltech Nokia Libre Logiciels GPL Attali Gnome Dock Compiler Bench undelete reiserfs recovery recover Thread Serveur Optimisation Server Apache Album RSS Flash Air XML Firefox Blender Opensource Orange Internet LiveBox Animaux Droits Logiciels Libres Google Loi RBS netcat Bash Téléphone Spam 3D NIDIA Standards Widgets Merise UML Streaming Developpement Perl MySQL Firewall Parfeu Bayes Novell Réseau Python LVS Salon FTP Dedibox Chrome Administration IRC Gentoo troll video Nouveauté Finance Etudiant spéctacle OpenOffice Javascript Java Info Eclpise ATI design mootools KDE Bureau Design GNU C Pages Jaunes Fun Bijoux Art mail Admin vmware fedora linux VirtualBox VMWare Qemu KVM vim Grep Fglrx Webkit GTK google web JQuery Coup de gueule coup de gueule Lois SVG Raycaster Canvas Hadopi Desktop Jeux Les Sims développement astuces Screen firefox Mobile Safaru Wine bash commande astuce Wave ooc C++ C# Netbook Hercules Bureautique Twitter Safari W3C chrome kazehakaze midori navigateur gecko webkit Links Liens Pyhon Nouvelles Metal Perso Conférence JDLL PCC Roadsend OOC News Acteur Cinéma Chanteur Seven windows orthographe pcc php jdll Slide Application mysql Html twitter python screen curses SVN Android Gphone iPhone OpenGL MindMap Projet Javascrtip Ajax Guitare Tab Tablature JSON firebug Chromium Iphone Titanium music sound android ogg ogv streaming

  • twitter entries...
follow me on Twitter

Valid XHTML 1.0 Strict