目的
私が実装した動画配信の仕組みを見ながら、動画配信に必要な基本知識を理解することが目的です。
インフラ構成
- 以下に今回使用したリソースを挙げます
- CloudFront
- S3
- SQS
- EventBridge
- Cognito
- Mediaconvert
- EKS
- Aurora
-
以前は SNS + ラムダの構成でしたが、以下の理由でSQSに移行しました。
- ラムダから EKS の api を叩く方法だと、EKS が落ちたらジョブが失われてしまう
- ラムダの保守性が低い
流れ
アップロード
- ユーザは cognito にログイン
- ユーザは SDK を通じて cognito からトークンを取得します
- ユーザは、先ほどもらったアクセストークンを使って、S3 にアクセスするための identityId, STS トークンを Cognito から取得します
- ユーザはアプリに動画作成のリクエストを送ります
- もちろん、ここでは動画のメタ情報をアプリDB(Aurora)に登録するだけです
- ステータスは UserUploadPrepared にします
- 同時に、S3 のアップロードのエンドポイントをもらいます
- フロントで S3 のアップロードのエンドポイントを構築してもいいです
- ユーザは、STSトークンを使って、S3に直接動画をアップロードします
- IAM Policy の設定により、S3 内の自分のフォルダ(identityId によって判別)以外へのアップロードは禁止されています
- 本プロジェクトのフロントは、簡易的な C++ デスクトップアプリで実装したので、SDK For C++ を使いました
- TransferManager クラスを使うことで、進捗を把握するためのプログレスバーを実装することができます
- S3 へのアップロードが完了したら、S3 イベント通知により SQS キュー(video upload queue)にメッセージが送信されます
- EKS 上のワーカーが5秒間隔で SQS をポーリングし、アップロードされたファイルを処理します
- ファイルの拡張子とContent-Typeの整合性をチェック(こちらの記事を参考)
- 拡張子とContent-Typeが一致しない場合、ステータスを UserUploadInvalidPath に設定
- 整合性チェックに通った場合、まずステータスを UserUploadComplete に設定
- 画像ファイル(PNG, JPG)の場合は MediaConvert 処理をスキップして処理完了
- 動画ファイルの場合、MediaConvert の Probe API を使用して解像度、フレームレート、再生時間を取得
- 取得した情報:幅(Width)、高さ(Height)、FPS(Numerator/Denominator)、再生時間(Duration)
- MediaConvert にジョブを作成し、ステータスを MediaconvertJobCreated に更新
- ファイルの拡張子とContent-Typeの整合性をチェック(こちらの記事を参考)
- MediaConvert はアップロードされた mp4 や mov を、ストリーミング再生可能な HLS に変換して、S3 に配置します
- HLS は、各セグメントのメタデータを保持する m3u8 ファイルと、実際の動画データを持つ ts ファイルから構成されます
- EventBridge は、MediaConvert の状態変化を監視し、完了通知を SQS キューに送信します
- MediaConvert のステータスは、
["SUBMITTED", "INPUT_INFORMATION", "PROGRESSING", "STATUS_UPDATE", "COMPLETE", "ERROR", "CANCELED"]
のいずれかです(詳細はこちら)
- MediaConvert のステータスは、
- EKS 上の別のワーカーが5秒間隔で SQS をポーリングし、MediaConvert の完了通知を処理します
- 完了通知を受けたワーカーは、アプリDB(Aurora)の動画ステータスを COMPLETE などに更新します
- アプリの動画のステータスが COMPLETE になっていれば、ユーザがその動画にアクセスできます
動画再生
- ユーザはアプリから動画のメタデータを取得します
- アプリは S3 のエンドポイントから cloudfront のエンドポイントに変換して、ユーザに返します
- 必要に応じて、cloudfront のエンドポイントに署名します
- 署名付きURLと署名付きクッキーが選択肢としてありますが、HLSは複数ファイルにアクセスするため、署名付きクッキーが推奨されています
- ユーザは、取得した cloudfront のエンドポイントを使って、動画をストリーミング再生します
Youtube
署名付きURL or 署名付きクッキー
上述したように、ストリーミング再生では、複数のセグメントファイルへのアクセスが必要なので、署名付きURLではなくて署名付きクッキーが一般的に推奨されている。
しかし、Youtube のネットワークタブを見ると、署名付きクッキーではなくて、署名付きURLが使われていた。おそらく、m3u8 ファイルだけではなくて、全ての ts ファイルのエンドポイントに対しても署名しているのだと思う。m3u8 ファイル上の全ての ts ファイルのエンドポイントを署名付きURLに変更するのは、かなり技術的に難しそう。
ただ、署名付きクッキーが使えない場合は、このように実装するしかないのかもしれない。例えば、C++ の Qt ライブラリや、Unreal Engine の Media Player は、ヘッダーを持たせることができないので、どうしてもアクセスを制限したい場合は、署名付きURLを採用するしかない。
(そもそも、HLS じゃなくて DASH 使っているとか、細かい部分は違うかもしれないが、大まかなイメージはこんな感じだと思う)