Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 267
Jamie Taylor Avatar asked Feb 25 '14 15:02

Jamie Taylor


People also ask

What is progressive streaming?

A progressive video stream is a single video file being streamed over the internet. This type of file is often a . mp4 but can be in many different formats. The progressive video can be stretched and squashed to fit different screen sizes, but regardless of the device playing it, the video file will always be the same.

How do I stream with PHP?

The PHP wrapper allows access to the languages' own input and output streams, along with access to temporary memory and disk-backed file streams. To get access to the standard input stream, you can use php://stdin, which is read-only.

Does MP4 support streaming?

Mp4 is a great format for downloadable videos but it's no fit for video streaming.


1 Answers

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.

like image 117
Dmitry Shovtyuk Avatar answered Oct 14 '22 16:10

Dmitry Shovtyuk