🍕

UnityのWebRTCのHWエンコードで使われるNvidia VideoCodecを更新してH264のレベル6.2に対応してみる

13 min read

概要

UnityのRenderStreamingを使う際、ハードウェアエンコードを有効時、内部的に使われるNvidiaのVideo Codec SDKが古く、高解像度のコーデックレベルに対応していない問題があったので、自分で差し替えを試す。その方法がうまくいったので、備忘録としてまとめる。

※1以下の内容は将来的に公式側で対応されるか、仕様変更で動かなくなる可能性がある
※2高解像度といっても現実的に動くレベルは3840x2160まで、UnityのWebRTCの制約上では4096x4096までは可能だが、明らかに動作が遅くなる

事前準備

ビルドをする前に以下のページに書かれているSDKやアプリケーションをインストールする。CUDAやVulkanSDK、cmakeのバージョンは下記ページ中のものと合わせなくても動いたので、今後仕様変更や破壊的変更がない限りは新しいバージョンでも大丈夫そう

https://github.com/Unity-Technologies/com.unity.webrtc/blob/develop/Plugin~/README.md

以下に順序を追ってインストールなどの方法を解説する

依存するSDKやライブラリをインストール

ソースコードからのビルドには、ビルド時に依存するSDKやライブラリをインストールする必要がある。作業にはターミナルやVisualStudioが必要になる
上のリンク中のWindowsでのライブラリインストール方法のように管理者権限でPowerShellを立ち上げ、Chocolateyを使ってインストールするのがおすすめするが、セキュリティの都合からChocolatey入れられない環境では、以下のリンクからダウンロードできる。Chocolateyのパッケージ名に合わせてリンクを置いている
※Windows 10 64bit版前提で進めているので、他の環境を使う場合は多少リンクが変わるかも

cuda: https://developer.nvidia.com/cuda-downloads
(アーカイブ: https://developer.nvidia.com/cuda-toolkit-archive)

vcredist2010: https://www.microsoft.com/ja-jp/download/details.aspx?id=26999
vcredist2013: https://www.microsoft.com/ja-jp/download/details.aspx?id=40784
vcredist140: https://support.microsoft.com/ja-jp/topic/the-latest-supported-visual-c-downloads-2647da03-1eea-4433-9aff-95f26a218cc0
(使用しているOS環境に合わせる、ここでは x64 をダウンロードする)

windows-sdk-10-version-1809-all: https://developer.microsoft.com/ja-jp/windows/downloads/sdk-archive/
(Windows 10 SDK バージョン 1809SDKのインストール のほうを選ぶ。Visual Studio Installerからでもインストールできる(下図のように個別のコンポーネントから、 Windows 10 SDK (10.0.17763.0)にチェックしてインストール)

VulkanSDK: https://vulkan.lunarg.com/sdk/home
(下図のように WindowsSDK Installer を選ぶ。図は1.2.162.1のものだけど、最新版で大丈夫そう)

cmake: https://cmake.org/download/
(Platform のうち、Windows x64 Installer (ファイル末尾が.msi)を選ぶ)

Visual Studio 2019のインストール

githubのページには書かれていないけど、Visual Studioも必要なのでDLする
Visual Studio 2019: https://visualstudio.microsoft.com/ja/downloads/

Nvidia Video Codec SDKをインストール

今回の差し替え元になるVideoCodec SDKもダウンロードしてインストールする
NVIDIA VIDEO CODEC SDK: https://developer.nvidia.com/nvidia-video-codec-sdk/download

https://developer.nvidia.com/nvidia-video-codec-sdk/download

環境変数を確認する

今回使用するcmakeは依存ライブラリを探す際に特定の環境変数を使用するので、事前に確認する
特にChocolateyを使わずにライブラリをインストールした場合、環境変数が通っていない場合が多いので必ず確認しておく
以下はPowerShellでの確認方法になる。bashで確認したいときは echo $CUDA_PATH とすればできる

$env:CUDA_PATH
$env:VULKAN_SDK

通っていない場合はシステム環境変数から CUDA_PATHVULKAN_SDK の変数を追加し、上の図のように、ライブラリのバージョンが書かれているフォルダまでのパス(そのフォルダ以下に bininclude フォルダが含まれるまでのファイルパス)を値の箇所に記述する

システム環境変数 PATH にcmake.exeのバイナリが置いてあるディレクトリが含まれているか確認する。cmake --version のコマンドからcmakeのバージョンを見れるか確認するとパス通っているか確認できる

cmake --version

通っていない場合はシステム環境変数から Path の末尾にcmake.exeが含まれているファイルパスを追加する(例: C:\Program Files\CMake\bin)
システム環境変数を操作した場合、WindowsをOSごと再起動するか、PowerShellの再起動が必要になるので注意

※VideoCodec SDKのファイルは直接差し替えることになるので、環境変数にパスを通しておく必要はなし

ソースコードダウンロードからビルドまで

com.unity.webrtcのパッケージをGitHubからダウンロードしてビルドするのだけど、注意点がいくつかあったので、そこを気を付けながらビルドをしていく

バージョン指定してwebrtcリポジトリをクローン

https://github.com/Unity-Technologies/com.unity.webrtc/releases からリポジトリをダウンロードする

gitのCLIからリポジトリをクローンする場合、ブランチ指定しない限りデフォルトブランチの develop を引っ張るので、念のためリリース版のブランチを指定する(以下の例では release/2.4.0-exp.3 を指定)

git clone -b release/2.4.0-exp.3 https://github.com/Unity-Technologies/com.unity.webrtc.git

カスタマイズされたwebrtcのソースコードをダウンロード

BuildScripts~ フォルダ内にある、 build_plugin_win.cmd をテキストエディタで開き LIBWEBRTC_DOWNLOAD_URL の後ろのURLをコピーする。記事と同じバージョン(2.4.0-exp.3なら、おそらくhttps://github.com/Unity-Technologies/com.unity.webrtc/releases/download/M89/webrtc-win.zip のアドレスになっているかと思う(2.3.3あたりの古いバージョンならURL中の M89M85 になっている)
ダウンロード後、zipファイルをwebrtc.zipにリネームし、右クリックからすべて展開(T) で解凍し、できた webrtc フォルダごと Plugin~ フォルダ内にコピーする
以下のようになっていれば大丈夫

VideoCodec関連のファイルを置き換える

既存ファイルを待避する

Plugin~/NvCodec 内にあるフォルダをすべて別の場所に移動させるか、末尾に_bakなどを追加して待避する

VideoCodecのSDKからファイルをコピーする

Video Codec SDKのフォルダ直下にある InterfaceLib フォルダを Plugin~/NvCodec フォルダ内にコピーする

それぞれのフォルダを Interface > includeLib > lib にリネームする
(補足:Interface フォルダは ***.h が入っている。 Lib フォルダは linuxWin32x64 フォルダが入っており、それらのフォルダ内には ***.lib***.so が入っている)

Video Codec SDKの Samples フォルダ内にある NvCodecUtils フォルダを同様に Plugin~/NvCodec フォルダ内にコピーする

コピー後 Plugin~/NvCodec 以下のファイルがこのようになっていればOK

コピー後の一部ファイルを削除する

コピー後の Plugin~/NvCodec/NvCodec/NvEncoder フォルダ内にある、NvEncoder.cppNvEncoder.hNvEncoderCuda.cppNvEncoderCuda.h 以外のファイルを削除する

インクルードファイル指定の一部を変更する

Plugin~/NvCodec/NvCodec/NvDecoder 内の NvDecoder.cppNvDecoder.h をテキストエディタで開き、 #include "../../../Interface/nvcuvid.h" の箇所を #include "nvcuvid.h" に置き換える

Plugin~/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp
//#include "../../../Interface/nvcuvid.h" //元のコード
#include "nvcuvid.h" //変更後のコード
Plugin~/NvCodec/NvCodec/NvDecoder/NvDecoder.h
//#include "../../../Interface/nvcuvid.h" //元のコード
#include "nvcuvid.h" //変更後のコード

エンコード時に使用されるH264コーデックのレベルを変更する

現行バージョン(2.3.3や2.4.0あたりの話)UnityのWebRTCライブラリではHWエンコード時に動作するコードをNvidiaのVideoCodecのサンプルコードから流用しているが、 このサンプルコードはH264コーデックを使用するようにハードコードされており、またコーデックレベルも固定化されているため、直接書き替えない限りはコーデックを変更することができない。 今回はここを変更してH264コーデックのレベルを6.2に変更してみる

Plugin~/WebRTCPlugin/Codec/NvCodec フォルダ内の NvEncoder.cpp をテキストエディタで開く。おそらく110行目あたりの nvEncConfig.encodeCodecConfig.h264Config.level = NV_ENC_LEVEL_H264_51; を書き換える

Plugin~/WebRTCPlugin/Codec/NvCodec/NvEncoder.cpp
//nvEncConfig.encodeCodecConfig.h264Config.level = NV_ENC_LEVEL_H264_51; //元のコード
nvEncConfig.encodeCodecConfig.h264Config.level = NV_ENC_LEVEL_H264_62;

他、 NV_ENC_LEVEL_H264_60 (レベル6.0相当)や NV_ENC_LEVEL_H264_61 (レベル6.1相当)までの定義は差し替え後の nvEncodeAPI.h で定義されているので、自分の用途に合わせて書き換える

Plugin~/WebRTCPlugin/Codec/NvCodec 配下のファイルはVideoCodec SDKの Samples/NvCodec/NvEncoder 配下と同名のファイルが並んでいるが、中身は別物なので置き換えないように注意する

ビルドする

ターミナル(PowerShell、bashなど)で Plugin~ フォルダ(CMakeLists.txtがある一番上のフォルダ)までカレントディレクトリを変更する。カレントディレクトリ変更後、以下の cmake コマンドを実行する(ConfigureとGenerateが実行される)

cmake . -G "Visual Studio 16 2019" -A x64 -B "build64"

cmake コマンド実行後、 Plugin~ フォルダ内に build64 というフォルダが作られているので、そのフォルダ内にある webrtc.sln ファイルをダブルクリックして実行する。Visual Studioがインストールされていれば、自動的にVisual Studioが起動する

Visual Studio起動後、構成が Debug になっているので、 Release を選択する

ソリューションエクスプローラーから ALL_BUILD の箇所で右クリックし、 ビルド(U) を選択し、ビルドを実行する

実行完了後、エラーが無ければ出力タブで以下のように表示される。エラーがある場合はファイルの置き換えやソースコードの書き換えを飛ばしていないか確認する

※上の内容を再確認してもエラーが出る場合は、CUDAやVideoCodecに破壊的変更があるのかもしれないので、少しずつ前のバージョンに戻して試してみる

ビルド後、Runtime/Plugins/x86_64 フォルダ内の webrtc.dll ファイルがビルドしたものに上書きされているので、エクスプローラーから更新日時を確認する

ビルドしたWebRTCライブラリを利用してみる

実際にカスタマイズしたWebRTCライブラリをUnityRenderStreamingを通して使ってみる。これ以降は確認するだけなので、純粋にWebRTCの更新をしたいだけなら上記まででよい

Unity側のセットアップ

UnityRenderStreamingで使われるWebRTCプラグインとしてパッケージマネージャに登録する
Unity2020.3以降のバージョンで適当なプロジェクトを作成する

Package Managerの画面から、Add package from disk を選択する

ダイアログが開くので、ソースコード(com.unity.webrtc フォルダの直下)の package.json を選択し、 開く ボタンをクリックする

Custom の箇所にWebRTCのライブラリが追加されていればOK

Package Managerの画面から、Add package from git URL を選択する

テキストボックスが表示されるので com.unity.renderstreaming を入力する

ダイアログが表示されるので、 OK を押す、押した後Unityが再起動するのを待つ。開かない場合は自分でUnityを起動する

再起動後、追加したUnityRenderStreamingのパッケージで今回カスタマイズしたWebRTCのライブラリが使われているかを確認するため、Package Manager 右上にある歯車マークをクリックし、 Advanced Project Settings を選択する

Project Settingsのウィンドウが表示される。右側画面の下の方にある Advanced Settings 項目の中にある、 Show Dependencies の箇所のチェックボックスをONにする

Project Settingsのウィンドウを閉じ、再度Package Managerの画面から、 Unity Render Streaming を選択し、 Dependencies の箇所をクリックして開き、 Is using の箇所を確認する。バージョン一致、末尾が(local)になっていればカスタマイズしたWebRTCのライブラリが使われている

※UnityRenderStreamingのバージョンごとに依存するWebRTCのバージョンは違うので注意

ついでにサンプルも実行するので、 Samples の箇所をクリックして開き、 Example 横の Import をクリックしてサンプルプロジェクトをダウンロードする

サンプルがダウンロードできたら Assets 配下の Samples/Unity Render Streaming/3.1.0-exp.1/Example/WebBrowserInput の中にある WebBrowserInput のシーンを開く

Hierarchyから Render Streaming Camera 1 を選択、Inspectorの Camera Streamer (Script) の解像度をベンチマークとして4Kサイズに変更してみる

ハードウェアエンコードを有効にするため、Hierarchyから Render Streaming を選択し、Render Streaming (Script)Hardware Encoder Support にチェック、 Run On Awake にもチェックを入れる(入れないとHWエンコードが有効にならない?)

また、バージョン 3.1.0-exp.1 でのバグなのか、デフォルトではHttpSignalingが設定されているが、Unity実行時なぜかWebSocketSignalingに変更されるようで、URL設定がおかしくなるので、初めから以下のように WebSocketSignaling にしておき、 Signaling URLws://localhost に変更する

WebApp側の実行

Unityのメニューから Edit > Render Streaming > Download web app を選択する

ダイアログが開くので、ターミナルで開きやすい箇所にダウンロードする。(例: ユーザーのホームディレクトリとか)
ダウンロードしたファイル webserver.exe をターミナルから実行する。以下の例はPowerShellだが、bashの場合は ./webserver.exe --websocket で実行できる。なお日本語環境のPowerShellだとバックスラッシュ()は¥に置き換わる

.\webserver.exe --websocket


実行後、ターミナルの画面は開きっぱなしにしておく

上のコマンドでWebApp用のサーバーと接続用のWebSocketサーバーが起動されるので、この状態でブラウザ(Chrome)を開く、URLは localhost/videoplayer/index.html ← コピーしてURLバーにペーストして開く
この画面が出たら待機する

Unityの実行、WebAppの実行

Unityを実行する

Unity実行後、ブラウザ画面に表示されている再生ボタンをクリックして接続する

再生後、Unity側の画面がブラウザにも表示される

Unity公式のWebRTCライブラリはリリース版でも結構な頻度で破壊的変更に関する議論がされているようなので、将来的にこの方法は使えなくなるかも

https://github.com/Unity-Technologies/com.unity.webrtc/issues/249