

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 CloudFront 串流影片
<a name="tutorial-stream-video-with-cloudfront"></a>

媒體工作流程通常會將隨需影片 (VOD) 檔案、HTTP 即時串流 (HLS) 套件、映像和圖形，存放在編輯器、生產者和自動化系統使用 NFS 或 SMB 寫入 的 ONTAP 磁碟區的 FSx 上。

透過將 Amazon S3 存取點連接至 FSx for ONTAP 磁碟區，CloudFront 可以直接從磁碟區提供內容。編輯器和生產系統一律以 NFS 或 SMB 的方式發佈至磁碟區，CloudFront 會透過存取點擷取內容，檢視器會從最近的 CloudFront 節點接收內容。

在本教學課程中，您將範例影片編碼為 HLS 適應性位元速率套件、將輸出上傳至連接至 FSx for ONTAP 磁碟區的存取點、使用原始存取控制設定 CloudFront 分佈，讓檢視器無法繞過 CloudFront 直接到達磁碟區，並確認串流播放端對端。

**注意**  
本教學課程大約需要 **40 到 60 分鐘**才能完成。 AWS 服務 使用的 會針對您建立的資源產生費用。如果您立即完成所有步驟，包括**清除**區段，則美國東部 （維吉尼亞北部） 的預期成本低於 **1 美元**。 AWS 區域此預估不包含 FSx for ONTAP 磁碟區本身的持續費用。

## 模式的運作方式
<a name="tutorial-cf-how-it-works"></a>

請求流程為：
+ 檢視器的播放器 （瀏覽器、行動應用程式、智慧型電視） 會從 CloudFront 網域請求 HLS 主播放清單。
+ CloudFront 會檢查其邊緣快取。錯過時，CloudFront 會使用 Signature 第 4 版 (SigV4) 搭配其原始存取控制 (OAC) 簽署請求，並將其轉送至存取點的 Amazon S3 端點。
+ 存取點會針對其存取政策授權請求，允許 CloudFront 服務主體範圍限定於您的分佈，並從 FSx 傳回 ONTAP 磁碟區的請求物件。
+ CloudFront 會在邊緣快取回應，並將其傳回給檢視器。

HLS 套件會混合兩種可從不同快取政策中受益的檔案類型：
+ **播放清單** (`.m3u8`) 說明哪些區段組成串流。使用短 `Cache-Control` TTL，讓您可以快速發佈更新的播放清單。
+ **區段** (`.ts`) 包含編碼的視訊和音訊。一旦寫入，區段的內容永遠不會變更，因此請使用不可變的長 `Cache-Control` TTL。

## 先決條件
<a name="tutorial-cf-prerequisites"></a>
+ 已連接 Amazon S3 存取點的 ONTAP 磁碟區的 FSx。存取點必須具有**網際網路**原始伺服器，以便 CloudFront 可以連接它。如需說明，請參閱[建立存取點](fsxn-creating-access-points.md)。
+ AWS CLI 第 2 版已安裝並設定了可建立 CloudFront 分佈、原始存取控制和存取點政策的登入資料。
+ [FFmpeg](https://ffmpeg.org/) 安裝在本機，用於將範例影片編碼為 HLS。
+ 來源影片檔案。本教學課程使用 Blender Foundation 的 [Sintel 預](https://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4)告片，這是在 Creative Commons 下發行的 52 秒 1080p 剪輯。

## 步驟 1：將來源視訊編碼為 HLS 套件
<a name="tutorial-cf-encode"></a>

使用 FFmpeg 以 360p、720p 和 1080p 產生具有逼真的over-the-top(OTT) 位元速率的三變式 HLS 套件。產生的套件包含一個主播放清單，參考每個變數的播放清單，每個播放清單都列出四秒的傳輸串流區段。

1. 下載來源影片。

   ```
   $ mkdir -p ~/media && cd ~/media
   curl -sSL -o sintel-1080p.mp4 \
       https://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4
   ```

1. 使用三種適應性位元速率變體將影片編碼至 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 套件上傳至存取點
<a name="tutorial-cf-upload"></a>

上傳套件兩次：使用短 TTL 的播放清單一次，使用長不可變 TTL 的區段一次。正確設定`Content-Type`很重要：大多數玩家需要 `application/vnd.apple.mpegurl` `.m3u8`和 `video/mp2t` `.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：建立原始存取控制
<a name="tutorial-cf-oac"></a>

原始存取控制 (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 分佈
<a name="tutorial-cf-distribution"></a>

使用存取點別名做為原始網域來建立 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 分佈的價格分級](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PriceClass.html)。

1. 建立分佈。

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

   在回應中記下分佈 ID、ARN 和網域名稱。分佈大約需要五分鐘的時間進行部署。您可以在部署時繼續執行步驟 5。

## 步驟 5：連接允許 CloudFront 的存取點政策
<a name="tutorial-cf-ap-policy"></a>

存取點政策會授予 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}}"
               }
           }
       }]
   }
   ```

1. 將政策連接至存取點。

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

## 步驟 6：驗證播放
<a name="tutorial-cf-verify"></a>

等待分佈達到`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/mp2t`、 `cache-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 中開啟主播放清單 URL，或使用 JavaScript 播放器將其內嵌在網頁中，例如 [hls.js。](https://github.com/video-dev/hls.js)

## 擴展模式
<a name="tutorial-cf-extending"></a>
+ **搭配 HTTPS 使用自訂網域。**為您的網域請求 ACM 憑證，將其連接到 分佈，然後新增指向 CloudFront 網域的 CNAME 記錄。如需說明，請參閱[搭配 CloudFront 使用自訂 URLs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-procedures.html)。
+ **使用簽章的 URLs Cookie 保護高級內容。**對於需要授權的內容 （訂閱服務、早期存取預覽、地理圍欄內容），請使用 CloudFront 簽章的 URLs Cookie。請參閱[使用簽章的 URLs Cookie 提供私有內容](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html)。
+ **當您發佈新內容時，快取會失效。**當您取代播放清單或上傳新的 HLS 套件時，請使用 從 CloudFront 邊緣`aws cloudfront create-invalidation`移除舊版本。對於具有長 TTLs，通常不需要失效，因為每個套件的區段檔案名稱是唯一的。
+ **為以瀏覽器為基礎的玩家啟用 CORS。**如果不同網域上的瀏覽器型 HLS 播放器載入串流，請使用 CloudFront 回應`Access-Control-Allow-Origin`標頭政策將標頭新增至回應。
+ **記錄檢視器請求。**啟用 CloudFront 標準記錄或即時日誌，以擷取檢視器對分析、計費或濫用偵測的請求。

## 疑難排解
<a name="tutorial-cf-troubleshooting"></a>

403 從 CloudFront 禁止  
存取點政策遺失、不包含 CloudFront 服務主體，或 `AWS:SourceArn`條件參考錯誤的分佈 ARN。使用 驗證政策，`aws s3control get-access-point-policy`並確認分發 ARN 符合您`aws cloudfront create-distribution`回應中的政策。

播放器載入主播放清單，但無法播放  
檢查區段檔案是否具有 `Content-Type: video/mp2t`，而播放清單是否具有 `Content-Type: application/vnd.apple.mpegurl`。有些玩家會拒絕具有一般內容類型的客群。使用正確的`--content-type`旗標重新上傳。

新的播放清單需要一些時間才能聯絡觀眾  
CloudFront 會快取 `Cache-Control`標頭所設定 TTL 的播放清單。如果您需要較短的 TTL，請使用較小的`max-age`值重新上傳播放清單，或建立失效。區段沒有此問題，因為其內容不會變更。

`x-cache: Miss from cloudfront` 在每個請求上  
當區域中的檢視器第一次請求檔案時，這是正常的。CloudFront 會在遺漏時從原始伺服器擷取 ，並快取 TTL 的回應。從該節點對相同檔案的後續請求會傳回 `Hit from cloudfront`。

直接存取存取點遭拒  
這是預期的行為。OAC 需要來自 CloudFront SigV4-signed請求，且存取點政策限制對 CloudFront 服務主體的存取。檢視器只能透過分發網域存取內容。

## 清除
<a name="tutorial-cf-clean-up"></a>

停用並刪除分佈，然後刪除剩餘的資源。必須先停用分佈，才能將其刪除，這需要幾分鐘的時間。

停用需要來自 的兩個值`get-distribution-config`：`ETag`適用於 的 `--if-match`，以及適用於 的內部`DistributionConfig`物件 `--distribution-config`（完整回應也包含`update-distribution`不接受的 ETag)。

```
$ # 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
```