View a markdown version of this page

使用流式传输视频 CloudFront - FSx for ONTAP

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用流式传输视频 CloudFront

媒体工作流通常将完成的内容(视频点播 (VOD) 文件、HTTP 直播 (HLS) 包、图像和图形)存储在 FSx for ONTAP 卷上,编辑、制作人和自动化系统使用 NFS 或 SMB 写入该卷。

通过连接到 FSx for ONTAP 卷的 Amazon S3 接入点, CloudFront 可以直接从该卷中提供内容。编辑和制作系统像往常一样通过 NFS 或 SMB 向卷发布内容,通过接入点 CloudFront 获取内容,观众则从最近的 CloudFront 边缘位置接收内容。

在本教程中,您将示例视频编码为 HLS 自适应比特率包,将输出上传到连接到 FSx for ONTAP 卷的接入点,使用原始访问控制配置 CloudFront 分发以使观众无法绕过直接访问音量,并验证直播是否端 CloudFront 对端播放。

注意

本教程大约需要 40 到 60 分钟才能完成。 AWS 服务 使用者会对您创建的资源产生费用。如果您及时完成所有步骤,包括清理部分,则美国东部(弗吉尼亚北部)的预期费用将低于 1美元 AWS 区域。该估算值不包括 FSx 对 ONTAP 容量本身的持续收费。

模式是如何运作的

请求流为:

  • 观众的播放器(浏览器、移动应用程序、智能电视)向 CloudFront 域请求HLS主播放列表。

  • CloudFront 检查其边缘缓存。如果未成功, CloudFront 则使用签名版本 4 (Sigv4) 及其源访问控制 (OAC) 对请求进行签名,然后将其转发到接入点的 Amazon S3 终端节点。

  • 接入点根据其访问策略授权请求,该策略允许 CloudFront 服务主体范围限于您的分发,并从 FSx for ONTAP 卷返回请求的对象。

  • CloudFront 在边缘缓存响应并将其返回给查看者。

HLS 包混合了两种类型的文件,它们受益于不同的缓存策略:

  • 播放列表 (.m3u8) 描述了哪些片段构成了直播内容。使用简短的 Cache-Control TTL,这样您就可以快速发布更新的播放列表。

  • 片段 (.ts) 包含编码后的视频和音频。写入后,片段的内容永远不会改变,因此请使用长的、不可变Cache-Control的 TTL。

先决条件

  • 连接了 Amazon S3 接入点的 ONTAP 卷的 FSx。接入点必须有互联网网络来源,这样 CloudFront 才能到达它。有关说明,请参阅创建接入点

  • AWS CLI 安装并配置了版本 2,其凭据可以创建 CloudFront 分配、源访问控制和接入点策略。

  • FFmpeg 安装在本地,用于将示例视频编码为 HLS。

  • 源视频文件。本教程使用了Blender基金会的 Sintel预告片,这是一段在知识共享下发布的52秒1080p片段。

第 1 步:将源视频编码为 HLS 包

使用 FFmpeg 制作 360p、720p 和 1080p 的三种变体 HLS 软件包,并具有逼真的过顶 (OTT) 比特率。生成的软件包包括一个主播放列表,该播放列表引用了每个变体的播放列表,每个播放列表都列出了四秒钟的传输流片段。

  1. 下载源视频。

    $ mkdir -p ~/media && cd ~/media curl -sSL -o sintel-1080p.mp4 \ https://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4
  2. 使用三种自适应比特率变体将视频编码为 HLS。

    $ mkdir hls && cd hls ffmpeg -i ../sintel-1080p.mp4 \ -filter_complex "[0:v]split=3[v1][v2][v3]; \ [v1]scale=w=640:h=360[v1out]; \ [v2]scale=w=1280:h=720[v2out]; \ [v3]scale=w=1920:h=1080[v3out]" \ -map "[v1out]" -c:v:0 libx264 -b:v:0 800k -maxrate:v:0 856k -bufsize:v:0 1200k \ -map "[v2out]" -c:v:1 libx264 -b:v:1 3000k -maxrate:v:1 3200k -bufsize:v:1 4500k \ -map "[v3out]" -c:v:2 libx264 -b:v:2 5500k -maxrate:v:2 5900k -bufsize:v:2 8250k \ -preset veryfast -g 48 -keyint_min 48 -sc_threshold 0 \ -map a:0 -map a:0 -map a:0 -c:a aac -b:a:0 96k -b:a:1 128k -b:a:2 128k \ -f hls -hls_time 4 -hls_playlist_type vod -hls_flags independent_segments \ -hls_segment_filename "stream_%v/seg_%03d.ts" \ -master_pl_name master.m3u8 \ -var_stream_map "v:0,a:0,name:360p v:1,a:1,name:720p v:2,a:2,name:1080p" \ "stream_%v/playlist.m3u8"

    该命令生成一个目录树,其中包含一个主播放列表、三个变体播放列表以及每个变体的传输流片段。

    hls/ ├── master.m3u8 ├── stream_360p/ │ ├── playlist.m3u8 │ ├── seg_000.ts │ └── ... ├── stream_720p/ │ ├── playlist.m3u8 │ ├── seg_000.ts │ └── ... └── stream_1080p/ ├── playlist.m3u8 ├── seg_000.ts └── ...

步骤 2:将 HLS 包上传到接入点

上传软件包两次,一次用于包含短 TTL 的播放列表,一次用于具有长且不可变的 TTL 的片段。正确设置Content-Type很重要:大多数玩家都需要 for .m3u8application/vnd.apple.mpegurl f video/mp2t or .ts

access-point-alias替换为您的接入点别名。

$ # Playlists: short TTL, m3u8 content type aws s3 cp ~/media/hls/ "s3://access-point-alias/content/sintel/" \ --recursive --exclude "*" --include "*.m3u8" \ --content-type "application/vnd.apple.mpegurl" \ --cache-control "max-age=60" # Segments: long immutable TTL, ts content type aws s3 cp ~/media/hls/ "s3://access-point-alias/content/sintel/" \ --recursive --exclude "*" --include "*.ts" \ --content-type "video/mp2t" \ --cache-control "max-age=31536000,immutable"

确认上传的两个文件都符合预期的内容类型和缓存标头。

$ aws s3api head-object --bucket access-point-alias \ --key content/sintel/master.m3u8 \ --query '{ContentType:ContentType,CacheControl:CacheControl}'

步骤 3:创建源站访问控制

源访问控制 (OAC) 允许您对接入点的请求进行 CloudFront 签名,因此 CloudFront只能获取对象。如果没有 OAC,则查看者可以通过直接从接入点端点请求对象来绕过 CloudFront 。

$ aws cloudfront create-origin-access-control \ --origin-access-control-config \ 'Name=fsxn-media-oac,SigningProtocol=sigv4,SigningBehavior=always,OriginAccessControlOriginType=s3'

记下响应中的 Id。您将在下一步中使用它。

步骤 4:创建分 CloudFront 配

使用接入点别名作为源域创建 CloudFront 分配。使用CachingOptimized托管缓存策略,该策略遵循您在步骤 2 中设置的Cache-Control标头。

  1. 将以下配置保存到名为的文件中dist.json,替换占位符。

    { "CallerReference": "fsxn-media-1", "Comment": "FSx for ONTAP media delivery", "Enabled": true, "DefaultRootObject": "", "Origins": { "Quantity": 1, "Items": [{ "Id": "fsxn-ap", "DomainName": "access-point-alias.s3.region.amazonaws.com", "S3OriginConfig": {"OriginAccessIdentity": ""}, "OriginAccessControlId": "oac-id", "ConnectionAttempts": 3, "ConnectionTimeout": 10 }] }, "DefaultCacheBehavior": { "TargetOriginId": "fsxn-ap", "ViewerProtocolPolicy": "redirect-to-https", "AllowedMethods": { "Quantity": 2, "Items": ["GET", "HEAD"], "CachedMethods": {"Quantity": 2, "Items": ["GET", "HEAD"]} }, "Compress": true, "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6" }, "PriceClass": "PriceClass_100", "ViewerCertificate": {"CloudFrontDefaultCertificate": true} }
    注意

    PriceClass_100仅在北美和欧洲使用 CloudFront 边缘站点,这样可以降低本教程的成本。对于全局边缘覆盖范围,请将该值更改为PriceClass_All。有关更多信息,请参阅为 CloudFront 分配选择价格等级

  2. 创建发行版。

    $ aws cloudfront create-distribution --distribution-config file://dist.json \ --query 'Distribution.{Id:Id,DomainName:DomainName,ARN:ARN}'

    记下响应中的分发 ID、ARN 和域名。部署该分发大约需要五分钟。部署时,您可以继续执行步骤 5。

步骤 5:附加允许的接入点策略 CloudFront

接入点策略向 CloudFront 服务主体授予读取对象的权限,该权限仅限于使用AWS:SourceArn条件的特定分配。

  1. 将以下策略保存到名为的文件中ap-policy.json,替换占位符。

    { "Version": "2012-10-17", "Statement": [{ "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": {"Service": "cloudfront.amazonaws.com"}, "Action": "s3:GetObject", "Resource": "arn:aws:s3:region:account-id:accesspoint/access-point-name/object/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::account-id:distribution/distribution-id" } } }] }
  2. 将策略附加到接入点。

    $ aws s3control put-access-point-policy \ --account-id account-id \ --name access-point-name \ --policy file://ap-policy.json

步骤 6:验证播放

等待分发达到Deployed状态。

$ aws cloudfront get-distribution --id distribution-id \ --query 'Distribution.Status'

通过获取主播放列表 CloudFront。

$ curl -sS "https://distribution-domain/content/sintel/master.m3u8"

响应应列出三种变体。

#EXTM3U #EXT-X-VERSION:6 #EXT-X-STREAM-INF:BANDWIDTH=1031744,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2" stream_360p/playlist.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=3497301,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2" stream_720p/playlist.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=6311285,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2" stream_1080p/playlist.m3u8

检查响应标头的内容类型、缓存控制和缓存状态是否正确。

$ curl -sSI "https://distribution-domain/content/sintel/stream_1080p/seg_000.ts"

成功的响应会显示content-type: video/mp2tcache-control: max-age=31536000,immutable、和一个x-cache标头,指示响应是来自边缘还是源头。

最后,使用 FFmpeg 端对端播放直播,以确认所有片段都正确提取和解码。

$ ffprobe -v error \ -show_entries stream=codec_name,width,height \ -show_entries format=duration \ "https://distribution-domain/content/sintel/master.m3u8"

你也可以在 Safari 或 VLC 中打开主播放列表网址,或者使用诸如 hls.js 之类的 JavaScript 播放器将其嵌入到网页中。

扩展模式

  • 使用带有 HTTPS 的自定义域名。为您的域申请 ACM 证书,将其附加到分配中,然后添加指向该 CloudFront 域的 CNAME 记录。有关说明,请参阅将自定义 URL 与配合使用 CloudFront

  • 使用签名网址或签名 Cookie 保护优质内容。对于需要授权的内容(订阅服务、抢先体验预览、地理围栏内容),请使用 CloudFront 签名 URL 或签名 Cookie。请参阅使用签名网址和签名 Cookie 提供私有内容

  • 发布新内容时使缓存失效。替换播放列表或上传新的 HLS 包时,请使用从 aws cloudfront create-invalidation edge 中移除旧版本 CloudFront 。对于具有长 TTL 的不可变分段,通常不需要失效,因为每个包的分段文件名都是唯一的。

  • 为基于浏览器的玩家启用 CORS。如果其他网域中基于浏览器的 HLS 播放器加载了您的直播,请使用响应Access-Control-Allow-Origin标头策略向 CloudFront响应添加标头。

  • 记录查看者请求。启用 CloudFront标准日志记录或实时日志,以捕获查看者对分析、计费或滥用检测的请求。

问题排查

403 禁止进入 CloudFront

缺少接入点策略,不包括 CloudFront 服务主体,或者AWS:SourceArn条件引用了错误的分发 ARN。使用验证策略aws s3control get-access-point-policy并确认分发 ARN 与您的aws cloudfront create-distribution回复中的分发 ARN 相匹配。

播放器加载主播放列表但无法播放

检查片段文件是否有Content-Type: video/mp2t,播放列表是否有Content-Type: application/vnd.apple.mpegurl。一些玩家拒绝使用通用内容类型的片段。 Re-upload 使用正确的--content-type标志。

新的播放列表需要时间才能吸引观众

CloudFront 缓存标题设置的 TTL 的播放Cache-Control列表。如果您需要较短的 TTL,请使用较小的max-age值重新上传播放列表,或者创建无效状态。区段没有这个问题,因为它们的内容不会改变。

x-cache: Miss from cloudfront根据每一个请求

当某个区域的查看者第一次请求文件时,这是正常的。 CloudFront 失败时从原点获取并缓存 TTL 的响应。随后从该边缘站点请求相同文件的请求将返回Hit from cloudfront

拒绝直接访问接入点

这是预期行为。OAC 要求来自服务主体的 SigV4-signed 请求 CloudFront,接入点策略限制对 CloudFront 服务主体的访问。观看者只能通过分发域访问内容。

清理

禁用并删除该分配,然后删除其余资源。必须先禁用发行版,然后才能将其删除,这需要几分钟。

禁用需要来自的两个值get-distribution-configETagf --if-match or 和 for 的内部DistributionConfig对象--distribution-config(完整的响应还包含 ETag,它update-distribution不接受)。

$ # Capture the current ETag and the DistributionConfig body GET_ETAG=$(aws cloudfront get-distribution-config --id distribution-id \ --query 'ETag' --output text) aws cloudfront get-distribution-config --id distribution-id \ --query 'DistributionConfig' --output json \ | jq '.Enabled = false' > dist-updated.json # Disable the distribution. The response returns a new ETag. UPDATE_ETAG=$(aws cloudfront update-distribution --id distribution-id \ --if-match "$GET_ETAG" --distribution-config file://dist-updated.json \ --query 'ETag' --output text) # Wait for Status to reach Deployed before deleting. aws cloudfront get-distribution --id distribution-id \ --query 'Distribution.Status' # Delete the distribution using the ETag from the update call. aws cloudfront delete-distribution --id distribution-id \ --if-match "$UPDATE_ETAG" # Fetch the OAC ETag, then delete the OAC. OAC_ETAG=$(aws cloudfront get-origin-access-control --id oac-id \ --query 'ETag' --output text) aws cloudfront delete-origin-access-control --id oac-id \ --if-match "$OAC_ETAG" aws s3control delete-access-point-policy \ --account-id account-id --name access-point-name aws s3 rm "s3://access-point-alias/content/sintel/" --recursive