PHP: iPad does not play MP4 videos provided by PHP, but with direct access it does

I’ve been trying to find a solution to this problem for several days, I tried all the tips that I could find here on stackoverflow and other platforms. And yet there is no solution.

I embed a video through an HTML5 tag:

<video poster="thumb.png" controls="controls" preload="none" width="640" height="480"> <source src="provider.php?secure=12345" type="video/mp4"> </video> 

I am trying to deliver an MP4 video file via PHP, and not directly link it. Binding a mp4 file directly works and plays the file!

Testing:

Notes:

  • all the links above have direct access / playback of the video file without a built-in tag
  • The video works in all browsers on Windows (but not on Safari / Chrome on the iPad, possibly not on the iPhone).

My setup:

  • testing device: iPad iOS 6 (I don’t have a mac, cannot debug )
  • iPad with Safari and Chrome (tried both browsers)
  • my server is public with domainfactory
  • debugging tool: Firefox 29 Web Developer Console / WIN7

.htaccess in the test folder sets the MIME type and Accept-Ranges:

 AddType video/mp4 .mp4 <IfModule mod_headers.c> Header set Accept-Ranges "bytes" </IfModule> 


Even if I created the same header (compare test URLs 1. and 2. ), iPad does not play the file through a PHP request.

Instead, I always get this scroll button:

ipad strikedthrough play button

Headings 1. (mp4 direct call) :

mp4 direct call

Headers 2. The same headers as above, but configured by PHP (mp4 supplied by PHP):

enter image description here

-

I also tried reading the entire video file and sending it to the browser using PHP fread () , fpassthru () and file_get_contents () , but the iPad always shows a cannot-play-icon.

-

My hosted server does not support Connection keep-alive, could this be a problem? Is the iPad a .php interpretation other than .mp4?

Can someone help me get rid of the pain? I am completely stuck.



PS: I tried to consider:

  • Request a range of bytes (206 partial content) 01 02 03
  • proper video encoding 04
  • used other encoded videos when testing
  • disabled zlib.output_compression in php scripts



UPDATE: debug console

I finally got my friend’s MAC address, connected the iPad, opened the Debug console on Safari on the Mac, loaded the page on the iPad and checked the error messages appearing on the Mac (by the way, how much more complex could the power of the apple develop us ...). For all test scripts this error appears:

 Failed to load resource: Plug-in handled load 
+11
html5 php ipad video
Jun 04 '14 at 16:35
source share
2 answers

Wow, that was hard!


1. The first serious problem

It turned out that this is not an encoding problem, but a problem with the mp4 container header set during the video conversion process. The iPad obviously has a problem with MP4 videos that are prepared for progressive streaming .

At first I found that in a conversation here . After converting the video, I always used the MP4 Quick Launch tool to prepare the video file for progressive stream. This was necessary for streaming a video file to Flash Player in parts (gradually), so it did not download the entire file (and the user had to wait).

With Handbrake, there is a similar setting called Web Optimized . He does the same:

 Web Optimized Also known as "Fast Start" This places the container header at the start of the file, optimizing it for streaming across the web. 

If you turn it on and convert your video, the iPad will not play the video! Instead, you will receive the error message "Operation could not be completed."

ipad strikedthrough play button

Check and test it yourself: resources for testing videos .


2. The second problem

In the production environment, I always used PHP to check the abstract. As I found out, the iPad does not send referrer information. This also prevents streaming, and you will also see a “can not play” symbol (icon with skipped playback).


3. The third problem

I could not understand why, but the iPad only accepts streaming video from this script http://ideone.com/NPSlw5

 <?php // disable zlib so that progress bar of player shows up correctly if(ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } $folder = '.'; $filename = 'video.mp4'; $path = $folder.'/'.$filename; // from: http://licson.net/post/stream-videos-php/ if (file_exists($path)) { // Clears the cache and prevent unwanted output ob_clean(); $mime = "video/mp4"; // The MIME type of the file, this should be replaced with your own. $size = filesize($path); // The size of the file // Send the content type header header('Content-type: ' . $mime); // Check if it a HTTP range request if(isset($_SERVER['HTTP_RANGE'])){ // Parse the range header to get the byte offset $ranges = array_map( 'intval', // Parse the parts into integer explode( '-', // The range separator substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header ) ); // If the last range param is empty, it means the EOF (End of File) if(!$ranges[1]){ $ranges[1] = $size - 1; } // Send the appropriate headers header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges: bytes'); header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range // Send the ranges we offered header( sprintf( 'Content-Range: bytes %d-%d/%d', // The header format $ranges[0], // The start range $ranges[1], // The end range $size // Total size of the file ) ); // It time to output the file $f = fopen($path, 'rb'); // Open the file in binary mode $chunkSize = 8192; // The size of each chunk to output // Seek to the requested start range fseek($f, $ranges[0]); // Start outputting the data while(true){ // Check if we have outputted all the data requested if(ftell($f) >= $ranges[1]){ break; } // Output the data echo fread($f, $chunkSize); // Flush the buffer immediately @ob_flush(); flush(); } } else { // It not a range request, output the file anyway header('Content-Length: ' . $size); // Read the file @readfile($path); // and flush the buffer @ob_flush(); flush(); } } die(); ?> 


I hope this information helps others deal with this issue.


Update: After three months in the production environment, some of my users still reported playback issues. There seems to be another problem with Safari. I advised them to use Chrome for the iPad , this fixed it.



PS: A couple of days of research and hassle just to play a video file, which, by the way, works on all other devices. This again proves to me that Apple was successful only because of a lot of marketing, and not because of a lot of software.

+15
Jun 06 '14 at 7:30
source share

Thank you for your contribution, it is very important ... But even your code could not do this for my iphone.

Even if I still don't know why, the following code worked for me. Probably for the line:

 header('HTTP/1.1 416 Requested Range Not Satisfiable'); 

I got this here: https://github.com/tikiatua/internal-assets-plugin/issues/9

 $fp = fopen($filepath, "rb"); $size = filesize($filepath); $length = $size; $start = 0; $end = $size - 1; header('Content-type: video/mp4'); header("Accept-Ranges: 0-$length"); if (isset($_SERVER['HTTP_RANGE'])) { $c_start = $start; $c_end = $end; list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); if (strpos($range, ',') !== false) { header('HTTP/1.1 416 Requested Range Not Satisfiable'); header("Content-Range: bytes $start-$end/$size"); exit; } if ($range == '-') { $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); $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); exit; 

Hope I helped someone! Greets everyone

0
Jun 20 '19 at 15:49
source share



All Articles