Skip to content

新しいカメラをサポートする

Baku 麦 edited this page Jun 12, 2024 · 11 revisions

Tethrはオープンソースプロジェクトです。お使いのカメラをサポートさせるには、自分でコードを書いてプルリクエストを送るか、誰かに依頼するかのいずれかの方法があります。

自分でコードを書く

もしあなたが開発者であれば、自分で新しいカメラをサポートするためのコードを書くのが最も早い方法です。特にこのテキストを読んでくださっている日本語話者であるあなたは、SonyやCanon、NikonやSigmaといった国産ベンダーのための実装をするにはうってつけかもしれません。なぜなら、カメラのSDKやAPIのドキュメントはまず日本語で書かれていることが多いため、英語のドキュメントよりも自然で読みやすいからです。

Tethrは以下の技術スタックによって成り立っています。

  • WebUSB API: USBデバイスをブラウザから制御するためのAPI
  • Picture Transfer Protocol (PTP): デジタルカメラを制御したり、画像を取得するためのプロトコル

WebUSBは、USB接続されたデバイスに対し、バイトバッファの送受信などより低レイヤーな通信機能を提供しています。しかしセキュリティの観点から、現時点では限られたブラウザでのみサポートされています。

PTPは2000年代に標準化された通信プロトコルで、そのUSB実装がTethrでは使用されています。しかしながら多くのベンダーはPTPに対して独自の拡張を行なっているため、カメラごとに追加の実装が必要となります。新しいカメラのサポートをするということは、主にそうした拡張に対応するコードを書くことを意味します。gPhoto2といったテザー撮影のためのCLIや、Capture OneDragonframeといった撮影アプリの内部では、様々なカメラに対応するための個別的な実装が行われています。

Tethrのクラス階層

Tethrは、USB接続されたカメラに限らず、Webカメラや将来的には無線越しのカメラにも対応するように設計されているため、このようなクラス階層を持っています。

  • Tethr: カメラオブジェクトを表現抽象クラス。全ての設定やオペレーションに 'unsupported'(非サポート)を返す

恐らくあなたが追加したいであろうカメラは TethrPTPUSBのサブクラスとして実装することになります。

実装の手順

リポジトリをforkし、以下の手順に従ってサポートコードの実装を行ってください。デバッグのためのデモ画面は、yarn devで起動できます。

  1. TethrPTPUSBを継承したサブクラスを作成し、 src/TethrPTPUSB/Tethr{YourCamera}.ts というファイル名で保存します。
  2. 新しいクラスのメゾッドを独自拡張に従ってオーバーライドします。
    • 例: open, close, set{ConfigName}, get{ConfigName}Desc
    • get{ConfigName}は、get{ConfigName}Descを元に既に実装されているため、オーバーライドする必要はありません。
    • カメラとの通信には多くの場合、PTP規格に準じた通信機能を提供する this.device: PTPDevice メンバを使用します。より低レイヤーな通信のために this.device.usb: USBDevice にアクセスすることもできます。
  3. getVendorSpecificPTPUSBClass に、新しいカメラを検出し対応するサブクラスを返すコードを追加します。

プロトコル解析の方法

多くのカメラのSDKは、WindowsやmacOSで読み込むことのできるダイナミックライブラリとして提供されているため、JavaScriptで動作させるために必要な、バイト列の構造といった情報はリバースエンジニアリングによって得る必要があります。そのためには次のような方法があります。

公式SDKを解読する

SDKに含まれているドキュメントやライブラリのヘッダーから、独自のOpCodeやEvenCode、DevicePropCodeを推測することが出来ます。たとえば、以下はPanasonic製LUMIXのSDKのヘッダーですが、ここからシャッタースピードのDevicePropCodeが0x02000030であることが読み取れます。

enum Lmx_event_id : UINT32 {
  // Event/Callback registration ID:ISO information
  LMX_DEF_LIB_EVENT_ID_ISO = 0x02000020,
  // Event/Callback registration ID:ShutterSpeed information
  LMX_DEF_LIB_EVENT_ID_SHUTTER = 0x02000030,
  // Event/Callback registration ID:Apertuer information
  LMX_DEF_LIB_EVENT_ID_APERTURE = 0x02000040,
  // Event/Callback registration ID:WhiteBalance information
  LMX_DEF_LIB_EVENT_ID_WHITEBALANCE = 0x02000050,
  // Event/Callback registration ID:Exposure
  LMX_DEF_LIB_EVENT_ID_EXPOSURE = 0x02000060,
};

また、SDKに同梱されているサンプルアプリの中には、通信内容をログ表示するものもあります。以下はSigma fpの例ですが、ここから通信に使われているプロトコルを解析することができます。

image

libgphoto2からコードリーディングする

例えばptp.hにはベンダー固有のIDやコードの定数が宣言されています。リポジトリから対応したいカメラ名を検索すると、ヒントとなるような実装が見つかるかもしれません。ただし、参考にする場合はソースコードのライセンスを遵守し、必要な場合は適切に参考にしたソースコードへのURLやアトリビュートをコメントとして加えてください。

Wiresharkを使う

そのカメラに対応したテザー撮影アプリを操作しながら、[Wireshark]でUSB通信を覗き見する方法方法が、最もローレイヤーかつ確実にパケット解析をすることが出来ます。

起動後、USB通信の解析に必要なモジュールをインストールし、リストからカメラのUSB接続に該当するポート(XHC20など)を選択します。次に、フィルターに _ws.col.info contains "(completed)"を入力し、青い右矢印で反映させます。行それぞれがカメラとPCの通信を表しています。

  • URB_BULK out (completed): PC → カメラ
  • URB_BULK in (completed): カメラ → PC

image

Hexコードのうち、PTPに関わる部分は、Leftover Capture Data(青色でハイライトされている箇所)となります。PTP仕様を参考にしつつ解析してください。

ただしApple Silicon搭載のMacは以下の手順が必要です。

  1. リカバリーモードで起動し、SIPを無効にする
  2. Wiresharkに表示されているXHCから始まるポートそれぞれに、sudo ifconfig XHC# up を実行する

PTP仕様の要点

PTP仕様はPIMA 15740:2000で、USB接続によるPTPの仕様はPDFで公開されています。全てを通読するのは骨が折れるので、大まかに要点を纏めておきます。

通信タイプ

PTPには以下の3つの通信タイプがあります。それぞれ、PTPDeviceのメゾッド名に対応しています。

  • sendCommand: カメラに短いコマンドを送信する(例: 露出設定、シャッター)
    sequenceDiagram
    		autonumber
    		Host->>+Camera: Command (OpCodeを送信)
    		Camera->>+Host: Response
    
    Loading
  • sendData: カメラにまとまったデータを送信する
    sequenceDiagram
    		autonumber
    		Host->>+Camera: Command (OpCodeを送信)
    		Host->>+Camera: Data (実データ)
    		Camera->>+Host: Response
    
    Loading
  • receiveData: カメラからまとまったデータを取得する
    sequenceDiagram
    		autonumber
    		Host->>+Camera: Command (OpCodeを送信)
    		Camera->>+Host: Data (実データ)
    		Camera->>+Host: Response
    
    Loading

一方で、カメラ側から設定値の変更などを割り込み通知する通信タイプも存在します。

sequenceDiagram
	autonumber
	participant Host
	Camera->>+Host: Event
Loading

カメラからの割り込み通知を受け取るためには、以下のようにonEventCodeメゾッドを実装します。

// TethrPTPUSBのメゾッド内
this.devce.onEventCode(EventCode, (event: PTPEvent) => {
	// イベントに応じた処理
})

パケットの構造

常にリトルエンディアンが用いられます。 例えば、0x1234567878 56 34 12 としてエンコードされます。

Field Size Description
Length 4 パケット全体のバイト数(Lengthフィールド自体を含む)
PTPBlockType 2 通信タイプ
Code 2 コマンドの種類
Transaction ID 4 通信ごとに割り振られるID
Payload ?? コマンドに応じたデータ

パケットは、Command, Data, Response, Eventのいずれかに分類されます。それぞれの種類に応じて、CodeとPayloadの内容が異なります。

  • PTPBlockType.Command
    • Code: OpCode
    • Payload: Uint32Array(4)
  • PTPBlockType.Data
    • Code: OpCode
    • Payload: ArrayBuffer
  • PTPBlockType.Response
    • Code: ResCode
    • Payload: Uint32Array(4)
  • PTPBlockType.Event
    • Code: EventCode
    • Payload: Uint32Array(4)

以下はCommandの例です。

0c 00 00 00 | Length: 0xc = 12 bytes
01 00       | PTPType: `0x0001`= Command
01 10       | OpCode: `0x1001` = GetDeviceInfo
12 00 00 00 | Transaction ID `0x12` = 18
0a 00 00 00 | Payload[0]: 10
0b 00 00 00 | Payload[1]: 11
0c 00 00 00 | Payload[2]: 12
0d 00 00 00 | Payload[3]: 13

コードの種類

  • OpCode: カメラに対して送信するコマンドの種類。PTP仕様ではOperationCode
  • ResCode: カメラからの応答の種類。PTP仕様ではResponseCode
  • EventCode: カメラからの割り込み通知の種類

カメラの設定値

露出モードやシャッタースピードといった設定値ははDevicePropと呼ばれており、それぞれに固有の整数値ID DevicePropCodeが割り振られています。また、設定値の範囲やデフォルト値、単位などの情報は、DevicePropDescという構造体によって表現されています。設定値の読み書きには、OpCode.GetDevicePropDescOpCode.SetDevicePropValue といったコマンドが用いられます。

Note

ドキュメントによってはPropはProperty, DescはDescriptorと表記されることがあります。

その他のtips

以下は、ベンダー拡張仕様を解析する際に役立つかもしれないtipsです。

  • TethrPTPUSB内のベンダー毎の実装例を参考にするのも良いかもしれません
  • Sigma fpについて
    • 割り込み通知がありません。その代わり、receiveData(OpCodeSigma.GetCamStatus2)を定期的に送信することで、カメラの状態を更新する必要があります。
    • 設定値やDescriptorは、IFDと呼ばれるExifフォーマットに準拠した構造体で表現されています。
  • カメラによってはテザー接続すると本体側の操作が無効になるものもあります。
  • AF可能なレンズのフォーカス位置設定は、Sigma fpのように絶対値で指定できるものもあれば、LUMIXのように相対値でオフセットさせることしかできないカメラもあります
  • ライブビュー表示はPTPの仕様には含まれていません。カメラ固有の仕様を解析する必要があります
    • おおよそ、receiveData(OpCodeVendor.LiveviewImage)のような拡張コードが存在します。payload内のある特定のオフセットからのバイト列がそのままJPEG画像になっていることが多いです。受信したパケットにマジックナンバーの ff 08が含まれてないか探すと良いかもしれません。
  • 独自仕様のPayloadの最後の数バイトがChecksumになっていることもあります。
  • ブロックの作成には、DataViewを用いるほかに、PTPDataViewを使うと便利です。

誰かに依頼する

開発者ではないものの、KomaなどTethrが使われているアプリで対応してほしいカメラがある場合、issueを立ててみるのも一つの方法です。

商用案件での導入を検討されている場合、作者であるBaku Hashimotoに直接依頼することも可能です。予算規模にもよりますが、私への実機の貸出が可能な場合 >$6000、おおよそ1週間内での対応を考えています。一方で、アニメーション作家や学生の方、自主制作に活かしたい方には、できる限りサポートをさせて頂きたいと考えています。しかしSDKの仕様によっては、ご希望の機能すべての実装が難しい可能性もあることをご留意ください。 気になることがあれば、mail@baku89.com までお気軽にお問い合わせください。

Clone this wiki locally