Skip to content

Instantly share code, notes, and snippets.

@ranacseruet
Last active October 30, 2024 12:43
Show Gist options
  • Save ranacseruet/9826293 to your computer and use it in GitHub Desktop.
Save ranacseruet/9826293 to your computer and use it in GitHub Desktop.
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();
}
}
@carcinocron
Copy link

carcinocron commented Sep 22, 2020

I am using the <video> tag (only test chrome so far) and my videos keep getting stuck "buffering" randomly starting around 40s up to 3 minutes. My sample video isn't much longer. I've never been able to finish watching a video. Manual seeking works (in general and when stuck buffering) but if I try to watch it straight through it always gets stuck buffering.

@rorenzo
Copy link

rorenzo commented Dec 15, 2020

hi, I'm using your code, I made just a little mod. It work very well on windows (all browser), but in MACOS (all browser) the video does not play. any idea? thanks

@lizardpeter
Copy link

Is there a way to use this and prevent users from downloading the video? The video can be downloaded by just right clicking after the entire video has been played. I am looking for a way to prevent it from being downloaded (like streaming services use).

@korosh-abbasy
Copy link

thank you so much
the things I did

add the first line in part setHeader()
session_write_close();


line 110
print( $data ) ;


@marlon33
Copy link

Has anyone managed to block downloads?
I made this code, but it only works in chrome and Opera, firefox does not work, anyone to help?

if(isset($_SERVER["HTTP_REFERER"]) && $_SERVER['HTTP_SEC_FETCH_SITE'] == 'same-origin'){
  $filePath ="videos/depoimentos/".$filePath.".mp4";
  if(file_exists($filePath)){
    $stream = new VideoStream($filePath);
    $stream->start();
  }else{
    echo "Error!";
  }
}

@mathe42
Copy link

mathe42 commented Jul 28, 2021

Is there some simple changes to use it with http2=http/2=h2 ?

I changed for example

header('HTTP/1.1 416 Requested Range Not Satisfiable');

to

header('', false, 416);

So no HTTP/1.1 in my code...

The class always responsed with a 416...

@mathe42
Copy link

mathe42 commented Jul 28, 2021

Never mind now it works maybe some strange caching...

@rayj00
Copy link

rayj00 commented Oct 13, 2021

I have an existing Webrtc one2many broadcasting application working. I also have a php based social website that is working. I need to integrate the Webrtc into the social website. Any recommendations how to go about implementing this?

@David263
Copy link

Doesn't work for me when I use the default video player of the browser. In Firefox, it won't change position. In Chrome, it won't enable the player. Your published example, http://demo.codesamplez.com/html5/video, seems to work everywhere because the player doesn't use the library code. It has the video path specified explicitly.

@marlon33
Copy link

Whta are you try to do??

the code work for me

@David263
Copy link

David263 commented Jul 15, 2022

I gave up on this library and experimented and created my own code that works. I have no idea what the problem was. Thanks anyway. Maybe I didn't understand exactly how and where to use this code.

@marlon33
Copy link

It's awesome, can you share your code??

@David263
Copy link

David263 commented Jul 15, 2022

It's still under development. It works great, but fails on Safari/MacOS. I am having great difficulty figuring out how to debug it on Safari, which seems to not have Dev Tools. It is an app that asks the user for a password, then provides a list of video files as buttons. Clicking a button plays the file, with repositioning, using the default browser video player. The entire app is a bit lengthy for simply posting, but eventually I can release it as a repository.

@marlon33
Copy link

Understand, if you need some help, just put here

@bondan20
Copy link

bondan20 commented Sep 1, 2022

for mp4 work sir
how to play m3u8 Sir?

@marlon33
Copy link

marlon33 commented Sep 2, 2022

for mp4 work sir how to play m3u8 Sir?

It's note possible, becouse the file m3u8 is a stream file

@bondan20
Copy link

bondan20 commented Sep 8, 2022

for mp4 work sir how to play m3u8 Sir?

It's note possible, becouse the file m3u8 is a stream file

not work sir,
please create streaming for m3u8 sir :D heheheh

@himadrisaha89
Copy link

Working fine for me for small size video file but for big size file I am getting net::ERR_CONTENT_LENGTH_MISMATCH 206 (Partial Content) in chrome console and of course the status is failed in chrome network tab.
Any Help !!!

@David263
Copy link

David263 commented Sep 8, 2022

himadrisaha89, this usually means that the software that is sending the streaming file is either calculating or formatting the size information that it sends to the client player incorrectly. If you look at the network information in a browser developer tool, you might be able to see the problem.

@michalcz17
Copy link

Is it possible for the controls not to be displayed on that page? Or at least not be able to download the video? or do you need a script for that?

@micilini
Copy link

micilini commented Nov 16, 2022

This code need UPDATE, he cannot read EXTERNAL URL VIDEO, only local videos, for solve this problem I create a new package that works with laravel, check it out: https://github.com/micilini/video-stream.

My Package can handle local and external video files 👯

@WordpressArtist
Copy link

I got an error please help

filesize(): stat failed for http://laraveltest.test/ElephantsDream.mp4

got error here. from class VideoStream
$this->size = filesize($this->path);

where code

$a = 'http://laraveltest.test/ElephantsDream.mp4'; // public folder video
$stream = new VideoStream($a);
return response()->stream(function() use ($stream) {
$stream->start();
});

Any help please using laravel 9

@carcinocron
Copy link

trying to access a file using http:// does not look valid to me, probably you want something like one of these:

$a = storage_path('ElephantsDream.mp4');
$a = public_path('ElephantsDream.mp4');
$a = asset('ElephantsDream.mp4');

depending on where the file is physically in your laravel directory.

@WordpressArtist
Copy link

@carcinocron thanks so external url not supported right ?

@jackbaron
Copy link

Is there a way to do this same process but with AJAX?

@vincentckk
Copy link

Here is my code. For sure require to check URL and File. Just assume file path / URL is correct.
// Check starting string
if (substr($this->path, 0, 3) == 'htt' || substr($this->path, 0, 3) == 'ftp') { /* External URL - allow_url_fopen=On /
$headers = get_headers($this->path, 1);
$this->size = $headers["Content-Length"];
}else{ /
Internal file */
$this->size = filesize($this->path);
}
:
//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);
// Use stream_get_contents instead of fseek
echo stream_get_contents($this->stream, -1, $this->start);

@MohammedTarekAbd
Copy link

Hello i need a help
what i get is only the video.mp4 that I moved to the root server

@kicktv
Copy link

kicktv commented Oct 29, 2024

The script worked for me, but forward and backward movement of the video is not supported.

This script is better supports Video of forward and backward movement :
https://gist.github.com/kicktv/4239c499101494538d384ce7995bb1af

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment