1つの入力、数百のプログラム — MediaLiveで24時間365日のFASTチャンネルを運用
24時間365日稼働するFASTチャンネルは、毎日、永遠に、数百ものユニークなプログラムを再生します。AWS MediaLiveは、チャンネルあたりの入力アタッチメントを20に制限しています。単純な設計(ビデオごとに1つの入力)では、昼食前にスロットが不足し、ライブストリームをオフラインにするチャンネルの再起動を余儀なくされます。私たちはこれを、チャンネルが再生するすべてのプログラムに対応する1つの動的入力と、デプロイ、部分的な障害、オペレーターの編集全体でタイムラインを自己修復させるためのプログラムレベルのオーケストレーション層を上に構築することで解決しました。
これは、そのオーケストレーションが実際にどのように機能するかについてのエンジニアリングストーリーです。
クイック概要
| 項目 | 詳細 |
|---|---|
| ドメイン | AWS MediaLive上での24時間365日FASTチャンネルのオーケストレーション |
| チャンネルあたりの入力アタッチメント | 1つの動的入力 + 1つのスレート(+ オプションのSRT) — 20入力の制限を大幅に下回る |
| プログラムごとの識別 | すべてのアクション名に埋め込まれた8バイトの16進数programId |
| スレートフィルしきい値 | 6秒以上のギャップはスレート切り替えアクションを受け取る |
| スケジュール容量 | チャンネルあたり1500アクション (AWSの厳格な制限、事前検証済み) |
| 自己修復 | デプロイごとに孤立アクションのスイープが実行される |
| ステータス | 本番環境で稼働中 |
ビジネス上の問題
FASTチャンネルは24時間365日稼働するサービスです。オペレーターは1週間または1ヶ月分のユニークなビデオを事前にスケジュールし、チャンネルはそれらを正確な秒で再生する必要があります。具体的には、ソースをクリーンに切り替え、ウォーターマークを安定させ、広告ブレイクキューを発行し、あらゆるギャップを放送局ブランドのスレートで埋めます。視聴者は、黒いフレーム、停止したロゴ、または先週のプログラムがループしているのを見るべきではありません。
このオーケストレーションは、オペレーターが行うすべての操作に耐えなければなりません。例えば、明日の番組編成の編集、週半ばでの広告ブレイクの追加、単一の失敗したプログラム後の再デプロイ、『Deploy』を2回クリックするような競合操作などです。システムは、すべての変更をライブチャンネルに対してアトミックに適用するか、あるいはクリーンに回復します。『チャンネルが誰も理解できない状態になった』というのは、ライブ配信中のインフラストラクチャでは許容される結果ではありません。
60秒でわかるMediaLive入門
AWS MediaLiveは、長時間稼働するクラウドエンコーダーです。1つ以上の入力(ソースストリームまたはファイルURL)と、時間指定されたアクション(この入力に切り替える、このオーバーレイをオンにする、このキューを挿入するなど)のスケジュールを与えます。エンコーダーは永続的に稼働し、スケジュールされたタイムスタンプでアクションを実行し、視聴者のプレーヤーが消費するHLSマニフェストを出力します。
以下の2つのAWSの制限が、ダウンストリームのすべてを形作ります。
- チャンネルあたり20の入力アタッチメント。 厳格な制限です。ビデオごとに1つの入力で単純に設計された「人気映画トップ20」チャンネルは、21番目の映画でこの上限に達してしまいます。
- チャンネルあたり1500のスケジュールアクション。 これも厳格な制限です。各プログラムは複数のアクション(入力切り替え、レンディションごとのウォーターマーク、広告キュー、スレート切り替え)で構成されるため、実際の最大値は一度に数百プログラムに近くなります。
これら両方の制限は、ユニークなコンテンツを無期限に再生することを想定された24時間365日稼働のチャンネルにとって重要です。
単純なアプローチが失敗する理由
明白な方法はそれぞれ異なる形で破綻します。
- 「プログラムごとに1つの入力」では、初日で20アタッチメントの上限に達してしまいます。さらに追加するにはチャンネルの再作成が必要であり、チャンネルの再作成には60〜90秒かかり、その間ライブストリームは中断します。24時間365日稼働のサービスでは許容できません。
- 「デプロイごとにチャンネルを再作成する」。デプロイのたびに同じ問題が発生します。番組が変更されるたびに視聴者はサービス中断を経験します。これは、ライブであるべきチャンネルにとっては現実的な選択肢ではありません。
- 「1週間全体を1つの巨大なループファイルに事前エンコードする」。編集モデルを破壊します。明日の編成を並べ替えたいですか?1週間全体を再エンコードしてください。広告を挿入したいですか?再エンコードしてください。FASTチャンネルがビジネスとして機能する理由全体は、動的な番組編成とブレイクごとの広告挿入にありますが、すべてを1つのファイルに焼き付けると、これら両方が不可能になります。
- 「複数のチャンネルを並行して実行する」。プレーヤーがそれらを切り替える必要があり、AWSのコストが増大し、オーディオ、MediaPackage、CDNパイプラインが分断されます。問題を解決するどころか、増幅させてしまいます。
私たちに残された手段は、AWSが文書化している単一の機能、すなわち動的入力における$urlPath$プレースホルダーと、その上にどのようなオーケストレーションでも自由に構築できることでした。
これが重要な理由。重要なのは$urlPath$を使うことではありません。AWSがそれを文書化しています。重要なのは、部分的なデプロイ、オペレーターによる編集、同時実行のリトライに耐え、ライブチャンネルをUIから誰も説明できない状態に陥らせることなく、その上に自己修復型のオーケストレーション層を構築することです。
私たちのソリューション
1つのMediaLiveチャンネル。正確なURL $urlPath$にリンクされた1つの動的入力。ギャップを埋めるための1つのスレート入力。各プログラムの境界で、InputSwitchScheduleActionSettingsアクションが、そのプログラムの実際のS3パスで動的入力のURLを上書きします。チャンネルは新しい入力アタッチメントを必要とせず、再起動も不要で、オフラインになることもありません。
興味深い作業は、その1つの入力の上で動作するオーケストレーション層です。具体的には、失敗後に孤立したアクションをクリーンアップできるように各アクションにプログラムIDを付与すること、視聴者がホールドフレームを見ることがないように6秒以上のギャップをスレートで埋めること、オーバーレイがバッファリングフレームを通して点滅しないように各入力切り替え後に適切なタイミングでウォーターマークをアクティブ化すること、そしてAWSでデプロイ中に失敗するのではなくUIで失敗するように1500アクションの制限に対して事前チェックを行うことです。
図1・チャンネルアーキテクチャ
アーキテクチャ
- スレート入力 — プログラム間のギャップを埋めるためにチャンネルにアタッチされた静的アセット。
- 動的入力 — Sources: [{ Url: "$urlPath$" }]、Type: MP4_FILEで一度作成されます。URLはプレースホルダーであり、実際のパスはスケジュール時にプログラムごとに提供されます。
- Lambdaオーケストレーター(fastChannel-lambda-fun/index.js) — チャンネルに表示される各アクティビティを所有します。プログラムごとに8バイトの16進数programIdを生成し、関連するすべてのアクションにそれをタグ付けします。
- MediaLiveスケジュール — 1つのBatchUpdateScheduleCommandを通じて流れるアクションの単一の順序付きリスト。AWSにより1500アクションに制限されています。
- バックエンド事前チェック(schedule.service.ts) — すべてのデプロイ前にLambdaのGET_SCHEDULE_COUNTを呼び出し、新しいアクションが上限を超過する場合は処理を拒否します。
孤立スイープ(sweepIncompleteProgramGroups) — デプロイごとに実行されます。programIdでアクションをグループ化し、アンカーとなるinput-switchアクションが欠落しているグループを削除します。
主要なエンジニアリング上の決定
1. プログラムごとに1つの動的入力、1つのURLオーバーライド
動的入力はSources: [{ Url: "$urlPath$" }]で作成されます。各プログラムの境界で、LambdaはInputSwitchScheduleActionSettingsアクションを発行し、UrlPath: [program.videoUrl](そのプログラムのMP4の実際のS3 URL)を提供します。MediaLiveは実行時にプレースホルダーを置き換え、実際のソースから取得します。
これで、1つの入力アタッチメントがチャンネルが再生するすべてのプログラムに対応し、チャンネルの寿命にわたって実質的に無制限のユニークなビデオを提供できるようになりました。これは、特定の時点におけるデプロイごとのスケジュールアクションの制限によってのみ制約されます。20入力の制限はもはや考慮すべき制約ではなく、新しいコンテンツを追加するためにチャンネルを再起動する必要もありません。
図2・動的入力URLオーバーライド

意図的に行ったトレードオフ。動的入力はURLを事前に検証しません。MediaLiveは切り替え時にのみプレースホルダーを解決するため、404エラーはデプロイ時の拒否ではなく、ストリーム側のエラーとして現れます。私たちは、1つの入力というアーキテクチャのシンプルさと引き換えに、そのコストを受け入れています。バックエンドの個別のffprobe検証により、デプロイ前に不正な形式のソースが検出されます。
2. プログラムタグ付きアクション名による自己修復
Lambdaによって発行されるすべてのアクションは、プログラムの8バイト16進数programIdをその名前に含んでいます。例えば、input-switch-${programId}、watermark-on-${programId}-${rendition}、ad-break-start-${programId}-${i}、program-end-${programId}、slate-switch-${programId}といった形です。
すべてのデプロイ後、sweepIncompleteProgramGroupsは現在チャンネル上にあるすべてのアクションをリストアップし、埋め込まれたprogramIdによってそれらをグループ化し、アンカーとなるinput-switchアクションが欠落しているグループを削除します。これは、失敗した部分的なデプロイ、競合する編集、およびプログラムのアクションが中途半端に残り、チャンネルを不安定な状態にする可能性のあるその他の状況に対するクリーンアップパスです。
アクション名のエンコーディングは、IDメカニズム全体を構成しています。MediaLive自体には「プログラム」という概念がありません。オーケストレーション層が命名規則を通じてそれを投影しています。
これが重要な理由。すべてのアクション名にプログラムIDが埋め込まれていなければ、孤立スイープはどのアクションが一緒であるべきかを知る方法がありません。アクションレベルのクリーンアップは、多すぎる(チャンネル全体のリセット)か、少なすぎる(オフにならないウォーターマークが残る)かのいずれかになってしまいます。命名規則がそのままデータモデルなのです。
3. 6秒スレートルール
プログラムが完全に隣接することは稀で、あるMP4の終わりと次のスケジュールされたプログラムの開始の間には、常に数秒のギャップが生じます。オーケストレーションは、そのギャップが6秒以上(MIN_SLATE_GAP_MS = 6000)である場合に、`slate-switch`アクションを発行します。
このしきい値は恣意的なものではありません。MediaLiveは、任意の2つのスケジュールアクション間に最低5秒の間隔を義務付けており、次のプログラムの`input-switch`よりも短い間隔で`slate-switch`を発行すると拒否されます。6秒ルールは、MediaLiveが必要とするギャップを提供するとともに、オーケストレーション層に1秒間のクロックドリフト安全マージンを与えます。6秒未満の場合は、デプロイの拒否を危険にさらすよりも、前のプログラムの最終フレームを一時的にフリーズさせます。
4. 測定されたアクティベーション遅延を伴うレンディションごとのウォーターマーク
ウォーターマークは、出力レンディション(1080p、720p、480p、360p)ごとに発行されるStaticImageOutputActivateアクションであり、1プログラムあたり4つのアクションとなります。各アクションは、そのプログラムの`input-switch`から1,500ミリ秒後に発火します。
遅延が存在するのは、入力切り替え後の最初のフレームがまだバッファリングされているためです。正確な切り替えの瞬間にオーバーレイをアクティブ化すると、まだ完全にレンダリングされていないフレームにオーバーレイが描画される際に短いちらつきが発生する可能性があります。1,500ミリ秒は、テストにおいて4つのレンディションすべてで一貫してクリーンなアクティベーションを生成した値です。これは測定された定数であり、MediaLiveで文書化されたパラメータではありません。また、コードの1箇所に集中しているため、将来の調整は1行の変更で済みます。
意図的に行ったトレードオフ。レンディションごとのオーバーレイアクションは、単一のグローバルオーバーレイと比較して、アクション数が4倍になります。私たちはこのコストを受け入れています。なぜなら、レンディションごとのパスにより、MediaLiveが単一のオーバーレイを4つすべてにダウンスケールするのではなく、各出力がそのピクセル寸法に正確に合ったウォーターマークを得られるからです。その結果、SD出力ではロゴが視覚的に鮮明になり、1500の上限が問題になるまでに数百のプログラムに対応する十分なアクション予算が残ります。
5. UIで事前チェックされる1500アクションの制限
AWSはMediaLiveチャンネルのスケジュールアクションを1500に厳格に制限しています。プログラムあたり約7~8アクション(入力切り替え + 4つのウォーターマーク + 2つの広告キュー + 時折のスレート)で、チャンネルは広告密度とスレート頻度に応じて約180~200のアクティブなプログラムを保持します。これは長期間のデプロイにとって実際の制約であり、正確な数はプログラムごとの複雑さに依存します。
すべてのデプロイ前に、バックエンドはLambdaのGET_SCHEDULE_COUNTを呼び出します。これはDescribeScheduleCommandを介してチャンネル上のライブアクションをカウントし、{ liveCount, capacity: 1500 }を返します。もしliveCount + (newPrograms × 8)が1500を超過する場合、バックエンドはMediaLiveに何も送信する前に、正確な許容数を伴うSCHEDULE_ACTION_CAP_EXCEEDEDをスローします。オペレーターはUIで制限を確認し、まず過去のプログラムをクリアするよう指示されます。デプロイが中途半端に失敗することはありません。
図3・あるプログラムのアクションタイムライン

結果
- 1つのMediaLiveチャンネルが、その寿命にわたって実質的に無制限のユニークなプログラムを、1つの動的入力アタッチメントで提供します。20入力の制限はもはや考慮すべき制約ではなく、任意の時点での容量は入力制限ではなくスケジュールアクションの制限によって管理されます。
- チャンネルオーケストレーションは自己修復型です。すべてのデプロイは孤立スイープで終了するため、失敗した部分的なデプロイによってスケジュールが矛盾した状態になることはありません。
- スケジュール容量は制限されており、可視化されています。オペレーターは、デプロイ中にAWSによる不透明な拒否としてではなく、クリックする前にUIで1500アクションの上限を確認できます。
- プログラムIDの命名規則が全体の識別層であり、それは文字列です。新しいインフラ、追加のストレージ、依存関係は一切ありません。MediaLiveのフラットなアクションリストへの「プログラム」の可能な限り最もシンプルな投影です。
テクノロジースタック: AWS MediaLive・AWS Lambda・NestJS・MongoDB・TypeScript・Node 18・AWS SDK v3(@aws-sdk/client-medialive)

