Manage video with PHP

2

I'm setting up a file management system, but I'm having trouble displaying the videos.

It has an interpreter and in some part I have an "ignorer".

$mime = mime_content_type($path);

if(in_array($mime, self::$ignore)){
    $len = filesize($path);

    header("Content-type: $mime");
    header("Content-Length: {$len}");
    readfile($path);
    exit();
}

The problem is with videos, which due to being dynamic do not play correctly.

Problem

  • When rendering the video in this way, I can not go forward in time unless the buffer has already been loaded.
  • If the video ends I can not go back to watch again.

Search

Doubt

  • How to manage videos through PHP?

Note

We're using equipment with different configurations, some running on Nginx and some running Apache.

In order to make it as compatible as possible with both, all requests are centralized and handled via PHP.

.htaccess

<IfModule mod_rewrite.c>
    RewriteEngine On

    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

Nginx

location / {
    try_files ~ ~ /index.php?url=$uri&$args;
}

So it is not possible to create rules of .htaccess or ngnix as:

RewriteCond %{REQUEST_URI} \.(webm|mp4)$
RewriteRule ^(.*)$ - [L]
    
asked by anonymous 21.09.2017 / 18:51

1 answer

1

By making a brief note in the script link there were some "problems", this script was probably set to .htaccess specific , ie it would have to be adjusted both need, same goes for Ngnix.

I assume this is due to the $_SERVER['PATH_INFO'] that is only generated by rewriting, except that with the experience that has line 4 it will not work ( SCRIPT_FILENAME ). Other than that the author put a set_time_limit(0); in the middle of the whille, which I think is redundancy. I can not say that this compromises the operation and I do not want to criticize anyone, I'm just stating that the script is made to work in a specific situation, I've re-scripted it to work with a simple GET (querystring):

<?php
set_time_limit(0); //Remove o limite

//Pega o mimetype
function mimeType($file)
{
    $mimetype = false;

    if (class_exists('finfo')) {//PHP5.4+
        $finfo     = finfo_open(FILEINFO_MIME_TYPE);
        $mimetype  = finfo_file($finfo, $file);
        finfo_close($finfo);
    } else if (function_exists('mime_content_type')) {//php5.3 ou inferiror
        $mimetype = mime_content_type($file);
    }

    return $mimetype;
}

if (empty($_GET['video'])) {
    die('Video não definido');
}

//Pega a URL do video
$file = 'videos/' . str_replace('../', '', $_GET['video']);

$mime = mimeType($file);

if (!$mime || strpos($mime, 'video/') !== 0) {
    die('Erro ao ler o video ou mime-type inválido');
}

$fp = fopen($file, 'rb');

$size   = filesize($file); // File size
$length = $size;           // Content length
$start  = 0;               // Start byte
$end    = $size - 1;       // End byte

header('Content-type: ' . $mime);
header('Accept-Ranges: 0-' . $length);

if (isset($_SERVER['HTTP_RANGE'])) {
    $c_start = $start;
    $c_end   = $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 ' . $start . '-' . $end . '/' . $size);
        exit;
    }

    if ($range == '-') {
        $c_start = $size - substr($range, 1);
    } else {
        $range  = explode('-', $range);
        $c_start = $range[0];
        $c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
    }

    $c_end = ($c_end > $end) ? $end : $c_end;

    if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
        exit;
    }

    $start  = $c_start;
    $end    = $c_end;
    $length = $end - $start + 1;
    fseek($fp, $start);
    header('HTTP/1.1 206 Partial Content');
}

header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
header('Content-Length: ' . $length);

$buffer = 1024 * 8;
while (!feof($fp) && ($p = ftell($fp)) <= $end) {

    if ($p + $buffer > $end) {
        $buffer = $end - $p + 1;
    }

    echo fread($fp, $buffer);
    flush();
}

fclose($fp);

If the video address is based on a bank ID, you would need to adjust:

//Exemplo teórico, pode adaptar pro seu ORM ou mysqli, é apenas para esclarecer a lógica
$consulta = $pdo->prepare('SELECT filename FROM videos WHERE id = :id');

//Pega o ID pela QUERYSTRING
$consulta->bindValue(':id', $_GET['videoid'], PDO::PARAM_INT);

$consulta->execute();
$linha = $consulta->fetch(PDO::FETCH_ASSOC);

//Pega a URL do video
$file = 'videos/' . $linha['filename'];
  

Warning: I'm looking at the docs and a few things about HTTP to better understand the range, I'll fix any shortcomings in the script soon

Tests

In the chrome (opera) I bounded to 3G Fast (simulation), the HTML looked like this:

<video src="range.php?video=1.mp4" controls preload="none"></video><hr>
<video src="range.php?video=2.mp4" controls preload="none"></video><hr>
<video src="range.php?video=3.mp4" controls preload="none"></video><hr>

And in the console:

Igaveplayinthefirstvideoandthendraggedthecontrolforwardandresultedin:

I'mjustnotsurewhyIgeneratedmultiplerequests,butIthinkitmightbethatthescriptisstillnotperfectordownloadingthevideoafter"seek" depends on the machine and / or browser's CODECs of the internet).

Image of "seekbar":

    
21.09.2017 / 19:52