8722 sujets

Développement web côté serveur, CMS

Bonjour,
je travaille sur une partie d'un projet dans laquelle je dois faire lire des fichiers audio ou video. La lecture se fait simplement à l'aide des balises html5 appropriées.
L'ennui c'est que les fichiers peuvent être stockés en des endroits du serveurs dont l'accès est interdit en http. J'utilise donc un script php (qui a le droit de lire ces fichiers où qu'ils soient) qui devrait se comporter comme le type de fichier qu'il lit. Pourtant, lorsque le navigateur (Safari) lit le résultat du script, je n'ai pas droit au player audio/video habituel, il n'y a pas de progress bar, seulement l'indication "Diffusion en direct":
http://cl.ly/image/0G3Q1e2e3Z3t/ppp.png

C'est assez embêtant ! Voilà mon script (tout ce qu'il y a de plus simple):
<?php
	if(isset($_GET['path'])) $path=$_GET['path'];
	if(isset($path)) {
		if(file_exists($path)) {
			if(is_file($path)) {
				if(is_readable($path)) {
					header('Content-type: '.mime_content_type($path));
					header('Content-Length: '.filesize($path));
					readfile($path);
				}
			}
		}
	}
?>

(La double vérification de variable s'explique par des inclusions éventuelles qui ne font pas intervenir de données GET)

J'imagine qu'il faut ajouter des header particuliers mais j'ai eu beau chercher, rien n'a fonctionné.
Merci de votre aide Smiley smile
Modifié par juliendargelos (11 Jun 2014 - 23:57)
+---------------------------------------------------------------------------------------------+
|  +---------------------------------------------------------------------------------------+  |
|  |                                                                                       |  |
|  |                                                                                       |  |
|  |  %%%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%       |  |
|  |  %%............%%          %%............%%  %%................................%%     |  |
|  |  %%............%%          %%............%%  %%............%%%%%%%%%%%%..........%%   |  |
|  |  %%%%..........%%          %%..........%%%%  %%%%..........%%          %%..........%% |  |
|  |    %%..........%%          %%..........%%        %%........%%          %%..........%% |  |
|  |    %%..........%%          %%..........%%        %%........%%          %%..........%% |  |
|  |    %%..........%%          %%..........%%        %%........%%%%%%%%%%%%..........%%   |  |
|  |    %%..........%%          %%..........%%        %%..........................%%%%     |  |
|  |    %%..........%%          %%..........%%        %%........%%%%%%%%%%%%%%%%%%         |  |
|  |    %%..........%%          %%..........%%        %%........%%                         |  |
|  |    %%..........%%          %%..........%%        %%........%%                         |  |
|  |    %%............%%      %%............%%        %%........%%                         |  |
|  |    %%..............%%%%%%..............%%    %%%%............%%%%                     |  |
|  |      %%%%..........................%%%%      %%................%%                     |  |
|  |          %%%%..................%%%%          %%................%%                     |  |
|  |              %%%%%%%%%%%%%%%%%%              %%%%%%%%%%%%%%%%%%%%                     |  |
|  |                                                                                       |  |
|  |                                                                                       |  |
|  +---------------------------------------------------------------------------------------+  |
+---------------------------------------------------------------------------------------------+

Si avec ça j'ai pas de réponse... Smiley langue (Désolé pour les utilisateurs de mobile)
Modifié par juliendargelos (10 Jun 2014 - 20:13)
Le comportement que tu décris me semble normal pour une lecture de fichier en streaming. Chaque navigateur l'interprète à sa sauce (teste sous Firefox et Chrome, tu verras).

A part ça, dans ton script, file_exists et is_readable font double emploi :

http://www.php.net//manual/fr/function.is-readable.php

a écrit :

is_readable — Indique si un fichier existe et est accessible en lecture


Et avec ton script on peut peut récupérer n'importe fichier de ton serveur (source PHP, /etc/password, contenu de la base de données, etc) vu que tu ne fais aucun filtrage sur ta varaible "$_GET['path']".
FraiseTagada99 a écrit :
Le comportement que tu décris me semble normal pour une lecture de fichier en streaming. Chaque navigateur l'interprète à sa sauce (teste sous Firefox et Chrome, tu verras).

Moi il me semble qu'il te semble mal ! Smiley lol Lorsque l'on lit directement un fichier mp3, on a bien la progress bar que je ne parviens pas à obtenir avec mon script.

FraiseTagada99 a écrit :

A part ça, dans ton script, file_exists et is_readable font double emploi :

http://www.php.net//manual/fr/function.is-readable.php


is_readable — Indique si un fichier existe et est accessible en lecture

Je ne savais pas, je vais pouvoir alléger mes imbrications conditionnelles... Smiley confused

FraiseTagada99 a écrit :
Et avec ton script on peut peut récupérer n'importe fichier de ton serveur (source PHP, /etc/password, contenu de la base de données, etc) vu que tu ne fais aucun filtrage sur ta varaible &quot;$_GET['path']&quot;.

Comme je disais dans mon post initial, il ne s'agit là que d'un tout petit fragment du projet ! Ce script est lui même stocké dans une zone inaccessible en http, il est inclus par un autre script qui vérifie des sessions et toute les données qui passent par lui.

Merci quand même de ton aide Smiley smile
juliendargelos a écrit :

Moi il me semble qu'il te semble mal ! Smiley lol Lorsque l'on lit directement un fichier mp3, on a bien la progress bar que je ne parviens pas à obtenir avec mon script.



C'est le test que j'ai fait, lecture par ton script puis en direct, sous Firefox, Chrome et IE j'ai exactement le même comportement dans les 2 cas. Je n'ai pas testé sous Safari puisque ce navigateur n'est utilisé que par les web designers et pas par les vraies personnes. Smiley smile
FraiseTagada99 a écrit :
Je n'ai pas testé sous Safari puisque ce navigateur n'est utilisé que par les web designers et pas par les vraies personnes.

Je suis une fausse personne donc Smiley decu

Non sans blague, il ne faut pas être si déterministe en ce qui concerne la nature des utilisateurs de Safari ! Pour rappel il est quand même installé par défaut sur tous les terminaux et ordinateurs Apple ! Et ça fait 10,02% d'internautes selon StatCounter.

Bref je vais pas m'emballer c'est pas le problème. D'abord car mon projet est une web app qui sera exclusivement exécutée depuis safari, et ensuite car j'écris depuis un malheureux iPod qui ne me permet pas de réaliser les tests que tu as fait. Je le ferais demain quand j'aurais un laptop sous la main. Je te tiens au courant. (Mais je suis tout de même surpris de ce que tu avance)
Modifié par juliendargelos (11 Jun 2014 - 00:44)
StatCounter c'est bien l'analytics utilisé par à peine 3% des sites qui utilisent un outil d'analyse externe ? Leurs stats doivent bien être bien représentatives de la réalité. Smiley smile
StatCounter base ses statistiques sur plus de 3 millions de sites web avec un total de 15 milliards de pages vues par mois (Ça fait quand même un échantillon correct pour établir des statistiques assez fiables).

Mais ce n'est pas cela qui me pose problème, je suis à la recherche d'une solution pour mon script. Comme je l'ai dis, je t'en dirais plus demain.


Smiley winner 300 Posts Smiley ola (C'est un gâteau d'anniversaire...)
                                        ____     ___     ___
                                       |___ \   / _ \   / _ \
                              OOOOOOO    __) | | | | | | | | |  OOOOOOO
                     OOOOOOOO           |__ <  | | | | | | | |          OOOOOOOO
                OOOOO                   ___) | | |_| | | |_| |                  OOOOO
            OOOO                       |____/   \___/   \___/                       OOOO
        OOOO  O                                                                      O  OOOO
        O       OOOOO                                                          OOOOO       O
        O OOOOO      OOOOOOOOOOOOOOOOOOOOOOOOO        OOOOOOOOOOOOOOOOOOOOOOOOO      OOOOO O
        OOO    OO                            OOOOOOOOOO                            OO    OOO
         OO     OOOOOO       OOOOO         OOO        OOO         OOOOO       OOOOOO     OO
          O OOOO     OOOOOOOOO   OOOOOOOOOO   OOOOOOOO   OOOOOOOOOO   OOOOOOOOO     OOOO O
          OOO  O                                                                    O  OOO
          OO   OO             OOO OO                            OO OOO             OO   OO
         OOOO  OO            O      OO                        OO      O            OO  OOOO
        OOOOO  OO  OOO       O      OO          OOOO          OO      O       OOO  OO  OOOOO
        O O OO O  OO  OOO    O      OO      OOOO    OOOO      OO      O    OOO  OO  O OO O O
       OO OO   OO        O    O    OO    OOO            OOO    OO    O    O        OO   OO OO
       OO  O  O           O    OOOOO   OO                  OO   OOOOO    O           O  O  OO
        O  O O   OO        OO        OOO                    OOO        OO        OO   O O  O
        O   OO    OO        OO      OO          OOOO          OO      OO        OO    OO   O
        OO        OOO        OO   OOO         OO    OO         OOO   OO        OOO        OO
         OO      OO OO        OO OO         OO        OO         OO OO        OO OO      OO
          OOO  OO     OO       OOO         OO          OO         OOO       OO     OO  OOO
             OO        O                 OO              OO                 O        OO
               OOOOOOOOOOOO         OOOO                    OOOO         OOOOOOOOOOOO
                         OOOOOOOOOOOO                          OOOOOOOOOOOO
                                    OOOOOOOOOOOOOOOOOOOOOOOOOOOO

Modifié par juliendargelos (11 Jun 2014 - 19:22)
ET si c'était un problème de buffering ?

Au lieu d'utiliser readfile, essaie, juste pour voir, d'utiliser fopen/fread/fclose + version binaire de echo, et renseigne-toi s'il y a un moyen de flush explicitement la sortie avant la fin du script. Ou alors, voir si on peut régler la taille du buffer de lecture pour readfile.

Étant donné que tu agis comme un intermédiaire de transmission en direct, toute bufférisation non terminale est suceptible d'introduire des délais / saccades / coupures / etc. si elle est mal gérée / pas configurée avec la taille optimum.

<troll> D'ici je vois le drame, tu vas voir la France perdre avec 30 secondes de retard </troll>
Modifié par QuentinC (11 Jun 2014 - 07:12)
Le truc c'est que readfile() est spécialement conçu pour ce genre de cas. Avec fopen/fread/flcose tu as de gros risques de saturer la mémoire de ton serveur.
a écrit :
Avec fopen/fread/flcose tu as de gros risques de saturer la mémoire de ton serveur.


Je ne vois pas comment si tu balances des paquets de 4 Ko.
J'ai remplacé la ligne avec readfile par ceci, mais le résultat est le même (le chargement de la page est juste plus long):
$stream_file=fopen($path,'r');
while(!feof($stream_file)) echo fread($stream_file,4000);
fclose($stream_file);

J'ai également tenté plusieurs combinaison avec les fonctions flush() et ob_clean(), mais rien n'a fonctionné.

Pour répondre à FraiseTagada99, le comportement normal de la balise audio est bien celui que je décris; avec une progress bar. Voilà un récapitulatif des tests que j'ai fait:

upload/43453-player.png


Bon, il semblerait que Safari fasse des siennes. C'est le seul qui ne se comporte pas comme les autres navigateurs avec mon script.
Je ne sais toujours pas comment faire...

! RECTIFICATION !
En fait, lors de la lecture de fichier mp3 depuis mon script, tous les navigateurs se comportent de la même manière: la progress bar n'indique que l'avancement de la musique, on ne peut avancer ou reculer la musique (ce que l'on peut faire lorsque l'on lit directement le fichier). Le problème n'est donc pas particulier à Safari !
Modifié par juliendargelos (11 Jun 2014 - 18:03)
OK, tu peux comparer les entêtes HTTP quand tu lis ton fichier via ton script puis en direct.

Ton script (straming qui foire) :


Status Code:200 OK

Connection:Keep-Alive
Content-Length:5816291
Content-Type:audio/mpeg
Date:Wed, 11 Jun 2014 18:55:27 GMT
Keep-Alive:timeout=5, max=99
Server:Apache/2.4.9 (Win64) PHP/5.5.12
X-Powered-By [langue]HP/5.5.12


En direct (streaming ok) :


Status Code:206 Partial Content

Accept-Ranges:bytes
Connection:Keep-Alive
Content-Length:1
Content-Range:bytes 521034-521034/5816291
Content-Type:audio/mpeg
Date:Wed, 11 Jun 2014 18:56:17 GMT
ETag:"58bfe3-3c6ebd7e73500"
Keep-Alive:timeout=5, max=100
Last-Modified:Tue, 09 Sep 2003 20:59:32 GMT
Server:Apache/2.4.9 (Win64) PHP/5.5.12


Et je pense que la réponse à ta question va se trouver ici :

http://stackoverflow.com/questions/17896634/streaming-a-video-with-php
http://stackoverflow.com/questions/157318/resumable-downloads-when-using-php-to-send-the-file
Modifié par FraiseTagada99 (11 Jun 2014 - 21:05)
C'est vrai que j'aurais du penser à comparer les headers ! (En même temps je ne sais pas comment on fait... Comment on fait ?)
Enfin, même avec ça, j'aurais pas réussi à pondre un script similaire à ceux des topics vers lesquels tu m'a dirigés. Mais l'essentiel c'est que ça fonctionne !
Merci mille fois, je ne pensais réussir à résoudre ce problème Smiley jap

Voilà mon code final (juste le fragment qui nous importe):
if(isset($_GET['path'])) $path=$_GET['path'];
	if(isset($path)) {
		if(is_readable($path)) {
			if(is_file($path)) {
				header_remove();
				header('Content-Type: '.mime_content_type($path));
				header('Accept-Ranges: 0-'.$length);
				if(isset($_SERVER['HTTP_RANGE'])) $range=$_SERVER['HTTP_RANGE'];
				elseif($apache=apache_request_headers()) {
					$headers=array();
					foreach($apache as $header=>$val) $headers[strtolower($header)]=$val;
					if(isset($headers['range'])) $range=$headers['range'];
					else $range=false;
				}
				else $range=false;
				if(isset($range)) {
					$fp=@fopen($path,'rb');
					$size=filesize($path);
					$length=$size;
					$start=0;
					$end=$size-1;
					$c_start=$start;
					$c_end=$end;
					list(,$range)=explode('=',$range,2);
					if(strpos($range,',')!==false) {
						header('HTTP/1.1 416 Requested Range Not Satisfiable');
						header('Content-Range: bytes '.$start.'-'.$end.'/'.$size);
						exit;
					}
					if($range0=='-') $c_start=$size-substr($range,1);
					else {
						$range=explode('-',$range);
						$c_start=$range[0];
						$c_end=(isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
					}
					$c_end=($c_end>$end) ? $end : $c_end;
					if($c_start>$c_end || $c_start>$size-1 || $c_end>=$size) {
						header('HTTP/1.1 416 Requested Range Not Satisfiable');
						header('Content-Range: bytes '.$start.'-'.$end.'/'.$size);
						exit;
					}
					$start=$c_start;
					$end=$c_end;
					$length=$end-$start+1;
					fseek($fp,$start);
					header('HTTP/1.1 206 Partial Content');
					header('Content-Range: bytes '.$start.'-'.$end.'/'.$size);
					header('Content-Length: '.$length);
					fseek($fp,$start);
					$buffer=1024*8;
					while(!feof($fp) && ($p=ftell($fp))<=$end) {
						if($p+$buffer>$end) $buffer=$end-$p+1;
						set_time_limit(0);
						echo fread($fp,$buffer);
						flush();
					}
					fclose($fp);
				}
				else {
					header('HTTP/1.1 206 Partial Content');
					header('Content-Length:1');
					readfile($path);
				}
			}
		}
	}
}

Modifié par juliendargelos (11 Jun 2014 - 23:56)
juliendargelos a écrit :
C'est vrai que j'aurais du penser à comparer les headers ! (En même temps je ne sais pas comment on fait... Comment on fait ?)


J'ai juste regardé avec les outils de dev Web de Firefox/Chrome (onglet réseau).
Ok, je n'avais pas remarqué cette fonctionnalité.


[EDIT]

En fait les problème n'est pas tout-à-fait résolu; c'est-à-dire que sa résolution a engendré un autre problème puisque lorsque le script de lecture en streaming est en exécution, on ne peut pas exécuter d'autres scripts en même temps Smiley ohwell
J'ai ouvert un autre topic ici
Modifié par juliendargelos (13 Jun 2014 - 17:12)