皆さまご存知のRealsenseですが,そのデバイス自身の素晴らしさもさることながら,SDKのプラットフォームの対応の幅広さも本当に素晴らしい点だと思います.
librealsenseレポジトリのReadmeも一行目から
とマルチプラットフォームを謳っております.
今回,AndroidからRealsenseを使いたいなぁと思って調べていて,もちろん公式対応はあるのですが,Androidのサポートはネイティブ(java)だけなのですね......
一方,Android向けにビルドもできるゲームエンジン,UnityにもRelasenseは公式に対応しているのですが,このUnityPackageはPC向けサポートのみということでAndroidには使えず......
AndroidなUnityでRealsenseを使いたい!ということで,「Android Unity Realsense」とかでググると,@UnlimitedBuildWorksさんの神記事が出てきます.
これで万事解決といえば解決なのですが,この方法だとjavaでRealsenseSDKを叩いてC#側に情報を渡すWrapperを自作しないといけないという......
つらい......日頃Unityでヌルく生きてるへっぽこエンジニアにはつらすぎる...…javaなんて書きたくないよう...…
ということで,Realsenseの公式UnityPackage「Intel.RealSense.unitypackage」をAndroid向けに使用できる方法を模索し,なんとか使えるようになったっぽいので,その方法をまとめます.
※ とりあえずサンプルシーンが動いていることを確認しただけなので,何か問題あればごめんなさい.ウチではもろもろ責任取りません(免責事項)
方針
AndroidだってLinuxベースなんだから,Intel.RealSense.unitypackageをLinux向けに使えるようにすりゃいいんでしょ,というのが基本方針です.
Intel.RealSense.unitypackage の中身を見ると,C#のコードに加え,realsense2.dll
とIntel.RealSense.dll
があります.
C#実装はUnity様がなんかいい感じにAndroid向けにビルドしてくれます.なので残りはこの2つのライブラリ.ということで,まずはrealsense2.dll
とIntel.RealSense.dll
をAndroid向けに使えるようにします.
ただ,それだけだと真の問題「Androidでのネイティブ(C/C++)ライブラリ経由なUSBデバイス使用ができない問題」にぶつかります.
realsense2.dll
はUSBデバイスであるRealsense実機とのやり取りにlibusbを使っているようですが,AndroidではAndroidSDK経由ではないUSBデバイスとのやり取りは普通には難しいようです(参考1,参考2)
(このあたりは上記の@UnlimitedBuildWorksさんの記事にも詳しいです)
ということで,最後にUSBデバイス利用のパーミッション問題の解消をがんばります.
まとめると,大まかに
という手順で進めると,晴れてIntel.RealSense.unitypackage を使ったUnityアプリがAndroid上で使えるようになります.
準備
まず,Unity,Androidの開発環境は既にあることを前提とします.UnityEditor,VisualStudio,AndroidStudio,...などご自由にご準備ください.UnityはAndroid向けにビルドできるように「Android Build Support」を入れ,AndroidSDKなどの設定を済ませておいてください.
開発環境のOSはWindowsでやってます.Unity は2019.4.14で確認してますが,別にバージョンにあまりに依存した内容はないので2018.4以上とかならなんでも大丈夫だと思います(未検証だけど)
また,Realsense関連は以下を手元に用意してください.
以前にダウンロード・クローンしていたとしても,最新のものを改めて持ってくることをおすすめします.
(自分の手元で,古いUnityPackageと最新のソースの組合せで作業してたらAPIが変わってたみたいでハマりました(同じv2系だったにも関わらず😭))
次に,作業用のUnityProjectを作成し,Intel.RealSense.unitypackageをインポートしておいてください.また,Build SettingsからAndroidにSwitch Platformしておいてください.
手順詳細
1. Realsenseのライブラリ(realsense2.dll)のAndroid対応
これは,LibrealsenseのAndroidWrapperをビルドすることでlibrealsense.so
が生成されるのでそれを流用します.
ということで,まずはLibrealsenseのAndroidWrapperのビルドから.
- AndroidStudioで
librealsense/wrappers/android
を開く -
もし以下のようなエラーが出たら,手前味噌ですがこちらを参考にしてください
A problem occurred configuring project ':camera'. > Failed to notify project evaluation listener. > org.gradle.api.file.ProjectLayout.fileProperty(Lorg/gradle/api/provider/Provider;)Lorg/gradle/api/file/RegularFileProperty;
- 適当なアプリに設定し,Build > Build Bundle(s) / APK(s)からアプリをビルドする
ビルドが終わると,librealsense/wrappers/android/build/jniLibs
以下にarmeabi-v7a
フォルダとarm64-v8a
フォルダができていて,その中にlibrealsense2.so
があると思います.
次に,このlibrealsense2.so
をUnity側に入れていきます.RealsenseのUnityPackageを入れたUnityProjectで作業を進めます.
-
Assets/RealSenseSDK2.0/Plugins
以下に適当なフォルダ(ここではWindowsフォルダ)を作成し,そこにDLLを移動する -
先ほど作成した
librealsense2.so
を,armeabi-v7a
フォルダとarm64-v8a
フォルダごと,Assets/RealSenseSDK2.0/Plugins
以下にコピーする -
Assets/RealSenseSDK2.0/Plugins/Windows
以下のrealsense2.dll
を選択,Inspectorを開き,Include PlatformをEditorとStandaloneだけにチェックし,Applyする -
Assets/RealSenseSDK2.0/Plugins/armeabi-v7a
以下のlibrealsense2.so
を選択,Inspectorを開き,Include PlatformをAndroidだけにチェックし,Applyする -
同様に
Assets/RealSenseSDK2.0/Plugins/arm64-v8a
以下のlibrealsense2.so
を選択,Inspectorを開き,Include PlatformをAndroidだけにチェックし,Applyする
これでRealsenseのライブラリ(realsense2.dll)のAndroid対応ができました!
2. RealsenseのC#ラッパー(Intel.RealSense.dll)のAndroid対応
次はIntel.RealSense.dll
ですが,そもそもこのDLLって何なんでしょうか...?
Intel.RealSense.unitypackage のC#コードを読んでいるとそのヒントがあります.このUnityPackage内のC#コードには,どこにもDLLImportなどのネイティブプラグインを扱うコードが見当たりません.
これより「どこかのマネージドプラグインでネイティブプラグインをラップしており,C#コードはそれを利用しているのだろうな🤔」という予想が立ちます.こうなると,もう「どこかのマネージドプラグイン」はIntel.RealSense.dll
であろうことが分かります.
つまり,Intel.RealSense.dll
の正体はLibrealsenseのC#ラッパーであり.そのソースはlibrealsense/wrappers/csharp/Intel.RealSense
にあります.これをAndroid向けにビルドできればよいのです.
「C#のAndroid向けビルドか~~ ざまりんとかか~~??😵😵」とかなるんですが,冷静に考えてC#をAndroid向けにビルドしてくれるやつは身近にいます.そう,Unityにビルドさせればよいのです!
ということで,以下のようにやってみます.
-
Assets/RealSenseSDK2.0/Plugins/Windows/Intel.RealSense.dll
を消去する- この時点でめちゃくちゃエラーが出ますが,一旦放置します
-
librealsense/wrappers/csharp/Intel.RealSense
フォルダを,丸ごとUnityのAssets/RealSenseSDK2.0
以下にコピーする- まだエラーが出てますが,そのままで大丈夫です
-
Assets/RealSenseSDK2.0/Intel.RealSense
に,AssemblyDefinitionファイル(ここではIntel.RealSense.asmdefとします)を作成する -
Assets/RealSenseSDK2.0/Scripts
フォルダにあるRealSense.asmdefをInspectorから編集してAssemblyDefinitionReferenceにIntel.RealSense.asmdefを追加する -
Assets/RealSenseSDK2.0/Misc/Utils
フォルダにあるUtils.asmdefに対しても同様にAssemblyDefinitionReferenceにIntel.RealSense.asmdefを追加する- ここでエラーはすべてなくなるはずです
-
今回はネイティブプラグインのデバッグビルドまでやっていないので,
Assets/RealSenseSDK2.0/Intel.RealSense/NativeMethods.cs
のdlName
をDEBUGのときもrealsense2としておく -
Assets/RealSenseSDK2.0/Misc/Editor
にあるCreateAssetBundles.cs
にAndroid時の実装を追加する
**Assets > Build AssetBundles** からアセットバンドルをビルドしておく
この時点で,RealsenseSDKのサンプルシーンなどをUnityのAndroidビルド・デプロイすると,普通にapkファイルは作れてAndroidデバイスでアプリを実行できます.
ただし,そのAndroidデバイスにRealsenseを接続してもアプリ側で認識されず,期待通り機能しません.それはUSBデバイス利用のパーミッション問題が残っているからです.
3. AndroidでのUSBデバイス利用のパーミッション問題の解消
最後に,USBデバイスの認識を可能にしていきます.
上述の通り,そもそもの問題はlibrealsense.so
内で使っているlibusbがそのままではAndroidでは使えない,AndroidでUSBデバイスを使うにはAndroidSDKのAPI経由でアクセスしないといけない,ということです.
LibrealsenseのAndroidWrapperがどのようにそこを解決しているのかを見ると,RsContext.init
関数から始まり,いろいろ経てDeviceWatcher
クラスのaddDevice
関数が呼ばれ,そこでAndroidSDKのUsbManager経由でUSBデバイスをオープンし,そのファイルディスクリプタをネイティブに渡しています.
このファイルディスクリプタをネイティブに渡すところまで実現できれば,あとはlibrealsense側でよしなにやってくれているはずだと踏んで,このあたりの処理をUnityに持っていくことを考えます.
-
Assets/RealSenseSDK2.0/Scripts
以下に適当なフォルダ(ここではDevicePermissionフォルダ)を作成し,以下のファイルをlibrealsenseのwrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense
からコピーする-
DeviceListener.java
,DeviceWatcher.java
,Enumerator.java
,LrsClass.java
,UsbDesc.java
,UsbUtilities.java
-
-
DeviceWatcher.java
を叩くクラス(ここではDeviceConnect.java
)を,同じくAssets/RealSenseSDK2.0/Scripts
に作成,以下のように実装する
-
C#側からAndroidのCameraのRuntimePermissionを取得し,
DeviceConnect.java
を叩くスクリプト(ここではDeviceConnect.cs
)を,同じくAssets/RealSenseSDK2.0/Scripts
に作成,以下のように実装する
- こちらは https://qiita.com/utibenkei/items/65b56c13f43ce5809561 を参考にさせていただきました,感謝です🙏🙏
-
Assetsフォルダ以下に
Plugins/Android
とフォルダを作成,さらにres/xml
ファイルをその下に作成し,librealsenseのwrappers/android/librealsense/src/main/res/xml
にあるusb_filter.xml
をコピーする -
先ほど作った
Assets/Plugins/Android
に以下のAndroidManifest.xml
を配置する
これで,Realsenseの公式UnityPackage「Intel.RealSense.unitypackage」をAndroid向けに使用することができるようになります!お疲れさまでした.
動作確認
では実際に「Intel.RealSense.unitypackage」のサンプルシーンをAndroidデバイスで動かしてみます.
- Realsenseのサンプルシーン(ここでは「StartHere」シーン)を開く
- 空のGameObjectを作成し,先ほど作ったDeviceConnectコンポーネントを追加する
- アプリをビルドし,Androidデバイスへデプロイする
- AndroidデバイスにRealsenseデバイス(ここではD435i)を接続する
これでアプリがRealsenseデバイスを認識し,メニューが現れるはずです!
あとは適当にメニューを選べば,ちゃんとRealsenseデバイスからの情報を取得できる様子がわかると思います.
また,初回以降は,RealsenseデバイスをUSBでAndroidデバイスに接続したタイミングでポップアップが出て,アプリが起動できるようになるはずです.
さいごに
とりあえず速報的にここに書いたけど,将来的に自分でもこんがらがること必至なので,librealsense本線にプルリクしたいなぁ~~という気持ち.