新しいカメラをサポートする - baku89/tethr GitHub Wiki
Tethr はオープンソースプロジェクトです。お使いのカメラをサポートさせるには、自分でコードを書いてプルリクエストを送るか、誰かに依頼するかのいずれかの方法を選べます。
自分でコードを書く
もしあなたが開発者であれば、自分で新しいカメラをサポートするためのコードを書くのが最も早い方法です。
[!NOTE] 特にこのテキストを読んでくださっている日本語話者であるあなたは、Sony や Canon、Nikon や Sigma といった国産ベンダーのための実装をするにはうってつけかもしれません。なぜなら、カメラの SDK や API のドキュメントはまず日本語で書かれていることが多いため、英語のドキュメントよりも自然で読みやすいからです。
Tethr is mainly built on top of the following technologies:
Tethrは主に以下の技術をベースにしています。
- WebUSB API: USB デバイスをブラウザから制御するための API
- Picture Transfer Protocol (PTP): デジタルカメラを制御したり、画像を取得するためのプロトコル
WebUSBは、USB接続されたデバイスに対し、バイトバッファの送受信などより低レイヤーな通信機能を提供しています。しかしセキュリティの観点から、現時点では限られたブラウザでのみサポートされています。
PTPは2000年代に標準化された通信プロトコルで、そのUSB実装がTethrでは使用されています。しかしながら多くのベンダーはPTPに対して独自の拡張を行なっているため、カメラごとに追加の実装が必要となります。新しいカメラのサポートをするということは、主にそうした拡張に対応するコードを書くことを意味します。gphoto2といったテザー撮影のための CLI や、Capture One、Dragonframeといった撮影アプリの内部では、様々なカメラに対応するための個別的な実装が行われいるようです。
Tethr のクラス階層
Tethrは、USB接続されたカメラに限らず、Web カメラや将来的には無線越しのカメラにも対応するように設計されているため、このようなクラス階層を持っています。
- Tethr: カメラオブジェクトを表す抽象クラス。全ての設定やオペレーションに
'unsupported'(非サポート)を返す- TethrWebcam: Web カメラ
- TethrPTPUSB: USB 接続された PTP カメラ
恐らくあなたが追加したいであろうカメラは TethrPTPUSBのサブクラスとして実装することになります。
実装の手順
リポジトリを forkし、以下の手順に従ってサポートコードの実装を行ってください。デバッグのためのデモ画面は、yarn devで起動できます。
- TethrPTPUSBを継承したサブクラスを作成し、
src/TethrPTPUSB/Tethr{YourCamera}.tsというファイル名で保存します。 - 新しいクラスのメゾッドを独自拡張に従ってオーバーライドします。
- 例:
open,close,set{ConfigName},get{ConfigName}Desc get{ConfigName}は、get{ConfigName}Descを元に既に実装されているため、オーバーライドする必要はありません。- カメラとの通信には多くの場合、PTP 規格に準じた通信機能を提供する
this.device: PTPDeviceメンバを使用します。より低レイヤーな通信のためにthis.device.usb: USBDeviceにアクセスすることもできます。
- 例:
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 の例ですが、ここから通信に使われているプロトコルを解析することができます。
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
Hex コードのうち、PTP に関わる部分は、Leftover Capture Data(青色でハイライトされている箇所)となります。PTP 仕様を参考にしつつ解析してください。
[!NOTE] ただし Apple Silicon 搭載の Mac は以下の手順が必要です。
- リカバリーモードで起動し、SIPを無効にする.
- 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 (+ResCode) - sendData: カメラにまとまったデータを送信する
sequenceDiagram autonumber Host->>+Camera: Command (+OpCode) Host->>+Camera: Data (+Payload) Camera->>+Host: Response (+ResCode) - receiveData: カメラからまとまったデータを取得する
sequenceDiagram autonumber Host->>+Camera: Command (+OpCode) Camera->>+Host: Data (+Payload) Camera->>+Host: Response (+ResCode)
一方で、カメラ側から設定値の変更などを割り込み通知する通信タイプも存在します。
sequenceDiagram
autonumber
participant Host
Camera->>+Host: Event (+EventCode)
カメラからの割り込み通知を受け取るためには、以下のようにonEventCodeメゾッドを実装します。
// In the method of TethrPTPUSB
this.devce.onEventCode(EventCode, (event: PTPEvent) => {
// Do something
})
パケットの構造
常にリトルエンディアンが用いられます。 例えば、0x12345678 は 78 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)
- Code:
- PTPBlockType.Data
- Code:
OpCode - Payload:
ArrayBuffer
- Code:
- PTPBlockType.Response
- Code:
ResCode - Payload:
Uint32Array(4)
- Code:
- PTPBlockType.Event
- Code:
EventCode - Payload:
Uint32Array(4)
- Code:
以下は 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: カメラに対して送信するコマンドの種類ResCode: カメラからの応答の種類EventCode: カメラからの割り込み通知の種類
[!NOTE] PTP仕様では、OpCodeはOperationCode, ResCodeはResponseCodeと表記されています。
カメラの設定値
露出モードやシャッタースピードといった設定値はDevicePropと呼ばれており、それぞれに固有の整数値 IDであるDevicePropCode が割り振られています。また、設定値の範囲や設定可能な値のリスト、デフォルト値といった情報はDevicePropDescという構造体によって表現されています。設定値の読み書きには、OpCode.GetDevicePropDesc、OpCode.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、おおよそ 2週間内での対応を考えています。一方で、アニメーション作家や学生の方、自主制作に活かしたい方には、できる限りサポートをさせて頂きたいと考えています。その際にはSDKの仕様によっては、ご希望の機能すべての実装が難しい可能性もあることをご留意ください。[email protected] までお気軽にお問い合わせください。