Streaming an mp4 through a php file progressively - php

I'm working on a more secure streaming method for our video player. Because each file requires special token authentication and also only allows each token to be loaded once, I'm running MP4 through a php container. This works perfectly inside a HTML5 video tag and prevents users from easily downloading the source.
I'm adapting some code from here
<?php
include('...'); //include site functions
/*
Here I connect to the database, and retrieve the
location of the video which is returned as
$video = "http://domain.tld/folder/file.mp4"
This has been removed for this SO example.
*/
$file = $video;
$fp = #fopen($file, 'rb');
$head = array_change_key_case(get_headers($file, TRUE));
$size = $head['content-length'];
//$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
header('Content-type: video/mp4');
//header("Accept-Ranges: 0-$length");
header("Accept-Ranges: bytes");
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();
?>
Problems now arise as I'd like to be able to seek into the video. Skipping to an area in the video which has not been loaded crashes the <video> tag (and chrome).
I'd assume that since the HTML5 player is seeking in seconds and the PHP is loading in bytes, that this cannot be done. Which is why I'm asking here.
What can I do (if anything) to allow progressive streaming?
Is there a more appropriate container format I could use which would serve the same purpose?

In addition to what dlopez said, I recommend to use 3rd-party solution for progressive download with seeking capabilities (AKA pseudo-streaming). You may take a look at the PD solutions listed in Wikipedia: https://en.wikipedia.org/wiki/Progressive_download
Most of them can also prevent video hotlinking protection as well.

I think that you have a conceptual failure. You are treating the mp4 file like if it were a "raw data file". I try to explain myself.
Imagine that you have a text file, and you want to get the chars from position X. You can open the file, point the cursor to the correct position and then read byte by byte your text. This will work fine.
But now image that you want to do the same but with a text processor file. Would you expect the same results? No, because you have a lot of metadata in the file that prevents you from doing that.
Your problem is basically the same. You need to take in consideration the format of the file, managing the file with libraries designed for it, or doing by yourself.
The other option will be to work with raw data files, but in the case of video files, these are going to be really big files.
I hope that I helped you.

I ran into a similar problem. Using stream_get_content($fp, $start) instead of fseek fixed the issue. I was able to get the video to stream fine on all browsers, and seeking (or skipping) throughout the video worked without a problem.

Fully Functional Code
$file = "z.mp4";
$length= filesize($file);
$offset = 0;
$f = fopen($file, 'r');
stream_get_contents($f, $offset);
$pos = 0;
while($pos < $length){
$chunk = min($length-$pos, 1024*8);
echo fread($f, $chunk);
flush();
ob_flush();
$pos += $chunk;
}

You can't use fseek(5654) if you are using file path with http://example.com...... You should use c:/file/abc.mp4 instead. It may solve your problem...

Related

How to open video/mp4 with php

I'm trying to read mp4 files with PHP, my initial code was
$file = 'https://s3-sa-east-1.amazonaws.com/onlytestes/video.mp4';
header('Content-type: video/mp4');
readfile($file);
But that way I couldn't use the length bar of the video, skip or even go back, until the video is 100% loaded.
Of course when I read the file directly (video.mp4) everything is fine.
I solved this problem with the following code:
$request = 'video.mp4';
$file = $request;
$fp = #fopen($file, 'rb');
$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
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();
But it only works with local files, I think HTTP_RANGE does not work, it returns the following error in the console
''Failed to load resource: the server responded with a status of 416 (Requested Range Not Satisfiable)''
I need to read Amazon S3 videos, for example: video here
Does anyone have any ideas?
Have you read Document Manual Amazon SDK..?
(1) Download latest stable version of SDK
(2) Extract the .zip file & place in wamp/www folder
(3) Rename config-sample.inc.php file to config.inc.php
(4) Add the access key & secret key (retrieved from Amazon S3 account) into
above file, save & exit
(5) create a sample file to display public / private objects from Amazon S3
thank you Yusnur Hidayah
But I still have problems,
Opening the file by the SDK / StreamWrapper, it takes about 18 seconds for the video to start, see
http://192.241.159.176/file.php
This way it is impossible to make available to users
Usually opens in 1 or 2 seconds, see
https://s3-sa-east-1.amazonaws.com/onlytestes/video.mp4

How to make live streaming using php?

I have live-streaming link where its format extension is .m3u8.
and I want it to be live in to my page.
I tried this code but it does'nt work
<?php
$file = 'http://93.87.85.70/PLTV/88888888/224/3221226661/04.m3u8';
$fp = #fopen($file, 'rb');
$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
header('Content-type: video/mp4');
//header("Accept-Ranges: 0-$length");
header("Accept-Ranges: bytes");
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();
?>
Is there something wrong or I am needed to add something there?
Or if this code is wrong what another method am gonna use.
.m3u8 is a playlist file, not an MP4 video. It's commonly used with HLS streams.
An HLS stream is made up of a whole collection of files. There will be audio/video file segments every several seconds, possibly at several bitrates, with the playlist. The playlist is updated regularly.
Even if you did want to proxy these files, your script is not the way to do it. Best to leave it up to the web server (Nginx, Apache, whatever you're using), as that's what it does best. Your script isn't respecting the upstream content type, headers, etc. It's also ignoring any errors on fopen(). I wouldn't use fopen() on a URL anyway... the wrappers aren't always enabled, and then you don't have access to the real status codes and headers. And finally, you've hardcoded the upstream path so your users would only get the playlist and not the media files.
All you have to do is use a client-side player than handles HLS, like JWPlayer or similar. This has nothing to do with PHP. It's done with JavaScript and the Media Source Extensions API.

PHP Stream to html5 Video, Page Navigation not working

i have troubles with Streaming .mp4 Videos from PHP to a HTML5 Video Tag. The Video Streams fine and also moving forward and backward works. BUT: If i click on any Link in my Menu, the Site will not change. The loading Icon Appears on the Tab and the Dev Tools show that there is a request. But the request "waits" until the Video Stream End. So if the Video ended, the new Page will load, but not before.
Any ideas on this?
PS: Adding session_write_close(); before streaming the File solves the Problem. But it looks a bit too hacky for me...
<video style="width:100%;" preload="metadata" controls="">
<source src="/uploads/getfile?image_path=5%2FOKzAAFlSub-VLsnFWvkPWXBLluwOV-Q5DIuqJkPpDubahlAosK.mp4&type=20" type="video/mp4">
Your browser does not support the video tag.
</video>
PHP Code:
$file = Yii::getAlias('#app') . '/../files/uploads/' .$image_path;
$fp = #fopen($file, 'rb');
$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
header('Content-type: video/mp4');
header("Accept-Ranges: 0-$length");
header("Accept-Ranges: bytes");
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);
ob_flush();
}
fclose($fp);
exit();
Adding session_write_close(); before streaming the File solves the Problem. But it looks a bit too hacky for me...
There isn’t much “hacky” about this.
A long-running script that keeps a session open will block access to that same session for all other scripts that get started later on (as with your menu link click.)
To avoid that, you close the session in the long-running script as soon as you are done with it, so that the lock on the session data file can be released.
What is quite “hacky” though, is streaming video data via a script in the first place. That is what you should avoid doing in the first place, if at all possible. It is not a good idea in terms of memory usage and script runtimes.
It is better to use already exist PHP package to support partial download. Here is one of them from pear:
http://pear.php.net/manual/en/package.http.http-download.php
It support several types of download (partial, stream , ..). Here is a sample code:
$dl = &new HTTP_Download();
$dl->setData($data);
$dl->setLastModified($unix_timestamp);
$dl->setContentType('application/x-gzip');
$dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, 'latest.tgz');
$dl->send();

Streaming a video with PHP

I currently have this code:
function Stream($file)
{
header_remove();
$arr = get_headers($file);
foreach ($arr as &$value) {if((strpos($value,'Content-Type')!== false)){header($value);}}
if (isset($_SERVER['HTTP_RANGE'])) {
rangeDownload($file);
}
else {
header('HTTP/1.1 206 Partial Content');
header("Content-Length:1");
//foreach ($arr as &$value) {if((strpos($value,'Content-Length')!== false)){header($value);}}
//header("Content-Range:bytes 21056-21056/243957100");
readfile($file);
}
}
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;
}
// 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 ($range0 == '-') {
// 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;
}
/* 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;
}
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1; // Calculate new content length
fseek($fp, $start);
header('HTTP/1.1 206 Partial Content');
}
// 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;
}
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.
}
fclose($fp);
}
I can see that all 3 requests are made and the file download begins yet instead of the video screen appearing the stream cancels. I have tried mimics standards apache headers without success, I am trying to have it serve video content with the widest possible support (e.g. content type detection).
I just had to go through the same thing for a job I was asked to do. I had no issues making it work for chrome/firefox but I had issues with Safari on the Mac/iPad. I had referenced multiple sources and tried many things, in the end I threw everything I had to get a clean slate, and decided to try fix the issue using the code that was in this issue since it was very similar to what I already had, but different enough to give me more clarity.
Here is the final working version using the code from the original question as a base but with the necessary fixes that worked for me. Hopefully this helps someone else.
if (isset($_SERVER['HTTP_RANGE'])) {
header('Content-Type: video/'.$ext); // reference your own mime variable here
$fp = #fopen($filepath, 'rb');
$size = filesize($filepath); // 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");
// multipart/byteranges
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
$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;
}
// 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 == '-') {
// 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;
}
/* 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;
}
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1; // Calculate new content length
header('HTTP/1.1 206 Partial Content');
// 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;
fseek($fp,$start);
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;
}
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.
}
fclose($fp);
}
This is the ENTIRE code that I use, with the exception of a few variables before it to build the filepath and video extension/mime type. This has been tested on Chrome(latest), Firefox(latest), Safari 7.0.6 on macbook pro, Safari on iOS 7
The issue is in the range download function.In this function you are headers are correct.but you are printing the complete file as the response.
These changes should fix your problem.
// Notify the client the byte range we'll be outputting
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
Here you need to seek the file to start specified in the content-range headers.
fseek($fp,$start) /* MISSING CODE */
Inserting the above line should fix your problem.
// 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;
}
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.
}
fclose($fp);

VideoJS Flash Version doesn't Play Files (PHP Passthrough Stream)

I am Using the newest Version of VideoJS and Put my Videos Through PHP in the Following way:
Called like: $this->streaming->handleDownload($videoPath, "video/mp4");
The Function is defined like:
public function handleDownload($file, $mime = "") {
if (is_file($file)) {
header("Content-Type: $mime");
if (isset($_SERVER['HTTP_RANGE'])) { // do it for any device that supports byte-ranges not only iPhone
header("X-Info: Starting Ranged Download of $file");
$this->rangeDownload($file);
} else {
header("Content-Length: ".filesize($file));
header("X-Info: Starting Straight Download of $file");
readfile($file);
}
} else {
header("HTTP/1.1 500 Internal Server Error");
$msg = "The requested path was no file!";
}
}
The rangeDownload() is defined:
private function rangeDownload($file) {
ob_end_clean();
$fp = #fopen($file, 'rb');
$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
header("Accept-Ranges: 0-$length");
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) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
exit;
}
header("X-Got-Range: $range, $range0");
// 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 ($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;
}
// 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;
}
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1; // Calculate new content length
fseek($fp, $start);
header('HTTP/1.1 206 Partial Content');
}
// 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;
}
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.
}
fclose($fp);
}
I am Passing the Video through PHP, because the Files should be only Visible when Logged In.
I do the things with the Partial Content/Ranges because I want to be able to Click in the Middle of The Position bar and The Video should start to play from there.
That all works well with Chrome, Newer Safari, iOS, but (Because of the Flash Fallback) doesn't work in IE or Firefox (Video is mp4) (Perhaps it only takes TOO long to load)
The Range Works in Chrome, but well, the Flash Version of VideoJS doesn't even Ask for the Range-Download, so it gets the other Version.
If I play the Video directly (With PHP-streaming, but Without VideoJS) in Firefox (Just call the Video-URL in the URL Bar) it Starts directly and Loads while it Plays; VideoJS doesn't
What do I need to change that the Videos starts to Play directly in VideoJS Flash-Version?
I have found the Solution (by accident ;-) )
The Problem was, that The Flash-Player asks for The Video as a Complete File. As Long as The Server doesn't answer, that there are Streaming-Versions Available (Accept-Ranges: 0-$size) the Player doesn't Ask for them.
So I set the Header and it Worked.
So All a small Tutorial for all that have Problems with VideoJS Flash Fallback:
Videos need to be encoded via MP4Box (See MP4 in Video.js not playing until fully loaded)
The PHP Passthrough Script needs to send following Headers:
header("Content-Type: $videoMimeType");
header("Content-Length: $videoByteCount");
header("Accept-Ranges: 0-$videoByteCount");
If You want to handle the Range (Identified by isset($_SERVER["HTTP_RANGE"]) ) you need also to specify those headers:
header("Content-Range: bytes $startByte-$endByte/$videoByteCount");
This works for me (See Code in my Question). It works with Chrome, FF, iOS, Safari (IE not tested yet) with Flash and HTML5 with mp4-Files

Categories