2010-06-27 13 views
16

Ich benutze ein PHP-Skript, um Videoanfragen zu validieren, bevor sie geliefert werden. Dieses Skript funktioniert wie erwartet auf dem Desktop mit Safari und Chrome. Aber auf iOS bekomme ich einen kaputten Spielknopf.MP4 wird abgespielt, wenn direkt zugegriffen wird, aber nicht, wenn PHP gelesen wird, auf iOS

Ich bin mir sicher, dass das Video richtig für iPhone/iPad kodiert ist, denn wenn ich direkt darauf zugreife, funktioniert es wie erwartet.

Der entsprechende PHP-Code:

$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() ähnlich ist readfile() aber für sehr große Dateien, in den Kommentaren auf der PHP-Handbuch Seite gefunden. http://php.net/manual/en/function.readfile.php In jedem Fall test-video.mp4 sind nur ~ 5 MB ., die kleiner ist als die Speichergrenze - und in diesem Fall kann ich eigentlich in der normalen readfile() und erzeugen genau das gleiche Verhalten ersetzen)

die Header ich bekomme, wenn ich test-video.mp4 direkt zugreifen, sind:

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 

Die Header aus dem PHP-Skript sind:

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 

ich viele verschiedene Permutationen von Headern versucht habe, auch sie genau die von einer direkten Anfrage, ohne Erfolg übereinstimmen.

Hat jemand Erfolg gehabt, HTML5-Video durch PHP auf iOS zu bedienen?

[Anmerkung:. Ich würde versuchen, X-Sendfile verwenden, aber die Website ist auf einem gemeinsamen Host mit sehr begrenztem Zugang]

EDIT: Ich lese, dass iOS über Dateierweiterungen empfindlich sein kann, so Ich habe versucht, eine RewriteRule einzurichten, die MP4-Anfragen zurück zu meinem ursprünglichen PHP-Skript schreibt, aber das half auch nicht.

Antwort

8

Wenn Sie es selbst so behandeln, müssen Sie auch Byte-Range-Anforderungen selbst behandeln.

+4

Genau richtig - landete ich nur das Kopieren der 'rangeDownload()' Funktion von Anhang A des folgenden Beitrages: http://mobiforge.com/developing/story/content-delivery-mobile-devices (Was ist cool Über diese Funktion ist es auch möglich, dass der Benutzer die Wiedergabe von jedem Punkt des Videos startet, indem er einfach auf die Timeline klickt.) Ich glaube, ich war besonders verwirrt wegen des Problems, weil es perfekt auf dem Desktop funktionierte, während iOS dieses Byte hat Bereich Anforderung. – JKS

15

Versuchen:

$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 vielen Dank Jungs! Dieses Problem behoben Videos auf dem iPhone 4S (iOS6), funktioniert auch auf Android 4.1.1 und alle Desktop-Browser, die ich getestet habe – teejay

+0

Vielen Dank! Dieses Problem beschäftigt mich seit einigen Wochen. Ich habe deine Lösung vor einiger Zeit ausprobiert, muss sie aber falsch implementiert haben ... Heute wurde es erneut versucht und MP4 läuft jetzt problemlos in Safari, iOS und Desktop, durch PHP. Für andere ist es (mehr oder weniger), dass Safari nicht einfach einen mp4 anfordert. Stattdessen wird eine anfängliche Anforderung erstellt, wie Sie es erwarten, aber dann viele nachfolgende Chunk-Anforderungen, die den Range-Header verwenden. – Synexis

+0

Okay, das funktioniert tatsächlich. Ich verstehe immer noch nicht warum. – Geo

0

Wenn Sie Datei von HTTP-URL zu lesen dann statt filsize() Funktion, die Sie Benutzer unter Code für get Dateigröße

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

Ich hatte ein Problem mit diesem 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

Wie oben erwähnt zu streamen oder Wiedergabe MP4-Videos mit PHP, benötigen Sie Byte zu behandeln reicht, wenn Sie die richtige Wiedergabe auf Safari und iOS wollen.

rangeDownload() Funktion, die in den vorherigen Antworten erwähnt wird, macht den Job ziemlich gut.

Ich möchte ein anderes Stück dieses Puzzles erwähnen - stellen Sie sicher, dass die Quelle im Video mit .mp4 wie in <video source="url/yourfile.php/referenceForFile.mp4"> endet. Dies macht Browser-Ding, dass es eine Videodatei ist, und es beginnt, es als eins zu behandeln.

Innerhalb yourfile.php können Sie den eingehenden Verweis für Ihre Datei mit $_SERVER['PATH_INFO'] oder innerhalb REQUEST_URI greifen. Keine Notwendigkeit, es als ?id=someId.mp4 übergeben, direkter Schrägstrich Ansatz sieht eher wie eine echte Datei.

Um es zusammenzufassen, aus meiner Erfahrung eine Videodatei von PHP zu dienen richtig benötigen Sie:

  • Byte Bereich Support. Der Browser teilt dem Server mit, welchen Teil der Datei er benötigt, und der Server muss mit diesem Inhalt des Bytebereichs antworten.
  • Haben Sie Ihre moov atom am Anfang der Datei
  • <video source="...file.mp4"> Quelle Attribut von Video-Tag aussehen muss wie eine .mp4-Datei (Sie -movflags +faststart oder MP4Box FFmpegs nutzen könnten). Ohne dies konnten meine Videos nur in Chrome und nicht in Safari/iOS abgespielt werden.
  • Direkt HTML5-Player, oder Sie können eine Bibliothek verwenden, wie videojs

schrieb ich dies auf Grund meiner Erfahrung mit Tausenden von Videos auf meinem Musik-Video-Website dient. Dies ist zwar nicht für alle der Fall, aber ich fand diese browserübergreifende und geräteübergreifende Einrichtung wie erwartet.

Verwandte Themen