はじめに
はじめまして。
システム開発推進部第2Gの山本です。
フロントエンドやバックエンドの開発を主に行っております。
今回は担当している音楽配信サービスの楽曲配信のCDN移行を行った話について記載します。
弊社では音楽配信サービスを提供しており、HLS (HTTP Live Streaming) を用いた楽曲のストリーミング配信を行っています。
これまでストリーミング配信時に利用していたCDN事業者のCDNサービスが終了するとの通知があったため、急ぎで新たなCDNの選定が必要になりました。
当初、CDNの乗り換え先としてAWS CloudFront を候補として検討しましたが、初回リクエスト時の HLS変換 に時間がかかることが課題となり、最終的に Fastly CDNを採用することになりました。
本記事では、CloudFrontを利用したHLS変換の仕組みを説明し、最終的にFastly CDNへ移行するまでの流れを紹介します。
旧CDNからのHLS配信について
今まで使用していたCDNでは以下のフローでHLS配信を行っていました。
旧CDNでの配信フロー
- ユーザーが CDN に楽曲をリクエスト
- CDN がキャッシュを確認し、キャッシュがあればそのまま返却
- キャッシュがない場合は、楽曲取得API へリクエストを送信し m4a 形式の楽曲を取得
- CDN が m4a を HLS (m3u8 + TS) に変換
- HLS変換後の m3u8 + TS をユーザーへ返却
- CDN が変換済みの HLS ファイルをキャッシュ
新たなCDNでも同様の配信フローを維持しつつ、HLS 変換を適切に処理できるような仕組みを構築する必要がありました。
CloudFront + Lambda@Edge + MediaConvert + S3を利用した楽曲配信
CloudFront + Lambda@Edge + MediaConvert + S3を利用したHLS変換・配信の実装について詳しく解説していきます。
AWS公式のブログを参考にさせていただきました。
弊社のシステムは AWS 上で構築されているため、以下のAWSリソースを採用することとしました。
• CloudFront:CDNとして利用。キャッシュがない場合はLambdaをトリガー。
• Lambda (Lambda@Edge):CloudFrontのオリジンリクエスト時に動作し、S3やAPIをチェック。
• MediaConvert:m4aをHLS形式(m3u8 + AAC)に変換。
• S3:変換済みのHLSファイル(m3u8 + AAC)を保存。
CloudFront経由のHLS配信フローとAWS構成図
今回構築した配信フローとAWS構成図となります。
- ユーザー が CloudFront にリクエスト
- CloudFront がキャッシュを確認、キャッシュがあればそのまま返却
- キャッシュがない場合、Lambda@Edge をトリガー
- Lambda@Edge がS3をチェックし、HLSファイル(m3u8)があれば返却
- S3にHLSファイルがない場合、楽曲取得API にリクエストし m4a ファイルを取得
- 取得した m4a を MediaConvert で HLS (m3u8 + AAC) に変換
- HLS変換後の m3u8 + AAC ファイルを S3 に保存
- HLS変換後の m3u8 + AAC をユーザーへ返却
- CloudFront が変換後の HLS ファイルをキャッシュ
Lambda@Edgeのコード
以下、Lambda の実装の中で重要な部分を抜粋して解説します。
- HLSマニフェストのレスポンス生成
HLSマニフェストファイルを適切にレスポンスさせる。function createManifestWrapper(manifestBody) {return {status: '200',statusDescription: 'OK',headers: {'access-control-allow-origin': [{ value: '*' }],'content-type': [{ value: 'application/vnd.apple.mpegurl' }],'cache-control': [{ value: 'max-age=3' }],},body: manifestBody,};} - S3からHLSマニフェストを取得
S3に変換済みのHLSファイルがあるかチェック。
HLSファイルがあればファイルを返却。
HLSファイルがなければエラーをスロー。async function getManifest(qsParams) {const s3Path = `${qsParams.musicId}/${qsParams.musicId}.m3u8`;const bucketParams = {'Bucket': 'yourHlsBucket','Key': s3Path};try {const manifest = await s3.send(new GetObjectCommand(bucketParams));return manifest;} catch (err) {err.statusCode = 404;throw err;}} - MediaConvertジョブの作成
S3にファイルがない場合、MediaConvert を利用して m4a から HLS (m3u8 + AAC) に変換。
SegmentLength: 10 により10秒単位でHLSセグメントを生成。
変換後は s3://${hlsS3bucket}/${qsParams.musicId}/ に保存される。async function createMediaConvertJob(musicUrl, qsParams) {const s3Path = `s3://${hlsS3bucket}/${qsParams.musicId}/`;const mediaConvertJobParams = {"Role": 'yourHlsMediaConvertJobIamRole',"Settings": {"OutputGroups": [{"Name": "Apple HLS","OutputGroupSettings": {"Type": "HLS_GROUP_SETTINGS","HlsGroupSettings": {"SegmentLength": 10,"Destination": s3Path}}}],"Inputs": [{"FileInput": musicUrl,"AudioSelectors": {"Audio Selector 1": { "DefaultSelection": "DEFAULT" }}}]}};const command = new CreateJobCommand(mediaConvertJobParams);return await mediaConvert.send(command);} - Lambda@Edge のメインハンドラー
exports.handler ではユーザーリクエストを処理する流れを定義する。exports.handler = async (event) => {const request = event.Records[0].cf.request;// クエリ文字列から各キーのバリデーションチェックを行い、オブジェクトとして返却const qsParams = parseQueryString(request.querystring, request.uri);try {const manifest = await getManifest(qsParams);return createManifestWrapper(manifest.Body);} catch (err) {if (err.statusCode == 404) {// APIから音源取得const musicData = await getMusicData(qsParams);// MediaConvertジョブ作成await createMediaConvertJob(musicData.url, qsParams);// HLSファイル変換中にプレイヤー側が止まらないためにイントロ用マニフェストを返却return createIntroManifest();}}};
CloudFront + Lambda@Edge + MediaConvert + S3を利用した配信の課題と解決策
課題
CloudFront + Lambda@Edge + MediaConvert + S3を利用した楽曲配信の構築は完了しましたが、初回アクセス時にHLS変換が必要になるため、初回再生時には時間がかかるという課題が浮上してきました。
CDN移行後のアクセスは、すべて初回アクセス扱いになるため、全楽曲がこの問題に直面しました。
- 初回アクセス時のHLS変換
初回アクセス時には S3 バケットに HLS 変換済みのファイル (m3u8 + AAC) が存在しません。
そのため、初回のみ m4a を HLS に変換する処理が必要になります。
HLS変換には 楽曲の長さに関わらず約10秒前後 かかり、この間ユーザーは再生を開始できないようになります。 -
変換中の再生
変換中でも一部のデータを返して再生を開始する設定もありますが、シークバーを動かすと変換が完了していない部分は再生できない という問題が発生しました。
このため、最終的にHLS変換が完了してから再生を開始する方式にしました。 -
MediaConvertのコスト
CDN移行前にHLSファイルを事前に作成し、初回アクセス時の変換を不要にすることで、再生遅延を解消することにしました。
しかし、配信楽曲数が多いため、MediaConvertを使用するとコストが大幅に増加することが判明しました。
解決策: EC2 + FFmpeg による事前HLS変換
上記の課題を解決するため、MediaConvertを使用せず、EC2上でFFmpegを利用したバッチ処理を構築しました。
1. EC2上でFFmpegを構築
2. 対象楽曲の音源ファイルを取得
3. 音源ファイルをFFmpegでHLSファイル (m3u8 + AAC) に変換
4. 変換後のHLSファイルをS3に保存
これにより、初回アクセス時はMediaConvertを使用した変換処理を不要にし、すぐに再生できるように準備しました。
ただ、CDN移行まで全楽曲分バッチ処理を流すほどの時間がなかったため、直近聞かれている曲に限定してバッチ処理を行なっており、マイナー楽曲に対しては事前変換を行なっておりませんでした。
また、新規で配信される楽曲に対しては考慮しておらず、楽曲公開前に事前変換するといった対策ができておりませんでした。
そのため、マイナー楽曲や新曲に対しては依然として、初回アクセス時のHLS変換による再生遅延の問題がありました。
Fastly CDNを利用した楽曲配信
CloudFrontを使用したHLS配信では、初回リクエスト時にMediaConvertを利用した変換処理が発生し、再生開始までの遅延が大きな課題となっていました。
この課題を解決するために 最終的にFastlyを採用しました。
Fastlyを採用した理由
Fastlyを採用した理由は以下の通りです。
- 初回再生時の遅延が少ない
CloudFrontでは初回リクエスト時にHLS変換が発生し、10秒以上の遅延が発生していたが、Fastlyではこの遅延が大幅に短縮可能。
そのため全楽曲再生遅延の問題が解決。 -
シークバーを動かした際の再生途切れを防止
CloudFrontではシーク時に未変換の部分があると再生が途切れる問題があったが、Fastlyではスムーズなシークが可能。 -
旧CDNと同様な配信フローが可能
旧CDNのようにオリジンからのm4a取得→HLS変換→キャッシュという流れを再現できる。
Fastly CDNを利用したHLS配信フロー
- ユーザーが Fastly CDN に楽曲をリクエスト
- Fastly CDN がキャッシュを確認し、キャッシュがあればそのまま返却
- キャッシュがない場合は、楽曲取得API へリクエストを送信し m4a 形式の楽曲を取得
- Fastly CDN が m4a を HLS (m3u8 + AAC) に変換
- HLS変換後の m3u8 + AAC をユーザーへ返却
- Fastly CDN が変換済みの HLS ファイルをキャッシュ
カスタムVCLについて
Fastlyでは Varnish Configuration Language (VCL) を使用してカスタム処理を記述できます。
今回は下記の内容をカスタムVCLを用いて実現しました。
• オリジンからの楽曲(m4a)の取得
• 不正再生防止策のためのパラメータ設定
fastlyに移行したことで、初回再生までに時間がかかるという問題が解決し、ユーザー体験を損なう懸念がなくなりました。
まとめ
本記事では、今まで使用していたCDN事業者のCDNサービスに伴う HLS 配信の移行プロセスについて解説しました。当初は AWS CloudFront を利用した HLS 配信を構築しましたが、初回リクエスト時に MediaConvert による変換処理が発生し、再生遅延が生じる という課題に直面しました。この問題を解決するために、以下の対応を行いました。
移行のステップと課題解決の流れ
- AWS CloudFront, Lambda@Edge, MediaConvert を利用した HLS 変換
• 初回アクセス時に HLS 変換が発生するため、再生開始までに 約10秒の遅延 が発生。
• ユーザー体験の悪化が課題となった。 -
事前変換のための FFmpeg バッチ処理の導入
• EC2上で FFmpeg を利用して HLS ファイル (m3u8 + AAC) を事前生成。
• 事前変換により初回再生の遅延を削減。
• ただし、全楽曲を事前変換する時間がなかったため、人気楽曲のみを対象とする部分的な対応となった。 -
CloudFront の代替として Fastly の採用
• Fastly では 初回リクエスト時の HLS 変換遅延が発生しない ため、ユーザー体験が大幅に向上。
• 旧CDN と同様のフローを Fastly で再現できるため、よりスムーズな移行が可能になった。
CDN の選定や HLS 配信の構築を検討している方の参考になれば幸いです。
最後まで読んでいただき、ありがとうございました。
この記事を書いた人

最近書いた記事
2025.06.06楽曲ストリーミング配信のCDN移行を行った話
2024.10.30Seleniumを使ってリリース時の動作確認を自動化したい
2024.06.10ChatGPT-4oとMacアプリで開発をより効率的に
2023.12.05ChatGPTで仕様に沿ったコードレビューをしてくれる GPTを作成してみた