开发者

Video "streaming" for mobile devices with Amazon CloudFront

开发者 https://www.devze.com 2023-04-03 11:01 出处:网络
I maintain a video app for a client and we\'ve just been throug开发者_Go百科h the process of porting the desktop site to Amazon S3 + CloudFront through the streaming distribution they offer. To be cle

I maintain a video app for a client and we've just been throug开发者_Go百科h the process of porting the desktop site to Amazon S3 + CloudFront through the streaming distribution they offer. To be clear, I'm not talking about live streaming (what most of my Google's pick up) and I'm not talking about Flash streaming.

We have a mobile site that currently uses Influxis. It's not hugely expensive but we'd like to consolidate on Amazon given that they should be more stable than Influxis has been over the past few months.

I'll admit that while I know quite a bit about Flash streaming, I'm not 100% up to speed on HTML video. Is it just a direct download or can it do variable bitrates? Ideally we'd like it to be able to scale down for people on poor 3G connections.

Even if it's "possible", does Amazon support bandwidth scaling through its download distribution? As I understand it, its streaming distribution is only good for Flash/RTMP.

If anybody has any recommendations for good mobile streaming, I'll welcome them too.


Very old question, but I posted my solution to a similar problem on a similar post, so I am sharing it here as well Stream AWS S3 HLS Videos in iOS Browsers

---------------UPDATE BELOW--------------- (and grab a comfy seat)

Delivering protected video from S3 via Cloudfront using secure cookies (for iOS based browsers + all Safari) and secure urls for Chrome and everything else.

website architecture:

  • Frontend: ReactJS
  • Backend: NodeJS
  • cloud service architecture: https://aws.amazon.com/blogs/media/creating-a-secure-video-on-demand-vod-platform-using-aws/ (and attached lab guide)

Presumptions: equivalent setup to above cloud architecture, specifically the IAM configuration for CF to S3 bucket, and the related S3 security configurations for IAM and CORS.

TL/DR:

NON-SAFARI aka Chrome etc - use secure urls (VERY easy OOTB); the above guide worked for chrome, but not for safari.

Safari requires secure cookies for streaming hls natively, and won’t let recognize xhr.beforeRequest overloads at all. SAFARI / iOS BROWSERS BASED ON SAFARI - use secure cookies Everything below, explains this.

Setting cookies, is easy enough sounding! Its probably why there is no end to end example anywhere in AWS CloudFront, AWS Forums, or AWSDeveloper Slack channel, that its presumed to be easy because, hey its just cookies right?

Right. END TL/DR

Solution Details

The ‘AH-HA!’ moment was finally understanding that for this to work, you need to be able to set a cookie for a cloudfront server, from your own server, which is basically an enormous web security no-no. aka - ‘domains need to be the same, all the way down/up the network call’

comments here https://jwplayer-support-archive.netlify.app/questions/16356614-signed-cookies-on-cloudfront-with-hls-and-dash

and here link https://www.spacevatican.org/2015/5/1/using-cloudfront-signed-cookies/

both combined with original AWS documentation about signed cookies with a cname of a domain to apply to subdomains, all combined for me finally.

The solution is:

  1. setup a CNAME for your cloudfront instance; ie: you can’t set a cookie against 5j1h24j1j.cloudfront.net as you don’t own it, but you can CNAME something like cloudfront.<your-domain>.com in your DNS. good docs exists for this particular step at https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html
  2. Important - you need to also setup a reference for this CNAME in your CF Distribution. (if you want SSL, you need to re-sign your domain cert with cloudfront.<your-domain>.com, then upload this cert to AWS Certificate Manager, so it will be referencable in the CF Distribution edit screen drop down list (which, you can’t save unless you select something).
  3. for your local development box, set up a hosts file overload for whatever the NodeJS listening/bound IP is. ie: if your node is bound to 0.0.0.0, then edit your /etc/hosts to have a line 0.0.0.0 dev.<your-domain>.com - when you deploy to your production host, the domain will obviously work there.
  4. Now, in your backend (aka server side) code, where you will set the cookies, you need to set the domain parameter, and you can’t directly wildcard but you can leave it as <your-domain.com>(which in a browser, if you inspect using developer tools, you will see listed as .<your-domain>.com NOTE THE LEADING DOT. This is fine, and expected behaviour for a modern browser; essentially saying ‘any subdomain of <your-domain>.com will have these cookies accessible for.

What the above does, is make sure that END TO END, your are able to send the cookie, assigned to the .<your-domain>.com from a call starting in dev.<your-domain>.com or your future production <your-domain>.com through to the same uri but on a different port for your backend, then on to CF via your CNAME which is a subdomain the cookie can see now. At this point, its up to CF to pass on the required headers to the S3 instance.

But wait, there is more to do client side first. A thing that blocked me even seeing the cookies in the first place, was the fact they don’t get set unless the requestor/initiator uses a ‘withCredentials: true’ flag in the network call that starts it. In my code, that is a ReactJS componentDidMount() based Axios network REST GET call to my backend nodeJS endpoint for the video list (which the nodeJS gets from graphQL in AWS, but thats not needed for this explanation of my fix).

componentDidMount() {
        axios.get('http://dev.<your-domain>.com:3000/api/my-data-endpoint'
        ,{
          withCredentials: true,
        })
         .then(vidData => {
          this.setState({
            ....//set stuff  for player component include to use
          });

        })
    }

When my axios call did not have ‘withCredentials: true’, the cookies were never sent back; as soon as i had that? my cookies were at least sent back to the first caller, localhost (with no domain parameter in the cookie, it defaults to calling, which i had as local host at the time), which therefore meant it would never pass it to CF, which was the 2435h23l4jjfsj.cloudfront.net name at that point.

So, updating axios to use dev.<your-domain>.com for server access, and the withCredentials flag, my cookies were set, on the call to my backend info about the videos. As AWS documentation does point out, the cookies need to be fully set BEFORE the call for secure content, so this is accomplished.

In the above described call to my api, i get back something like

{src:’https://cloudfront.<your-domain>.com/path-to-secure-register-m3u8-file’, qps:’?policy=x&signature=y&key-pair-id=z’, blah blah}

[sidebar - signed urls are all generated in the cloud by a lambda] For Chrome, the player code will append the two together, then Wherever you instantiate your video.js player, overload the videojs.Hls.xhr.beforeRequest as follows

videojs.Hls.xhr.beforeRequest = function (options) {
  options.uri = `${options.uri}${videojs.getAllPlayers()[0].options().token}`;
  return options;
 };

which puts the query string of ?policy=x&signature=y&Key-Pair-ID=z on the end of every sub-file in the stream after the register m3u8 file kicks it off.

the backend call to the api described above, also tears apart the QP’s to set the cookies before the json is sent as a response, as follows

res.cookie("CloudFront-Key-Pair-Id", keypair, {httpOnly: true, path: "/", domain: ‘<your-domain>.com'});
res.cookie("CloudFront-Signature", sig, {httpOnly: true, path: "/", domain: ‘<your-domain>.com'});
res.cookie("CloudFront-Policy", poli, {httpOnly: true, path: "/", domain: ‘<your-domain>.com'});

INTERRUPT - now we have set withCredentials to true, you probably see CORS issues; fun. in your server side code (my reactJS) i set a few headers in my nodejs router

res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Origin", "http://dev.<your-domain>.com:8080"); // will be set to just <your-domain>.com for production

At this point, stuff still wasn’t working though. This is because the cloud code was putting the CF 234hgjghg.cloudfront.net domain into the policy, and not my CNAME mapping. I updated this in the cloud. So now my calls for video data, returned urls to the secure m3u8 using cloudfront.<your-domain>.com and not the cloudfront.net which is described here https://forums.aws.amazon.com/thread.jspa?messageID=610961&#610961 in the last response step 3.

At THIS point, if i used safari debug tools, I knew i was close, because my responses to attempted streaming changed from the no key or cookie xml, to

<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>

error, and in it, was a reference to my S3 bucket. This meant to me, that my CF distribution was essentially happy with the cookie based policy, key-id, and signature, and had passed me on to S3, but S3 told me to get lost.

The good thing at this point though, was that the 3 required cloudfront cookies were set from dev.<your-domain>.com all the way through to the cloudfront.<your-domain>.com calls for the m3u8 register file, and then in all the subsequent calls to a .ts or .m3u8

OK, so I spent a bit of time in the s3 config (not editing anything, just reviewing everything… which looked 100% fine to me), and then went back to CF distribution behaviours edit page, where you setup headers to forward. settings (listed below, then a screenshot of mine):

  • cache and origin request settings: use legacy cache settings
  • cache based on selected request headers - whitelist
    • add origin, access-control-request-headers, access-control-request-method. you will need explicitly type the last 2 in, they didn’t auto-complete for me nor show in the suggestion list, but add custom button worked.
    • object caching: use origin cache headers
    • forward cookies/query strings - none(improves caching) on both
    • restrict viewer access (use signed urls or cookies) - yes (this is the entire point of this headache lol)
    • trusted signer, self

Video "streaming" for mobile devices with Amazon CloudFront

After the distribution had saved and propagated, Safari and Chrome video playing both worked!

This was quite a rabbits hole and a degree (or 15) more difficult than I anticipated, but of course once its all written out, it all seems so logical and obvious. I hope this at least partially helps the others i found on the internet with secure streaming private content across all major browsers using AWS Cloudfront infront of S3


Packetized video (i.e. "HLS") - for iOS devices is delivered via HTTP. There is no simple way to provide this effectively using cloudfront.

Wowza Media Server supports dynamic HLS streams using SMIL playlists. We utilize WMS in our workflow - feel free to contact me if you'd like to do some testing!

We have some extra functionality - like adding imagery to audio only streams when a users bandwidth is insufficient for video versions of the stream.

Your question really speaks about iOS devices - although you mention mobile - RTSP connectivity is require to deliver to Blackberries (although some of the latest kit supports flash - like the playbook).

Same goes for Android <= 2.1 - no flash support - so content delivery is generally done via RTSP.

Unfortunately - RTSP doesn't support that kind of smooth delivery - it's one rate - and that is it :)


Use AWS MediaConvert to convert your mp4 to a HLS stream - playlist (.m3u8) as well as encrypted .ts files. If you need more security, make it encrypted.

Then you’ll need a player like VideoJS which can play both progressive download videos and audio, as well as streaming video and audio. And VideoJS can also read the URL to the decryption file and use that in real-time as it downloads, de-encrypts and plays your encrypted .ts segments.

Also check out Amazon S3 Video Player for WordPress which will let you do all of the above without any coding.

0

精彩评论

暂无评论...
验证码 换一张
取 消