26

I have a problem with the implementation of videos in javascript and PHP.

index.php

session_start()

// do other stuff
include ‘video.php’

video.php

<?php
If(!$_REQUEST[‘play’]){
    // displaying video.html
}
else
{
    // play video
$fp = @fopen($file, 'rb');

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

// Now that we've gotten so far without errors we send the accept range header

/* At the moment we only support single ranges.
 * Multiple ranges requires some more work to ensure it works correctly
 * and comply with the specifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
 *
 * Multirange support announces itself with:
 * header('Accept-Ranges: bytes');
 *
 * Multirange content must be sent with multipart/byteranges media type,
 * (mediatype = mimetype)
 * as well as a boundary header to indicate the various chunks of data.
 */
 
header('Content-type: video/mp4');
header('Accept-Ranges: bytes');
// multipart/byteranges
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
if (isset($_SERVER['HTTP_RANGE'])){
    $c_start = $start;
    $c_end   = $end;
    // Extract the range string
    list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
    // Make sure the client hasn't sent us a multibyte range
    if (strpos($range, ',') !== false){
        // (?) Shoud this be issued here, or should the first
        // range be used? Or should the header be ignored and
        // we output the whole content?
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header("Content-Range: bytes $start-$end/$size");
        // (?) Echo some info to the client?
        exit;
    } // fim do if

    // If the range starts with an '-' we start from the beginning
    // If not, we forward the file pointer
    // And make sure to get the end byte if spesified
    if ($range{0} == '-'){
        // The n-number of the last bytes is requested
        $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;
    } // fim do if
    /* Check the range and make sure it's treated according to the specs.
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
     */
    // End bytes can not be larger than $end.
    $c_end = ($c_end > $end) ? $end : $c_end;
    // Validate the requested range and return an error if it's not correct.
    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");
        // (?) Echo some info to the client?
        exit;
    } // fim do if

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

// Notify the client the byte range we'll be outputting
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
// Start buffered download
$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end){
    if ($p + $buffer > $end){
        // In case we're only outputtin a chunk, make sure we don't
        // read past the length
        $buffer = $end - $p + 1;
    } // fim do if
    set_time_limit(0); // Reset time limit for big files
    echo fread($fp, $buffer);
    flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
 } // fim do while
 fclose($fp);
 exit();
}

video.html

<!doctype html>
<html lang="fr-FR">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <video controls>
            <source src="index.php?format=mp4&play=1&id=someid type="video/mp4" />
        </video>
    </body>
</html>

Everything works fine but for some reason I don't know, when I start playing a video and then click on a link on my page or use the browser's back button, the browser freezes and waits a while ( variable ) before following the link.

The profiler indicates that the requested page change is in pending status.

If I position the video cursor further away, the browser unlocks instantly and follows the link I clicked.

And finally, if I reload that page, the issue doesn't appear anymore. Seems that chrome waits for something but it's not cached since my cache is disabled.

This occurs only with chrome desktop, all other browsers do the job correctly.

** EDIT **

I found the problem's origin.

To play my video I go through my main file index.php which serves as my router in which I initialize the session with session_start().

When the video is playing and I click on a link or the back button of my browser, I go back to index.php. This is when chrome gets stuck during the session_start()

It looks like there's a conflict with the sessions as long as the video is playing.

akio
  • 556
  • 4
  • 18
  • 5
    Have a look at https://ma.ttias.be/php-session-locking-prevent-sessions-blocking-in-requests/ – Nico Haase Nov 14 '18 at 16:00
  • Great explanations. But where am I supposed to add the session_write_close ? Into video.php ? To unlock session after sending my video chunk ? – akio Nov 14 '18 at 16:40
  • 1
    Unlock it at soon as possible - put heavy loaded stuff behind closing it – Nico Haase Nov 14 '18 at 16:43
  • Do you have any ideas why this occurs only with chrome ? – akio Nov 14 '18 at 18:08
  • Thank you `akio` for the question and `Niko Haase` for the link. I think the solution with session_write_close should be described as a standard answer authored by @NicoHaase. It is for sure +1 answer :) – lubart Jun 29 '20 at 08:41

2 Answers2

1

If you're not writing anything in the session then use session_write_close() juse after the session_start() or if you writing some session variable then after that..

when we start session it will open session file to read. Read is only closed after the script end. In meantime if you're going to open new page then it will stuck.. So use this function to prevent it.

https://www.php.net/manual/en/function.session-write-close.php

session_start();
session_write_close();

// do other stuff
include ‘video.php’

You can read the session variable after the session_write_close().. there is no issue in that.. but if you're writing something after it.. it will not write that value in the session variable.

Ashok Gadri
  • 495
  • 4
  • 10
1

To prevent attempting to restart a session while an existing session is open, use session_status:

if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

Even if you use session_write_close, it's still good practice to check for an existing session.

That way, if you are including multiple files that each try to start a session, they won't step on each other.

Tullio
  • 94
  • 4