Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP serve MP4 - Chrome "Provisional headers are shown/request is not finished yet" bug

I want to check for users' subscription before allow them to see the video, for this reason I use PHP to interact with Stripe to check for user's subscription, and than used PHP script to serve MP4 to browser

It works fine the first time a video is played in Google Chrome (Using HTML5 player)... But when I close the video and play it again, the video doesn't play anymore... I can NOT also reload the current page. It's like server stops working.

When I inspect the 1st video request (the one which played), in the Timing tab i see: "CAUTION: request is not finished yet!" (screenshot below)

CAUTION: request is not finished yet!

When I inspect the 2nd video request (the one did not play), in Headers tab it says "[caution sign] Provisional headers are shown" (screenshot below)

enter image description here

everything worked as expected in Safari or Firefox

Anyone has any idea what is going on? The only way for the video to play again is to close the current tab, enter the site again. Reloading doesn't work!

like image 492
Keo Strife Avatar asked Sep 22 '14 13:09

Keo Strife


1 Answers

I suggest you use the following function instead of your current 'streaming script'. If you pass $filename_output, it will serve the file as download, and it will stream otherwise!

It should work in every browser.

serveFile('/where/my/vid.mp4');

public function serveFile($filename, $filename_output = false, $mime = 'application/octet-stream')
{
    $buffer_size = 8192;
    $expiry = 90; //days
    if(!file_exists($filename))
    {
        throw new Exception('File not found: ' . $filename);
    }
    if(!is_readable($filename))
    {
        throw new Exception('File not readable: ' . $filename);
    }

    header_remove('Cache-Control');
    header_remove('Pragma');

    $byte_offset = 0;
    $filesize_bytes = $filesize_original = filesize($filename);

    header('Accept-Ranges: bytes', true);
    header('Content-Type: ' . $mime, true);

    if($filename_output)
    {
        header('Content-Disposition: attachment; filename="' . $filename_output . '"');
    }

    // Content-Range header for byte offsets
    if (isset($_SERVER['HTTP_RANGE']) && preg_match('%bytes=(\d+)-(\d+)?%i', $_SERVER['HTTP_RANGE'], $match))
    {
        $byte_offset = (int) $match[1];//Offset signifies where we should begin to read the file            
        if (isset($match[2]))//Length is for how long we should read the file according to the browser, and can never go beyond the file size
        {
            $filesize_bytes = min((int) $match[2], $filesize_bytes - $byte_offset);
        }
        header("HTTP/1.1 206 Partial content");
        header(sprintf('Content-Range: bytes %d-%d/%d', $byte_offset, $filesize_bytes - 1, $filesize_original)); ### Decrease by 1 on byte-length since this definition is zero-based index of bytes being sent
    }

    $byte_range = $filesize_bytes - $byte_offset;

    header('Content-Length: ' . $byte_range);
    header('Expires: ' . date('D, d M Y H:i:s', time() + 60 * 60 * 24 * $expiry) . ' GMT');

    $buffer = '';
    $bytes_remaining = $byte_range;

    $handle = fopen($filename, 'r');
    if(!$handle)
    {
        throw new Exception("Could not get handle for file: " .  $filename);
    }
    if (fseek($handle, $byte_offset, SEEK_SET) == -1)
    {
        throw new Exception("Could not seek to byte offset %d", $byte_offset);
    }

    while ($bytes_remaining > 0)
    {
        $chunksize_requested = min($buffer_size, $bytes_remaining);
        $buffer = fread($handle, $chunksize_requested);
        $chunksize_real = strlen($buffer);
        if ($chunksize_real == 0)
        {
            break;
        }
        $bytes_remaining -= $chunksize_real;
        echo $buffer;
        flush();
    }
}
like image 115
twicejr Avatar answered Oct 21 '22 14:10

twicejr