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

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

Related

Streaming MP3: iOS restarts HTML5 audio after 2 minutes

I'm trying to stream an mp3 file and play it via HTML5 audio tag.
After about 2 minutes, iOS starts playing the audio from the start. I tested this on Safari # iOS 11.4.
Below is simplified code:
File: index.html
<!DOCTYPE html>
<html>
<body>
<audio controls><source src="audio.php" type="audio/mpeg"></audio>
</body>
</html>
File: audio.php
header('Content-Type: audio/mpeg');
header('Content-Disposition: inline; filename="audio.mp3"');
header('X-Pad: avoid browser bug');
header('Cache-Control: no-cache');
readfile('audio.mp3');
Audio works if I use audio.mp3 file directly
Everything works well on all other devices / platforms (all browsers on Mac, Windows and Android).
What is wrong with this setup?
How to properly stream audio to iOS?
Thanks!
i had the same problem
this article helps me to solve the problem
https://mobiforge.com/design-development/content-delivery-mobile-devices
the problem is on some header parameters that must be set to work on IOS
//insert file path
$file = 'insert_file_path';
//audio/mpeg for mp3 file
$mime_type = 'audio/mpeg';
if (is_file($file)) {
header("Content-type: $mime_type");
if (isset($_SERVER['HTTP_RANGE'])) { // do it for any device that supports byte-ranges not only iPhone
rangeDownload($file);
} else {
header("Content-Length: " . filesize($file));
readfile($file);
}
}else {
// file not exist
// some error...
}
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 ($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
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);
}

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 an mp4 through a php file progressively

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...

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);

php stream any mp4

I'm developing a web app that converts videos and allows to play them through Flowplayer.
On the current status, I use ffmpeg to convert the videos to mp4 and qtfaststart to fix their metadata for streaming. Everything is working smoothly as I can download any converted mp4 and view it correctly.
For serving the videos to Flowplayer, I use a php file which contains the following (summarized) code:
header("Content-Type: {$mediatype}");
if ( empty($_SERVER['HTTP_RANGE']) )
{
if ( $filetype == 'flv' && $seekPos != 0 )
{
header("Content-Length: " . ($filesize + 13));
print('FLV');
print(pack('C', 1));
print(pack('C', 1));
print(pack('N', 9));
print(pack('N', 9));
}
else
{
header("Content-Length: {$filesize}");
}
$fh = fopen($filepath, "rb") or die("Could not open file: {$filepath}");
# seek to requested file position
fseek($fh, $seekPos);
# output file
while(!feof($fh))
{
# output file without bandwidth limiting
echo fread($fh, $filesize);
}
fclose($fh);
}
else //violes rfc2616, which requires ignoring the header if it's invalid
{
$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);
}
Unfortunately, it is working only for the majority of the videos. For some of them, Flowplayer keeps returning Error 200, even though they were encoded correctly.
How can I fix this? Is it a coding problem or those videos are faulty?

Categories