概要
GCS に HTML ファイル、動画ファイルを配置し、それをクライアントから観れるようにします。
一応ありがちな Cloud CDN を経由するパターンにします。GCS の組み込みキャッシュも効きますが、ここは Cloud CDN で。
ちなみに 2024/02/23 現在 Media CDN は Google にリクエストしないと使えず、U-NEXT が本気で使っている代物で大衆化されてなさそうなので使ってません。
環境
- Windows 11 Pro
- Ubuntu 22.04 on WSL2
- Python 3.10.7
やり方
Python を使うので必要な人は適当なディレクトリを掘って pyenv で local にバージョンを指定してください。以下は一例。
mkdir hls-poc && cd hls-poc
pyenv install 3.10.7
pyenv local 3.10.7
$ cat .python-version
3.10.7
ffmpeg をインストールする
sudo apt -y update
sudo apt install ffmpeg
動画ファイルを取得する
ファイルが入り乱れるのでフォルダ内で作業します。
mkdir resource && cd resource
wget https://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_h264.mov
MOV=big_buck_bunny_720p_h264.mov
Big Buck Bunny(BBB) という動画が公開されています。感謝。ライセンスはご確認を。
ffmpeg でマニフェストとセグメントファイルを作成する
ffmpeg -i $MOV \
-c:v copy -c:a copy \
-f hls \
-hls_time 9 \
-hls_playlist_type vod \
-hls_segment_type fmp4 \
-hls_fmp4_init_filename "init.mp4" \
-hls_segment_filename "seg.%3d.mp4" \
media.m3u8
こんな感じに。元の mov ファイルは削除して大丈夫です。
$ ls
init.mp4 seg.003.mp4 seg.008.mp4 seg.013.mp4 seg.018.mp4 seg.023.mp4 seg.028.mp4 seg.033.mp4 seg.038.mp4 seg.043.mp4 seg.048.mp4 seg.053.mp4 seg.058.mp4 seg.063.mp4
media.m3u8 seg.004.mp4 seg.009.mp4 seg.014.mp4 seg.019.mp4 seg.024.mp4 seg.029.mp4 seg.034.mp4 seg.039.mp4 seg.044.mp4 seg.049.mp4 seg.054.mp4 seg.059.mp4 seg.064.mp4
seg.000.mp4 seg.005.mp4 seg.010.mp4 seg.015.mp4 seg.020.mp4 seg.025.mp4 seg.030.mp4 seg.035.mp4 seg.040.mp4 seg.045.mp4 seg.050.mp4 seg.055.mp4 seg.060.mp4 seg.065.mp4
seg.001.mp4 seg.006.mp4 seg.011.mp4 seg.016.mp4 seg.021.mp4 seg.026.mp4 seg.031.mp4 seg.036.mp4 seg.041.mp4 seg.046.mp4 seg.051.mp4 seg.056.mp4 seg.061.mp4 seg.066.mp4
seg.002.mp4 seg.007.mp4 seg.012.mp4 seg.017.mp4 seg.022.mp4 seg.027.mp4 seg.032.mp4 seg.037.mp4 seg.042.mp4 seg.047.mp4 seg.052.mp4 seg.057.mp4 seg.062.mp4
GCS バケットを用意し、CORS ポリシーを更新する
バケット作成
PROJECT=[YOUR_OWN_PROJECT_ID]
cd ..
gsutil mb gs://$PROJECT
JSON ファイル作成
[
{
"origin": ["*"],
"method": ["GET"],
"responseHeader": ["Content-Type"],
"maxAgeSeconds": 3600
}
]
ポリシー適用
gcloud storage buckets update gs://$PROJECT --cors-file=cors.json
resource フォルダをアップロードする
gsutil cp -r resource gs://$PROJECT
index.html をアップロードする
videoSrc
は GCS にアップロードした際の media.m3u8 ファイルの GCS パスに変更してください。あとはそのままで OK です。
あと、GCS は外部に公開してください。これ重要です。
<!DOCTYPE html>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<div>
<h2>HTTP Live Streaming</h2>
<video id="video" width="640" height="360" controls></video>
</div>
<script>
var video = document.getElementById("video");
var videoSrc =
"https://storage.googleapis.com/[YOUR_OWN_BUCKET_NAME]/resource/media.m3u8";
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = videoSrc;
}
</script>
gsutil cp index.html gs://$PROJECT
いったん、これで GCS にある index.html にアクセスすれば BBB が再生される状態です。確認してみてください。
以下が表示される Web ページです。すでに LB を用意した後の URL ですが、私の場合は http://34.144.254.201/index.html
にアクセスしています。(すでにこの IP アドレスは解放しています。アクセスできませんので悪しからず。)
Cloud CDN を有効化しつつ Cloud Load Balacing で公開する
今回は簡易に HTTP で公開します。
が、この手順についてはコンソール上でぽちぽちなり Terraform で管理するなりするための手順が公式ドキュメントにしっかり記載されていますので、こちらをご覧ください。
念のため確認
まずは GCS の index.html に直アクセスした場合の様子です。
200 OK が返ってきています。
次に、LB 経由で index.html にアクセスした場合の様子です。
一回目は Cloud CDN にキャッシュが保存されていないので、開いたらまず画面更新してください。
すると、以下の結果となるはずです。
304 Not Modified が返ってきています。
これはキャッシュされたリソースへの暗黙のリダイレクトです。
つまり、一回目のアクセスによって静的コンテンツは Cloud CDN(お近くのエッジキャッシュ)にキャッシュされ、二回目のアクセスは Cloud CDN のキャッシュにアクセスしています。
おめでとうございます 🎉
余談
実は、index.html はキャッシュヒットしていますが、その他の動画コンテンツは下図の通り from disk cache
、つまりお使いのブラウザキャッシュから読み込まれています。
Cloud CDN と言えどそれを支えるのは単なるサーバです。できることならサーバの負荷は減らしたいですし、動画を見る側にとってもなるべく早く観たいはずです。
なので、毎回 Cloud CDN のキャッシュサーバにアクセスするのではなく、可能な範囲でブラウザのキャッシュから読み込むようになっています。
キャッシュに興味のある方は、以下 URL にアクセスしてキャッシュが保存されている場所を確認するなり、Chrome の設定画面よりキャッシュを削除するなりしてください。
chrome://version/
キャッシュなしで試したい場合は開発者ツールの Network タブより Disable cache
を選択しましょう。毎回 200 OK で返ってくるようになります。
なぜこのチェックボックスをオンにしたらキャッシュヒットしなくなるの?と思った方、キャッシュの世界へようこそ。
参考にしたサイト
HLS.js 公式 GitHub リポジトリ
localhost で簡単に HLS 配信を Python でしてみる