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:
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
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:
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:
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 respectsAccept-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.
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.