在iOS浏览器中流式传输AWS S3 HLS视频

问题描述

如何在iOS Safari浏览器中流式传输HLS(.m3u8)?我的视频存储在AWS S3存储桶中,访问视频和音频.m3u8的唯一方法是传递签名的URL。

我正在使用videojs播放视频。 videojs.Hls.xhr.beforeRequest在iOS浏览器上不起作用。我还读到iOS不支持MSE,是否可以使用其他方法传递签名的URL,以便能够在iOS浏览器上流式传输我的视频?

这是我的示例代码错误屏幕截图:

videojs.Hls.xhr.beforeRequest = function(options) {

               
                if (options.uri.includes('Audio')) {
                    options.uri = options.uri + '?Policy=' + policy + '&Key-Pair-Id=' + keyPairId + '&Signature=' + signature;

                }
                else if (options.uri.includes('Video')) {
                    options.uri = options.uri + '?Policy=' + policy + '&Key-Pair-Id=' + keyPairId + '&Signature=' + signature;
                   
                }

                return options
}

var overrideNative = false;

var player = videojs('video-test',{
  "controls": true,"fluid": true,"preload": 'none',"techOrder": ["html5"],"html5": {
            "hls": {
                "withCredentials": true,overrideNative: overrideNative,},nativeVideoTracks: !overrideNative,nativeAudioTracks: !overrideNative,nativeTextTracks: !overrideNative
        });

player.src(
{
   src: url,type: "application/x-mpegURL",withCredentials: true
});

enter image description here

解决方法

完全相同的问题,除了在ReactJS中实现 videojs vhs覆盖无效,因为它与Safari和解析(或不解析)选项有关,以查看通过寄存器m3u8的后续调用的安全性参数。

还有其他一些人在处理这个问题,例如 https://github.com/awslabs/unicornflix/issues/15

我已经尝试了一切,从Amazon IVS + VideoJS尝试到将我的类模块重写为功能来尝试我发现的示例;基本上总是回到这个问题上

---------------更新如下--------------- (并获得舒适的座位)

使用安全Cookie(适用于基于iOS的浏览器和所有Safari)以及用于Chrome和其他所有功能的安全网址,通过Cloudfront从S3传输受保护的视频。

网站架构:

假设:等同于上述云架构的设置,特别是CF到S3存储桶的IAM配置,以及IAM和CORS的相关S3安全配置。

TL / DR

NON-SAFARI aka Chrome等-使用安全网址(非常容易OOTB);上面的指南适用于Chrome,但不适用于Safari。

Safari需要安全的Cookie才能以本地方式流式传输hls,并且根本无法识别xhr.beforeRequest重载。 基于SAFARI的SAFARI / iOS浏览器-使用安全Cookie 下面的所有内容对此进行了解释。

设置cookie,听起来很简单!可能是为什么为什么它在AWS CloudFront,AWS论坛或AWSDeveloper Slack频道中没有端到端示例的原因,所以它被认为很容易,因为嘿,它的cookie对吗?

对。 END TL / DR

解决方案详细信息

“ AH-HA!”时刻终于明白,要使其正常工作,您需要能够从自己的服务器上为Cloudfront服务器设置Cookie,这基本上是一项巨大的Web安全禁忌。又名-“在整个网络通话过程中,域名都必须相同”

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

以及此处的链接https://www.spacevatican.org/2015/5/1/using-cloudfront-signed-cookies/

结合原始的AWS文档,其中包含关于签名Cookie的信息,其中具有域名的cname应用于子域,所有这些最终都对我有用。

解决方案是:

  1. 为您的Cloudfront实例设置CNAME;即:您无法对5j1h24j1j.cloudfront.net设置cookie,因为您不拥有它,但是您可以在DNS中对诸如cloudfront。 .com之类的CNAME进行命名。在https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html
  2. 的此特定步骤中存在良好的文档
  3. 重要-您还需要在CF发行版中为此CNAME设置参考。 (如果您需要SSL,则需要使用cloudfront。 .com重新签名您的域证书,然后将此证书上传到AWS Certificate Manager,因此它将在CF Distribution编辑屏幕下拉列表中被引用(除非选择某些内容,否则无法保存。)
  4. 对于您的本地开发盒,为任何NodeJS侦听/绑定IP设置主机文件重载。即:如果您的节点绑定到0.0.0.0,则编辑/ etc / hosts以使其具有一行0.0.0.0 dev。 .com-当您部署到生产主机时,该域显然将在那里工作。
  5. 现在,在后端(又称为服务器端)代码中,您将在其中设置cookie,需要设置domain参数,并且不能直接通配符,但可以将其保留为 (在浏览器中,如果您使用开发人员工具进行检查,则将显示为。 .com。注意领先点。对于现代浏览器而言,这是很好的行为,也是预期的行为;本质上说“ .com将可访问这些Cookie。

上面的操作是确保END TO END,以便您能够通过从dev。 .com或电话开始的呼叫发送分配给。 .com的cookie。您将来的产品 .com到相同的uri,但在后端的端口不同,然后通过CNAME到CF,这是cookie现在可以看到的子域。此时,由CF决定是否将所需的标头传递到S3实例。

但是,等等,还有更多要做的事情首先是客户端。 一开始让我什至没有看到Cookie的原因是,它们没有被放置 除非请求者/发起者在启动它的网络调用中使用“ withCredentials:true”标志。在我的代码中,这是对视频列表的后端NodeJS端点的基于ReactJS componentDidMount()的Axios网络REST GET调用(nodeJS从AWS的graphQL获取,但这对于我的修复方法来说并不需要)。>

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

        })
    }

当我的axios呼叫中没有“ withCredentials:true”时,就永远不会将cookie发送回去;我一拥有那?我的cookie至少被发送回了第一个调用者localhost(cookie中没有域参数,默认情况下是调用,当时我作为本地主机),这意味着它将永远不会将其传递给CF,是那时的2435h23l4jjfsj.cloudfront.net名称。

因此,更新axios以使用dev。 .com进行服务器访问,并在调用有关视频的后端信息的调用中设置了withCredentials标志和cookie。正如AWS文档所指出的那样,需要在调用安全内容之前对cookie进行完全设置,从而完成此操作。

在上述对我的api的调用中,我得到了类似的信息

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

[侧边栏-签名的URL都是由Lambda在云中生成的] 对于Chrome,播放器代码会将两者附加在一起,然后在实例化video.js播放器的任何地方,按如下所示重载videojs.Hls.xhr.beforeRequest

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

在寄存器m3u8文件启动后,将查询字符串?policy = x&signature = y&Key-Pair-ID = z放在流中每个子文件的末尾。

上述api的后端调用在将json作为响应发送之前,也将QP拆开以设置cookie,如下所示

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

中断-现在我们将withCredentials设置为true,您可能会看到CORS问题;好玩 在服务器端代码(我的reactJS)中,我在我的nodejs路由器中设置了一些标头

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

在这一点上,东西仍然没有用。这是因为云代码将CF 234hgjghg.cloudfront.net域放入了策略中,而不是我的CNAME映射中。我在云端更新了它。因此,现在我对视频数据的呼叫,是使用cloudfront。 .com而不是在最后响应步骤3 https://forums.aws.amazon.com/thread.jspa?messageID=610961&#610961中描述的cloudfront.net将URL返回到安全的m3u8。

在这一点上,如果我使用了safari调试工具,我就知道自己已经接近了,因为我对尝试流式传输的响应从no key或cookie xml变为了

<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
错误,其中包含对我的S3存储桶的引用。对我来说,这意味着我的CF发行版基本上对基于cookie的策略,密钥ID和签名感到满意,并且已经将我传递给S3,但是S3告诉我迷路。

这时的好处是,从dev。 .com一直到cloudfront设置了3个必需的cloudfront cookie。 .com要求m3u8注册文件,然后在对.ts或.m3u8

的所有后续调用中

好的,所以我花了一些时间在s3配置中(不编辑任何内容,只是查看所有内容……对我来说100%没问题),然后返回CF分发行为编辑页面,在其中设置标头前锋。 设置(下面列出,然后是我的截图):

  • 缓存和原始请求设置:使用旧版缓存设置
  • 基于所选请求标头的缓存-白名单
    • 添加来源,访问控制请求标头,访问控制请求方法。您需要明确输入最后2个,它们对我来说并不会自动完成,也不会显示在建议列表中,但是添加自定义按钮即可。
    • 对象缓存:使用原始缓存头
    • 转发cookie /查询字符串-两者都没有(改进缓存)
    • 限制查看者访问(使用签名的URL或cookie)-是的(这是令人头疼的全部内容)
    • 可信任的签名人,自我

enter image description here 发行版保存并传播后,Safari和Chrome视频播放都可以正常工作!

这是一个比我预想的要大得多的兔子洞和一个学位(或15个),但当然,一旦全部写完,一切似乎都非常合理和明显。我希望这至少可以部分地帮助我在互联网上找到的其他人,使用S3的AWS Cloudfront infront在所有主要浏览器上提供安全的流式私有内容

,

这似乎很有希望,但我仍在尝试弄清它的hls版本是什么样(这是破折号的示例):https://github.com/videojs/video.js/issues/5247#issuecomment-735299266