-
-
Save codler/3906826 to your computer and use it in GitHub Desktop.
<?php | |
# Nginx don't have PATH_INFO | |
if (!isset($_SERVER['PATH_INFO'])) { | |
$_SERVER['PATH_INFO'] = substr($_SERVER["ORIG_SCRIPT_FILENAME"], strlen($_SERVER["SCRIPT_FILENAME"])); | |
} | |
$request = substr($_SERVER['PATH_INFO'], 1); | |
$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(); | |
?> |
Thanks for the code is very useful.
Works with small videos, but when I play a 300mb video doesn't work.
Change Accept-Ranges: 0-$length
to Accept-Ranges: bytes
. Otherwise some clients may request the all data at once instead of some chunks.
Thanks, I don't know if it was that or video problem. I think the large file was corrupted.
Hello,
really thank you for this code and try this on Firefox and chrome but i think the HTTP_RANGE doesn't work i stream the video from amazon s3 any idea ?
You saved my day! Thanks!
Just want to note that there seems to be a problem when using webm video files instead of mp4. Many network requests appear but the video does not continue to play. Unfortunately I could not find a solution for this issue.
@q2apro see https://github.com/diversen/http-send-file I use this to send video(mp4/webm/ogv)
thank you very much! this the only code I found that works!
absolutely awesome! thanks for that!
Python code, to download based on range header.
url = "http://changeyour-range-based-URL.snippetbucket.com/5MB.zip"
headers = {"Range": "bytes=0-100"} # first 100 bytes
r = get(url, headers=headers)
That is really good script. What if we've hosted the video on s3/oss(amazon/alicloud) bucket instead of our server. For that case this script will work?
Works well in Chrome and firefox, but not working in Safari!
Hi, i'm having a problem with this script (And many others i found) it works excellent for most of my videos, but i found a problem with large files.. For example if the file is about 300mb everything is ok.. but i tried a HD mp4 h264 video that is > 1h and 2.5 gigs and there's no way to make it play.. Is not a coding problem ..
First thing this code fix the filesize 32bit limitation for larger files:
if(substr(PHP_OS, 0, 3) == "WIN"){ exec('for %I in ("'.$file.'") do @echo %~zI', $output); $size = $output[0]; }else{ $size = filesize($file); }
But then don't know if it's a fopen and must use CURL, not sure if is the fseek handling large files.. someone with more experience than me can check and test this code for larger files? would be great!
Happy Coding!
Some many years later...thank you so much, this is awesome!
Everything works fine but for some reason I don't know, when I start playing a video and then click on a link on my page or use the browser's back button, the browser freezes and waits a while ( variable ) before following the link.
The profiler indicates that the requested page change is in pending status.
If I position the video cursor further away, the browser unlocks instantly and follows the link I clicked.
And finally if I reload that page, the issue doesn't appears anymore. Seems that chrome have cached the video.
Any idea ?
EDIT
it seems that problem occurs only with chrome..
While debugging script, I saw that value in "Content-range" header always empty.
That's involve that chrome tries to fully load video and not partially. I think this is that causing the issue.
Any idea ?
EDIT2
And of course I changed Accept-Ranges: 0-$length
to Accept-Ranges: bytes
Change
Accept-Ranges: 0-$length
toAccept-Ranges: bytes
. Otherwise some clients may request the all data at once instead of some chunks.
Thanks @Rob--W, this saved me a bunch of time
Great work, thank you very much
@fadiabualnaser
I think the AWS works, but the AWS sdk package causes memory leak issue when file seek:
function stream($file_path='', $file_name='', $file_size=0, $file_type='')
{
header('Content-Type: '. $file_type);
header('Accept-Ranges: bytes');
$headers = $this->s3->headObject(array(
'Bucket' => $this->bucket,
'Key' => $this->folder.'/'.$file_path,
));
$size = $file_size; // File size
$size = $headers['ContentLength'];
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
// header('X-Custom:'.$_SERVER['HTTP_RANGE']);
// header('Content-Disposition: inline; filename="' . rawurlencode($file_name) . '"');
$this->s3->registerStreamWrapper();
$file = "s3://{$this->bucket}/{$this->folder}/{$file_path}";
$context = stream_context_create(array('s3' => array('seekable' => true)));
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;
header('HTTP/1.1 206 Partial Content');
}
header("Content-Range: bytes $start-$end/$size");
header('Content-Length: ' . $length);
$stream = fopen($file, 'r', false, $context);
if (isset($_SERVER['HTTP_RANGE'])) {
fseek($stream, $start); // this causes error.. I think is the AWS package problem. It will try to cache the entire video into the ram.
}
$buffer = 1024 * 4;
while (!feof($stream) && ($p = ftell($stream)) <= $end) {
if ($p + $buffer > $end) {
$buffer = $end - $p + 1;
}
set_time_limit(0);
echo fread($stream, $buffer);
flush();
}
fclose($stream);
exit();
}
Great! You saved my day too!
Thank you so much! Works!
Han Lin Yap great work. Thank you for sharing.
Everything works fine but for some reason I don't know, when I start playing a video and then click on a link on my page or use the browser's back button, the browser freezes and waits a while ( variable ) before following the link.
The profiler indicates that the requested page change is in pending status.
If I position the video cursor further away, the browser unlocks instantly and follows the link I clicked.And finally if I reload that page, the issue doesn't appears anymore. Seems that chrome have cached the video.
Any idea ?
EDIT
it seems that problem occurs only with chrome..
While debugging script, I saw that value in "Content-range" header always empty.
That's involve that chrome tries to fully load video and not partially. I think this is that causing the issue.Any idea ?
EDIT2
And of course I changedAccept-Ranges: 0-$length
toAccept-Ranges: bytes
Same here... some ideas?
Thanks dude!
I had to implement ranging for video downloading for iOS video player purposes (Flutter) and it works!
I adopted it for Slim framework of course :)
Epic, this is the only thing I have found that works!
Props to you!