skip to Main Content

With help from these links, I have managed to stream a MP4 video from a private S3 bucket using PHP:

Streaming private videos from Amazon S3

https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-stream-wrapper.html

This is the code that does the streaming from S3:

$path = 's3://' . $bucket . '/' . $fullFileName;

$this->s3Client->registerStreamWrapper();

if ($stream = fopen('s3://' . $bucket . '/' . $fullFileName, 'r')) {

    while (!feof($stream)) {
        echo fread($stream, 1024); 
    } 

    fclose($stream); 
} //if

And I am using these headers which are required to allow the user to jump around in the video:

header("Content-Type: video/mp4");
header('Accept-Ranges: bytes');
header("Content-Disposition: inline;");
header("Content-Transfer-Encoding: binaryn");
header('Connection: close');

This all works very nicely – but once I start the video playing and then click on a link (a good old fashioned HTML hyperlink) the video just keeps playing for what seems like a random amount of time.

This image shows the Chrome tab with the video continuing to play whilst a new request is pending:

video continuing to play whilst a new request is pending

This is my HTML5 video tag (edited for simplicity):

<video width="100%"  controls preload="metadata" controlsList="nodownload">
    <source src="/view-resource/1234" type="video/mp4">
</video>

I suspect that the PHP streaming function is just running on until it gets to the end of the file. I tried interrupting it using connection_aborted():

while (!feof($stream) && !connection_aborted()) {
    echo fread($stream, 1024); 
} //while

And tried setting: ignore_user_abort(false);

Any other suggestions?

2

Answers


  1. Chosen as BEST ANSWER

    After a bit of tinkering and thanks to @Sammitch pointing out what was going on with the sessions, I have got it working exactly the way I wanted. Here is the "final" code:

    session_write_close();
    
    $this->s3Client->registerStreamWrapper();
    
        
    ignore_user_abort(false);
    
    ob_end_clean();
    
    if ($stream = fopen('s3://' . $bucket . '/' . $fullFileName, 'r')) {
    
        while (!feof($stream) && !connection_aborted()) {
            echo fread($stream, 1024); 
            flush();
        } //while
    
        fclose($stream); 
        ob_start();
    
    } //if
    

    The Accept-Ranges header allows the user to jump around in the video. Even though I did follow the instructions to make s3 stream seekable at one point it doesn't seem necessary here. Perhaps someone can comment on why.

    Here are the final headers:

    header("Content-Type: video/mp4");
    header('Accept-Ranges: bytes');
    header("Content-Disposition: inline;");
    header("Content-Transfer-Encoding: binaryn");
    header('Connection: close');
    

    1. While your code "streams" in the sense that it’s not storing anything, it’s also not "streaming seekably" in the sense of respecting Range: headers, despite advertising that it respects Accept-Ranges. It always reads from the start of the file to the end of the file, and the player might be confused by this. It might just be playing whatever it happened to have received up until the request was cancelled.

      If you’re not respecting range requests, then omit the Accept-Ranges header.

      If you do want to support Range requests, then you’ll need more code about it, as well as to open the S3 resource as a seekable stream.

    2. If you start a session, then perform a long-running task, eg: streaming data, any subsequent request will have to wait for that first request to finish and release the session. Call session_write_close() before a long running task to allow other requests to be handled in parallel.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search