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!

upvote
  flag
Are the files local? If yes - how do you serve your files? – divaka
2 upvote
  flag
What seems strange to me is Range header. In your case it is bytes=0- . The typical format of Content-Range header is this bytes 0-499/1234 which means first 500 bytes of a file with size of 1234 bytes. That's why I'm asking how do you serving your files, because you need to support resumable downloading/streaming of your files. Check this for more info: //allinonescript.com/a/4451376/1412896 – divaka
upvote
  flag
Docs for content range here: w3.org/Protocols/rfc2616/rfc2616-sec14.html – divaka
upvote
  flag
And another helpful answer for you explaining Chrome behaviour: //allinonescript.com/a/18745164/1412896 Sorry about throwing info at you, just trying to help :) – divaka
upvote
  flag
Range is sent by chrome to server as the first request for the video. Content-Range is header response from server? It doesn't solve my case, but thanks anyway :) – Keo Strife
upvote
  flag
Could you add the response from your server where you are serving your mp4 files? – divaka

3 Answers 11

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();
    }
}

Well i certainly is an intriguing problem. And is really hard to determine the root cause here. But if i was in your case i would check for two things.

  1. First i would make sure that keep-alive is set to off KeepAlive Off on your httpd.conf

  2. Afterwards test it with this configuration.

  3. Then i would disable all browser caching:

    header('Cache-Control: no-cache, no-store, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0');

  4. Final test after that.

The issue seems to be either with keep-alive or browser caching or even both, but i can not understand why it appears only in chrome. As a last resort make sure that you are not using any extensions that may create problems like Adblock.

Hope i get you going with these information :)

I had this same issue when trying to stream audio files. I ended up actually solving my problem by sheer coincidence.

I at some point created a script that would reduce the bitrate of the files to 128kbps using FFMPEG (works for videos too as far as i know). After that when inspecting in chrome I started to notice that all of my requests where completing after a couple of seconds, as opposed to staying incomplete indefinitely as before. Not sure if this viable for you not, but compressed the files solved this issue as well as increasing streaming speeds so I highly recommend this.

Not the answer you're looking for? Browse other questions tagged or ask your own question.