Skip to content

Guide for supporting a new camera

Baku 麦 edited this page Jun 14, 2024 · 4 revisions

Tethr is an open-source project. To support your camera, you can either write the code yourself and send a pull request, or ask someone to do it.

Write the code yourself

If you are a developer, writing the code yourself is the fastest way to support a new camera. Tethr is mainly built on top of the following technologies:

WebUSB provides low-level communication features such as sending and receiving byte buffers to USB-connected devices. However, from a security perspective, it is currently supported only in limited browsers.

PTP, on the other hand, is a communication protocol standardized in the 2000s, and its USB implementation is used in Tethr. However, many vendors have made proprietary extensions to PTP, so additional implementation is required for each camera. Thus, supporting a new camera means writing code to support such extensions. CLI tools like gphoto2 and tethering software like Capture One and Dragonframe have collections of vendor-specific implementations for various cameras internally.

Class hierarchy of Tethr

Tethr is designed to support not only USB-connected cameras but also webcams and potentially wireless cameras in the future. For the extensibility, it has several abstraction layers as follows:

  • Tethr: A base abstract class representing a camera object. It returns 'unsupported' for all settings and operations.

In most cases, the camera you want to add support for will be implemented as a subclass of TethrPTPUSB.

Implementation steps

To implement the support code, fork the repository and follow the steps below. Run' yarn dev' to start the demo screen for debugging.

  1. Create a subclass that inherits from TethrPTPUSB and save it with the file name src/TethrPTPUSB/Tethr{YourCamera}.ts.

  2. Override the methods of the new class according to the vendor-specific extensions.

    • e.g. open, close, set{ConfigName}, get{ConfigName}Desc
    • You don't need to override get{ConfigName} because it is already implemented based on get{ConfigName}Desc.
    • You can use the this.device: PTPDevice for communication with the camera, which provides functions compliant with the PTP specification. You can also access the this.device.usb: USBDevice for lower-level communication.
  3. Add code to getVendorSpecificPTPUSBClass to detect the new camera based on the descriptor and return the corresponding subclass.

How to analyze the vendor-specific protocol

Since many camera SDKs are provided as dynamic libraries, information such as byte array structures can only be obtained by reverse engineering. Here are some methods for doing so.

Reading the official SDK

You can infer vendor-specific OpCode, EvenCode, and DevicePropCode from the documents and library headers included in the SDK. For example, the following is a header of Panasonic's LUMIX SDK, from which you can read that the DevicePropCode for the shutter speed is 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,
};

Also, the SDKs often include sample applications that display the communication content. For example, the following is a sample app of Sigma fp, from which you can grasp the packet structure.

image

Seeking the corresponding code in libgphoto2

For instance, [ptp.h]](https://github.com/gphoto/libgphoto2/blob/master/camlibs/ptp2/ptp.h) declares constants for vendor-specific IDs and codes. You may find hints for the implementation you want by searching for the camera name in the repository. Note that if you refer to the source code, you should comply with the license of the source code and add the URL or attribution to the source code you referred to as a comment if necessary.

Using Wireshark

The most reliable way to analyze packet communication is to use Wireshark to peek at USB communication while operating a tethring software that supports the camera.

After starting Wireshark, install the necessary modules for USB communication analysis, select the port corresponding to the USB connection of the camera from the list (e.g., XHC20), and enter `_ws.col.info contains "(completed)" in the filter. Each line represents the communication between the camera and the PC.

  • URB_BULK out (completed): PC to camera
  • URB_BULK in (completed): Camera to PC

image

The part of the Hex code related to PTP is the Leftover Capture Data (highlighted in blue). Analyze it regarding the PTP specification, as mentioned below.

Note

Follow the steps below if you use a Mac with Apple Silicon.

  1. Boot in recovery mode and disable SIP.
  2. Execute sudo ifconfig XHC# up for each port starting with XHC displayed in Wireshark.

Key points of the PTP specification

The PTP specification is defined in PIMA 15740:2000, and its USB adaptation is published in PDF. Here are some key points summarized.

Communication types

PTP has three communication types, each corresponding to the method name of PTPDevice.

  • sendCommand: Send a short command to the camera (e.g., exposure mode, shutter)
    sequenceDiagram
    		autonumber
    		Host->>+Camera: Command (+OpCode)
    		Camera->>+Host: Response (+ResCode)
    
    Loading
  • sendData: Send data to the camera
    sequenceDiagram
    		autonumber
    		Host->>+Camera: Command (+OpCode)
    		Host->>+Camera: Data (+Payload)
    		Camera->>+Host: Response (+ResCode)
    
    Loading
  • receiveData: Retrieve data from the camera
    sequenceDiagram
    		autonumber
    		Host->>+Camera: Command (+OpCode)
    		Camera->>+Host: Data (+Payload)
    		Camera->>+Host: Response (+ResCode)
    
    Loading

Additionally, there is a communication type for the camera to interrupt and notify the host of camera settings changes.

sequenceDiagram
	autonumber
	participant Host
	Camera->>+Host: Event (+EventCode)
Loading

To listen for interrupt notifications from the camera, implement the onEventCode method as follows.

// In the method of TethrPTPUSB
this.devce.onEventCode(EventCode, (event: PTPEvent) => {
	// Do something
})

Packet structure

The packet structure is always in little-endian. For example, 0x12345678 is encoded as 78 56 34 12.

Field Size Description
Length 4 The total number of bytes in the packet (including the Length field itself)
PTPBlockType 2 Block type
Code 2 The type of code
Transaction ID 4 ID assigned for each communication
Payload ?? Actual data

The types of packets (called “block” in PTP) are classified into Command, Data, Response, and Event. The contents of Code and Payload differ depending on the type.

  • 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)

The following is an example of 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

Code types

  • OpCode: The command type sent to the camera.
  • ResCode: The type of response from the camera.
  • EventCode: The type of interrupt event from the camera.

Note

In PTP specs, OpCode stands for OperationCode, ResCode stands for ResponseCode.

Device properties

Settings such as exposure mode and shutter speed are called DeviceProp, each assigned a unique integer value ID called DevicePropCode. Information such as default value, value range, and list of configurable values are represented by a structure called DevicePropDesc. Commands such as OpCode.GetDevicePropDesc and OpCode.SetDevicePropValue are used to read and write settings.

Note

Prop is sometimes referred to as Property, and Desc as Descriptor in some documents.

Hints

Here are some tips that may be helpful when analyzing vendor-specific protocols.

  • It might be good to refer to the implementation examples for each vendor in TethrPTPUSB.

  • About the Sigma fp:

    • It does not support interrupt notification. Instead, you need to periodically send receiveData(OpCodeSigma.GetCamStatus2) to update the camera status.
    • The settings and descriptors are represented in a data format called IFD, also used in Exif.
  • Some cameras disable operations on the body when tethered.

  • The focus position settings for lenses that support AF can be specified as absolute values, like with the Sigma fp, or only offset as relative values, like with LUMIX.

  • Live view display is not included in the PTP specifications. It is necessary to analyze the camera-specific specifications.

  • There are generally extended codes like receiveData(OpCodeVendor.LiveviewImage). Often, a part of the payload becomes JPEG data as is. It might be good to look for the magic number ff 08 in the received packets. The last few bytes of a proprietary payload may also be a checksum.

  • For creating packets, in addition to using DataView, it is convenient to use PTPDataView.

Ask someone to do it

If you are not a developer but want to support a camera in an app that uses Tethr, such as Koma, you can create an issue to request support for the camera. Also, it would be helpful for me as a developer if you consider sponsoring me on GitHub Sponsors.