はじめに
Akamai ではライブストリーミングで利用できるライブオリジンと CDN を提供しています。
Akamai の提供するライブオリジンである MSL4 (Media Service Live 4) では、ストリーミング形式は HLS/DASH/CMAF をサポートしていますが、どうやって CDN によるライブストリーミングのテストを行うかがよく課題にあがります。
なぜなら、HLS/DASH/CMAF でのインジェストをサポートしているエンコーダーはハードウェア・ソフトウェア含め有償のものしか無いからです。(無償のエンコーダーをご存知でしたらコメントいただけると嬉しいです!)
そのため、テストを行う環境を用意すること自体のハードルが高くなっています。
そこで、今回はエンコーダーとして FFmpeg を利用することで、誰でも簡単にテストできる環境を構築します。
本稿の内容には、Akamai の環境構築も含まれているので、FFmpeg の利用方法だけを見たい方は こちら をクリックしてください。
MSL4 を利用せず、他のライブオリジンと Akamai の CDN でライブストリーミングをされる場合、上記のエンコーダの制限は当てはまらず、利用するライブオリジンの仕様に従います。
テスト環境作成手順
最終的には以下の構成でテストを行います。
AMD (Adaptive Media Delivery) とはストリーミング配信に特化した CDN です。
詳細は以下で確認できます。
Adaptive Media Delivery
Akamai の設定は API 経由で作成するので、事前設定をしていない場合、以下の手順を参考にしてみてください。
API が利用できない場合などは、もちろん GUI で作成しても構いません。
MSL4 (ライブオリジン) の設定
この API ガイドをもとに設定を進めます。
設定に必要なパラメーターを取得
設定の中で Akamai の契約 ID と CP コードが必要なため、それぞれ API 経由で情報を取得します。
後ほど利用するのでどこかにメモしておいてください。
契約 ID
以下のエンドポイントから GET で契約 ID を取得します。
https://{hostname}/config-media-live/v2/msl-origin/contracts
[
{
"contractId": "Contract-123",
"contractName": "Example Company",
︙
}
]
CP コード
以下のエンドポイントから GET で CP コードを取得します。
https://{hostname}/config-media-live/v2/msl-origin/cpcodes?type=INGEST
[
{
"id": 1234567,
"name": "msl_example_cpcode",
"contracts": [
{
"contractId": "Contract-123"
}
]
}
]
オリジンを新規作成
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/config-media-live/v2/msl-origin/origins
Body に必要なパラメータを設定します。
contractId
はさきほど取得したパラメータを入力します。
hostName
は16文字以内で任意の文字列(英数字のみ)を入力します。
cpcode
はさきほど取得したパラメータを入力します。
{
"emailIds": [
"admin@example.com"
],
"encoderZone": "JAPAN",
"contractId": "Contract-123",
"hostName": "mslexampleorigin",
"cpcode": 1234567
}
Status Code が 202Accepted
であれば正常にリクエストが受け付けられています。
レスポンスボディは空ですが、これは正常な動作です。
オリジン作成の進捗を確認するために、以下のエンドポイントから GET で情報を取得します。
https://{hostname}/config-media-live/v2/msl-origin/origins
[
{
"hostName": "014-dn001-mslexampleorigin.akamaiorigin.net",
"hostNameIdentifier": "mslexampleorigin",
"cpcode": 1234567,
"id": 2345678,
︙
"status": "PROVISIONED"
}
]
status
が PROVISIONED
になっていれば作成完了です。
完了まで約40-50分程度かかります。
ストリームを新規作成
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/config-media-live/v2/msl-origin/streams
Body に必要なパラメータを設定します。
origin
の hostName
、cpcode
には前項で確認したパラメータをそのまま入力します。
contractId
は取得したパラメータを入力します。
encoderZone
は JAPAN
を入力します。(もちろん JAPAN 以外の地域も指定可能です)
format
は今回 HLS を入力します。(DASH、CMAF も指定可能です)
name
は任意の文字列を入力します。
cpcode
はさきほど取得したパラメータを入力します。
{
"origin": {
"hostName": "014-dn001-mslexampleorigin.akamaiorigin.net",
"cpcode": 1234567
},
"contractId": "Contract-123",
"encoderZone": "JAPAN",
"format": "HLS",
"name": "mslexamplestream",
"cpcode": 1234567
}
Status Code が 202 Accepted
であれば正常にリクエストが受け付けられています。
こちらもレスポンスボディは空ですが、正常な動作です。
作成の進捗を確認するために、以下のエンドポイントから GET で情報を取得します。
https://{hostname}/config-media-live/v2/msl-origin/origins
{
"totalSize": 1,
"page": 1,
"pageSize": null,
"streams": [
{
"id": 2345679,
"name": "mslexamplestream",
"format": "HLS",
"type": "MSL4",
"cpcode": 1234567,
"originHostName": "014-dn001-mslexampleorigin.akamaiorigin.net",
"modifiedDate": "2022-01-01T00:00:00.000Z",
"primaryStorageCpcode": 3456789,
"backupStorageCpcode": 0,
"createdBy": "admin",
"createdDate": "2022-01-01T00:00:00.000Z",
"streamAuth": false,
"dvrWindowInMin": 0,
"provisionDetail": {
"status": "PROVISIONED"
},
"encoderZone": "JAPAN"
}
]
}
provisionDetail
の status
が PROVISIONED
になっていれば作成完了です。
完了まで約2-3時間程度かかります。
これで MSL4 の設定は完了です。
AMD (CDN) の設定
このガイドをもとに設定を進めます。
この設定におけるすべてのリクエストには以下のヘッダを付与してください。
PAPI-Use-Prefixes: true
Edge Hostname の作成
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/papi/v1/edgehostnames?contractId=ctr_Contract-123
Body に必要なパラメータを設定します。
domainPrefix
にはお好きなサブドメイン名を入力します。
以下の例だと最終的な FQDN は streaming-example.akamaized.net
に設定されます。
それ以外のパラメータは例と同様でOKです。
{
"useCases": [
{
"type": "GLOBAL",
"useCase": "Segmented_Media_Mode",
"option": "LIVE"
}
],
"secureNetwork": "SHARED_CERT",
"productId": "prd_Adaptive_Media_Delivery",
"domainPrefix": "streaming-example",
"domainSuffix": "akamaized.net",
"ipVersionBehavior": "IPV6_COMPLIANCE"
}
成功すると Status Code 201 Created
とともに以下の Body が返されます。
{
"edgeHostnameLink": "/papi/v1/edgehostnames/1234567?contractId=Contract-123"
}
返されたリンク内の数字は edgehostnameId
として後ほど利用するのでメモしておいてください。
CP Code 作成
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/papi/v1/cpcodes?contractId=ctr_Contract-123
Body に必要なパラメータを設定します。
cpcodeName
にはさきほど設定した Edge Hostname を指定するとわかりやすいです。
以下の例だと最終的な FQDN は streaming-example.akamaized.net
に設定されます。
それ以外のパラメータは例と同様でOKです。
{
"productId": "prd_Adaptive_Media_Delivery",
"cpcodeName": "streaming-example.akamaized.net"
}
成功すると Status Code 201 Created
とともに以下の Body が返されます。
{
"cpcodeLink": "/papi/v1/cpcodes/1234567?contractId=Contract-123"
}
返されたリンク内の数字は CP Code の id
として後ほど利用するのでメモしておいてください。
Property の作成
Property では CDN 自体の設定をしていきます。
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/papi/v1/properties?contractId=ctr_Contract-123
Body に必要なパラメータを設定します。
propertyName
にもさきほど設定した Edge Hostname を指定するとわかりやすいです。
それ以外のパラメータは例と同様でOKです。
{
"productId": "prd_Adaptive_Media_Delivery",
"propertyName": "streaming-example.akamaized.net",
"ruleFormat": "latest"
}
成功すると Status Code 201 Created
とともに以下の Body が返されます。
{
"propertyLink": "/papi/v1/properties/1234567?contractId=Contract-123"
}
Property の編集
Property バージョンの取得
Property を編集するためにバージョン情報を取得します。
Property の作成で返されたリンクのパスに /versions
を付与したエンドポイントから GET で情報を取得します。
https://{hostname}/papi/v1/properties/1234567/versions?contractId=ctr_Contract-123
{
"propertyId": "1234567",
"propertyName": "streaming-example.akamaized.net",
"accountId": "Account-123",
"contractId": "Contract-123",
"groupId": "1234567",
"assetId": "1234567",
"versions": {
"items": [
{
"propertyVersion": 1,
"updatedByUser": "admin",
"updatedDate": "2022-01-01T00:00:00.000Z",
"productionStatus": "INACTIVE",
"stagingStatus": "INACTIVE",
"etag": "d8ccb79ff18c8de1352c8ae2e56aadafabec1794",
"productId": "Adaptive_Media_Delivery",
"ruleFormat": "latest"
}
]
}
}
上記の出力の propertyId
、 propertyVersion
、 etag
を次の設定で利用します。
Property に作成した Edge Hostname を追加する
以下のエンドポイントに PUT でリクエストを送ります。
https://{hostname}/papi/v1/properties/prp_1234567/versions/1/hostnames?contractId=ctr_Contract-123&validateHostnames=false&includeCertStatus=false
Body に必要なパラメータを設定します。
cnameFrom
と cnameTo
にはさきほど設定した Edge Hostname を指定します。
edgeHostnameId
の数字部分にはメモしておいた値を指定します。
それ以外のパラメータは例と同様でOKです。
また、この作業ではリクエストヘッダに If-Match
を追加し、さきほど取得した etag
の値を設定してリクエストする必要があるのでご注意ください。
If-Match: "d8ccb79ff18c8de1352c8ae2e56aadafabec1794"
[
{
"cnameType": "EDGE_HOSTNAME",
"cnameFrom": "streaming-example.akamaized.net",
"cnameTo": "streaming-example.akamaized.net",
"edgeHostnameId": "ehn_1234567"
}
]
本稿執筆時の2022年11月30日において API 経由では Akamai の共有証明書が割り当てられません。
そのため、テストでは HTTPS は利用せず、HTTP のみで行います。
Property の Rule を変更する
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/papi/v1/properties/prp_1234567/versions/1/rules?contractId=ctr_Contract-123&validateMode=full&validateRules=true&dryRun=false
Body に必要なパラメータを設定します。
以下の例の中でいくつか変更が必要な部分があるので、例のあとに解説します。
{
"rules": {
"name": "default",
"children": [
{
"name": "Default CORS Policy",
"children": [],
"behaviors": [
{
"name": "modifyOutgoingResponseHeader",
"options": {
"action": "MODIFY",
"avoidDuplicateHeaders": false,
"newHeaderValue": "*",
"standardModifyHeaderName": "ACCESS_CONTROL_ALLOW_ORIGIN"
}
},
{
"name": "modifyOutgoingResponseHeader",
"options": {
"action": "MODIFY",
"avoidDuplicateHeaders": false,
"newHeaderValue": "GET,POST,OPTIONS",
"standardModifyHeaderName": "ACCESS_CONTROL_ALLOW_METHODS"
}
},
{
"name": "modifyOutgoingResponseHeader",
"options": {
"action": "MODIFY",
"avoidDuplicateHeaders": false,
"newHeaderValue": "origin,range,hdntl,hdnts,CMCD-Request,CMCD-Object,CMCD-Status,CMCD-Session",
"standardModifyHeaderName": "ACCESS_CONTROL_ALLOW_HEADERS"
}
},
{
"name": "modifyOutgoingResponseHeader",
"options": {
"action": "MODIFY",
"avoidDuplicateHeaders": false,
"newHeaderValue": "Server,range,hdntl,hdnts,Akamai-Mon-Iucid-Ing,Akamai-Mon-Iucid-Del,Akamai-Request-BC",
"standardModifyHeaderName": "ACCESS_CONTROL_EXPOSE_HEADERS"
}
},
{
"name": "modifyOutgoingResponseHeader",
"options": {
"action": "MODIFY",
"avoidDuplicateHeaders": false,
"newHeaderValue": "true",
"standardModifyHeaderName": "ACCESS_CONTROL_ALLOW_CREDENTIALS"
}
},
{
"name": "modifyOutgoingResponseHeader",
"options": {
"action": "MODIFY",
"avoidDuplicateHeaders": false,
"newHeaderValue": "86400",
"standardModifyHeaderName": "ACCESS_CONTROL_MAX_AGE"
}
}
],
"criteria": [],
"criteriaMustSatisfy": "all",
"comments": ""
}
],
"behaviors": [
{
"name": "origin",
"options": {
"originType": "MEDIA_SERVICE_LIVE",
"mslorigin": "014-dn001-mslexampleorigin.akamaiorigin.net"
}
},
{
"name": "cpCode",
"options": {
"value": {
"id": 1234567,
"description": "streaming-example.akamaized.net",
"products": [
"Adaptive_Media_Delivery"
],
"name": "streaming-example.akamaized.net"
}
}
},
{
"name": "segmentedMediaOptimization",
"options": {
"behavior": "LIVE",
"showAdvanced": false,
"enableUllStreaming": false
}
},
{
"name": "originCharacteristics",
"options": {
"authenticationMethod": "AUTOMATIC",
"country": "JAPAN",
"authenticationMethodTitle": ""
}
},
{
"name": "contentCharacteristicsAMD",
"options": {
"catalogSize": "UNKNOWN",
"contentType": "HD",
"dash": false,
"hds": false,
"hls": true,
"popularityDistribution": "UNKNOWN",
"segmentDurationHLS": "SEGMENT_DURATION_6S",
"segmentSizeHLS": "UNKNOWN",
"smooth": false
}
},
{
"name": "clientCharacteristics",
"options": {
"country": "JAPAN"
}
},
{
"name": "cacheKeyQueryParams",
"options": {
"behavior": "IGNORE_ALL"
}
},
{
"name": "segmentedContentProtection",
"options": {
"enabled": false,
"hlsMediaEncryption": false,
"tokenAuthenticationTitle": "",
"mediaEncryptionTitle": "",
"dashMediaEncryption": false
}
},
{
"name": "dynamicThroughtputOptimization",
"options": {
"enabled": true
}
}
],
"options": {
"is_secure": true
}
}
}
オリジンサーバーのパラメータを変更します。
mslorigin
に作成した MSL4 のオリジンホスト名を入力します。
それ以外のパラメータは下記の例のままでOKです。
{
"name": "origin",
"options": {
"originType": "MEDIA_SERVICE_LIVE",
"mslorigin": "014-dn001-mslexampleorigin.akamaiorigin.net"
}
}
CP Code のパラメータを変更します。
id
はさきほどメモした値を指定します。
description
と name
には作成した CP Code の名前を指定します。
{
"name": "cpCode",
"options": {
"value": {
"id": 1234567,
"description": "streaming-example.akamaized.net",
"products": [
"Adaptive_Media_Delivery"
],
"name": "streaming-example.akamaized.net"
}
}
}
上記を変更した内容を Body としてリクエストします。
成功すると Status Code 200 OK
と Property の内容が返されます。
{
"accountId": "act_Account-123",
"contractId": "ctr_Contract-123",
"groupId": "grp_12345",
"propertyId": "prp_1234567",
"propertyVersion": 1,
︙
Property を展開する
以下のエンドポイントに POST でリクエストを送ります。
https://{hostname}/papi/v1/properties/prp_1234567/activations?contractId=ctr_Contract-123
Body に必要なパラメータを設定します。
notifyEmails
には管理者のアドレスを指定します。
それ以外のパラメータは例と同様でOKです。
{
"acknowledgeAllWarnings": true,
"activationType": "ACTIVATE",
"fastPush": true,
"ignoreHttpErrors": true,
"notifyEmails": [
"admin@example.com"
],
"useFastFallback": false,
"network": "PRODUCTION",
"note": "First activation",
"propertyVersion": 1
}
成功すると Status Code 201 Created
とともに以下の Body が返されます。
{
"activationLink": "/papi/v1/properties/1234567/activations/1234567?contractId=Contract-123"
}
これで MSL4 と AMD の設定は完了です。
ライブストリーミングをテストする
今回は Linode 上から MSL4 に Ingest します。
無料のソフトウェアエンコーダーで HLS での Ingest をサポートしているものがないと思われるため、FFmpeg を利用します。
仮想マシンを作成する
まずは Linode の仮想マシンを作成します。
以下の StackScripts を利用すると、私と同じ環境でお試しいただけます。
もちろんご自身のお好きな環境にFFmpeg をインストールしても構いません。
テスト用映像素材の準備
ここではテスト用の動画として有名な「Big Buck Bunny」を利用します。
以下のリンクからご自身の環境にダウンロードしてください。
$ curl -O https://download.blender.org/demo/movies/BBB/bbb_sunflower_1080p_30fps_normal.mp4
FFmpeg のパラメータを作成する
FFmpeg のパラメータ作成は非常に難解です。
そこで、Akamai 社員の有志が作成した便利なコマンド生成ツールを利用します。(とても便利です!!)
設定できるところはたくさんありますが、最低限必要な部分だけ設定します。
Input
は Loop file のまま、さきほどダウンロードしたファイル名を指定してください。(必要に応じてファイルが保管されている場所のパスも)
Optimize
の Segment size
は 6s
を指定します。AMDで設定した値と同じです。
Outputs
では HLS live stream
を指定します。
すると、追加入力項目が出てくるので、以下のように設定します。
Packaging
: HLS (ts) - discrete
EP type
: Akamai
Stream ID
: Stream の ID
Event name
: 任意の文字列
Stream ID
は MSL4 の Stream の ID です。
以下のエンドポイントから GET で情報を取ると、id
というキーがあるのでこの値を入力してください。
https://{hostname}/config-media-live/v2/msl-origin/origins
設定はこんな感じ。
各パラメータを入力すると、以下のように Shell Script が出力されます。
#!/bin/bash
#Generated by command builder r0.3h
timestamp="$(date +%s)"
ffmpeg \
\
-re -fflags +genpts -stream_loop -1 -i testclip.mp4 \
\
-flags +global_header -r 30000/1001 \
\
-filter_complex "scale=1280x720" \
\
-pix_fmt yuv420p \
-c:v libx264 \
\
-b:v:0 3000K -maxrate:v:0 3000K -bufsize:v:0 3000K/2 \
\
-g:v 30 -keyint_min:v 30 -sc_threshold:v 0 \
\
-color_primaries bt709 -color_trc bt709 -colorspace bt709 \
\
-c:a aac -ar 48000 -b:a 96k \
\
-map 0:v:0 \
-map 0:a:0 \
\
-preset veryfast \
-tune zerolatency \
\
-hls_init_time 6.006 \
-hls_time 6.006 \
-hls_list_size 20 \
-hls_flags delete_segments \
-hls_base_url $timestamp/ \
-var_stream_map 'a:0,agroup:a0,default:0 v:0,agroup:a0' \
-hls_segment_filename 'http://p-ep2345678.i.akamaientrypoint.net/2345678/streamtest/'$timestamp/stream%v_%05d.ts \
-master_pl_name master.m3u8 \
-http_user_agent Akamai_Broadcaster_v1.0 \
-http_persistent 1 \
-f hls \
http://p-ep2345678.i.akamaientrypoint.net/2345678/streamtest/level_%v.m3u8
これを Linode の仮想マシン上で実行すると、Big Buck Bunny が延々とループ配信されます。
Shell Script を配置する
生成されたスクリプトを仮想マシンに配置します。
ここでは bbb_stream.sh
というファイル名で保存しています。
このままでは実行できないので、実行権限を付与します。
$ chmod +x bbb_stream.sh
実行権限が付与されているかどうか確認します。
$ ls -l bbb_stream.sh
-rwxrwxr-x 1 admini admin 986 Jan 01 00:00 bbb_stream.sh
x
がついているのでOKです。
ライブストリーミングを開始する
Shell Script の準備ができているので、あとは実行するだけです。
$ ./bbb_stream.sh
ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 11 (Ubuntu 11.2.0-19ubuntu1)
︙
エラーが発生しなければOKです。
ライブを視聴する
Akamai のデモプレイヤーを利用してライブ配信を視聴します。
ライブは URL は以下のフォーマットで配信されます。
http://[hostname]/hls/live/[stream_id]/[event_name]/[filename].m3u8
今回の構成だと以下のようになります。
http://streaming-example.akamaized.net/hls/live/2345678/streamtest/master.m3u8
こちらをプレイヤーに指定して再生します。
問題なく再生されていればOKです。
まとめ
今回は Akamai のライブ配信設定から FFmpeg を利用してライブストリーミングを行う手順までを解説しました。
最も難解な FFmpeg を簡単に使えるようになるので、テストのハードルは大きく下がると思います。
ただ、利用したスクリプトは例外処理等はまったくないため、あくまでも検証用として使ってください。
AMD(CDN)は API で作成しましたが、とても手間がかかるので 後日別の記事で Terraform での展開についても紹介したいと思います。
(追記)Terraform での展開についてまとめました。