WebGL環境でWebM形式の動画再生に対応したいの…
再生できない時だけMP4動画に切り替えたりできないの?
JavaScriptのネイティブコードを書く事になるけどフォールバックの仕組みを実装すれば良いわ。
UnityのWebGLビルドで複数形式の動画再生をフォールバックで対応する方法の解説記事です。
本記事の内容を実践すると、ブラウザによって最適な形式の動画を選択して再生することが可能になります。
例えば、HTMLの<video>タグによるWebM形式動画の再生では、Opera miniは非対応、iOS版Safariは部分的に対応というステータスになっています。
参考:“WebM” | Can I use… Support tables for HTML5, CSS3, etc
このような場合、WebM対応ブラウザではWebM形式動画、非対応ブラウザではMP4形式動画にフォールバックして再生させれば良いです。
これは、HTMLで言う<video>と<source>タグによるフォールバックに似ています。
参考:<video>: 動画埋め込み要素 – HTML: ハイパーテキストマークアップ言語 | MDN
本記事では、このように複数形式の動画再生をフォールバックで対応させる方法を解説していきます。動画はVideo Playerコンポーネントで再生するものとします。
Video PlayerはUnityバージョンによっては特定プラットフォームのブラウザで再生されない不具合があることにご注意ください。
iOSやAndroidなどモバイル環境に対応する場合は、Unity 2022.1以降を使用する必要があります。
- Unity 2023.1.8f1
目次 非表示
前提条件
動画再生にはVideo Playerコンポーネントを使用して再生するものとします。
例では、Quadオブジェクトを配置し、同オブジェクトにVideo Playerコンポーネントをアタッチし、Render ModeをMaterial Overrideに設定し、マテリアルテクスチャに動画の内容を反映するものとします。
これはRender ModeをRender Textureに設定し、Render Texture経由で描画する方法でも問題ありません。
Video PlayerコンポーネントのPlay On Awakeは予めOFFにしてください。
WebGL環境におけるVideo Playerの基本的な使い方は、以下記事で解説しています。
実装の流れ
フォールバックによる動画再生の流れは次のようになります。
- 動画URLと動画形式のペアをリストとして用意する
- リストの先頭から順に次の処理を繰り返す
- 動画形式がブラウザでサポートされているか判定
- 再生可能なら再生、そうでなければ次の要素に進む
- 正常に再生出来たら処理を終了、そうでなければ次の要素に進む
- 全ての要素の動画再生に失敗したら、再生不可とする
上記の実装の流れについて順番に解説していきます。
動画形式がブラウザでサポートされているかチェックする
JavaScript側での実装になりますが、HTMLMediaElement.canPlayTypeメソッドで判定できます。
引数には、「video/webm」「video/mp4」などの動画形式の文字列を指定します。
戻り値は、次の3種類です。
- 空文字 – 再生不可
- probably – おそらく再生可能
- maybe – 再生可能かどうかの十分な情報なし
したがって、空文字の場合は再生不可、それ以外の場合は実際にVideo Playerなどで動画再生するまでは分からないとみなせば良いでしょう。
このメソッドはJavaScriptネイティブで使う必要があるため、Unityから使うには次のようにjslibスクリプトとして実装する必要があります。
Unity 2021.2以降からは、C#から引数で受け取った文字列はUTF8ToString関数を使うように変更されています。
参考:Interaction with browser scripting – Unity マニュアル
Pointer_stringify関数は将来的に廃止される予定なので、古いUnityバージョンからアップグレードした場合などは、この処理を置き換える必要があります。
UnityのC#スクリプトからは、次のように呼び出して使います。
Video Playerで対象動画を再生する
ブラウザで再生できる可能性がある結果が得られた場合は、Video Playerコンポーネントに対して動画を設定して再生すれば良いです。
ここで正常再生できるとエラーにはなりませんが、失敗するとVideoPlayer.errorReceivedプロパティからエラーイベントが通知されます。
参考:Video.VideoPlayer-errorReceived – Unity スクリプトリファレンス
ただし、当環境でビルドして検証したところ、同一の動画に対して2度エラー通知が来る状況でしたので、次のように特定メッセージの時だけフォールバックの処理に進むこととします。
実際に拾うエラーメッセージは次の文言になっています。
フォールバックの実装
前述の動画形式のサポートチェックで結果が空文字だった場合、または動画再生に失敗してエラー通知を受け取った場合は再生不可であることが確定したと判断できます。
この場合、次の動画に対して同様にサポートチェック→動画再生の順に試みます。
これを成功するまで繰り返し、再生候補の動画が無くなったら再生失敗とみなします。基本的に最終候補はすべてのブラウザで絶対再生可能な動画であることが望ましいです。
サンプルスクリプト
ここまでの流れを踏まえ、動画形式のサポートチェック、動画再生、およびフォールバックを全て実装した例を示します。
スクリプトが2つあることにご注意ください。
上記スクリプトをそれぞれ次のようなパスに保存してください。
- FallbackVideoPlayer.cs
- Assets配下のPlugins以外のフォルダ
- 例 : Assets/Scripts/FallbackVideoPlayer.cs
- CanPlayType.jslib
- Plugins配下のフォルダ
- 例 : Assets/Plugins/WebGL/CanPlayType.jslib
以下、配置パスの例です。
参考:プラグインのインポートと設定 – Unity マニュアル
保存したら、適当なゲームオブジェクトにFallbackVideoPlayerをアタッチし、インスペクターより次の項目を設定してください。
- Video Player – Video Playerコンポーネント
- Videos – フォールバック対象の動画情報
- Format – 動画形式(video/webm、video/mp4など)
- Url – 動画のURL
異なるドメイン配下のパスとして動画ファイルを配置する場合、CORSの設定を正しく行う必要があります。
以下、設定例です。
設定が完了したら、一度Unityエディタで正しく再生できるか確認することをお勧めします。
本記事のサンプルはエディタ上でも再生できるように設計しています。
本記事では、WebGLビルドした後、同一ドメインのサーバーにビルドファイルを配置してプレイするものとします。
以下、サーバー側の配置例です。
実行結果
URLを正しく設定し、CORSの設定も問題なくできれば、次のように動画が再生されるようになります。
例えば、最初の候補の動画(例ではWebM形式)が再生できなかった場合、次の候補の動画(例ではMP4形式)を再生するような挙動になります。
例ではわかりやすさのため、動画形式の文字列を下に表示しています。
正しく再生されない場合、ブラウザのコンソールログを確認するなどで原因を特定する必要があります。
例えば、CORS設定が正しくない場合は次のようなエラーが出ます。
URLが正しくないなどで動画が見つからない場合は次のような404エラーが出ます。
また、iOSなど主にモバイルプラットフォームでのみ再生できない場合は、Unityバージョンが2021以前であるなどの要因で、Video Player側の不具合で再生不可になっている可能性があります。
スクリプトの説明
.jslibファイル側では、ブラウザチェックのために、まず<video>タグを生成します。
Unity C#側から受け取る文字列は、UTF8ToString関数で変換する必要があります。
そして、得られた結果の文字列をUnity C#が解釈できる形式に変換するため、以下処理でバッファを確保して結果を格納しています。
上記処理が走る度に、タグ生成や結果の格納用バッファ確保などによりGCが発生することにご注意ください。
例えば<video>タグの生成を抑えたい場合は、初回だけタグを生成してグローバル変数に格納しておき、2回目以降はそのタグを使いまわすといった方法が考えられます。
上記関数は、Unity C#側ではDllImport属性でインポートして使います。
Unityエディタ時でも動作するように、WebGLのビルド環境以外では常に「maybe」を返すダミー処理としています。
Unity C#側では、前準備としてVideoPlayerの動画再生に失敗したときにエラーを受け取れるようにするため、次の処理でコールバックを登録しています。
参考:Video.VideoPlayer-errorReceived – Unity スクリプトリファレンス
動画再生時では、まずブラウザでのサポートチェックを実施します。その後、再生できる可能性がある場合だけVideoPlayer.Playメソッドで再生を試みます。
再生できない場合は次の候補の動画にフォールバックします。
VideoPlayerによる再生でエラーが発生した場合は再生不可とみなします。これは、前述のVideoPlayer.errorReceivedイベントとして受け取ります。
このコールバックでは、次の候補の動画にフォールバックする処理を行っています。
1つの動画再生では複数のエラー通知が発行される可能性があるため、次の内容のエラーだけ通過させるように対策しています。
ブラウザサポートのチェックを行う必要性
ここまで解説した方法では、ブラウザの動画形式サポートチェック→動画再生という順に処理を行っていました。
実は、動画形式のサポートチェックを行わなくてもフォールバック再生のロジックは実装可能です。
ただし、その場合は非対応動画にも関わらず動画をダウンロードする処理が走ってしまいます。これは、無駄に通信帯域を消費することに繋がります。
以下は、HLS形式動画の再生に失敗してWebM形式動画にフォールバックする例です。
事前に動画形式のサポートチェックを行う事で、もし再生不可の動画ならダウンロードせずに次の動画にフォールバックさせる事が可能になります。
HLSの場合は、動画形式に「application/vnd.apple.mpegurl」を指定すれば良いです。
WebGL以外のプラットフォームでの挙動
本記事で解説した方法は、WebGL以外の環境でも動作します。
ただし、WebGL環境とは違い、必ず動画のダウンロード処理が走ることにご注意ください。
さいごに
WebGL環境における動画のフォールバック再生は、ブラウザの動画形式サポートチェック→サポートされていれば動画再生を試みる→エラーなら次の候補の動画に対して前述の処理を実施するというロジックで実現できます。
JavaScriptコードを書く必要がありますが、無駄な通信帯域を消費しないためにはブラウザのサポートチェックを挟むのが望ましいです。
サポートチェックは必ず再生可能を保証するものではなく、実際に動画再生してエラーにならないかのチェックも必要な事にご注意ください。