SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

今日からできる! 動画配信基盤システム構築

Webブラウザ上で動画が再生される流れを追ってみよう~MSE/EMEを用いた動画の再生~

今日からできる! 動画配信基盤システム構築 第6回

  • X ポスト
  • このエントリーをはてなブックマークに追加

hls.jsの流れを追ってみる

おさらい

 前回の記事では、hls.jsをこのように利用していました。

var hls = new Hls({
  "debug": true
});
hls.loadSource("playlist.m3u8");
hls.attachMedia(video);

 それではプレイリストファイルが読み込まれて動画が再生されるまでの流れを追ってみましょう。

 といっても1行ずつ追っていくのは本質的ではないので、映像データが読み込まれていく流れをポイントごとに解説していきます。

 基本的には、前回ffmpegを使ってmp4ファイルからm3u8ファイルを生成した流れの逆を辿っていくことになります。

プレイリストファイルのパース

 プレイリストファイルがloadSourceされると、MANIFEST_LOADINGイベントが発火されます。hls.js内ではイベント駆動で処理が行われていくので、それを念頭に置くと流れが追いやすいです。

 playlist-loaderのonManifestLoadingによってマスタープレイリストがパースされることで、levelsという名前でABRの対象となるストリームの一覧と、各ストリームの情報(ビットレートやコーデック情報)が得られます。

 この時にチャンクに関する情報が記載されたプレイリスト(levelPlaylist)のurlも得られるので、マスタープレイリストとほぼ同様の手順でパースされていき、チャンクファイルのURLが得られます。

src/loader/playlist-loader.ts
private load(context: PlaylistLoaderContext): void {

  (中略)

  // マスタープレイリストか、チャンクに関する情報が記載されたプレイリストかを判定
  if (
    M3U8Parser.isMediaPlaylist(string) ||
    context.type !== PlaylistContextType.MANIFEST
  ) {
    this.handleTrackOrLevelPlaylist(
      response,
      stats,
      context,
      networkDetails || null,
      loader,
    );
  } else {
    this.handleMasterPlaylist(response, stats, context, networkDetails);
  }

(中略)

private handleMasterPlaylist(
  response: LoaderResponse,
  stats: LoaderStats,
  context: PlaylistLoaderContext,
  networkDetails: any,
): void {
  const hls = this.hls;
  const string = response.data as string;

  const url = getResponseUrl(response, context);

  const parsedResult = M3U8Parser.parseMasterPlaylist(string, url);
    
  (中略)
    
  // マスタープレイリストをパースした結果が得られる
  const {
    contentSteering,
    levels,
    sessionData,
    sessionKeys,
    startTimeOffset,
    variableList,
  } = parsedResult;

トランスマックス

 チャンクファイルがロードされると、トランスマックスという処理が走ります。コンテナ形式を移し替える処理のことで、今回はMPEG-TS形式のチャンクをfMP4形式にしています。その結果、MSEのSourceBufferで受け付けられるようになります。

src/controller/stream-controller.ts
protected _handleFragmentLoadProgress(data: FragLoadedData) {

  (中略)

  const transmuxer = (this.transmuxer =
    this.transmuxer ||
    new TransmuxerInterface(
      this.hls,
      PlaylistLevelType.MAIN,
      this._handleTransmuxComplete.bind(this),
      this._handleTransmuxerFlush.bind(this),
    ));

  (中略)

  transmuxer.push(
    payload,
    initSegmentData,
      audioCodec,
    videoCodec,
    frag,
    part,
    details.totalduration,
    accurateTimeOffset,
    chunkMeta,
    initPTS,
  );
}

 そして出来たfmp4(arrayBuffer型)をMSEのSourcebufferにappendBufferすることで、再生に必要なチャンクのデータが集まりきります。これによって、MediaSourceオブジェクトとして動画再生の準備が整います。

src/controller/buffer-controller.ts
private appendExecutor(data: Uint8Array, type: SourceBufferName) {

  (中略)

  // sb は音声や映像などのtrackごとのSourceBufferとして定義されています
  sb.appendBuffer(data);
}

MediaSourceとvideo要素の紐付け

 HTML上で動画を再生させるために、チャンクが格納されたSourceBufferとvideo要素が紐づいている必要があります。

 まずは、受け取ったvideo要素からmediaSourceを取得します。そのmediaSourceにaddSourceBufferすることで、紐づいたSourceBufferが得られます。

 先ほどappendBufferしてチャンクを格納したのは、ここで得られたSourceBufferに対してだったというわけですね。

src/controller/buffer-controller.ts
private onMediaAttaching(
    event: Events.MEDIA_ATTACHING,
    data: MediaAttachingData,
  ) {

  (中略)

  // hls.attachMedia(video); で渡したmedia要素からmediaSourceを取得
  const ms = (this.mediaSource = data.mediaSource || new MediaSource());

(中略)

private createSourceBuffers() {

  (中略)

  const sb = mediaSource.addSourceBuffer(
    mimeType,
  ) as ExtendedSourceBuffer;

  (中略)
  // 先述した音声や映像などのtrackごとのSourceBufferとして持っておく
  track.buffer = sb;

 これで、hls.jsを介してMSEを用いた動画再生が始まります。

次のページ
hls.jsを用いて、もう少し色々なことをしてみる

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
今日からできる! 動画配信基盤システム構築連載記事一覧

もっと読む

この記事の著者

今西 勇太(合同会社DMM.com)(イマニシ ユウタ)

 2021年にDMM.comへ新卒入社。配信基盤グループで動画再生プレイヤーのフロントエンド開発・運用を担当しています。現在はThink! Frontendの主催や、PS4/5, テレビ向けのDMM TV開発に携わっています。日常系アニメが好きです。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/21814 2025/07/30 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング