🎮

Nuxt3 + video.js で Chrome でストリーミング再生ができない問題に遭遇したので hls.js を使って解決した

2023/03/29に公開

こんにちは。

業務で Nuxt3 + TypeScript で作られたプロダクトに、hls.js を使って動画をストリーミング再生できる機能を実装したのでその内容について書きたいと思います。

はじめに

元々、ストリーミング動画を使用したプロダクトを提供しており、以前は video.js を使っていました。しかし、Nuxt3 にアップデートしたところ、Chrome で動画が再生できない問題に直面しました。調査のため、コンソールやネットワークも確認しましたが、特にエラーらしきものは見つかりませんでした。調べてみても、この問題についての解決方法が見つからなかったため、別の方法を模索することにしました。

そこで、video.js の代替となるものがないか色々調べてみたところ、hls.js というものを見つけました。

HLS

HLSは、Appleによって開発されたストリーミングプロトコルであり、動画やオーディオの配信に使用されます。
HLSは、動画を短い時間(通常は2〜10秒)に分割したチャンクにして、これらのチャンクを別々のファイルとして配信することにより、ストリーミング中に動画を再生するために必要なデータのみをダウンロードすることができます。

(参考
HLS(HTTPライブストリーミング)とは - 意味をわかりやすく
HTTPライブストリーミングとは?| HLSストリーミング

Can I Use hls ?

Can I Use で hls のサポート状況を見てみると、iOS 系は問題無さそうですね。ただし、特に PC のブラウザはサポートされていないことがわかります。

https://caniuse.com/?search=hls

hls.js

経験上、動画に関するプロダクトは扱ったことがなかったので知らなかったのですが、Weekly Download 数も40万前後、last publish も 3 days ago と結構活発に開発が行われていそうですね。

https://www.npmjs.com/package/hls.js

下記はトレンドの推移。
video.js よりは少ないものの、ほぼほぼ2強という感じです。

https://npmtrends.com/hls-parser-vs-hls.js-vs-m3u8-vs-video.js-vs-videojs-vs-videojs-contrib-hls

video.js の代わりにこちらを使って実装していきます。

実装

環境

実装の前に、開発環境です。一部抜粋します。

Nuxt.js は v3.2、hls.js は実装時期で最新版だった v1.3.3 を指定しています。
また、@types/hls.js として型定義ファイルも提供されていたのでこちらもインストールします。

https://yarnpkg.com/package/@types/hls.js

"dependencies": {
  "@types/hls.js": "^1.0.0",
  "hls.js": "^1.3.3",
},
"devDependencies": {
  "nuxt": "3.2.0",
  "typescript": "^4.7.3",
}

実装

それでは実際に実装してきます。
まずは動画を表示する要素のコンポーネントを作ってみます。

<video> タグは動画を再生するためのタグで、ref 属性によって video タグにアクセスできます。このコンポーネントの例では、他にボタン要素があり、そのボタンを押下することによって handleVideoPlayClick が発火するようになっていますが、どのように動画を再生するかはその時々で決めてあげれば良いです。

また、hls.js がサポートされている場合は、Hls クラスのインスタンスを作成し、loadSource メソッドで URL を読み込み、attachMedia メソッドでビデオ要素と関連付けます。

video.canPlayType(type)は、ブラウザが特定のメディアタイプを再生できるかどうかを判断するためのメソッドです。このメソッドには、typeという引数があり、この引数には再生をサポートするかどうかを確認するメディアタイプを文字列で渡します。メディアタイプは MIME タイプの形式で指定します。
application/vnd.apple.mpegurl は、Safariなど、HLS に対応した一部のブラウザによってサポートされていて、HLS がネイティブサポートしている場合はそのまま URL が video.src にセットされます。

<script lang="ts" setup>
import Hls from 'hls.js'

const videoRef = ref<HTMLVideoElement>()

const handleVideoPlayClick = () => {
  if (!videoRef.value) return

  const video = videoRef.value

if (Hls.isSupported()) {
    const hls = new Hls({ enableWorker: false })
    hls.loadSource('http://example.com/hls/stream.m3u8')
    hls.attachMedia(video)
    video.play()
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = 'http://example.com/hls/stream.m3u8'
    video.play()
  }
}
</script>

<template>
  <div>
    <video
      ref="videoRef"
      width="640"
      height="360"
      controls
    />
    <button @click="handleVideoPlayClick">
  </div>
</template>

できた。

https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement/canPlayType

まとめ

今回、Nuxt3環境で動画をHTTP Live Streamingで再生するためにvideo.jsを使用したところ、再生ができない事象に遭遇しました。そこで、代替方法としてhls.jsを使用することにしました。再生ができない原因についてはまだ不明ですが、絶賛情報を募集しております。

以上

Discussion