Skip to content

Instantly share code, notes, and snippets.

@codler
Created October 17, 2012 17:18
Show Gist options
  • Star 63 You must be signed in to star a gist
  • Fork 19 You must be signed in to fork a gist
  • Save codler/3906826 to your computer and use it in GitHub Desktop.
Save codler/3906826 to your computer and use it in GitHub Desktop.
Support HTTP Header Range, mp4, php.php/mp4.mp4
<?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();
?>
@tedchou12
Copy link

@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();
    }

@Serabass
Copy link

Great! You saved my day too!

@pakeil
Copy link

pakeil commented Feb 24, 2021

Thank you so much! Works!

@TatiSamuel
Copy link

Han Lin Yap great work. Thank you for sharing.

@sidelux
Copy link

sidelux commented Apr 14, 2021

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

Same here... some ideas?

@pawtwa
Copy link

pawtwa commented May 23, 2021

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment