PHP serve MP4 - Chrome “Provisional headers are sh

2019-01-19 14:22发布

问题:

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)

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)

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!

回答1:

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


回答2:

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 :)



回答3:

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.