Streaming an mp4 through a php file progressively
Asked Answered
R

5

6

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?

Restricted answered 25/2, 2014 at 15:46 Comment(1)
Your best best I would think is to find a library for PHP meant for dealing with video files that support the mp4 format. Then I would think you almost need an AJAX call that can update the stream to the point selected in the player or else to reload it in the browser.Kerge
D
2

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.

Daffi answered 25/2, 2014 at 19:1 Comment(1)
Yes, This is what I feared. I guess I'll be forced into using a flash solution, boo. Thanks for your answer!Restricted
A
4

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.

Alegre answered 26/2, 2014 at 0:21 Comment(0)
D
2

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.

Daffi answered 25/2, 2014 at 19:1 Comment(1)
Yes, This is what I feared. I guess I'll be forced into using a flash solution, boo. Thanks for your answer!Restricted
A
0

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.

Apyretic answered 15/1, 2016 at 22:31 Comment(0)
M
0

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;
}
Mandolin answered 29/5, 2017 at 18:51 Comment(0)
C
-1

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

Chart answered 22/6, 2014 at 6:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.