Instantly share code, notes, and snippets.

Embed
What would you like to do?
PHP VideoStream class for HTML5 video streaming
<?php
/**
* Description of VideoStream
*
* @author Rana
* @link http://codesamplez.com/programming/php-html5-video-streaming-tutorial
*/
class VideoStream
{
private $path = "";
private $stream = "";
private $buffer = 102400;
private $start = -1;
private $end = -1;
private $size = 0;
function __construct($filePath)
{
$this->path = $filePath;
}
/**
* Open stream
*/
private function open()
{
if (!($this->stream = fopen($this->path, 'rb'))) {
die('Could not open stream for reading');
}
}
/**
* Set proper header to serve the video content
*/
private function setHeader()
{
ob_get_clean();
header("Content-Type: video/mp4");
header("Cache-Control: max-age=2592000, public");
header("Expires: ".gmdate('D, d M Y H:i:s', time()+2592000) . ' GMT');
header("Last-Modified: ".gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT' );
$this->start = 0;
$this->size = filesize($this->path);
$this->end = $this->size - 1;
header("Accept-Ranges: 0-".$this->end);
if (isset($_SERVER['HTTP_RANGE'])) {
$c_start = $this->start;
$c_end = $this->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 $this->start-$this->end/$this->size");
exit;
}
if ($range == '-') {
$c_start = $this->size - substr($range, 1);
}else{
$range = explode('-', $range);
$c_start = $range[0];
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
}
$c_end = ($c_end > $this->end) ? $this->end : $c_end;
if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $this->start-$this->end/$this->size");
exit;
}
$this->start = $c_start;
$this->end = $c_end;
$length = $this->end - $this->start + 1;
fseek($this->stream, $this->start);
header('HTTP/1.1 206 Partial Content');
header("Content-Length: ".$length);
header("Content-Range: bytes $this->start-$this->end/".$this->size);
}
else
{
header("Content-Length: ".$this->size);
}
}
/**
* close curretly opened stream
*/
private function end()
{
fclose($this->stream);
exit;
}
/**
* perform the streaming of calculated range
*/
private function stream()
{
$i = $this->start;
set_time_limit(0);
while(!feof($this->stream) && $i <= $this->end) {
$bytesToRead = $this->buffer;
if(($i+$bytesToRead) > $this->end) {
$bytesToRead = $this->end - $i + 1;
}
$data = fread($this->stream, $bytesToRead);
echo $data;
flush();
$i += $bytesToRead;
}
}
/**
* Start streaming video content
*/
function start()
{
$this->open();
$this->setHeader();
$this->stream();
$this->end();
}
}
@annopnod

This comment has been minimized.

Show comment
Hide comment
@annopnod

annopnod Jul 15, 2014

line 39: header("Content-Type: video/mp4");
cannot play .webm file format

you should add:

$ext = strtolower(pathinfo($this->path, PATHINFO_EXTENSION));
switch ($ext)
{
case "mp4":
header("Content-Type: video/mp4");
break;
case "webm":
header("Content-Type: video/webm");
break;
}

annopnod commented Jul 15, 2014

line 39: header("Content-Type: video/mp4");
cannot play .webm file format

you should add:

$ext = strtolower(pathinfo($this->path, PATHINFO_EXTENSION));
switch ($ext)
{
case "mp4":
header("Content-Type: video/mp4");
break;
case "webm":
header("Content-Type: video/webm");
break;
}

@basicssm

This comment has been minimized.

Show comment
Hide comment
@basicssm

basicssm Oct 17, 2014

I play only 40 second of video?

Why?

basicssm commented Oct 17, 2014

I play only 40 second of video?

Why?

@ziggi

This comment has been minimized.

Show comment
Hide comment
@ziggi

ziggi May 31, 2015

Very useful. Thank you.

ziggi commented May 31, 2015

Very useful. Thank you.

@scipper

This comment has been minimized.

Show comment
Hide comment
@scipper

scipper Jun 2, 2015

Very nice! solved my problems :)

scipper commented Jun 2, 2015

Very nice! solved my problems :)

@dirkx

This comment has been minimized.

Show comment
Hide comment
@dirkx

dirkx Sep 1, 2015

May be an idea to add a if(connection_aborted()) near line 104 or as part of the while().

dirkx commented Sep 1, 2015

May be an idea to add a if(connection_aborted()) near line 104 or as part of the while().

@elangodhilipan

This comment has been minimized.

Show comment
Hide comment
@elangodhilipan

elangodhilipan Sep 4, 2015

all type video formats are not supported in this code... so please any body help me for all types of video supported streaming code..

elangodhilipan commented Sep 4, 2015

all type video formats are not supported in this code... so please any body help me for all types of video supported streaming code..

@NguyenTungs

This comment has been minimized.

Show comment
Hide comment
@NguyenTungs

NguyenTungs Oct 23, 2015

Very nice! But The user reload video when streaming ...

NguyenTungs commented Oct 23, 2015

Very nice! But The user reload video when streaming ...

@sadiq810

This comment has been minimized.

Show comment
Hide comment
@sadiq810

sadiq810 Dec 28, 2015

too much helpful ,thanks alot.....

sadiq810 commented Dec 28, 2015

too much helpful ,thanks alot.....

@harish40

This comment has been minimized.

Show comment
Hide comment
@harish40

harish40 Feb 1, 2016

How to use the code & where to replace the video path plzz let me know

harish40 commented Feb 1, 2016

How to use the code & where to replace the video path plzz let me know

@nalmada

This comment has been minimized.

Show comment
Hide comment
@nalmada

nalmada Mar 4, 2016

@ranacseruet
works great !!
do you have the same thing for players like VLC and DivX ?

nalmada commented Mar 4, 2016

@ranacseruet
works great !!
do you have the same thing for players like VLC and DivX ?

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Mar 8, 2016

Thank you!

ghost commented Mar 8, 2016

Thank you!

@Lixus

This comment has been minimized.

Show comment
Hide comment
@Lixus

Lixus Mar 10, 2016

@ranacseruet Thanks for this script! I just noticed that video streaming with this does not work on Android 5 and 6 devices... Any ideas?

Lixus commented Mar 10, 2016

@ranacseruet Thanks for this script! I just noticed that video streaming with this does not work on Android 5 and 6 devices... Any ideas?

@jadrovski

This comment has been minimized.

Show comment
Hide comment
@jadrovski

jadrovski Jun 2, 2016

Thank you, very nice solution

jadrovski commented Jun 2, 2016

Thank you, very nice solution

@lynxfg

This comment has been minimized.

Show comment
Hide comment
@lynxfg

lynxfg Jul 29, 2016

Very nice class !
But the url with http doesn't work :
$stream = new VideoStream(".http://www.exemple.com/video.mp4");
i need a help.
thanks

lynxfg commented Jul 29, 2016

Very nice class !
But the url with http doesn't work :
$stream = new VideoStream(".http://www.exemple.com/video.mp4");
i need a help.
thanks

@inanbayram

This comment has been minimized.

Show comment
Hide comment
@inanbayram

inanbayram Sep 6, 2016

@ranacseruet It's possible to stream multiple video files in one php script?

$stream1 = new VideoStream($file1);
$stream1->start();

$stream2 = new VideoStream($file2);
$stream2->start();

If I try the following code, the script is just serving the first video file. Of course I removed the exit's in the class

inanbayram commented Sep 6, 2016

@ranacseruet It's possible to stream multiple video files in one php script?

$stream1 = new VideoStream($file1);
$stream1->start();

$stream2 = new VideoStream($file2);
$stream2->start();

If I try the following code, the script is just serving the first video file. Of course I removed the exit's in the class

@ImSeaWorld

This comment has been minimized.

Show comment
Hide comment
@ImSeaWorld

ImSeaWorld Oct 8, 2016

@annopnod Why are you making an extra variable?

Turn this:
$ext = strtolower(pathinfo($this->path, PATHINFO_EXTENSION)); switch ($ext) { etc etc

Into this:
switch (strtolower(pathinfo($this->path, PATHINFO_EXTENSION))) { etc etc

You are never going to use $ext again. No point in adding variables that are not going to be used.

EDIT: A better way you could do this, is use an array. Line 39 with the Content-Type header can be turned into a "one-liner".

I added on line 16, the video formats allowed for html(could be more than listed):
private $videoFormats = array("mp4"=>"video/mp4", "webm"=>"video/webm", "ogg"=>"video/ogg");

On line 39 or the Content-Type line I replaced-
This: header("Content-Type: video/mp4");
With this: header("Content-Type: ".($this->videoFormats[strtolower(pathinfo($this->path, PATHINFO_EXTENSION))]));

ImSeaWorld commented Oct 8, 2016

@annopnod Why are you making an extra variable?

Turn this:
$ext = strtolower(pathinfo($this->path, PATHINFO_EXTENSION)); switch ($ext) { etc etc

Into this:
switch (strtolower(pathinfo($this->path, PATHINFO_EXTENSION))) { etc etc

You are never going to use $ext again. No point in adding variables that are not going to be used.

EDIT: A better way you could do this, is use an array. Line 39 with the Content-Type header can be turned into a "one-liner".

I added on line 16, the video formats allowed for html(could be more than listed):
private $videoFormats = array("mp4"=>"video/mp4", "webm"=>"video/webm", "ogg"=>"video/ogg");

On line 39 or the Content-Type line I replaced-
This: header("Content-Type: video/mp4");
With this: header("Content-Type: ".($this->videoFormats[strtolower(pathinfo($this->path, PATHINFO_EXTENSION))]));

@wayneworkman

This comment has been minimized.

Show comment
Hide comment
@wayneworkman

wayneworkman Nov 29, 2016

I would like to use this code in a GNU GPLv3 licensed project. Can you please license this code as GNU GPLv3 ?

wayneworkman commented Nov 29, 2016

I would like to use this code in a GNU GPLv3 licensed project. Can you please license this code as GNU GPLv3 ?

@gkandemi

This comment has been minimized.

Show comment
Hide comment
@gkandemi

gkandemi Dec 28, 2016

Hello,

how to use this class in codeigniter? I tried but I didn't response.

Regards
Gokhan

gkandemi commented Dec 28, 2016

Hello,

how to use this class in codeigniter? I tried but I didn't response.

Regards
Gokhan

@mwichary

This comment has been minimized.

Show comment
Hide comment
@mwichary

mwichary Feb 13, 2017

This basically just saved my life. Thank you.

mwichary commented Feb 13, 2017

This basically just saved my life. Thank you.

@acoudray

This comment has been minimized.

Show comment
Hide comment
@acoudray

acoudray Feb 19, 2017

Hello,

This is a really awesome class you've done here. But I am asking myself if there is a way to find when the video ends streaming ? Thank you a lot.

acoudray commented Feb 19, 2017

Hello,

This is a really awesome class you've done here. But I am asking myself if there is a way to find when the video ends streaming ? Thank you a lot.

@lk77

This comment has been minimized.

Show comment
Hide comment
@lk77

lk77 Jul 6, 2017

Hello,

the seeking don't working on chrome.

I think you need to change line 48 :
header("Accept-Ranges: 0-".$this->end);
by
header("Accept-Ranges: bytes 0-".$this->end);

lk77 commented Jul 6, 2017

Hello,

the seeking don't working on chrome.

I think you need to change line 48 :
header("Accept-Ranges: 0-".$this->end);
by
header("Accept-Ranges: bytes 0-".$this->end);

@reszko

This comment has been minimized.

Show comment
Hide comment
@reszko

reszko Jul 6, 2017

Hi. Add this statement in stream method before while: session_write_close(). Without it, the browser freezes when a file is streaming when you make new requests, like click to go to another page for example. Before I use session_write_close(), a new request only worked in Firefox. In another browsers all new requests make browser keeps loading until the stream finishes.

reszko commented Jul 6, 2017

Hi. Add this statement in stream method before while: session_write_close(). Without it, the browser freezes when a file is streaming when you make new requests, like click to go to another page for example. Before I use session_write_close(), a new request only worked in Firefox. In another browsers all new requests make browser keeps loading until the stream finishes.

@petermajewskicom

This comment has been minimized.

Show comment
Hide comment
@petermajewskicom

petermajewskicom Sep 19, 2017

If you're using PHP5+

remove Line 76 (fseek($this->stream, $this->start);)

Line 109 change to $data = @stream_get_contents($this->stream, $bytesToRead, $i);

tested on Chrome, Safari and FF

petermajewskicom commented Sep 19, 2017

If you're using PHP5+

remove Line 76 (fseek($this->stream, $this->start);)

Line 109 change to $data = @stream_get_contents($this->stream, $bytesToRead, $i);

tested on Chrome, Safari and FF

@ConstantinFF

This comment has been minimized.

Show comment
Hide comment
@ConstantinFF

ConstantinFF Oct 6, 2017

Is there an option to stream multiple video files one after the other, like it is for audio files?

ConstantinFF commented Oct 6, 2017

Is there an option to stream multiple video files one after the other, like it is for audio files?

@AlexandrBabich

This comment has been minimized.

Show comment
Hide comment
@AlexandrBabich

AlexandrBabich Oct 13, 2017

Hello guys. I had same error in IE11. To solve this issue you need add header("Accept-Ranges: bytes"); after 83 string.
This header is important for IE browsers.

AlexandrBabich commented Oct 13, 2017

Hello guys. I had same error in IE11. To solve this issue you need add header("Accept-Ranges: bytes"); after 83 string.
This header is important for IE browsers.

@TRC4

This comment has been minimized.

Show comment
Hide comment
@TRC4

TRC4 Oct 21, 2017

@lynxfg insert this code on top

start();exit; // starting classes class VideoStream **php script has problem with real time ![1](https://user-images.githubusercontent.com/17369574/31848516-0059aab8-b635-11e7-9884-32cf692c2677.png) ![2](https://user-images.githubusercontent.com/17369574/31848517-00824c70-b635-11e7-8a77-0dc3cbed8458.png) **

TRC4 commented Oct 21, 2017

@lynxfg insert this code on top

start();exit; // starting classes class VideoStream **php script has problem with real time ![1](https://user-images.githubusercontent.com/17369574/31848516-0059aab8-b635-11e7-9884-32cf692c2677.png) ![2](https://user-images.githubusercontent.com/17369574/31848517-00824c70-b635-11e7-8a77-0dc3cbed8458.png) **
@toddpage

This comment has been minimized.

Show comment
Hide comment
@toddpage

toddpage Jan 30, 2018

It only plays 2 - 4 seconds of the video on both desktop and iphone. But seems to still download the file.

toddpage commented Jan 30, 2018

It only plays 2 - 4 seconds of the video on both desktop and iphone. But seems to still download the file.

@vikash2567

This comment has been minimized.

Show comment
Hide comment
@vikash2567

vikash2567 Feb 14, 2018

I have used the above code in my website. When i refresh the page again and again then video play from starting, this is not a video live streaming. How to implement the video live streaming please tell me?

vikash2567 commented Feb 14, 2018

I have used the above code in my website. When i refresh the page again and again then video play from starting, this is not a video live streaming. How to implement the video live streaming please tell me?

@glennricster

This comment has been minimized.

Show comment
Hide comment
@glennricster

glennricster Apr 18, 2018

I don't understand what the point of this is. Perhaps I am missing something. However, rather than using all this heavy machinery to display a video, why not just put the path to the video into the src of the html video element?

glennricster commented Apr 18, 2018

I don't understand what the point of this is. Perhaps I am missing something. However, rather than using all this heavy machinery to display a video, why not just put the path to the video into the src of the html video element?

@95B

This comment has been minimized.

Show comment
Hide comment
@95B

95B Apr 19, 2018

@ImSeaWorld
Couldn't you use this mime_content_type($this->path) to get the MIME content type header dynamically?

header("Content-Type: ".mime_content_type($this->path));

95B commented Apr 19, 2018

@ImSeaWorld
Couldn't you use this mime_content_type($this->path) to get the MIME content type header dynamically?

header("Content-Type: ".mime_content_type($this->path));

@yisaketsegaye

This comment has been minimized.

Show comment
Hide comment
@yisaketsegaye

yisaketsegaye Apr 21, 2018

Thank you that was so helpfull

yisaketsegaye commented Apr 21, 2018

Thank you that was so helpfull

@rahultrivedi20

This comment has been minimized.

Show comment
Hide comment
@rahultrivedi20

rahultrivedi20 Jun 4, 2018

My Video is 285 MB. I am trying to play using video streaming from aws server. It is playing directly on aws s3 server. But not able to play directly from video streaming.

rahultrivedi20 commented Jun 4, 2018

My Video is 285 MB. I am trying to play using video streaming from aws server. It is playing directly on aws s3 server. But not able to play directly from video streaming.

@irfanzsheikh

This comment has been minimized.

Show comment
Hide comment
@irfanzsheikh

irfanzsheikh Sep 13, 2018

Hello I want to load subtitles in it. how can I do it?
I used to load subtitles like this

could you please help me to how would I use this line in your code?

irfanzsheikh commented Sep 13, 2018

Hello I want to load subtitles in it. how can I do it?
I used to load subtitles like this

could you please help me to how would I use this line in your code?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment