-1

guys so I have a problem consistently and reliably seeking videos with HTML5. I am getting the videos from an AWS S3 Bucket using Nodejs all videos are in mp4 format. I have tried multiple things to get the video's current time to move every time (most of the time it works but occasionally it doesn't move) but to no avail. Heres my code:

router.get("/*", (req, res, next) => {
    let params = {
      Bucket: "bucketName",
      Key: decodeURIComponent(req.path.substring(1))
    };

    s3.getObject(params, function(err, data) {
      if (err) {
        res.status(500).send(err);
      } else {
        res.contentType(data.ContentType);
        res.send(data.Body);
      }
    });
  });
}

I've been doing some reading and people are saying you can use byte-range requests and request the whole video through a byte-range request. This guy seems to do it with a local file but I am at a loss about how to do it with an s3 file. See post: can't seek html5 video or audio in chrome. The other suggestion I've heard of people doing is HLS encoding but I am not sure what is the best way or how to implement them can someone point me in the right direction?

  • Perhaps consider simply sending the browser client an S3 pre-signed URL and let the browser and S3 work this out. Instead of you proxying the entire download from S3. – jarmod Jan 19 '21 at 20:09
  • @jarmod would this fix the seeking problem? – Braiden Parkinson Jan 19 '21 at 21:55
  • You'd have to try it. I don't know what your client would do. You can also research HLS or DASH options for video streaming, typically via CloudFront ([example](https://stackoverflow.com/questions/62190517/authorising-hls-streaming-files-from-amazon-s3-directory)). – jarmod Jan 20 '21 at 01:02
  • Let me get this straight. You downvoted my post because you seem to think signed URLs will work it out. (which by the way signed URLs are for security purposes not resolving byte-range requests. Please prove me wrong) and you don't even know if it will work? – Braiden Parkinson Jan 21 '21 at 06:35
  • You’re not providing *any* support here for ranged GETs. If you want to support them, because that is what an HTML5 compliant browser will send, then you have to support those on your back-end. S3 does support ranged GETs, but your code makes no attempt to do that. The reason I proposed the signed URL approach is because it’s analogous to what you’re doing presently (just dumping the entire file to the client) and you claim that it works most of the time - hence my proposal, which should work at least as well, and probably better, because it does the same thing but in a better way. – jarmod Jan 21 '21 at 12:58
  • By the way, yes, S3 pre-signed URLs support ranged GETs. Also, I tested an S3 pre-signed URL for an MP4 in a simple web page with HTML5 video and it worked fine in Chrome (I could seek at will and didn't see any errors). Note: the earlier comments are exactly that - comments. They were not intended to be a definitive answer (I would have supplied an answer, if so). They were designed to help move you forward. Hope this helps. – jarmod Jan 21 '21 at 14:48
  • @jarmod, can you show me the simple website with the example? I apologize for my frustration. It just seemed as though you were downvoting my post off of what appeared to me to be a guess. – Braiden Parkinson Jan 21 '21 at 15:18
  • I'll add it as an answer shortly, because there's limited space in comments. If it's not helpful then I can delete it later. – jarmod Jan 21 '21 at 15:37

1 Answers1

1

I think the best answer is probably to implement an HLS or DASH streaming solution. Here is an example of HLS with S3 and CloudFront. And here is a more comprehensive Best Practices for Streaming Media Delivery.

Right now, your app server is simply reading the entire video file from S3 and then sending the entire video file contents directly to the client in an HTTP response. While it appears to work, it might be better to avoid proxying this content, and instead serve it directly to the client from S3 (or CloudFront). One way to do that, for private content, is to send the client an S3 pre-signed URL.

I tested a simple HTML5 video web page against an S3-hosted MPEG video file and was able to view it fine on Chrome, as well as seek back and forth at will. I tested with a relatively small MPEG (15MB).

<html>
<body>
    <h1>Stack Overflow 65796272 Video Sample</h1>
    <p>This video and associated poster are sourced from Amazon S3 via pre-signed URL.</p>
    <div id="container">
      <video id='video' controls="controls" preload='none' width="600" poster="https://poster-presigned-url-here">
        <source id='mp4' src="https://video-presigned-url-here" type='video/mp4' />
        <p>Your user agent does not support the HTML5 video element.</p>
      </video>
    </div>
</body>
</html>

I pre-created the poster and video pre-signed URLs using the awscli, but you can do this using an AWS SDK and serve them dynamically to your client (or inject them into the HTML sent to the client using any standard template engine such as Express.js). You can remove the poster, if not needed. Note that pre-signed URLs are time-limited.

jarmod
  • 46,751
  • 9
  • 81
  • 86