株式会社ACCESSでは、2024年度の新卒研修において「メディア」をテーマとした研修を実施しました。 その研修で使用したソースコードの一部を修正のうえ公開しています。
- 2024年度メディア研修で利用したソースコード
- 研修課題の概要
- 依存関係
- 研修用1080p動画の準備
- 作業ディレクトリのセットアップ
- 1080p.mp4を複数の品質にエンコードし、セグメント分割する
- サーバーの起動手順
- 実装課題
- 解答
- 参考
5段階の解像度でストリーミング再生ができる動画プレイヤーの実装を目指します。
そのために、media-player-demo-2024/www/abr/task.js 内の3つの関数を、後述する仕様に従って修正してもらいます。
- ffmpeg
- 動作確認済みバージョン: 7.1.1
- bento4
- 動作確認済みバージョン: 1.6.0-641
- golang
- 動作確認済みバージョン: 1.22.1
動作確認は、MacBook Pro 2023(M3チップ搭載)および Google Chrome で行いました。
$ cd /tmp
$ curl -O https://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_1080p_h264.mov
$ ffmpeg -i big_buck_bunny_1080p_h264.mov -ss 00:00:00 -to 00:03:00 -r 30 1080p.mp4このプロジェクトでは、Blender Foundation によって提供されている映像作品 Big Buck Bunny(big_buck_bunny_1080p_h264.mov)を使用します。
この動画は Creative Commons Attribution 3.0 ライセンス のもとで公開されています。
著作権表記:
(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org
⚠️ 本プロジェクトには動画ファイルは含まれていません。
ユーザーが上記の手順で動画をダウンロードし、加工したものを使用する前提となっています。
$ cd /path/to/media-player-demo-2024
$ git fetch
$ git pull origin main$ cd media-player-demo-2024
$ ./script/create_dash.sh /tmp/1080p.mp4
…
DONE!-
1080p.mp4を5段階の解像度に再エンコード
この際、再生時間を示すテキストを動画に追加(出力先:media-player-demo-2024/media/mp4/*)- 作成されるファイル:
426x240.mp4,640x360.mp4,854x480.mp4,1280x720.mp4,1920x1080.mp4
- 作成されるファイル:
-
それぞれの解像度の動画ファイルをフラグメント化
- 出力先:
media-player-demo-2024/media/fmp4/*
- 出力先:
-
DASH ストリーミング用のマニフェストファイルとセグメントファイルを生成
- 出力先:
media-player-demo-2024/www/segments/*
- 出力先:
-
manifest.mpd- DASHストリーミング用のマニフェストファイル
-
video/avc1/0~4/{init.mp4 | seg-0~179.m4s}- 0: 426x240
1: 640x360
2: 854x480
3: 1280x720
4: 1920x1080 に対応 - 各ディレクトリには以下のセグメントが含まれる
init.mp4:seg-0~179.m4sを再生するために必要な情報を含むseg-n.m4s: 各ファイルは1秒分の映像データ
- 0: 426x240
-
audio/en/mp4a.40.2/{init.mp4 | seg-0~180.m4s}- セグメント数は本来180個を想定していましたが、実際には181個作成されます(課題への影響はありません)
$ ./script/run_server.sh
Enable throttling mode? 0(disable) or 1(enable): 0
Start Server without throttling mode
Please access http://localhost:8080$ ./script/run_server.sh # Ctrl+Cで一度停止後、再起動
Enable throttling mode? 0(disable) or 1(enable): 1
Start Server without throttling mode
Please access http://localhost:8080/?auto=1
...
/segments/video/avc1/0/seg-33.m4s : Sleep for 812ms
...サーバログについての補足: 例えば以下のようなログを確認した場合。
/segments/video/avc1/0/seg-33.m4s : Sleep for 812ms
これは、解像度 426x240のseg-33.m4sの取得に812msかかる予定という意味です。
周期的にVideoセグメントの取得時間が変化します。取得時間が増加し続けているタイミングもあれば、減少し続けるタイミングもあります。
実装課題の最終目的は「任意のタイミングで必要な音声・映像セグメントを取得し、解像度変更にも対応した滑らかな動画再生を行う」ことです。
以下の3つの課題をクリアすれば、5段階の解像度を用いたストリーミングプレイヤーが完成します。
実装演習課題に取り組む際のキーポイント:
-
必要なタイミングで必要な量のAudio・Videoセグメントをサーバーから取得する
- 例: 1920x1080の動画を0〜5秒再生したい場合、
init.mp4とseg-0〜4.m4sを取得する - 解像度を途中で変更する場合も、その解像度のセグメントを取得するだけでよい
- 例: 1920x1080の動画を0〜5秒再生したい場合、
-
取得したセグメントは MSE API を用いてブラウザへ渡す
- 解像度を変更した場合は、対象の解像度の
init.mp4を最初に渡す必要がある- 例:
426x240から1920x1080に切り替える場合は、まず1920x1080のinit.mp4を渡した上で、seg-n.m4sを渡す。これを行わないと再生が乱れる。
- 例:
- 解像度を変更した場合は、対象の解像度の
役割: 指示を受けたセグメント(Audio, Videoどちらも)を指すURLを生成する。
関数が呼ばれるタイミング: セグメントの取得が必要だと判断した時
function buildSegmentRequestURL(isAudio, isInitialSegment, resolutionId, lastSegmentIndexBeingLoaded);
isAudio (boolean)- true: Audioセグメントの取得指示, false: Videoセグメントの取得指示
isInitialSegment (boolean)- true: 初期セグメントの取得指示, false: 一般セグメントの取得指示
resolutionId (int, 0~4)- 0: 426x240, 1: 640x360, 2: 854x480, 3: 1280x720, 4:1920x1080 に対応
- 例えば、0が渡ってきた場合、426x240向けの解像度のセグメントを取得したいということ
lastSegmentIndexBeingLoaded (int: -1~179)- -1:
isInitialSegment:true時の未定義値 - 0~179: 取得したい一般セグメントのIndex
- 例えば、0が渡ってきた場合、seg-0.m4sを取得したいということ
- -1:
- 期待する返り値(string)
segments/で始まる、適切な相対パス- 例えば、解像度 426x240 の初期セグメントを指すURLを返したい場合は、
segments/video/avc1/0/init.mp4を返せば良い
- 例えば、解像度 426x240 の初期セグメントを指すURLを返したい場合は、
http://localhost:8080 にアクセスする。 Webサイトは表示されますが、動画再生は開始されません。
依然として動画再生は開始しません。 しかし、Google ChromeのDevtoolのNetworkタブを確認すると、選択した解像度に対応したセグメントの取得が行われていることを確認できます。
役割: 引数に渡ってきたbufferをsourceBufferに渡す
関数が呼ばれるタイミング: セグメントの取得が完了した時
function appendBuffer(sourceBuffer, buffer);
- sourceBuffer(SourceBuffer)
- buffer (ArrayBuffer)
buildSegmentRequestURL()の返り値を元に取得したセグメントデータ
- 期待する返り値 (void)
選択した動画解像度にスムーズに切り替わり、正常に動画再生がされるようになります。 ただし、解像度「自動」は機能しません。
役割: ネットワーク状況やバッファの状況を踏まえて、自動モードで再生中の解像度を決定する。
関数が呼ばれるタイミング: 解像度選択のUIにて「自動」を選択している際に、新たなセグメントを取得しようとする直前
function getOptimalResolution(playerState);
- playerState (object)
- playerState.currentTime (HTMLMediaElement.currentTime): 現在の再生時間
- playerState.videoSource (SourceBuffer): Video Source
- playerState.audioSource (SourceBuffer): Audio Source
- playerState.lastSegmentIndexBeingLoaded (int, 0~179): 取得したい最新の一般セグメントのIndex
- playerState.lastLoadedSegmentIndex (int, 0~179): ブラウザに渡した最新の一般セグメントのIndex
- playerState.resolutionId: 0: 426x240, 1: 640x360, 2: 854x480, 3: 1280x720, 4:1920x1080 に対応
- playerState.isOffline(boolean): true: 現在オフライン状態, false: 現在オンライン状態
- activeRequests (XMLHttpRequest オブジェクト list): Audio, Video セグメントの取得のためのHTTP Requestのうち、フェッチ中、もしくはフェッチ予定のリクエストの数リスト
- lengthを取得したい場合は、activeRequests.lengthとする。
- 期待する返り値(int: 0~4)
- 現在の状況を踏まえた上で、指定したい解像度のID
- 0: 426x240, 1: 640x360, 2: 854x480, 3: 1280x720, 4:1920x1080
課題3は、自由記述問題です。 動画再生を止めることなく、できる限り高解像度な動画再生を目指しましょう。
解答は、media-player-demo-2024/www/abr/solution.jsにあります。
このファイルに従い、media-player-demo-2024/www/abr/task.js を修正すると、期待される挙動の一例を確認できます。
- Media Source Extensions に関する解説(2018年6月6日投稿: 鍋島 公章)
- 2025 年のメディア研修で使用されているソースコード
- https://github.com/aAkimiShimada/media-player-demo
- 本リポジトリをベースに幾らか手を加えた実装が用意されました。2025年の研修においてはこちらが使用されています。