概要
Three.jsでHTMLVideoElement
をThree.js空間の特定のオブジェクトのテクスチャとして扱う際に複数の動画を任意のタイミングで切り替える際に切り替え時に稀に動画再生がされない問題に悩まされたので、対策として有効だったことを備忘録として記します。(検証端末:モバイルブラウザ(Android Chrome1049, iOS Safari))
Three.js空間上で不必要になったインスタンスの解放処理
Three.jsで作成したインスタンスは自動的には解放されないので、明示的に解放処理を入れてください。
私の場合は、動画の切り替えの際に前回の動画を貼り付けているテクスチャに対してThree.jsのtexture.dispose()することで対処しました。これをしない場合、iOS SafariでWebGL context lost
エラーに苦しめられました。
HTMLVideoElementのpreload
属性を設定
結論からいうと以下のようにHTMLVideoElementのpreload
属性をmetadata
に設定してください。
const videoElement: HTMLVideoElement = document.createElement('video')
videoElement.preload = 'metadata'
HTMLVideoElementのpreload
属性にはnone
|metadata
|auto
の3つがあります。このpreload
属性は、HTMLが読み込まれた場合に動画がデータをどこまで読み込むかの設定になります。preload
属性を何も設定しない場合は、auto
が設定され可能な限り複数の動画のプリロード時にHTMLVideoElement.play()が実行できるように読み込みがされるようです。metadata
に設定された場合は、動画のサイズ、トラックリスト、再生時間をプリロードするようです。また、none
に設定された場合は、プリロードがされず動画再生のタイミングで全ての動画データを読み込むようです。
今回のように複数動画がある場合にauto
を設定すると以下のようにエラーが発生します。
video MEDIA_ERR_DECODE occurs randomly
HTML5の仕様書によるとこのエラーはメディアリソースが使用可能であることが確認された後、メディアリソースをデコードする際に何らかのエラーが発生した。
場合に発生するようです。(エラー詳細)
HTMLVideoElementのリソースの解放処理
ブラウザ上で動画を複数扱う場合は、以下のように前回の動画リソースを削除することが推奨されています。具体的には、以下のようにしてください。
const videoElement: HTMLVideoElement = document.createElement('video')
videoElement.src = 'prevVideoSource'
...// 何らかの処理
// 前回の動画が必要無くなったタイミングでリソースの解放処理をする
videoElement.src = ''
// 今回使う動画リソースの登録
videoElement.src = 'currentVideoSource'
おわりに
最近は、重いコンテンツのWebアプリを作る機会が増えてきたこともあり、今回のようにブラウザでも解放処理が必要だったりと面白いですね。