views:

90

answers:

3

I'm writing a web app that serves H.264 encoded MP4 video. In Chrome and Safari, it does this via an HTML5 video tag.

In order to control access to these videos, their contents are served via PHP using a really simply mechanism:

header('Content-type: video/mp4');
readfile($filename);
exit;

No matter what I do, the videos will not stream. Additionally:

  • If I change the source code to serve the files directly, using the same video tag but linking to an Apache-served copy of the video with no PHP pass-through, streaming works fine.
  • Even when streaming doesn't work, I can always right click on the greyed-out HTML5 player and download the file through the PHP pass-through - and it plays great offline.

Any ideas? I'm pulling my hair out!

+1  A: 

Maybe. Try adding also the content length header:

header('Content-length: '.filesize($filename));

If this still doesn't work, check for any output before readfile (echo's or whitespace before <?php). Check also that you don't have whitespace after ?> or simply omit ?> (it's not mandatory if you have nothing after).

As Bruno mentioned, to support streaming, you also need to obey the Range header. Here's a simplified example that respects only the left bound:

if (empty($_SERVER["HTTP_RANGE"])) {
    //do your current stuff...
}
else { //violes rfc2616, which requires ignoring  the header if it's invalid
    preg_match("/^bytes=(\d+)-/i",$_SERVER["HTTP_RANGE"], $matches);
         $offset = (int) $matches[1];
    if ($offset < $filesize && $offset >= 0) {
        if (@fseek($fp, $offset, SEEK_SET) != 0)
            die("err");
        header("HTTP/1.1 206 Partial Content");
        header("Content-Range: bytes $offset-".($filesize - 1)."/$filesize");
    }
    else {
        header("HTTP/1.1 416 Requested Range Not Satisfiable");
        die();
    }
        //fread in loop here
}
Artefacto
I guess you meant `Content-Length` instead of `Content-size`
Bruno
@Bruno You're right. Thanks!
Artefacto
A: 

See comments!

Using readfile is not recommended for streaming video files since it loads the whole file into memory before outputting. This causes serious problems with memory running out.

Try reading and outputting the file chunk by chunk.

zaf
That's not true. In fact, this is the most efficient way to do it (using PHP and excluding `virtual`). I don't know where you got that idea.
Artefacto
Can you point to some references? As there are plenty of references against. Example: http://teddy.fr/blog/how-serve-big-files-through-php
zaf
And another http://coding.derkeiler.com/Archive/PHP/php.general/2006-10/msg00708.html
zaf
Artefacto
@Artefacto Straight to the source! I was mistaken, thanks for enlightening me (and hopefully others).
zaf
A: 

When streaming files to HTML5 Embedded video player you still have to add headers that inform the player with information about the video.

you can't just expect to run a read readfile() command and things will magically work, sorry bud, but programming is not that easy. (Wish it was).

heres a small application you can use to stream properly or just learn from.

http://stream.xmoov.com/download/xmoov-php/

RobertPitt