Status: recovered by static analysis of the official Linux SDK and implemented
in src/camera/insta360/link2c.rs via the frame command. Readback and
GET_LEN/GET_INFO have been validated on a physical camera. The camera owner
subsequently confirmed that framing writes work in manual testing. That is
user-reported hardware validation, not an automated visual-motion test.
No SDK executable was run.
Official repository: https://github.com/Insta360Develop/Link-SDK
Analyzed revision: 2dd2c78186b0335a530042ae49accbef249d6a95
Binary: UVCCamera_Linux/bin/libUVCCamera.so (x86-64 ELF)
SHA-256: 1caf4198fc10f8f16ace1f93263fa8627f7cfb60902688549faac48863798061
Public declarations: UVCCamera_Linux/include/uvc_common.h (HostPTZInfo)
and uvc_camera.h (GetHostPTZ / SetHostPTZ). Example:
UVCCamera_Linux/test/main.cc, case 1108.
Inspect without executing the SDK:
nm -DC libUVCCamera.so | grep HostPTZ
objdump -dC -Mintel libUVCCamera.so > sdk.asm- Transport:
UVCIOC_CTRL_QUERYon the video node, not libusb. - Extension GUID:
E307E649-4618-A3FF-82FC-2D8B5F216773. - Unit ID on the attached Link 2C: 10. Resolve/validate by GUID.
- Selector: 0x13.
- Request:
UVC_SET_CUR(0x01). - Logical payload: 8 bytes.
| Byte offset | Encoding | Meaning |
|---|---|---|
| 0 | uint16 little-endian | Zoom ratio; example accepts 100..400 (1x..4x) |
| 2 | uint16 little-endian | Horizontal crop center, normalized X times 65535 |
| 4 | uint16 little-endian | Vertical crop center, normalized Y times 65535 |
| 6 | uint8 | Movement step; SDK example uses 20 |
| 7 | uint8 | Zoom step; SDK example uses 20 |
Encoding example, not a hardware test:
import struct
payload = struct.pack('<HHHBB', 200, 32767, 32767, 20, 20)
assert payload.hex(' ') == 'c8 00 ff 7f ff 7f 14 14'This corresponds to 2x zoom and approximately centered X/Y using the SDK's truncating normalized-coordinate conversion. There is no separate left/right opcode: change X or Y while preserving the other position and zoom values. Screen-direction polarity, edge clamping, step timing, and interaction with Auto Framing remain to be checked visually on hardware.
The SDK first sends GET_LEN (0x85, two-byte result) on the same unit/selector.
It allocates a zero-filled buffer of that returned length, copies the first
min(length, 8) payload bytes, and sends SET_CUR. If GET_LEN fails it falls
back to 8. A linkctl implementation should reject unexpected lengths rather
than blindly copying this truncation/zero-padding behavior.
There is no checksum, extra packet header, AI-mode write, or driver detach in this setter's call chain.
Do not confuse this with unit 9 / selector 0x13, the head/half/whole-body composition selector documented for other Link functionality.
ELF virtual addresses in the analyzed binary:
0x70d82:LinuxUVCCameraExtendController::SetHostPTZbuilds 8 bytes.0x70de3through0x70e5d: copies three uint16 values and two bytes.0x70e82: selector argument is 0x13.0x70e8a: callsSendCommand2.0x68b28:SendCommand2resolvesGetExtensionUnit2Id, obtains GET_LEN, then sends the payload throughSetData.0x823fe: descriptor parser matcheskExtUnit2Guidat0x8d8f0. Its raw GUID bytes are49 e6 07 e3 18 46 ff a3 82 fc 2d 8b 5f 21 67 73.0x80e44:LinuxUVCDeviceImp::SetDataselects request 0x01.0x80aa1:CtrlQueryuses ioctl0xc0107521(UVCIOC_CTRL_QUERY on x86-64).
The SDK's GetHostPTZ does not read unit 10 / selector 0x13. It calls
GetVideoMode and extracts coordinates from the mode-state payload:
- Extension GUID:
FAF1672D-B71B-4793-8C91-7B1C9B7F95F8. - Unit ID on the attached Link 2C: 9. Resolve/validate by GUID.
- Selector: 0x02.
- Request:
UVC_GET_CUR(0x81), after GET_LEN. - X: uint16 little-endian at byte offsets 38..39 (0x26).
- Y: uint16 little-endian at byte offsets 40..41 (0x28).
The SDK explicitly selects this decoding for PIDs 0x4c03 and 0x4c05.
The public VideoMode enum also identifies 0x07 as AutoFraming. The linkctl
implementation accepts mode 0x00, 0x01, and 0x07; it rejects special modes,
unknown modes, and the 0xFF idle/transition sentinel. It only
reaches these fields when the mode-state payload has at least 52 bytes;
46-byte special-mode payloads take a separate path. The attached camera
previously reported a 56-byte mode payload through linkctl info.
GetHostPTZ fills only zoomX and zoomY; it does not populate ratio or the
step fields. Do not assume a zero-initialized structure returned by it is
safe to pass directly to SetHostPTZ. Read/preserve zoom separately.
Static evidence: GetHostPTZ at 0x5f748; GetVideoMode at 0x6a2e2;
PID 0x4c03 comparison at 0x6a4d3; X/Y extraction at 0x6a4ea / 0x6a503.
On the connected Link 2C (2e1a:4c03, /dev/video0):
- Unit 10 / 0x13 GET_LEN returned
08 00(8 bytes). - Unit 10 / 0x13 GET_INFO returned
03(GET and SET supported). - Mode 0x07, 56-byte mode payload: frame readback was X=32767, Y=32767, zoom=100 (approximately centered at 1x).
The CLI rounds normalized input to the nearest uint16 coordinate rather than truncating as the SDK example does: 0.5 encodes to 32768. Unspecified coordinates retain their exact raw readback values.
The raw transport and encoding are identified, and framing writes have been confirmed working by the camera owner. Edge behavior and Auto Framing interactions still need broader validation. Keep Link 2 gimbal controls separate from Link 2C digital crop controls, preserve the activity guard, validate GUIDs and lengths, and use readback plus a visible stream to test the result. Never write to unit 9's mode payload merely to move the crop.