1

I have reviewed several similar codes, I have tried them all, but they all work when the multimedia file is hosted locally, when the multimedia file is in a url it does not work, it does not play the video.

They could suggest what changes should be used for this script to work on both url and local media files.

    //$path = 'video.mp4';
    $path = 'https://ia601407.us.archive.org/5/items/205_20210726/205.mp4';
    $file = $path;

    $fp = @fopen($file, 'rb');
    $size = filesize($file);
    $length = $size;
    $start = 0;
    $end = $size - 1;

    header('Content-type: video/mp4');
    header("Accept-Ranges: bytes");

    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[0] == '-'){
            $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;
        }
        set_time_limit(0);

        ob_clean();

        echo fread($fp, $buffer);
        flush();
    }
    fclose($fp);
    exit();
  • `filesize` will not work for urls, you need to find another way to determine the size or remove `($p = ftell($fp)) <= $end` and just check for `feof` – apokryfos Jul 26 '21 at 04:48
  • @apokryfos I see, then the script would be slower being remote or not? –  Jul 26 '21 at 04:52
  • Yes it will. Remote streams are not seekable using file handlers so you need to read the entire file up to the requested point every time. You can instead create another function that forwards the range request to the remote server and serves the response (like a proxy) assuming the remote server supports range requests. Alternatively you can cache the file locally on the first request for it and then use this code on the locally cached file – apokryfos Jul 26 '21 at 04:54
  • @apokryfos I already tried to make that change, but the script takes a long time to execute, it seems to be an infinite execution. –  Jul 26 '21 at 04:56
  • @apokryfos I had come across three questions about caching:: https://stackoverflow.com/questions/39648687/php-first-fseek-in-stream-then-fread https://stackoverflow.com/questions/5458674/how-to-read-a-video-file-placed-at-server-in-php-video-files-name-includes-ht https://stackoverflow.com/questions/4986335/how-to-rewind-an-http-stream-file-in-php-other-than-fclose-and-fopen-again But they do not give many details on how to use it and, in the test attempts without knowing how to do it, there is also that delay in the execution of the script and, –  Jul 26 '21 at 04:59
  • @apokryfos it seems to be infinite so it does not print any response. –  Jul 26 '21 at 04:59

1 Answers1

0

So here's a basic solution to cache a remote file locally so you can re-use your code:

    if (substr($path, 0, 6) === 'http://' || substr($path, 0, 7) === 'https://') {
       $file = md5($path);
       if (!file_exists($file)) {
           $urlStream = fopen($path, 'r');
           $dest = fopen($file, 'w');
           stream_copy_to_stream($urlStream, $dest);  
           fclose($urlStream); 
           fclose($dest);
       }
    } else {   
        $file = $path;
    }
    $fp = @fopen($file, 'rb');
    $size = filesize($file);
    $length = $size;
    $start = 0;
    $end = $size - 1;

    header('Content-type: video/mp4');
    header("Accept-Ranges: bytes");

    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[0] == '-'){
            $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;
        }
        set_time_limit(0);

        ob_clean();

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

This will create a file based on the accessed URL. You might want to modify the path you store these files in but this is the general idea. The advantage is you will only need to cache the file once, however you may need to use some process to occasionally clean up the cache (like a scheduled task or something like that)

apokryfos
  • 38,771
  • 9
  • 70
  • 114
  • It works but it does not allow to go ahead or go back of the video. –  Jul 26 '21 at 17:20
  • I'm going to open another question, what I had mentioned is already solved, I already think that the other thing already comes out of the question, thank you. –  Jul 26 '21 at 17:36