2010-06-27 11 views
16

J'utilise un script PHP pour valider les requêtes vidéo avant de les diffuser. Ce script fonctionne comme prévu sur le bureau, avec Safari et Chrome. Mais sur iOS, j'ai un bouton de lecture cassé.Lecture MP4 en cas d'accès direct, mais pas en lecture PHP, sur iOS

Je suis sûr que la vidéo est correctement encodée pour l'iPhone/iPad, parce que lorsque j'y accède directement, cela fonctionne comme prévu.

Le code PHP pertinent:

$file_name = 'test-video.mp4'; 
$file_size = (string)(filesize($file_name)); 
header('Content-Type: video/mp4'); 
header('Content-Length: '.$file_size); 
readfile_chunked($file_name); 
exit; 

(readfile_chunked() est similaire à readfile() mais pour des fichiers très volumineux, trouvés dans les commentaires sur la page du manuel PHP:. http://php.net/manual/en/function.readfile.php En tout état de cause, test-video.mp4 est seulement environ 5 Mo ., ce qui est inférieur à la limite de mémoire - et dans ce cas je peux effectivement remplacer dans le readfile() normal et produire exactement le même comportement)

les en-têtes que je reçois lorsque j'accède directement test-video.mp4 sont:

Accept-Ranges:bytes 
Connection:Keep-Alive 
Content-Length:5558749 
Content-Type:video/mp4 
Date:Sun, 27 Jun 2010 21:02:09 GMT 
Etag:"1c04757-54d1dd-489944c5a6400" 
Keep-Alive:timeout=10, max=30 
Last-Modified:Tue, 22 Jun 2010 01:25:36 GMT 
Server:Apache/2.2.15 (CentOS) mod_ssl/2.2.15 0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635 

Les en-têtes du script PHP sont:

Connection:Keep-Alive 
Content-Disposition:inline; filename="test-video.mp4" 
Content-Length:5558749 
Content-Type:video/mp4 
Date:Sun, 27 Jun 2010 21:03:32 GMT 
Keep-Alive:timeout=10, max=15 
Server:Apache/2.2.15 (CentOS) mod_ssl/2.2.15 0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635 
X-Powered-By:PHP/5.2.13 

J'ai essayé beaucoup de permutations différentes d'en-têtes, les jumelant même exactement à ceux d'une demande directe, en vain.

Quelqu'un a-t-il réussi à diffuser des vidéos HTML5 via PHP, sur iOS?

[Note:. Je voudrais essayer d'utiliser X-Sendfile, mais le site est sur un hôte partagé avec un accès très limité]

EDIT: Je lisais que iOS peut être sensible sur les extensions de fichiers, de sorte J'ai essayé de configurer une RewriteRule qui réécrit les requêtes MP4 à mon script PHP original, mais cela n'a pas aidé non plus.

Répondre

8

Si vous le manipulez vous-même, vous devrez également gérer vous-même les demandes d'octets.

+4

Exactement - J'ai fini par copier simplement la fonction 'rangeDownload()' de l'Annexe A de l'article suivant: http://mobiforge.com/developing/story/content-delivery-mobile-devices (ce qui est cool à propos de cette fonction, elle permet également à l'utilisateur de démarrer la lecture à partir de n'importe quel point de la vidéo, simplement en cliquant sur la timeline.) Je pense que j'ai été particulièrement dérouté par le problème car il fonctionne parfaitement sur le bureau, alors que iOS a cet octet exigence de gamme. – JKS

15

Essayez:

$arquivo_caminho = 'path\file' 

    if (is_file($arquivo_caminho)){ 
     header("Content-type: video/mp4"); // change mimetype 

     if (isset($_SERVER['HTTP_RANGE'])){ // do it for any device that supports byte-ranges not only iPhone 
      rangeDownload($arquivo_caminho); 
     } else { 
      header("Content-length: " . filesize($arquivo_caminho)); 
      readfile($arquivo_caminho); 
     } // fim do if 
    } // fim do if 

    function rangeDownload($file){ 
     $fp = @fopen($file, 'rb'); 

     $size = filesize($file); // File size 
     $length = $size;   // Content length 
     $start = 0;    // Start byte 
     $end = $size - 1;  // End byte 
     // Now that we've gotten so far without errors we send the accept range header 
     /* At the moment we only support single ranges. 
     * Multiple ranges requires some more work to ensure it works correctly 
     * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 
     * 
     * Multirange support annouces itself with: 
     * header('Accept-Ranges: bytes'); 
     * 
     * Multirange content must be sent with multipart/byteranges mediatype, 
     * (mediatype = mimetype) 
     * as well as a boundry header to indicate the various chunks of data. 
     */ 
     header("Accept-Ranges: 0-$length"); 
     // header('Accept-Ranges: bytes'); 
     // multipart/byteranges 
     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 
     if (isset($_SERVER['HTTP_RANGE'])){ 
      $c_start = $start; 
      $c_end = $end; 

      // Extract the range string 
      list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); 
      // Make sure the client hasn't sent us a multibyte range 
      if (strpos($range, ',') !== false){ 
       // (?) Shoud this be issued here, or should the first 
       // range be used? Or should the header be ignored and 
       // we output the whole content? 
       header('HTTP/1.1 416 Requested Range Not Satisfiable'); 
       header("Content-Range: bytes $start-$end/$size"); 
       // (?) Echo some info to the client? 
       exit; 
      } // fim do if 
      // If the range starts with an '-' we start from the beginning 
      // If not, we forward the file pointer 
      // And make sure to get the end byte if spesified 
      if ($range{0} == '-'){ 
       // The n-number of the last bytes is requested 
       $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; 
      } // fim do if 
      /* Check the range and make sure it's treated according to the specs. 
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html 
      */ 
      // End bytes can not be larger than $end. 
      $c_end = ($c_end > $end) ? $end : $c_end; 
      // Validate the requested range and return an error if it's not correct. 
      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"); 
       // (?) Echo some info to the client? 
       exit; 
      } // fim do if 

      $start = $c_start; 
      $end = $c_end; 
      $length = $end - $start + 1; // Calculate new content length 
      fseek($fp, $start); 
      header('HTTP/1.1 206 Partial Content'); 
     } // fim do if 

     // Notify the client the byte range we'll be outputting 
     header("Content-Range: bytes $start-$end/$size"); 
     header("Content-Length: $length"); 

     // Start buffered download 
     $buffer = 1024 * 8; 
     while(!feof($fp) && ($p = ftell($fp)) <= $end){ 
      if ($p + $buffer > $end){ 
       // In case we're only outputtin a chunk, make sure we don't 
       // read past the length 
       $buffer = $end - $p + 1; 
      } // fim do if 

      set_time_limit(0); // Reset time limit for big files 
      echo fread($fp, $buffer); 
      flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. 
     } // fim do while 

     fclose($fp); 
    } // fim do function 
+0

wow merci beaucoup les gars! ce fixe les problèmes de vidéos sur iPhone 4S (iOS6), fonctionne également sur Android 4.1.1 et tous les navigateurs de bureau que j'ai testé – teejay

+0

Merci! Ce problème me dérange depuis plusieurs semaines maintenant. En fait, j'ai essayé votre solution il y a un certain temps mais je dois l'avoir mal implémentée ... J'ai encore essayé aujourd'hui et MP4 passent maintenant par PHP dans Safari, iOS et Desktop, sans problème. Pour les autres, ce qui se passe (plus ou moins) est que Safari ne demande pas simplement un mp4. Au lieu de cela, il fait une requête initiale comme vous l'attendriez, mais ensuite de nombreuses requêtes de bloc suivantes en utilisant l'en-tête Range. – Synexis

+0

ok, cela fonctionne réellement. Je ne comprends toujours pas pourquoi .. – Geo

0

Si vous lisez le fichier d'URL http alors au lieu de filsize() vous fonctionnez utilisateur ci-dessous le code pour la taille du fichier get

function getFileSize($file) { 
     $ch = curl_init($file); 
     curl_setopt($ch, CURLOPT_NOBODY, true); 
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
     curl_setopt($ch, CURLOPT_HEADER, true); 
     curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 
     $data = curl_exec($ch); 
     curl_close($ch); 
     $contentLength=0; 
     if (preg_match('/Content-Length: (\d+)/', $data, $matches)) { 

      // Contains file size in bytes 
      $contentLength = (int)$matches[1]; 

     } 
     return $contentLength; 
} 
0

J'ai eu un problème avec ce code.

Fix:

set_time_limit(0); // Reset time limit for big files 

ob_clean(); //added 

echo fread($fp, $buffer); 
flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. 
0

Comme l'a dit ci-dessus pour diffuser ou vidéos MP4 de lecture à l'aide de PHP, vous devrez gérer l'octet varie si vous voulez une bonne lecture sur Safari et iOS.

rangeDownload() La fonction mentionnée dans les réponses précédentes fait plutôt bien l'affaire.

Je veux mentionner une autre pièce de ce puzzle - assurez-vous que la source dans la vidéo se termine par .mp4 comme dans <video source="url/yourfile.php/referenceForFile.mp4">. Cela rend le navigateur que c'est un fichier vidéo, et il commence à le traiter comme un seul.

À l'intérieur yourfile.php, vous pouvez saisir la référence entrante pour votre fichier en utilisant $_SERVER['PATH_INFO'] ou au REQUEST_URI. Pas besoin de le passer comme un ?id=someId.mp4, l'approche directe de barre oblique ressemble plus à un vrai dossier.

En résumé, de mon expérience au service d'un fichier vidéo à partir de PHP correctement, vous aurez besoin:

  • support des octets de plage. Le navigateur indique au serveur la partie du fichier dont il a besoin et le serveur doit répondre avec ce contenu de gamme d'octets.
  • Demandez à votre moov atom au début du fichier (vous pouvez utiliser -movflags +faststart ou MP4Box de ffmpeg)
  • <video source="...file.mp4"> attribut source de la balise vidéo doit ressembler à un fichier .mp4. Sans cela, mes vidéos ne jouaient que dans Chrome et pas dans Safari/iOS.
  • lecteur HTML5 direct, ou vous pouvez utiliser une bibliothèque comme videojs

J'ai écrit ce basé sur mon expérience de servir des milliers de vidéos sur mon site vidéo de musique. Bien que cela puisse ne pas être le cas pour tous, mais j'ai trouvé que cette configuration cross-browser et cross-device fonctionne comme prévu.