Reading mp4 files with PHP
Asked Answered
W

2

8

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.

Warfeld answered 3/4, 2013 at 20:45 Comment(5)
And your question is?Miry
That Content-Length header is invalid. Also, are you sending a good Content-Type header?Stuffing
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. ThanksWarfeld
What's with the downvotes? This is a perfectly legitimate question.Buffoon
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)?Dominions
B
12

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.

Buffoon answered 3/4, 2013 at 21:7 Comment(2)
This works really well for my purposes, but I've noticed that it always loads the same file when I hit a URL. To force it to get a fresh URL, I have to add a GET parameter. This is alright since all of my files will be loaded as file.php?path/to/file, but I was wondering what causes this. Is it being cached on the server side or client side? Resetting my browser doesn't seem to take care of it. Mostly curiosity, though. Loading the files works well in the production code.Resourceful
@StevenSokulski While you can add headers to prevent caching, intermediate caches might ignore them, so your safest bet is to continue changing the URL as you are doing. This way, you can go the opposite direction and use far future headers to save bandwidth.Buffoon
W
6

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.

Wheezy answered 4/12, 2017 at 5:11 Comment(2)
Is it seekable?Sixpenny
@ViktorJoras It's been a while, but in my testing everything worked correctly. If it's not, trying adding the Content-Type as mentioned above and it should work.Wheezy

© 2022 - 2024 — McMap. All rights reserved.