6
votes

I'm trying to read mp4 file with PHP and what I'm doing now is this:

<?php
header("Content-Length: filesize");
readfile('file.mp4');
?>

but this way I can't skip or even go back until the video is not loaded 100%. Of course when I read directly from the file (video.mp4) everything goes well.

Thanks.

2
And your question is?Class
That Content-Length header is invalid. Also, are you sending a good Content-Type header?Tom van der Woerdt
My quesntion is how I can read the file correctly so I can go back and forth as I can when I'm accessing the file directly. Thanksuser2221276
What's with the downvotes? This is a perfectly legitimate question.cmc
Are you trying to play the video in a player using a http link as the video source? It is not very clear from your question, maybe you want to edit to state your intent more clearly. Also, which player are your using? What is your command line (if any)?Andrea

2 Answers

10
votes

You need to implement the skipping functionality yourself in PHP. This is a code snippet that will do that.

<?php

$path = 'file.mp4';

$size=filesize($path);

$fm=@fopen($path,'rb');
if(!$fm) {
  // You can also redirect here
  header ("HTTP/1.0 404 Not Found");
  die();
}

$begin=0;
$end=$size;

if(isset($_SERVER['HTTP_RANGE'])) {
  if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
    $begin=intval($matches[0]);
    if(!empty($matches[1])) {
      $end=intval($matches[1]);
    }
  }
}

if($begin>0||$end<$size)
  header('HTTP/1.0 206 Partial Content');
else
  header('HTTP/1.0 200 OK');

header("Content-Type: video/mp4");
header('Accept-Ranges: bytes');
header('Content-Length:'.($end-$begin));
header("Content-Disposition: inline;");
header("Content-Range: bytes $begin-$end/$size");
header("Content-Transfer-Encoding: binary\n");
header('Connection: close');

$cur=$begin;
fseek($fm,$begin,0);

while(!feof($fm)&&$cur<$end&&(connection_status()==0))
{ print fread($fm,min(1024*16,$end-$cur));
  $cur+=1024*16;
  usleep(1000);
}
die();

More Performance

Note that this is not the most efficient way to do it, because the whole file needs to go through PHP, so you will just need to try how it goes for you.

Assuming the reason you want to do this is to restrict access, and you need more efficiency later, you can use a flag for the web server.

Apache with X-Sendfile module or lightty (nginx info here)

$path = 'file.mp4';
header("X-Sendfile: $path");
die();

This is a bit more advanced and you should only use it if you need it, but it is relaxing to know you have an upgrade option when you start out with something that is rather easy but has mediocre performance.

5
votes

I found a much cheaper PHP-only way by experimenting with the accepted solution. Although I haven't tested this against X-Sendfile, I suspect the performance is even better overall for your site, since it means a shorter Apache file.

You only need the following code to be put out seekable .mp4 videos from PHP (haven't tested with .webm or other types):

$file='path/file.mp4';

header('Content-Type: video/mp4'); #Optional if you'll only load it from other pages
header('Accept-Ranges: bytes');
header('Content-Length:'.filesize($file));

readfile($file);

Much cleaner and faster; the accepted answer's code resulted in the video taking a very long time to load for some reason, loading the video with this code was instantaneous (both tests were on a local server with a 7.25 MB video file).

Tested with Chrome, Firefox, and Edge's default video players.

Edit: tested without header('Content-Type: video/mp4');, it still works if the file's loaded from another page! If you directly access the URL, there will be no video player; the code will just be printed out.