Android端末 USB 3.0 カメラ接続

弊社ではAndroid端末にUSBカメラを接続して表示するするアプリを公開しております。

これまで確認してきたカメラはUSB 2.0で動作するカメラでした。最近USB 3.0で動作するAndroid端末が増えてきましたのでUSB 3.0でどの程度動作するか調査してみました。

Android端末でUSB 3.0の動作可否確認

まずは、USB 3.0で動作可否についての確認から行いました。

Pixel 5はUSBメモリ(USB 3.0)を接続した際、SuperSpeedで動作していました※1

※1 USB 3.0ではSuperSpeed(5GB/s)の転送速度が利用可能になります。

USBメモリ(USB 3.0)-Android接続のプロトコルアナライザ

Pixel 5でSuperSpeed(USB 3.0)の転送ができていることが確認できたので、USB 3.0カメラで試してみることにしました。

USBカメラの転送タイプや ペイロードフォーマット

Androidではアプリ内で転送データを解析して利用しています。

USBカメラの転送タイプやペイロードフォーマットによって、アプリで処理する内容が変わるので各ケースで動作が異なります。

転送タイプには4種類あり、USBカメラのストリームではBulk転送やIsochronous転送が利用されます。

  • Bulk転送:大規模なパケットの通信で利用されることが多く、帯域幅が利用できるようになるまで遅延することもあります。
  • Isochronous転送:定期的で継続的な通信が行われます。制限された遅延で動作します。

ペイロードフォーマットには非圧縮やMJPEG、H.264などの指定が可能です。

今回下記のケースでUSB 3.0の動作を確認しました。

Bulk転送Isochronous転送
非圧縮(YUY2)
MJPEGx(カメラを持っていないため)

USB 3.0 Bulkカメラ - Androidの接続(非圧縮)

最初にBulk転送のカメラが動作するかについて調査をしました。

USB 3.0カメラとして弊社開発のカメラを利用しました。転送タイプはBulk、ペイロードフォーマットは非圧縮、解像度は1920 x 1080、フレームレートは30fpsで確認を行いました。

まずはUSB 3.0カメラとして認識してくれたのですが、最初はエラーで動いてくれませんでした。USB 2.0で動作させる前提で作られていたため、USB 3.0とDescriptorが異なっていたためエラーになってました。その箇所を修正したところ、映像が表示されるようになりました。

しかし、10fps程度でしか表示されておらず、USB 2.0のソースのままでは不安定な挙動になっておりました。

ということで、USB 3.0向けにソースの修正が必要になりました。まずはUSB 2.0での接続か、USB 3.0での接続かをアプリ側で認識できるようにさせました。

判断の方法はUSBのDescriptorでDevice DescriptorのbcdUSBの値を確認することで判断することができます。

Device Descriptorは下記のように定義されています。

Device Descriptorの概要
  1. bDescriptorType: DEVICE Descriptorのタイプ
  2. bcdUSB: USBのリリース番号(i.e., 2.10 is 210H)
  3. bDeviceClass: クラスコード(USB-IFで定義されています)
  4. bDeviceSubClass: サブクラスコード(USB-IFで定義されています)
  5. bDeviceProtocol: プロトコルコード(USB-IFで定義されています)
  6. bMaxPacketSize0: エンドポイント0の最大パケットサイズ
  7. idVendor: ベンダーID
  8. idProduct: プロダクトID
  9. bcdDevice: デバイスのリリース番号
  10. iManufacturer: 製造者名のインデックス
  11. iProduct: プロダクト名のインデックス
  12. iSerialNumber: シリアル番号のインデックス
  13. bNumConfigurations: Configureationの数

USB 3.0向けのソースを作れるようにできたので、USB 3.0のデータ取得のボトルネックについて調査を進め、プロトコルアナライザでデータ取得の間隔を調べたり、ソース上でブロックしている部分を調べていきました。

その結果、キャッシュしているデータ領域が不十分で遅くなっているようでした。

USB 3.0となったことで取得するデータ量が増えてUSB 2.0の設定のままではデータ領域が少なく、待ち時間が発生していました。

そのため内部で利用しているバッファ量を増やし、またデータ取得の高速化のために一度に取得するデータ量を増やすことで、解像度 1920x1080、フレームレート 30fpsの映像が表示されました。

USB 3.0 Isochronousカメラ - Android の接続(非圧縮)

次にIsochronous転送のカメラが動くか試してみました。

USB 3.0 Isochronousカメラは、logicoolのBRIO Ultra HDを使ってみました。

Androidは先ほどと同じくPixel 5を利用しました。

Bulkで動作するようになったのでIsochronousのカメラも簡単に動作するはず、と思って接続してみたのですが、うまくうごきませんでした。

転送タイプはIsochronous、ペイロードフォーマットは 非圧縮、解像度は1920x1080、フレームレートは30fpsで動作させてみたのですが10fpsも表示されず、全然速度がでませんでしたので、どうなっているのか確認してみました。

プロトコルアナライザを用いて、データ転送がどのようになっているか調べてみたところ、USB 3.0での転送は行われていたのですが、データ転送量が全く足りていませんでした。

プロトコルアナライザの内容を精査したところ、最大パケットサイズが少ないEndpointを選択していました。

USB Endpointの説明

USBのEndpointはUSBのストリーム毎に複数持つことができ、Endpoint毎にIsochronousやBulkの転送タイプの情報や最大パケットのサイズを持っています。Endpointの詳細については、Endpoint Descriptorで定義されています。

Endpoint Descriptの概要
  1. bLength: Descriptorのサイズ
  2. bDescriptorType: Endpoint Descriptorのタイプ
  3. bEndpointAddress: Endpointのアドレス
  4. bmAttributes: Endpointの属性
  5. wMaxPacketSize: Endpointの最大パケットサイズ
  6. bInterval: Endpointをポーリングする間隔

上記、Endpoint DescriptorはUSB 2.0、USB 3.0で共通なのですが、USB 3.0ではSuperSpeed Endpoint CompanionというDescriptorがEndpoint毎に設定されます。

SuperSpeed Endpoint Companionの概要
  1. bLength: Descriptorのサイズ
  2. bDescriptorType: SUPERSPEED_USB_ENDPOINT_COMPANION Descriptorのタイプ
  3. bMaxBurst: バーストの一部として送受信できるパケットの最大数
  4. bmAttributes: SuperSpeed Endpoint Companionの属性
  5. wBytesPerInterval: 毎Intervalの転送最大サイズ

Endpointの最大パケットのサイズは下記で決定されます。

BulkIsochronous
USB 2.0最大パケットサイズ※2 x (追加トランザクション※3 + 1)最大パケットサイズ※2 x (追加トランザクション※3 + 1)
USB 3.0wMaxPacketSize x (bMaxBurst + 1)wMaxPacketSize x (bMaxBurst + 1) x (Mult + 1)※4
Endpointの最大パケットサイズ

※2 wMaxPacketSizeの0:10bitの値
※3 wMaxPacketSizeの11:12bitの値で最大2
※4 SuperSpeed Endpoint ComanionのbmAttributesの0:1bitの値で最大2

Endpoint選択ロジックの変更

このBurstとMultの設定を踏まえて最大パケットサイズのEndpointを選択するように変更したところ、USB 3.0 Isochronous の非圧縮のデータが25fps程度で動作するようになりました。

25fpsになっている件の調査

25fpsしか取れなかったので、どうなっているか調査をしてみました。

Endpointの設定を変更してプロトコルアナライザのデータを眺めてみたり、アプリのバッファ量を大きくしたり、小さくしたり試したのですが変化なく、25fps程度しか取得されませんでした。

Windowsの動作をUSBキャプチャツールで確認してみたところ、Windowsでも25fps程度しか表示されませんでした。

ということで、25fps出ていますので、30fpsで表示する調査はあきらめました。何か取得方法を変えないといけないのかもしれません。うまく表示する方法が見つかりましたら記載します。

USB 3.0 Isochronousカメラ - Androidの接続(MJPEG)

logicoolのBRIO Ultra HD にMJPEGのペイロードフォーマットもあったので、こちらでも動作を試してみました。

転送タイプはIsochronous、 ペイロードフォーマットは MJPEG、解像度は1280x720、フレームレートは60fpsで表示してみたところ50fps程度の速度で表示されており、表示が少し足りませんでした。

また、MJPEGの最大解像度の4096x2160が30fpsで表示を試してみたところ5fps程度で表示されており、遅延も発生していました。

表示が追い付いていない件について調べてみたところ、JPEG→RGB変換に時間がかかっていました。

変換の高速化にはSIMDやマルチスレッドでの対応があります。SIMDを利用した高速化はlibJPEG-turboを利用で行っています。

マルチスレッド対応で高速化ができるかもしれません。ただ、少し手間がかかるのでまた時間を作ってやろうかと思います。

USB 3.0のH.264カメラやの動作可否も試したいのですが、現在持っていないのでまた今度にしたいと思います。

USB 3.0 カメラとAndroid接続の展望

なんとか、USB 3.0カメラをAndroidで表示させることができましたが、USB 3.0カメラはAndroidでどう使われていくのですかね?

USB 3.0のカメラはマシンビジョンや工業用カメラが多く、通信などでの利用よりも解析などで利用されます。

そのため、AndroidでUSB 3.0のカメラを使えるメリットは解析にあると考えてます。

内視鏡や顕微鏡の映像をリアルタイムに、場所を問わずに解析するようなシステムで利用されるのではないでしょうか。

ただ、デメリットはUSB 3.0になることで最大消費電力の上限が高く、高精細な映像を使うので電池消費が速くなります。また、転送速度が速くなっているため、Android端末によっては動作しない場合もあります。

ただ、これらのデメリットはUSB 2.0カメラとAndroidがはじめて接続できた頃と状況と似ているので、時間が解決してくれることを期待したいです。