Spaces:
Running
Running
stouchinglhh Claude Code commited on
Commit ·
383d0b9
1
Parent(s): 849e0ae
collector: 更新采集工程至 v1.3.5(RGB 断联根因=DECXIN 停在等时档 alt7 每 68~116 秒静默停摆,改自适应档位:libuvc 新增 uvc_set_altsetting_override() 运行时接口、起始取最保守档、连停 2 次或连开不起来 3 次自动降档并落盘;camera-service 加静默卡死自愈与 SIGTERM 有界收尾;夹爪扫描配对从 USB 根端口改走 ESP32 NVS 序列号;s80c 上色工具修隔帧重复。另:不含误提交的 rec003.zip)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- collector/.gitignore +2 -0
- collector/README.md +164 -1
- collector/config/__init__.py +1 -1
- collector/core/gripper/camera_service_src/CMakeLists.txt +100 -0
- collector/core/gripper/camera_service_src/README.md +181 -0
- collector/core/gripper/camera_service_src/build.sh +244 -0
- collector/core/gripper/camera_service_src/src/camera_service.c +1514 -0
- collector/core/gripper/camera_service_src/tests/test_altsetting_ladder.c +202 -0
- collector/core/gripper/camera_service_src/tests/test_frame_integrity.c +45 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/CMakeLists.txt +230 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/LICENSE.txt +31 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/README.md +29 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/isight_imac.txt +228 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/isight_macbook.txt +228 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/logitech_hd_pro_920.txt +1817 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/ms_lifecam_show.txt +767 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/quickcampro9000.txt +1543 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/quickcampro9000_builtin_ctrls.txt +13 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cameras/quickcampro9000_extra_ctrls.txt +18 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/changelog.txt +140 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cmake/FindJpegPkg.cmake +82 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cmake/FindLibUSB.cmake +51 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/cmake/FindOpenCVPkg.cmake +86 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/doxygen.conf +0 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/include/libuvc/libuvc.h +833 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/include/libuvc/libuvc_config.h.in +22 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/include/libuvc/libuvc_internal.h +326 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/include/utlist.h +490 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/libuvc.pc.in +11 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/libuvcConfig.cmake +17 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/ctrl-gen.c +2259 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/ctrl-gen.py +302 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/ctrl.c +165 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/device.c +2005 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/diag.c +376 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/example.c +226 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/frame-mjpeg.c +222 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/frame.c +475 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/init.c +163 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/misc.c +58 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/stream.c +1752 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/src/test.c +153 -0
- collector/core/gripper/camera_service_src/third_party/libuvc/standard-units.yaml +518 -0
- collector/core/gripper/camera_service_src/tools/discover_uvc_config.c +1463 -0
- collector/core/gripper/camera_service_src/tools/sonix_flash_probe.c +618 -0
- collector/core/gripper/devices/gripper_serial.py +67 -15
- collector/core/gripper/devices/uvc_camera_service.py +195 -36
- collector/core/gripper/fays_serial_probe.py +23 -2
- collector/core/gripper/fays_single.py +170 -81
- collector/core/gripper/paths.py +6 -0
collector/.gitignore
CHANGED
|
@@ -28,6 +28,8 @@ venv_lite/
|
|
| 28 |
.claude/
|
| 29 |
keypoints_output/
|
| 30 |
exports/
|
|
|
|
|
|
|
| 31 |
tools/weights/
|
| 32 |
wheels/ # scripts/pack_wheels.py 生成的离线部署包(不入库,拷给客户用)
|
| 33 |
imgui.ini
|
|
|
|
| 28 |
.claude/
|
| 29 |
keypoints_output/
|
| 30 |
exports/
|
| 31 |
+
# 本地取回的录制素材与上色产物(双目原视频等,体积大且属外部数据)
|
| 32 |
+
rec001(1)/
|
| 33 |
tools/weights/
|
| 34 |
wheels/ # scripts/pack_wheels.py 生成的离线部署包(不入库,拷给客户用)
|
| 35 |
imgui.ini
|
collector/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# collector — Multimodal Data Acquisition SDK · 多模态数据采集 SDK
|
| 2 |
|
| 3 |
-

|
| 5 |

|
| 6 |
|
|
@@ -361,6 +361,128 @@ are documented in [docs/index.md](docs/index.md#开发约定).
|
|
| 361 |
|
| 362 |
### Changelog
|
| 363 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
- **v1.3.4** — a newly plugged-in gripper now generates its own runtime
|
| 365 |
calibration and connects, with no manual step. Previously onboarding a new
|
| 366 |
Fays S80M took two manual steps — click `device_setup` on the gripper's host
|
|
@@ -833,6 +955,47 @@ i18n 文案经 `tr()` 翻译、PyQt5 信号参数用 `object` 封送大整数、
|
|
| 833 |
|
| 834 |
### 更新记录
|
| 835 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
- **v1.3.4** — 新夹爪接入即自动生成运行标定并连接,不再需要任何人工步骤。
|
| 837 |
此前接入一只新 Fays S80M 要人工两步——先在夹爪上位机点 `device_setup`、
|
| 838 |
再在采集机跑 `tools/import_gripper_calibration.py` 导入,漏掉任一步主程序
|
|
|
|
| 1 |
# collector — Multimodal Data Acquisition SDK · 多模态数据采集 SDK
|
| 2 |
|
| 3 |
+

|
| 4 |

|
| 5 |

|
| 6 |
|
|
|
|
| 361 |
|
| 362 |
### Changelog
|
| 363 |
|
| 364 |
+
- **v1.3.5** — the camera service heals itself, and gripper scan pairing moved
|
| 365 |
+
from USB root ports to NVS serials.
|
| 366 |
+
**Cameras:** the UVC cameras (DECXIN `1bcf:2d4f` plus two Sightac `0c45:636f`)
|
| 367 |
+
can wedge silently with no process touching them — on 2026-09-15 the right
|
| 368 |
+
Sightac was streaming normally at 21:53 and simply refused to start at 09:24,
|
| 369 |
+
with no usbfs or uvcvideo message in between and the machine awake all night.
|
| 370 |
+
What the user sees ("RGB shows only the first frame", "camera-service did not
|
| 371 |
+
obtain valid MJPG frames from all cameras within the time limit") is really a
|
| 372 |
+
failure to recover, not a failure to stay up. Two different wedges need two
|
| 373 |
+
different cures: a **stall** (stream running, `received` freezes, not a single
|
| 374 |
+
kernel message, 6–82 s at random, load-correlated) recovers from rebuilding
|
| 375 |
+
the libusb context alone (6/6 and 5/5 measured — a USB reset is a wasted
|
| 376 |
+
cost), while a **hard failure** (`uvc_start_streaming failed: I/O error`,
|
| 377 |
+
`UVC format/interface negotiation failed: Invalid mode`, or
|
| 378 |
+
`negotiated_payload` alternating 3072/0) does not (9 consecutive attempts
|
| 379 |
+
failed; the device firmware is wedged) and only a USB reset / re-enumeration
|
| 380 |
+
clears it. Added a 3-second stall watchdog (`STREAM_STALL_TIMEOUT_SECONDS`,
|
| 381 |
+
4 s for the first frame); stalls rebuild the libusb context, hard failures
|
| 382 |
+
escalate to a USB reset after **2 consecutive** attempts (capped at
|
| 383 |
+
`USB_RESET_LIMIT` 3, since without a cap a genuinely dead device would reset
|
| 384 |
+
every 2 seconds and pin the two neighbours on the same hub), and a stall
|
| 385 |
+
clears that counter so stalls can never add up into a spurious reset. The
|
| 386 |
+
reset now happens **before** `uvc_stop_streaming` (`teardown_failed_open`):
|
| 387 |
+
on a wedged device `uvc_stop_streaming` waits for isochronous transfers that
|
| 388 |
+
never return, which measurably burns the client's 2-second grace period and
|
| 389 |
+
gets the process SIGKILLed mid-teardown, leaving the device on an active alt
|
| 390 |
+
setting so the next start fails the same way. Every threshold is derived
|
| 391 |
+
backwards from the client's `SERVICE_START_TIMEOUT_S = 10.0` (second failure
|
| 392 |
+
at t≈2 s → reset → frames by t≈3 s); the old "reset after 20 s of zero
|
| 393 |
+
frames" gate could never fire in a real start, and was attached to the
|
| 394 |
+
zero-frame branch the observed `I/O error` never reaches. Measured reset
|
| 395 |
+
semantics: on a healthy device it is an in-place port reset (kernel logs
|
| 396 |
+
`reset high-speed USB device number N`, devnum unchanged); on a wedged device
|
| 397 |
+
the device drops and re-enumerates (devnum changes) — that is what heals it,
|
| 398 |
+
and libusb reports `rc=-4 (No such device)` during that window, which is
|
| 399 |
+
misleading but harmless. Also fixed the per-second fps reporting baseline
|
| 400 |
+
(it counted from zero, so a restart reported the whole process lifetime as
|
| 401 |
+
one window: 164/207/251 fake fps).
|
| 402 |
+
**RGB disconnects, root cause:** self-healing was only half the story — the
|
| 403 |
+
camera still dropped out afterwards. DECXIN stalls silently every 68–116 s
|
| 404 |
+
while running the high-bandwidth isochronous setting alt7, on **both**
|
| 405 |
+
controllers tested (AMD `1022:43fc`/PCI `0000:0a:00.0` and
|
| 406 |
+
`1022:15b7`/`0000:74:00.4`). The mechanism is **not determined**: measured
|
| 407 |
+
throughput at the moment of the stalls was only 1.66 MB/s mean / 2.63 peak,
|
| 408 |
+
far under the 10.24 MB/s alt7 reserves, so "not enough bandwidth" does not
|
| 409 |
+
explain it. alt6 holds, and both rungs deliver **identical 30.00 fps**, so
|
| 410 |
+
alt7's extra per-frame headroom (327680 vs 241664 B) buys nothing observable.
|
| 411 |
+
Two earlier conclusions are retracted: *"alt7 is fine on bus 1"* — that
|
| 412 |
+
supposedly clean leg never ran at alt7 at all, because the old binary's ini
|
| 413 |
+
fields were inert and the service merely echoed them, so the only trustworthy
|
| 414 |
+
indicator of a leg's real setting is the `[LIBUVC-QUIRK] ... alt=N payload=M
|
| 415 |
+
... xfers=` line; and the controller-specific claim that followed from it.
|
| 416 |
+
`forced_altsetting`/`forced_payload` are now **authoritative** through a new
|
| 417 |
+
libuvc runtime API `uvc_set_altsetting_override()` (declared in `libuvc.h`,
|
| 418 |
+
implemented in `device.c`) — before this they were only validated and
|
| 419 |
+
printed, so editing the ini changed nothing, which is very easy to misread.
|
| 420 |
+
The starting rung is chosen in Python, not hardcoded per machine: learned >
|
| 421 |
+
the table's most conservative rung > the scanner's preferred one, because the
|
| 422 |
+
rack's bus is not permanent. A ladder then steps down one rung and persists
|
| 423 |
+
the conclusion under `vid:pid × controller PCI path × camera serial` after 2
|
| 424 |
+
consecutive stalls or 3 consecutive failed opens (the latter covers a
|
| 425 |
+
setting that will not fit at STREAMON, where the stream never starts and the
|
| 426 |
+
stall path can never fire); `CLEAN_RUN_SECONDS = 300` clears the stall
|
| 427 |
+
counter. A downgrade must clear **both** counters — the thresholds are
|
| 428 |
+
equality tests, so a counter left standing on its threshold never fires
|
| 429 |
+
again and the failed-open path would jam after a single step. Both log lines
|
| 430 |
+
capture the count *before* the downgrade clears it. The scanner truthfully
|
| 431 |
+
reports the descriptor's preferred rung (alt7); the app checks it is *any*
|
| 432 |
+
rung of the ladder and refuses only on a genuine desync. The C source and its
|
| 433 |
+
build recipe moved into `core/gripper/camera_service_src/` (`./build.sh` is
|
| 434 |
+
the single entry point), and `--test` now runs `ctest` — the ladder test was
|
| 435 |
+
registered with `add_test` but had never actually been executed, and the
|
| 436 |
+
jam-bug above fell exactly inside what it should have covered. Verified on
|
| 437 |
+
hardware: forced alt7 stalled at 78 s and 116 s, the service logged the
|
| 438 |
+
downgrade and recovered to alt6 for 169 s at 30 fps with zero stalls; the
|
| 439 |
+
app-generated ini (alt6 default) then ran **600 s across three legs with 0
|
| 440 |
+
stalls / 0 rebuilds / 0 failed opens / 0 resets** (17513/17496/17487 frames),
|
| 441 |
+
and a post-install 300 s regression on the deployed binary was clean too. The
|
| 442 |
+
ladder test was reverse-verified as non-vacuous: removing the counter reset
|
| 443 |
+
makes it abort.
|
| 444 |
+
**Grippers:** ported the scan chain from the delivery package
|
| 445 |
+
`设备扫描逻辑与程序_20260914`. Pairing used to go through USB root
|
| 446 |
+
ports/controllers — a premise that was simply wrong, since the Fays is a
|
| 447 |
+
USB3/FT602 device on its own 5000M bus and never shared a root with the
|
| 448 |
+
UVC/ESP32 devices. The chain is now: UVC group → the unique ESP32 (path
|
| 449 |
+
starting `bus-outer.`) → send `QF` over serial to read the Fays serial bound
|
| 450 |
+
in its NVS → match against the official SDK's enumerated `device.serial`,
|
| 451 |
+
with a clear refusal when any step is not unique and no fallback to guessing
|
| 452 |
+
by port or enumeration order. The SDK probe is preceded by a
|
| 453 |
+
`validate_fays_superspeed` pre-check and then enumerates serials fast
|
| 454 |
+
(`--serial-only-fast`; a timeout now decodes stdout/stderr and appends the
|
| 455 |
+
last 20 lines instead of reporting a bare timeout). Dual-rig runs skip a
|
| 456 |
+
device another rig holds instead of claiming it. On the serial side the fixed
|
| 457 |
+
boot delay became a 2-second polling `?` handshake, the port is opened
|
| 458 |
+
`exclusive=True`, and `QF` (query) / `WF:<serial>` (write) commands were
|
| 459 |
+
added; `GripperSerial.send()` looked commands up by their first character
|
| 460 |
+
only (`[:1]`), so `"QF"` could never match its own expected list. Prerequisite:
|
| 461 |
+
the ESP32 firmware must understand `QF`/`WF:` — the current `online/esp32_proc`
|
| 462 |
+
firmware does not, and will fail closed with "查询 Fays 序列号失败: ERR".
|
| 463 |
+
Added the offline self-check `tools/tests/test_gripper_fays_pairing.py`.
|
| 464 |
+
**s80c colorize tool:** fixed a duplicated-every-other-frame bug in
|
| 465 |
+
`tools/s80c_arm_convert/convert_arm_dataset.py` (an independent fix committed
|
| 466 |
+
after v1.3.4). `pooled_episodes_v1` recordings write **two rows per frame**
|
| 467 |
+
(`frame_index` = 0,0,1,1,…, with identical `timestamp`/`hardware_ns` in both
|
| 468 |
+
rows) while the video has one frame; the tool used the row count as the frame
|
| 469 |
+
count and then indexed `hw_ns[i]` by video frame number, so every timestamp
|
| 470 |
+
was sent twice — and the offline ISP reuses the previous output for a
|
| 471 |
+
duplicate timestamp, which is what duplicated every other frame. Measured on
|
| 472 |
+
rec001's lossless output: 394 of 833 frames byte-identical to their
|
| 473 |
+
predecessor, all 394 at odd positions (the second of each timestamp pair),
|
| 474 |
+
versus 0 in the source video. The same root cause also **halved the IMU
|
| 475 |
+
sidecar** — `hw_ns[:n_frames]` only reached the 417th timestamp and every
|
| 476 |
+
later IMU sample was dropped by the "later than the last frame" rule, leaving
|
| 477 |
+
416 rows / 13.8 s; after the fix, 832 rows / 27.7 s against a 27.8 s video.
|
| 478 |
+
Redundant rows are now folded by `frame_index`, but **only when every row
|
| 479 |
+
under that index has identical `timestamp` and `hardware_ns`**; otherwise it
|
| 480 |
+
warns and leaves the data untouched. Of the 142 parquets carrying
|
| 481 |
+
`frame_index`, 129 already had one row per frame (no-op), 1 was genuinely
|
| 482 |
+
redundant (rec001, 1666 rows → 833 frames), and 12 had differing timestamps
|
| 483 |
+
between rows (folding would lose them, so they are warn-only). The colorize
|
| 484 |
+
chain itself is untouched: same config, same offline ISP, `--sdk-wb-auto` as
|
| 485 |
+
before, with grey-world gains bit-identical to the pre-fix run.
|
| 486 |
- **v1.3.4** — a newly plugged-in gripper now generates its own runtime
|
| 487 |
calibration and connects, with no manual step. Previously onboarding a new
|
| 488 |
Fays S80M took two manual steps — click `device_setup` on the gripper's host
|
|
|
|
| 955 |
|
| 956 |
### 更新记录
|
| 957 |
|
| 958 |
+
- **v1.3.5** — 相机服务能自愈了;夹爪扫描配对从 USB ��端口改走 NVS 序列号。
|
| 959 |
+
**相机侧**:UVC 相机(DECXIN `1bcf:2d4f` 与两个 Sightac `0c45:636f`)会**静默
|
| 960 |
+
卡死、且不需要任何进程碰它**——2026-09-15 实证右目 Sightac 是自己夜里坏的
|
| 961 |
+
(21:53 还在正常推流,09:24 启动就坏,中间没有任何进程打开过它、内核无
|
| 962 |
+
usbfs/uvcvideo 消息,机器整夜没睡)。用户看到的「RGB 只剩第一帧」「未在限定
|
| 963 |
+
时间取得全部相机的有效 MJPG 帧」,根子是**服务不会自愈**,不是**服务会倒**。
|
| 964 |
+
两种卡死要分开治:**停摆**(开着流突然不出帧、`received` 冻住、内核一条消息
|
| 965 |
+
都没有,6~82 秒随机、与负载正相关)**重建 libusb 上下文就够**(实测 6/6、5/5
|
| 966 |
+
全恢复,USB 复位是白付的代价);**开不起来**(`uvc_start_streaming failed:
|
| 967 |
+
I/O error`、`UVC format/interface negotiation failed: Invalid mode`、
|
| 968 |
+
`negotiated_payload` 在 3072↔0 之间跳)**重建上下文救不回来**(实测连试 9 次
|
| 969 |
+
全败,设备固件侧卡死了),只有 USB 复位/重枚举能洗。新增 3 秒停滞看门狗
|
| 970 |
+
(`STREAM_STALL_TIMEOUT_SECONDS`,首帧 4 秒);停摆(`rc>0`)重建 libusb
|
| 971 |
+
上下文;开不起来(`rc<0`)**连续 2 次**升级成 USB 复位,`USB_RESET_LIMIT` 3 次
|
| 972 |
+
封顶(设备真坏了时不设上限会变成每 2 秒复位一次、把同 Hub 的另外两路一直按在
|
| 973 |
+
地上),而停摆会清零该计数,免得几次停摆凑数误触发复位去打扰邻居。**复位要抢在
|
| 974 |
+
`uvc_stop_streaming` 之前**(`teardown_failed_open`)——卡死设备上
|
| 975 |
+
`uvc_stop_streaming` 会一直等它那批永远回不来的等时传输,实测能把客户端那 2 秒
|
| 976 |
+
宽限耗光而被 SIGKILL,进程死在这中间、设备就停在激活的 alt setting 上,下一轮
|
| 977 |
+
照样起不来(09:24 的日志正是断在 startup failed 与 usb reset 之间)。所有阈值由
|
| 978 |
+
客户端窗口 `SERVICE_START_TIMEOUT_S = 10.0` 倒推:第 2 次失败在 t≈2 秒触发复位、
|
| 979 |
+
t≈3 秒就能出帧;旧的「重开 20 秒零帧才复位」在真实启动场景**永远够不到**,且只
|
| 980 |
+
挂在零帧分支上。复位语义实测:打在**健康**设备上是原地端口复位(devnum 不变),
|
| 981 |
+
打在**卡死**设备上是掉线重枚举(devnum 变了)**这才会好**,libusb 在那窗口里返回
|
| 982 |
+
`rc=-4 (No such device)` 是报错但事办了。真机验证:第 1 次失败不复位、第 2 次
|
| 983 |
+
`usb reset: rc=0 (Success)`、打满 3 次后停手;三路 90 秒回归 0 停摆 0 误复位
|
| 984 |
+
(2694/2683/2684 帧 ≈29.9fps);SIGTERM 退出 1.16 秒,不超客户端 2 秒宽限。
|
| 985 |
+
**夹爪侧**:移植交付包「设备扫描逻辑与程序_20260914」。配对从 **USB 根端口/
|
| 986 |
+
控制器**改走 **NVS 序列号**——旧规则(外加「同一 USB2 总线出现双 rig 就拒扫」)
|
| 987 |
+
的前提就是错的:Fays 是 USB3/FT602 挂在配套的 5000M 总线上,跟 UVC/ESP32 本来
|
| 988 |
+
就不共根,只能靠猜。新链路:UVC 组 → 唯一 ESP32(`bus-outer.` 开头)→ 串口发
|
| 989 |
+
`QF` 读它 NVS 里绑定的 Fays 序列号 → 与官方 SDK 枚举出的 `device.serial` 比对,
|
| 990 |
+
任一步不唯一就明确拒绝,不再按端口或枚举顺序猜。SDK 探测前先做超速预检,再以
|
| 991 |
+
`--serial-only-fast` 快速枚举序列号(超时不再只报一句 timeout,解码
|
| 992 |
+
stdout/stderr 并附最后 20 行);双 rig 会「在用时跳过」另一 rig 正持有的设备
|
| 993 |
+
而不是占用。串口侧:固定 2 秒死等改成轮询 `?` 握手,端口加 `exclusive=True`,
|
| 994 |
+
新增 `QF` 查询 / `WF:<serial>` 写入;顺带修 `send()` 只拿首字符查命令表
|
| 995 |
+
(`[:1]`)导致 `"QF"` 永远匹配不上自己期望表的 bug。**前置条件:ESP32 固件必须
|
| 996 |
+
认识 `QF`/`WF:`,当前 `online/esp32_proc` 的固件还不认识,会失败关闭并报
|
| 997 |
+
「查询 Fays 序列号失败: ERR」,需先烧固件。** 新增离线自检
|
| 998 |
+
`tools/tests/test_gripper_fays_pairing.py`。
|
| 999 |
- **v1.3.4** — 新夹爪接入即自动生成运行标定并连接,不再需要任何人工步骤。
|
| 1000 |
此前接入一只新 Fays S80M 要人工两步——先在夹爪上位机点 `device_setup`、
|
| 1001 |
再在采集机跑 `tools/import_gripper_calibration.py` 导入,漏掉任一步主程序
|
collector/config/__init__.py
CHANGED
|
@@ -3,4 +3,4 @@
|
|
| 3 |
版本号集中定义于此:settings.APP_VERSION 及其他模块均从这里取值。
|
| 4 |
"""
|
| 5 |
|
| 6 |
-
__version__ = "1.3.
|
|
|
|
| 3 |
版本号集中定义于此:settings.APP_VERSION 及其他模块均从这里取值。
|
| 4 |
"""
|
| 5 |
|
| 6 |
+
__version__ = "1.3.5" # 夹爪扫描配对改走 ESP32 NVS 序列号 + camera-service 自愈(两个都是「不恢复」的毛病,不是「会坏」的毛病)。相机侧:UVC 相机(DECXIN 1bcf:2d4f + 两个 Sightac 0c45:636f)会**静默卡死且不需要任何进程碰它**,用户看到的「RGB 只剩第一帧」「camera-service 未在限定时间取得全部相机的有效 MJPG 帧」根子是**服务不会自愈**——实证 2026-09-15 右目 Sightac 是自己夜里坏的(21:53 还在正常推流,09:24 启动就坏,中间没有任何进程打开过它、内核无 usbfs/uvcvideo 消息,机器整夜没睡、14.9 万条内核日志里查不到肇事者)→ 别再去查「谁把它弄脏的」,服务必须能自愈。两种卡死要分开治:**停摆**(开着流突然不出帧、`received` 冻住、内核一条消息都没有,6~82 秒随机、与负载正相关)**重建 libusb 上下文就够**(实测 6/6、5/5 全恢复,USB 复位是白付的代价);**开不起来**(`uvc_start_streaming failed: I/O error` / `UVC format/interface negotiation failed: Invalid mode` / `negotiated_payload` 在 3072↔0 之间跳)**重建上下文救不回来**(实测连试 9 次全败,设备固件侧卡死了),只有 USB 复位/重枚举能洗。改动:新增 3 秒停滞看门狗(`STREAM_STALL_TIMEOUT_SECONDS`,首帧 4 秒)——此前整路静默报废没有任何人管;停摆(`rc>0`)走 `uvc_exit`+`uvc_init` 重建上下文;开不起来(`rc<0`)计入 `hard_failures`,**连续 2 次**升级成 USB 复位(`USB_RESET_LIMIT` 3 次封顶——设备真坏了时不设上限会变成每 2 秒复位一次、把同 Hub 的另外两路一直按在地上),而停摆会**清零**该计数,免得几次停摆凑数误触发复位去打扰邻居;**复位要抢在 `uvc_stop_streaming` 之前**(`teardown_failed_open`)——卡死设备上 `uvc_stop_streaming` 会一直等它那批永远回不来的等时传输,实测能把客户端那 2 秒宽限耗光而被 SIGKILL,进程死在这中间、设备就停在激活的 alt setting 上,下一轮启动照样起不来(用户 09:24 的日志正是断在 startup failed 与 usb reset 之间);所有阈值由客户端窗口倒推(`SERVICE_START_TIMEOUT_S = 10.0`:每次失败约 0.3 秒 + 1 秒间隔,第 2 次失败在 t≈2 秒触发复位、重枚举加重开约 1 秒、t≈3 秒就能出帧,10 秒够用;旧的「重开 20 秒零帧才复位」在真实启动场景**永远够不到**——20 > 10,且只挂在零帧分支上,实测到的 `I/O error` 根本不走那条);复位语义实测——打在**健康**设备上是原地端口复位(内核 `reset high-speed USB device number N`,**devnum 不变**),打在**卡死**设备上是掉线再重枚举(`USB disconnect` + `new high-speed USB device`,**devnum 变了**)**这才会好**,而 libusb 在掉线重枚举的窗口里返回 `rc=-4 (No such device)` 是**报错但事办了**、别被它误导;`libusb_reset_device` 用 `dlsym(RTLD_DEFAULT, …)` 取,不新增 NEEDED、不需要 root(设备节点 udev MODE=0666);顺带修每秒 fps 上报基线(`previous_received` 从 0 起算,重开后把进程启动至今的全部帧数当成本窗口增量,算出 164/207/251 的假速率——纯日志问题,但会把人带偏)。真机验证:确定性升级测试(第 1 次失败不复位、第 2 次 `usb reset: rc=0 (Success)`、打满 3 次后停手)、三路 90 秒回归 0 停摆 0 误复位(2694/2683/2684 帧 ≈29.9fps)、SIGTERM 退出 1.16 秒(旧 1.13 秒)不超客户端 2 秒宽限。**源码位置**:`online/` 在 .gitignore:56 里、不入库;本次把这两个程序的源码连同构建配方搬进了 `core/gripper/camera_service_src/`(`./build.sh` 是唯一构建入口——它替我们处理 libusb 的 pkg-config shim、`-DCMAKE_POLICY_VERSION_MINIMUM=3.5`、以及链接后去掉 RUNPATH 里那个空条目,手工 cmake 会在这些地方卡住;产物装到 `core/gripper/native/camera_service/build/`,旧件按 `.pre_rebuild_<时间戳>` 备份),以后不必再从 libusb 头文件、pkg-config、RUNPATH 那一整串摸起。交付副本 `programs/camera_service/src/camera_service.c` 未动、与 `程序清单.txt` 第 18 行一致。**RGB 断联���根因(2026-09-15 查明,本条是用户「还是会断联」的正面回答):服务自愈之后相机仍会断,断的原因是 DECXIN 跑在高带宽等时档 alt7 上时每 68~116 秒静默停摆一次,两块控制器(AMD `1022:43fc`/PCI `0000:0a:00.0` 与 `1022:15b7`/`0000:74:00.4`)上都一样。机制**未查明**:停摆当时实际吞吐只有 1.66 MB/s(峰值 2.63),离 alt7 预留的 10.24 MB/s 差得远,「带宽不够」解释不通——**别把机制写死**。alt6 同样三路下长跑不停,而两档出帧率一模一样(都 30.00 fps),alt7 多出来的每帧余量(327680 对 241664 字节)换不到任何看得见的好处。此前两个结论已推翻、别再引用:「bus 1 上 alt7 是好的」——那条所谓干净腿根本没跑在 alt7 上,旧二进制里 ini 的 `forced_*` 是死的、服务只把 ini 的值原样打进日志,读到的 `alt=7 payload=1280` 是它在**复读**,**判断一条腿真实档位只认 `[LIBUVC-QUIRK] ... alt=N payload=M ... xfers=` 那一行**;以及同源的「停摆是控制器特异的」。改动:libuvc 新增运行时接口 `uvc_set_altsetting_override()`(声明在 `include/libuvc/libuvc.h`、实现在 `src/device.c`,`uvc_device_handle_t` 是 calloc 出来的、新字段自动零初始化),`forced_altsetting`/`forced_payload` 从此**真正生效**——此前这两个 ini 字段只用于校验和打印,改 ini 完全不改变行为,极容易误判;起始档位由 app 决定、取**档位表里最保守的一档**(`uvc_camera_service.py` 的 `_starting_mode` 优先级:上一轮学到的 > 最后一档 > 扫描程序报的首选档),**不按机器/控制器写死**(机架插在哪条总线不固定,用户明说不是永久的),曾经那张「敌意控制器」表已删;降档梯子 `maybe_downgrade_altsetting`:当前档位连着停摆 2 次、或连着开不起来 3 次(后者覆盖 STREAMON 阶段就排不下带宽、流根本起不来的情形,停摆那条路永远等不到)就退一档并把结论**落盘**(键 = `vid:pid × 控制器 PCI 路径 × 相机序列号`,一个键一个文件,放 `native/gripper_version1/camera_modes/`——**不能放服务的 runtime 目录,那里每次租约释放整个删掉**),稳定跑满 `CLEAN_RUN_SECONDS=300` 清零停摆计数;**降档必须同时清掉两个计数**——门槛是拿 `==` 比的,只清一个的话另一条路降完一档就永远不再命中,表里剩下的档位形同不存在(DECXIN 只有两档,正好把这个缺陷盖住了);两条路的日志都在调用**之前**抓计数,否则清零后只能打出「0 停摆」,正好把最该看的那次降档说成没有依据;`tools/discover_uvc_config.c` 如实报**相机描述符里的首选档**(alt7),app 拿它跟服务端档位表对一遍、确认这一对是梯子上的任意一档,对不上就报「扫描程序与服务端档位表已经脱节」(那说明两个程序分开编了,必须一起重编);`./build.sh --test` 从点名跑 `test-frame-integrity` 改成走 `ctest`——`tests/test_altsetting_ladder.c` 虽然在 `CMakeLists.txt` 里注册了 `add_test`,此前却一次也没被执行过,上面那个「降一档就卡死」正好落在它本该覆盖的范围内。真机验证:ini 强制 alt7 → 78 秒停摆 → 116 秒再停 → 服务打出 `2 stalls at alt=7; dropping to alt=6 payload=944` → 重开在 alt6 → 169 秒 30fps 零停摆,整条降档+恢复路径走通;装 app 自己生成的 ini(alt6 默认)跑 **600 秒三路:0 停摆 / 0 重建 / 0 启动失败 / 0 复位**,17513/17496/17487 帧(30.00/30.00/29.80 fps),日志里 `[LIBUVC-QUIRK] … alt=6 payload=944 endpoint=944 xfers=32 (altsetting override)` 证明确实跑在 alt6 上、ini 真的生效了;装机后再做一次 300 秒回归同样全零停摆;装机产物与源码构建逐个 md5 相同、RUNPATH 仍只有 `$ORIGIN/third_party/libuvc`。离线侧新增 `tools/tests/test_camera_altsetting_mode.py`(19 条,覆盖最保守档/学到值优先/表外值忽略/扫描程序与服务端脱节必须先报错,并钉死与 C 测试共用的落盘键串),`test_altsetting_ladder.c` 补上「两个计数都要清零」的断言并**反向验证过不是空跑的**(把清零那行注释掉,该测试当场断言中止)。装机后需重启主程序才能同时拿到新的 `uvc_camera_service.py` 与新服务,否则两边档位要求对不上会像 2026-09-15 09:24 那样整个起不来。扫描侧:移植交付包「设备扫描逻辑与程序_20260914」,夹爪与 UVC/ESP32 的配对从 **USB 根端口/控制器**改走 **NVS 序列号**——旧规则(`_bus_root`/`_root_port`,外加「同一 USB2 总线出现双 rig 就拒扫」)的前提就是错的:Fays 是 USB3/FT602 挂在配套的 5000M 总线上,跟 UVC/ESP32 本来就不共根,只能靠猜。新链路:UVC 组 → 唯一 ESP32(`bus-outer.` 开头)→ 串口发 `QF` 读它 NVS 里绑定的 Fays 序列号 → 与官方 SDK 枚举出的 `device.serial` 比对(`_resolve_fays_group` 三步,任一步不唯一就明确拒绝;`_match_esp` 拒收空序列号,不再按端口或枚举顺序猜)。SDK 探测前先 `validate_fays_superspeed` 预检,再以 `serial_only_fast=True` 快速枚举序列号(`fays_serial_probe.py` 加 `--serial-only-fast`;超时不再只报一句 timeout,解码 stdout/stderr 并附最后 20 行),`_probe_serial` 交叉核对 SDK serial 与 NVS 绑定;双 rig 新增「在用时跳过」——`fays_device_guard(port, timeout=0.0)` 探到另一 rig 正持有该设备就跳过而不是占用。串口侧:`CONNECT_BOOT_DELAY = 2.0` 死等改成 `CONNECT_BOOT_TIMEOUT = 2.0` 轮询 `?` 握手(实际尝试次数按真实值上报,不再报上限),打开加 `exclusive=True`,新增 `query_fays_serial()`/`set_fays_serial()`(`QF` 查询 / `WF:<serial>` 写入,值校验 `[A-Za-z0-9._-]{1,64}`);顺带修 `GripperSerial.send()` 只拿首字符去查命令表(`[:1]`)——`"QF"` 因此永远匹配不上自己的期望表,改成先查全命令再退回首字符。UVC 发现程序侧:`_native_env(binary)` 改成优先用二进制自带的 `third_party/libuvc`(不再指望环境里恰好有),`_run_discovery` 加互斥的 `--topology-only`/`--identity-only`/`--skip-flash` 三种扫描深度。**前置条件:ESP32 固件必须认识 `QF`/`WF:`,当前 `online/esp32_proc` 的固件还不认识,会失败关闭并报「查询 Fays 序列号失败: ERR」,需先烧固件。** 新增离线自检 `tools/tests/test_gripper_fays_pairing.py`(假串口 + 假桥接 + 假探针,覆盖 QF/WF 往返、ESP 匹配、按序列号解析、整套 acquire、UVC 关联,并核对部署的原生程序确实内嵌了新参数)。s80c 上色工具修隔帧重复(`tools/s80c_arm_convert/convert_arm_dataset.py`,v1.3.4 之后单独提交的补丁):`pooled_episodes_v1` 这版录制**每帧写两行**(`frame_index` = 0,0,1,1,…,同一帧两行的 `timestamp`/`hardware_ns` 逐值相同)而视频只有一帧,工具把行数当帧数(`n = tbl.num_rows`)、主流程再按视频帧号 1:1 取 `hw_ns[i]`,于是每个时间戳被连送两遍——离线 ISP 对重复时间戳复用上一帧的输出,输出视频就隔帧重复。实测 rec001 无损版 833 帧里 394 帧与前一帧逐字节相同、且 394 个全落在奇数位(每对时间戳的第二个),源视频 0 重复;同一根因还让 IMU 侧车**少一半**(修复前 `hw_ns[:n_frames]` 只够到第 417 个时间戳,其后的 IMU 样本全被「晚于末帧 hw」规则丢掉,输出只剩 416 行 / 13.8 秒,修复后 832 行 / 27.7 秒,与视频时长 27.8 秒对上)。现按 `frame_index` 折叠冗余行,但**只在同一 `frame_index` 名下所有行的 `timestamp` 与 `hardware_ns` 逐值相同时才折**,否则打告警原样不动——全库 142 个带 `frame_index` 的 parquet 里 129 个本就每帧一行(空操作)、1 个真冗余(rec001,1666 行 → 833 帧)、12 个行间时间戳互不相同(折叠会丢时间戳,一律只告警)。上色链路一行未动,同一份 config、同一个离线 ISP、`--sdk-wb-auto` 照旧,灰世界增益与修复前逐位相同。承接 v1.3.4 的新夹爪接入即自动生成运行标定并连接(Fays S80M):此前接入一只新夹爪要人工两步——先在夹爪上位机点 `device_setup`、再在采集机跑 `tools/import_gripper_calibration.py` 导入,漏掉任一步主程序直接拒绝启动(「当前 Fays 缺少运行标定文件…请先在夹爪上位机中运行 device_setup」);现在 `SingleFaysLease.acquire()` 发现 per-serial YAML 缺失时,就在设备锁内调厂商导出程序从**这只夹爪自身**读出厂标定、生成 SDK YAML 与 ORB YAML(鱼眼 `stereoRectify` 求 P1/R1 + IMU 外参 `T_b_c1` + 探针读五项噪声/频率),复核落盘路径后直接连上,已有标定走 `isfile` 快路径(启动零额外开销、不碰设备)——这两个文件是**逐设备**出厂标定不可跨设备复用(实测 098/099 三组全不同),只能现场读;设备面板夹爪条目右键加「重新读取出厂标定」(录制中禁用,标定前自动关该夹爪、完成后开回),`tools/import_gripper_calibration.py` 加 `--generate`(旧文件先备份 `.bak_<时间戳>`)供命令行强制重生成;顺带修掉生成产物第 3 行的出处注释——厂商模板写死指向 `3500000261870088`,照抄会让每只新夹爪的 IMU 外参都被记成「取自另一台设备」,现改写成本机序列号(历史产物不动)。承接 v1.3.3 的力矩阵/触觉力与 RGB 视频的时间戳对齐:**力侧此前完全没有时间戳**——同一行里的 RGB 帧和力样本不是同时刻采集的,RGB 走外部帧源队列取队头最旧帧、力/矩阵走 latest-wins 单槽取当下最新,于是「行号相同」≠「时刻相同」,力恒领先 RGB 且领先量随录制时长增长(RGB 源 30.84fps vs 写线程 30fps,实测约 1 帧/秒,下游观测到的滞后 3 帧就是这么来的),力矩阵走独立泵线程还与 3 向量力各记各的时刻。修法是让每行自描述:`tactile_ready` 加第 5 个载荷 `capture_ns`(在 SDK 回调入口取),`pipeline.write_tactile_force/_force_matrix` 落成独立的 `observation.gripper_{side}_force_ns` / `_force_matrix_ns` 稀疏 int64 列(与 `hardware_ns` 同宿主单调钟时基,features 里声明 `encoding: host_monotonic_ns`),下游按时间取最近邻即可还原真实配对,精度上界=半个力样本间隔;顺带修 `rgb_frame_ready`/`stereo_frame_ready` 的 `pyqtSignal(int)` 把 `time.monotonic_ns()` 按 C++ qint32 静默截断成负数(每 2.147s 翻符号),改 `object` 封送——这也解释了此前用 `hardware_ns` 解回卷时凭空造出上百帧假漂移、把"中位 5.8 帧 / 最大 ±80 帧"报成对齐误差(真值是 ~3 帧,正确规则是**无符号**:单调钟 + FIFO 消费 ⇒ 真步长恒非负);新增 `scripts/align_modalities.py` 处理端最近邻对齐工具,对 v1.3.3 之前录的 episode 明确报「无法对齐(只能重录)」而不用增长率去猜。承接 v1.3.2 的 SLAM 陈旧帧入库前拦截与轨迹显示深拷贝修复。:桥接按「连续回退次数」判定——单帧脏读丢弃、连丢 30 帧才判定时钟真跳变并换基准放行(此前每一帧回退都会让核心库清空 IMU 队并重建地图、位姿停发 0.2–1.34 s,静止时更久),新增 `[TIME_DROP]`/`[TIME_REBASE]` 现场行并**无条件**打进 GUI 日志(此前落进未匹配行被 50 条上限吃掉——实测原生日志 4 次回退、GUI 日志 0 条,真出事时反而看不见),且带 `seq`/`prev_seq` 对比结论以区分「SDK 乱序投递 / 重复投递同一帧 / 帧配了旧时间戳」三种成因(时间戳上三者长得一模一样,对策完全不同);逐秒遥测 `[FPS_DATA]` 改「只在异常时出声」(健康静默、进入异常与恢复各一行、持续异常每 30 s 重复,全量逐秒流仍在原生日志里),并修 `wait_sdk_ready` 就绪门被**粘滞** `state.error` 锁死——任何信息性输出(如 ld.so 的 `cannot be preloaded ... ignored.`)都会让标定明明已完成的这一次返回 False、报出误导的「未等到 [FAYS-CALIB] 标记」(2026-09-10 18:32 假超时事故,文案白名单 + 去掉粘滞判断双管修);原生日志在 runtime 目录 `rmtree` 之前留档到 `logs/slam_native/`(保留最近 20 份),修「一次干净退出的会话其原生日志不可恢复」——排查 SLAM 崩溃时唯一的证据曾随目录一起被删;轨迹显示去掉 Qt 主线程上的整条深拷贝(正是这次拷贝让 `_rendered_trajectory is traj` 判等恒为假、每个位姿都在主线程复制随会话线性增长的点列,即「画一段时间后帧率变低」的直接原因),显示上限 400→6000,并以向量化投影 + 一次 `drawPolyline` 绘制,修「十几秒后轨迹显出折角」(降采样按 stride 翻倍使顶点数恒落 [上限/2, 上限],上限 400 时每段跨 67 ms 且随 stride 每翻一倍继续变粗,观感像 SLAM 出点变慢而数据一直满 30 Hz,是绘制侧丢的);RGB 外部队列在录制开始/结束/中止三个边界排空,修「上一段残留 + 本段启动窗口累积被写成新视频开头」(实测每段开头约 28 帧与上一段 episode 末帧逐帧匹配,队列满时还占 ~110 MB/槽);任务进度只上报后端当前任务列表里存在的任务(本地自建任务平台没这个项目,上报必被拒 400),且后端该项目口径为 `"sessions"`(从没收到过任何上报)时首次送本机**全量**当基线——只送增量会把后端计数从 session 数砸成增量本身(现场:3/3/3 的项目再录一条变 1)。承接 v1.3.1 的力矩阵 5 档落盘与 SLAM 停落 slam_pose 列。
|
collector/core/gripper/camera_service_src/CMakeLists.txt
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.16)
|
| 2 |
+
project(ksq_camera_service C)
|
| 3 |
+
|
| 4 |
+
set(CMAKE_C_STANDARD 11)
|
| 5 |
+
set(CMAKE_C_STANDARD_REQUIRED ON)
|
| 6 |
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
| 7 |
+
|
| 8 |
+
set(BUILD_EXAMPLE OFF CACHE BOOL "" FORCE)
|
| 9 |
+
set(BUILD_TEST OFF CACHE BOOL "" FORCE)
|
| 10 |
+
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
|
| 11 |
+
|
| 12 |
+
add_subdirectory(third_party/libuvc)
|
| 13 |
+
|
| 14 |
+
find_package(Threads REQUIRED)
|
| 15 |
+
find_package(PkgConfig REQUIRED)
|
| 16 |
+
pkg_check_modules(LIBUSB REQUIRED IMPORTED_TARGET libusb-1.0)
|
| 17 |
+
|
| 18 |
+
add_executable(ksq-camera-service
|
| 19 |
+
src/camera_service.c
|
| 20 |
+
)
|
| 21 |
+
target_include_directories(ksq-camera-service PRIVATE
|
| 22 |
+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/libuvc/include
|
| 23 |
+
)
|
| 24 |
+
target_compile_definitions(ksq-camera-service PRIVATE
|
| 25 |
+
_GNU_SOURCE
|
| 26 |
+
)
|
| 27 |
+
target_compile_options(ksq-camera-service PRIVATE
|
| 28 |
+
-Wall -Wextra -Wpedantic
|
| 29 |
+
)
|
| 30 |
+
target_link_libraries(ksq-camera-service PRIVATE
|
| 31 |
+
LibUVC::UVC
|
| 32 |
+
PkgConfig::LIBUSB
|
| 33 |
+
Threads::Threads
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
add_executable(sonix-flash-probe
|
| 37 |
+
tools/sonix_flash_probe.c
|
| 38 |
+
)
|
| 39 |
+
target_compile_options(sonix-flash-probe PRIVATE
|
| 40 |
+
-Wall -Wextra -Wpedantic
|
| 41 |
+
)
|
| 42 |
+
target_link_libraries(sonix-flash-probe PRIVATE
|
| 43 |
+
PkgConfig::LIBUSB
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
add_executable(discover-uvc-config
|
| 47 |
+
tools/discover_uvc_config.c
|
| 48 |
+
)
|
| 49 |
+
target_include_directories(discover-uvc-config PRIVATE
|
| 50 |
+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/libuvc/include
|
| 51 |
+
)
|
| 52 |
+
target_compile_definitions(discover-uvc-config PRIVATE
|
| 53 |
+
_GNU_SOURCE
|
| 54 |
+
)
|
| 55 |
+
target_compile_options(discover-uvc-config PRIVATE
|
| 56 |
+
-Wall -Wextra -Wpedantic
|
| 57 |
+
)
|
| 58 |
+
target_link_libraries(discover-uvc-config PRIVATE
|
| 59 |
+
LibUVC::UVC
|
| 60 |
+
PkgConfig::LIBUSB
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
install(TARGETS ksq-camera-service RUNTIME DESTINATION bin)
|
| 64 |
+
|
| 65 |
+
# Hardware-free integrity regression tests for the production callback.
|
| 66 |
+
enable_testing()
|
| 67 |
+
add_executable(test-frame-integrity tests/test_frame_integrity.c)
|
| 68 |
+
target_compile_definitions(test-frame-integrity PRIVATE _GNU_SOURCE)
|
| 69 |
+
target_compile_options(test-frame-integrity PRIVATE -Wall -Wextra -Wpedantic)
|
| 70 |
+
target_link_libraries(test-frame-integrity PRIVATE
|
| 71 |
+
LibUVC::UVC PkgConfig::LIBUSB Threads::Threads)
|
| 72 |
+
add_test(NAME frame-integrity COMMAND test-frame-integrity)
|
| 73 |
+
|
| 74 |
+
# The altsetting ladder decides which isochronous setting each camera is opened
|
| 75 |
+
# with, and its state-file key has to be derived identically in Python. Neither
|
| 76 |
+
# is observable without hardware, so pin both here.
|
| 77 |
+
add_executable(test-altsetting-ladder tests/test_altsetting_ladder.c)
|
| 78 |
+
target_compile_definitions(test-altsetting-ladder PRIVATE _GNU_SOURCE)
|
| 79 |
+
target_compile_options(test-altsetting-ladder PRIVATE -Wall -Wextra -Wpedantic)
|
| 80 |
+
target_link_libraries(test-altsetting-ladder PRIVATE
|
| 81 |
+
LibUVC::UVC PkgConfig::LIBUSB Threads::Threads)
|
| 82 |
+
add_test(NAME altsetting-ladder COMMAND test-altsetting-ladder)
|
| 83 |
+
|
| 84 |
+
# Checkout deployment keeps the bundled libuvc next to these executables.
|
| 85 |
+
# Avoid CMake embedding the build machine's absolute checkout directory.
|
| 86 |
+
#
|
| 87 |
+
# CMake appends an empty element to this list for ksq-camera-service (it counts
|
| 88 |
+
# Threads::Threads as an empty link directory), which would leave the RUNPATH as
|
| 89 |
+
# "$ORIGIN/third_party/libuvc:" -- and an empty element means "also search the
|
| 90 |
+
# current working directory". build.sh strips that element after the link and
|
| 91 |
+
# fails the build if it survives; do not "fix" it here by hand, the per-target
|
| 92 |
+
# SKIP_BUILD_RPATH and global CMAKE_SKIP_RPATH routes both make it worse
|
| 93 |
+
# (CMake then emits the entire empty list).
|
| 94 |
+
foreach(_target ksq-camera-service discover-uvc-config test-frame-integrity
|
| 95 |
+
test-altsetting-ladder)
|
| 96 |
+
set_target_properties(${_target} PROPERTIES
|
| 97 |
+
BUILD_RPATH "$ORIGIN/third_party/libuvc"
|
| 98 |
+
INSTALL_RPATH "$ORIGIN/../lib"
|
| 99 |
+
BUILD_RPATH_USE_ORIGIN TRUE)
|
| 100 |
+
endforeach()
|
collector/core/gripper/camera_service_src/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 相机服务与 UVC 扫描程序(源码入库版)
|
| 2 |
+
|
| 3 |
+
`ksq-camera-service`(libuvc 推流服务,走 IPC 给主程序供帧)与
|
| 4 |
+
`discover-uvc-config`(UVC 相机扫描:拓扑枚举 + Flash 身份读取 + 生成配置)的
|
| 5 |
+
**源码**。用 `./build.sh` 构建,产物安装到
|
| 6 |
+
`core/gripper/native/camera_service/build/`。
|
| 7 |
+
|
| 8 |
+
## 为什么源码在这里
|
| 9 |
+
|
| 10 |
+
这两个程序的源码原先**只存在于 `online/`**(`.gitignore:56` 整个目录不上传),
|
| 11 |
+
`core/gripper/native/` 下只有编译好的二进制,重建配方只活在当时那次排查的会话里
|
| 12 |
+
—— 二进制一旦丢失或要改一行,就得从头摸清 libusb 头文件、pkg-config、RUNPATH
|
| 13 |
+
这一整串。2026-09-15 把源码连同构建配方搬进 core,让采集程序自己的相机逻辑随包入库。
|
| 14 |
+
|
| 15 |
+
## 目录
|
| 16 |
+
|
| 17 |
+
```
|
| 18 |
+
CMakeLists.txt 与交付包 online/camera_service/CMakeLists.txt **逐字节一致**
|
| 19 |
+
src/camera_service.c 推流服务
|
| 20 |
+
tools/discover_uvc_config.c UVC 扫描程序(三档扫描深度)
|
| 21 |
+
tools/sonix_flash_probe.c Sonix Flash 读写工具(CMakeLists 里的目标之一)
|
| 22 |
+
tests/test_frame_integrity.c 无硬件回归测试(MJPEG 回调完整性)
|
| 23 |
+
third_party/libuvc/ vendored libuvc 0.0.7,含本地补丁 ksq_altsetting_quirks
|
| 24 |
+
(src/stream.c:DECXIN 3060→1280、Sightac 3072→800)
|
| 25 |
+
build.sh 构建脚本(唯一入口)
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
## 构建
|
| 29 |
+
|
| 30 |
+
```sh
|
| 31 |
+
core/gripper/camera_service_src/build.sh # 构建 + 安装(旧的先备份)
|
| 32 |
+
core/gripper/camera_service_src/build.sh --test # 顺带跑 test-frame-integrity
|
| 33 |
+
core/gripper/camera_service_src/build.sh --no-install
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
依赖:`gcc`、`cmake >= 3.16`、`make`、libusb-1.0 头文件(`libusb-1.0-0-dev`,或退到
|
| 37 |
+
conda 的 `include/libusb-1.0`)。libjpeg 头文件可选,但**缺了 libuvc 会退化成不支持
|
| 38 |
+
MJPEG 解码**,构建脚本会告警 —— 线上产物是带 libjpeg 的,两者不一致。
|
| 39 |
+
|
| 40 |
+
安装会按 `.pre_rebuild_<时间戳>` 备份旧二进制,回退就是覆盖回去。
|
| 41 |
+
|
| 42 |
+
## 三个已踩过的坑(改脚本前先看)
|
| 43 |
+
|
| 44 |
+
1. **`*** 目标模式不含有"%"`**:真正的病因是 vendored `FindLibUSB.cmake` 走
|
| 45 |
+
`find_library()` 找不到库(系统只有 `libusb-1.0.so.0`,没有给链接器用的
|
| 46 |
+
`libusb-1.0.so`),把 `LibUSB::LibUSB-NOTFOUND` 写进了依赖表;GNU make 把那条
|
| 47 |
+
依赖里的第二个冒号当成静态模式规则的分隔符,于是报出这个与病因毫不相干的错。
|
| 48 |
+
解法是 `-DLibUSB_LIBRARY=...` 预置缓存,让 `find_library` 整个跳过。
|
| 49 |
+
2. **pkg-config 里只能有一个 `-I`**:`FindLibUSB.cmake` 把 `${LibUSB_INCLUDE_DIRS}`
|
| 50 |
+
不 quote 地交给 `set_target_properties`,两个 `-I` 就变成三个实参报错。而
|
| 51 |
+
libuvc 内部用 `<libusb.h>`、两个工具用 `<libusb-1.0/libusb.h>`,两种写法都要能
|
| 52 |
+
解析 —— 所以 shim 目录里同时放 `libusb-1.0/` 与 `libusb.h` 两个软链,只给一个 `-I`。
|
| 53 |
+
3. **`ksq-camera-service` 的 RUNPATH 会多一个空条目**(`$ORIGIN/third_party/libuvc:`):
|
| 54 |
+
CMake 把它链的 `Threads::Threads` 在本机算成了空目录。RUNPATH 里的空条目等于让
|
| 55 |
+
加载器去**当前工作目录**找 `libuvc.so.0`,线上那份手工链的没有这个口子,所以
|
| 56 |
+
构建脚本会在链接后把这个冒号就地改成字符串终止符(等价 `chrpath -d` 的收缩改写;
|
| 57 |
+
本机没有 chrpath/patchelf)。校验段会断言空条目已消失。
|
| 58 |
+
|
| 59 |
+
## 等时档位(altsetting):ini 说了算,服务会自己降档
|
| 60 |
+
|
| 61 |
+
**`forced_altsetting` / `forced_payload` 是真正生效的**,不是校验用的装饰。服务
|
| 62 |
+
打开设备后立刻把这两个值交给 libuvc 的新 API `uvc_set_altsetting_override()`
|
| 63 |
+
(声明在 `include/libuvc/libuvc.h`,实现在 `src/device.c`,`uvc_device_handle_t`
|
| 64 |
+
是 `calloc` 出来的,新字段自动零初始化,没设过就退回编译期那张
|
| 65 |
+
`ksq_altsetting_quirks` 表)。**改档位只需要改 ini。**
|
| 66 |
+
|
| 67 |
+
在此之前这两个 ini 字段只用于校验和打印,真正决定档位的是编译进 libuvc 的表
|
| 68 |
+
——所以「改 ini」完全不改变行为,这一点极容易误判。**判断一条腿实际跑在哪个档
|
| 69 |
+
位,只认日志里的 `[LIBUVC-QUIRK] ... alt=N payload=M ... xfers=` 那一行**(带
|
| 70 |
+
`(altsetting override)` 后缀表示走的覆盖值);服务自己那行
|
| 71 |
+
`vidpid=... alt=7 payload=1280` 只是 ini 的回声,在旧二进制上与真实档位无关。
|
| 72 |
+
|
| 73 |
+
档位表在 `src/camera_service.c`(`kDecxinModes` / `kSightacModes`),**必须按
|
| 74 |
+
从好到差降序**,降档梯子按表往下走一格、走到最后一项就不再降。两个门槛分开计:
|
| 75 |
+
连着停摆 `STALLS_BEFORE_ALTSETTING_DOWNGRADE`(2) 次降档;连着开不起来
|
| 76 |
+
`OPEN_FAILURES_BEFORE_ALTSETTING_DOWNGRADE`(3) 次也降档(覆盖「STREAMON 阶段
|
| 77 |
+
就排不下带宽、流根本起不来」那种,停摆那条路永远等不到)。结论按
|
| 78 |
+
「相机序列号 × 控制器 PCI 路径���落盘到 `state_dir`,下次开机直接读。
|
| 79 |
+
|
| 80 |
+
**降档必须把两个计数都清零**(2026-09-15 修)。门槛是拿 `==` 比的,计数停在
|
| 81 |
+
门槛上就再也不会命中——原来 `maybe_downgrade_altsetting` 只清了 `stalls_at_mode`,
|
| 82 |
+
于是「开不起来」那条路**降完一档就永久卡死**,表里剩下的档位形同不存在
|
| 83 |
+
(DECXIN 只有两档,正好被掩盖住;三档机型上就会露出来)。两条路的日志都在
|
| 84 |
+
调用**之前**抓计数,否则清零后只能打出「0 failed opens」,把最该看的那次降档
|
| 85 |
+
说成没有依据。
|
| 86 |
+
|
| 87 |
+
**这套东西能测,而且以前从来没被测过**(2026-09-15 修)。`tests/test_altsetting_ladder.c`
|
| 88 |
+
一直在 `CMakeLists.txt` 里注册着 `add_test`,但 `build.sh --test` 是点名跑
|
| 89 |
+
`test-frame-integrity` 的,梯子那个**一次也没执行过**——上面那个「降一档就卡死」
|
| 90 |
+
的 bug 正好落在它本该覆盖的范围内。现在 `--test` 走 `ctest`,新增测试只要
|
| 91 |
+
`add_test` 就自动纳进来。反向验证过:把清零那行注释掉,`altsetting-ladder`
|
| 92 |
+
立刻断言中止,不是个空跑的测试。
|
| 93 |
+
|
| 94 |
+
**起始档位由 app 决定,不在这张表里**:`uvc_camera_service.py` 的
|
| 95 |
+
`_starting_mode` 取**表里最保守的一档**(=最后一项)。实测 DECXIN 的 alt7
|
| 96 |
+
(10.24 MB/s)在整机三路一起出流时每 45~130 秒必停摆一次,alt6(7.552 MB/s)
|
| 97 |
+
同样三路下长跑不停,而**两档出帧率一模一样(都 30.00fps)**——alt7 多出来的每帧
|
| 98 |
+
余量(327680 对 241664 B)换不到任何看得见的好处,所以默认不用它。想要那点余量
|
| 99 |
+
就把 ini 改回 alt7,服务停两次会自己退回 alt6 并把结论落盘。
|
| 100 |
+
|
| 101 |
+
`tools/discover_uvc_config.c` 报的是**相机描述符里的首选档**(alt7),是如实汇报
|
| 102 |
+
硬件,不是起始档;app 拿它跟服务端档位表对一遍,对不上就报「扫描程序与服务端
|
| 103 |
+
档位表已经脱节」——那说明两个程序分开编了,必须一起重编。
|
| 104 |
+
|
| 105 |
+
## SIGTERM 收尾:join 必须有界(2026-09-15 修)
|
| 106 |
+
|
| 107 |
+
**症状**:服务收到 SIGTERM 后退不出来。实测线上二进制卡了 3 分钟以上,只攥着
|
| 108 |
+
DECXIN 的 `/dev/bus/usb/007/032` 一个设备 fd(left/right 都已干净释放),日志
|
| 109 |
+
停在断流那一刻——连 `[SUPERVISION] parent exited` 都没打出来。
|
| 110 |
+
|
| 111 |
+
**病根**:相机线程陷在 `uvc_open` 的同步 libusb 调用里(卡死的设备上它不返回),
|
| 112 |
+
永远走不到循环头那个 `g_stop` 判断;而 `main` 堵在 `pthread_join` 上等它。
|
| 113 |
+
`g_stop` 置了也没用——没人去看。
|
| 114 |
+
|
| 115 |
+
**后果**:客户端只等 2 秒(`uvc_camera_service.py` 的 `terminate()` → 等 2 秒 →
|
| 116 |
+
`kill()`),超了就是 SIGKILL,进程死在 libusb 调用中间、设备停在激活的 alt
|
| 117 |
+
setting 上,下一轮照样起不来——正是 2026-09-15 09:24 那次事故的形状。
|
| 118 |
+
`teardown_failed_open` 里「复位抢在 `uvc_stop_streaming` 之前」只覆盖**失败路径**,
|
| 119 |
+
覆盖不到「线程已经卡在 `uvc_open` 里」这一种。
|
| 120 |
+
|
| 121 |
+
**改法**:`pthread_join` → `pthread_timedjoin_np`。整体预算 1.5 秒、单路 0.8 秒,
|
| 122 |
+
超时就 `pthread_detach` 掉继续走,`main` 一返回进程即结束(内核替我们释放句柄),
|
| 123 |
+
并打一行 `收尾超时:线程未在预算内退出(多半卡在设备调用里),不再等它`。
|
| 124 |
+
跳过超时那一路的统计与 `pthread_mutex_destroy`——线程可能还在用,销毁是 UAF。
|
| 125 |
+
|
| 126 |
+
**一个坑(第一版就踩了)**:这个 join 循环**同时就是服务的主运行循环**——它在
|
| 127 |
+
**启动时**进入、一直跑到 `g_stop`,不是「收尾时才走的一段」。所以截止时间不能在
|
| 128 |
+
进循环前算好:第一版把 `shutdown_deadline` 放在 `for` 之前,结果服务启动 1.5 秒后
|
| 129 |
+
三路一起「收尾超时」、自己退出了(现象:三路 `总帧=0`、日志里三行收尾超时、
|
| 130 |
+
`SIGTERM → pid` 那行根本没机会打)。现在改成**等 `g_stop` 真置位时才开始计时**,
|
| 131 |
+
没置位时只用 200ms 短超时轮询(成本可忽略)。
|
| 132 |
+
|
| 133 |
+
**验证**:正常路径真机 45 秒三路 —— left/right 各 1351 帧满帧、decxin 1111 帧,
|
| 134 |
+
SIGTERM 退出 **0.71 秒**(改前 0.56 秒,仍在 2 秒宽限内),服务全程不自杀。
|
| 135 |
+
**超时那条分支本身没能在真机上触发**(没法按需把设备弄卡),只做了代码审查。
|
| 136 |
+
|
| 137 |
+
## 与交付包的关系
|
| 138 |
+
|
| 139 |
+
`CMakeLists.txt` 与四个 C 源文件都从交付包 `online/camera_service/` 原样搬来
|
| 140 |
+
(逐字节一致),本仓库只新增 `build.sh` 与这份 README。**改这里的 C 源码时要同步
|
| 141 |
+
回交付包**,否则两边会分叉。
|
| 142 |
+
|
| 143 |
+
构建产物与线上二进制的实测关系(2026-09-15):
|
| 144 |
+
|
| 145 |
+
| 产物 | 与线上二进制 |
|
| 146 |
+
|---|---|
|
| 147 |
+
| `discover-uvc-config` | **逐字节一致**(sha256 `b9d8bba870f60c11…`)——配方忠实 |
|
| 148 |
+
| `ksq-camera-service` | **源码已领先线上**(多了下面的 SIGTERM 收尾修复),且编译选项不同:CMake Release �� `-O3 -DNDEBUG`,线上那份是手工 `-O2`(源码无 `assert()`,`NDEBUG` 无语义影响) |
|
| 149 |
+
| `sonix-flash-probe` | 同上,代码同源、编译选项不同 |
|
| 150 |
+
| `libuvc.so.0.0.7` | 代码同源、编译选项不同;`ksq_altsetting_quirks` 补丁与 `[LIBUVC-QUIRK]` 日志**都在**,NEEDED 三者一致(libusb-1.0.so.0 / libjpeg.so.8 / libc.so.6) |
|
| 151 |
+
|
| 152 |
+
也就是说 `build.sh` 现在是**单一机制**(一套 CMake 配置编全部目标),代价是
|
| 153 |
+
`ksq-camera-service` 与当年手工链的那个二进制不是同一个字节。重新构建后应重新做
|
| 154 |
+
一次三路相机回归,而不是假定等价。
|
| 155 |
+
|
| 156 |
+
**2026-09-15 19:29 已按此装机**(`STAMP=20260915_192940`):`ksq-camera-service`
|
| 157 |
+
现在带自适应档位 + 两个计数都清零的修复。装机前三路回归已做(app 生成的 ini、
|
| 158 |
+
alt6 默认,**600 秒三路零停摆零重建,decxin/left 30.00fps、right 29.80fps**,
|
| 159 |
+
17513/17496/17487 帧),装机后又做了一次 300 秒回归。
|
| 160 |
+
|
| 161 |
+
**2026-09-15 11:34 曾按此装机**(`STAMP=20260915_113444`):
|
| 162 |
+
`core/gripper/native/camera_service/build/` 里的 `ksq-camera-service` 当时是
|
| 163 |
+
`07f1f5a34b569b7b`(带 SIGTERM 收尾修复),旧件 `b64b74d810abe482` 备份在同目录
|
| 164 |
+
`.pre_rebuild_20260915_113444`;`discover-uvc-config` 装前装后同为
|
| 165 |
+
`b9d8bba870f60c11`,装不装等价。装机前三路回归已做(90 秒 ×2 腿 + 45 秒修复腿)。
|
| 166 |
+
**另注意 `build.sh` 的安装段不备份 `third_party/libuvc/`**(`rm -f` 直接覆盖、
|
| 167 |
+
`include/` 还是 `rm -rf`),所以这次装机前手工备份了库文件为
|
| 168 |
+
`libuvc.so.0.0.7.pre_rebuild_20260915_113330` + `include.pre_rebuild_20260915_113330/`。
|
| 169 |
+
改脚本时补上这段备份更稳妥。
|
| 170 |
+
|
| 171 |
+
### glibc 下限:`ksq-camera-service` 2.34 → 2.38(不是新约束)
|
| 172 |
+
|
| 173 |
+
新编的 `ksq-camera-service` 引用 `__isoc23_strtol@GLIBC_2.38`(GCC 15 默认 C23,
|
| 174 |
+
`strtol` 解析成 C23 语义的符号),最低 glibc 从线上的 2.34 抬到 2.38。看着像门槛
|
| 175 |
+
变高,其实**没有新增约束**:那个**逐字节一致**的 `discover-uvc-config` 本来就要求
|
| 176 |
+
GLIBC_2.38,而它是扫描时每次都要在采集机上跑的 —— 能跑扫描程序就一定能跑这个
|
| 177 |
+
服务。其余产物(`sonix-flash-probe`、libuvc)两边都是 2.34。
|
| 178 |
+
|
| 179 |
+
不要去给 CMake 钉 `-DCMAKE_C_STANDARD=17` 来消掉它:那会连带把
|
| 180 |
+
`discover-uvc-config` 的 `strtol` 换回旧符号,而它正是目前唯一能逐字节复现线上
|
| 181 |
+
二进制的产物,是「配方忠实」的活证据。
|
collector/core/gripper/camera_service_src/build.sh
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# 构建 libuvc 相机服务(ksq-camera-service)与 UVC 扫描程序(discover-uvc-config),
|
| 3 |
+
# 产物安装到 core/gripper/native/camera_service/build/。
|
| 4 |
+
#
|
| 5 |
+
# 为什么有这份脚本:这两个程序的源码原先只在 online/(.gitignore 里),core/ 下
|
| 6 |
+
# 只有编译好的二进制,重建配方只活在当时那次会话里 —— 一旦二进制丢了或要改一行
|
| 7 |
+
# 就得从头摸。现在源码随包入库(core/gripper/camera_service_src/),配方固化于此。
|
| 8 |
+
#
|
| 9 |
+
# 依赖:gcc、cmake(>=3.16)、make、libusb-1.0 头文件;libjpeg 头文件可选(缺了
|
| 10 |
+
# libuvc 会退化:不支持 MJPEG 解码,实测产物 NEEDED 里就没有 libjpeg.so.8)。
|
| 11 |
+
#
|
| 12 |
+
# 用法:
|
| 13 |
+
# ./build.sh 构建 + 安装(旧的同名二进制先备份为 .pre_rebuild_<时间戳>)
|
| 14 |
+
# ./build.sh --no-install 只构建,不动 core/gripper/native/
|
| 15 |
+
# ./build.sh --test 构建后跑 ctest(全部无硬件回归测试)
|
| 16 |
+
# ./build.sh --jobs 4 限制并行度
|
| 17 |
+
set -euo pipefail
|
| 18 |
+
|
| 19 |
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 20 |
+
REPO="$(cd "$HERE/../../.." && pwd)"
|
| 21 |
+
OUT="$REPO/core/gripper/native/camera_service/build"
|
| 22 |
+
BUILD="$HERE/build"
|
| 23 |
+
SHIM="$BUILD/shim"
|
| 24 |
+
|
| 25 |
+
DO_INSTALL=1
|
| 26 |
+
DO_TEST=0
|
| 27 |
+
JOBS="$(nproc 2>/dev/null || echo 4)"
|
| 28 |
+
|
| 29 |
+
while [ $# -gt 0 ]; do
|
| 30 |
+
case "$1" in
|
| 31 |
+
--no-install) DO_INSTALL=0 ;;
|
| 32 |
+
--test) DO_TEST=1 ;;
|
| 33 |
+
--jobs) JOBS="${2:?--jobs 需要一个数字}"; shift ;;
|
| 34 |
+
-h|--help) sed -n '2,18p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
| 35 |
+
*) echo "未知参数: $1(-h 看用法)" >&2; exit 2 ;;
|
| 36 |
+
esac
|
| 37 |
+
shift
|
| 38 |
+
done
|
| 39 |
+
|
| 40 |
+
die() { echo "错误: $*" >&2; exit 1; }
|
| 41 |
+
|
| 42 |
+
# ---------------------------------------------------------------- 依赖定位
|
| 43 |
+
|
| 44 |
+
# libusb 头文件:系统装了 libusb-1.0-0-dev 最省事;否则退到 conda 的 include。
|
| 45 |
+
# 注意头文件父目录要进 -I,因为源码里既有 <libusb-1.0/libusb.h>(sonix_flash_probe)
|
| 46 |
+
# 也有 <libusb.h>(libuvc 自己),两种写法都得能解析。
|
| 47 |
+
find_libusb_include() {
|
| 48 |
+
local candidate
|
| 49 |
+
for candidate in \
|
| 50 |
+
"${LIBUSB_INCLUDE_PARENT:-}" \
|
| 51 |
+
/usr/include \
|
| 52 |
+
/usr/local/include \
|
| 53 |
+
"${CONDA_PREFIX:-}/include" \
|
| 54 |
+
"$HOME/miniconda3/include" \
|
| 55 |
+
"$HOME/anaconda3/include"
|
| 56 |
+
do
|
| 57 |
+
[ -n "$candidate" ] && [ -f "$candidate/libusb-1.0/libusb.h" ] && {
|
| 58 |
+
echo "$candidate"; return 0; }
|
| 59 |
+
done
|
| 60 |
+
return 1
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
# libusb 运行库:优先系统 multiarch 的 .so.0(只链 soname,产物不带任何本机
|
| 64 |
+
# RUNPATH 依赖),再退到 conda。
|
| 65 |
+
find_libusb_library() {
|
| 66 |
+
local candidate
|
| 67 |
+
for candidate in \
|
| 68 |
+
"${LIBUSB_LIBRARY:-}" \
|
| 69 |
+
/usr/lib/x86_64-linux-gnu/libusb-1.0.so.0 \
|
| 70 |
+
/usr/lib/libusb-1.0.so.0 \
|
| 71 |
+
/usr/local/lib/libusb-1.0.so.0 \
|
| 72 |
+
"${CONDA_PREFIX:-}/lib/libusb-1.0.so" \
|
| 73 |
+
"$HOME/miniconda3/lib/libusb-1.0.so" \
|
| 74 |
+
"$HOME/anaconda3/lib/libusb-1.0.so"
|
| 75 |
+
do
|
| 76 |
+
[ -n "$candidate" ] && [ -e "$candidate" ] && { echo "$candidate"; return 0; }
|
| 77 |
+
done
|
| 78 |
+
return 1
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
LIBUSB_INC_PARENT="$(find_libusb_include)" \
|
| 82 |
+
|| die "找不到 libusb-1.0 头文件。装 libusb-1.0-0-dev,或用 LIBUSB_INCLUDE_PARENT=<含 libusb-1.0/ 的目录> 指定。"
|
| 83 |
+
LIBUSB_LIB="$(find_libusb_library)" \
|
| 84 |
+
|| die "找不到 libusb-1.0 运行库。装 libusb-1.0-0,或用 LIBUSB_LIBRARY=<路径> 指定。"
|
| 85 |
+
|
| 86 |
+
# libjpeg:FindJpegPkg 会先走 CMake 自带的 FindJPEG,这里预置缓存值把它钉死在
|
| 87 |
+
# 系统头/库上,免得 cmake 是 conda 装的时候误抓 conda 的 libjpeg。
|
| 88 |
+
JPEG_ARGS=()
|
| 89 |
+
if [ -f /usr/include/jpeglib.h ]; then
|
| 90 |
+
JPEG_ARGS+=("-DJPEG_INCLUDE_DIR=/usr/include")
|
| 91 |
+
for lib in /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib/libjpeg.so; do
|
| 92 |
+
[ -e "$lib" ] && { JPEG_ARGS+=("-DJPEG_LIBRARY=$lib"); break; }
|
| 93 |
+
done
|
| 94 |
+
else
|
| 95 |
+
echo "警告: 没有 /usr/include/jpeglib.h,libuvc 将编译成不支持 MJPEG 解码" >&2
|
| 96 |
+
fi
|
| 97 |
+
|
| 98 |
+
# ---------------------------------------------------------------- 头文件 shim
|
| 99 |
+
#
|
| 100 |
+
# 顶层的 CMakeLists 用 pkg_check_modules(LIBUSB REQUIRED ... libusb-1.0),而系统
|
| 101 |
+
# 上根本没有 libusb-1.0.pc(那个文件由 -dev 包提供)。这里现造一个,指向真实头
|
| 102 |
+
# 文件和运行库的 soname(-l:libusb-1.0.so.0 不带 -L,所以不会给产物塞 RUNPATH)。
|
| 103 |
+
#
|
| 104 |
+
# 只有**一个** -I,而且是 shim 目录而不是真实头目录:vendored 的
|
| 105 |
+
# FindLibUSB.cmake 把 ${LibUSB_INCLUDE_DIRS} 不 quote 地交给
|
| 106 |
+
# set_target_properties,一旦 pkg-config 给出两个 -I,它就会变成三个实参报
|
| 107 |
+
# 「set_target_properties called with incorrect number of arguments」。
|
| 108 |
+
# 而两种 include 写法都得满足 —— libuvc 内部是 <libusb.h>,两个工具是
|
| 109 |
+
# <libusb-1.0/libusb.h> —— 所以 shim 目录里同时放 libusb-1.0/ 和 libusb.h 两个软链。
|
| 110 |
+
|
| 111 |
+
mkdir -p "$SHIM/include" "$SHIM/pkgconfig"
|
| 112 |
+
ln -sfn "$LIBUSB_INC_PARENT/libusb-1.0" "$SHIM/include/libusb-1.0"
|
| 113 |
+
ln -sfn "$LIBUSB_INC_PARENT/libusb-1.0/libusb.h" "$SHIM/include/libusb.h"
|
| 114 |
+
|
| 115 |
+
cat > "$SHIM/pkgconfig/libusb-1.0.pc" <<EOF
|
| 116 |
+
prefix=$SHIM
|
| 117 |
+
exec_prefix=\${prefix}
|
| 118 |
+
libdir=$(dirname "$LIBUSB_LIB")
|
| 119 |
+
includedir=$SHIM/include
|
| 120 |
+
|
| 121 |
+
Name: libusb-1.0
|
| 122 |
+
Description: C API for USB device access from Linux, Mac OS X, Windows, OpenBSD/NetBSD and Solaris userspace
|
| 123 |
+
Version: 1.0.27
|
| 124 |
+
Libs: -l:$(basename "$LIBUSB_LIB")
|
| 125 |
+
Cflags: -I\${includedir}
|
| 126 |
+
EOF
|
| 127 |
+
|
| 128 |
+
# ---------------------------------------------------------------- 配置 + 构建
|
| 129 |
+
#
|
| 130 |
+
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5:libuvc 的 cmake_minimum_required 是 3.1,
|
| 131 |
+
# CMake 4.x 直接拒绝,给个下限放行。
|
| 132 |
+
# -DLibUSB_LIBRARY=...:vendored 的 FindLibUSB.cmake 走 find_library(NAMES
|
| 133 |
+
# ${LibUSB_LIBRARIES}),而系统的 libusb 只有 .so.0、没有给链接器用的 .so 软链,
|
| 134 |
+
# find_library 会失败并把 "LibUSB::LibUSB-NOTFOUND" 写进依赖表 —— GNU make 会
|
| 135 |
+
# 把那条依赖里的第二个冒号当成静态模式规则分隔符,报出与病因毫不相干的
|
| 136 |
+
# 「*** 目标模式不含有"%"」。预置这个缓存变量让 find_library 整个跳过。
|
| 137 |
+
|
| 138 |
+
echo "libusb 头文件 : $LIBUSB_INC_PARENT"
|
| 139 |
+
echo "libusb 运行库 : $LIBUSB_LIB"
|
| 140 |
+
echo "构建目录 : $BUILD"
|
| 141 |
+
|
| 142 |
+
PKG_CONFIG_PATH="$SHIM/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" \
|
| 143 |
+
cmake -S "$HERE" -B "$BUILD" \
|
| 144 |
+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
| 145 |
+
-DBUILD_EXAMPLE=OFF \
|
| 146 |
+
-DBUILD_TEST=OFF \
|
| 147 |
+
-DLibUSB_LIBRARY="$LIBUSB_LIB" \
|
| 148 |
+
"${JPEG_ARGS[@]}"
|
| 149 |
+
|
| 150 |
+
cmake --build "$BUILD" -j"$JOBS"
|
| 151 |
+
|
| 152 |
+
# ksq-camera-service 的 RUNPATH 末尾会多出一个空条目("$ORIGIN/third_party/libuvc:")——
|
| 153 |
+
# CMake 把它链的 Threads::Threads 在本机算成了空目录,空条目进了 rpath 列表。RUNPATH
|
| 154 |
+
# 里的空条目等于让加载器去**当前工作目录**找 libuvc.so.0,线上那份是手工 gcc 链的、
|
| 155 |
+
# 没有这个口子。这里把那个冒号就地改成字符串终止符:长度不变、ELF 结构不动,等价于
|
| 156 |
+
# `chrpath -d` 的收缩改写(chrpath/patchelf 本机都没有)。只在真的出现时才改写。
|
| 157 |
+
|
| 158 |
+
strip_empty_rpath_entry() {
|
| 159 |
+
local binary="$1"
|
| 160 |
+
[ -f "$binary" ] || return 0
|
| 161 |
+
command -v python3 >/dev/null 2>&1 || return 0
|
| 162 |
+
python3 - "$binary" <<'PY'
|
| 163 |
+
import sys
|
| 164 |
+
path = sys.argv[1]
|
| 165 |
+
needle = b"$ORIGIN/third_party/libuvc:\x00"
|
| 166 |
+
fixed = b"$ORIGIN/third_party/libuvc\x00\x00"
|
| 167 |
+
with open(path, "rb") as handle:
|
| 168 |
+
data = handle.read()
|
| 169 |
+
if needle not in data:
|
| 170 |
+
sys.exit(0)
|
| 171 |
+
with open(path, "wb") as handle:
|
| 172 |
+
handle.write(data.replace(needle, fixed))
|
| 173 |
+
print(f" 去掉 RUNPATH 空条目: {path}")
|
| 174 |
+
PY
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
for binary in ksq-camera-service discover-uvc-config sonix-flash-probe; do
|
| 178 |
+
strip_empty_rpath_entry "$BUILD/$binary"
|
| 179 |
+
done
|
| 180 |
+
|
| 181 |
+
# ---------------------------------------------------------------- 校验
|
| 182 |
+
#
|
| 183 |
+
# 产物必须链上 libuvc.so.0 且 RUNPATH 为 $ORIGIN/third_party/libuvc —— 部署布局
|
| 184 |
+
# 靠的就是这个相对路径(ksq-camera-service 里 libusb_reset_device 是 dlsym 取的,
|
| 185 |
+
# 所以它自己 NEEDED 里没有 libusb,libuvc 带进来就够)。
|
| 186 |
+
|
| 187 |
+
for binary in ksq-camera-service discover-uvc-config; do
|
| 188 |
+
[ -x "$BUILD/$binary" ] || die "没有产出 $BUILD/$binary"
|
| 189 |
+
if command -v readelf >/dev/null 2>&1; then
|
| 190 |
+
readelf -d "$BUILD/$binary" | grep -q 'libuvc.so.0' \
|
| 191 |
+
|| die "$binary 没有链接 libuvc.so.0"
|
| 192 |
+
readelf -d "$BUILD/$binary" | grep -q '\$ORIGIN/third_party/libuvc' \
|
| 193 |
+
|| die "$binary 的 RUNPATH 不是 \$ORIGIN/third_party/libuvc"
|
| 194 |
+
readelf -d "$BUILD/$binary" | grep -q 'libuvc:' \
|
| 195 |
+
&& die "$binary 的 RUNPATH 里还有空条目(strip_empty_rpath_entry 没生效)"
|
| 196 |
+
fi
|
| 197 |
+
done
|
| 198 |
+
readelf -d "$BUILD/third_party/libuvc/libuvc.so.0.0.7" 2>/dev/null | grep -q libjpeg \
|
| 199 |
+
&& echo "libuvc: 带 libjpeg(支持 MJPEG 解码)" \
|
| 200 |
+
|| echo "警告: libuvc 没链上 libjpeg —— 不带 MJPEG 解码,与线上产物不一致" >&2
|
| 201 |
+
|
| 202 |
+
if [ "$DO_TEST" = 1 ]; then
|
| 203 |
+
# 走 ctest 而不是点名跑某一个:以前这里写死跑 test-frame-integrity,
|
| 204 |
+
# 结果 test-altsetting-ladder 虽然在 CMakeLists 里注册了 add_test,却
|
| 205 |
+
# 从来没有被任何入口调起来 —— 降档梯子是真机上最难复现、最该靠测试
|
| 206 |
+
# 兜住的一块,却一直没人跑。新增测试只要 add_test 就自动纳进来。
|
| 207 |
+
echo "--- ctest ---"
|
| 208 |
+
( cd "$BUILD" && ctest --output-on-failure )
|
| 209 |
+
fi
|
| 210 |
+
|
| 211 |
+
if [ "$DO_INSTALL" = 0 ]; then
|
| 212 |
+
echo "已完成构建(--no-install,未改动 core/gripper/native/)"
|
| 213 |
+
exit 0
|
| 214 |
+
fi
|
| 215 |
+
|
| 216 |
+
# ---------------------------------------------------------------- 安装
|
| 217 |
+
#
|
| 218 |
+
# 旧的同名二进制按 .pre_rebuild_<时间戳> 备份,保留现场可比对(历史上这里存过
|
| 219 |
+
# pre_stall_watchdog / pre_usb_reset_escalation 两份,都是用来回退的)。
|
| 220 |
+
|
| 221 |
+
[ -d "$OUT" ] || die "安装目标不存在: $OUT"
|
| 222 |
+
|
| 223 |
+
STAMP="$(date +%Y%m%d_%H%M%S)"
|
| 224 |
+
for binary in ksq-camera-service discover-uvc-config sonix-flash-probe; do
|
| 225 |
+
[ -f "$BUILD/$binary" ] || continue
|
| 226 |
+
if [ -f "$OUT/$binary" ]; then
|
| 227 |
+
cp -p "$OUT/$binary" "$OUT/$binary.pre_rebuild_$STAMP"
|
| 228 |
+
fi
|
| 229 |
+
cp -p "$BUILD/$binary" "$OUT/$binary"
|
| 230 |
+
echo "安装 $binary → $OUT/$binary"
|
| 231 |
+
done
|
| 232 |
+
|
| 233 |
+
mkdir -p "$OUT/third_party/libuvc"
|
| 234 |
+
for name in libuvc.so libuvc.so.0 libuvc.so.0.0.7; do
|
| 235 |
+
[ -e "$BUILD/third_party/libuvc/$name" ] || continue
|
| 236 |
+
rm -f "$OUT/third_party/libuvc/$name"
|
| 237 |
+
cp -a "$BUILD/third_party/libuvc/$name" "$OUT/third_party/libuvc/$name"
|
| 238 |
+
done
|
| 239 |
+
rm -rf "$OUT/third_party/libuvc/include"
|
| 240 |
+
cp -a "$BUILD/third_party/libuvc/include" "$OUT/third_party/libuvc/include"
|
| 241 |
+
echo "安装 libuvc → $OUT/third_party/libuvc/"
|
| 242 |
+
|
| 243 |
+
echo
|
| 244 |
+
echo "完成。回退:把 $OUT/<名字>.pre_rebuild_$STAMP 覆盖回去即可。"
|
collector/core/gripper/camera_service_src/src/camera_service.c
ADDED
|
@@ -0,0 +1,1514 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* pthread_timedjoin_np 是 GNU 扩展,必须在任何头文件之前打开。 */
|
| 2 |
+
#define _GNU_SOURCE
|
| 3 |
+
|
| 4 |
+
#include <errno.h>
|
| 5 |
+
#include <fcntl.h>
|
| 6 |
+
#include <limits.h>
|
| 7 |
+
#include <poll.h>
|
| 8 |
+
#include <signal.h>
|
| 9 |
+
#include <stdint.h>
|
| 10 |
+
#include <stdio.h>
|
| 11 |
+
#include <stdlib.h>
|
| 12 |
+
#include <string.h>
|
| 13 |
+
#include <sys/socket.h>
|
| 14 |
+
#include <sys/stat.h>
|
| 15 |
+
#include <sys/time.h>
|
| 16 |
+
#include <sys/types.h>
|
| 17 |
+
#include <sys/un.h>
|
| 18 |
+
#include <sys/uio.h>
|
| 19 |
+
#include <time.h>
|
| 20 |
+
#include <unistd.h>
|
| 21 |
+
|
| 22 |
+
#include <libuvc/libuvc.h>
|
| 23 |
+
|
| 24 |
+
#include <dlfcn.h>
|
| 25 |
+
#include <pthread.h>
|
| 26 |
+
|
| 27 |
+
#define MAX_CAMERAS 8
|
| 28 |
+
#define FRAME_QUEUE_DEPTH 4
|
| 29 |
+
#define MAX_LINE 512
|
| 30 |
+
#define IPC_MAGIC 0x4b535146u /* "KSQF" */
|
| 31 |
+
#define IPC_VERSION 1u
|
| 32 |
+
/* 等时流静默停摆看门狗。DECXIN (1bcf:2d4f) 会在 6~82 秒之间无声停止推帧,
|
| 33 |
+
* 此后永不恢复,内核也不报任何错误(实测:同一总线同一 Hub 上的两路
|
| 34 |
+
* Sightac 全程 30fps 正常,只有 DECXIN 自己死)。停滞不是「暂时掉帧」:
|
| 35 |
+
* libuvc 丢完等时包后不会自愈,必须重开流。停滞超过这个秒数就中断本路,
|
| 36 |
+
* 交给 camera_main 的重试循环重开——否则整场会话只剩最后一帧,客户端
|
| 37 |
+
* 重连也只能重连进空气。 */
|
| 38 |
+
#define STREAM_STALL_TIMEOUT_SECONDS 3.0
|
| 39 |
+
|
| 40 |
+
/* 首次启动等首帧的时限(秒)。 */
|
| 41 |
+
#define FIRST_FRAME_TIMEOUT_SECONDS 4.0
|
| 42 |
+
/* 停摆重开时等首帧的时限(秒)。实测 DECXIN 被拆流后不会立刻回来,
|
| 43 |
+
* 4 秒内一帧都没有(会被误判成 startup failed 再拆一次,越拆越糟),
|
| 44 |
+
* 放宽到 20 秒再判死。 */
|
| 45 |
+
#define RESTART_FIRST_FRAME_TIMEOUT_SECONDS 20.0
|
| 46 |
+
|
| 47 |
+
/* 连着停摆几次才降档。
|
| 48 |
+
*
|
| 49 |
+
* 设成 2 而不是 1:单次停摆多半只是设备自己抽了一下——实测停摆 6 次里每次
|
| 50 |
+
* 单靠重建 libusb 上下文就能恢复;一停就降会把 alt7 的带宽白白让出去,而
|
| 51 |
+
* alt6 的单帧上限只有 241664 B(实测最大帧 234148 B,3% 余量),降下去是有
|
| 52 |
+
* 代价的。两次说明当前档位确实撑不住:实测 alt7 在整机三路出流时每 45~130 秒
|
| 53 |
+
* 必停一次(AMD 1022:15b7 / PCI 0000:74:00.4,以及 1022:43fc / PCI
|
| 54 |
+
* 0000:0a:00.0,两块都是),两次最多赔 260 秒,且结论会落盘,每台机器只赔这
|
| 55 |
+
* 一回。 */
|
| 56 |
+
#define STALLS_BEFORE_ALTSETTING_DOWNGRADE 2
|
| 57 |
+
|
| 58 |
+
/* 开不起来(rc<0)连着几次才降档。
|
| 59 |
+
*
|
| 60 |
+
* 与停摆分开计、门槛也更高:开不起来多半是设备侧卡死,而那有自己的一条恢复
|
| 61 |
+
* 路径(连续 2 次失败升级成 USB 复位,实测 t≈3 秒就能出帧),一开不起来就降
|
| 62 |
+
* 档会在正常恢复路径上白降。门槛 3 意味着复位试过、还是开不起来,档位才是
|
| 63 |
+
* 剩下的嫌疑。要覆盖的是另一种失败:STREAMON 阶段就排不下高带宽等时档
|
| 64 |
+
* (libusb_set_interface_alt_setting 报 -ENOSPC),此时流根本不会启动,
|
| 65 |
+
* 光靠停摆那条路永远等不到。 */
|
| 66 |
+
#define OPEN_FAILURES_BEFORE_ALTSETTING_DOWNGRADE 3
|
| 67 |
+
|
| 68 |
+
/* 一段流稳定跑够这么久,就把「本档位停摆计数」清零。
|
| 69 |
+
* 要比实测的停摆间隔(45~130 秒)明显长,否则计数永远清不掉。 */
|
| 70 |
+
#define CLEAN_RUN_SECONDS 300.0
|
| 71 |
+
|
| 72 |
+
/* 复位 USB 设备(等价于拔插一次)。只在一个场合用:重开之后一帧都没有,
|
| 73 |
+
* 说明设备侧真的卡住了(典型是上一次服务被 SIGKILL、设备停在激活的等时
|
| 74 |
+
* alt setting 上),只重建上下文救不回来。
|
| 75 |
+
*
|
| 76 |
+
* 平时停摆恢复**不要**走这里:复位是 USB 端口级操作,会重新枚举整条链路,
|
| 77 |
+
* 连带打扰同一总线同一 Hub 上的另外两路 Sightac(实测每次复位两个 Sightac
|
| 78 |
+
* 都掉一批帧)。实测停摆 6 次只用「重建 libusb 上下文」就 6 次全恢复,复位
|
| 79 |
+
* 是白付的代价。
|
| 80 |
+
*
|
| 81 |
+
* libusb 是 libuvc 的依赖、进程里必然已加载,所以按符号名取而不是直接链接
|
| 82 |
+
* ——这样不给二进制新增 NEEDED,交付树里 libuvc/libusb 的解析结果不变。 */
|
| 83 |
+
typedef int (*libusb_reset_device_fn)(struct libusb_device_handle *);
|
| 84 |
+
|
| 85 |
+
static libusb_reset_device_fn resolve_libusb_reset_device(void) {
|
| 86 |
+
static libusb_reset_device_fn cached = NULL;
|
| 87 |
+
static int resolved = 0;
|
| 88 |
+
if (!resolved) {
|
| 89 |
+
cached = (libusb_reset_device_fn)dlsym(RTLD_DEFAULT,
|
| 90 |
+
"libusb_reset_device");
|
| 91 |
+
resolved = 1;
|
| 92 |
+
}
|
| 93 |
+
return cached;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
static volatile sig_atomic_t g_stop = 0;
|
| 97 |
+
static int g_parent_pid = 0;
|
| 98 |
+
|
| 99 |
+
typedef struct {
|
| 100 |
+
char name[64];
|
| 101 |
+
int vid;
|
| 102 |
+
int pid;
|
| 103 |
+
int usb_bus;
|
| 104 |
+
char usb_port_path[64];
|
| 105 |
+
int streaming_interface;
|
| 106 |
+
/* 实际生效的等时档位。这两项不是「声明」——run_camera_once 打开设备后
|
| 107 |
+
* 会把它们交给 libuvc 的 uvc_set_altsetting_override(),由它同时决定
|
| 108 |
+
* COMMIT 的 payload 和 libusb 的等时传输几何。留空(-1)时退回 libuvc
|
| 109 |
+
* 的编译期兜底表。 */
|
| 110 |
+
int forced_altsetting;
|
| 111 |
+
int forced_payload;
|
| 112 |
+
int width;
|
| 113 |
+
int height;
|
| 114 |
+
int fps;
|
| 115 |
+
char format[16];
|
| 116 |
+
/* 相机的 USB 序列号、所在控制器的 PCI 路径、以及学习结果的存放目录。
|
| 117 |
+
* 三者只用来给「这台相机 × 这条物理路径」的档位选择建一个稳定的键,
|
| 118 |
+
* 见 mode_state_key()。控制器只进键、不参与选档。 */
|
| 119 |
+
char usb_serial[64];
|
| 120 |
+
char usb_controller[64];
|
| 121 |
+
char state_dir[PATH_MAX];
|
| 122 |
+
char socket_path[PATH_MAX];
|
| 123 |
+
} camera_config_t;
|
| 124 |
+
|
| 125 |
+
typedef struct {
|
| 126 |
+
uint32_t magic;
|
| 127 |
+
uint16_t version;
|
| 128 |
+
uint16_t header_bytes;
|
| 129 |
+
uint32_t sequence;
|
| 130 |
+
uint32_t width;
|
| 131 |
+
uint32_t height;
|
| 132 |
+
uint32_t fourcc;
|
| 133 |
+
uint32_t payload_bytes;
|
| 134 |
+
uint64_t timestamp_ns;
|
| 135 |
+
} ipc_frame_header_t;
|
| 136 |
+
|
| 137 |
+
typedef struct {
|
| 138 |
+
uint8_t *data;
|
| 139 |
+
size_t length;
|
| 140 |
+
uint32_t sequence;
|
| 141 |
+
uint64_t capture_timestamp_ns;
|
| 142 |
+
} frame_item_t;
|
| 143 |
+
|
| 144 |
+
typedef struct camera_state camera_state_t;
|
| 145 |
+
|
| 146 |
+
struct camera_state {
|
| 147 |
+
camera_config_t config;
|
| 148 |
+
pthread_t thread;
|
| 149 |
+
pthread_t writer_thread;
|
| 150 |
+
pthread_mutex_t mutex;
|
| 151 |
+
pthread_cond_t condition;
|
| 152 |
+
frame_item_t queue[FRAME_QUEUE_DEPTH];
|
| 153 |
+
size_t head;
|
| 154 |
+
size_t count;
|
| 155 |
+
int stopping;
|
| 156 |
+
int listen_fd;
|
| 157 |
+
int client_fd;
|
| 158 |
+
uint64_t frames_received;
|
| 159 |
+
uint64_t frames_output;
|
| 160 |
+
uint64_t frames_dropped;
|
| 161 |
+
uint64_t bad_jpeg_frames;
|
| 162 |
+
uint64_t bad_jpeg_logs;
|
| 163 |
+
uint64_t bad_jpeg_replaced;
|
| 164 |
+
uint64_t write_errors;
|
| 165 |
+
uint64_t reconnects;
|
| 166 |
+
uint64_t stalls;
|
| 167 |
+
/* 当前档位下已经停摆了几次,连着停够 STALLS_BEFORE_ALTSETTING_DOWNGRADE
|
| 168 |
+
* 次就降到下一档;中间只要有一段够长的稳定运行就清零(那次停摆是设备
|
| 169 |
+
* 自己抽了一下,不是档位撑不住)。 */
|
| 170 |
+
unsigned stalls_at_mode;
|
| 171 |
+
/* 当前档位下连着几次没能把流开起来(rc<0)。任何一次「开起来了」的证据
|
| 172 |
+
* 都会清零它——停摆也算,因为停摆说明设备是开得起来的。 */
|
| 173 |
+
unsigned open_failures_at_mode;
|
| 174 |
+
/* 连续「开不起来」的次数(rc<0)。与 stalls 分开计:停摆说明设备能开、
|
| 175 |
+
* 能出帧,只是中途死了;连续开不起来才是设备侧卡死的signature。 */
|
| 176 |
+
unsigned hard_failures;
|
| 177 |
+
unsigned usb_resets;
|
| 178 |
+
|
| 179 |
+
};
|
| 180 |
+
|
| 181 |
+
static int has_mjpeg_soi(const uint8_t *data, size_t length) {
|
| 182 |
+
return length >= 2 && data[0] == 0xff && data[1] == 0xd8;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
static int has_mjpeg_eoi(const uint8_t *data, size_t length) {
|
| 186 |
+
/* Some firmware variants append padding after the EOI marker, so scan
|
| 187 |
+
* backwards instead of requiring the final two bytes to be exactly
|
| 188 |
+
* FF D9. A truncated scan will not contain the marker at all. */
|
| 189 |
+
for (size_t index = length; index >= 2; --index) {
|
| 190 |
+
if (data[index - 2] == 0xff && data[index - 1] == 0xd9) {
|
| 191 |
+
return 1;
|
| 192 |
+
}
|
| 193 |
+
}
|
| 194 |
+
return 0;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
static int is_complete_mjpeg_payload(const uint8_t *data, size_t length) {
|
| 198 |
+
return has_mjpeg_soi(data, length) && has_mjpeg_eoi(data, length);
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
static void request_stop(int signal_number) {
|
| 202 |
+
(void)signal_number;
|
| 203 |
+
g_stop = 1;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
static char *trim(char *value) {
|
| 207 |
+
char *end;
|
| 208 |
+
while (*value == ' ' || *value == '\t' || *value == '\r' ||
|
| 209 |
+
*value == '\n') {
|
| 210 |
+
++value;
|
| 211 |
+
}
|
| 212 |
+
end = value + strlen(value);
|
| 213 |
+
while (end > value && (end[-1] == ' ' || end[-1] == '\t' ||
|
| 214 |
+
end[-1] == '\r' || end[-1] == '\n')) {
|
| 215 |
+
--end;
|
| 216 |
+
}
|
| 217 |
+
*end = '\0';
|
| 218 |
+
return value;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
static int parse_int(const char *value, int *result) {
|
| 222 |
+
char *end = NULL;
|
| 223 |
+
long parsed;
|
| 224 |
+
errno = 0;
|
| 225 |
+
parsed = strtol(value, &end, 0);
|
| 226 |
+
if (errno != 0 || end == value || *trim(end) != '\0' ||
|
| 227 |
+
parsed < INT_MIN || parsed > INT_MAX) {
|
| 228 |
+
return -1;
|
| 229 |
+
}
|
| 230 |
+
*result = (int)parsed;
|
| 231 |
+
return 0;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
static void set_defaults(camera_config_t *config) {
|
| 235 |
+
memset(config, 0, sizeof(*config));
|
| 236 |
+
config->usb_bus = -1;
|
| 237 |
+
config->streaming_interface = 1;
|
| 238 |
+
config->forced_altsetting = -1;
|
| 239 |
+
config->forced_payload = -1;
|
| 240 |
+
config->width = 640;
|
| 241 |
+
config->height = 480;
|
| 242 |
+
config->fps = 30;
|
| 243 |
+
snprintf(config->format, sizeof(config->format), "MJPEG");
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
static int assign_key(camera_config_t *config, const char *key,
|
| 247 |
+
const char *value) {
|
| 248 |
+
if (strcmp(key, "vid") == 0) {
|
| 249 |
+
return parse_int(value, &config->vid);
|
| 250 |
+
}
|
| 251 |
+
if (strcmp(key, "pid") == 0) {
|
| 252 |
+
return parse_int(value, &config->pid);
|
| 253 |
+
}
|
| 254 |
+
if (strcmp(key, "usb_bus") == 0) {
|
| 255 |
+
return parse_int(value, &config->usb_bus);
|
| 256 |
+
}
|
| 257 |
+
if (strcmp(key, "streaming_interface") == 0) {
|
| 258 |
+
return parse_int(value, &config->streaming_interface);
|
| 259 |
+
}
|
| 260 |
+
if (strcmp(key, "forced_altsetting") == 0) {
|
| 261 |
+
return parse_int(value, &config->forced_altsetting);
|
| 262 |
+
}
|
| 263 |
+
if (strcmp(key, "forced_payload") == 0) {
|
| 264 |
+
return parse_int(value, &config->forced_payload);
|
| 265 |
+
}
|
| 266 |
+
if (strcmp(key, "width") == 0) {
|
| 267 |
+
return parse_int(value, &config->width);
|
| 268 |
+
}
|
| 269 |
+
if (strcmp(key, "height") == 0) {
|
| 270 |
+
return parse_int(value, &config->height);
|
| 271 |
+
}
|
| 272 |
+
if (strcmp(key, "fps") == 0) {
|
| 273 |
+
return parse_int(value, &config->fps);
|
| 274 |
+
}
|
| 275 |
+
if (strcmp(key, "usb_port_path") == 0) {
|
| 276 |
+
snprintf(config->usb_port_path, sizeof(config->usb_port_path),
|
| 277 |
+
"%s", value);
|
| 278 |
+
return 0;
|
| 279 |
+
}
|
| 280 |
+
if (strcmp(key, "usb_serial") == 0) {
|
| 281 |
+
snprintf(config->usb_serial, sizeof(config->usb_serial), "%s", value);
|
| 282 |
+
return 0;
|
| 283 |
+
}
|
| 284 |
+
if (strcmp(key, "usb_controller") == 0) {
|
| 285 |
+
snprintf(config->usb_controller, sizeof(config->usb_controller),
|
| 286 |
+
"%s", value);
|
| 287 |
+
return 0;
|
| 288 |
+
}
|
| 289 |
+
if (strcmp(key, "state_dir") == 0) {
|
| 290 |
+
snprintf(config->state_dir, sizeof(config->state_dir), "%s", value);
|
| 291 |
+
return 0;
|
| 292 |
+
}
|
| 293 |
+
if (strcmp(key, "socket_path") == 0) {
|
| 294 |
+
snprintf(config->socket_path, sizeof(config->socket_path), "%s", value);
|
| 295 |
+
return 0;
|
| 296 |
+
}
|
| 297 |
+
if (strcmp(key, "format") == 0) {
|
| 298 |
+
snprintf(config->format, sizeof(config->format),
|
| 299 |
+
"%s", value);
|
| 300 |
+
return 0;
|
| 301 |
+
}
|
| 302 |
+
if (strcmp(key, "name") == 0) {
|
| 303 |
+
snprintf(config->name, sizeof(config->name), "%s", value);
|
| 304 |
+
return 0;
|
| 305 |
+
}
|
| 306 |
+
return 1;
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
static int is_supported_device(const camera_config_t *config) {
|
| 310 |
+
return (config->vid == 0x0c45 && config->pid == 0x636f) ||
|
| 311 |
+
(config->vid == 0x1bcf && config->pid == 0x2d4f);
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
/* 每个机型允许的「档位对」(altsetting, payload),按从好到差的顺序排列。
|
| 315 |
+
* 第 0 项是带宽最好的一档,停摆降级时往后走一格,走到最后一项就不再降
|
| 316 |
+
* ——**降级方向必须是从好到差,表序不能颠倒**。
|
| 317 |
+
*
|
| 318 |
+
* 这两张表必须与 libuvc 的编译期兜底表(third_party/libuvc/src/stream.c
|
| 319 |
+
* 的 ksq_altsetting_quirks)以及扫描程序(tools/discover_uvc_config.c 的
|
| 320 |
+
* profile_for)保持一致:libuvc 的表决定「没有覆盖值时的默认」,这里决定
|
| 321 |
+
* 「服务愿意接受哪些值」。
|
| 322 |
+
*
|
| 323 |
+
* **起始档位不在这张表里定**:起手用哪一档由 uvc_camera_service.py 的
|
| 324 |
+
* _starting_mode 写进 ini(表里最保守的一档,即最后一项),服务只负责接受
|
| 325 |
+
* 表内的值、并在反复停摆时降档。
|
| 326 |
+
*
|
| 327 |
+
* DECXIN 两档的账:alt7/1280 是 2 事务/微帧、10.24 MB/s,单帧上限 327680 B;
|
| 328 |
+
* alt6/944 是单事务档里最大的一档,8 transfer × 32 包 × 944 B = 241664 B 就是
|
| 329 |
+
* 单帧硬上限,而实测最大帧 234148 B——只剩 3% 余量。也就是说 alt7 的余量更
|
| 330 |
+
* 好看,但实测 alt7 在整机三路一起出流时每 45~130 秒必停摆一次,而 alt6 长跑
|
| 331 |
+
* 不停,**两档的出帧率完全一样(都 30.00fps)**——多出来的余量换不到任何看得
|
| 332 |
+
* 见的好处,所以默认就是 alt6。哪一档真的撑不住不靠推测,靠停摆实测往下走
|
| 333 |
+
* (见 maybe_downgrade_altsetting)。 */
|
| 334 |
+
typedef struct {
|
| 335 |
+
int altsetting;
|
| 336 |
+
int payload;
|
| 337 |
+
} altsetting_mode_t;
|
| 338 |
+
|
| 339 |
+
static const altsetting_mode_t kSightacModes[] = {
|
| 340 |
+
{3, 800},
|
| 341 |
+
};
|
| 342 |
+
static const altsetting_mode_t kDecxinModes[] = {
|
| 343 |
+
{7, 1280},
|
| 344 |
+
{6, 944},
|
| 345 |
+
};
|
| 346 |
+
|
| 347 |
+
static const altsetting_mode_t *mode_table_for(const camera_config_t *config,
|
| 348 |
+
size_t *count) {
|
| 349 |
+
if (config->vid == 0x0c45 && config->pid == 0x636f) {
|
| 350 |
+
*count = sizeof(kSightacModes) / sizeof(kSightacModes[0]);
|
| 351 |
+
return kSightacModes;
|
| 352 |
+
}
|
| 353 |
+
if (config->vid == 0x1bcf && config->pid == 0x2d4f) {
|
| 354 |
+
*count = sizeof(kDecxinModes) / sizeof(kDecxinModes[0]);
|
| 355 |
+
return kDecxinModes;
|
| 356 |
+
}
|
| 357 |
+
*count = 0;
|
| 358 |
+
return NULL;
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
static size_t mode_index_for(const camera_config_t *config) {
|
| 362 |
+
size_t count = 0;
|
| 363 |
+
const altsetting_mode_t *modes = mode_table_for(config, &count);
|
| 364 |
+
for (size_t i = 0; i < count; ++i) {
|
| 365 |
+
if (modes[i].altsetting == config->forced_altsetting &&
|
| 366 |
+
modes[i].payload == config->forced_payload) {
|
| 367 |
+
return i;
|
| 368 |
+
}
|
| 369 |
+
}
|
| 370 |
+
return count;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
/* 「这台相机 × 这条物理路径」的落盘键。
|
| 374 |
+
*
|
| 375 |
+
* 刻意不用总线号/端口路径:机架插在哪个口、哪条总线都不是永久的(用户
|
| 376 |
+
* 2026-09-15 明确说过 bus 1 不是永久的),端口一变键就变,要么把上一台
|
| 377 |
+
* 机器的结论套到新拓扑上,要么白学一遍。序列号取自相机描述符、随相机走;
|
| 378 |
+
* 控制器取自 PCI 路径、随插槽走。换电脑 / 换 USB 口都会得到新键,也就是
|
| 379 |
+
* 重新学一次——取保守的那一边:结论严格绑在学它的那条物理路径上。控制器
|
| 380 |
+
* 本身是不是变量并无定论(2026-09-15 实测 alt7 在 74:00.4 与 0a:00.0 上都
|
| 381 |
+
* 停摆),所以它只进键、不参与选档。
|
| 382 |
+
*
|
| 383 |
+
* Python 侧 core/gripper/devices/uvc_camera_service.py 用同一套规则造键去
|
| 384 |
+
* 查这个目录,两边的清洗规则必须逐字一致。 */
|
| 385 |
+
static void mode_state_key(const camera_config_t *config, char *out,
|
| 386 |
+
size_t size) {
|
| 387 |
+
char raw[192];
|
| 388 |
+
size_t written = 0;
|
| 389 |
+
|
| 390 |
+
snprintf(raw, sizeof(raw), "%04x_%04x_%s_%s", config->vid, config->pid,
|
| 391 |
+
config->usb_controller[0] != '\0' ? config->usb_controller
|
| 392 |
+
: "unknown",
|
| 393 |
+
config->usb_serial[0] != '\0' ? config->usb_serial : "noserial");
|
| 394 |
+
for (const char *cursor = raw; *cursor != '\0' && written + 1 < size;
|
| 395 |
+
++cursor) {
|
| 396 |
+
char c = *cursor;
|
| 397 |
+
int safe = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
| 398 |
+
(c >= '0' && c <= '9') || c == '-';
|
| 399 |
+
out[written++] = safe ? c : '_';
|
| 400 |
+
}
|
| 401 |
+
out[written] = '\0';
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
/* 把学到的档位写进 state_dir,下次开机直接用它,不必再赔一次停摆窗口。
|
| 405 |
+
* 尽力而为:目录建不出来、盘写不进去都只记日志,绝不能因此让相机起不来。 */
|
| 406 |
+
static void persist_mode_choice(const camera_state_t *camera) {
|
| 407 |
+
char key[200];
|
| 408 |
+
char path[PATH_MAX];
|
| 409 |
+
char temporary[PATH_MAX];
|
| 410 |
+
FILE *file;
|
| 411 |
+
|
| 412 |
+
if (camera->config.state_dir[0] == '\0') {
|
| 413 |
+
return;
|
| 414 |
+
}
|
| 415 |
+
mode_state_key(&camera->config, key, sizeof(key));
|
| 416 |
+
if (snprintf(path, sizeof(path), "%s/%s.mode", camera->config.state_dir,
|
| 417 |
+
key) >= (int)sizeof(path) ||
|
| 418 |
+
snprintf(temporary, sizeof(temporary), "%s.tmp.%d", path,
|
| 419 |
+
(int)getpid()) >= (int)sizeof(temporary)) {
|
| 420 |
+
fprintf(stderr, "[%s] state path too long; not persisting mode\n",
|
| 421 |
+
camera->config.name);
|
| 422 |
+
return;
|
| 423 |
+
}
|
| 424 |
+
if (mkdir(camera->config.state_dir, 0755) != 0 && errno != EEXIST) {
|
| 425 |
+
fprintf(stderr, "[%s] cannot create state dir %s: %s\n",
|
| 426 |
+
camera->config.name, camera->config.state_dir,
|
| 427 |
+
strerror(errno));
|
| 428 |
+
return;
|
| 429 |
+
}
|
| 430 |
+
file = fopen(temporary, "w");
|
| 431 |
+
if (file == NULL) {
|
| 432 |
+
fprintf(stderr, "[%s] cannot write %s: %s\n", camera->config.name,
|
| 433 |
+
temporary, strerror(errno));
|
| 434 |
+
return;
|
| 435 |
+
}
|
| 436 |
+
fprintf(file, "altsetting=%d\npayload=%d\n",
|
| 437 |
+
camera->config.forced_altsetting, camera->config.forced_payload);
|
| 438 |
+
fflush(file);
|
| 439 |
+
fsync(fileno(file));
|
| 440 |
+
fclose(file);
|
| 441 |
+
if (rename(temporary, path) != 0) {
|
| 442 |
+
fprintf(stderr, "[%s] cannot install %s: %s\n", camera->config.name,
|
| 443 |
+
path, strerror(errno));
|
| 444 |
+
unlink(temporary);
|
| 445 |
+
return;
|
| 446 |
+
}
|
| 447 |
+
fprintf(stderr, "[%s] remembered altsetting=%d payload=%d for this "
|
| 448 |
+
"camera/controller in %s\n", camera->config.name,
|
| 449 |
+
camera->config.forced_altsetting, camera->config.forced_payload,
|
| 450 |
+
path);
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
static int validate_config(const camera_config_t *config, size_t index) {
|
| 454 |
+
if (config->name[0] == '\0' || config->vid <= 0 || config->pid <= 0 ||
|
| 455 |
+
config->usb_bus < 0 || config->usb_port_path[0] == '\0' ||
|
| 456 |
+
config->socket_path[0] == '\0' || config->streaming_interface < 0 ||
|
| 457 |
+
config->forced_altsetting < 0 || config->forced_payload <= 0 ||
|
| 458 |
+
config->width <= 0 || config->height <= 0 || config->fps <= 0 ||
|
| 459 |
+
strcasecmp(config->format, "MJPEG") != 0) {
|
| 460 |
+
fprintf(stderr, "invalid camera config at index %zu\n", index);
|
| 461 |
+
return -1;
|
| 462 |
+
}
|
| 463 |
+
if (!is_supported_device(config)) {
|
| 464 |
+
fprintf(stderr, "%s: unsupported UVC device %04x:%04x; only Sightac "
|
| 465 |
+
"0c45:636f and DECXIN 1bcf:2d4f are supported\n",
|
| 466 |
+
config->name, config->vid, config->pid);
|
| 467 |
+
return -1;
|
| 468 |
+
}
|
| 469 |
+
if (config->vid == 0x0c45 && config->pid == 0x636f &&
|
| 470 |
+
(config->width != 640 || config->height != 480 || config->fps != 30)) {
|
| 471 |
+
fprintf(stderr, "%s: unsupported Sightac mode; expected 640x480 "
|
| 472 |
+
"MJPEG@30\n", config->name);
|
| 473 |
+
return -1;
|
| 474 |
+
}
|
| 475 |
+
if (config->vid == 0x1bcf && config->pid == 0x2d4f &&
|
| 476 |
+
(config->width != 1280 || config->height != 960 || config->fps != 30)) {
|
| 477 |
+
fprintf(stderr, "%s: unsupported DECXIN mode; expected 1280x960 "
|
| 478 |
+
"MJPEG@30\n", config->name);
|
| 479 |
+
return -1;
|
| 480 |
+
}
|
| 481 |
+
/* 档位对必须整体命中表里某一项。只查「alt 在表里」是不够的:alt 和
|
| 482 |
+
* payload 是一个整体(alt7 的端点就是 1280 B/包),把 7 和 944 拼在
|
| 483 |
+
* 一起会让 libuvc 用 alt7 的端点去跑 944 的预算,而 944 只是 COMMIT
|
| 484 |
+
* 上写的数——2026-09-15 主程序连不上夹爪就是这两项被拆开配出来的。 */
|
| 485 |
+
{
|
| 486 |
+
size_t count = 0;
|
| 487 |
+
mode_table_for(config, &count);
|
| 488 |
+
if (mode_index_for(config) < count) {
|
| 489 |
+
return 0;
|
| 490 |
+
}
|
| 491 |
+
}
|
| 492 |
+
fprintf(stderr, "%s: unsupported altsetting pair alt=%d payload=%d; this "
|
| 493 |
+
"camera accepts only the pairs in the service's mode table\n",
|
| 494 |
+
config->name, config->forced_altsetting, config->forced_payload);
|
| 495 |
+
return -1;
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
static int configs_conflict(const camera_config_t *left,
|
| 499 |
+
const camera_config_t *right) {
|
| 500 |
+
if (strcmp(left->socket_path, right->socket_path) == 0) {
|
| 501 |
+
return 1;
|
| 502 |
+
}
|
| 503 |
+
return left->vid == right->vid && left->pid == right->pid &&
|
| 504 |
+
left->usb_bus == right->usb_bus &&
|
| 505 |
+
strcmp(left->usb_port_path, right->usb_port_path) == 0;
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
static int load_config(const char *path, camera_config_t *configs,
|
| 509 |
+
size_t *count) {
|
| 510 |
+
FILE *file;
|
| 511 |
+
char line[MAX_LINE];
|
| 512 |
+
camera_config_t *current = NULL;
|
| 513 |
+
size_t index = 0;
|
| 514 |
+
|
| 515 |
+
file = fopen(path, "r");
|
| 516 |
+
if (file == NULL) {
|
| 517 |
+
fprintf(stderr, "cannot open config %s: %s\n", path, strerror(errno));
|
| 518 |
+
return -1;
|
| 519 |
+
}
|
| 520 |
+
while (fgets(line, sizeof(line), file) != NULL) {
|
| 521 |
+
char *text = trim(line);
|
| 522 |
+
char *equals;
|
| 523 |
+
if (*text == '\0' || *text == '#' || *text == ';') {
|
| 524 |
+
continue;
|
| 525 |
+
}
|
| 526 |
+
if (*text == '[') {
|
| 527 |
+
char *close = strchr(text, ']');
|
| 528 |
+
if (close == NULL || index >= MAX_CAMERAS) {
|
| 529 |
+
fclose(file);
|
| 530 |
+
return -1;
|
| 531 |
+
}
|
| 532 |
+
*close = '\0';
|
| 533 |
+
set_defaults(&configs[index]);
|
| 534 |
+
snprintf(configs[index].name, sizeof(configs[index].name),
|
| 535 |
+
"%s", trim(text + 1));
|
| 536 |
+
current = &configs[index++];
|
| 537 |
+
continue;
|
| 538 |
+
}
|
| 539 |
+
if (current == NULL || (equals = strchr(text, '=')) == NULL) {
|
| 540 |
+
fclose(file);
|
| 541 |
+
return -1;
|
| 542 |
+
}
|
| 543 |
+
*equals = '\0';
|
| 544 |
+
{
|
| 545 |
+
char *key = trim(text);
|
| 546 |
+
char *value = trim(equals + 1);
|
| 547 |
+
char *comment = strchr(value, '#');
|
| 548 |
+
if (comment != NULL) {
|
| 549 |
+
*comment = '\0';
|
| 550 |
+
value = trim(value);
|
| 551 |
+
}
|
| 552 |
+
if (assign_key(current, key, value) < 0) {
|
| 553 |
+
fprintf(stderr, "invalid config value %s=%s\n", key, value);
|
| 554 |
+
fclose(file);
|
| 555 |
+
return -1;
|
| 556 |
+
}
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
fclose(file);
|
| 560 |
+
if (index == 0 || index > MAX_CAMERAS) {
|
| 561 |
+
return -1;
|
| 562 |
+
}
|
| 563 |
+
for (size_t i = 0; i < index; ++i) {
|
| 564 |
+
if (validate_config(&configs[i], i) != 0) {
|
| 565 |
+
return -1;
|
| 566 |
+
}
|
| 567 |
+
for (size_t j = 0; j < i; ++j) {
|
| 568 |
+
if (configs_conflict(&configs[i], &configs[j])) {
|
| 569 |
+
fprintf(stderr,
|
| 570 |
+
"camera config conflict between %s and %s "
|
| 571 |
+
"(duplicate output or USB identity)\n",
|
| 572 |
+
configs[j].name, configs[i].name);
|
| 573 |
+
return -1;
|
| 574 |
+
}
|
| 575 |
+
}
|
| 576 |
+
}
|
| 577 |
+
*count = index;
|
| 578 |
+
return 0;
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
+
static int uvc_port_path(uvc_device_t *device, char *buffer,
|
| 582 |
+
size_t buffer_size) {
|
| 583 |
+
uint8_t ports[8];
|
| 584 |
+
int count;
|
| 585 |
+
size_t used = 0;
|
| 586 |
+
count = uvc_get_port_numbers(device, ports, sizeof(ports));
|
| 587 |
+
if (count < 0) {
|
| 588 |
+
return -1;
|
| 589 |
+
}
|
| 590 |
+
buffer[0] = '\0';
|
| 591 |
+
for (int i = 0; i < count; ++i) {
|
| 592 |
+
int written = snprintf(buffer + used, buffer_size - used, "%s%u",
|
| 593 |
+
i == 0 ? "" : ".", ports[i]);
|
| 594 |
+
if (written < 0 || (size_t)written >= buffer_size - used) {
|
| 595 |
+
return -1;
|
| 596 |
+
}
|
| 597 |
+
used += (size_t)written;
|
| 598 |
+
}
|
| 599 |
+
return 0;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
static uvc_error_t open_matching_device(uvc_context_t *context,
|
| 603 |
+
const camera_config_t *config,
|
| 604 |
+
uvc_device_handle_t **result,
|
| 605 |
+
char *actual_path,
|
| 606 |
+
size_t actual_path_size) {
|
| 607 |
+
uvc_device_t **list = NULL;
|
| 608 |
+
uvc_error_t error;
|
| 609 |
+
*result = NULL;
|
| 610 |
+
error = uvc_get_device_list(context, &list);
|
| 611 |
+
if (error != UVC_SUCCESS) {
|
| 612 |
+
return error;
|
| 613 |
+
}
|
| 614 |
+
for (size_t i = 0; list[i] != NULL; ++i) {
|
| 615 |
+
uvc_device_descriptor_t *descriptor = NULL;
|
| 616 |
+
uvc_device_handle_t *candidate = NULL;
|
| 617 |
+
error = uvc_get_device_descriptor(list[i], &descriptor);
|
| 618 |
+
if (error != UVC_SUCCESS || descriptor == NULL ||
|
| 619 |
+
descriptor->idVendor != (uint16_t)config->vid ||
|
| 620 |
+
descriptor->idProduct != (uint16_t)config->pid ||
|
| 621 |
+
(int)uvc_get_bus_number(list[i]) != config->usb_bus) {
|
| 622 |
+
uvc_free_device_descriptor(descriptor);
|
| 623 |
+
continue;
|
| 624 |
+
}
|
| 625 |
+
if (uvc_port_path(list[i], actual_path, actual_path_size) != 0 ||
|
| 626 |
+
strcmp(actual_path, config->usb_port_path) != 0) {
|
| 627 |
+
uvc_free_device_descriptor(descriptor);
|
| 628 |
+
continue;
|
| 629 |
+
}
|
| 630 |
+
error = uvc_open(list[i], &candidate);
|
| 631 |
+
uvc_free_device_descriptor(descriptor);
|
| 632 |
+
if (error != UVC_SUCCESS || candidate == NULL) {
|
| 633 |
+
fprintf(stderr, "[%s] matched bus=%d port=%s open failed: %s (%d)\n",
|
| 634 |
+
config->name, config->usb_bus, actual_path,
|
| 635 |
+
uvc_strerror(error), error);
|
| 636 |
+
uvc_free_device_list(list, 1);
|
| 637 |
+
return error == UVC_SUCCESS ? UVC_ERROR_IO : error;
|
| 638 |
+
}
|
| 639 |
+
*result = candidate;
|
| 640 |
+
uvc_free_device_list(list, 1);
|
| 641 |
+
return UVC_SUCCESS;
|
| 642 |
+
}
|
| 643 |
+
uvc_free_device_list(list, 1);
|
| 644 |
+
return UVC_ERROR_NO_DEVICE;
|
| 645 |
+
}
|
| 646 |
+
|
| 647 |
+
static uint64_t timespec_nanoseconds(struct timespec value) {
|
| 648 |
+
if (value.tv_sec < 0 || value.tv_nsec < 0 || value.tv_nsec >= 1000000000L) {
|
| 649 |
+
return 0;
|
| 650 |
+
}
|
| 651 |
+
return (uint64_t)value.tv_sec * 1000000000ull +
|
| 652 |
+
(uint64_t)value.tv_nsec;
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
static int configure_ipc(camera_state_t *camera) {
|
| 656 |
+
struct sockaddr_un address;
|
| 657 |
+
mode_t old_umask;
|
| 658 |
+
size_t path_length = strlen(camera->config.socket_path);
|
| 659 |
+
|
| 660 |
+
if (path_length >= sizeof(address.sun_path)) {
|
| 661 |
+
fprintf(stderr, "[%s] Unix socket path is too long: %s\n",
|
| 662 |
+
camera->config.name, camera->config.socket_path);
|
| 663 |
+
return -1;
|
| 664 |
+
}
|
| 665 |
+
/*
|
| 666 |
+
* Use a byte-stream Unix socket instead of SOCK_SEQPACKET. A DECXIN
|
| 667 |
+
* MJPEG frame can exceed the kernel's AF_UNIX packet-size limit; a
|
| 668 |
+
* length-bearing stream header avoids EMSGSIZE while retaining one
|
| 669 |
+
* complete frame per read_packet() at the protocol level.
|
| 670 |
+
*/
|
| 671 |
+
camera->listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
| 672 |
+
if (camera->listen_fd < 0) {
|
| 673 |
+
fprintf(stderr, "[%s] create Unix socket failed: %s\n",
|
| 674 |
+
camera->config.name, strerror(errno));
|
| 675 |
+
return -1;
|
| 676 |
+
}
|
| 677 |
+
memset(&address, 0, sizeof(address));
|
| 678 |
+
address.sun_family = AF_UNIX;
|
| 679 |
+
memcpy(address.sun_path, camera->config.socket_path, path_length + 1);
|
| 680 |
+
unlink(camera->config.socket_path);
|
| 681 |
+
old_umask = umask(0);
|
| 682 |
+
if (bind(camera->listen_fd, (struct sockaddr *)&address,
|
| 683 |
+
sizeof(address)) < 0) {
|
| 684 |
+
umask(old_umask);
|
| 685 |
+
fprintf(stderr, "[%s] bind Unix socket %s failed: %s\n",
|
| 686 |
+
camera->config.name, camera->config.socket_path,
|
| 687 |
+
strerror(errno));
|
| 688 |
+
close(camera->listen_fd);
|
| 689 |
+
camera->listen_fd = -1;
|
| 690 |
+
return -1;
|
| 691 |
+
}
|
| 692 |
+
umask(old_umask);
|
| 693 |
+
if (chmod(camera->config.socket_path, 0666) != 0 ||
|
| 694 |
+
listen(camera->listen_fd, 1) != 0) {
|
| 695 |
+
fprintf(stderr, "[%s] prepare Unix socket %s failed: %s\n",
|
| 696 |
+
camera->config.name, camera->config.socket_path,
|
| 697 |
+
strerror(errno));
|
| 698 |
+
close(camera->listen_fd);
|
| 699 |
+
camera->listen_fd = -1;
|
| 700 |
+
unlink(camera->config.socket_path);
|
| 701 |
+
return -1;
|
| 702 |
+
}
|
| 703 |
+
fprintf(stderr, "[%s] IPC socket=%s format=MJPEG size=%dx%d fps=%d\n",
|
| 704 |
+
camera->config.name, camera->config.socket_path,
|
| 705 |
+
camera->config.width, camera->config.height, camera->config.fps);
|
| 706 |
+
return 0;
|
| 707 |
+
}
|
| 708 |
+
|
| 709 |
+
static int accept_ipc_client(camera_state_t *camera) {
|
| 710 |
+
while (!g_stop && camera->client_fd < 0) {
|
| 711 |
+
struct pollfd descriptor = {
|
| 712 |
+
.fd = camera->listen_fd,
|
| 713 |
+
.events = POLLIN,
|
| 714 |
+
.revents = 0,
|
| 715 |
+
};
|
| 716 |
+
int ready = poll(&descriptor, 1, 500);
|
| 717 |
+
if (ready < 0 && errno == EINTR) {
|
| 718 |
+
continue;
|
| 719 |
+
}
|
| 720 |
+
if (ready <= 0) {
|
| 721 |
+
continue;
|
| 722 |
+
}
|
| 723 |
+
camera->client_fd = accept(camera->listen_fd, NULL, NULL);
|
| 724 |
+
if (camera->client_fd < 0 && errno != EINTR) {
|
| 725 |
+
return errno;
|
| 726 |
+
}
|
| 727 |
+
}
|
| 728 |
+
return camera->client_fd >= 0 ? 0 : ECANCELED;
|
| 729 |
+
}
|
| 730 |
+
|
| 731 |
+
static int send_all(int fd, const void *data, size_t length) {
|
| 732 |
+
const uint8_t *cursor = (const uint8_t *)data;
|
| 733 |
+
while (length > 0) {
|
| 734 |
+
ssize_t sent = send(fd, cursor, length, MSG_NOSIGNAL);
|
| 735 |
+
if (sent < 0) {
|
| 736 |
+
if (errno == EINTR) {
|
| 737 |
+
continue;
|
| 738 |
+
}
|
| 739 |
+
return errno;
|
| 740 |
+
}
|
| 741 |
+
if (sent == 0) {
|
| 742 |
+
return EPIPE;
|
| 743 |
+
}
|
| 744 |
+
cursor += (size_t)sent;
|
| 745 |
+
length -= (size_t)sent;
|
| 746 |
+
}
|
| 747 |
+
return 0;
|
| 748 |
+
}
|
| 749 |
+
|
| 750 |
+
static int write_frame(camera_state_t *camera, const frame_item_t *item) {
|
| 751 |
+
ipc_frame_header_t header;
|
| 752 |
+
int error;
|
| 753 |
+
|
| 754 |
+
if (accept_ipc_client(camera) != 0) {
|
| 755 |
+
return g_stop ? ECANCELED : ENOTCONN;
|
| 756 |
+
}
|
| 757 |
+
memset(&header, 0, sizeof(header));
|
| 758 |
+
header.magic = IPC_MAGIC;
|
| 759 |
+
header.version = IPC_VERSION;
|
| 760 |
+
header.header_bytes = (uint16_t)sizeof(header);
|
| 761 |
+
header.sequence = item->sequence;
|
| 762 |
+
header.width = (uint32_t)camera->config.width;
|
| 763 |
+
header.height = (uint32_t)camera->config.height;
|
| 764 |
+
header.fourcc = (uint32_t)('M' | ('J' << 8) | ('P' << 16) | ('G' << 24));
|
| 765 |
+
header.payload_bytes = (uint32_t)item->length;
|
| 766 |
+
/*
|
| 767 |
+
* Preserve the timestamp assigned by libuvc when the complete UVC frame
|
| 768 |
+
* was received. The socket writer can run later (or burst after a
|
| 769 |
+
* client connects), so send time would measure IPC scheduling rather than
|
| 770 |
+
* camera acquisition cadence.
|
| 771 |
+
*/
|
| 772 |
+
header.timestamp_ns = item->capture_timestamp_ns;
|
| 773 |
+
|
| 774 |
+
error = send_all(camera->client_fd, &header, sizeof(header));
|
| 775 |
+
if (error == 0) {
|
| 776 |
+
error = send_all(camera->client_fd, item->data, item->length);
|
| 777 |
+
}
|
| 778 |
+
/* Any partial write invalidates byte-stream framing, including timeout. */
|
| 779 |
+
if (error != 0) {
|
| 780 |
+
close(camera->client_fd);
|
| 781 |
+
camera->client_fd = -1;
|
| 782 |
+
}
|
| 783 |
+
return error;
|
| 784 |
+
}
|
| 785 |
+
|
| 786 |
+
static void enqueue_frame(uvc_frame_t *frame, camera_state_t *camera) {
|
| 787 |
+
uint8_t *copy;
|
| 788 |
+
size_t tail;
|
| 789 |
+
uint64_t capture_timestamp_ns;
|
| 790 |
+
uint8_t *source = NULL;
|
| 791 |
+
size_t source_length = 0;
|
| 792 |
+
uint32_t source_sequence = 0;
|
| 793 |
+
if (frame == NULL || frame->data == NULL || frame->data_bytes == 0 ||
|
| 794 |
+
frame->frame_format != UVC_FRAME_FORMAT_MJPEG) {
|
| 795 |
+
return;
|
| 796 |
+
}
|
| 797 |
+
if (!is_complete_mjpeg_payload(frame->data, frame->data_bytes)) {
|
| 798 |
+
int should_log;
|
| 799 |
+
uint64_t bad_count;
|
| 800 |
+
pthread_mutex_lock(&camera->mutex);
|
| 801 |
+
camera->bad_jpeg_frames++;
|
| 802 |
+
camera->bad_jpeg_logs++;
|
| 803 |
+
camera->frames_dropped++;
|
| 804 |
+
bad_count = camera->bad_jpeg_frames;
|
| 805 |
+
should_log = bad_count == 1 || camera->bad_jpeg_logs % 100 == 0;
|
| 806 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 807 |
+
if (should_log) {
|
| 808 |
+
fprintf(stderr,
|
| 809 |
+
"[%s] dropped incomplete MJPEG sequence=%u bytes=%zu "
|
| 810 |
+
"bad=%llu\n",
|
| 811 |
+
camera->config.name, frame->sequence, frame->data_bytes,
|
| 812 |
+
(unsigned long long)bad_count);
|
| 813 |
+
}
|
| 814 |
+
/* Never fabricate a new sample from an older image. The sequence
|
| 815 |
+
* gap is intentional and preserves honest acquisition timestamps. */
|
| 816 |
+
return;
|
| 817 |
+
}
|
| 818 |
+
source = frame->data;
|
| 819 |
+
source_length = frame->data_bytes;
|
| 820 |
+
source_sequence = frame->sequence;
|
| 821 |
+
capture_timestamp_ns = timespec_nanoseconds(frame->capture_time_finished);
|
| 822 |
+
if (capture_timestamp_ns == 0) {
|
| 823 |
+
pthread_mutex_lock(&camera->mutex);
|
| 824 |
+
camera->frames_dropped++;
|
| 825 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 826 |
+
return;
|
| 827 |
+
}
|
| 828 |
+
copy = malloc(source_length);
|
| 829 |
+
if (copy == NULL) {
|
| 830 |
+
pthread_mutex_lock(&camera->mutex);
|
| 831 |
+
camera->frames_dropped++;
|
| 832 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 833 |
+
return;
|
| 834 |
+
}
|
| 835 |
+
memcpy(copy, source, source_length);
|
| 836 |
+
pthread_mutex_lock(&camera->mutex);
|
| 837 |
+
if (camera->stopping) {
|
| 838 |
+
free(copy);
|
| 839 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 840 |
+
return;
|
| 841 |
+
}
|
| 842 |
+
if (camera->count == FRAME_QUEUE_DEPTH) {
|
| 843 |
+
free(camera->queue[camera->head].data);
|
| 844 |
+
camera->head = (camera->head + 1) % FRAME_QUEUE_DEPTH;
|
| 845 |
+
camera->count--;
|
| 846 |
+
camera->frames_dropped++;
|
| 847 |
+
}
|
| 848 |
+
tail = (camera->head + camera->count) % FRAME_QUEUE_DEPTH;
|
| 849 |
+
camera->queue[tail].data = copy;
|
| 850 |
+
camera->queue[tail].length = source_length;
|
| 851 |
+
camera->queue[tail].sequence = source_sequence;
|
| 852 |
+
camera->queue[tail].capture_timestamp_ns = capture_timestamp_ns;
|
| 853 |
+
camera->count++;
|
| 854 |
+
camera->frames_received++;
|
| 855 |
+
|
| 856 |
+
pthread_cond_signal(&camera->condition);
|
| 857 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
static void frame_callback(uvc_frame_t *frame, void *user_ptr) {
|
| 861 |
+
enqueue_frame(frame, (camera_state_t *)user_ptr);
|
| 862 |
+
}
|
| 863 |
+
|
| 864 |
+
static void *writer_main(void *user_ptr) {
|
| 865 |
+
camera_state_t *camera = (camera_state_t *)user_ptr;
|
| 866 |
+
for (;;) {
|
| 867 |
+
frame_item_t item;
|
| 868 |
+
memset(&item, 0, sizeof(item));
|
| 869 |
+
pthread_mutex_lock(&camera->mutex);
|
| 870 |
+
while (camera->count == 0 && !camera->stopping) {
|
| 871 |
+
pthread_cond_wait(&camera->condition, &camera->mutex);
|
| 872 |
+
}
|
| 873 |
+
if (camera->count == 0 && camera->stopping) {
|
| 874 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 875 |
+
break;
|
| 876 |
+
}
|
| 877 |
+
item = camera->queue[camera->head];
|
| 878 |
+
camera->queue[camera->head].data = NULL;
|
| 879 |
+
camera->head = (camera->head + 1) % FRAME_QUEUE_DEPTH;
|
| 880 |
+
camera->count--;
|
| 881 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 882 |
+
|
| 883 |
+
int write_error = write_frame(camera, &item);
|
| 884 |
+
if (write_error == 0) {
|
| 885 |
+
pthread_mutex_lock(&camera->mutex);
|
| 886 |
+
camera->frames_output++;
|
| 887 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 888 |
+
} else if (write_error == ECANCELED && g_stop) {
|
| 889 |
+
/* Normal shutdown: do not report queued frames canceled by the
|
| 890 |
+
* stop signal as an output failure. */
|
| 891 |
+
} else {
|
| 892 |
+
pthread_mutex_lock(&camera->mutex);
|
| 893 |
+
camera->write_errors++;
|
| 894 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 895 |
+
fprintf(stderr, "[%s] output write failed: %s (%d)\n",
|
| 896 |
+
camera->config.name, strerror(write_error), write_error);
|
| 897 |
+
}
|
| 898 |
+
free(item.data);
|
| 899 |
+
}
|
| 900 |
+
return NULL;
|
| 901 |
+
}
|
| 902 |
+
|
| 903 |
+
static double monotonic_seconds(void) {
|
| 904 |
+
struct timespec now;
|
| 905 |
+
if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
|
| 906 |
+
return 0.0;
|
| 907 |
+
}
|
| 908 |
+
return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
|
| 909 |
+
}
|
| 910 |
+
|
| 911 |
+
static void report_stats(camera_state_t *camera, double elapsed,
|
| 912 |
+
uint64_t previous_received,
|
| 913 |
+
uint64_t previous_output,
|
| 914 |
+
uint64_t previous_dropped,
|
| 915 |
+
uint64_t previous_bad_jpeg) {
|
| 916 |
+
uint64_t received;
|
| 917 |
+
uint64_t output;
|
| 918 |
+
uint64_t dropped;
|
| 919 |
+
uint64_t bad_jpeg;
|
| 920 |
+
uint64_t bad_replaced;
|
| 921 |
+
uint64_t write_errors;
|
| 922 |
+
size_t queued;
|
| 923 |
+
|
| 924 |
+
pthread_mutex_lock(&camera->mutex);
|
| 925 |
+
received = camera->frames_received;
|
| 926 |
+
output = camera->frames_output;
|
| 927 |
+
dropped = camera->frames_dropped;
|
| 928 |
+
bad_jpeg = camera->bad_jpeg_frames;
|
| 929 |
+
bad_replaced = camera->bad_jpeg_replaced;
|
| 930 |
+
write_errors = camera->write_errors;
|
| 931 |
+
queued = camera->count;
|
| 932 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 933 |
+
if (elapsed <= 0.0) {
|
| 934 |
+
return;
|
| 935 |
+
}
|
| 936 |
+
fprintf(stderr,
|
| 937 |
+
"[%s] fps_in=%.2f fps_out=%.2f received=%llu output=%llu "
|
| 938 |
+
"dropped=%llu (+%llu) bad_jpeg=%llu (+%llu) "
|
| 939 |
+
"bad_replaced=%llu write_errors=%llu queue=%zu\n",
|
| 940 |
+
camera->config.name,
|
| 941 |
+
(double)(received - previous_received) / elapsed,
|
| 942 |
+
(double)(output - previous_output) / elapsed,
|
| 943 |
+
(unsigned long long)received, (unsigned long long)output,
|
| 944 |
+
(unsigned long long)dropped,
|
| 945 |
+
(unsigned long long)(dropped - previous_dropped),
|
| 946 |
+
(unsigned long long)bad_jpeg,
|
| 947 |
+
(unsigned long long)(bad_jpeg - previous_bad_jpeg),
|
| 948 |
+
(unsigned long long)bad_replaced,
|
| 949 |
+
(unsigned long long)write_errors, queued);
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
static void close_ipc(camera_state_t *camera) {
|
| 953 |
+
pthread_mutex_lock(&camera->mutex);
|
| 954 |
+
camera->stopping = 1;
|
| 955 |
+
pthread_cond_broadcast(&camera->condition);
|
| 956 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 957 |
+
if (camera->writer_thread != 0) {
|
| 958 |
+
pthread_join(camera->writer_thread, NULL);
|
| 959 |
+
camera->writer_thread = 0;
|
| 960 |
+
}
|
| 961 |
+
for (size_t i = 0; i < FRAME_QUEUE_DEPTH; ++i) {
|
| 962 |
+
free(camera->queue[i].data);
|
| 963 |
+
camera->queue[i].data = NULL;
|
| 964 |
+
}
|
| 965 |
+
camera->head = 0;
|
| 966 |
+
camera->count = 0;
|
| 967 |
+
if (camera->client_fd >= 0) {
|
| 968 |
+
close(camera->client_fd);
|
| 969 |
+
camera->client_fd = -1;
|
| 970 |
+
}
|
| 971 |
+
if (camera->listen_fd >= 0) {
|
| 972 |
+
close(camera->listen_fd);
|
| 973 |
+
camera->listen_fd = -1;
|
| 974 |
+
}
|
| 975 |
+
unlink(camera->config.socket_path);
|
| 976 |
+
}
|
| 977 |
+
|
| 978 |
+
static void reset_usb_device(camera_state_t *camera,
|
| 979 |
+
uvc_device_handle_t *device) {
|
| 980 |
+
struct libusb_device_handle *usb_handle = uvc_get_libusb_handle(device);
|
| 981 |
+
libusb_reset_device_fn reset_device = resolve_libusb_reset_device();
|
| 982 |
+
if (usb_handle == NULL || reset_device == NULL) {
|
| 983 |
+
fprintf(stderr,
|
| 984 |
+
"[%s] cannot reset device (handle=%p symbol=%p)\n",
|
| 985 |
+
camera->config.name, (void *)usb_handle, (void *)reset_device);
|
| 986 |
+
return;
|
| 987 |
+
}
|
| 988 |
+
int reset_rc = reset_device(usb_handle);
|
| 989 |
+
fprintf(stderr, "[%s] usb reset: rc=%d (%s)\n", camera->config.name,
|
| 990 |
+
reset_rc, uvc_strerror(reset_rc));
|
| 991 |
+
}
|
| 992 |
+
|
| 993 |
+
/* 上一次复位之后留给客户端的余量:客户端只等 10 秒就把服务收掉,所以
|
| 994 |
+
* 「第几次失败才升级成复位」是拿这个窗口倒推出来的,不是拍的。
|
| 995 |
+
* 实测卡死设备的失败很快(每次约 0.3 秒 + 1 秒间隔),第 2 次失败触发
|
| 996 |
+
* 复位的时刻在 t≈2 秒,重枚举加重开约 1 秒,t≈3 秒就能出帧——10 秒够用。
|
| 997 |
+
* 门槛设成「连续 2 次」而不是「1 次」:单次失败往往只是设备还没缓过来
|
| 998 |
+
* (那种情况重建上下文就够,实测 6/6 全恢复),一失败就复位会白白打扰
|
| 999 |
+
* 同 Hub 的另外两路。
|
| 1000 |
+
*
|
| 1001 |
+
* 上限 USB_RESET_LIMIT 次:设备真坏了的时候,不设上限会变成每 2 秒复位
|
| 1002 |
+
* 一次、把另外两路一直按在地上。打满就不再复位,退回普通重试。 */
|
| 1003 |
+
#define HARD_FAILURES_BEFORE_RESET 1
|
| 1004 |
+
#define USB_RESET_LIMIT 3
|
| 1005 |
+
|
| 1006 |
+
/* 停摆够次数就退一档,并记住这次选择。
|
| 1007 |
+
*
|
| 1008 |
+
* 为什么要有这条路(而不是起手就用最保守档、或者按机器写死):起手档已经
|
| 1009 |
+
* 是最保守的那一档了,这条路是给**操作者手动把 ini 改成高带宽档**兜底的
|
| 1010 |
+
* ——想要 alt7 那点每帧余量就改,真撑不住时服务停两次自己退回 alt6 并把结论
|
| 1011 |
+
* 落盘,下次开机直接用,不用人去记哪台机器该用哪档。按机器写死则是另一回事:
|
| 1012 |
+
* 机架插在哪条总线、哪台机器都不固定,写死要每台各配一份,而且写死的那个
|
| 1013 |
+
* 判断(「这台控制器扛不扛得住」)本身就没被测准过——试错要便宜得多。
|
| 1014 |
+
*
|
| 1015 |
+
* 返回 1 表示已降档。 */
|
| 1016 |
+
static int maybe_downgrade_altsetting(camera_state_t *camera) {
|
| 1017 |
+
size_t count = 0;
|
| 1018 |
+
const altsetting_mode_t *modes = mode_table_for(&camera->config, &count);
|
| 1019 |
+
size_t index = mode_index_for(&camera->config);
|
| 1020 |
+
|
| 1021 |
+
if (modes == NULL || index + 1 >= count) {
|
| 1022 |
+
return 0;
|
| 1023 |
+
}
|
| 1024 |
+
camera->config.forced_altsetting = modes[index + 1].altsetting;
|
| 1025 |
+
camera->config.forced_payload = modes[index + 1].payload;
|
| 1026 |
+
/* 两个计数都要归零,各自对应一条降档触发路径。漏掉哪个,那条路就
|
| 1027 |
+
* 只能降一次:stalls 由本函数清零,open_failures 若不清零,判等
|
| 1028 |
+
* (== 门槛)在降档后永远不再成立,梯子在「开不起来」那条路上走
|
| 1029 |
+
* 完一格就卡死,后两档形同不存在。 */
|
| 1030 |
+
camera->stalls_at_mode = 0;
|
| 1031 |
+
camera->open_failures_at_mode = 0;
|
| 1032 |
+
persist_mode_choice(camera);
|
| 1033 |
+
return 1;
|
| 1034 |
+
}
|
| 1035 |
+
|
| 1036 |
+
static void escalate_to_usb_reset(camera_state_t *camera,
|
| 1037 |
+
uvc_device_handle_t *device) {
|
| 1038 |
+
if (camera->hard_failures < HARD_FAILURES_BEFORE_RESET) {
|
| 1039 |
+
return;
|
| 1040 |
+
}
|
| 1041 |
+
if (camera->usb_resets >= USB_RESET_LIMIT) {
|
| 1042 |
+
return;
|
| 1043 |
+
}
|
| 1044 |
+
camera->usb_resets++;
|
| 1045 |
+
reset_usb_device(camera, device);
|
| 1046 |
+
}
|
| 1047 |
+
|
| 1048 |
+
/* 失败路径的收尾。复位要抢在 uvc_stop_streaming 之前:
|
| 1049 |
+
* 卡死的设备上 uvc_stop_streaming 会一直等它那批永远回不来的等时传输,
|
| 1050 |
+
* 实测能卡满客户端那 2 秒宽限期、被 SIGKILL 打断——进程死在这中间,
|
| 1051 |
+
* 设备就停在激活的 alt setting 上,下一轮启动照样起不来(用户 09:24
|
| 1052 |
+
* 的日志正是断在 startup failed 与 usb reset 之间)。复位会取消在途
|
| 1053 |
+
* 传输,先复位再停流,这条路径才走得完。 */
|
| 1054 |
+
static void teardown_failed_open(camera_state_t *camera,
|
| 1055 |
+
uvc_device_handle_t *device) {
|
| 1056 |
+
escalate_to_usb_reset(camera, device);
|
| 1057 |
+
uvc_stop_streaming(device);
|
| 1058 |
+
uvc_close(device);
|
| 1059 |
+
close_ipc(camera);
|
| 1060 |
+
}
|
| 1061 |
+
|
| 1062 |
+
static int run_camera_once(camera_state_t *camera, uvc_context_t *context) {
|
| 1063 |
+
uvc_device_handle_t *device = NULL;
|
| 1064 |
+
uvc_stream_ctrl_t control;
|
| 1065 |
+
char actual_path[64] = {0};
|
| 1066 |
+
uvc_error_t error;
|
| 1067 |
+
double previous_report = monotonic_seconds();
|
| 1068 |
+
uint64_t previous_received = 0;
|
| 1069 |
+
uint64_t previous_output = 0;
|
| 1070 |
+
uint64_t previous_dropped = 0;
|
| 1071 |
+
uint64_t previous_bad_jpeg = 0;
|
| 1072 |
+
|
| 1073 |
+
pthread_mutex_lock(&camera->mutex);
|
| 1074 |
+
uint64_t initial_received = camera->frames_received;
|
| 1075 |
+
/* 每次重开都要把上报基线对齐到当前累计值。留在 0 会让重开后的第一次
|
| 1076 |
+
* 上报把「进程启动至今的全部帧数」当成本窗口的增量,算出 200+fps 这种
|
| 1077 |
+
* 假速率(纯日志问题,不影响出帧,但会把人带偏)。 */
|
| 1078 |
+
previous_received = camera->frames_received;
|
| 1079 |
+
previous_output = camera->frames_output;
|
| 1080 |
+
previous_dropped = camera->frames_dropped;
|
| 1081 |
+
previous_bad_jpeg = camera->bad_jpeg_frames;
|
| 1082 |
+
camera->stopping = 0;
|
| 1083 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 1084 |
+
error = open_matching_device(context, &camera->config, &device,
|
| 1085 |
+
actual_path, sizeof(actual_path));
|
| 1086 |
+
if (error != UVC_SUCCESS) {
|
| 1087 |
+
fprintf(stderr, "[%s] device %04x:%04x bus=%d port=%s unavailable: %s\n",
|
| 1088 |
+
camera->config.name, camera->config.vid, camera->config.pid,
|
| 1089 |
+
camera->config.usb_bus, camera->config.usb_port_path,
|
| 1090 |
+
uvc_strerror(error));
|
| 1091 |
+
close_ipc(camera);
|
| 1092 |
+
return -1;
|
| 1093 |
+
}
|
| 1094 |
+
/* 在协商之前把 ini 里的档位钉到设备句柄上。libuvc 自己的编译期表只是
|
| 1095 |
+
* 兜底:真正决定用哪一档的是这份 ini,而 ini 里的值来自扫描程序实测的
|
| 1096 |
+
* 端点描述符、或者上一轮学到的结论。 */
|
| 1097 |
+
error = uvc_set_altsetting_override(device, camera->config.forced_altsetting,
|
| 1098 |
+
(unsigned int)camera->config.forced_payload);
|
| 1099 |
+
if (error != UVC_SUCCESS) {
|
| 1100 |
+
fprintf(stderr, "[%s] cannot apply altsetting override alt=%d payload=%d: %s\n",
|
| 1101 |
+
camera->config.name, camera->config.forced_altsetting,
|
| 1102 |
+
camera->config.forced_payload, uvc_strerror(error));
|
| 1103 |
+
teardown_failed_open(camera, device);
|
| 1104 |
+
return -1;
|
| 1105 |
+
}
|
| 1106 |
+
memset(&control, 0, sizeof(control));
|
| 1107 |
+
error = uvc_get_stream_ctrl_format_size(
|
| 1108 |
+
device, &control, UVC_FRAME_FORMAT_MJPEG,
|
| 1109 |
+
camera->config.width, camera->config.height, camera->config.fps);
|
| 1110 |
+
if (error != UVC_SUCCESS ||
|
| 1111 |
+
control.bInterfaceNumber != camera->config.streaming_interface) {
|
| 1112 |
+
fprintf(stderr, "[%s] UVC format/interface negotiation failed: %s interface=%u expected=%d\n",
|
| 1113 |
+
camera->config.name, uvc_strerror(error),
|
| 1114 |
+
control.bInterfaceNumber, camera->config.streaming_interface);
|
| 1115 |
+
teardown_failed_open(camera, device);
|
| 1116 |
+
return -1;
|
| 1117 |
+
}
|
| 1118 |
+
fprintf(stderr,
|
| 1119 |
+
"[%s] vidpid=%04x:%04x bus=%d port=%s interface=%u alt=%d payload=%d "
|
| 1120 |
+
"format=MJPEG size=%dx%d fps=%d socket=%s negotiated_payload=%u\n",
|
| 1121 |
+
camera->config.name, camera->config.vid, camera->config.pid,
|
| 1122 |
+
camera->config.usb_bus, actual_path, control.bInterfaceNumber,
|
| 1123 |
+
camera->config.forced_altsetting, camera->config.forced_payload,
|
| 1124 |
+
camera->config.width, camera->config.height, camera->config.fps,
|
| 1125 |
+
camera->config.socket_path, control.dwMaxPayloadTransferSize);
|
| 1126 |
+
error = uvc_start_streaming(device, &control, frame_callback, camera, 0);
|
| 1127 |
+
if (error != UVC_SUCCESS) {
|
| 1128 |
+
fprintf(stderr, "[%s] uvc_start_streaming failed: %s\n",
|
| 1129 |
+
camera->config.name, uvc_strerror(error));
|
| 1130 |
+
teardown_failed_open(camera, device);
|
| 1131 |
+
return -1;
|
| 1132 |
+
}
|
| 1133 |
+
/* Publish IPC only once MJPG negotiation, STREAMON and a valid frame
|
| 1134 |
+
* have succeeded. Socket existence is a readiness promise to the parent. */
|
| 1135 |
+
int first_frame = 0;
|
| 1136 |
+
int restarting = camera->reconnects > 0;
|
| 1137 |
+
double first_frame_timeout = restarting
|
| 1138 |
+
? RESTART_FIRST_FRAME_TIMEOUT_SECONDS : FIRST_FRAME_TIMEOUT_SECONDS;
|
| 1139 |
+
double deadline = monotonic_seconds() + first_frame_timeout;
|
| 1140 |
+
while (!g_stop && monotonic_seconds() < deadline) {
|
| 1141 |
+
pthread_mutex_lock(&camera->mutex);
|
| 1142 |
+
first_frame = camera->frames_received > initial_received;
|
| 1143 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 1144 |
+
if (first_frame) break;
|
| 1145 |
+
usleep(10000);
|
| 1146 |
+
}
|
| 1147 |
+
if (!first_frame || configure_ipc(camera) != 0) {
|
| 1148 |
+
fprintf(stderr, "[%s] startup failed: no valid MJPG frame or IPC unavailable\n", camera->config.name);
|
| 1149 |
+
teardown_failed_open(camera, device);
|
| 1150 |
+
return -1;
|
| 1151 |
+
}
|
| 1152 |
+
if (pthread_create(&camera->writer_thread, NULL, writer_main, camera) != 0) {
|
| 1153 |
+
fprintf(stderr, "[%s] writer thread creation failed\n", camera->config.name);
|
| 1154 |
+
uvc_stop_streaming(device);
|
| 1155 |
+
uvc_close(device);
|
| 1156 |
+
close_ipc(camera);
|
| 1157 |
+
return -1;
|
| 1158 |
+
}
|
| 1159 |
+
int stalled = 0;
|
| 1160 |
+
double last_progress = monotonic_seconds();
|
| 1161 |
+
double stream_started = last_progress;
|
| 1162 |
+
int clean_run_credited = 0;
|
| 1163 |
+
uint64_t last_received = 0;
|
| 1164 |
+
pthread_mutex_lock(&camera->mutex);
|
| 1165 |
+
last_received = camera->frames_received;
|
| 1166 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 1167 |
+
|
| 1168 |
+
while (!g_stop) {
|
| 1169 |
+
sleep(1);
|
| 1170 |
+
if (g_parent_pid > 1 && getppid() != g_parent_pid) {
|
| 1171 |
+
fprintf(stderr,
|
| 1172 |
+
"[%s] parent %d exited; stopping stream\n",
|
| 1173 |
+
camera->config.name, g_parent_pid);
|
| 1174 |
+
g_stop = 1;
|
| 1175 |
+
break;
|
| 1176 |
+
}
|
| 1177 |
+
double now = monotonic_seconds();
|
| 1178 |
+
if (now - previous_report >= 5.0) {
|
| 1179 |
+
report_stats(camera, now - previous_report,
|
| 1180 |
+
previous_received, previous_output,
|
| 1181 |
+
previous_dropped, previous_bad_jpeg);
|
| 1182 |
+
pthread_mutex_lock(&camera->mutex);
|
| 1183 |
+
previous_received = camera->frames_received;
|
| 1184 |
+
previous_output = camera->frames_output;
|
| 1185 |
+
previous_dropped = camera->frames_dropped;
|
| 1186 |
+
previous_bad_jpeg = camera->bad_jpeg_frames;
|
| 1187 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 1188 |
+
previous_report = now;
|
| 1189 |
+
}
|
| 1190 |
+
/* 停滞看门狗:见 STREAM_STALL_TIMEOUT_SECONDS 的注释。这里不能
|
| 1191 |
+
* 只等 g_stop——流死了 g_stop 永远不会置位,重试循环也就永远
|
| 1192 |
+
* 轮不到,整路相机就此静默报废。 */
|
| 1193 |
+
pthread_mutex_lock(&camera->mutex);
|
| 1194 |
+
uint64_t received_now = camera->frames_received;
|
| 1195 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 1196 |
+
/* 稳定跑够 CLEAN_RUN_SECONDS 就把本档位的停摆计数清零:说明这个档位
|
| 1197 |
+
* 是撑得住的,之前那次停摆是设备自己抽了一下。 */
|
| 1198 |
+
if (!clean_run_credited && now - stream_started >= CLEAN_RUN_SECONDS) {
|
| 1199 |
+
clean_run_credited = 1;
|
| 1200 |
+
camera->stalls_at_mode = 0;
|
| 1201 |
+
}
|
| 1202 |
+
if (received_now != last_received) {
|
| 1203 |
+
last_received = received_now;
|
| 1204 |
+
last_progress = now;
|
| 1205 |
+
} else if (now - last_progress >= STREAM_STALL_TIMEOUT_SECONDS) {
|
| 1206 |
+
pthread_mutex_lock(&camera->mutex);
|
| 1207 |
+
camera->stalls++;
|
| 1208 |
+
pthread_mutex_unlock(&camera->mutex);
|
| 1209 |
+
fprintf(stderr,
|
| 1210 |
+
"[%s] stream stalled: no frame for %.1fs "
|
| 1211 |
+
"(received=%llu); restarting UVC stream\n",
|
| 1212 |
+
camera->config.name, now - last_progress,
|
| 1213 |
+
(unsigned long long)received_now);
|
| 1214 |
+
stalled = 1;
|
| 1215 |
+
break;
|
| 1216 |
+
}
|
| 1217 |
+
}
|
| 1218 |
+
uvc_stop_streaming(device);
|
| 1219 |
+
uvc_close(device);
|
| 1220 |
+
close_ipc(camera);
|
| 1221 |
+
/* 返回 0 会被 camera_main 当成正常收尾而退出重试循环;停摆用 +1 单独
|
| 1222 |
+
* 标记,让 camera_main 知道还要重建 libusb 上下文。 */
|
| 1223 |
+
return stalled ? 1 : 0;
|
| 1224 |
+
}
|
| 1225 |
+
|
| 1226 |
+
static void *camera_main(void *user_ptr) {
|
| 1227 |
+
camera_state_t *camera = (camera_state_t *)user_ptr;
|
| 1228 |
+
uvc_context_t *context = NULL;
|
| 1229 |
+
uvc_error_t error = uvc_init(&context, NULL);
|
| 1230 |
+
if (error != UVC_SUCCESS) {
|
| 1231 |
+
fprintf(stderr, "[%s] uvc_init failed: %s\n", camera->config.name,
|
| 1232 |
+
uvc_strerror(error));
|
| 1233 |
+
return NULL;
|
| 1234 |
+
}
|
| 1235 |
+
while (!g_stop) {
|
| 1236 |
+
if (g_parent_pid > 1 && getppid() != g_parent_pid) {
|
| 1237 |
+
fprintf(stderr,
|
| 1238 |
+
"[SUPERVISION] parent %d exited; stopping service\n",
|
| 1239 |
+
g_parent_pid);
|
| 1240 |
+
g_stop = 1;
|
| 1241 |
+
break;
|
| 1242 |
+
}
|
| 1243 |
+
int rc = run_camera_once(camera, context);
|
| 1244 |
+
if (rc == 0) {
|
| 1245 |
+
break;
|
| 1246 |
+
}
|
| 1247 |
+
camera->reconnects++;
|
| 1248 |
+
if (rc > 0) {
|
| 1249 |
+
/* 停摆那次是开得起来、出过帧的,设备侧没卡死;把连续失败清零,
|
| 1250 |
+
* 免得几次停摆的失败凑够数、误触发 USB 复位去打扰另���两路。 */
|
| 1251 |
+
camera->hard_failures = 0;
|
| 1252 |
+
/* 开得起来 ⇒ 这个档位至少被控制器接受了,清掉开不起来的计数。 */
|
| 1253 |
+
camera->open_failures_at_mode = 0;
|
| 1254 |
+
/* 当前档位连着停够次数就退一档(并落盘)。降档要在重建上下文
|
| 1255 |
+
* 之前生效——重开的流才会用新的 alt/payload。 */
|
| 1256 |
+
camera->stalls_at_mode++;
|
| 1257 |
+
if (camera->stalls_at_mode == STALLS_BEFORE_ALTSETTING_DOWNGRADE) {
|
| 1258 |
+
/* 计数要在降档之前抓下来:maybe_downgrade_altsetting 成功时
|
| 1259 |
+
* 会把它清零,之后再读就只能打出「0 stalls」,正好把最该
|
| 1260 |
+
* 看的那次降级说成没有依据。 */
|
| 1261 |
+
unsigned stalls = camera->stalls_at_mode;
|
| 1262 |
+
int previous_alt = camera->config.forced_altsetting;
|
| 1263 |
+
if (maybe_downgrade_altsetting(camera)) {
|
| 1264 |
+
fprintf(stderr,
|
| 1265 |
+
"[%s] %u stalls at alt=%d; dropping to alt=%d "
|
| 1266 |
+
"payload=%d\n",
|
| 1267 |
+
camera->config.name, stalls, previous_alt,
|
| 1268 |
+
camera->config.forced_altsetting,
|
| 1269 |
+
camera->config.forced_payload);
|
| 1270 |
+
} else {
|
| 1271 |
+
fprintf(stderr,
|
| 1272 |
+
"[%s] %u stalls but alt=%d is already the lowest "
|
| 1273 |
+
"setting for this camera; staying put\n",
|
| 1274 |
+
camera->config.name, stalls,
|
| 1275 |
+
camera->config.forced_altsetting);
|
| 1276 |
+
}
|
| 1277 |
+
}
|
| 1278 |
+
/* 停摆:同一个 libusb 上下文里的设备对象在复位后已经失效,
|
| 1279 |
+
* 用它重开只会拿到一个永远不出帧的死句柄(表现为
|
| 1280 |
+
* 「negotiated OK 但 20 秒一帧没有」)。新进程能恢复、同进程
|
| 1281 |
+
* 重开不能,差别就在这里——重建上下文=拿到与「新进程」等价
|
| 1282 |
+
* 的干净枚举。 */
|
| 1283 |
+
fprintf(stderr, "[%s] rebuilding libusb context after stall\n",
|
| 1284 |
+
camera->config.name);
|
| 1285 |
+
uvc_exit(context);
|
| 1286 |
+
context = NULL;
|
| 1287 |
+
if (uvc_init(&context, NULL) != UVC_SUCCESS) {
|
| 1288 |
+
fprintf(stderr, "[%s] uvc_init failed after stall; "
|
| 1289 |
+
"camera thread exiting\n", camera->config.name);
|
| 1290 |
+
return NULL;
|
| 1291 |
+
}
|
| 1292 |
+
} else {
|
| 1293 |
+
/* 开不起来(rc<0):计数到第 2 次就升级成 USB 复位,见
|
| 1294 |
+
* escalate_to_usb_reset。 */
|
| 1295 |
+
camera->hard_failures++;
|
| 1296 |
+
/* 复位也救不回来时,档位是剩下的嫌疑——可能在 STREAMON 阶段就
|
| 1297 |
+
* 排不下这个等时档,那样流根本不会启动,停摆那条路永远等不到。 */
|
| 1298 |
+
camera->open_failures_at_mode++;
|
| 1299 |
+
if (camera->open_failures_at_mode ==
|
| 1300 |
+
OPEN_FAILURES_BEFORE_ALTSETTING_DOWNGRADE) {
|
| 1301 |
+
/* 和停摆那条路一样,计数要在降档之前抓下来:成功降档会把
|
| 1302 |
+
* 它清零,之后再读就打不出触发这次降级的次数。 */
|
| 1303 |
+
unsigned failed = camera->open_failures_at_mode;
|
| 1304 |
+
int previous_alt = camera->config.forced_altsetting;
|
| 1305 |
+
if (maybe_downgrade_altsetting(camera)) {
|
| 1306 |
+
fprintf(stderr,
|
| 1307 |
+
"[%s] %u failed opens at alt=%d; dropping to "
|
| 1308 |
+
"alt=%d payload=%d\n",
|
| 1309 |
+
camera->config.name, failed, previous_alt,
|
| 1310 |
+
camera->config.forced_altsetting,
|
| 1311 |
+
camera->config.forced_payload);
|
| 1312 |
+
} else {
|
| 1313 |
+
fprintf(stderr,
|
| 1314 |
+
"[%s] %u failed opens but alt=%d is already the "
|
| 1315 |
+
"lowest setting for this camera; staying put\n",
|
| 1316 |
+
camera->config.name, failed,
|
| 1317 |
+
camera->config.forced_altsetting);
|
| 1318 |
+
}
|
| 1319 |
+
}
|
| 1320 |
+
}
|
| 1321 |
+
if (!g_stop) {
|
| 1322 |
+
sleep(1);
|
| 1323 |
+
}
|
| 1324 |
+
}
|
| 1325 |
+
uvc_exit(context);
|
| 1326 |
+
return NULL;
|
| 1327 |
+
}
|
| 1328 |
+
|
| 1329 |
+
static void print_usage(const char *program) {
|
| 1330 |
+
fprintf(stderr,
|
| 1331 |
+
"usage: %s --config FILE [--validate-only] "
|
| 1332 |
+
"[--parent-pid PID]\n",
|
| 1333 |
+
program);
|
| 1334 |
+
}
|
| 1335 |
+
|
| 1336 |
+
/* SIGTERM 之后必须退得掉。客户端(uvc_camera_service.py)只给 2 秒宽限:
|
| 1337 |
+
* terminate() → 等 2 秒 → kill()。超了就是 SIGKILL,进程死在 libusb 调用
|
| 1338 |
+
* 中间、设备停在激活的 alt setting 上,下一轮照样起不来——正是 2026-09-15
|
| 1339 |
+
* 09:24 那次事故的形状。
|
| 1340 |
+
*
|
| 1341 |
+
* 光置 g_stop 不够:相机线程可能正陷在 uvc_open 的同步 libusb 调用里(卡死
|
| 1342 |
+
* 的设备上它不返回),永远走不到循环头那个 g_stop 判断,pthread_join 于是
|
| 1343 |
+
* 永远等下去。实测:生产二进制收到 SIGTERM 后 3 分钟不退,只攥着 DECXIN 的
|
| 1344 |
+
* fd,left/right 都已干净释放,日志停在断流那一刻——连
|
| 1345 |
+
* [SUPERVISION] parent exited 都没打出来。
|
| 1346 |
+
*
|
| 1347 |
+
* 所以 join 必须有界:整体 1.5 秒、单路 0.8 秒(健康时三路是并行收尾的,
|
| 1348 |
+
* 实测全程 0.56 秒,留足余量)。超时就 detach、继续走,main 一返回进程即
|
| 1349 |
+
* 结束——至少把能收的收干净了,跟客户端硬杀不是一回事。
|
| 1350 |
+
*
|
| 1351 |
+
* 注意 join 循环同时就是主运行循环,所以正常运行时不能套这个截止时间,
|
| 1352 |
+
* 只能短超时轮询着等 g_stop(见 main 里那段说明)。 */
|
| 1353 |
+
#define SHUTDOWN_JOIN_TOTAL_MS 1500
|
| 1354 |
+
#define SHUTDOWN_JOIN_PER_CAMERA_MS 800
|
| 1355 |
+
#define SHUTDOWN_JOIN_POLL_MS 200
|
| 1356 |
+
|
| 1357 |
+
/* 距 deadline 还有多少毫秒(≤0 表示已过期)。 */
|
| 1358 |
+
static long ms_until(const struct timespec *deadline) {
|
| 1359 |
+
struct timespec now;
|
| 1360 |
+
clock_gettime(CLOCK_REALTIME, &now);
|
| 1361 |
+
return (long)(deadline->tv_sec - now.tv_sec) * 1000L +
|
| 1362 |
+
(long)(deadline->tv_nsec - now.tv_nsec) / 1000000L;
|
| 1363 |
+
}
|
| 1364 |
+
|
| 1365 |
+
/* 现在起 ms 毫秒之后的时刻。pthread_timedjoin_np 按 CLOCK_REALTIME 算。 */
|
| 1366 |
+
static struct timespec deadline_after_ms(long ms) {
|
| 1367 |
+
struct timespec t;
|
| 1368 |
+
clock_gettime(CLOCK_REALTIME, &t);
|
| 1369 |
+
t.tv_sec += ms / 1000;
|
| 1370 |
+
t.tv_nsec += (ms % 1000) * 1000000L;
|
| 1371 |
+
if (t.tv_nsec >= 1000000000L) {
|
| 1372 |
+
t.tv_sec += 1;
|
| 1373 |
+
t.tv_nsec -= 1000000000L;
|
| 1374 |
+
}
|
| 1375 |
+
return t;
|
| 1376 |
+
}
|
| 1377 |
+
|
| 1378 |
+
int main(int argc, char **argv) {
|
| 1379 |
+
const char *config_path = NULL;
|
| 1380 |
+
int validate_only = 0;
|
| 1381 |
+
camera_config_t configs[MAX_CAMERAS];
|
| 1382 |
+
camera_state_t cameras[MAX_CAMERAS];
|
| 1383 |
+
size_t camera_count = 0;
|
| 1384 |
+
size_t started = 0;
|
| 1385 |
+
struct sigaction action;
|
| 1386 |
+
|
| 1387 |
+
for (int i = 1; i < argc; ++i) {
|
| 1388 |
+
if (strcmp(argv[i], "--config") == 0 && i + 1 < argc) {
|
| 1389 |
+
config_path = argv[++i];
|
| 1390 |
+
} else if (strcmp(argv[i], "--validate-only") == 0) {
|
| 1391 |
+
validate_only = 1;
|
| 1392 |
+
} else if (strcmp(argv[i], "--parent-pid") == 0 &&
|
| 1393 |
+
i + 1 < argc) {
|
| 1394 |
+
if (parse_int(argv[++i], &g_parent_pid) != 0 ||
|
| 1395 |
+
g_parent_pid <= 1) {
|
| 1396 |
+
fprintf(stderr, "invalid --parent-pid\n");
|
| 1397 |
+
return 2;
|
| 1398 |
+
}
|
| 1399 |
+
} else {
|
| 1400 |
+
print_usage(argv[0]);
|
| 1401 |
+
return 2;
|
| 1402 |
+
}
|
| 1403 |
+
}
|
| 1404 |
+
if (config_path == NULL || load_config(config_path, configs,
|
| 1405 |
+
&camera_count) != 0) {
|
| 1406 |
+
print_usage(argv[0]);
|
| 1407 |
+
return 2;
|
| 1408 |
+
}
|
| 1409 |
+
if (validate_only) {
|
| 1410 |
+
for (size_t i = 0; i < camera_count; ++i) {
|
| 1411 |
+
fprintf(stdout,
|
| 1412 |
+
"valid[%zu] name=%s vidpid=%04x:%04x bus=%d port=%s "
|
| 1413 |
+
"interface=%d alt=%d payload=%d mode=%dx%d MJPEG@%d "
|
| 1414 |
+
"socket=%s\n",
|
| 1415 |
+
i, configs[i].name, configs[i].vid, configs[i].pid,
|
| 1416 |
+
configs[i].usb_bus, configs[i].usb_port_path,
|
| 1417 |
+
configs[i].streaming_interface,
|
| 1418 |
+
configs[i].forced_altsetting, configs[i].forced_payload,
|
| 1419 |
+
configs[i].width, configs[i].height, configs[i].fps,
|
| 1420 |
+
configs[i].socket_path);
|
| 1421 |
+
}
|
| 1422 |
+
return 0;
|
| 1423 |
+
}
|
| 1424 |
+
memset(&action, 0, sizeof(action));
|
| 1425 |
+
action.sa_handler = request_stop;
|
| 1426 |
+
sigemptyset(&action.sa_mask);
|
| 1427 |
+
sigaction(SIGINT, &action, NULL);
|
| 1428 |
+
sigaction(SIGTERM, &action, NULL);
|
| 1429 |
+
|
| 1430 |
+
memset(cameras, 0, sizeof(cameras));
|
| 1431 |
+
for (size_t i = 0; i < camera_count; ++i) {
|
| 1432 |
+
cameras[i].config = configs[i];
|
| 1433 |
+
cameras[i].listen_fd = -1;
|
| 1434 |
+
cameras[i].client_fd = -1;
|
| 1435 |
+
pthread_mutex_init(&cameras[i].mutex, NULL);
|
| 1436 |
+
pthread_cond_init(&cameras[i].condition, NULL);
|
| 1437 |
+
if (pthread_create(&cameras[i].thread, NULL, camera_main,
|
| 1438 |
+
&cameras[i]) != 0) {
|
| 1439 |
+
fprintf(stderr, "[%s] capture thread creation failed\n",
|
| 1440 |
+
cameras[i].config.name);
|
| 1441 |
+
g_stop = 1;
|
| 1442 |
+
break;
|
| 1443 |
+
}
|
| 1444 |
+
started++;
|
| 1445 |
+
}
|
| 1446 |
+
/* 这个 join 循环**就是服务的主运行循环**——它在启动时进入、一直跑到
|
| 1447 |
+
* g_stop,不是「收尾时才走的一段」。所以截止时间不能在进循环前就算好
|
| 1448 |
+
* (那样正常运行时也会到点超时、服务自己退出),要等 g_stop 真置位时
|
| 1449 |
+
* 才开始计时。没置位时只用 200ms 短超时轮询,成本可忽略。 */
|
| 1450 |
+
int shutdown_started = 0;
|
| 1451 |
+
struct timespec shutdown_deadline = {0, 0};
|
| 1452 |
+
for (size_t i = 0; i < started; ++i) {
|
| 1453 |
+
int join_rc;
|
| 1454 |
+
for (;;) {
|
| 1455 |
+
long budget = SHUTDOWN_JOIN_POLL_MS;
|
| 1456 |
+
if (g_stop) {
|
| 1457 |
+
if (!shutdown_started) {
|
| 1458 |
+
shutdown_deadline =
|
| 1459 |
+
deadline_after_ms(SHUTDOWN_JOIN_TOTAL_MS);
|
| 1460 |
+
shutdown_started = 1;
|
| 1461 |
+
}
|
| 1462 |
+
long left = ms_until(&shutdown_deadline);
|
| 1463 |
+
budget = left < SHUTDOWN_JOIN_PER_CAMERA_MS
|
| 1464 |
+
? left : SHUTDOWN_JOIN_PER_CAMERA_MS;
|
| 1465 |
+
/* 总预算已用尽:不再等,但还是要问一次——线程可能早就退了
|
| 1466 |
+
* (健康的那几路是并行收尾的),0 超时的 join 会立刻返回。 */
|
| 1467 |
+
if (budget < 0) {
|
| 1468 |
+
budget = 0;
|
| 1469 |
+
}
|
| 1470 |
+
}
|
| 1471 |
+
struct timespec limit = deadline_after_ms(budget);
|
| 1472 |
+
join_rc = pthread_timedjoin_np(cameras[i].thread, NULL, &limit);
|
| 1473 |
+
if (join_rc != ETIMEDOUT) {
|
| 1474 |
+
break;
|
| 1475 |
+
}
|
| 1476 |
+
if (!g_stop) {
|
| 1477 |
+
continue; /* 正常运行中的轮询到期,接着等 */
|
| 1478 |
+
}
|
| 1479 |
+
if (budget == 0) {
|
| 1480 |
+
break; /* 预算已尽,放弃这一路 */
|
| 1481 |
+
}
|
| 1482 |
+
}
|
| 1483 |
+
if (join_rc == ETIMEDOUT) {
|
| 1484 |
+
/* 线程没在预算内退出——多半还陷在设备调用里。不能为它把整个
|
| 1485 |
+
* 进程陪进去,否则客户端 2 秒宽限一到就是 SIGKILL、设备留脏。
|
| 1486 |
+
* detach 后 main 一返回进程即结束,内核会替我们释放设备句柄。
|
| 1487 |
+
* 跳过下面的统计与销毁:那些结构线程可能还在用,销毁它们是
|
| 1488 |
+
* use-after-free。 */
|
| 1489 |
+
fprintf(stderr,
|
| 1490 |
+
"[%s] 收尾超时:线程未在预算内退出(多半卡在设备调用里),"
|
| 1491 |
+
"不再等它\n", cameras[i].config.name);
|
| 1492 |
+
pthread_detach(cameras[i].thread);
|
| 1493 |
+
continue;
|
| 1494 |
+
}
|
| 1495 |
+
pthread_mutex_lock(&cameras[i].mutex);
|
| 1496 |
+
fprintf(stderr, "[%s] received=%llu output=%llu dropped=%llu "
|
| 1497 |
+
"bad_jpeg=%llu bad_replaced=%llu write_errors=%llu "
|
| 1498 |
+
"retries=%llu stalls=%llu usb_resets=%u\n",
|
| 1499 |
+
cameras[i].config.name,
|
| 1500 |
+
(unsigned long long)cameras[i].frames_received,
|
| 1501 |
+
(unsigned long long)cameras[i].frames_output,
|
| 1502 |
+
(unsigned long long)cameras[i].frames_dropped,
|
| 1503 |
+
(unsigned long long)cameras[i].bad_jpeg_frames,
|
| 1504 |
+
(unsigned long long)cameras[i].bad_jpeg_replaced,
|
| 1505 |
+
(unsigned long long)cameras[i].write_errors,
|
| 1506 |
+
(unsigned long long)cameras[i].reconnects,
|
| 1507 |
+
(unsigned long long)cameras[i].stalls,
|
| 1508 |
+
cameras[i].usb_resets);
|
| 1509 |
+
pthread_mutex_unlock(&cameras[i].mutex);
|
| 1510 |
+
pthread_cond_destroy(&cameras[i].condition);
|
| 1511 |
+
pthread_mutex_destroy(&cameras[i].mutex);
|
| 1512 |
+
}
|
| 1513 |
+
return started == camera_count ? 0 : 1;
|
| 1514 |
+
}
|
collector/core/gripper/camera_service_src/tests/test_altsetting_ladder.c
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Deterministic tests for the altsetting ladder. No USB devices involved.
|
| 2 |
+
*
|
| 3 |
+
* The ladder is the thing that lets one binary work both on controllers that
|
| 4 |
+
* can carry DECXIN's high-bandwidth isochronous setting and on those that
|
| 5 |
+
* cannot, so its two failure modes are worth pinning down:
|
| 6 |
+
*
|
| 7 |
+
* - silently accepting a pair the camera cannot do. alt and payload are one
|
| 8 |
+
* unit (alt7's endpoint *is* 1280 B/packet); pairing 7 with 944 is exactly
|
| 9 |
+
* the configuration that took the whole gripper down on 2026-09-15 with
|
| 10 |
+
* "unsupported DECXIN mode" -> rc=2.
|
| 11 |
+
* - silently refusing to step down, which leaves the camera wedged at a
|
| 12 |
+
* setting its controller cannot carry.
|
| 13 |
+
* - stepping down once and then jamming, because the counter that triggered
|
| 14 |
+
* the step is never cleared back below its threshold: the comparison is an
|
| 15 |
+
* equality against the threshold, so a counter left standing on it never
|
| 16 |
+
* fires again and every rung below the second is unreachable.
|
| 17 |
+
*
|
| 18 |
+
* The state-file name is also pinned, because uvc_camera_service.py has to
|
| 19 |
+
* derive the same key in Python to read the result back; the two are only kept
|
| 20 |
+
* in step by this assertion and its Python counterpart.
|
| 21 |
+
*/
|
| 22 |
+
#undef NDEBUG
|
| 23 |
+
#include <assert.h>
|
| 24 |
+
#include <dirent.h>
|
| 25 |
+
#include <sys/stat.h>
|
| 26 |
+
#include <unistd.h>
|
| 27 |
+
|
| 28 |
+
#define main camera_service_main
|
| 29 |
+
#include "../src/camera_service.c"
|
| 30 |
+
#undef main
|
| 31 |
+
|
| 32 |
+
static camera_config_t make_camera(int vid, int pid, int width, int height) {
|
| 33 |
+
camera_config_t config;
|
| 34 |
+
set_defaults(&config);
|
| 35 |
+
snprintf(config.name, sizeof(config.name), "unit_1_test");
|
| 36 |
+
config.vid = vid;
|
| 37 |
+
config.pid = pid;
|
| 38 |
+
config.usb_bus = 1;
|
| 39 |
+
snprintf(config.usb_port_path, sizeof(config.usb_port_path), "2.2.2");
|
| 40 |
+
config.width = width;
|
| 41 |
+
config.height = height;
|
| 42 |
+
config.fps = 30;
|
| 43 |
+
snprintf(config.format, sizeof(config.format), "MJPEG");
|
| 44 |
+
snprintf(config.socket_path, sizeof(config.socket_path), "/tmp/x.sock");
|
| 45 |
+
snprintf(config.usb_serial, sizeof(config.usb_serial), "DECXIN-01");
|
| 46 |
+
snprintf(config.usb_controller, sizeof(config.usb_controller),
|
| 47 |
+
"0000:74:00.4");
|
| 48 |
+
return config;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
static camera_config_t make_decxin(void) {
|
| 52 |
+
camera_config_t config = make_camera(0x1bcf, 0x2d4f, 1280, 960);
|
| 53 |
+
config.forced_altsetting = 7;
|
| 54 |
+
config.forced_payload = 1280;
|
| 55 |
+
return config;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
static void test_accepts_every_rung_and_nothing_else(void) {
|
| 59 |
+
camera_config_t config = make_decxin();
|
| 60 |
+
|
| 61 |
+
assert(validate_config(&config, 0) == 0); /* alt7/1280, the preferred rung */
|
| 62 |
+
config.forced_altsetting = 6;
|
| 63 |
+
config.forced_payload = 944;
|
| 64 |
+
assert(validate_config(&config, 0) == 0); /* alt6/944, the fallback rung */
|
| 65 |
+
|
| 66 |
+
/* Alt and payload are a matched pair, so a mixed pair must be refused even
|
| 67 |
+
* though both numbers appear in the table on their own. */
|
| 68 |
+
config.forced_altsetting = 7;
|
| 69 |
+
config.forced_payload = 944;
|
| 70 |
+
assert(validate_config(&config, 0) == -1);
|
| 71 |
+
config.forced_altsetting = 6;
|
| 72 |
+
config.forced_payload = 1280;
|
| 73 |
+
assert(validate_config(&config, 0) == -1);
|
| 74 |
+
/* A pair nobody has measured must not be accepted on faith either. */
|
| 75 |
+
config.forced_altsetting = 5;
|
| 76 |
+
config.forced_payload = 800;
|
| 77 |
+
assert(validate_config(&config, 0) == -1);
|
| 78 |
+
|
| 79 |
+
config = make_camera(0x0c45, 0x636f, 640, 480);
|
| 80 |
+
config.forced_altsetting = 3;
|
| 81 |
+
config.forced_payload = 800;
|
| 82 |
+
assert(validate_config(&config, 0) == 0);
|
| 83 |
+
config.forced_altsetting = 4; /* the pre-2026-08-28 1600 B setting */
|
| 84 |
+
config.forced_payload = 1600;
|
| 85 |
+
assert(validate_config(&config, 0) == -1);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
static void test_steps_down_one_rung_and_then_stops(void) {
|
| 89 |
+
camera_state_t camera;
|
| 90 |
+
memset(&camera, 0, sizeof(camera));
|
| 91 |
+
camera.config = make_decxin();
|
| 92 |
+
|
| 93 |
+
assert(mode_index_for(&camera.config) == 0);
|
| 94 |
+
/* Both counters are driven to non-zero first: stepping down has to clear
|
| 95 |
+
* *each* of them. The thresholds are tested with ==, so a counter left
|
| 96 |
+
* standing at the threshold never fires again and that whole trigger path
|
| 97 |
+
* gets one step and then jams -- the rungs below it might as well not
|
| 98 |
+
* exist. Stalls and failed opens are separate paths and each clears its
|
| 99 |
+
* own; a downgrade is a fresh start for both. */
|
| 100 |
+
camera.stalls_at_mode = 99;
|
| 101 |
+
camera.open_failures_at_mode = 99;
|
| 102 |
+
assert(maybe_downgrade_altsetting(&camera) == 1);
|
| 103 |
+
assert(camera.config.forced_altsetting == 6);
|
| 104 |
+
assert(camera.config.forced_payload == 944);
|
| 105 |
+
assert(mode_index_for(&camera.config) == 1);
|
| 106 |
+
assert(camera.stalls_at_mode == 0);
|
| 107 |
+
assert(camera.open_failures_at_mode == 0);
|
| 108 |
+
|
| 109 |
+
/* alt6 is the last rung: a further stall must not invent a worse one. */
|
| 110 |
+
assert(maybe_downgrade_altsetting(&camera) == 0);
|
| 111 |
+
assert(camera.config.forced_altsetting == 6);
|
| 112 |
+
assert(camera.config.forced_payload == 944);
|
| 113 |
+
|
| 114 |
+
/* Sightac has a single rung, so it never steps anywhere. */
|
| 115 |
+
camera.config = make_camera(0x0c45, 0x636f, 640, 480);
|
| 116 |
+
camera.config.forced_altsetting = 3;
|
| 117 |
+
camera.config.forced_payload = 800;
|
| 118 |
+
assert(maybe_downgrade_altsetting(&camera) == 0);
|
| 119 |
+
assert(camera.config.forced_altsetting == 3);
|
| 120 |
+
|
| 121 |
+
/* A pair that is in no table at all must not be walked off the end. */
|
| 122 |
+
camera.config = make_decxin();
|
| 123 |
+
camera.config.forced_payload = 944; /* 7/944: refused by validate_config */
|
| 124 |
+
assert(mode_index_for(&camera.config) == 2);
|
| 125 |
+
assert(maybe_downgrade_altsetting(&camera) == 0);
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
static void test_downgrade_is_remembered_under_a_stable_key(void) {
|
| 129 |
+
char parent[] = "/tmp/ksq-ladder-test-XXXXXX";
|
| 130 |
+
char state_dir[PATH_MAX];
|
| 131 |
+
char expected[PATH_MAX];
|
| 132 |
+
char contents[128];
|
| 133 |
+
camera_state_t camera;
|
| 134 |
+
FILE *file;
|
| 135 |
+
size_t read_bytes;
|
| 136 |
+
|
| 137 |
+
assert(mkdtemp(parent) != NULL);
|
| 138 |
+
/* A subdirectory that does not exist yet: persist_mode_choice creates it. */
|
| 139 |
+
snprintf(state_dir, sizeof(state_dir), "%s/camera_modes", parent);
|
| 140 |
+
memset(&camera, 0, sizeof(camera));
|
| 141 |
+
camera.config = make_decxin();
|
| 142 |
+
snprintf(camera.config.state_dir, sizeof(camera.config.state_dir), "%s",
|
| 143 |
+
state_dir);
|
| 144 |
+
|
| 145 |
+
assert(maybe_downgrade_altsetting(&camera) == 1);
|
| 146 |
+
|
| 147 |
+
/* Key = vid_pid_controller_serial with everything outside [A-Za-z0-9-]
|
| 148 |
+
* replaced by '_'. uvc_camera_service._mode_state_key must produce this
|
| 149 |
+
* exact string for the learned value to be read back. */
|
| 150 |
+
snprintf(expected, sizeof(expected), "%s/1bcf_2d4f_0000_74_00_4_DECXIN-01.mode",
|
| 151 |
+
state_dir);
|
| 152 |
+
file = fopen(expected, "r");
|
| 153 |
+
assert(file != NULL);
|
| 154 |
+
read_bytes = fread(contents, 1, sizeof(contents) - 1, file);
|
| 155 |
+
contents[read_bytes] = '\0';
|
| 156 |
+
fclose(file);
|
| 157 |
+
assert(strcmp(contents, "altsetting=6\npayload=944\n") == 0);
|
| 158 |
+
|
| 159 |
+
/* No temp file may be left behind next to the result. */
|
| 160 |
+
{
|
| 161 |
+
DIR *dir = opendir(state_dir);
|
| 162 |
+
struct dirent *entry;
|
| 163 |
+
int seen = 0;
|
| 164 |
+
assert(dir != NULL);
|
| 165 |
+
while ((entry = readdir(dir)) != NULL) {
|
| 166 |
+
if (entry->d_name[0] == '.') {
|
| 167 |
+
continue;
|
| 168 |
+
}
|
| 169 |
+
++seen;
|
| 170 |
+
assert(strstr(entry->d_name, ".tmp.") == NULL);
|
| 171 |
+
}
|
| 172 |
+
closedir(dir);
|
| 173 |
+
assert(seen == 1);
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
unlink(expected);
|
| 177 |
+
rmdir(state_dir);
|
| 178 |
+
rmdir(parent);
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
static void test_missing_state_dir_is_not_fatal(void) {
|
| 182 |
+
camera_state_t camera;
|
| 183 |
+
memset(&camera, 0, sizeof(camera));
|
| 184 |
+
camera.config = make_decxin();
|
| 185 |
+
/* Empty state_dir means "do not remember anything" and must still step
|
| 186 |
+
* down in memory for the rest of this session. */
|
| 187 |
+
assert(camera.config.state_dir[0] == '\0');
|
| 188 |
+
assert(maybe_downgrade_altsetting(&camera) == 1);
|
| 189 |
+
assert(camera.config.forced_altsetting == 6);
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
int main(void) {
|
| 193 |
+
/* validate_config reports refusals on stderr; that is the production
|
| 194 |
+
* behaviour and is expected here, so keep the test output readable. */
|
| 195 |
+
freopen("/dev/null", "w", stderr);
|
| 196 |
+
test_accepts_every_rung_and_nothing_else();
|
| 197 |
+
test_steps_down_one_rung_and_then_stops();
|
| 198 |
+
test_downgrade_is_remembered_under_a_stable_key();
|
| 199 |
+
test_missing_state_dir_is_not_fatal();
|
| 200 |
+
printf("altsetting ladder: all assertions passed\n");
|
| 201 |
+
return 0;
|
| 202 |
+
}
|
collector/core/gripper/camera_service_src/tests/test_frame_integrity.c
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Test the actual callback and queue without opening USB devices. */
|
| 2 |
+
#undef NDEBUG
|
| 3 |
+
#include <assert.h>
|
| 4 |
+
#define main camera_service_main
|
| 5 |
+
#include "../src/camera_service.c"
|
| 6 |
+
#undef main
|
| 7 |
+
|
| 8 |
+
int main(void) {
|
| 9 |
+
camera_state_t camera = {0};
|
| 10 |
+
uint8_t good[] = {0xff, 0xd8, 0x01, 0x02, 0xff, 0xd9};
|
| 11 |
+
uint8_t short_frame[] = {0xff};
|
| 12 |
+
uvc_frame_t frame = {0};
|
| 13 |
+
pthread_mutex_init(&camera.mutex, NULL);
|
| 14 |
+
pthread_cond_init(&camera.condition, NULL);
|
| 15 |
+
frame.frame_format = UVC_FRAME_FORMAT_MJPEG;
|
| 16 |
+
frame.data = good;
|
| 17 |
+
frame.data_bytes = sizeof(good);
|
| 18 |
+
frame.sequence = 1;
|
| 19 |
+
frame.capture_time_finished.tv_sec = 2;
|
| 20 |
+
enqueue_frame(&frame, &camera);
|
| 21 |
+
assert(camera.count == 1);
|
| 22 |
+
assert(camera.queue[0].sequence == 1);
|
| 23 |
+
assert(camera.queue[0].capture_timestamp_ns == 2000000000ull);
|
| 24 |
+
frame.data = short_frame;
|
| 25 |
+
frame.data_bytes = sizeof(short_frame);
|
| 26 |
+
frame.sequence = 2;
|
| 27 |
+
frame.capture_time_finished.tv_sec = 3;
|
| 28 |
+
enqueue_frame(&frame, &camera);
|
| 29 |
+
assert(camera.count == 1); /* No old image under a new timestamp. */
|
| 30 |
+
assert(camera.bad_jpeg_frames == 1);
|
| 31 |
+
assert(camera.frames_dropped == 1);
|
| 32 |
+
assert(camera.bad_jpeg_replaced == 0);
|
| 33 |
+
assert(camera.queue[0].capture_timestamp_ns == 2000000000ull);
|
| 34 |
+
frame.data = good;
|
| 35 |
+
frame.data_bytes = sizeof(good);
|
| 36 |
+
frame.sequence = 3;
|
| 37 |
+
enqueue_frame(&frame, &camera);
|
| 38 |
+
assert(camera.count == 2);
|
| 39 |
+
assert(camera.queue[1].sequence == 3);
|
| 40 |
+
assert(camera.queue[1].capture_timestamp_ns == 3000000000ull);
|
| 41 |
+
for (size_t i = 0; i < FRAME_QUEUE_DEPTH; ++i) free(camera.queue[i].data);
|
| 42 |
+
pthread_mutex_destroy(&camera.mutex);
|
| 43 |
+
pthread_cond_destroy(&camera.condition);
|
| 44 |
+
return 0;
|
| 45 |
+
}
|
collector/core/gripper/camera_service_src/third_party/libuvc/CMakeLists.txt
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.1)
|
| 2 |
+
project(libuvc
|
| 3 |
+
VERSION 0.0.7
|
| 4 |
+
LANGUAGES C
|
| 5 |
+
)
|
| 6 |
+
|
| 7 |
+
# Additional search scripts path for libusb-1.0, libjpeg, OpenCV
|
| 8 |
+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
|
| 9 |
+
|
| 10 |
+
# Xcode and Visual Studio do not using CMAKE_BUILD_TYPE cache variable
|
| 11 |
+
# so we use Release build type only with single configuration generators.
|
| 12 |
+
if (NOT CMAKE_CONFIGURATION_TYPES)
|
| 13 |
+
if(NOT CMAKE_BUILD_TYPE)
|
| 14 |
+
message(STATUS "No build type selected, default to Release")
|
| 15 |
+
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
|
| 16 |
+
endif()
|
| 17 |
+
endif()
|
| 18 |
+
|
| 19 |
+
if(NOT CMAKE_BUILD_TARGET)
|
| 20 |
+
message(STATUS "No target type selected, default to both shared and static library")
|
| 21 |
+
set(CMAKE_BUILD_TARGET "Both" CACHE STRING "" FORCE)
|
| 22 |
+
endif()
|
| 23 |
+
|
| 24 |
+
option(BUILD_EXAMPLE "Build example program" ON)
|
| 25 |
+
option(BUILD_TEST "Build test program" OFF)
|
| 26 |
+
option(ENABLE_UVC_DEBUGGING "Enable UVC debugging" OFF)
|
| 27 |
+
|
| 28 |
+
set(libuvc_DESCRIPTION "A cross-platform library for USB video devices")
|
| 29 |
+
set(libuvc_URL "https://github.com/libuvc/libuvc")
|
| 30 |
+
|
| 31 |
+
set(SOURCES
|
| 32 |
+
src/ctrl.c
|
| 33 |
+
src/ctrl-gen.c
|
| 34 |
+
src/device.c
|
| 35 |
+
src/diag.c
|
| 36 |
+
src/frame.c
|
| 37 |
+
src/init.c
|
| 38 |
+
src/stream.c
|
| 39 |
+
src/misc.c
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
find_package(LibUSB)
|
| 43 |
+
|
| 44 |
+
# JpegPkg name to differ from shipped with CMake
|
| 45 |
+
find_package(JpegPkg QUIET)
|
| 46 |
+
if(JPEG_FOUND)
|
| 47 |
+
message(STATUS "Building libuvc with JPEG support.")
|
| 48 |
+
set(LIBUVC_HAS_JPEG TRUE)
|
| 49 |
+
list(APPEND SOURCES src/frame-mjpeg.c)
|
| 50 |
+
else()
|
| 51 |
+
message(WARNING "JPEG not found. libuvc will not support JPEG decoding.")
|
| 52 |
+
endif()
|
| 53 |
+
|
| 54 |
+
if((UNIX AND NOT APPLE) OR MINGW OR WIN32)
|
| 55 |
+
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
| 56 |
+
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
| 57 |
+
find_package(Threads REQUIRED)
|
| 58 |
+
set(threads Threads::Threads)
|
| 59 |
+
endif()
|
| 60 |
+
|
| 61 |
+
if(${CMAKE_BUILD_TARGET} MATCHES "Shared")
|
| 62 |
+
set(BUILD_UVC_SHARED TRUE)
|
| 63 |
+
elseif(${CMAKE_BUILD_TARGET} MATCHES "Static")
|
| 64 |
+
set(BUILD_UVC_STATIC TRUE)
|
| 65 |
+
elseif(${CMAKE_BUILD_TARGET} MATCHES "Both")
|
| 66 |
+
set(BUILD_UVC_SHARED TRUE)
|
| 67 |
+
set(BUILD_UVC_STATIC TRUE)
|
| 68 |
+
else()
|
| 69 |
+
message(FATAL_ERROR "Invalid build type ${CMAKE_BUILD_TARGET}" )
|
| 70 |
+
endif()
|
| 71 |
+
|
| 72 |
+
if(BUILD_UVC_SHARED)
|
| 73 |
+
add_library(uvc SHARED ${SOURCES})
|
| 74 |
+
set_target_properties(uvc PROPERTIES
|
| 75 |
+
VERSION ${libuvc_VERSION}
|
| 76 |
+
SOVERSION ${libuvc_VERSION_MAJOR}
|
| 77 |
+
# Exported name of target within namespace LibUVC
|
| 78 |
+
EXPORT_NAME UVCShared
|
| 79 |
+
)
|
| 80 |
+
list(APPEND UVC_TARGETS uvc)
|
| 81 |
+
if(NOT LibUVC_STATIC)
|
| 82 |
+
add_library(LibUVC::UVC ALIAS uvc)
|
| 83 |
+
endif()
|
| 84 |
+
# Aliases defined here available only if project included
|
| 85 |
+
# via addsubdirectory
|
| 86 |
+
add_library(LibUVC::UVCShared ALIAS uvc)
|
| 87 |
+
endif()
|
| 88 |
+
|
| 89 |
+
if(BUILD_UVC_STATIC)
|
| 90 |
+
add_library(uvc_static STATIC ${SOURCES})
|
| 91 |
+
set_target_properties(uvc_static PROPERTIES
|
| 92 |
+
OUTPUT_NAME uvc
|
| 93 |
+
# Exported name of target within namespace LibUVC
|
| 94 |
+
EXPORT_NAME UVCStatic
|
| 95 |
+
)
|
| 96 |
+
list(APPEND UVC_TARGETS uvc_static)
|
| 97 |
+
add_library(LibUVC::UVCStatic ALIAS uvc_static)
|
| 98 |
+
if(LibUVC_STATIC)
|
| 99 |
+
add_library(LibUVC::UVC ALIAS uvc_static)
|
| 100 |
+
endif()
|
| 101 |
+
endif()
|
| 102 |
+
|
| 103 |
+
configure_file(include/libuvc/libuvc_config.h.in
|
| 104 |
+
include/libuvc/libuvc_config.h
|
| 105 |
+
@ONLY
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
foreach(target_name IN LISTS UVC_TARGETS)
|
| 109 |
+
target_include_directories(${target_name}
|
| 110 |
+
PUBLIC
|
| 111 |
+
# Different paths for includes for build and install phase supported
|
| 112 |
+
# via INSTALL_INTERFACE and BUILD_INTERFACE generator expressions.
|
| 113 |
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
|
| 114 |
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
|
| 115 |
+
$<INSTALL_INTERFACE:include>
|
| 116 |
+
)
|
| 117 |
+
target_link_libraries(${target_name}
|
| 118 |
+
# libusb-1.0 used internally so we link to it privately.
|
| 119 |
+
PRIVATE LibUSB::LibUSB ${threads}
|
| 120 |
+
)
|
| 121 |
+
if(JPEG_FOUND)
|
| 122 |
+
target_link_libraries(${target_name}
|
| 123 |
+
PRIVATE JPEG::JPEG
|
| 124 |
+
)
|
| 125 |
+
endif()
|
| 126 |
+
set_target_properties(${target_name} PROPERTIES
|
| 127 |
+
PUBLIC_HEADER "include/libuvc/libuvc.h;${CMAKE_CURRENT_BINARY_DIR}/include/libuvc/libuvc_config.h"
|
| 128 |
+
)
|
| 129 |
+
if(ENABLE_UVC_DEBUGGING)
|
| 130 |
+
target_compile_definitions(${target_name}
|
| 131 |
+
PRIVATE
|
| 132 |
+
UVC_DEBUGGING
|
| 133 |
+
)
|
| 134 |
+
find_library(
|
| 135 |
+
log-lib
|
| 136 |
+
log
|
| 137 |
+
)
|
| 138 |
+
target_link_libraries(
|
| 139 |
+
${target_name}
|
| 140 |
+
PRIVATE
|
| 141 |
+
${log-lib}
|
| 142 |
+
)
|
| 143 |
+
endif()
|
| 144 |
+
endforeach()
|
| 145 |
+
|
| 146 |
+
if(BUILD_EXAMPLE)
|
| 147 |
+
add_executable(example src/example.c)
|
| 148 |
+
find_package(Threads)
|
| 149 |
+
target_link_libraries(example
|
| 150 |
+
PRIVATE
|
| 151 |
+
LibUVC::UVC
|
| 152 |
+
Threads::Threads
|
| 153 |
+
)
|
| 154 |
+
endif()
|
| 155 |
+
|
| 156 |
+
if(BUILD_TEST)
|
| 157 |
+
# OpenCV defines targets with transitive dependencies not with namespaces but using opencv_ prefix.
|
| 158 |
+
# This targets provide necessary include directories and linked flags.
|
| 159 |
+
find_package(OpenCVPkg REQUIRED
|
| 160 |
+
COMPONENTS
|
| 161 |
+
opencv_core
|
| 162 |
+
opencv_highgui
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
add_executable(uvc_test src/test.c)
|
| 166 |
+
target_link_libraries(uvc_test
|
| 167 |
+
PRIVATE
|
| 168 |
+
LibUVC::UVC
|
| 169 |
+
opencv_core
|
| 170 |
+
opencv_highgui
|
| 171 |
+
)
|
| 172 |
+
endif()
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
include(GNUInstallDirs)
|
| 176 |
+
set(CMAKE_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/libuvc)
|
| 177 |
+
|
| 178 |
+
install(
|
| 179 |
+
TARGETS ${UVC_TARGETS}
|
| 180 |
+
EXPORT libuvcTargets
|
| 181 |
+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
| 182 |
+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
| 183 |
+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
| 184 |
+
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
| 185 |
+
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libuvc
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
install(EXPORT libuvcTargets
|
| 189 |
+
FILE libuvcTargets.cmake
|
| 190 |
+
NAMESPACE LibUVC::
|
| 191 |
+
DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
install(FILES
|
| 195 |
+
cmake/FindLibUSB.cmake
|
| 196 |
+
cmake/FindJpegPkg.cmake
|
| 197 |
+
DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
include(CMakePackageConfigHelpers)
|
| 201 |
+
write_basic_package_version_file(libuvcConfigVersion.cmake
|
| 202 |
+
COMPATIBILITY AnyNewerVersion
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
install(FILES
|
| 206 |
+
${CMAKE_CURRENT_BINARY_DIR}/libuvcConfigVersion.cmake
|
| 207 |
+
DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
if(JPEG_FOUND)
|
| 211 |
+
# If we have used JPEG library we need to
|
| 212 |
+
# add linker flag for it in config file for pkgconfig
|
| 213 |
+
set(PKGCONFIG_JPEG_LDFLAG "-ljpeg")
|
| 214 |
+
endif()
|
| 215 |
+
|
| 216 |
+
configure_file(libuvc.pc.in
|
| 217 |
+
${PROJECT_BINARY_DIR}/libuvc.pc
|
| 218 |
+
@ONLY
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
install(FILES
|
| 222 |
+
${CMAKE_CURRENT_BINARY_DIR}/libuvc.pc
|
| 223 |
+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
install(FILES libuvcConfig.cmake
|
| 227 |
+
DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/LICENSE.txt
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Software License Agreement (BSD License)
|
| 2 |
+
|
| 3 |
+
Copyright (C) 2010-2015 Ken Tossell
|
| 4 |
+
All rights reserved.
|
| 5 |
+
|
| 6 |
+
Redistribution and use in source and binary forms, with or without
|
| 7 |
+
modification, are permitted provided that the following conditions
|
| 8 |
+
are met:
|
| 9 |
+
|
| 10 |
+
* Redistributions of source code must retain the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer.
|
| 12 |
+
* Redistributions in binary form must reproduce the above
|
| 13 |
+
copyright notice, this list of conditions and the following
|
| 14 |
+
disclaimer in the documentation and/or other materials provided
|
| 15 |
+
with the distribution.
|
| 16 |
+
* Neither the name of the author nor other contributors may be
|
| 17 |
+
used to endorse or promote products derived from this software
|
| 18 |
+
without specific prior written permission.
|
| 19 |
+
|
| 20 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 21 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 22 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 23 |
+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 24 |
+
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 25 |
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 26 |
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 27 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 28 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 29 |
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 30 |
+
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 31 |
+
POSSIBILITY OF SUCH DAMAGE.
|
collector/core/gripper/camera_service_src/third_party/libuvc/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
`libuvc` is a cross-platform library for USB video devices, built atop `libusb`.
|
| 2 |
+
It enables fine-grained control over USB video devices exporting the standard USB Video Class
|
| 3 |
+
(UVC) interface, enabling developers to write drivers for previously unsupported devices,
|
| 4 |
+
or just access UVC devices in a generic fashion.
|
| 5 |
+
|
| 6 |
+
## Getting and Building libuvc
|
| 7 |
+
|
| 8 |
+
Prerequisites: You will need `libusb` and [CMake](http://www.cmake.org/) installed.
|
| 9 |
+
|
| 10 |
+
To build, you can just run these shell commands:
|
| 11 |
+
|
| 12 |
+
git clone https://github.com/libuvc/libuvc
|
| 13 |
+
cd libuvc
|
| 14 |
+
mkdir build
|
| 15 |
+
cd build
|
| 16 |
+
cmake ..
|
| 17 |
+
make && sudo make install
|
| 18 |
+
|
| 19 |
+
and you're set! If you want to change the build configuration, you can edit `CMakeCache.txt`
|
| 20 |
+
in the build directory, or use a CMake GUI to make the desired changes.
|
| 21 |
+
|
| 22 |
+
There is also `BUILD_EXAMPLE` and `BUILD_TEST` options to enable the compilation of `example` and `uvc_test` programs. To use them, replace the `cmake ..` command above with `cmake .. -DBUILD_TEST=ON -DBUILD_EXAMPLE=ON`.
|
| 23 |
+
Then you can start them with `./example` and `./uvc_test` respectively. Note that you need OpenCV to build the later (for displaying image).
|
| 24 |
+
|
| 25 |
+
## Developing with libuvc
|
| 26 |
+
|
| 27 |
+
The documentation for `libuvc` can currently be found at https://libuvc.github.io/.
|
| 28 |
+
|
| 29 |
+
Happy hacking!
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/isight_imac.txt
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Bus 001 Device 007: ID 05ac:8501 Apple, Inc. Built-in iSight [Micron]
|
| 3 |
+
Device Descriptor:
|
| 4 |
+
bLength 18
|
| 5 |
+
bDescriptorType 1
|
| 6 |
+
bcdUSB 2.00
|
| 7 |
+
bDeviceClass 239 Miscellaneous Device
|
| 8 |
+
bDeviceSubClass 2 ?
|
| 9 |
+
bDeviceProtocol 1 Interface Association
|
| 10 |
+
bMaxPacketSize0 64
|
| 11 |
+
idVendor 0x05ac Apple, Inc.
|
| 12 |
+
idProduct 0x8501 Built-in iSight [Micron]
|
| 13 |
+
bcdDevice 1.89
|
| 14 |
+
iManufacturer 1 Micron
|
| 15 |
+
iProduct 2 Built-in iSight
|
| 16 |
+
iSerial 0
|
| 17 |
+
bNumConfigurations 1
|
| 18 |
+
Configuration Descriptor:
|
| 19 |
+
bLength 9
|
| 20 |
+
bDescriptorType 2
|
| 21 |
+
wTotalLength 267
|
| 22 |
+
bNumInterfaces 2
|
| 23 |
+
bConfigurationValue 1
|
| 24 |
+
iConfiguration 0
|
| 25 |
+
bmAttributes 0x80
|
| 26 |
+
(Bus Powered)
|
| 27 |
+
MaxPower 100mA
|
| 28 |
+
Interface Association:
|
| 29 |
+
bLength 8
|
| 30 |
+
bDescriptorType 11
|
| 31 |
+
bFirstInterface 0
|
| 32 |
+
bInterfaceCount 2
|
| 33 |
+
bFunctionClass 14 Video
|
| 34 |
+
bFunctionSubClass 3 Video Interface Collection
|
| 35 |
+
bFunctionProtocol 0
|
| 36 |
+
iFunction 0
|
| 37 |
+
Interface Descriptor:
|
| 38 |
+
bLength 9
|
| 39 |
+
bDescriptorType 4
|
| 40 |
+
bInterfaceNumber 0
|
| 41 |
+
bAlternateSetting 0
|
| 42 |
+
bNumEndpoints 1
|
| 43 |
+
bInterfaceClass 14 Video
|
| 44 |
+
bInterfaceSubClass 1 Video Control
|
| 45 |
+
bInterfaceProtocol 0
|
| 46 |
+
iInterface 0
|
| 47 |
+
VideoControl Interface Descriptor:
|
| 48 |
+
bLength 13
|
| 49 |
+
bDescriptorType 36
|
| 50 |
+
bDescriptorSubtype 1 (HEADER)
|
| 51 |
+
bcdUVC 1.00
|
| 52 |
+
wTotalLength 49
|
| 53 |
+
dwClockFrequency 13.500000MHz
|
| 54 |
+
bInCollection 1
|
| 55 |
+
baInterfaceNr( 0) 1
|
| 56 |
+
VideoControl Interface Descriptor:
|
| 57 |
+
bLength 16
|
| 58 |
+
bDescriptorType 36
|
| 59 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 60 |
+
bTerminalID 1
|
| 61 |
+
wTerminalType 0x0201 Camera Sensor
|
| 62 |
+
bAssocTerminal 0
|
| 63 |
+
iTerminal 0
|
| 64 |
+
wObjectiveFocalLengthMin 0
|
| 65 |
+
wObjectiveFocalLengthMax 0
|
| 66 |
+
wOcularFocalLength 0
|
| 67 |
+
bControlSize 1
|
| 68 |
+
bmControls 0x00000000
|
| 69 |
+
VideoControl Interface Descriptor:
|
| 70 |
+
bLength 11
|
| 71 |
+
bDescriptorType 36
|
| 72 |
+
bDescriptorSubtype 5 (PROCESSING_UNIT)
|
| 73 |
+
Warning: Descriptor too short
|
| 74 |
+
bUnitID 2
|
| 75 |
+
bSourceID 1
|
| 76 |
+
wMaxMultiplier 0
|
| 77 |
+
bControlSize 2
|
| 78 |
+
bmControls 0x00000039
|
| 79 |
+
Brightness
|
| 80 |
+
Saturation
|
| 81 |
+
Sharpness
|
| 82 |
+
Gamma
|
| 83 |
+
iProcessing 0
|
| 84 |
+
bmVideoStandards 0x 9
|
| 85 |
+
None
|
| 86 |
+
SECAM - 625/50
|
| 87 |
+
VideoControl Interface Descriptor:
|
| 88 |
+
bLength 9
|
| 89 |
+
bDescriptorType 36
|
| 90 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 91 |
+
bTerminalID 3
|
| 92 |
+
wTerminalType 0x0101 USB Streaming
|
| 93 |
+
bAssocTerminal 0
|
| 94 |
+
bSourceID 2
|
| 95 |
+
iTerminal 0
|
| 96 |
+
Endpoint Descriptor:
|
| 97 |
+
bLength 7
|
| 98 |
+
bDescriptorType 5
|
| 99 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 100 |
+
bmAttributes 3
|
| 101 |
+
Transfer Type Interrupt
|
| 102 |
+
Synch Type None
|
| 103 |
+
Usage Type Data
|
| 104 |
+
wMaxPacketSize 0x0008 1x 8 bytes
|
| 105 |
+
bInterval 10
|
| 106 |
+
Interface Descriptor:
|
| 107 |
+
bLength 9
|
| 108 |
+
bDescriptorType 4
|
| 109 |
+
bInterfaceNumber 1
|
| 110 |
+
bAlternateSetting 0
|
| 111 |
+
bNumEndpoints 0
|
| 112 |
+
bInterfaceClass 14 Video
|
| 113 |
+
bInterfaceSubClass 2 Video Streaming
|
| 114 |
+
bInterfaceProtocol 0
|
| 115 |
+
iInterface 0
|
| 116 |
+
VideoStreaming Interface Descriptor:
|
| 117 |
+
bLength 14
|
| 118 |
+
bDescriptorType 36
|
| 119 |
+
bDescriptorSubtype 1 (INPUT_HEADER)
|
| 120 |
+
bNumFormats 1
|
| 121 |
+
wTotalLength 155
|
| 122 |
+
bEndPointAddress 130
|
| 123 |
+
bmInfo 0
|
| 124 |
+
bTerminalLink 3
|
| 125 |
+
bStillCaptureMethod 0
|
| 126 |
+
bTriggerSupport 0
|
| 127 |
+
bTriggerUsage 0
|
| 128 |
+
bControlSize 1
|
| 129 |
+
bmaControls( 0) 27
|
| 130 |
+
VideoStreaming Interface Descriptor:
|
| 131 |
+
bLength 27
|
| 132 |
+
bDescriptorType 36
|
| 133 |
+
bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)
|
| 134 |
+
bFormatIndex 1
|
| 135 |
+
bNumFrameDescriptors 3
|
| 136 |
+
guidFormat {55595659-0000-1000-8000-00aa00389b71}
|
| 137 |
+
bBitsPerPixel 16
|
| 138 |
+
bDefaultFrameIndex 1
|
| 139 |
+
bAspectRatioX 0
|
| 140 |
+
bAspectRatioY 0
|
| 141 |
+
bmInterlaceFlags 0x00
|
| 142 |
+
Interlaced stream or variable: No
|
| 143 |
+
Fields per frame: 1 fields
|
| 144 |
+
Field 1 first: No
|
| 145 |
+
Field pattern: Field 1 only
|
| 146 |
+
bCopyProtect 0
|
| 147 |
+
VideoStreaming Interface Descriptor:
|
| 148 |
+
bLength 38
|
| 149 |
+
bDescriptorType 36
|
| 150 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 151 |
+
bFrameIndex 1
|
| 152 |
+
bmCapabilities 0x00
|
| 153 |
+
Still image unsupported
|
| 154 |
+
wWidth 640
|
| 155 |
+
wHeight 480
|
| 156 |
+
dwMinBitRate 383976960
|
| 157 |
+
dwMaxBitRate 383976960
|
| 158 |
+
dwMaxVideoFrameBufferSize 614400
|
| 159 |
+
dwDefaultFrameInterval 333333
|
| 160 |
+
bFrameIntervalType 0
|
| 161 |
+
dwMinFrameInterval 333333
|
| 162 |
+
dwMaxFrameInterval 333333
|
| 163 |
+
dwFrameIntervalStep 0
|
| 164 |
+
VideoStreaming Interface Descriptor:
|
| 165 |
+
bLength 38
|
| 166 |
+
bDescriptorType 36
|
| 167 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 168 |
+
bFrameIndex 2
|
| 169 |
+
bmCapabilities 0x00
|
| 170 |
+
Still image unsupported
|
| 171 |
+
wWidth 352
|
| 172 |
+
wHeight 288
|
| 173 |
+
dwMinBitRate 383976960
|
| 174 |
+
dwMaxBitRate 383976960
|
| 175 |
+
dwMaxVideoFrameBufferSize 202752
|
| 176 |
+
dwDefaultFrameInterval 333333
|
| 177 |
+
bFrameIntervalType 0
|
| 178 |
+
dwMinFrameInterval 333333
|
| 179 |
+
dwMaxFrameInterval 333333
|
| 180 |
+
dwFrameIntervalStep 0
|
| 181 |
+
VideoStreaming Interface Descriptor:
|
| 182 |
+
bLength 38
|
| 183 |
+
bDescriptorType 36
|
| 184 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 185 |
+
bFrameIndex 3
|
| 186 |
+
bmCapabilities 0x00
|
| 187 |
+
Still image unsupported
|
| 188 |
+
wWidth 320
|
| 189 |
+
wHeight 240
|
| 190 |
+
dwMinBitRate 383976960
|
| 191 |
+
dwMaxBitRate 383976960
|
| 192 |
+
dwMaxVideoFrameBufferSize 153600
|
| 193 |
+
dwDefaultFrameInterval 333333
|
| 194 |
+
bFrameIntervalType 0
|
| 195 |
+
dwMinFrameInterval 333333
|
| 196 |
+
dwMaxFrameInterval 333333
|
| 197 |
+
dwFrameIntervalStep 0
|
| 198 |
+
Interface Descriptor:
|
| 199 |
+
bLength 9
|
| 200 |
+
bDescriptorType 4
|
| 201 |
+
bInterfaceNumber 1
|
| 202 |
+
bAlternateSetting 1
|
| 203 |
+
bNumEndpoints 1
|
| 204 |
+
bInterfaceClass 14 Video
|
| 205 |
+
bInterfaceSubClass 2 Video Streaming
|
| 206 |
+
bInterfaceProtocol 0
|
| 207 |
+
iInterface 0
|
| 208 |
+
Endpoint Descriptor:
|
| 209 |
+
bLength 7
|
| 210 |
+
bDescriptorType 5
|
| 211 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 212 |
+
bmAttributes 5
|
| 213 |
+
Transfer Type Isochronous
|
| 214 |
+
Synch Type Asynchronous
|
| 215 |
+
Usage Type Data
|
| 216 |
+
wMaxPacketSize 0x1400 3x 1024 bytes
|
| 217 |
+
bInterval 1
|
| 218 |
+
Device Qualifier (for other device speed):
|
| 219 |
+
bLength 10
|
| 220 |
+
bDescriptorType 6
|
| 221 |
+
bcdUSB 2.00
|
| 222 |
+
bDeviceClass 14 Video
|
| 223 |
+
bDeviceSubClass 2 Video Streaming
|
| 224 |
+
bDeviceProtocol 0
|
| 225 |
+
bMaxPacketSize0 8
|
| 226 |
+
bNumConfigurations 1
|
| 227 |
+
Device Status: 0x0000
|
| 228 |
+
(Bus Powered)
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/isight_macbook.txt
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Bus 001 Device 010: ID 05ac:8501 Apple, Inc. Built-in iSight [Micron]
|
| 3 |
+
Device Descriptor:
|
| 4 |
+
bLength 18
|
| 5 |
+
bDescriptorType 1
|
| 6 |
+
bcdUSB 2.00
|
| 7 |
+
bDeviceClass 239 Miscellaneous Device
|
| 8 |
+
bDeviceSubClass 2 ?
|
| 9 |
+
bDeviceProtocol 1 Interface Association
|
| 10 |
+
bMaxPacketSize0 64
|
| 11 |
+
idVendor 0x05ac Apple, Inc.
|
| 12 |
+
idProduct 0x8501 Built-in iSight [Micron]
|
| 13 |
+
bcdDevice 1.89
|
| 14 |
+
iManufacturer 1 Micron
|
| 15 |
+
iProduct 2 Built-in iSight
|
| 16 |
+
iSerial 0
|
| 17 |
+
bNumConfigurations 1
|
| 18 |
+
Configuration Descriptor:
|
| 19 |
+
bLength 9
|
| 20 |
+
bDescriptorType 2
|
| 21 |
+
wTotalLength 267
|
| 22 |
+
bNumInterfaces 2
|
| 23 |
+
bConfigurationValue 1
|
| 24 |
+
iConfiguration 0
|
| 25 |
+
bmAttributes 0x80
|
| 26 |
+
(Bus Powered)
|
| 27 |
+
MaxPower 100mA
|
| 28 |
+
Interface Association:
|
| 29 |
+
bLength 8
|
| 30 |
+
bDescriptorType 11
|
| 31 |
+
bFirstInterface 0
|
| 32 |
+
bInterfaceCount 2
|
| 33 |
+
bFunctionClass 14 Video
|
| 34 |
+
bFunctionSubClass 3 Video Interface Collection
|
| 35 |
+
bFunctionProtocol 0
|
| 36 |
+
iFunction 0
|
| 37 |
+
Interface Descriptor:
|
| 38 |
+
bLength 9
|
| 39 |
+
bDescriptorType 4
|
| 40 |
+
bInterfaceNumber 0
|
| 41 |
+
bAlternateSetting 0
|
| 42 |
+
bNumEndpoints 1
|
| 43 |
+
bInterfaceClass 14 Video
|
| 44 |
+
bInterfaceSubClass 1 Video Control
|
| 45 |
+
bInterfaceProtocol 0
|
| 46 |
+
iInterface 0
|
| 47 |
+
VideoControl Interface Descriptor:
|
| 48 |
+
bLength 13
|
| 49 |
+
bDescriptorType 36
|
| 50 |
+
bDescriptorSubtype 1 (HEADER)
|
| 51 |
+
bcdUVC 1.00
|
| 52 |
+
wTotalLength 49
|
| 53 |
+
dwClockFrequency 13.500000MHz
|
| 54 |
+
bInCollection 1
|
| 55 |
+
baInterfaceNr( 0) 1
|
| 56 |
+
VideoControl Interface Descriptor:
|
| 57 |
+
bLength 16
|
| 58 |
+
bDescriptorType 36
|
| 59 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 60 |
+
bTerminalID 1
|
| 61 |
+
wTerminalType 0x0201 Camera Sensor
|
| 62 |
+
bAssocTerminal 0
|
| 63 |
+
iTerminal 0
|
| 64 |
+
wObjectiveFocalLengthMin 0
|
| 65 |
+
wObjectiveFocalLengthMax 0
|
| 66 |
+
wOcularFocalLength 0
|
| 67 |
+
bControlSize 1
|
| 68 |
+
bmControls 0x00000000
|
| 69 |
+
VideoControl Interface Descriptor:
|
| 70 |
+
bLength 11
|
| 71 |
+
bDescriptorType 36
|
| 72 |
+
bDescriptorSubtype 5 (PROCESSING_UNIT)
|
| 73 |
+
Warning: Descriptor too short
|
| 74 |
+
bUnitID 2
|
| 75 |
+
bSourceID 1
|
| 76 |
+
wMaxMultiplier 0
|
| 77 |
+
bControlSize 2
|
| 78 |
+
bmControls 0x00000039
|
| 79 |
+
Brightness
|
| 80 |
+
Saturation
|
| 81 |
+
Sharpness
|
| 82 |
+
Gamma
|
| 83 |
+
iProcessing 0
|
| 84 |
+
bmVideoStandards 0x 9
|
| 85 |
+
None
|
| 86 |
+
SECAM - 625/50
|
| 87 |
+
VideoControl Interface Descriptor:
|
| 88 |
+
bLength 9
|
| 89 |
+
bDescriptorType 36
|
| 90 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 91 |
+
bTerminalID 3
|
| 92 |
+
wTerminalType 0x0101 USB Streaming
|
| 93 |
+
bAssocTerminal 0
|
| 94 |
+
bSourceID 2
|
| 95 |
+
iTerminal 0
|
| 96 |
+
Endpoint Descriptor:
|
| 97 |
+
bLength 7
|
| 98 |
+
bDescriptorType 5
|
| 99 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 100 |
+
bmAttributes 3
|
| 101 |
+
Transfer Type Interrupt
|
| 102 |
+
Synch Type None
|
| 103 |
+
Usage Type Data
|
| 104 |
+
wMaxPacketSize 0x0008 1x 8 bytes
|
| 105 |
+
bInterval 10
|
| 106 |
+
Interface Descriptor:
|
| 107 |
+
bLength 9
|
| 108 |
+
bDescriptorType 4
|
| 109 |
+
bInterfaceNumber 1
|
| 110 |
+
bAlternateSetting 0
|
| 111 |
+
bNumEndpoints 0
|
| 112 |
+
bInterfaceClass 14 Video
|
| 113 |
+
bInterfaceSubClass 2 Video Streaming
|
| 114 |
+
bInterfaceProtocol 0
|
| 115 |
+
iInterface 0
|
| 116 |
+
VideoStreaming Interface Descriptor:
|
| 117 |
+
bLength 14
|
| 118 |
+
bDescriptorType 36
|
| 119 |
+
bDescriptorSubtype 1 (INPUT_HEADER)
|
| 120 |
+
bNumFormats 1
|
| 121 |
+
wTotalLength 155
|
| 122 |
+
bEndPointAddress 130
|
| 123 |
+
bmInfo 0
|
| 124 |
+
bTerminalLink 3
|
| 125 |
+
bStillCaptureMethod 0
|
| 126 |
+
bTriggerSupport 0
|
| 127 |
+
bTriggerUsage 0
|
| 128 |
+
bControlSize 1
|
| 129 |
+
bmaControls( 0) 27
|
| 130 |
+
VideoStreaming Interface Descriptor:
|
| 131 |
+
bLength 27
|
| 132 |
+
bDescriptorType 36
|
| 133 |
+
bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)
|
| 134 |
+
bFormatIndex 1
|
| 135 |
+
bNumFrameDescriptors 3
|
| 136 |
+
guidFormat {55595659-0000-1000-8000-00aa00389b71}
|
| 137 |
+
bBitsPerPixel 16
|
| 138 |
+
bDefaultFrameIndex 1
|
| 139 |
+
bAspectRatioX 0
|
| 140 |
+
bAspectRatioY 0
|
| 141 |
+
bmInterlaceFlags 0x00
|
| 142 |
+
Interlaced stream or variable: No
|
| 143 |
+
Fields per frame: 1 fields
|
| 144 |
+
Field 1 first: No
|
| 145 |
+
Field pattern: Field 1 only
|
| 146 |
+
bCopyProtect 0
|
| 147 |
+
VideoStreaming Interface Descriptor:
|
| 148 |
+
bLength 38
|
| 149 |
+
bDescriptorType 36
|
| 150 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 151 |
+
bFrameIndex 1
|
| 152 |
+
bmCapabilities 0x00
|
| 153 |
+
Still image unsupported
|
| 154 |
+
wWidth 640
|
| 155 |
+
wHeight 480
|
| 156 |
+
dwMinBitRate 383976960
|
| 157 |
+
dwMaxBitRate 383976960
|
| 158 |
+
dwMaxVideoFrameBufferSize 614400
|
| 159 |
+
dwDefaultFrameInterval 333333
|
| 160 |
+
bFrameIntervalType 0
|
| 161 |
+
dwMinFrameInterval 333333
|
| 162 |
+
dwMaxFrameInterval 333333
|
| 163 |
+
dwFrameIntervalStep 0
|
| 164 |
+
VideoStreaming Interface Descriptor:
|
| 165 |
+
bLength 38
|
| 166 |
+
bDescriptorType 36
|
| 167 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 168 |
+
bFrameIndex 2
|
| 169 |
+
bmCapabilities 0x00
|
| 170 |
+
Still image unsupported
|
| 171 |
+
wWidth 352
|
| 172 |
+
wHeight 288
|
| 173 |
+
dwMinBitRate 383976960
|
| 174 |
+
dwMaxBitRate 383976960
|
| 175 |
+
dwMaxVideoFrameBufferSize 202752
|
| 176 |
+
dwDefaultFrameInterval 333333
|
| 177 |
+
bFrameIntervalType 0
|
| 178 |
+
dwMinFrameInterval 333333
|
| 179 |
+
dwMaxFrameInterval 333333
|
| 180 |
+
dwFrameIntervalStep 0
|
| 181 |
+
VideoStreaming Interface Descriptor:
|
| 182 |
+
bLength 38
|
| 183 |
+
bDescriptorType 36
|
| 184 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 185 |
+
bFrameIndex 3
|
| 186 |
+
bmCapabilities 0x00
|
| 187 |
+
Still image unsupported
|
| 188 |
+
wWidth 320
|
| 189 |
+
wHeight 240
|
| 190 |
+
dwMinBitRate 383976960
|
| 191 |
+
dwMaxBitRate 383976960
|
| 192 |
+
dwMaxVideoFrameBufferSize 153600
|
| 193 |
+
dwDefaultFrameInterval 333333
|
| 194 |
+
bFrameIntervalType 0
|
| 195 |
+
dwMinFrameInterval 333333
|
| 196 |
+
dwMaxFrameInterval 333333
|
| 197 |
+
dwFrameIntervalStep 0
|
| 198 |
+
Interface Descriptor:
|
| 199 |
+
bLength 9
|
| 200 |
+
bDescriptorType 4
|
| 201 |
+
bInterfaceNumber 1
|
| 202 |
+
bAlternateSetting 1
|
| 203 |
+
bNumEndpoints 1
|
| 204 |
+
bInterfaceClass 14 Video
|
| 205 |
+
bInterfaceSubClass 2 Video Streaming
|
| 206 |
+
bInterfaceProtocol 0
|
| 207 |
+
iInterface 0
|
| 208 |
+
Endpoint Descriptor:
|
| 209 |
+
bLength 7
|
| 210 |
+
bDescriptorType 5
|
| 211 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 212 |
+
bmAttributes 5
|
| 213 |
+
Transfer Type Isochronous
|
| 214 |
+
Synch Type Asynchronous
|
| 215 |
+
Usage Type Data
|
| 216 |
+
wMaxPacketSize 0x1400 3x 1024 bytes
|
| 217 |
+
bInterval 1
|
| 218 |
+
Device Qualifier (for other device speed):
|
| 219 |
+
bLength 10
|
| 220 |
+
bDescriptorType 6
|
| 221 |
+
bcdUSB 2.00
|
| 222 |
+
bDeviceClass 14 Video
|
| 223 |
+
bDeviceSubClass 2 Video Streaming
|
| 224 |
+
bDeviceProtocol 0
|
| 225 |
+
bMaxPacketSize0 8
|
| 226 |
+
bNumConfigurations 1
|
| 227 |
+
Device Status: 0x0000
|
| 228 |
+
(Bus Powered)
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/logitech_hd_pro_920.txt
ADDED
|
@@ -0,0 +1,1817 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Bus 001 Device 018: ID 046d:082d Logitech, Inc. HD Pro Webcam C920
|
| 3 |
+
Device Descriptor:
|
| 4 |
+
bLength 18
|
| 5 |
+
bDescriptorType 1
|
| 6 |
+
bcdUSB 2.00
|
| 7 |
+
bDeviceClass 239 Miscellaneous Device
|
| 8 |
+
bDeviceSubClass 2 ?
|
| 9 |
+
bDeviceProtocol 1 Interface Association
|
| 10 |
+
bMaxPacketSize0 64
|
| 11 |
+
idVendor 0x046d Logitech, Inc.
|
| 12 |
+
idProduct 0x082d HD Pro Webcam C920
|
| 13 |
+
bcdDevice 0.11
|
| 14 |
+
iManufacturer 0
|
| 15 |
+
iProduct 2 HD Pro Webcam C920
|
| 16 |
+
iSerial 1 E1CA2A7F
|
| 17 |
+
bNumConfigurations 1
|
| 18 |
+
Configuration Descriptor:
|
| 19 |
+
bLength 9
|
| 20 |
+
bDescriptorType 2
|
| 21 |
+
wTotalLength 3452
|
| 22 |
+
bNumInterfaces 4
|
| 23 |
+
bConfigurationValue 1
|
| 24 |
+
iConfiguration 0
|
| 25 |
+
bmAttributes 0x80
|
| 26 |
+
(Bus Powered)
|
| 27 |
+
MaxPower 500mA
|
| 28 |
+
Interface Association:
|
| 29 |
+
bLength 8
|
| 30 |
+
bDescriptorType 11
|
| 31 |
+
bFirstInterface 0
|
| 32 |
+
bInterfaceCount 2
|
| 33 |
+
bFunctionClass 14 Video
|
| 34 |
+
bFunctionSubClass 3 Video Interface Collection
|
| 35 |
+
bFunctionProtocol 0
|
| 36 |
+
iFunction 0
|
| 37 |
+
Interface Descriptor:
|
| 38 |
+
bLength 9
|
| 39 |
+
bDescriptorType 4
|
| 40 |
+
bInterfaceNumber 0
|
| 41 |
+
bAlternateSetting 0
|
| 42 |
+
bNumEndpoints 1
|
| 43 |
+
bInterfaceClass 14 Video
|
| 44 |
+
bInterfaceSubClass 1 Video Control
|
| 45 |
+
bInterfaceProtocol 0
|
| 46 |
+
iInterface 0
|
| 47 |
+
VideoControl Interface Descriptor:
|
| 48 |
+
bLength 13
|
| 49 |
+
bDescriptorType 36
|
| 50 |
+
bDescriptorSubtype 1 (HEADER)
|
| 51 |
+
bcdUVC 1.00
|
| 52 |
+
wTotalLength 214
|
| 53 |
+
dwClockFrequency 300.000000MHz
|
| 54 |
+
bInCollection 1
|
| 55 |
+
baInterfaceNr( 0) 1
|
| 56 |
+
VideoControl Interface Descriptor:
|
| 57 |
+
bLength 18
|
| 58 |
+
bDescriptorType 36
|
| 59 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 60 |
+
bTerminalID 1
|
| 61 |
+
wTerminalType 0x0201 Camera Sensor
|
| 62 |
+
bAssocTerminal 0
|
| 63 |
+
iTerminal 0
|
| 64 |
+
wObjectiveFocalLengthMin 0
|
| 65 |
+
wObjectiveFocalLengthMax 0
|
| 66 |
+
wOcularFocalLength 0
|
| 67 |
+
bControlSize 3
|
| 68 |
+
bmControls 0x00020a2e
|
| 69 |
+
Auto-Exposure Mode
|
| 70 |
+
Auto-Exposure Priority
|
| 71 |
+
Exposure Time (Absolute)
|
| 72 |
+
Focus (Absolute)
|
| 73 |
+
Zoom (Absolute)
|
| 74 |
+
PanTilt (Absolute)
|
| 75 |
+
Focus, Auto
|
| 76 |
+
VideoControl Interface Descriptor:
|
| 77 |
+
bLength 11
|
| 78 |
+
bDescriptorType 36
|
| 79 |
+
bDescriptorSubtype 5 (PROCESSING_UNIT)
|
| 80 |
+
Warning: Descriptor too short
|
| 81 |
+
bUnitID 3
|
| 82 |
+
bSourceID 1
|
| 83 |
+
wMaxMultiplier 16384
|
| 84 |
+
bControlSize 2
|
| 85 |
+
bmControls 0x0000175b
|
| 86 |
+
Brightness
|
| 87 |
+
Contrast
|
| 88 |
+
Saturation
|
| 89 |
+
Sharpness
|
| 90 |
+
White Balance Temperature
|
| 91 |
+
Backlight Compensation
|
| 92 |
+
Gain
|
| 93 |
+
Power Line Frequency
|
| 94 |
+
White Balance Temperature, Auto
|
| 95 |
+
iProcessing 0
|
| 96 |
+
bmVideoStandards 0x1b
|
| 97 |
+
None
|
| 98 |
+
NTSC - 525/60
|
| 99 |
+
SECAM - 625/50
|
| 100 |
+
NTSC - 625/50
|
| 101 |
+
VideoControl Interface Descriptor:
|
| 102 |
+
bLength 27
|
| 103 |
+
bDescriptorType 36
|
| 104 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 105 |
+
bUnitID 6
|
| 106 |
+
guidExtensionCode {d09ee423-7811-314f-ae52-d2fb8a8d3b48}
|
| 107 |
+
bNumControl 10
|
| 108 |
+
bNrPins 1
|
| 109 |
+
baSourceID( 0) 3
|
| 110 |
+
bControlSize 2
|
| 111 |
+
bmControls( 0) 0xff
|
| 112 |
+
bmControls( 1) 0x03
|
| 113 |
+
iExtension 0
|
| 114 |
+
VideoControl Interface Descriptor:
|
| 115 |
+
bLength 27
|
| 116 |
+
bDescriptorType 36
|
| 117 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 118 |
+
bUnitID 8
|
| 119 |
+
guidExtensionCode {e48e6769-0f41-db40-a850-7420d7d8240e}
|
| 120 |
+
bNumControl 7
|
| 121 |
+
bNrPins 1
|
| 122 |
+
baSourceID( 0) 1
|
| 123 |
+
bControlSize 2
|
| 124 |
+
bmControls( 0) 0x3b
|
| 125 |
+
bmControls( 1) 0x03
|
| 126 |
+
iExtension 0
|
| 127 |
+
VideoControl Interface Descriptor:
|
| 128 |
+
bLength 28
|
| 129 |
+
bDescriptorType 36
|
| 130 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 131 |
+
bUnitID 9
|
| 132 |
+
guidExtensionCode {a94c5d1f-11de-8744-840d-50933c8ec8d1}
|
| 133 |
+
bNumControl 17
|
| 134 |
+
bNrPins 1
|
| 135 |
+
baSourceID( 0) 1
|
| 136 |
+
bControlSize 3
|
| 137 |
+
bmControls( 0) 0xf3
|
| 138 |
+
bmControls( 1) 0xff
|
| 139 |
+
bmControls( 2) 0x23
|
| 140 |
+
iExtension 0
|
| 141 |
+
VideoControl Interface Descriptor:
|
| 142 |
+
bLength 27
|
| 143 |
+
bDescriptorType 36
|
| 144 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 145 |
+
bUnitID 10
|
| 146 |
+
guidExtensionCode {1502e449-34f4-fe47-b158-0e885023e51b}
|
| 147 |
+
bNumControl 7
|
| 148 |
+
bNrPins 1
|
| 149 |
+
baSourceID( 0) 1
|
| 150 |
+
bControlSize 2
|
| 151 |
+
bmControls( 0) 0xaa
|
| 152 |
+
bmControls( 1) 0x07
|
| 153 |
+
iExtension 0
|
| 154 |
+
VideoControl Interface Descriptor:
|
| 155 |
+
bLength 27
|
| 156 |
+
bDescriptorType 36
|
| 157 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 158 |
+
bUnitID 11
|
| 159 |
+
guidExtensionCode {212de5ff-3080-2c4e-82d9-f587d00540bd}
|
| 160 |
+
bNumControl 2
|
| 161 |
+
bNrPins 1
|
| 162 |
+
baSourceID( 0) 1
|
| 163 |
+
bControlSize 2
|
| 164 |
+
bmControls( 0) 0x00
|
| 165 |
+
bmControls( 1) 0x41
|
| 166 |
+
iExtension 0
|
| 167 |
+
VideoControl Interface Descriptor:
|
| 168 |
+
bLength 27
|
| 169 |
+
bDescriptorType 36
|
| 170 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 171 |
+
bUnitID 12
|
| 172 |
+
guidExtensionCode {41769ea2-04de-e347-8b2b-f4341aff003b}
|
| 173 |
+
bNumControl 11
|
| 174 |
+
bNrPins 1
|
| 175 |
+
baSourceID( 0) 3
|
| 176 |
+
bControlSize 2
|
| 177 |
+
bmControls( 0) 0x07
|
| 178 |
+
bmControls( 1) 0x7f
|
| 179 |
+
iExtension 0
|
| 180 |
+
VideoControl Interface Descriptor:
|
| 181 |
+
bLength 9
|
| 182 |
+
bDescriptorType 36
|
| 183 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 184 |
+
bTerminalID 4
|
| 185 |
+
wTerminalType 0x0101 USB Streaming
|
| 186 |
+
bAssocTerminal 0
|
| 187 |
+
bSourceID 3
|
| 188 |
+
iTerminal 0
|
| 189 |
+
Endpoint Descriptor:
|
| 190 |
+
bLength 7
|
| 191 |
+
bDescriptorType 5
|
| 192 |
+
bEndpointAddress 0x83 EP 3 IN
|
| 193 |
+
bmAttributes 3
|
| 194 |
+
Transfer Type Interrupt
|
| 195 |
+
Synch Type None
|
| 196 |
+
Usage Type Data
|
| 197 |
+
wMaxPacketSize 0x0040 1x 64 bytes
|
| 198 |
+
bInterval 8
|
| 199 |
+
Interface Descriptor:
|
| 200 |
+
bLength 9
|
| 201 |
+
bDescriptorType 4
|
| 202 |
+
bInterfaceNumber 1
|
| 203 |
+
bAlternateSetting 0
|
| 204 |
+
bNumEndpoints 0
|
| 205 |
+
bInterfaceClass 14 Video
|
| 206 |
+
bInterfaceSubClass 2 Video Streaming
|
| 207 |
+
bInterfaceProtocol 0
|
| 208 |
+
iInterface 0
|
| 209 |
+
VideoStreaming Interface Descriptor:
|
| 210 |
+
bLength 16
|
| 211 |
+
bDescriptorType 36
|
| 212 |
+
bDescriptorSubtype 1 (INPUT_HEADER)
|
| 213 |
+
bNumFormats 3
|
| 214 |
+
wTotalLength 2822
|
| 215 |
+
bEndPointAddress 129
|
| 216 |
+
bmInfo 0
|
| 217 |
+
bTerminalLink 4
|
| 218 |
+
bStillCaptureMethod 0
|
| 219 |
+
bTriggerSupport 0
|
| 220 |
+
bTriggerUsage 0
|
| 221 |
+
bControlSize 1
|
| 222 |
+
bmaControls( 0) 27
|
| 223 |
+
bmaControls( 1) 27
|
| 224 |
+
bmaControls( 2) 27
|
| 225 |
+
VideoStreaming Interface Descriptor:
|
| 226 |
+
bLength 27
|
| 227 |
+
bDescriptorType 36
|
| 228 |
+
bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)
|
| 229 |
+
bFormatIndex 1
|
| 230 |
+
bNumFrameDescriptors 19
|
| 231 |
+
guidFormat {59555932-0000-1000-8000-00aa00389b71}
|
| 232 |
+
bBitsPerPixel 16
|
| 233 |
+
bDefaultFrameIndex 1
|
| 234 |
+
bAspectRatioX 0
|
| 235 |
+
bAspectRatioY 0
|
| 236 |
+
bmInterlaceFlags 0x00
|
| 237 |
+
Interlaced stream or variable: No
|
| 238 |
+
Fields per frame: 2 fields
|
| 239 |
+
Field 1 first: No
|
| 240 |
+
Field pattern: Field 1 only
|
| 241 |
+
bCopyProtect 0
|
| 242 |
+
VideoStreaming Interface Descriptor:
|
| 243 |
+
bLength 54
|
| 244 |
+
bDescriptorType 36
|
| 245 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 246 |
+
bFrameIndex 1
|
| 247 |
+
bmCapabilities 0x00
|
| 248 |
+
Still image unsupported
|
| 249 |
+
wWidth 640
|
| 250 |
+
wHeight 480
|
| 251 |
+
dwMinBitRate 24576000
|
| 252 |
+
dwMaxBitRate 147456000
|
| 253 |
+
dwMaxVideoFrameBufferSize 614400
|
| 254 |
+
dwDefaultFrameInterval 333333
|
| 255 |
+
bFrameIntervalType 7
|
| 256 |
+
dwFrameInterval( 0) 333333
|
| 257 |
+
dwFrameInterval( 1) 416666
|
| 258 |
+
dwFrameInterval( 2) 500000
|
| 259 |
+
dwFrameInterval( 3) 666666
|
| 260 |
+
dwFrameInterval( 4) 1000000
|
| 261 |
+
dwFrameInterval( 5) 1333333
|
| 262 |
+
dwFrameInterval( 6) 2000000
|
| 263 |
+
VideoStreaming Interface Descriptor:
|
| 264 |
+
bLength 54
|
| 265 |
+
bDescriptorType 36
|
| 266 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 267 |
+
bFrameIndex 2
|
| 268 |
+
bmCapabilities 0x00
|
| 269 |
+
Still image unsupported
|
| 270 |
+
wWidth 160
|
| 271 |
+
wHeight 90
|
| 272 |
+
dwMinBitRate 1152000
|
| 273 |
+
dwMaxBitRate 6912000
|
| 274 |
+
dwMaxVideoFrameBufferSize 28800
|
| 275 |
+
dwDefaultFrameInterval 333333
|
| 276 |
+
bFrameIntervalType 7
|
| 277 |
+
dwFrameInterval( 0) 333333
|
| 278 |
+
dwFrameInterval( 1) 416666
|
| 279 |
+
dwFrameInterval( 2) 500000
|
| 280 |
+
dwFrameInterval( 3) 666666
|
| 281 |
+
dwFrameInterval( 4) 1000000
|
| 282 |
+
dwFrameInterval( 5) 1333333
|
| 283 |
+
dwFrameInterval( 6) 2000000
|
| 284 |
+
VideoStreaming Interface Descriptor:
|
| 285 |
+
bLength 54
|
| 286 |
+
bDescriptorType 36
|
| 287 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 288 |
+
bFrameIndex 3
|
| 289 |
+
bmCapabilities 0x00
|
| 290 |
+
Still image unsupported
|
| 291 |
+
wWidth 160
|
| 292 |
+
wHeight 120
|
| 293 |
+
dwMinBitRate 1536000
|
| 294 |
+
dwMaxBitRate 9216000
|
| 295 |
+
dwMaxVideoFrameBufferSize 38400
|
| 296 |
+
dwDefaultFrameInterval 333333
|
| 297 |
+
bFrameIntervalType 7
|
| 298 |
+
dwFrameInterval( 0) 333333
|
| 299 |
+
dwFrameInterval( 1) 416666
|
| 300 |
+
dwFrameInterval( 2) 500000
|
| 301 |
+
dwFrameInterval( 3) 666666
|
| 302 |
+
dwFrameInterval( 4) 1000000
|
| 303 |
+
dwFrameInterval( 5) 1333333
|
| 304 |
+
dwFrameInterval( 6) 2000000
|
| 305 |
+
VideoStreaming Interface Descriptor:
|
| 306 |
+
bLength 54
|
| 307 |
+
bDescriptorType 36
|
| 308 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 309 |
+
bFrameIndex 4
|
| 310 |
+
bmCapabilities 0x00
|
| 311 |
+
Still image unsupported
|
| 312 |
+
wWidth 176
|
| 313 |
+
wHeight 144
|
| 314 |
+
dwMinBitRate 2027520
|
| 315 |
+
dwMaxBitRate 12165120
|
| 316 |
+
dwMaxVideoFrameBufferSize 50688
|
| 317 |
+
dwDefaultFrameInterval 333333
|
| 318 |
+
bFrameIntervalType 7
|
| 319 |
+
dwFrameInterval( 0) 333333
|
| 320 |
+
dwFrameInterval( 1) 416666
|
| 321 |
+
dwFrameInterval( 2) 500000
|
| 322 |
+
dwFrameInterval( 3) 666666
|
| 323 |
+
dwFrameInterval( 4) 1000000
|
| 324 |
+
dwFrameInterval( 5) 1333333
|
| 325 |
+
dwFrameInterval( 6) 2000000
|
| 326 |
+
VideoStreaming Interface Descriptor:
|
| 327 |
+
bLength 54
|
| 328 |
+
bDescriptorType 36
|
| 329 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 330 |
+
bFrameIndex 5
|
| 331 |
+
bmCapabilities 0x00
|
| 332 |
+
Still image unsupported
|
| 333 |
+
wWidth 320
|
| 334 |
+
wHeight 180
|
| 335 |
+
dwMinBitRate 4608000
|
| 336 |
+
dwMaxBitRate 27648000
|
| 337 |
+
dwMaxVideoFrameBufferSize 115200
|
| 338 |
+
dwDefaultFrameInterval 333333
|
| 339 |
+
bFrameIntervalType 7
|
| 340 |
+
dwFrameInterval( 0) 333333
|
| 341 |
+
dwFrameInterval( 1) 416666
|
| 342 |
+
dwFrameInterval( 2) 500000
|
| 343 |
+
dwFrameInterval( 3) 666666
|
| 344 |
+
dwFrameInterval( 4) 1000000
|
| 345 |
+
dwFrameInterval( 5) 1333333
|
| 346 |
+
dwFrameInterval( 6) 2000000
|
| 347 |
+
VideoStreaming Interface Descriptor:
|
| 348 |
+
bLength 54
|
| 349 |
+
bDescriptorType 36
|
| 350 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 351 |
+
bFrameIndex 6
|
| 352 |
+
bmCapabilities 0x00
|
| 353 |
+
Still image unsupported
|
| 354 |
+
wWidth 320
|
| 355 |
+
wHeight 240
|
| 356 |
+
dwMinBitRate 6144000
|
| 357 |
+
dwMaxBitRate 36864000
|
| 358 |
+
dwMaxVideoFrameBufferSize 153600
|
| 359 |
+
dwDefaultFrameInterval 333333
|
| 360 |
+
bFrameIntervalType 7
|
| 361 |
+
dwFrameInterval( 0) 333333
|
| 362 |
+
dwFrameInterval( 1) 416666
|
| 363 |
+
dwFrameInterval( 2) 500000
|
| 364 |
+
dwFrameInterval( 3) 666666
|
| 365 |
+
dwFrameInterval( 4) 1000000
|
| 366 |
+
dwFrameInterval( 5) 1333333
|
| 367 |
+
dwFrameInterval( 6) 2000000
|
| 368 |
+
VideoStreaming Interface Descriptor:
|
| 369 |
+
bLength 54
|
| 370 |
+
bDescriptorType 36
|
| 371 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 372 |
+
bFrameIndex 7
|
| 373 |
+
bmCapabilities 0x00
|
| 374 |
+
Still image unsupported
|
| 375 |
+
wWidth 352
|
| 376 |
+
wHeight 288
|
| 377 |
+
dwMinBitRate 8110080
|
| 378 |
+
dwMaxBitRate 48660480
|
| 379 |
+
dwMaxVideoFrameBufferSize 202752
|
| 380 |
+
dwDefaultFrameInterval 333333
|
| 381 |
+
bFrameIntervalType 7
|
| 382 |
+
dwFrameInterval( 0) 333333
|
| 383 |
+
dwFrameInterval( 1) 416666
|
| 384 |
+
dwFrameInterval( 2) 500000
|
| 385 |
+
dwFrameInterval( 3) 666666
|
| 386 |
+
dwFrameInterval( 4) 1000000
|
| 387 |
+
dwFrameInterval( 5) 1333333
|
| 388 |
+
dwFrameInterval( 6) 2000000
|
| 389 |
+
VideoStreaming Interface Descriptor:
|
| 390 |
+
bLength 54
|
| 391 |
+
bDescriptorType 36
|
| 392 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 393 |
+
bFrameIndex 8
|
| 394 |
+
bmCapabilities 0x00
|
| 395 |
+
Still image unsupported
|
| 396 |
+
wWidth 432
|
| 397 |
+
wHeight 240
|
| 398 |
+
dwMinBitRate 8294400
|
| 399 |
+
dwMaxBitRate 49766400
|
| 400 |
+
dwMaxVideoFrameBufferSize 207360
|
| 401 |
+
dwDefaultFrameInterval 333333
|
| 402 |
+
bFrameIntervalType 7
|
| 403 |
+
dwFrameInterval( 0) 333333
|
| 404 |
+
dwFrameInterval( 1) 416666
|
| 405 |
+
dwFrameInterval( 2) 500000
|
| 406 |
+
dwFrameInterval( 3) 666666
|
| 407 |
+
dwFrameInterval( 4) 1000000
|
| 408 |
+
dwFrameInterval( 5) 1333333
|
| 409 |
+
dwFrameInterval( 6) 2000000
|
| 410 |
+
VideoStreaming Interface Descriptor:
|
| 411 |
+
bLength 54
|
| 412 |
+
bDescriptorType 36
|
| 413 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 414 |
+
bFrameIndex 9
|
| 415 |
+
bmCapabilities 0x00
|
| 416 |
+
Still image unsupported
|
| 417 |
+
wWidth 640
|
| 418 |
+
wHeight 360
|
| 419 |
+
dwMinBitRate 18432000
|
| 420 |
+
dwMaxBitRate 110592000
|
| 421 |
+
dwMaxVideoFrameBufferSize 460800
|
| 422 |
+
dwDefaultFrameInterval 333333
|
| 423 |
+
bFrameIntervalType 7
|
| 424 |
+
dwFrameInterval( 0) 333333
|
| 425 |
+
dwFrameInterval( 1) 416666
|
| 426 |
+
dwFrameInterval( 2) 500000
|
| 427 |
+
dwFrameInterval( 3) 666666
|
| 428 |
+
dwFrameInterval( 4) 1000000
|
| 429 |
+
dwFrameInterval( 5) 1333333
|
| 430 |
+
dwFrameInterval( 6) 2000000
|
| 431 |
+
VideoStreaming Interface Descriptor:
|
| 432 |
+
bLength 54
|
| 433 |
+
bDescriptorType 36
|
| 434 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 435 |
+
bFrameIndex 10
|
| 436 |
+
bmCapabilities 0x00
|
| 437 |
+
Still image unsupported
|
| 438 |
+
wWidth 800
|
| 439 |
+
wHeight 448
|
| 440 |
+
dwMinBitRate 28672000
|
| 441 |
+
dwMaxBitRate 172032000
|
| 442 |
+
dwMaxVideoFrameBufferSize 716800
|
| 443 |
+
dwDefaultFrameInterval 333333
|
| 444 |
+
bFrameIntervalType 7
|
| 445 |
+
dwFrameInterval( 0) 333333
|
| 446 |
+
dwFrameInterval( 1) 416666
|
| 447 |
+
dwFrameInterval( 2) 500000
|
| 448 |
+
dwFrameInterval( 3) 666666
|
| 449 |
+
dwFrameInterval( 4) 1000000
|
| 450 |
+
dwFrameInterval( 5) 1333333
|
| 451 |
+
dwFrameInterval( 6) 2000000
|
| 452 |
+
VideoStreaming Interface Descriptor:
|
| 453 |
+
bLength 50
|
| 454 |
+
bDescriptorType 36
|
| 455 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 456 |
+
bFrameIndex 11
|
| 457 |
+
bmCapabilities 0x00
|
| 458 |
+
Still image unsupported
|
| 459 |
+
wWidth 800
|
| 460 |
+
wHeight 600
|
| 461 |
+
dwMinBitRate 38400000
|
| 462 |
+
dwMaxBitRate 184320000
|
| 463 |
+
dwMaxVideoFrameBufferSize 960000
|
| 464 |
+
dwDefaultFrameInterval 416666
|
| 465 |
+
bFrameIntervalType 6
|
| 466 |
+
dwFrameInterval( 0) 416666
|
| 467 |
+
dwFrameInterval( 1) 500000
|
| 468 |
+
dwFrameInterval( 2) 666666
|
| 469 |
+
dwFrameInterval( 3) 1000000
|
| 470 |
+
dwFrameInterval( 4) 1333333
|
| 471 |
+
dwFrameInterval( 5) 2000000
|
| 472 |
+
VideoStreaming Interface Descriptor:
|
| 473 |
+
bLength 50
|
| 474 |
+
bDescriptorType 36
|
| 475 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 476 |
+
bFrameIndex 12
|
| 477 |
+
bmCapabilities 0x00
|
| 478 |
+
Still image unsupported
|
| 479 |
+
wWidth 864
|
| 480 |
+
wHeight 480
|
| 481 |
+
dwMinBitRate 33177600
|
| 482 |
+
dwMaxBitRate 159252480
|
| 483 |
+
dwMaxVideoFrameBufferSize 829440
|
| 484 |
+
dwDefaultFrameInterval 416666
|
| 485 |
+
bFrameIntervalType 6
|
| 486 |
+
dwFrameInterval( 0) 416666
|
| 487 |
+
dwFrameInterval( 1) 500000
|
| 488 |
+
dwFrameInterval( 2) 666666
|
| 489 |
+
dwFrameInterval( 3) 1000000
|
| 490 |
+
dwFrameInterval( 4) 1333333
|
| 491 |
+
dwFrameInterval( 5) 2000000
|
| 492 |
+
VideoStreaming Interface Descriptor:
|
| 493 |
+
bLength 42
|
| 494 |
+
bDescriptorType 36
|
| 495 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 496 |
+
bFrameIndex 13
|
| 497 |
+
bmCapabilities 0x00
|
| 498 |
+
Still image unsupported
|
| 499 |
+
wWidth 960
|
| 500 |
+
wHeight 720
|
| 501 |
+
dwMinBitRate 55296000
|
| 502 |
+
dwMaxBitRate 165888000
|
| 503 |
+
dwMaxVideoFrameBufferSize 1382400
|
| 504 |
+
dwDefaultFrameInterval 666666
|
| 505 |
+
bFrameIntervalType 4
|
| 506 |
+
dwFrameInterval( 0) 666666
|
| 507 |
+
dwFrameInterval( 1) 1000000
|
| 508 |
+
dwFrameInterval( 2) 1333333
|
| 509 |
+
dwFrameInterval( 3) 2000000
|
| 510 |
+
VideoStreaming Interface Descriptor:
|
| 511 |
+
bLength 42
|
| 512 |
+
bDescriptorType 36
|
| 513 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 514 |
+
bFrameIndex 14
|
| 515 |
+
bmCapabilities 0x00
|
| 516 |
+
Still image unsupported
|
| 517 |
+
wWidth 1024
|
| 518 |
+
wHeight 576
|
| 519 |
+
dwMinBitRate 47185920
|
| 520 |
+
dwMaxBitRate 141557760
|
| 521 |
+
dwMaxVideoFrameBufferSize 1179648
|
| 522 |
+
dwDefaultFrameInterval 666666
|
| 523 |
+
bFrameIntervalType 4
|
| 524 |
+
dwFrameInterval( 0) 666666
|
| 525 |
+
dwFrameInterval( 1) 1000000
|
| 526 |
+
dwFrameInterval( 2) 1333333
|
| 527 |
+
dwFrameInterval( 3) 2000000
|
| 528 |
+
VideoStreaming Interface Descriptor:
|
| 529 |
+
bLength 38
|
| 530 |
+
bDescriptorType 36
|
| 531 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 532 |
+
bFrameIndex 15
|
| 533 |
+
bmCapabilities 0x00
|
| 534 |
+
Still image unsupported
|
| 535 |
+
wWidth 1280
|
| 536 |
+
wHeight 720
|
| 537 |
+
dwMinBitRate 73728000
|
| 538 |
+
dwMaxBitRate 147456000
|
| 539 |
+
dwMaxVideoFrameBufferSize 1843200
|
| 540 |
+
dwDefaultFrameInterval 1000000
|
| 541 |
+
bFrameIntervalType 3
|
| 542 |
+
dwFrameInterval( 0) 1000000
|
| 543 |
+
dwFrameInterval( 1) 1333333
|
| 544 |
+
dwFrameInterval( 2) 2000000
|
| 545 |
+
VideoStreaming Interface Descriptor:
|
| 546 |
+
bLength 34
|
| 547 |
+
bDescriptorType 36
|
| 548 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 549 |
+
bFrameIndex 16
|
| 550 |
+
bmCapabilities 0x00
|
| 551 |
+
Still image unsupported
|
| 552 |
+
wWidth 1600
|
| 553 |
+
wHeight 896
|
| 554 |
+
dwMinBitRate 114688000
|
| 555 |
+
dwMaxBitRate 172032000
|
| 556 |
+
dwMaxVideoFrameBufferSize 2867200
|
| 557 |
+
dwDefaultFrameInterval 1333333
|
| 558 |
+
bFrameIntervalType 2
|
| 559 |
+
dwFrameInterval( 0) 1333333
|
| 560 |
+
dwFrameInterval( 1) 2000000
|
| 561 |
+
VideoStreaming Interface Descriptor:
|
| 562 |
+
bLength 30
|
| 563 |
+
bDescriptorType 36
|
| 564 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 565 |
+
bFrameIndex 17
|
| 566 |
+
bmCapabilities 0x00
|
| 567 |
+
Still image unsupported
|
| 568 |
+
wWidth 1920
|
| 569 |
+
wHeight 1080
|
| 570 |
+
dwMinBitRate 165888000
|
| 571 |
+
dwMaxBitRate 165888000
|
| 572 |
+
dwMaxVideoFrameBufferSize 4147200
|
| 573 |
+
dwDefaultFrameInterval 2000000
|
| 574 |
+
bFrameIntervalType 1
|
| 575 |
+
dwFrameInterval( 0) 2000000
|
| 576 |
+
VideoStreaming Interface Descriptor:
|
| 577 |
+
bLength 30
|
| 578 |
+
bDescriptorType 36
|
| 579 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 580 |
+
bFrameIndex 18
|
| 581 |
+
bmCapabilities 0x00
|
| 582 |
+
Still image unsupported
|
| 583 |
+
wWidth 2304
|
| 584 |
+
wHeight 1296
|
| 585 |
+
dwMinBitRate 238878720
|
| 586 |
+
dwMaxBitRate 238878720
|
| 587 |
+
dwMaxVideoFrameBufferSize 5971968
|
| 588 |
+
dwDefaultFrameInterval 4999998
|
| 589 |
+
bFrameIntervalType 1
|
| 590 |
+
dwFrameInterval( 0) 4999998
|
| 591 |
+
VideoStreaming Interface Descriptor:
|
| 592 |
+
bLength 30
|
| 593 |
+
bDescriptorType 36
|
| 594 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 595 |
+
bFrameIndex 19
|
| 596 |
+
bmCapabilities 0x00
|
| 597 |
+
Still image unsupported
|
| 598 |
+
wWidth 2304
|
| 599 |
+
wHeight 1536
|
| 600 |
+
dwMinBitRate 283115520
|
| 601 |
+
dwMaxBitRate 283115520
|
| 602 |
+
dwMaxVideoFrameBufferSize 7077888
|
| 603 |
+
dwDefaultFrameInterval 4999998
|
| 604 |
+
bFrameIntervalType 1
|
| 605 |
+
dwFrameInterval( 0) 4999998
|
| 606 |
+
VideoStreaming Interface Descriptor:
|
| 607 |
+
bLength 6
|
| 608 |
+
bDescriptorType 36
|
| 609 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 610 |
+
bColorPrimaries 1 (BT.709,sRGB)
|
| 611 |
+
bTransferCharacteristics 1 (BT.709)
|
| 612 |
+
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
|
| 613 |
+
VideoStreaming Interface Descriptor:
|
| 614 |
+
bLength 28
|
| 615 |
+
bDescriptorType 36
|
| 616 |
+
bDescriptorSubtype 16 (FORMAT_FRAME_BASED)
|
| 617 |
+
bFormatIndex 2
|
| 618 |
+
bNumFrameDescriptors 17
|
| 619 |
+
guidFormat {48323634-0000-1000-8000-00aa00389b71}
|
| 620 |
+
bBitsPerPixel 16
|
| 621 |
+
bDefaultFrameIndex 1
|
| 622 |
+
bAspectRatioX 0
|
| 623 |
+
bAspectRatioY 0
|
| 624 |
+
bmInterlaceFlags 0x00
|
| 625 |
+
Interlaced stream or variable: No
|
| 626 |
+
Fields per frame: 2 fields
|
| 627 |
+
Field 1 first: No
|
| 628 |
+
Field pattern: Field 1 only
|
| 629 |
+
bCopyProtect 0
|
| 630 |
+
bVariableSize 1
|
| 631 |
+
VideoStreaming Interface Descriptor:
|
| 632 |
+
bLength 54
|
| 633 |
+
bDescriptorType 36
|
| 634 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 635 |
+
bFrameIndex 1
|
| 636 |
+
bmCapabilities 0x00
|
| 637 |
+
Still image unsupported
|
| 638 |
+
wWidth 640
|
| 639 |
+
wHeight 480
|
| 640 |
+
dwMinBitRate 24576000
|
| 641 |
+
dwMaxBitRate 147456000
|
| 642 |
+
dwDefaultFrameInterval 333333
|
| 643 |
+
bFrameIntervalType 7
|
| 644 |
+
dwBytesPerLine 0
|
| 645 |
+
dwFrameInterval( 0) 333333
|
| 646 |
+
dwFrameInterval( 1) 416666
|
| 647 |
+
dwFrameInterval( 2) 500000
|
| 648 |
+
dwFrameInterval( 3) 666666
|
| 649 |
+
dwFrameInterval( 4) 1000000
|
| 650 |
+
dwFrameInterval( 5) 1333333
|
| 651 |
+
dwFrameInterval( 6) 2000000
|
| 652 |
+
VideoStreaming Interface Descriptor:
|
| 653 |
+
bLength 54
|
| 654 |
+
bDescriptorType 36
|
| 655 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 656 |
+
bFrameIndex 2
|
| 657 |
+
bmCapabilities 0x00
|
| 658 |
+
Still image unsupported
|
| 659 |
+
wWidth 160
|
| 660 |
+
wHeight 90
|
| 661 |
+
dwMinBitRate 1152000
|
| 662 |
+
dwMaxBitRate 6912000
|
| 663 |
+
dwDefaultFrameInterval 333333
|
| 664 |
+
bFrameIntervalType 7
|
| 665 |
+
dwBytesPerLine 0
|
| 666 |
+
dwFrameInterval( 0) 333333
|
| 667 |
+
dwFrameInterval( 1) 416666
|
| 668 |
+
dwFrameInterval( 2) 500000
|
| 669 |
+
dwFrameInterval( 3) 666666
|
| 670 |
+
dwFrameInterval( 4) 1000000
|
| 671 |
+
dwFrameInterval( 5) 1333333
|
| 672 |
+
dwFrameInterval( 6) 2000000
|
| 673 |
+
VideoStreaming Interface Descriptor:
|
| 674 |
+
bLength 54
|
| 675 |
+
bDescriptorType 36
|
| 676 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 677 |
+
bFrameIndex 3
|
| 678 |
+
bmCapabilities 0x00
|
| 679 |
+
Still image unsupported
|
| 680 |
+
wWidth 160
|
| 681 |
+
wHeight 120
|
| 682 |
+
dwMinBitRate 1536000
|
| 683 |
+
dwMaxBitRate 9216000
|
| 684 |
+
dwDefaultFrameInterval 333333
|
| 685 |
+
bFrameIntervalType 7
|
| 686 |
+
dwBytesPerLine 0
|
| 687 |
+
dwFrameInterval( 0) 333333
|
| 688 |
+
dwFrameInterval( 1) 416666
|
| 689 |
+
dwFrameInterval( 2) 500000
|
| 690 |
+
dwFrameInterval( 3) 666666
|
| 691 |
+
dwFrameInterval( 4) 1000000
|
| 692 |
+
dwFrameInterval( 5) 1333333
|
| 693 |
+
dwFrameInterval( 6) 2000000
|
| 694 |
+
VideoStreaming Interface Descriptor:
|
| 695 |
+
bLength 54
|
| 696 |
+
bDescriptorType 36
|
| 697 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 698 |
+
bFrameIndex 4
|
| 699 |
+
bmCapabilities 0x00
|
| 700 |
+
Still image unsupported
|
| 701 |
+
wWidth 176
|
| 702 |
+
wHeight 144
|
| 703 |
+
dwMinBitRate 2027520
|
| 704 |
+
dwMaxBitRate 12165120
|
| 705 |
+
dwDefaultFrameInterval 333333
|
| 706 |
+
bFrameIntervalType 7
|
| 707 |
+
dwBytesPerLine 0
|
| 708 |
+
dwFrameInterval( 0) 333333
|
| 709 |
+
dwFrameInterval( 1) 416666
|
| 710 |
+
dwFrameInterval( 2) 500000
|
| 711 |
+
dwFrameInterval( 3) 666666
|
| 712 |
+
dwFrameInterval( 4) 1000000
|
| 713 |
+
dwFrameInterval( 5) 1333333
|
| 714 |
+
dwFrameInterval( 6) 2000000
|
| 715 |
+
VideoStreaming Interface Descriptor:
|
| 716 |
+
bLength 54
|
| 717 |
+
bDescriptorType 36
|
| 718 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 719 |
+
bFrameIndex 5
|
| 720 |
+
bmCapabilities 0x00
|
| 721 |
+
Still image unsupported
|
| 722 |
+
wWidth 320
|
| 723 |
+
wHeight 180
|
| 724 |
+
dwMinBitRate 4608000
|
| 725 |
+
dwMaxBitRate 27648000
|
| 726 |
+
dwDefaultFrameInterval 333333
|
| 727 |
+
bFrameIntervalType 7
|
| 728 |
+
dwBytesPerLine 0
|
| 729 |
+
dwFrameInterval( 0) 333333
|
| 730 |
+
dwFrameInterval( 1) 416666
|
| 731 |
+
dwFrameInterval( 2) 500000
|
| 732 |
+
dwFrameInterval( 3) 666666
|
| 733 |
+
dwFrameInterval( 4) 1000000
|
| 734 |
+
dwFrameInterval( 5) 1333333
|
| 735 |
+
dwFrameInterval( 6) 2000000
|
| 736 |
+
VideoStreaming Interface Descriptor:
|
| 737 |
+
bLength 54
|
| 738 |
+
bDescriptorType 36
|
| 739 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 740 |
+
bFrameIndex 6
|
| 741 |
+
bmCapabilities 0x00
|
| 742 |
+
Still image unsupported
|
| 743 |
+
wWidth 320
|
| 744 |
+
wHeight 240
|
| 745 |
+
dwMinBitRate 6144000
|
| 746 |
+
dwMaxBitRate 36864000
|
| 747 |
+
dwDefaultFrameInterval 333333
|
| 748 |
+
bFrameIntervalType 7
|
| 749 |
+
dwBytesPerLine 0
|
| 750 |
+
dwFrameInterval( 0) 333333
|
| 751 |
+
dwFrameInterval( 1) 416666
|
| 752 |
+
dwFrameInterval( 2) 500000
|
| 753 |
+
dwFrameInterval( 3) 666666
|
| 754 |
+
dwFrameInterval( 4) 1000000
|
| 755 |
+
dwFrameInterval( 5) 1333333
|
| 756 |
+
dwFrameInterval( 6) 2000000
|
| 757 |
+
VideoStreaming Interface Descriptor:
|
| 758 |
+
bLength 54
|
| 759 |
+
bDescriptorType 36
|
| 760 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 761 |
+
bFrameIndex 7
|
| 762 |
+
bmCapabilities 0x00
|
| 763 |
+
Still image unsupported
|
| 764 |
+
wWidth 352
|
| 765 |
+
wHeight 288
|
| 766 |
+
dwMinBitRate 8110080
|
| 767 |
+
dwMaxBitRate 48660480
|
| 768 |
+
dwDefaultFrameInterval 333333
|
| 769 |
+
bFrameIntervalType 7
|
| 770 |
+
dwBytesPerLine 0
|
| 771 |
+
dwFrameInterval( 0) 333333
|
| 772 |
+
dwFrameInterval( 1) 416666
|
| 773 |
+
dwFrameInterval( 2) 500000
|
| 774 |
+
dwFrameInterval( 3) 666666
|
| 775 |
+
dwFrameInterval( 4) 1000000
|
| 776 |
+
dwFrameInterval( 5) 1333333
|
| 777 |
+
dwFrameInterval( 6) 2000000
|
| 778 |
+
VideoStreaming Interface Descriptor:
|
| 779 |
+
bLength 54
|
| 780 |
+
bDescriptorType 36
|
| 781 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 782 |
+
bFrameIndex 8
|
| 783 |
+
bmCapabilities 0x00
|
| 784 |
+
Still image unsupported
|
| 785 |
+
wWidth 432
|
| 786 |
+
wHeight 240
|
| 787 |
+
dwMinBitRate 8294400
|
| 788 |
+
dwMaxBitRate 49766400
|
| 789 |
+
dwDefaultFrameInterval 333333
|
| 790 |
+
bFrameIntervalType 7
|
| 791 |
+
dwBytesPerLine 0
|
| 792 |
+
dwFrameInterval( 0) 333333
|
| 793 |
+
dwFrameInterval( 1) 416666
|
| 794 |
+
dwFrameInterval( 2) 500000
|
| 795 |
+
dwFrameInterval( 3) 666666
|
| 796 |
+
dwFrameInterval( 4) 1000000
|
| 797 |
+
dwFrameInterval( 5) 1333333
|
| 798 |
+
dwFrameInterval( 6) 2000000
|
| 799 |
+
VideoStreaming Interface Descriptor:
|
| 800 |
+
bLength 54
|
| 801 |
+
bDescriptorType 36
|
| 802 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 803 |
+
bFrameIndex 9
|
| 804 |
+
bmCapabilities 0x00
|
| 805 |
+
Still image unsupported
|
| 806 |
+
wWidth 640
|
| 807 |
+
wHeight 360
|
| 808 |
+
dwMinBitRate 18432000
|
| 809 |
+
dwMaxBitRate 110592000
|
| 810 |
+
dwDefaultFrameInterval 333333
|
| 811 |
+
bFrameIntervalType 7
|
| 812 |
+
dwBytesPerLine 0
|
| 813 |
+
dwFrameInterval( 0) 333333
|
| 814 |
+
dwFrameInterval( 1) 416666
|
| 815 |
+
dwFrameInterval( 2) 500000
|
| 816 |
+
dwFrameInterval( 3) 666666
|
| 817 |
+
dwFrameInterval( 4) 1000000
|
| 818 |
+
dwFrameInterval( 5) 1333333
|
| 819 |
+
dwFrameInterval( 6) 2000000
|
| 820 |
+
VideoStreaming Interface Descriptor:
|
| 821 |
+
bLength 54
|
| 822 |
+
bDescriptorType 36
|
| 823 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 824 |
+
bFrameIndex 10
|
| 825 |
+
bmCapabilities 0x00
|
| 826 |
+
Still image unsupported
|
| 827 |
+
wWidth 800
|
| 828 |
+
wHeight 448
|
| 829 |
+
dwMinBitRate 28672000
|
| 830 |
+
dwMaxBitRate 172032000
|
| 831 |
+
dwDefaultFrameInterval 333333
|
| 832 |
+
bFrameIntervalType 7
|
| 833 |
+
dwBytesPerLine 0
|
| 834 |
+
dwFrameInterval( 0) 333333
|
| 835 |
+
dwFrameInterval( 1) 416666
|
| 836 |
+
dwFrameInterval( 2) 500000
|
| 837 |
+
dwFrameInterval( 3) 666666
|
| 838 |
+
dwFrameInterval( 4) 1000000
|
| 839 |
+
dwFrameInterval( 5) 1333333
|
| 840 |
+
dwFrameInterval( 6) 2000000
|
| 841 |
+
VideoStreaming Interface Descriptor:
|
| 842 |
+
bLength 54
|
| 843 |
+
bDescriptorType 36
|
| 844 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 845 |
+
bFrameIndex 11
|
| 846 |
+
bmCapabilities 0x00
|
| 847 |
+
Still image unsupported
|
| 848 |
+
wWidth 800
|
| 849 |
+
wHeight 600
|
| 850 |
+
dwMinBitRate 38400000
|
| 851 |
+
dwMaxBitRate 230400000
|
| 852 |
+
dwDefaultFrameInterval 333333
|
| 853 |
+
bFrameIntervalType 7
|
| 854 |
+
dwBytesPerLine 0
|
| 855 |
+
dwFrameInterval( 0) 333333
|
| 856 |
+
dwFrameInterval( 1) 416666
|
| 857 |
+
dwFrameInterval( 2) 500000
|
| 858 |
+
dwFrameInterval( 3) 666666
|
| 859 |
+
dwFrameInterval( 4) 1000000
|
| 860 |
+
dwFrameInterval( 5) 1333333
|
| 861 |
+
dwFrameInterval( 6) 2000000
|
| 862 |
+
VideoStreaming Interface Descriptor:
|
| 863 |
+
bLength 54
|
| 864 |
+
bDescriptorType 36
|
| 865 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 866 |
+
bFrameIndex 12
|
| 867 |
+
bmCapabilities 0x00
|
| 868 |
+
Still image unsupported
|
| 869 |
+
wWidth 864
|
| 870 |
+
wHeight 480
|
| 871 |
+
dwMinBitRate 33177600
|
| 872 |
+
dwMaxBitRate 199065600
|
| 873 |
+
dwDefaultFrameInterval 333333
|
| 874 |
+
bFrameIntervalType 7
|
| 875 |
+
dwBytesPerLine 0
|
| 876 |
+
dwFrameInterval( 0) 333333
|
| 877 |
+
dwFrameInterval( 1) 416666
|
| 878 |
+
dwFrameInterval( 2) 500000
|
| 879 |
+
dwFrameInterval( 3) 666666
|
| 880 |
+
dwFrameInterval( 4) 1000000
|
| 881 |
+
dwFrameInterval( 5) 1333333
|
| 882 |
+
dwFrameInterval( 6) 2000000
|
| 883 |
+
VideoStreaming Interface Descriptor:
|
| 884 |
+
bLength 54
|
| 885 |
+
bDescriptorType 36
|
| 886 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 887 |
+
bFrameIndex 13
|
| 888 |
+
bmCapabilities 0x00
|
| 889 |
+
Still image unsupported
|
| 890 |
+
wWidth 960
|
| 891 |
+
wHeight 720
|
| 892 |
+
dwMinBitRate 55296000
|
| 893 |
+
dwMaxBitRate 331776000
|
| 894 |
+
dwDefaultFrameInterval 333333
|
| 895 |
+
bFrameIntervalType 7
|
| 896 |
+
dwBytesPerLine 0
|
| 897 |
+
dwFrameInterval( 0) 333333
|
| 898 |
+
dwFrameInterval( 1) 416666
|
| 899 |
+
dwFrameInterval( 2) 500000
|
| 900 |
+
dwFrameInterval( 3) 666666
|
| 901 |
+
dwFrameInterval( 4) 1000000
|
| 902 |
+
dwFrameInterval( 5) 1333333
|
| 903 |
+
dwFrameInterval( 6) 2000000
|
| 904 |
+
VideoStreaming Interface Descriptor:
|
| 905 |
+
bLength 54
|
| 906 |
+
bDescriptorType 36
|
| 907 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 908 |
+
bFrameIndex 14
|
| 909 |
+
bmCapabilities 0x00
|
| 910 |
+
Still image unsupported
|
| 911 |
+
wWidth 1024
|
| 912 |
+
wHeight 576
|
| 913 |
+
dwMinBitRate 47185920
|
| 914 |
+
dwMaxBitRate 283115520
|
| 915 |
+
dwDefaultFrameInterval 333333
|
| 916 |
+
bFrameIntervalType 7
|
| 917 |
+
dwBytesPerLine 0
|
| 918 |
+
dwFrameInterval( 0) 333333
|
| 919 |
+
dwFrameInterval( 1) 416666
|
| 920 |
+
dwFrameInterval( 2) 500000
|
| 921 |
+
dwFrameInterval( 3) 666666
|
| 922 |
+
dwFrameInterval( 4) 1000000
|
| 923 |
+
dwFrameInterval( 5) 1333333
|
| 924 |
+
dwFrameInterval( 6) 2000000
|
| 925 |
+
VideoStreaming Interface Descriptor:
|
| 926 |
+
bLength 54
|
| 927 |
+
bDescriptorType 36
|
| 928 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 929 |
+
bFrameIndex 15
|
| 930 |
+
bmCapabilities 0x00
|
| 931 |
+
Still image unsupported
|
| 932 |
+
wWidth 1280
|
| 933 |
+
wHeight 720
|
| 934 |
+
dwMinBitRate 73728000
|
| 935 |
+
dwMaxBitRate 442368000
|
| 936 |
+
dwDefaultFrameInterval 333333
|
| 937 |
+
bFrameIntervalType 7
|
| 938 |
+
dwBytesPerLine 0
|
| 939 |
+
dwFrameInterval( 0) 333333
|
| 940 |
+
dwFrameInterval( 1) 416666
|
| 941 |
+
dwFrameInterval( 2) 500000
|
| 942 |
+
dwFrameInterval( 3) 666666
|
| 943 |
+
dwFrameInterval( 4) 1000000
|
| 944 |
+
dwFrameInterval( 5) 1333333
|
| 945 |
+
dwFrameInterval( 6) 2000000
|
| 946 |
+
VideoStreaming Interface Descriptor:
|
| 947 |
+
bLength 54
|
| 948 |
+
bDescriptorType 36
|
| 949 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 950 |
+
bFrameIndex 16
|
| 951 |
+
bmCapabilities 0x00
|
| 952 |
+
Still image unsupported
|
| 953 |
+
wWidth 1600
|
| 954 |
+
wHeight 896
|
| 955 |
+
dwMinBitRate 114688000
|
| 956 |
+
dwMaxBitRate 688128000
|
| 957 |
+
dwDefaultFrameInterval 333333
|
| 958 |
+
bFrameIntervalType 7
|
| 959 |
+
dwBytesPerLine 0
|
| 960 |
+
dwFrameInterval( 0) 333333
|
| 961 |
+
dwFrameInterval( 1) 416666
|
| 962 |
+
dwFrameInterval( 2) 500000
|
| 963 |
+
dwFrameInterval( 3) 666666
|
| 964 |
+
dwFrameInterval( 4) 1000000
|
| 965 |
+
dwFrameInterval( 5) 1333333
|
| 966 |
+
dwFrameInterval( 6) 2000000
|
| 967 |
+
VideoStreaming Interface Descriptor:
|
| 968 |
+
bLength 54
|
| 969 |
+
bDescriptorType 36
|
| 970 |
+
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
|
| 971 |
+
bFrameIndex 17
|
| 972 |
+
bmCapabilities 0x00
|
| 973 |
+
Still image unsupported
|
| 974 |
+
wWidth 1920
|
| 975 |
+
wHeight 1080
|
| 976 |
+
dwMinBitRate 165888000
|
| 977 |
+
dwMaxBitRate 995328000
|
| 978 |
+
dwDefaultFrameInterval 333333
|
| 979 |
+
bFrameIntervalType 7
|
| 980 |
+
dwBytesPerLine 0
|
| 981 |
+
dwFrameInterval( 0) 333333
|
| 982 |
+
dwFrameInterval( 1) 416666
|
| 983 |
+
dwFrameInterval( 2) 500000
|
| 984 |
+
dwFrameInterval( 3) 666666
|
| 985 |
+
dwFrameInterval( 4) 1000000
|
| 986 |
+
dwFrameInterval( 5) 1333333
|
| 987 |
+
dwFrameInterval( 6) 2000000
|
| 988 |
+
VideoStreaming Interface Descriptor:
|
| 989 |
+
bLength 6
|
| 990 |
+
bDescriptorType 36
|
| 991 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 992 |
+
bColorPrimaries 1 (BT.709,sRGB)
|
| 993 |
+
bTransferCharacteristics 1 (BT.709)
|
| 994 |
+
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
|
| 995 |
+
VideoStreaming Interface Descriptor:
|
| 996 |
+
bLength 11
|
| 997 |
+
bDescriptorType 36
|
| 998 |
+
bDescriptorSubtype 6 (FORMAT_MJPEG)
|
| 999 |
+
bFormatIndex 3
|
| 1000 |
+
bNumFrameDescriptors 17
|
| 1001 |
+
bFlags 1
|
| 1002 |
+
Fixed-size samples: Yes
|
| 1003 |
+
bDefaultFrameIndex 1
|
| 1004 |
+
bAspectRatioX 0
|
| 1005 |
+
bAspectRatioY 0
|
| 1006 |
+
bmInterlaceFlags 0x00
|
| 1007 |
+
Interlaced stream or variable: No
|
| 1008 |
+
Fields per frame: 1 fields
|
| 1009 |
+
Field 1 first: No
|
| 1010 |
+
Field pattern: Field 1 only
|
| 1011 |
+
bCopyProtect 0
|
| 1012 |
+
VideoStreaming Interface Descriptor:
|
| 1013 |
+
bLength 54
|
| 1014 |
+
bDescriptorType 36
|
| 1015 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1016 |
+
bFrameIndex 1
|
| 1017 |
+
bmCapabilities 0x00
|
| 1018 |
+
Still image unsupported
|
| 1019 |
+
wWidth 640
|
| 1020 |
+
wHeight 480
|
| 1021 |
+
dwMinBitRate 24576000
|
| 1022 |
+
dwMaxBitRate 147456000
|
| 1023 |
+
dwMaxVideoFrameBufferSize 614400
|
| 1024 |
+
dwDefaultFrameInterval 333333
|
| 1025 |
+
bFrameIntervalType 7
|
| 1026 |
+
dwFrameInterval( 0) 333333
|
| 1027 |
+
dwFrameInterval( 1) 416666
|
| 1028 |
+
dwFrameInterval( 2) 500000
|
| 1029 |
+
dwFrameInterval( 3) 666666
|
| 1030 |
+
dwFrameInterval( 4) 1000000
|
| 1031 |
+
dwFrameInterval( 5) 1333333
|
| 1032 |
+
dwFrameInterval( 6) 2000000
|
| 1033 |
+
VideoStreaming Interface Descriptor:
|
| 1034 |
+
bLength 54
|
| 1035 |
+
bDescriptorType 36
|
| 1036 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1037 |
+
bFrameIndex 2
|
| 1038 |
+
bmCapabilities 0x00
|
| 1039 |
+
Still image unsupported
|
| 1040 |
+
wWidth 160
|
| 1041 |
+
wHeight 90
|
| 1042 |
+
dwMinBitRate 1152000
|
| 1043 |
+
dwMaxBitRate 6912000
|
| 1044 |
+
dwMaxVideoFrameBufferSize 28800
|
| 1045 |
+
dwDefaultFrameInterval 333333
|
| 1046 |
+
bFrameIntervalType 7
|
| 1047 |
+
dwFrameInterval( 0) 333333
|
| 1048 |
+
dwFrameInterval( 1) 416666
|
| 1049 |
+
dwFrameInterval( 2) 500000
|
| 1050 |
+
dwFrameInterval( 3) 666666
|
| 1051 |
+
dwFrameInterval( 4) 1000000
|
| 1052 |
+
dwFrameInterval( 5) 1333333
|
| 1053 |
+
dwFrameInterval( 6) 2000000
|
| 1054 |
+
VideoStreaming Interface Descriptor:
|
| 1055 |
+
bLength 54
|
| 1056 |
+
bDescriptorType 36
|
| 1057 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1058 |
+
bFrameIndex 3
|
| 1059 |
+
bmCapabilities 0x00
|
| 1060 |
+
Still image unsupported
|
| 1061 |
+
wWidth 160
|
| 1062 |
+
wHeight 120
|
| 1063 |
+
dwMinBitRate 1536000
|
| 1064 |
+
dwMaxBitRate 9216000
|
| 1065 |
+
dwMaxVideoFrameBufferSize 38400
|
| 1066 |
+
dwDefaultFrameInterval 333333
|
| 1067 |
+
bFrameIntervalType 7
|
| 1068 |
+
dwFrameInterval( 0) 333333
|
| 1069 |
+
dwFrameInterval( 1) 416666
|
| 1070 |
+
dwFrameInterval( 2) 500000
|
| 1071 |
+
dwFrameInterval( 3) 666666
|
| 1072 |
+
dwFrameInterval( 4) 1000000
|
| 1073 |
+
dwFrameInterval( 5) 1333333
|
| 1074 |
+
dwFrameInterval( 6) 2000000
|
| 1075 |
+
VideoStreaming Interface Descriptor:
|
| 1076 |
+
bLength 54
|
| 1077 |
+
bDescriptorType 36
|
| 1078 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1079 |
+
bFrameIndex 4
|
| 1080 |
+
bmCapabilities 0x00
|
| 1081 |
+
Still image unsupported
|
| 1082 |
+
wWidth 176
|
| 1083 |
+
wHeight 144
|
| 1084 |
+
dwMinBitRate 2027520
|
| 1085 |
+
dwMaxBitRate 12165120
|
| 1086 |
+
dwMaxVideoFrameBufferSize 50688
|
| 1087 |
+
dwDefaultFrameInterval 333333
|
| 1088 |
+
bFrameIntervalType 7
|
| 1089 |
+
dwFrameInterval( 0) 333333
|
| 1090 |
+
dwFrameInterval( 1) 416666
|
| 1091 |
+
dwFrameInterval( 2) 500000
|
| 1092 |
+
dwFrameInterval( 3) 666666
|
| 1093 |
+
dwFrameInterval( 4) 1000000
|
| 1094 |
+
dwFrameInterval( 5) 1333333
|
| 1095 |
+
dwFrameInterval( 6) 2000000
|
| 1096 |
+
VideoStreaming Interface Descriptor:
|
| 1097 |
+
bLength 54
|
| 1098 |
+
bDescriptorType 36
|
| 1099 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1100 |
+
bFrameIndex 5
|
| 1101 |
+
bmCapabilities 0x00
|
| 1102 |
+
Still image unsupported
|
| 1103 |
+
wWidth 320
|
| 1104 |
+
wHeight 180
|
| 1105 |
+
dwMinBitRate 4608000
|
| 1106 |
+
dwMaxBitRate 27648000
|
| 1107 |
+
dwMaxVideoFrameBufferSize 115200
|
| 1108 |
+
dwDefaultFrameInterval 333333
|
| 1109 |
+
bFrameIntervalType 7
|
| 1110 |
+
dwFrameInterval( 0) 333333
|
| 1111 |
+
dwFrameInterval( 1) 416666
|
| 1112 |
+
dwFrameInterval( 2) 500000
|
| 1113 |
+
dwFrameInterval( 3) 666666
|
| 1114 |
+
dwFrameInterval( 4) 1000000
|
| 1115 |
+
dwFrameInterval( 5) 1333333
|
| 1116 |
+
dwFrameInterval( 6) 2000000
|
| 1117 |
+
VideoStreaming Interface Descriptor:
|
| 1118 |
+
bLength 54
|
| 1119 |
+
bDescriptorType 36
|
| 1120 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1121 |
+
bFrameIndex 6
|
| 1122 |
+
bmCapabilities 0x00
|
| 1123 |
+
Still image unsupported
|
| 1124 |
+
wWidth 320
|
| 1125 |
+
wHeight 240
|
| 1126 |
+
dwMinBitRate 6144000
|
| 1127 |
+
dwMaxBitRate 36864000
|
| 1128 |
+
dwMaxVideoFrameBufferSize 153600
|
| 1129 |
+
dwDefaultFrameInterval 333333
|
| 1130 |
+
bFrameIntervalType 7
|
| 1131 |
+
dwFrameInterval( 0) 333333
|
| 1132 |
+
dwFrameInterval( 1) 416666
|
| 1133 |
+
dwFrameInterval( 2) 500000
|
| 1134 |
+
dwFrameInterval( 3) 666666
|
| 1135 |
+
dwFrameInterval( 4) 1000000
|
| 1136 |
+
dwFrameInterval( 5) 1333333
|
| 1137 |
+
dwFrameInterval( 6) 2000000
|
| 1138 |
+
VideoStreaming Interface Descriptor:
|
| 1139 |
+
bLength 54
|
| 1140 |
+
bDescriptorType 36
|
| 1141 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1142 |
+
bFrameIndex 7
|
| 1143 |
+
bmCapabilities 0x00
|
| 1144 |
+
Still image unsupported
|
| 1145 |
+
wWidth 352
|
| 1146 |
+
wHeight 288
|
| 1147 |
+
dwMinBitRate 8110080
|
| 1148 |
+
dwMaxBitRate 48660480
|
| 1149 |
+
dwMaxVideoFrameBufferSize 202752
|
| 1150 |
+
dwDefaultFrameInterval 333333
|
| 1151 |
+
bFrameIntervalType 7
|
| 1152 |
+
dwFrameInterval( 0) 333333
|
| 1153 |
+
dwFrameInterval( 1) 416666
|
| 1154 |
+
dwFrameInterval( 2) 500000
|
| 1155 |
+
dwFrameInterval( 3) 666666
|
| 1156 |
+
dwFrameInterval( 4) 1000000
|
| 1157 |
+
dwFrameInterval( 5) 1333333
|
| 1158 |
+
dwFrameInterval( 6) 2000000
|
| 1159 |
+
VideoStreaming Interface Descriptor:
|
| 1160 |
+
bLength 54
|
| 1161 |
+
bDescriptorType 36
|
| 1162 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1163 |
+
bFrameIndex 8
|
| 1164 |
+
bmCapabilities 0x00
|
| 1165 |
+
Still image unsupported
|
| 1166 |
+
wWidth 432
|
| 1167 |
+
wHeight 240
|
| 1168 |
+
dwMinBitRate 8294400
|
| 1169 |
+
dwMaxBitRate 49766400
|
| 1170 |
+
dwMaxVideoFrameBufferSize 207360
|
| 1171 |
+
dwDefaultFrameInterval 333333
|
| 1172 |
+
bFrameIntervalType 7
|
| 1173 |
+
dwFrameInterval( 0) 333333
|
| 1174 |
+
dwFrameInterval( 1) 416666
|
| 1175 |
+
dwFrameInterval( 2) 500000
|
| 1176 |
+
dwFrameInterval( 3) 666666
|
| 1177 |
+
dwFrameInterval( 4) 1000000
|
| 1178 |
+
dwFrameInterval( 5) 1333333
|
| 1179 |
+
dwFrameInterval( 6) 2000000
|
| 1180 |
+
VideoStreaming Interface Descriptor:
|
| 1181 |
+
bLength 54
|
| 1182 |
+
bDescriptorType 36
|
| 1183 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1184 |
+
bFrameIndex 9
|
| 1185 |
+
bmCapabilities 0x00
|
| 1186 |
+
Still image unsupported
|
| 1187 |
+
wWidth 640
|
| 1188 |
+
wHeight 360
|
| 1189 |
+
dwMinBitRate 18432000
|
| 1190 |
+
dwMaxBitRate 110592000
|
| 1191 |
+
dwMaxVideoFrameBufferSize 460800
|
| 1192 |
+
dwDefaultFrameInterval 333333
|
| 1193 |
+
bFrameIntervalType 7
|
| 1194 |
+
dwFrameInterval( 0) 333333
|
| 1195 |
+
dwFrameInterval( 1) 416666
|
| 1196 |
+
dwFrameInterval( 2) 500000
|
| 1197 |
+
dwFrameInterval( 3) 666666
|
| 1198 |
+
dwFrameInterval( 4) 1000000
|
| 1199 |
+
dwFrameInterval( 5) 1333333
|
| 1200 |
+
dwFrameInterval( 6) 2000000
|
| 1201 |
+
VideoStreaming Interface Descriptor:
|
| 1202 |
+
bLength 54
|
| 1203 |
+
bDescriptorType 36
|
| 1204 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1205 |
+
bFrameIndex 10
|
| 1206 |
+
bmCapabilities 0x00
|
| 1207 |
+
Still image unsupported
|
| 1208 |
+
wWidth 800
|
| 1209 |
+
wHeight 448
|
| 1210 |
+
dwMinBitRate 28672000
|
| 1211 |
+
dwMaxBitRate 172032000
|
| 1212 |
+
dwMaxVideoFrameBufferSize 716800
|
| 1213 |
+
dwDefaultFrameInterval 333333
|
| 1214 |
+
bFrameIntervalType 7
|
| 1215 |
+
dwFrameInterval( 0) 333333
|
| 1216 |
+
dwFrameInterval( 1) 416666
|
| 1217 |
+
dwFrameInterval( 2) 500000
|
| 1218 |
+
dwFrameInterval( 3) 666666
|
| 1219 |
+
dwFrameInterval( 4) 1000000
|
| 1220 |
+
dwFrameInterval( 5) 1333333
|
| 1221 |
+
dwFrameInterval( 6) 2000000
|
| 1222 |
+
VideoStreaming Interface Descriptor:
|
| 1223 |
+
bLength 54
|
| 1224 |
+
bDescriptorType 36
|
| 1225 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1226 |
+
bFrameIndex 11
|
| 1227 |
+
bmCapabilities 0x00
|
| 1228 |
+
Still image unsupported
|
| 1229 |
+
wWidth 800
|
| 1230 |
+
wHeight 600
|
| 1231 |
+
dwMinBitRate 38400000
|
| 1232 |
+
dwMaxBitRate 230400000
|
| 1233 |
+
dwMaxVideoFrameBufferSize 960000
|
| 1234 |
+
dwDefaultFrameInterval 333333
|
| 1235 |
+
bFrameIntervalType 7
|
| 1236 |
+
dwFrameInterval( 0) 333333
|
| 1237 |
+
dwFrameInterval( 1) 416666
|
| 1238 |
+
dwFrameInterval( 2) 500000
|
| 1239 |
+
dwFrameInterval( 3) 666666
|
| 1240 |
+
dwFrameInterval( 4) 1000000
|
| 1241 |
+
dwFrameInterval( 5) 1333333
|
| 1242 |
+
dwFrameInterval( 6) 2000000
|
| 1243 |
+
VideoStreaming Interface Descriptor:
|
| 1244 |
+
bLength 54
|
| 1245 |
+
bDescriptorType 36
|
| 1246 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1247 |
+
bFrameIndex 12
|
| 1248 |
+
bmCapabilities 0x00
|
| 1249 |
+
Still image unsupported
|
| 1250 |
+
wWidth 864
|
| 1251 |
+
wHeight 480
|
| 1252 |
+
dwMinBitRate 33177600
|
| 1253 |
+
dwMaxBitRate 199065600
|
| 1254 |
+
dwMaxVideoFrameBufferSize 829440
|
| 1255 |
+
dwDefaultFrameInterval 333333
|
| 1256 |
+
bFrameIntervalType 7
|
| 1257 |
+
dwFrameInterval( 0) 333333
|
| 1258 |
+
dwFrameInterval( 1) 416666
|
| 1259 |
+
dwFrameInterval( 2) 500000
|
| 1260 |
+
dwFrameInterval( 3) 666666
|
| 1261 |
+
dwFrameInterval( 4) 1000000
|
| 1262 |
+
dwFrameInterval( 5) 1333333
|
| 1263 |
+
dwFrameInterval( 6) 2000000
|
| 1264 |
+
VideoStreaming Interface Descriptor:
|
| 1265 |
+
bLength 54
|
| 1266 |
+
bDescriptorType 36
|
| 1267 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1268 |
+
bFrameIndex 13
|
| 1269 |
+
bmCapabilities 0x00
|
| 1270 |
+
Still image unsupported
|
| 1271 |
+
wWidth 960
|
| 1272 |
+
wHeight 720
|
| 1273 |
+
dwMinBitRate 55296000
|
| 1274 |
+
dwMaxBitRate 331776000
|
| 1275 |
+
dwMaxVideoFrameBufferSize 1382400
|
| 1276 |
+
dwDefaultFrameInterval 333333
|
| 1277 |
+
bFrameIntervalType 7
|
| 1278 |
+
dwFrameInterval( 0) 333333
|
| 1279 |
+
dwFrameInterval( 1) 416666
|
| 1280 |
+
dwFrameInterval( 2) 500000
|
| 1281 |
+
dwFrameInterval( 3) 666666
|
| 1282 |
+
dwFrameInterval( 4) 1000000
|
| 1283 |
+
dwFrameInterval( 5) 1333333
|
| 1284 |
+
dwFrameInterval( 6) 2000000
|
| 1285 |
+
VideoStreaming Interface Descriptor:
|
| 1286 |
+
bLength 54
|
| 1287 |
+
bDescriptorType 36
|
| 1288 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1289 |
+
bFrameIndex 14
|
| 1290 |
+
bmCapabilities 0x00
|
| 1291 |
+
Still image unsupported
|
| 1292 |
+
wWidth 1024
|
| 1293 |
+
wHeight 576
|
| 1294 |
+
dwMinBitRate 47185920
|
| 1295 |
+
dwMaxBitRate 283115520
|
| 1296 |
+
dwMaxVideoFrameBufferSize 1179648
|
| 1297 |
+
dwDefaultFrameInterval 333333
|
| 1298 |
+
bFrameIntervalType 7
|
| 1299 |
+
dwFrameInterval( 0) 333333
|
| 1300 |
+
dwFrameInterval( 1) 416666
|
| 1301 |
+
dwFrameInterval( 2) 500000
|
| 1302 |
+
dwFrameInterval( 3) 666666
|
| 1303 |
+
dwFrameInterval( 4) 1000000
|
| 1304 |
+
dwFrameInterval( 5) 1333333
|
| 1305 |
+
dwFrameInterval( 6) 2000000
|
| 1306 |
+
VideoStreaming Interface Descriptor:
|
| 1307 |
+
bLength 54
|
| 1308 |
+
bDescriptorType 36
|
| 1309 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1310 |
+
bFrameIndex 15
|
| 1311 |
+
bmCapabilities 0x00
|
| 1312 |
+
Still image unsupported
|
| 1313 |
+
wWidth 1280
|
| 1314 |
+
wHeight 720
|
| 1315 |
+
dwMinBitRate 73728000
|
| 1316 |
+
dwMaxBitRate 442368000
|
| 1317 |
+
dwMaxVideoFrameBufferSize 1843200
|
| 1318 |
+
dwDefaultFrameInterval 333333
|
| 1319 |
+
bFrameIntervalType 7
|
| 1320 |
+
dwFrameInterval( 0) 333333
|
| 1321 |
+
dwFrameInterval( 1) 416666
|
| 1322 |
+
dwFrameInterval( 2) 500000
|
| 1323 |
+
dwFrameInterval( 3) 666666
|
| 1324 |
+
dwFrameInterval( 4) 1000000
|
| 1325 |
+
dwFrameInterval( 5) 1333333
|
| 1326 |
+
dwFrameInterval( 6) 2000000
|
| 1327 |
+
VideoStreaming Interface Descriptor:
|
| 1328 |
+
bLength 54
|
| 1329 |
+
bDescriptorType 36
|
| 1330 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1331 |
+
bFrameIndex 16
|
| 1332 |
+
bmCapabilities 0x00
|
| 1333 |
+
Still image unsupported
|
| 1334 |
+
wWidth 1600
|
| 1335 |
+
wHeight 896
|
| 1336 |
+
dwMinBitRate 114688000
|
| 1337 |
+
dwMaxBitRate 688128000
|
| 1338 |
+
dwMaxVideoFrameBufferSize 2867200
|
| 1339 |
+
dwDefaultFrameInterval 333333
|
| 1340 |
+
bFrameIntervalType 7
|
| 1341 |
+
dwFrameInterval( 0) 333333
|
| 1342 |
+
dwFrameInterval( 1) 416666
|
| 1343 |
+
dwFrameInterval( 2) 500000
|
| 1344 |
+
dwFrameInterval( 3) 666666
|
| 1345 |
+
dwFrameInterval( 4) 1000000
|
| 1346 |
+
dwFrameInterval( 5) 1333333
|
| 1347 |
+
dwFrameInterval( 6) 2000000
|
| 1348 |
+
VideoStreaming Interface Descriptor:
|
| 1349 |
+
bLength 54
|
| 1350 |
+
bDescriptorType 36
|
| 1351 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 1352 |
+
bFrameIndex 17
|
| 1353 |
+
bmCapabilities 0x00
|
| 1354 |
+
Still image unsupported
|
| 1355 |
+
wWidth 1920
|
| 1356 |
+
wHeight 1080
|
| 1357 |
+
dwMinBitRate 165888000
|
| 1358 |
+
dwMaxBitRate 995328000
|
| 1359 |
+
dwMaxVideoFrameBufferSize 4147200
|
| 1360 |
+
dwDefaultFrameInterval 333333
|
| 1361 |
+
bFrameIntervalType 7
|
| 1362 |
+
dwFrameInterval( 0) 333333
|
| 1363 |
+
dwFrameInterval( 1) 416666
|
| 1364 |
+
dwFrameInterval( 2) 500000
|
| 1365 |
+
dwFrameInterval( 3) 666666
|
| 1366 |
+
dwFrameInterval( 4) 1000000
|
| 1367 |
+
dwFrameInterval( 5) 1333333
|
| 1368 |
+
dwFrameInterval( 6) 2000000
|
| 1369 |
+
VideoStreaming Interface Descriptor:
|
| 1370 |
+
bLength 6
|
| 1371 |
+
bDescriptorType 36
|
| 1372 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 1373 |
+
bColorPrimaries 1 (BT.709,sRGB)
|
| 1374 |
+
bTransferCharacteristics 1 (BT.709)
|
| 1375 |
+
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
|
| 1376 |
+
Interface Descriptor:
|
| 1377 |
+
bLength 9
|
| 1378 |
+
bDescriptorType 4
|
| 1379 |
+
bInterfaceNumber 1
|
| 1380 |
+
bAlternateSetting 1
|
| 1381 |
+
bNumEndpoints 1
|
| 1382 |
+
bInterfaceClass 14 Video
|
| 1383 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1384 |
+
bInterfaceProtocol 0
|
| 1385 |
+
iInterface 0
|
| 1386 |
+
Endpoint Descriptor:
|
| 1387 |
+
bLength 7
|
| 1388 |
+
bDescriptorType 5
|
| 1389 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1390 |
+
bmAttributes 5
|
| 1391 |
+
Transfer Type Isochronous
|
| 1392 |
+
Synch Type Asynchronous
|
| 1393 |
+
Usage Type Data
|
| 1394 |
+
wMaxPacketSize 0x00c0 1x 192 bytes
|
| 1395 |
+
bInterval 1
|
| 1396 |
+
Interface Descriptor:
|
| 1397 |
+
bLength 9
|
| 1398 |
+
bDescriptorType 4
|
| 1399 |
+
bInterfaceNumber 1
|
| 1400 |
+
bAlternateSetting 2
|
| 1401 |
+
bNumEndpoints 1
|
| 1402 |
+
bInterfaceClass 14 Video
|
| 1403 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1404 |
+
bInterfaceProtocol 0
|
| 1405 |
+
iInterface 0
|
| 1406 |
+
Endpoint Descriptor:
|
| 1407 |
+
bLength 7
|
| 1408 |
+
bDescriptorType 5
|
| 1409 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1410 |
+
bmAttributes 5
|
| 1411 |
+
Transfer Type Isochronous
|
| 1412 |
+
Synch Type Asynchronous
|
| 1413 |
+
Usage Type Data
|
| 1414 |
+
wMaxPacketSize 0x0180 1x 384 bytes
|
| 1415 |
+
bInterval 1
|
| 1416 |
+
Interface Descriptor:
|
| 1417 |
+
bLength 9
|
| 1418 |
+
bDescriptorType 4
|
| 1419 |
+
bInterfaceNumber 1
|
| 1420 |
+
bAlternateSetting 3
|
| 1421 |
+
bNumEndpoints 1
|
| 1422 |
+
bInterfaceClass 14 Video
|
| 1423 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1424 |
+
bInterfaceProtocol 0
|
| 1425 |
+
iInterface 0
|
| 1426 |
+
Endpoint Descriptor:
|
| 1427 |
+
bLength 7
|
| 1428 |
+
bDescriptorType 5
|
| 1429 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1430 |
+
bmAttributes 5
|
| 1431 |
+
Transfer Type Isochronous
|
| 1432 |
+
Synch Type Asynchronous
|
| 1433 |
+
Usage Type Data
|
| 1434 |
+
wMaxPacketSize 0x0200 1x 512 bytes
|
| 1435 |
+
bInterval 1
|
| 1436 |
+
Interface Descriptor:
|
| 1437 |
+
bLength 9
|
| 1438 |
+
bDescriptorType 4
|
| 1439 |
+
bInterfaceNumber 1
|
| 1440 |
+
bAlternateSetting 4
|
| 1441 |
+
bNumEndpoints 1
|
| 1442 |
+
bInterfaceClass 14 Video
|
| 1443 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1444 |
+
bInterfaceProtocol 0
|
| 1445 |
+
iInterface 0
|
| 1446 |
+
Endpoint Descriptor:
|
| 1447 |
+
bLength 7
|
| 1448 |
+
bDescriptorType 5
|
| 1449 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1450 |
+
bmAttributes 5
|
| 1451 |
+
Transfer Type Isochronous
|
| 1452 |
+
Synch Type Asynchronous
|
| 1453 |
+
Usage Type Data
|
| 1454 |
+
wMaxPacketSize 0x0280 1x 640 bytes
|
| 1455 |
+
bInterval 1
|
| 1456 |
+
Interface Descriptor:
|
| 1457 |
+
bLength 9
|
| 1458 |
+
bDescriptorType 4
|
| 1459 |
+
bInterfaceNumber 1
|
| 1460 |
+
bAlternateSetting 5
|
| 1461 |
+
bNumEndpoints 1
|
| 1462 |
+
bInterfaceClass 14 Video
|
| 1463 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1464 |
+
bInterfaceProtocol 0
|
| 1465 |
+
iInterface 0
|
| 1466 |
+
Endpoint Descriptor:
|
| 1467 |
+
bLength 7
|
| 1468 |
+
bDescriptorType 5
|
| 1469 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1470 |
+
bmAttributes 5
|
| 1471 |
+
Transfer Type Isochronous
|
| 1472 |
+
Synch Type Asynchronous
|
| 1473 |
+
Usage Type Data
|
| 1474 |
+
wMaxPacketSize 0x0320 1x 800 bytes
|
| 1475 |
+
bInterval 1
|
| 1476 |
+
Interface Descriptor:
|
| 1477 |
+
bLength 9
|
| 1478 |
+
bDescriptorType 4
|
| 1479 |
+
bInterfaceNumber 1
|
| 1480 |
+
bAlternateSetting 6
|
| 1481 |
+
bNumEndpoints 1
|
| 1482 |
+
bInterfaceClass 14 Video
|
| 1483 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1484 |
+
bInterfaceProtocol 0
|
| 1485 |
+
iInterface 0
|
| 1486 |
+
Endpoint Descriptor:
|
| 1487 |
+
bLength 7
|
| 1488 |
+
bDescriptorType 5
|
| 1489 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1490 |
+
bmAttributes 5
|
| 1491 |
+
Transfer Type Isochronous
|
| 1492 |
+
Synch Type Asynchronous
|
| 1493 |
+
Usage Type Data
|
| 1494 |
+
wMaxPacketSize 0x03b0 1x 944 bytes
|
| 1495 |
+
bInterval 1
|
| 1496 |
+
Interface Descriptor:
|
| 1497 |
+
bLength 9
|
| 1498 |
+
bDescriptorType 4
|
| 1499 |
+
bInterfaceNumber 1
|
| 1500 |
+
bAlternateSetting 7
|
| 1501 |
+
bNumEndpoints 1
|
| 1502 |
+
bInterfaceClass 14 Video
|
| 1503 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1504 |
+
bInterfaceProtocol 0
|
| 1505 |
+
iInterface 0
|
| 1506 |
+
Endpoint Descriptor:
|
| 1507 |
+
bLength 7
|
| 1508 |
+
bDescriptorType 5
|
| 1509 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1510 |
+
bmAttributes 5
|
| 1511 |
+
Transfer Type Isochronous
|
| 1512 |
+
Synch Type Asynchronous
|
| 1513 |
+
Usage Type Data
|
| 1514 |
+
wMaxPacketSize 0x0a80 2x 640 bytes
|
| 1515 |
+
bInterval 1
|
| 1516 |
+
Interface Descriptor:
|
| 1517 |
+
bLength 9
|
| 1518 |
+
bDescriptorType 4
|
| 1519 |
+
bInterfaceNumber 1
|
| 1520 |
+
bAlternateSetting 8
|
| 1521 |
+
bNumEndpoints 1
|
| 1522 |
+
bInterfaceClass 14 Video
|
| 1523 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1524 |
+
bInterfaceProtocol 0
|
| 1525 |
+
iInterface 0
|
| 1526 |
+
Endpoint Descriptor:
|
| 1527 |
+
bLength 7
|
| 1528 |
+
bDescriptorType 5
|
| 1529 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1530 |
+
bmAttributes 5
|
| 1531 |
+
Transfer Type Isochronous
|
| 1532 |
+
Synch Type Asynchronous
|
| 1533 |
+
Usage Type Data
|
| 1534 |
+
wMaxPacketSize 0x0b20 2x 800 bytes
|
| 1535 |
+
bInterval 1
|
| 1536 |
+
Interface Descriptor:
|
| 1537 |
+
bLength 9
|
| 1538 |
+
bDescriptorType 4
|
| 1539 |
+
bInterfaceNumber 1
|
| 1540 |
+
bAlternateSetting 9
|
| 1541 |
+
bNumEndpoints 1
|
| 1542 |
+
bInterfaceClass 14 Video
|
| 1543 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1544 |
+
bInterfaceProtocol 0
|
| 1545 |
+
iInterface 0
|
| 1546 |
+
Endpoint Descriptor:
|
| 1547 |
+
bLength 7
|
| 1548 |
+
bDescriptorType 5
|
| 1549 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1550 |
+
bmAttributes 5
|
| 1551 |
+
Transfer Type Isochronous
|
| 1552 |
+
Synch Type Asynchronous
|
| 1553 |
+
Usage Type Data
|
| 1554 |
+
wMaxPacketSize 0x0be0 2x 992 bytes
|
| 1555 |
+
bInterval 1
|
| 1556 |
+
Interface Descriptor:
|
| 1557 |
+
bLength 9
|
| 1558 |
+
bDescriptorType 4
|
| 1559 |
+
bInterfaceNumber 1
|
| 1560 |
+
bAlternateSetting 10
|
| 1561 |
+
bNumEndpoints 1
|
| 1562 |
+
bInterfaceClass 14 Video
|
| 1563 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1564 |
+
bInterfaceProtocol 0
|
| 1565 |
+
iInterface 0
|
| 1566 |
+
Endpoint Descriptor:
|
| 1567 |
+
bLength 7
|
| 1568 |
+
bDescriptorType 5
|
| 1569 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1570 |
+
bmAttributes 5
|
| 1571 |
+
Transfer Type Isochronous
|
| 1572 |
+
Synch Type Asynchronous
|
| 1573 |
+
Usage Type Data
|
| 1574 |
+
wMaxPacketSize 0x1380 3x 896 bytes
|
| 1575 |
+
bInterval 1
|
| 1576 |
+
Interface Descriptor:
|
| 1577 |
+
bLength 9
|
| 1578 |
+
bDescriptorType 4
|
| 1579 |
+
bInterfaceNumber 1
|
| 1580 |
+
bAlternateSetting 11
|
| 1581 |
+
bNumEndpoints 1
|
| 1582 |
+
bInterfaceClass 14 Video
|
| 1583 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1584 |
+
bInterfaceProtocol 0
|
| 1585 |
+
iInterface 0
|
| 1586 |
+
Endpoint Descriptor:
|
| 1587 |
+
bLength 7
|
| 1588 |
+
bDescriptorType 5
|
| 1589 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1590 |
+
bmAttributes 5
|
| 1591 |
+
Transfer Type Isochronous
|
| 1592 |
+
Synch Type Asynchronous
|
| 1593 |
+
Usage Type Data
|
| 1594 |
+
wMaxPacketSize 0x13fc 3x 1020 bytes
|
| 1595 |
+
bInterval 1
|
| 1596 |
+
Interface Association:
|
| 1597 |
+
bLength 8
|
| 1598 |
+
bDescriptorType 11
|
| 1599 |
+
bFirstInterface 2
|
| 1600 |
+
bInterfaceCount 2
|
| 1601 |
+
bFunctionClass 1 Audio
|
| 1602 |
+
bFunctionSubClass 2 Streaming
|
| 1603 |
+
bFunctionProtocol 0
|
| 1604 |
+
iFunction 0
|
| 1605 |
+
Interface Descriptor:
|
| 1606 |
+
bLength 9
|
| 1607 |
+
bDescriptorType 4
|
| 1608 |
+
bInterfaceNumber 2
|
| 1609 |
+
bAlternateSetting 0
|
| 1610 |
+
bNumEndpoints 0
|
| 1611 |
+
bInterfaceClass 1 Audio
|
| 1612 |
+
bInterfaceSubClass 1 Control Device
|
| 1613 |
+
bInterfaceProtocol 0
|
| 1614 |
+
iInterface 0
|
| 1615 |
+
AudioControl Interface Descriptor:
|
| 1616 |
+
bLength 9
|
| 1617 |
+
bDescriptorType 36
|
| 1618 |
+
bDescriptorSubtype 1 (HEADER)
|
| 1619 |
+
bcdADC 1.00
|
| 1620 |
+
wTotalLength 38
|
| 1621 |
+
bInCollection 1
|
| 1622 |
+
baInterfaceNr( 0) 3
|
| 1623 |
+
AudioControl Interface Descriptor:
|
| 1624 |
+
bLength 12
|
| 1625 |
+
bDescriptorType 36
|
| 1626 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 1627 |
+
bTerminalID 1
|
| 1628 |
+
wTerminalType 0x0201 Microphone
|
| 1629 |
+
bAssocTerminal 0
|
| 1630 |
+
bNrChannels 1
|
| 1631 |
+
wChannelConfig 0x0003
|
| 1632 |
+
Left Front (L)
|
| 1633 |
+
Right Front (R)
|
| 1634 |
+
iChannelNames 0
|
| 1635 |
+
iTerminal 0
|
| 1636 |
+
AudioControl Interface Descriptor:
|
| 1637 |
+
bLength 9
|
| 1638 |
+
bDescriptorType 36
|
| 1639 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 1640 |
+
bTerminalID 3
|
| 1641 |
+
wTerminalType 0x0101 USB Streaming
|
| 1642 |
+
bAssocTerminal 0
|
| 1643 |
+
bSourceID 5
|
| 1644 |
+
iTerminal 0
|
| 1645 |
+
AudioControl Interface Descriptor:
|
| 1646 |
+
bLength 8
|
| 1647 |
+
bDescriptorType 36
|
| 1648 |
+
bDescriptorSubtype 6 (FEATURE_UNIT)
|
| 1649 |
+
bUnitID 5
|
| 1650 |
+
bSourceID 1
|
| 1651 |
+
bControlSize 1
|
| 1652 |
+
bmaControls( 0) 0x03
|
| 1653 |
+
Mute Control
|
| 1654 |
+
Volume Control
|
| 1655 |
+
iFeature 0
|
| 1656 |
+
Interface Descriptor:
|
| 1657 |
+
bLength 9
|
| 1658 |
+
bDescriptorType 4
|
| 1659 |
+
bInterfaceNumber 3
|
| 1660 |
+
bAlternateSetting 0
|
| 1661 |
+
bNumEndpoints 0
|
| 1662 |
+
bInterfaceClass 1 Audio
|
| 1663 |
+
bInterfaceSubClass 2 Streaming
|
| 1664 |
+
bInterfaceProtocol 0
|
| 1665 |
+
iInterface 0
|
| 1666 |
+
Interface Descriptor:
|
| 1667 |
+
bLength 9
|
| 1668 |
+
bDescriptorType 4
|
| 1669 |
+
bInterfaceNumber 3
|
| 1670 |
+
bAlternateSetting 1
|
| 1671 |
+
bNumEndpoints 1
|
| 1672 |
+
bInterfaceClass 1 Audio
|
| 1673 |
+
bInterfaceSubClass 2 Streaming
|
| 1674 |
+
bInterfaceProtocol 0
|
| 1675 |
+
iInterface 0
|
| 1676 |
+
AudioStreaming Interface Descriptor:
|
| 1677 |
+
bLength 7
|
| 1678 |
+
bDescriptorType 36
|
| 1679 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1680 |
+
bTerminalLink 3
|
| 1681 |
+
bDelay 255 frames
|
| 1682 |
+
wFormatTag 1 PCM
|
| 1683 |
+
AudioStreaming Interface Descriptor:
|
| 1684 |
+
bLength 11
|
| 1685 |
+
bDescriptorType 36
|
| 1686 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1687 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1688 |
+
bNrChannels 2
|
| 1689 |
+
bSubframeSize 2
|
| 1690 |
+
bBitResolution 16
|
| 1691 |
+
bSamFreqType 1 Discrete
|
| 1692 |
+
tSamFreq[ 0] 16000
|
| 1693 |
+
Endpoint Descriptor:
|
| 1694 |
+
bLength 9
|
| 1695 |
+
bDescriptorType 5
|
| 1696 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 1697 |
+
bmAttributes 5
|
| 1698 |
+
Transfer Type Isochronous
|
| 1699 |
+
Synch Type Asynchronous
|
| 1700 |
+
Usage Type Data
|
| 1701 |
+
wMaxPacketSize 0x0044 1x 68 bytes
|
| 1702 |
+
bInterval 4
|
| 1703 |
+
bRefresh 0
|
| 1704 |
+
bSynchAddress 0
|
| 1705 |
+
AudioControl Endpoint Descriptor:
|
| 1706 |
+
bLength 7
|
| 1707 |
+
bDescriptorType 37
|
| 1708 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1709 |
+
bmAttributes 0x01
|
| 1710 |
+
Sampling Frequency
|
| 1711 |
+
bLockDelayUnits 0 Undefined
|
| 1712 |
+
wLockDelay 0 Undefined
|
| 1713 |
+
Interface Descriptor:
|
| 1714 |
+
bLength 9
|
| 1715 |
+
bDescriptorType 4
|
| 1716 |
+
bInterfaceNumber 3
|
| 1717 |
+
bAlternateSetting 2
|
| 1718 |
+
bNumEndpoints 1
|
| 1719 |
+
bInterfaceClass 1 Audio
|
| 1720 |
+
bInterfaceSubClass 2 Streaming
|
| 1721 |
+
bInterfaceProtocol 0
|
| 1722 |
+
iInterface 0
|
| 1723 |
+
AudioStreaming Interface Descriptor:
|
| 1724 |
+
bLength 7
|
| 1725 |
+
bDescriptorType 36
|
| 1726 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1727 |
+
bTerminalLink 3
|
| 1728 |
+
bDelay 255 frames
|
| 1729 |
+
wFormatTag 1 PCM
|
| 1730 |
+
AudioStreaming Interface Descriptor:
|
| 1731 |
+
bLength 11
|
| 1732 |
+
bDescriptorType 36
|
| 1733 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1734 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1735 |
+
bNrChannels 2
|
| 1736 |
+
bSubframeSize 2
|
| 1737 |
+
bBitResolution 16
|
| 1738 |
+
bSamFreqType 1 Discrete
|
| 1739 |
+
tSamFreq[ 0] 24000
|
| 1740 |
+
Endpoint Descriptor:
|
| 1741 |
+
bLength 9
|
| 1742 |
+
bDescriptorType 5
|
| 1743 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 1744 |
+
bmAttributes 5
|
| 1745 |
+
Transfer Type Isochronous
|
| 1746 |
+
Synch Type Asynchronous
|
| 1747 |
+
Usage Type Data
|
| 1748 |
+
wMaxPacketSize 0x0064 1x 100 bytes
|
| 1749 |
+
bInterval 4
|
| 1750 |
+
bRefresh 0
|
| 1751 |
+
bSynchAddress 0
|
| 1752 |
+
AudioControl Endpoint Descriptor:
|
| 1753 |
+
bLength 7
|
| 1754 |
+
bDescriptorType 37
|
| 1755 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1756 |
+
bmAttributes 0x01
|
| 1757 |
+
Sampling Frequency
|
| 1758 |
+
bLockDelayUnits 0 Undefined
|
| 1759 |
+
wLockDelay 0 Undefined
|
| 1760 |
+
Interface Descriptor:
|
| 1761 |
+
bLength 9
|
| 1762 |
+
bDescriptorType 4
|
| 1763 |
+
bInterfaceNumber 3
|
| 1764 |
+
bAlternateSetting 3
|
| 1765 |
+
bNumEndpoints 1
|
| 1766 |
+
bInterfaceClass 1 Audio
|
| 1767 |
+
bInterfaceSubClass 2 Streaming
|
| 1768 |
+
bInterfaceProtocol 0
|
| 1769 |
+
iInterface 0
|
| 1770 |
+
AudioStreaming Interface Descriptor:
|
| 1771 |
+
bLength 7
|
| 1772 |
+
bDescriptorType 36
|
| 1773 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1774 |
+
bTerminalLink 3
|
| 1775 |
+
bDelay 255 frames
|
| 1776 |
+
wFormatTag 1 PCM
|
| 1777 |
+
AudioStreaming Interface Descriptor:
|
| 1778 |
+
bLength 11
|
| 1779 |
+
bDescriptorType 36
|
| 1780 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1781 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1782 |
+
bNrChannels 2
|
| 1783 |
+
bSubframeSize 2
|
| 1784 |
+
bBitResolution 16
|
| 1785 |
+
bSamFreqType 1 Discrete
|
| 1786 |
+
tSamFreq[ 0] 32000
|
| 1787 |
+
Endpoint Descriptor:
|
| 1788 |
+
bLength 9
|
| 1789 |
+
bDescriptorType 5
|
| 1790 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 1791 |
+
bmAttributes 5
|
| 1792 |
+
Transfer Type Isochronous
|
| 1793 |
+
Synch Type Asynchronous
|
| 1794 |
+
Usage Type Data
|
| 1795 |
+
wMaxPacketSize 0x0084 1x 132 bytes
|
| 1796 |
+
bInterval 4
|
| 1797 |
+
bRefresh 0
|
| 1798 |
+
bSynchAddress 0
|
| 1799 |
+
AudioControl Endpoint Descriptor:
|
| 1800 |
+
bLength 7
|
| 1801 |
+
bDescriptorType 37
|
| 1802 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1803 |
+
bmAttributes 0x01
|
| 1804 |
+
Sampling Frequency
|
| 1805 |
+
bLockDelayUnits 0 Undefined
|
| 1806 |
+
wLockDelay 0 Undefined
|
| 1807 |
+
Device Qualifier (for other device speed):
|
| 1808 |
+
bLength 10
|
| 1809 |
+
bDescriptorType 6
|
| 1810 |
+
bcdUSB 2.00
|
| 1811 |
+
bDeviceClass 239 Miscellaneous Device
|
| 1812 |
+
bDeviceSubClass 2 ?
|
| 1813 |
+
bDeviceProtocol 1 Interface Association
|
| 1814 |
+
bMaxPacketSize0 64
|
| 1815 |
+
bNumConfigurations 1
|
| 1816 |
+
Device Status: 0x0000
|
| 1817 |
+
(Bus Powered)
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/ms_lifecam_show.txt
ADDED
|
@@ -0,0 +1,767 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Bus 001 Device 010: ID 045e:0729 Microsoft Corp.
|
| 3 |
+
Device Descriptor:
|
| 4 |
+
bLength 18
|
| 5 |
+
bDescriptorType 1
|
| 6 |
+
bcdUSB 2.00
|
| 7 |
+
bDeviceClass 239 Miscellaneous Device
|
| 8 |
+
bDeviceSubClass 2 ?
|
| 9 |
+
bDeviceProtocol 1 Interface Association
|
| 10 |
+
bMaxPacketSize0 64
|
| 11 |
+
idVendor 0x045e Microsoft Corp.
|
| 12 |
+
idProduct 0x0729
|
| 13 |
+
bcdDevice 1.00
|
| 14 |
+
iManufacturer 1 Microsoft
|
| 15 |
+
iProduct 2 Microsoft� LifeCam Show(TM)
|
| 16 |
+
iSerial 0
|
| 17 |
+
bNumConfigurations 1
|
| 18 |
+
Configuration Descriptor:
|
| 19 |
+
bLength 9
|
| 20 |
+
bDescriptorType 2
|
| 21 |
+
wTotalLength 961
|
| 22 |
+
bNumInterfaces 5
|
| 23 |
+
bConfigurationValue 1
|
| 24 |
+
iConfiguration 0
|
| 25 |
+
bmAttributes 0x80
|
| 26 |
+
(Bus Powered)
|
| 27 |
+
MaxPower 320mA
|
| 28 |
+
Interface Association:
|
| 29 |
+
bLength 8
|
| 30 |
+
bDescriptorType 11
|
| 31 |
+
bFirstInterface 0
|
| 32 |
+
bInterfaceCount 2
|
| 33 |
+
bFunctionClass 14 Video
|
| 34 |
+
bFunctionSubClass 3 Video Interface Collection
|
| 35 |
+
bFunctionProtocol 0
|
| 36 |
+
iFunction 2 Microsoft� LifeCam Show(TM)
|
| 37 |
+
Interface Descriptor:
|
| 38 |
+
bLength 9
|
| 39 |
+
bDescriptorType 4
|
| 40 |
+
bInterfaceNumber 0
|
| 41 |
+
bAlternateSetting 0
|
| 42 |
+
bNumEndpoints 1
|
| 43 |
+
bInterfaceClass 14 Video
|
| 44 |
+
bInterfaceSubClass 1 Video Control
|
| 45 |
+
bInterfaceProtocol 0
|
| 46 |
+
iInterface 2 Microsoft� LifeCam Show(TM)
|
| 47 |
+
VideoControl Interface Descriptor:
|
| 48 |
+
bLength 13
|
| 49 |
+
bDescriptorType 36
|
| 50 |
+
bDescriptorSubtype 1 (HEADER)
|
| 51 |
+
bcdUVC 1.00
|
| 52 |
+
wTotalLength 79
|
| 53 |
+
dwClockFrequency 24.000000MHz
|
| 54 |
+
bInCollection 1
|
| 55 |
+
baInterfaceNr( 0) 1
|
| 56 |
+
VideoControl Interface Descriptor:
|
| 57 |
+
bLength 18
|
| 58 |
+
bDescriptorType 36
|
| 59 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 60 |
+
bTerminalID 1
|
| 61 |
+
wTerminalType 0x0201 Camera Sensor
|
| 62 |
+
bAssocTerminal 0
|
| 63 |
+
iTerminal 0
|
| 64 |
+
wObjectiveFocalLengthMin 0
|
| 65 |
+
wObjectiveFocalLengthMax 0
|
| 66 |
+
wOcularFocalLength 0
|
| 67 |
+
bControlSize 3
|
| 68 |
+
bmControls 0x00000a0a
|
| 69 |
+
Auto-Exposure Mode
|
| 70 |
+
Exposure Time (Absolute)
|
| 71 |
+
Zoom (Absolute)
|
| 72 |
+
PanTilt (Absolute)
|
| 73 |
+
VideoControl Interface Descriptor:
|
| 74 |
+
bLength 11
|
| 75 |
+
bDescriptorType 36
|
| 76 |
+
bDescriptorSubtype 5 (PROCESSING_UNIT)
|
| 77 |
+
Warning: Descriptor too short
|
| 78 |
+
bUnitID 2
|
| 79 |
+
bSourceID 1
|
| 80 |
+
wMaxMultiplier 0
|
| 81 |
+
bControlSize 2
|
| 82 |
+
bmControls 0x0000073b
|
| 83 |
+
Brightness
|
| 84 |
+
Contrast
|
| 85 |
+
Saturation
|
| 86 |
+
Sharpness
|
| 87 |
+
Gamma
|
| 88 |
+
Backlight Compensation
|
| 89 |
+
Gain
|
| 90 |
+
Power Line Frequency
|
| 91 |
+
iProcessing 0
|
| 92 |
+
bmVideoStandards 0x 9
|
| 93 |
+
None
|
| 94 |
+
SECAM - 625/50
|
| 95 |
+
VideoControl Interface Descriptor:
|
| 96 |
+
bLength 9
|
| 97 |
+
bDescriptorType 36
|
| 98 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 99 |
+
bTerminalID 3
|
| 100 |
+
wTerminalType 0x0101 USB Streaming
|
| 101 |
+
bAssocTerminal 0
|
| 102 |
+
bSourceID 2
|
| 103 |
+
iTerminal 0
|
| 104 |
+
VideoControl Interface Descriptor:
|
| 105 |
+
bLength 28
|
| 106 |
+
bDescriptorType 36
|
| 107 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 108 |
+
bUnitID 4
|
| 109 |
+
guidExtensionCode {5dc717a9-1941-da11-ae0e-000d56ac7b4c}
|
| 110 |
+
bNumControl 8
|
| 111 |
+
bNrPins 1
|
| 112 |
+
baSourceID( 0) 1
|
| 113 |
+
bControlSize 3
|
| 114 |
+
bmControls( 0) 0xf9
|
| 115 |
+
bmControls( 1) 0x01
|
| 116 |
+
bmControls( 2) 0xc0
|
| 117 |
+
iExtension 0
|
| 118 |
+
Endpoint Descriptor:
|
| 119 |
+
bLength 7
|
| 120 |
+
bDescriptorType 5
|
| 121 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 122 |
+
bmAttributes 3
|
| 123 |
+
Transfer Type Interrupt
|
| 124 |
+
Synch Type None
|
| 125 |
+
Usage Type Data
|
| 126 |
+
wMaxPacketSize 0x000a 1x 10 bytes
|
| 127 |
+
bInterval 5
|
| 128 |
+
Interface Descriptor:
|
| 129 |
+
bLength 9
|
| 130 |
+
bDescriptorType 4
|
| 131 |
+
bInterfaceNumber 1
|
| 132 |
+
bAlternateSetting 0
|
| 133 |
+
bNumEndpoints 0
|
| 134 |
+
bInterfaceClass 14 Video
|
| 135 |
+
bInterfaceSubClass 2 Video Streaming
|
| 136 |
+
bInterfaceProtocol 0
|
| 137 |
+
iInterface 0
|
| 138 |
+
VideoStreaming Interface Descriptor:
|
| 139 |
+
bLength 15
|
| 140 |
+
bDescriptorType 36
|
| 141 |
+
bDescriptorSubtype 1 (INPUT_HEADER)
|
| 142 |
+
bNumFormats 2
|
| 143 |
+
wTotalLength 587
|
| 144 |
+
bEndPointAddress 130
|
| 145 |
+
bmInfo 0
|
| 146 |
+
bTerminalLink 3
|
| 147 |
+
bStillCaptureMethod 2
|
| 148 |
+
bTriggerSupport 1
|
| 149 |
+
bTriggerUsage 1
|
| 150 |
+
bControlSize 1
|
| 151 |
+
bmaControls( 0) 27
|
| 152 |
+
bmaControls( 1) 27
|
| 153 |
+
VideoStreaming Interface Descriptor:
|
| 154 |
+
bLength 27
|
| 155 |
+
bDescriptorType 36
|
| 156 |
+
bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)
|
| 157 |
+
bFormatIndex 1
|
| 158 |
+
bNumFrameDescriptors 6
|
| 159 |
+
guidFormat {59555932-0000-1000-8000-00aa00389b71}
|
| 160 |
+
bBitsPerPixel 16
|
| 161 |
+
bDefaultFrameIndex 1
|
| 162 |
+
bAspectRatioX 0
|
| 163 |
+
bAspectRatioY 0
|
| 164 |
+
bmInterlaceFlags 0x00
|
| 165 |
+
Interlaced stream or variable: No
|
| 166 |
+
Fields per frame: 1 fields
|
| 167 |
+
Field 1 first: No
|
| 168 |
+
Field pattern: Field 1 only
|
| 169 |
+
bCopyProtect 0
|
| 170 |
+
VideoStreaming Interface Descriptor:
|
| 171 |
+
bLength 30
|
| 172 |
+
bDescriptorType 36
|
| 173 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 174 |
+
bFrameIndex 1
|
| 175 |
+
bmCapabilities 0x00
|
| 176 |
+
Still image unsupported
|
| 177 |
+
wWidth 352
|
| 178 |
+
wHeight 288
|
| 179 |
+
dwMinBitRate 196608000
|
| 180 |
+
dwMaxBitRate 196608000
|
| 181 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 182 |
+
dwDefaultFrameInterval 666667
|
| 183 |
+
bFrameIntervalType 1
|
| 184 |
+
dwFrameInterval( 0) 666667
|
| 185 |
+
VideoStreaming Interface Descriptor:
|
| 186 |
+
bLength 30
|
| 187 |
+
bDescriptorType 36
|
| 188 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 189 |
+
bFrameIndex 2
|
| 190 |
+
bmCapabilities 0x00
|
| 191 |
+
Still image unsupported
|
| 192 |
+
wWidth 640
|
| 193 |
+
wHeight 480
|
| 194 |
+
dwMinBitRate 196608000
|
| 195 |
+
dwMaxBitRate 196608000
|
| 196 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 197 |
+
dwDefaultFrameInterval 666667
|
| 198 |
+
bFrameIntervalType 1
|
| 199 |
+
dwFrameInterval( 0) 666667
|
| 200 |
+
VideoStreaming Interface Descriptor:
|
| 201 |
+
bLength 30
|
| 202 |
+
bDescriptorType 36
|
| 203 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 204 |
+
bFrameIndex 3
|
| 205 |
+
bmCapabilities 0x00
|
| 206 |
+
Still image unsupported
|
| 207 |
+
wWidth 320
|
| 208 |
+
wHeight 240
|
| 209 |
+
dwMinBitRate 196608000
|
| 210 |
+
dwMaxBitRate 196608000
|
| 211 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 212 |
+
dwDefaultFrameInterval 666667
|
| 213 |
+
bFrameIntervalType 1
|
| 214 |
+
dwFrameInterval( 0) 666667
|
| 215 |
+
VideoStreaming Interface Descriptor:
|
| 216 |
+
bLength 30
|
| 217 |
+
bDescriptorType 36
|
| 218 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 219 |
+
bFrameIndex 4
|
| 220 |
+
bmCapabilities 0x00
|
| 221 |
+
Still image unsupported
|
| 222 |
+
wWidth 176
|
| 223 |
+
wHeight 144
|
| 224 |
+
dwMinBitRate 196608000
|
| 225 |
+
dwMaxBitRate 196608000
|
| 226 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 227 |
+
dwDefaultFrameInterval 666667
|
| 228 |
+
bFrameIntervalType 1
|
| 229 |
+
dwFrameInterval( 0) 666667
|
| 230 |
+
VideoStreaming Interface Descriptor:
|
| 231 |
+
bLength 30
|
| 232 |
+
bDescriptorType 36
|
| 233 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 234 |
+
bFrameIndex 5
|
| 235 |
+
bmCapabilities 0x00
|
| 236 |
+
Still image unsupported
|
| 237 |
+
wWidth 160
|
| 238 |
+
wHeight 120
|
| 239 |
+
dwMinBitRate 196608000
|
| 240 |
+
dwMaxBitRate 196608000
|
| 241 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 242 |
+
dwDefaultFrameInterval 666667
|
| 243 |
+
bFrameIntervalType 1
|
| 244 |
+
dwFrameInterval( 0) 666667
|
| 245 |
+
VideoStreaming Interface Descriptor:
|
| 246 |
+
bLength 30
|
| 247 |
+
bDescriptorType 36
|
| 248 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 249 |
+
bFrameIndex 6
|
| 250 |
+
bmCapabilities 0x00
|
| 251 |
+
Still image unsupported
|
| 252 |
+
wWidth 800
|
| 253 |
+
wHeight 600
|
| 254 |
+
dwMinBitRate 196608000
|
| 255 |
+
dwMaxBitRate 196608000
|
| 256 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 257 |
+
dwDefaultFrameInterval 666667
|
| 258 |
+
bFrameIntervalType 1
|
| 259 |
+
dwFrameInterval( 0) 666667
|
| 260 |
+
VideoStreaming Interface Descriptor:
|
| 261 |
+
bLength 30
|
| 262 |
+
bDescriptorType 36
|
| 263 |
+
bDescriptorSubtype 3 (STILL_IMAGE_FRAME)
|
| 264 |
+
bEndpointAddress 0
|
| 265 |
+
bNumImageSizePatterns 6
|
| 266 |
+
wWidth( 0) 352
|
| 267 |
+
wHeight( 0) 288
|
| 268 |
+
wWidth( 1) 640
|
| 269 |
+
wHeight( 1) 480
|
| 270 |
+
wWidth( 2) 320
|
| 271 |
+
wHeight( 2) 240
|
| 272 |
+
wWidth( 3) 176
|
| 273 |
+
wHeight( 3) 144
|
| 274 |
+
wWidth( 4) 160
|
| 275 |
+
wHeight( 4) 120
|
| 276 |
+
wWidth( 5) 800
|
| 277 |
+
wHeight( 5) 600
|
| 278 |
+
bNumCompressionPatterns 6
|
| 279 |
+
VideoStreaming Interface Descriptor:
|
| 280 |
+
bLength 6
|
| 281 |
+
bDescriptorType 36
|
| 282 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 283 |
+
bColorPrimaries 0 (Unspecified)
|
| 284 |
+
bTransferCharacteristics 0 (Unspecified)
|
| 285 |
+
bMatrixCoefficients 0 (Unspecified)
|
| 286 |
+
VideoStreaming Interface Descriptor:
|
| 287 |
+
bLength 11
|
| 288 |
+
bDescriptorType 36
|
| 289 |
+
bDescriptorSubtype 6 (FORMAT_MJPEG)
|
| 290 |
+
bFormatIndex 2
|
| 291 |
+
bNumFrameDescriptors 9
|
| 292 |
+
bFlags 1
|
| 293 |
+
Fixed-size samples: Yes
|
| 294 |
+
bDefaultFrameIndex 1
|
| 295 |
+
bAspectRatioX 0
|
| 296 |
+
bAspectRatioY 0
|
| 297 |
+
bmInterlaceFlags 0x00
|
| 298 |
+
Interlaced stream or variable: No
|
| 299 |
+
Fields per frame: 1 fields
|
| 300 |
+
Field 1 first: No
|
| 301 |
+
Field pattern: Field 1 only
|
| 302 |
+
bCopyProtect 0
|
| 303 |
+
VideoStreaming Interface Descriptor:
|
| 304 |
+
bLength 30
|
| 305 |
+
bDescriptorType 36
|
| 306 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 307 |
+
bFrameIndex 1
|
| 308 |
+
bmCapabilities 0x00
|
| 309 |
+
Still image unsupported
|
| 310 |
+
wWidth 352
|
| 311 |
+
wHeight 288
|
| 312 |
+
dwMinBitRate 196608000
|
| 313 |
+
dwMaxBitRate 196608000
|
| 314 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 315 |
+
dwDefaultFrameInterval 666667
|
| 316 |
+
bFrameIntervalType 1
|
| 317 |
+
dwFrameInterval( 0) 666667
|
| 318 |
+
VideoStreaming Interface Descriptor:
|
| 319 |
+
bLength 30
|
| 320 |
+
bDescriptorType 36
|
| 321 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 322 |
+
bFrameIndex 2
|
| 323 |
+
bmCapabilities 0x00
|
| 324 |
+
Still image unsupported
|
| 325 |
+
wWidth 640
|
| 326 |
+
wHeight 480
|
| 327 |
+
dwMinBitRate 196608000
|
| 328 |
+
dwMaxBitRate 196608000
|
| 329 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 330 |
+
dwDefaultFrameInterval 666667
|
| 331 |
+
bFrameIntervalType 1
|
| 332 |
+
dwFrameInterval( 0) 666667
|
| 333 |
+
VideoStreaming Interface Descriptor:
|
| 334 |
+
bLength 30
|
| 335 |
+
bDescriptorType 36
|
| 336 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 337 |
+
bFrameIndex 3
|
| 338 |
+
bmCapabilities 0x00
|
| 339 |
+
Still image unsupported
|
| 340 |
+
wWidth 320
|
| 341 |
+
wHeight 240
|
| 342 |
+
dwMinBitRate 196608000
|
| 343 |
+
dwMaxBitRate 196608000
|
| 344 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 345 |
+
dwDefaultFrameInterval 666667
|
| 346 |
+
bFrameIntervalType 1
|
| 347 |
+
dwFrameInterval( 0) 666667
|
| 348 |
+
VideoStreaming Interface Descriptor:
|
| 349 |
+
bLength 30
|
| 350 |
+
bDescriptorType 36
|
| 351 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 352 |
+
bFrameIndex 4
|
| 353 |
+
bmCapabilities 0x00
|
| 354 |
+
Still image unsupported
|
| 355 |
+
wWidth 176
|
| 356 |
+
wHeight 144
|
| 357 |
+
dwMinBitRate 196608000
|
| 358 |
+
dwMaxBitRate 196608000
|
| 359 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 360 |
+
dwDefaultFrameInterval 666667
|
| 361 |
+
bFrameIntervalType 1
|
| 362 |
+
dwFrameInterval( 0) 666667
|
| 363 |
+
VideoStreaming Interface Descriptor:
|
| 364 |
+
bLength 30
|
| 365 |
+
bDescriptorType 36
|
| 366 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 367 |
+
bFrameIndex 5
|
| 368 |
+
bmCapabilities 0x00
|
| 369 |
+
Still image unsupported
|
| 370 |
+
wWidth 160
|
| 371 |
+
wHeight 120
|
| 372 |
+
dwMinBitRate 196608000
|
| 373 |
+
dwMaxBitRate 196608000
|
| 374 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 375 |
+
dwDefaultFrameInterval 666667
|
| 376 |
+
bFrameIntervalType 1
|
| 377 |
+
dwFrameInterval( 0) 666667
|
| 378 |
+
VideoStreaming Interface Descriptor:
|
| 379 |
+
bLength 30
|
| 380 |
+
bDescriptorType 36
|
| 381 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 382 |
+
bFrameIndex 6
|
| 383 |
+
bmCapabilities 0x00
|
| 384 |
+
Still image unsupported
|
| 385 |
+
wWidth 800
|
| 386 |
+
wHeight 600
|
| 387 |
+
dwMinBitRate 196608000
|
| 388 |
+
dwMaxBitRate 196608000
|
| 389 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 390 |
+
dwDefaultFrameInterval 666667
|
| 391 |
+
bFrameIntervalType 1
|
| 392 |
+
dwFrameInterval( 0) 666667
|
| 393 |
+
VideoStreaming Interface Descriptor:
|
| 394 |
+
bLength 30
|
| 395 |
+
bDescriptorType 36
|
| 396 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 397 |
+
bFrameIndex 7
|
| 398 |
+
bmCapabilities 0x00
|
| 399 |
+
Still image unsupported
|
| 400 |
+
wWidth 1024
|
| 401 |
+
wHeight 768
|
| 402 |
+
dwMinBitRate 196608000
|
| 403 |
+
dwMaxBitRate 196608000
|
| 404 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 405 |
+
dwDefaultFrameInterval 1333333
|
| 406 |
+
bFrameIntervalType 1
|
| 407 |
+
dwFrameInterval( 0) 1333333
|
| 408 |
+
VideoStreaming Interface Descriptor:
|
| 409 |
+
bLength 30
|
| 410 |
+
bDescriptorType 36
|
| 411 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 412 |
+
bFrameIndex 8
|
| 413 |
+
bmCapabilities 0x00
|
| 414 |
+
Still image unsupported
|
| 415 |
+
wWidth 1280
|
| 416 |
+
wHeight 960
|
| 417 |
+
dwMinBitRate 196608000
|
| 418 |
+
dwMaxBitRate 196608000
|
| 419 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 420 |
+
dwDefaultFrameInterval 1333333
|
| 421 |
+
bFrameIntervalType 1
|
| 422 |
+
dwFrameInterval( 0) 1333333
|
| 423 |
+
VideoStreaming Interface Descriptor:
|
| 424 |
+
bLength 30
|
| 425 |
+
bDescriptorType 36
|
| 426 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 427 |
+
bFrameIndex 9
|
| 428 |
+
bmCapabilities 0x00
|
| 429 |
+
Still image unsupported
|
| 430 |
+
wWidth 1600
|
| 431 |
+
wHeight 1200
|
| 432 |
+
dwMinBitRate 196608000
|
| 433 |
+
dwMaxBitRate 196608000
|
| 434 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 435 |
+
dwDefaultFrameInterval 1333333
|
| 436 |
+
bFrameIntervalType 1
|
| 437 |
+
dwFrameInterval( 0) 1333333
|
| 438 |
+
VideoStreaming Interface Descriptor:
|
| 439 |
+
bLength 42
|
| 440 |
+
bDescriptorType 36
|
| 441 |
+
bDescriptorSubtype 3 (STILL_IMAGE_FRAME)
|
| 442 |
+
bEndpointAddress 0
|
| 443 |
+
bNumImageSizePatterns 9
|
| 444 |
+
wWidth( 0) 352
|
| 445 |
+
wHeight( 0) 288
|
| 446 |
+
wWidth( 1) 640
|
| 447 |
+
wHeight( 1) 480
|
| 448 |
+
wWidth( 2) 320
|
| 449 |
+
wHeight( 2) 240
|
| 450 |
+
wWidth( 3) 176
|
| 451 |
+
wHeight( 3) 144
|
| 452 |
+
wWidth( 4) 160
|
| 453 |
+
wHeight( 4) 120
|
| 454 |
+
wWidth( 5) 800
|
| 455 |
+
wHeight( 5) 600
|
| 456 |
+
wWidth( 6) 1024
|
| 457 |
+
wHeight( 6) 768
|
| 458 |
+
wWidth( 7) 1280
|
| 459 |
+
wHeight( 7) 960
|
| 460 |
+
wWidth( 8) 1600
|
| 461 |
+
wHeight( 8) 1200
|
| 462 |
+
bNumCompressionPatterns 9
|
| 463 |
+
VideoStreaming Interface Descriptor:
|
| 464 |
+
bLength 6
|
| 465 |
+
bDescriptorType 36
|
| 466 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 467 |
+
bColorPrimaries 0 (Unspecified)
|
| 468 |
+
bTransferCharacteristics 0 (Unspecified)
|
| 469 |
+
bMatrixCoefficients 0 (Unspecified)
|
| 470 |
+
Interface Descriptor:
|
| 471 |
+
bLength 9
|
| 472 |
+
bDescriptorType 4
|
| 473 |
+
bInterfaceNumber 1
|
| 474 |
+
bAlternateSetting 1
|
| 475 |
+
bNumEndpoints 1
|
| 476 |
+
bInterfaceClass 14 Video
|
| 477 |
+
bInterfaceSubClass 2 Video Streaming
|
| 478 |
+
bInterfaceProtocol 0
|
| 479 |
+
iInterface 0
|
| 480 |
+
Endpoint Descriptor:
|
| 481 |
+
bLength 7
|
| 482 |
+
bDescriptorType 5
|
| 483 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 484 |
+
bmAttributes 5
|
| 485 |
+
Transfer Type Isochronous
|
| 486 |
+
Synch Type Asynchronous
|
| 487 |
+
Usage Type Data
|
| 488 |
+
wMaxPacketSize 0x0080 1x 128 bytes
|
| 489 |
+
bInterval 1
|
| 490 |
+
Interface Descriptor:
|
| 491 |
+
bLength 9
|
| 492 |
+
bDescriptorType 4
|
| 493 |
+
bInterfaceNumber 1
|
| 494 |
+
bAlternateSetting 2
|
| 495 |
+
bNumEndpoints 1
|
| 496 |
+
bInterfaceClass 14 Video
|
| 497 |
+
bInterfaceSubClass 2 Video Streaming
|
| 498 |
+
bInterfaceProtocol 0
|
| 499 |
+
iInterface 0
|
| 500 |
+
Endpoint Descriptor:
|
| 501 |
+
bLength 7
|
| 502 |
+
bDescriptorType 5
|
| 503 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 504 |
+
bmAttributes 5
|
| 505 |
+
Transfer Type Isochronous
|
| 506 |
+
Synch Type Asynchronous
|
| 507 |
+
Usage Type Data
|
| 508 |
+
wMaxPacketSize 0x0200 1x 512 bytes
|
| 509 |
+
bInterval 1
|
| 510 |
+
Interface Descriptor:
|
| 511 |
+
bLength 9
|
| 512 |
+
bDescriptorType 4
|
| 513 |
+
bInterfaceNumber 1
|
| 514 |
+
bAlternateSetting 3
|
| 515 |
+
bNumEndpoints 1
|
| 516 |
+
bInterfaceClass 14 Video
|
| 517 |
+
bInterfaceSubClass 2 Video Streaming
|
| 518 |
+
bInterfaceProtocol 0
|
| 519 |
+
iInterface 0
|
| 520 |
+
Endpoint Descriptor:
|
| 521 |
+
bLength 7
|
| 522 |
+
bDescriptorType 5
|
| 523 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 524 |
+
bmAttributes 5
|
| 525 |
+
Transfer Type Isochronous
|
| 526 |
+
Synch Type Asynchronous
|
| 527 |
+
Usage Type Data
|
| 528 |
+
wMaxPacketSize 0x0400 1x 1024 bytes
|
| 529 |
+
bInterval 1
|
| 530 |
+
Interface Descriptor:
|
| 531 |
+
bLength 9
|
| 532 |
+
bDescriptorType 4
|
| 533 |
+
bInterfaceNumber 1
|
| 534 |
+
bAlternateSetting 4
|
| 535 |
+
bNumEndpoints 1
|
| 536 |
+
bInterfaceClass 14 Video
|
| 537 |
+
bInterfaceSubClass 2 Video Streaming
|
| 538 |
+
bInterfaceProtocol 0
|
| 539 |
+
iInterface 0
|
| 540 |
+
Endpoint Descriptor:
|
| 541 |
+
bLength 7
|
| 542 |
+
bDescriptorType 5
|
| 543 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 544 |
+
bmAttributes 5
|
| 545 |
+
Transfer Type Isochronous
|
| 546 |
+
Synch Type Asynchronous
|
| 547 |
+
Usage Type Data
|
| 548 |
+
wMaxPacketSize 0x0b00 2x 768 bytes
|
| 549 |
+
bInterval 1
|
| 550 |
+
Interface Descriptor:
|
| 551 |
+
bLength 9
|
| 552 |
+
bDescriptorType 4
|
| 553 |
+
bInterfaceNumber 1
|
| 554 |
+
bAlternateSetting 5
|
| 555 |
+
bNumEndpoints 1
|
| 556 |
+
bInterfaceClass 14 Video
|
| 557 |
+
bInterfaceSubClass 2 Video Streaming
|
| 558 |
+
bInterfaceProtocol 0
|
| 559 |
+
iInterface 0
|
| 560 |
+
Endpoint Descriptor:
|
| 561 |
+
bLength 7
|
| 562 |
+
bDescriptorType 5
|
| 563 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 564 |
+
bmAttributes 5
|
| 565 |
+
Transfer Type Isochronous
|
| 566 |
+
Synch Type Asynchronous
|
| 567 |
+
Usage Type Data
|
| 568 |
+
wMaxPacketSize 0x0c00 2x 1024 bytes
|
| 569 |
+
bInterval 1
|
| 570 |
+
Interface Descriptor:
|
| 571 |
+
bLength 9
|
| 572 |
+
bDescriptorType 4
|
| 573 |
+
bInterfaceNumber 1
|
| 574 |
+
bAlternateSetting 6
|
| 575 |
+
bNumEndpoints 1
|
| 576 |
+
bInterfaceClass 14 Video
|
| 577 |
+
bInterfaceSubClass 2 Video Streaming
|
| 578 |
+
bInterfaceProtocol 0
|
| 579 |
+
iInterface 0
|
| 580 |
+
Endpoint Descriptor:
|
| 581 |
+
bLength 7
|
| 582 |
+
bDescriptorType 5
|
| 583 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 584 |
+
bmAttributes 5
|
| 585 |
+
Transfer Type Isochronous
|
| 586 |
+
Synch Type Asynchronous
|
| 587 |
+
Usage Type Data
|
| 588 |
+
wMaxPacketSize 0x1380 3x 896 bytes
|
| 589 |
+
bInterval 1
|
| 590 |
+
Interface Descriptor:
|
| 591 |
+
bLength 9
|
| 592 |
+
bDescriptorType 4
|
| 593 |
+
bInterfaceNumber 1
|
| 594 |
+
bAlternateSetting 7
|
| 595 |
+
bNumEndpoints 1
|
| 596 |
+
bInterfaceClass 14 Video
|
| 597 |
+
bInterfaceSubClass 2 Video Streaming
|
| 598 |
+
bInterfaceProtocol 0
|
| 599 |
+
iInterface 0
|
| 600 |
+
Endpoint Descriptor:
|
| 601 |
+
bLength 7
|
| 602 |
+
bDescriptorType 5
|
| 603 |
+
bEndpointAddress 0x82 EP 2 IN
|
| 604 |
+
bmAttributes 5
|
| 605 |
+
Transfer Type Isochronous
|
| 606 |
+
Synch Type Asynchronous
|
| 607 |
+
Usage Type Data
|
| 608 |
+
wMaxPacketSize 0x1400 3x 1024 bytes
|
| 609 |
+
bInterval 1
|
| 610 |
+
Interface Association:
|
| 611 |
+
bLength 8
|
| 612 |
+
bDescriptorType 11
|
| 613 |
+
bFirstInterface 2
|
| 614 |
+
bInterfaceCount 2
|
| 615 |
+
bFunctionClass 1 Audio
|
| 616 |
+
bFunctionSubClass 2 Streaming
|
| 617 |
+
bFunctionProtocol 0
|
| 618 |
+
iFunction 2 Microsoft� LifeCam Show(TM)
|
| 619 |
+
Interface Descriptor:
|
| 620 |
+
bLength 9
|
| 621 |
+
bDescriptorType 4
|
| 622 |
+
bInterfaceNumber 2
|
| 623 |
+
bAlternateSetting 0
|
| 624 |
+
bNumEndpoints 0
|
| 625 |
+
bInterfaceClass 1 Audio
|
| 626 |
+
bInterfaceSubClass 1 Control Device
|
| 627 |
+
bInterfaceProtocol 0
|
| 628 |
+
iInterface 2 Microsoft� LifeCam Show(TM)
|
| 629 |
+
AudioControl Interface Descriptor:
|
| 630 |
+
bLength 9
|
| 631 |
+
bDescriptorType 36
|
| 632 |
+
bDescriptorSubtype 1 (HEADER)
|
| 633 |
+
bcdADC 1.00
|
| 634 |
+
wTotalLength 39
|
| 635 |
+
bInCollection 1
|
| 636 |
+
baInterfaceNr( 0) 3
|
| 637 |
+
AudioControl Interface Descriptor:
|
| 638 |
+
bLength 12
|
| 639 |
+
bDescriptorType 36
|
| 640 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 641 |
+
bTerminalID 1
|
| 642 |
+
wTerminalType 0x0201 Microphone
|
| 643 |
+
bAssocTerminal 0
|
| 644 |
+
bNrChannels 1
|
| 645 |
+
wChannelConfig 0x0000
|
| 646 |
+
iChannelNames 0
|
| 647 |
+
iTerminal 0
|
| 648 |
+
AudioControl Interface Descriptor:
|
| 649 |
+
bLength 9
|
| 650 |
+
bDescriptorType 36
|
| 651 |
+
bDescriptorSubtype 6 (FEATURE_UNIT)
|
| 652 |
+
bUnitID 2
|
| 653 |
+
bSourceID 1
|
| 654 |
+
bControlSize 1
|
| 655 |
+
bmaControls( 0) 0x00
|
| 656 |
+
bmaControls( 1) 0x03
|
| 657 |
+
Mute
|
| 658 |
+
Volume
|
| 659 |
+
iFeature 0
|
| 660 |
+
AudioControl Interface Descriptor:
|
| 661 |
+
bLength 9
|
| 662 |
+
bDescriptorType 36
|
| 663 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 664 |
+
bTerminalID 3
|
| 665 |
+
wTerminalType 0x0101 USB Streaming
|
| 666 |
+
bAssocTerminal 1
|
| 667 |
+
bSourceID 2
|
| 668 |
+
iTerminal 0
|
| 669 |
+
Interface Descriptor:
|
| 670 |
+
bLength 9
|
| 671 |
+
bDescriptorType 4
|
| 672 |
+
bInterfaceNumber 3
|
| 673 |
+
bAlternateSetting 0
|
| 674 |
+
bNumEndpoints 0
|
| 675 |
+
bInterfaceClass 1 Audio
|
| 676 |
+
bInterfaceSubClass 2 Streaming
|
| 677 |
+
bInterfaceProtocol 0
|
| 678 |
+
iInterface 0
|
| 679 |
+
Interface Descriptor:
|
| 680 |
+
bLength 9
|
| 681 |
+
bDescriptorType 4
|
| 682 |
+
bInterfaceNumber 3
|
| 683 |
+
bAlternateSetting 1
|
| 684 |
+
bNumEndpoints 1
|
| 685 |
+
bInterfaceClass 1 Audio
|
| 686 |
+
bInterfaceSubClass 2 Streaming
|
| 687 |
+
bInterfaceProtocol 0
|
| 688 |
+
iInterface 0
|
| 689 |
+
AudioStreaming Interface Descriptor:
|
| 690 |
+
bLength 7
|
| 691 |
+
bDescriptorType 36
|
| 692 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 693 |
+
bTerminalLink 3
|
| 694 |
+
bDelay 1 frames
|
| 695 |
+
wFormatTag 1 PCM
|
| 696 |
+
AudioStreaming Interface Descriptor:
|
| 697 |
+
bLength 14
|
| 698 |
+
bDescriptorType 36
|
| 699 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 700 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 701 |
+
bNrChannels 1
|
| 702 |
+
bSubframeSize 2
|
| 703 |
+
bBitResolution 16
|
| 704 |
+
bSamFreqType 2 Discrete
|
| 705 |
+
tSamFreq[ 0] 44100
|
| 706 |
+
tSamFreq[ 1] 48000
|
| 707 |
+
Endpoint Descriptor:
|
| 708 |
+
bLength 9
|
| 709 |
+
bDescriptorType 5
|
| 710 |
+
bEndpointAddress 0x83 EP 3 IN
|
| 711 |
+
bmAttributes 1
|
| 712 |
+
Transfer Type Isochronous
|
| 713 |
+
Synch Type None
|
| 714 |
+
Usage Type Data
|
| 715 |
+
wMaxPacketSize 0x0062 1x 98 bytes
|
| 716 |
+
bInterval 4
|
| 717 |
+
bRefresh 0
|
| 718 |
+
bSynchAddress 0
|
| 719 |
+
AudioControl Endpoint Descriptor:
|
| 720 |
+
bLength 7
|
| 721 |
+
bDescriptorType 37
|
| 722 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 723 |
+
bmAttributes 0x01
|
| 724 |
+
Sampling Frequency
|
| 725 |
+
bLockDelayUnits 0 Undefined
|
| 726 |
+
wLockDelay 0 Undefined
|
| 727 |
+
Interface Descriptor:
|
| 728 |
+
bLength 9
|
| 729 |
+
bDescriptorType 4
|
| 730 |
+
bInterfaceNumber 4
|
| 731 |
+
bAlternateSetting 0
|
| 732 |
+
bNumEndpoints 1
|
| 733 |
+
bInterfaceClass 3 Human Interface Device
|
| 734 |
+
bInterfaceSubClass 1 Boot Interface Subclass
|
| 735 |
+
bInterfaceProtocol 1 Keyboard
|
| 736 |
+
iInterface 0
|
| 737 |
+
HID Device Descriptor:
|
| 738 |
+
bLength 9
|
| 739 |
+
bDescriptorType 33
|
| 740 |
+
bcdHID 1.10
|
| 741 |
+
bCountryCode 0 Not supported
|
| 742 |
+
bNumDescriptors 1
|
| 743 |
+
bDescriptorType 34 Report
|
| 744 |
+
wDescriptorLength 24
|
| 745 |
+
Report Descriptors:
|
| 746 |
+
** UNAVAILABLE **
|
| 747 |
+
Endpoint Descriptor:
|
| 748 |
+
bLength 7
|
| 749 |
+
bDescriptorType 5
|
| 750 |
+
bEndpointAddress 0x85 EP 5 IN
|
| 751 |
+
bmAttributes 3
|
| 752 |
+
Transfer Type Interrupt
|
| 753 |
+
Synch Type None
|
| 754 |
+
Usage Type Data
|
| 755 |
+
wMaxPacketSize 0x0001 1x 1 bytes
|
| 756 |
+
bInterval 10
|
| 757 |
+
Device Qualifier (for other device speed):
|
| 758 |
+
bLength 10
|
| 759 |
+
bDescriptorType 6
|
| 760 |
+
bcdUSB 2.00
|
| 761 |
+
bDeviceClass 239 Miscellaneous Device
|
| 762 |
+
bDeviceSubClass 2 ?
|
| 763 |
+
bDeviceProtocol 1 Interface Association
|
| 764 |
+
bMaxPacketSize0 64
|
| 765 |
+
bNumConfigurations 1
|
| 766 |
+
Device Status: 0x0000
|
| 767 |
+
(Bus Powered)
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/quickcampro9000.txt
ADDED
|
@@ -0,0 +1,1543 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Bus 001 Device 009: ID 046d:0809 Logitech, Inc. Webcam Pro 9000
|
| 3 |
+
Device Descriptor:
|
| 4 |
+
bLength 18
|
| 5 |
+
bDescriptorType 1
|
| 6 |
+
bcdUSB 2.00
|
| 7 |
+
bDeviceClass 239 Miscellaneous Device
|
| 8 |
+
bDeviceSubClass 2 ?
|
| 9 |
+
bDeviceProtocol 1 Interface Association
|
| 10 |
+
bMaxPacketSize0 64
|
| 11 |
+
idVendor 0x046d Logitech, Inc.
|
| 12 |
+
idProduct 0x0809 Webcam Pro 9000
|
| 13 |
+
bcdDevice 0.10
|
| 14 |
+
iManufacturer 0
|
| 15 |
+
iProduct 0
|
| 16 |
+
iSerial 2 XXXXXXXX
|
| 17 |
+
bNumConfigurations 1
|
| 18 |
+
Configuration Descriptor:
|
| 19 |
+
bLength 9
|
| 20 |
+
bDescriptorType 2
|
| 21 |
+
wTotalLength 2589
|
| 22 |
+
bNumInterfaces 4
|
| 23 |
+
bConfigurationValue 1
|
| 24 |
+
iConfiguration 0
|
| 25 |
+
bmAttributes 0x80
|
| 26 |
+
(Bus Powered)
|
| 27 |
+
MaxPower 500mA
|
| 28 |
+
Interface Association:
|
| 29 |
+
bLength 8
|
| 30 |
+
bDescriptorType 11
|
| 31 |
+
bFirstInterface 0
|
| 32 |
+
bInterfaceCount 2
|
| 33 |
+
bFunctionClass 14 Video
|
| 34 |
+
bFunctionSubClass 3 Video Interface Collection
|
| 35 |
+
bFunctionProtocol 0
|
| 36 |
+
iFunction 0
|
| 37 |
+
Interface Descriptor:
|
| 38 |
+
bLength 9
|
| 39 |
+
bDescriptorType 4
|
| 40 |
+
bInterfaceNumber 0
|
| 41 |
+
bAlternateSetting 0
|
| 42 |
+
bNumEndpoints 1
|
| 43 |
+
bInterfaceClass 14 Video
|
| 44 |
+
bInterfaceSubClass 1 Video Control
|
| 45 |
+
bInterfaceProtocol 0
|
| 46 |
+
iInterface 0
|
| 47 |
+
VideoControl Interface Descriptor:
|
| 48 |
+
bLength 13
|
| 49 |
+
bDescriptorType 36
|
| 50 |
+
bDescriptorSubtype 1 (HEADER)
|
| 51 |
+
bcdUVC 1.00
|
| 52 |
+
wTotalLength 245
|
| 53 |
+
dwClockFrequency 48.000000MHz
|
| 54 |
+
bInCollection 1
|
| 55 |
+
baInterfaceNr( 0) 1
|
| 56 |
+
VideoControl Interface Descriptor:
|
| 57 |
+
bLength 18
|
| 58 |
+
bDescriptorType 36
|
| 59 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 60 |
+
bTerminalID 1
|
| 61 |
+
wTerminalType 0x0201 Camera Sensor
|
| 62 |
+
bAssocTerminal 0
|
| 63 |
+
iTerminal 0
|
| 64 |
+
wObjectiveFocalLengthMin 0
|
| 65 |
+
wObjectiveFocalLengthMax 0
|
| 66 |
+
wOcularFocalLength 0
|
| 67 |
+
bControlSize 3
|
| 68 |
+
bmControls 0x0000080e
|
| 69 |
+
Auto-Exposure Mode
|
| 70 |
+
Auto-Exposure Priority
|
| 71 |
+
Exposure Time (Absolute)
|
| 72 |
+
PanTilt (Absolute)
|
| 73 |
+
VideoControl Interface Descriptor:
|
| 74 |
+
bLength 11
|
| 75 |
+
bDescriptorType 36
|
| 76 |
+
bDescriptorSubtype 5 (PROCESSING_UNIT)
|
| 77 |
+
Warning: Descriptor too short
|
| 78 |
+
bUnitID 2
|
| 79 |
+
bSourceID 1
|
| 80 |
+
wMaxMultiplier 16384
|
| 81 |
+
bControlSize 2
|
| 82 |
+
bmControls 0x0000175b
|
| 83 |
+
Brightness
|
| 84 |
+
Contrast
|
| 85 |
+
Saturation
|
| 86 |
+
Sharpness
|
| 87 |
+
White Balance Temperature
|
| 88 |
+
Backlight Compensation
|
| 89 |
+
Gain
|
| 90 |
+
Power Line Frequency
|
| 91 |
+
White Balance Temperature, Auto
|
| 92 |
+
iProcessing 0
|
| 93 |
+
bmVideoStandards 0x1b
|
| 94 |
+
None
|
| 95 |
+
NTSC - 525/60
|
| 96 |
+
SECAM - 625/50
|
| 97 |
+
NTSC - 625/50
|
| 98 |
+
VideoControl Interface Descriptor:
|
| 99 |
+
bLength 27
|
| 100 |
+
bDescriptorType 36
|
| 101 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 102 |
+
bUnitID 4
|
| 103 |
+
guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d221e}
|
| 104 |
+
bNumControl 10
|
| 105 |
+
bNrPins 1
|
| 106 |
+
baSourceID( 0) 2
|
| 107 |
+
bControlSize 2
|
| 108 |
+
bmControls( 0) 0xff
|
| 109 |
+
bmControls( 1) 0x03
|
| 110 |
+
iExtension 0
|
| 111 |
+
VideoControl Interface Descriptor:
|
| 112 |
+
bLength 27
|
| 113 |
+
bDescriptorType 36
|
| 114 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 115 |
+
bUnitID 13
|
| 116 |
+
guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d221f}
|
| 117 |
+
bNumControl 7
|
| 118 |
+
bNrPins 1
|
| 119 |
+
baSourceID( 0) 2
|
| 120 |
+
bControlSize 2
|
| 121 |
+
bmControls( 0) 0x6f
|
| 122 |
+
bmControls( 1) 0x01
|
| 123 |
+
iExtension 0
|
| 124 |
+
VideoControl Interface Descriptor:
|
| 125 |
+
bLength 28
|
| 126 |
+
bDescriptorType 36
|
| 127 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 128 |
+
bUnitID 8
|
| 129 |
+
guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d2251}
|
| 130 |
+
bNumControl 3
|
| 131 |
+
bNrPins 1
|
| 132 |
+
baSourceID( 0) 4
|
| 133 |
+
bControlSize 3
|
| 134 |
+
bmControls( 0) 0x19
|
| 135 |
+
bmControls( 1) 0x00
|
| 136 |
+
bmControls( 2) 0x00
|
| 137 |
+
iExtension 0
|
| 138 |
+
VideoControl Interface Descriptor:
|
| 139 |
+
bLength 28
|
| 140 |
+
bDescriptorType 36
|
| 141 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 142 |
+
bUnitID 10
|
| 143 |
+
guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d2252}
|
| 144 |
+
bNumControl 24
|
| 145 |
+
bNrPins 1
|
| 146 |
+
baSourceID( 0) 4
|
| 147 |
+
bControlSize 3
|
| 148 |
+
bmControls( 0) 0xff
|
| 149 |
+
bmControls( 1) 0xff
|
| 150 |
+
bmControls( 2) 0xff
|
| 151 |
+
iExtension 0
|
| 152 |
+
VideoControl Interface Descriptor:
|
| 153 |
+
bLength 28
|
| 154 |
+
bDescriptorType 36
|
| 155 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 156 |
+
bUnitID 14
|
| 157 |
+
guidExtensionCode {b7935ba4-15c7-0245-90f4-532a3b311365}
|
| 158 |
+
bNumControl 4
|
| 159 |
+
bNrPins 1
|
| 160 |
+
baSourceID( 0) 1
|
| 161 |
+
bControlSize 3
|
| 162 |
+
bmControls( 0) 0x0f
|
| 163 |
+
bmControls( 1) 0x00
|
| 164 |
+
bmControls( 2) 0x00
|
| 165 |
+
iExtension 0
|
| 166 |
+
VideoControl Interface Descriptor:
|
| 167 |
+
bLength 28
|
| 168 |
+
bDescriptorType 36
|
| 169 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 170 |
+
bUnitID 9
|
| 171 |
+
guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d2256}
|
| 172 |
+
bNumControl 5
|
| 173 |
+
bNrPins 1
|
| 174 |
+
baSourceID( 0) 4
|
| 175 |
+
bControlSize 3
|
| 176 |
+
bmControls( 0) 0x0c
|
| 177 |
+
bmControls( 1) 0x00
|
| 178 |
+
bmControls( 2) 0x00
|
| 179 |
+
iExtension 0
|
| 180 |
+
VideoControl Interface Descriptor:
|
| 181 |
+
bLength 28
|
| 182 |
+
bDescriptorType 36
|
| 183 |
+
bDescriptorSubtype 6 (EXTENSION_UNIT)
|
| 184 |
+
bUnitID 12
|
| 185 |
+
guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d2250}
|
| 186 |
+
bNumControl 17
|
| 187 |
+
bNrPins 1
|
| 188 |
+
baSourceID( 0) 4
|
| 189 |
+
bControlSize 3
|
| 190 |
+
bmControls( 0) 0xfe
|
| 191 |
+
bmControls( 1) 0x7f
|
| 192 |
+
bmControls( 2) 0x70
|
| 193 |
+
iExtension 0
|
| 194 |
+
VideoControl Interface Descriptor:
|
| 195 |
+
bLength 9
|
| 196 |
+
bDescriptorType 36
|
| 197 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 198 |
+
bTerminalID 5
|
| 199 |
+
wTerminalType 0x0101 USB Streaming
|
| 200 |
+
bAssocTerminal 0
|
| 201 |
+
bSourceID 4
|
| 202 |
+
iTerminal 0
|
| 203 |
+
** UNRECOGNIZED: 20 41 01 0b 82 06 61 63 70 50 ab 49 b8 cc b3 85 5e 8d 22 55 01 01 04 03 01 00 00 00 00 00 00 00
|
| 204 |
+
Endpoint Descriptor:
|
| 205 |
+
bLength 7
|
| 206 |
+
bDescriptorType 5
|
| 207 |
+
bEndpointAddress 0x87 EP 7 IN
|
| 208 |
+
bmAttributes 3
|
| 209 |
+
Transfer Type Interrupt
|
| 210 |
+
Synch Type None
|
| 211 |
+
Usage Type Data
|
| 212 |
+
wMaxPacketSize 0x0010 1x 16 bytes
|
| 213 |
+
bInterval 8
|
| 214 |
+
Interface Descriptor:
|
| 215 |
+
bLength 9
|
| 216 |
+
bDescriptorType 4
|
| 217 |
+
bInterfaceNumber 1
|
| 218 |
+
bAlternateSetting 0
|
| 219 |
+
bNumEndpoints 0
|
| 220 |
+
bInterfaceClass 14 Video
|
| 221 |
+
bInterfaceSubClass 2 Video Streaming
|
| 222 |
+
bInterfaceProtocol 0
|
| 223 |
+
iInterface 0
|
| 224 |
+
VideoStreaming Interface Descriptor:
|
| 225 |
+
bLength 16
|
| 226 |
+
bDescriptorType 36
|
| 227 |
+
bDescriptorSubtype 1 (INPUT_HEADER)
|
| 228 |
+
bNumFormats 3
|
| 229 |
+
wTotalLength 1852
|
| 230 |
+
bEndPointAddress 129
|
| 231 |
+
bmInfo 0
|
| 232 |
+
bTerminalLink 5
|
| 233 |
+
bStillCaptureMethod 2
|
| 234 |
+
bTriggerSupport 1
|
| 235 |
+
bTriggerUsage 0
|
| 236 |
+
bControlSize 1
|
| 237 |
+
bmaControls( 0) 27
|
| 238 |
+
bmaControls( 1) 27
|
| 239 |
+
bmaControls( 2) 27
|
| 240 |
+
VideoStreaming Interface Descriptor:
|
| 241 |
+
bLength 27
|
| 242 |
+
bDescriptorType 36
|
| 243 |
+
bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)
|
| 244 |
+
bFormatIndex 1
|
| 245 |
+
bNumFrameDescriptors 18
|
| 246 |
+
guidFormat {59555932-0000-1000-8000-00aa00389b71}
|
| 247 |
+
bBitsPerPixel 16
|
| 248 |
+
bDefaultFrameIndex 1
|
| 249 |
+
bAspectRatioX 0
|
| 250 |
+
bAspectRatioY 0
|
| 251 |
+
bmInterlaceFlags 0x00
|
| 252 |
+
Interlaced stream or variable: No
|
| 253 |
+
Fields per frame: 1 fields
|
| 254 |
+
Field 1 first: No
|
| 255 |
+
Field pattern: Field 1 only
|
| 256 |
+
bCopyProtect 0
|
| 257 |
+
VideoStreaming Interface Descriptor:
|
| 258 |
+
bLength 50
|
| 259 |
+
bDescriptorType 36
|
| 260 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 261 |
+
bFrameIndex 1
|
| 262 |
+
bmCapabilities 0x00
|
| 263 |
+
Still image unsupported
|
| 264 |
+
wWidth 640
|
| 265 |
+
wHeight 480
|
| 266 |
+
dwMinBitRate 24576000
|
| 267 |
+
dwMaxBitRate 147456000
|
| 268 |
+
dwMaxVideoFrameBufferSize 614400
|
| 269 |
+
dwDefaultFrameInterval 333333
|
| 270 |
+
bFrameIntervalType 6
|
| 271 |
+
dwFrameInterval( 0) 333333
|
| 272 |
+
dwFrameInterval( 1) 400000
|
| 273 |
+
dwFrameInterval( 2) 500000
|
| 274 |
+
dwFrameInterval( 3) 666666
|
| 275 |
+
dwFrameInterval( 4) 1000000
|
| 276 |
+
dwFrameInterval( 5) 2000000
|
| 277 |
+
VideoStreaming Interface Descriptor:
|
| 278 |
+
bLength 50
|
| 279 |
+
bDescriptorType 36
|
| 280 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 281 |
+
bFrameIndex 2
|
| 282 |
+
bmCapabilities 0x00
|
| 283 |
+
Still image unsupported
|
| 284 |
+
wWidth 160
|
| 285 |
+
wHeight 120
|
| 286 |
+
dwMinBitRate 1536000
|
| 287 |
+
dwMaxBitRate 9216000
|
| 288 |
+
dwMaxVideoFrameBufferSize 38400
|
| 289 |
+
dwDefaultFrameInterval 333333
|
| 290 |
+
bFrameIntervalType 6
|
| 291 |
+
dwFrameInterval( 0) 333333
|
| 292 |
+
dwFrameInterval( 1) 400000
|
| 293 |
+
dwFrameInterval( 2) 500000
|
| 294 |
+
dwFrameInterval( 3) 666666
|
| 295 |
+
dwFrameInterval( 4) 1000000
|
| 296 |
+
dwFrameInterval( 5) 2000000
|
| 297 |
+
VideoStreaming Interface Descriptor:
|
| 298 |
+
bLength 50
|
| 299 |
+
bDescriptorType 36
|
| 300 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 301 |
+
bFrameIndex 3
|
| 302 |
+
bmCapabilities 0x00
|
| 303 |
+
Still image unsupported
|
| 304 |
+
wWidth 176
|
| 305 |
+
wHeight 144
|
| 306 |
+
dwMinBitRate 2027520
|
| 307 |
+
dwMaxBitRate 12165120
|
| 308 |
+
dwMaxVideoFrameBufferSize 50688
|
| 309 |
+
dwDefaultFrameInterval 333333
|
| 310 |
+
bFrameIntervalType 6
|
| 311 |
+
dwFrameInterval( 0) 333333
|
| 312 |
+
dwFrameInterval( 1) 400000
|
| 313 |
+
dwFrameInterval( 2) 500000
|
| 314 |
+
dwFrameInterval( 3) 666666
|
| 315 |
+
dwFrameInterval( 4) 1000000
|
| 316 |
+
dwFrameInterval( 5) 2000000
|
| 317 |
+
VideoStreaming Interface Descriptor:
|
| 318 |
+
bLength 50
|
| 319 |
+
bDescriptorType 36
|
| 320 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 321 |
+
bFrameIndex 4
|
| 322 |
+
bmCapabilities 0x00
|
| 323 |
+
Still image unsupported
|
| 324 |
+
wWidth 320
|
| 325 |
+
wHeight 240
|
| 326 |
+
dwMinBitRate 6144000
|
| 327 |
+
dwMaxBitRate 36864000
|
| 328 |
+
dwMaxVideoFrameBufferSize 153600
|
| 329 |
+
dwDefaultFrameInterval 333333
|
| 330 |
+
bFrameIntervalType 6
|
| 331 |
+
dwFrameInterval( 0) 333333
|
| 332 |
+
dwFrameInterval( 1) 400000
|
| 333 |
+
dwFrameInterval( 2) 500000
|
| 334 |
+
dwFrameInterval( 3) 666666
|
| 335 |
+
dwFrameInterval( 4) 1000000
|
| 336 |
+
dwFrameInterval( 5) 2000000
|
| 337 |
+
VideoStreaming Interface Descriptor:
|
| 338 |
+
bLength 50
|
| 339 |
+
bDescriptorType 36
|
| 340 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 341 |
+
bFrameIndex 5
|
| 342 |
+
bmCapabilities 0x00
|
| 343 |
+
Still image unsupported
|
| 344 |
+
wWidth 352
|
| 345 |
+
wHeight 288
|
| 346 |
+
dwMinBitRate 8110080
|
| 347 |
+
dwMaxBitRate 48660480
|
| 348 |
+
dwMaxVideoFrameBufferSize 202752
|
| 349 |
+
dwDefaultFrameInterval 333333
|
| 350 |
+
bFrameIntervalType 6
|
| 351 |
+
dwFrameInterval( 0) 333333
|
| 352 |
+
dwFrameInterval( 1) 400000
|
| 353 |
+
dwFrameInterval( 2) 500000
|
| 354 |
+
dwFrameInterval( 3) 666666
|
| 355 |
+
dwFrameInterval( 4) 1000000
|
| 356 |
+
dwFrameInterval( 5) 2000000
|
| 357 |
+
VideoStreaming Interface Descriptor:
|
| 358 |
+
bLength 50
|
| 359 |
+
bDescriptorType 36
|
| 360 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 361 |
+
bFrameIndex 6
|
| 362 |
+
bmCapabilities 0x00
|
| 363 |
+
Still image unsupported
|
| 364 |
+
wWidth 640
|
| 365 |
+
wHeight 360
|
| 366 |
+
dwMinBitRate 18432000
|
| 367 |
+
dwMaxBitRate 110592000
|
| 368 |
+
dwMaxVideoFrameBufferSize 460800
|
| 369 |
+
dwDefaultFrameInterval 333333
|
| 370 |
+
bFrameIntervalType 6
|
| 371 |
+
dwFrameInterval( 0) 333333
|
| 372 |
+
dwFrameInterval( 1) 400000
|
| 373 |
+
dwFrameInterval( 2) 500000
|
| 374 |
+
dwFrameInterval( 3) 666666
|
| 375 |
+
dwFrameInterval( 4) 1000000
|
| 376 |
+
dwFrameInterval( 5) 2000000
|
| 377 |
+
VideoStreaming Interface Descriptor:
|
| 378 |
+
bLength 50
|
| 379 |
+
bDescriptorType 36
|
| 380 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 381 |
+
bFrameIndex 7
|
| 382 |
+
bmCapabilities 0x00
|
| 383 |
+
Still image unsupported
|
| 384 |
+
wWidth 640
|
| 385 |
+
wHeight 400
|
| 386 |
+
dwMinBitRate 20480000
|
| 387 |
+
dwMaxBitRate 122880000
|
| 388 |
+
dwMaxVideoFrameBufferSize 512000
|
| 389 |
+
dwDefaultFrameInterval 333333
|
| 390 |
+
bFrameIntervalType 6
|
| 391 |
+
dwFrameInterval( 0) 333333
|
| 392 |
+
dwFrameInterval( 1) 400000
|
| 393 |
+
dwFrameInterval( 2) 500000
|
| 394 |
+
dwFrameInterval( 3) 666666
|
| 395 |
+
dwFrameInterval( 4) 1000000
|
| 396 |
+
dwFrameInterval( 5) 2000000
|
| 397 |
+
VideoStreaming Interface Descriptor:
|
| 398 |
+
bLength 46
|
| 399 |
+
bDescriptorType 36
|
| 400 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 401 |
+
bFrameIndex 8
|
| 402 |
+
bmCapabilities 0x00
|
| 403 |
+
Still image unsupported
|
| 404 |
+
wWidth 768
|
| 405 |
+
wHeight 480
|
| 406 |
+
dwMinBitRate 29491200
|
| 407 |
+
dwMaxBitRate 147456000
|
| 408 |
+
dwMaxVideoFrameBufferSize 737280
|
| 409 |
+
dwDefaultFrameInterval 400000
|
| 410 |
+
bFrameIntervalType 5
|
| 411 |
+
dwFrameInterval( 0) 400000
|
| 412 |
+
dwFrameInterval( 1) 500000
|
| 413 |
+
dwFrameInterval( 2) 666666
|
| 414 |
+
dwFrameInterval( 3) 1000000
|
| 415 |
+
dwFrameInterval( 4) 2000000
|
| 416 |
+
VideoStreaming Interface Descriptor:
|
| 417 |
+
bLength 46
|
| 418 |
+
bDescriptorType 36
|
| 419 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 420 |
+
bFrameIndex 9
|
| 421 |
+
bmCapabilities 0x00
|
| 422 |
+
Still image unsupported
|
| 423 |
+
wWidth 800
|
| 424 |
+
wHeight 456
|
| 425 |
+
dwMinBitRate 29184000
|
| 426 |
+
dwMaxBitRate 145920000
|
| 427 |
+
dwMaxVideoFrameBufferSize 729600
|
| 428 |
+
dwDefaultFrameInterval 400000
|
| 429 |
+
bFrameIntervalType 5
|
| 430 |
+
dwFrameInterval( 0) 400000
|
| 431 |
+
dwFrameInterval( 1) 500000
|
| 432 |
+
dwFrameInterval( 2) 666666
|
| 433 |
+
dwFrameInterval( 3) 1000000
|
| 434 |
+
dwFrameInterval( 4) 2000000
|
| 435 |
+
VideoStreaming Interface Descriptor:
|
| 436 |
+
bLength 46
|
| 437 |
+
bDescriptorType 36
|
| 438 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 439 |
+
bFrameIndex 10
|
| 440 |
+
bmCapabilities 0x00
|
| 441 |
+
Still image unsupported
|
| 442 |
+
wWidth 800
|
| 443 |
+
wHeight 504
|
| 444 |
+
dwMinBitRate 32256000
|
| 445 |
+
dwMaxBitRate 161280000
|
| 446 |
+
dwMaxVideoFrameBufferSize 806400
|
| 447 |
+
dwDefaultFrameInterval 400000
|
| 448 |
+
bFrameIntervalType 5
|
| 449 |
+
dwFrameInterval( 0) 400000
|
| 450 |
+
dwFrameInterval( 1) 500000
|
| 451 |
+
dwFrameInterval( 2) 666666
|
| 452 |
+
dwFrameInterval( 3) 1000000
|
| 453 |
+
dwFrameInterval( 4) 2000000
|
| 454 |
+
VideoStreaming Interface Descriptor:
|
| 455 |
+
bLength 46
|
| 456 |
+
bDescriptorType 36
|
| 457 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 458 |
+
bFrameIndex 11
|
| 459 |
+
bmCapabilities 0x00
|
| 460 |
+
Still image unsupported
|
| 461 |
+
wWidth 800
|
| 462 |
+
wHeight 600
|
| 463 |
+
dwMinBitRate 38400000
|
| 464 |
+
dwMaxBitRate 192000000
|
| 465 |
+
dwMaxVideoFrameBufferSize 960000
|
| 466 |
+
dwDefaultFrameInterval 400000
|
| 467 |
+
bFrameIntervalType 5
|
| 468 |
+
dwFrameInterval( 0) 400000
|
| 469 |
+
dwFrameInterval( 1) 500000
|
| 470 |
+
dwFrameInterval( 2) 666666
|
| 471 |
+
dwFrameInterval( 3) 1000000
|
| 472 |
+
dwFrameInterval( 4) 2000000
|
| 473 |
+
VideoStreaming Interface Descriptor:
|
| 474 |
+
bLength 46
|
| 475 |
+
bDescriptorType 36
|
| 476 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 477 |
+
bFrameIndex 12
|
| 478 |
+
bmCapabilities 0x00
|
| 479 |
+
Still image unsupported
|
| 480 |
+
wWidth 864
|
| 481 |
+
wHeight 480
|
| 482 |
+
dwMinBitRate 33177600
|
| 483 |
+
dwMaxBitRate 165888000
|
| 484 |
+
dwMaxVideoFrameBufferSize 829440
|
| 485 |
+
dwDefaultFrameInterval 400000
|
| 486 |
+
bFrameIntervalType 5
|
| 487 |
+
dwFrameInterval( 0) 400000
|
| 488 |
+
dwFrameInterval( 1) 500000
|
| 489 |
+
dwFrameInterval( 2) 666666
|
| 490 |
+
dwFrameInterval( 3) 1000000
|
| 491 |
+
dwFrameInterval( 4) 2000000
|
| 492 |
+
VideoStreaming Interface Descriptor:
|
| 493 |
+
bLength 38
|
| 494 |
+
bDescriptorType 36
|
| 495 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 496 |
+
bFrameIndex 13
|
| 497 |
+
bmCapabilities 0x00
|
| 498 |
+
Still image unsupported
|
| 499 |
+
wWidth 960
|
| 500 |
+
wHeight 720
|
| 501 |
+
dwMinBitRate 55296000
|
| 502 |
+
dwMaxBitRate 165888000
|
| 503 |
+
dwMaxVideoFrameBufferSize 1382400
|
| 504 |
+
dwDefaultFrameInterval 1000000
|
| 505 |
+
bFrameIntervalType 3
|
| 506 |
+
dwFrameInterval( 0) 666666
|
| 507 |
+
dwFrameInterval( 1) 1000000
|
| 508 |
+
dwFrameInterval( 2) 2000000
|
| 509 |
+
VideoStreaming Interface Descriptor:
|
| 510 |
+
bLength 34
|
| 511 |
+
bDescriptorType 36
|
| 512 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 513 |
+
bFrameIndex 14
|
| 514 |
+
bmCapabilities 0x00
|
| 515 |
+
Still image unsupported
|
| 516 |
+
wWidth 1280
|
| 517 |
+
wHeight 720
|
| 518 |
+
dwMinBitRate 73728000
|
| 519 |
+
dwMaxBitRate 147456000
|
| 520 |
+
dwMaxVideoFrameBufferSize 1843200
|
| 521 |
+
dwDefaultFrameInterval 2000000
|
| 522 |
+
bFrameIntervalType 2
|
| 523 |
+
dwFrameInterval( 0) 1333333
|
| 524 |
+
dwFrameInterval( 1) 2000000
|
| 525 |
+
VideoStreaming Interface Descriptor:
|
| 526 |
+
bLength 34
|
| 527 |
+
bDescriptorType 36
|
| 528 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 529 |
+
bFrameIndex 15
|
| 530 |
+
bmCapabilities 0x00
|
| 531 |
+
Still image unsupported
|
| 532 |
+
wWidth 1280
|
| 533 |
+
wHeight 800
|
| 534 |
+
dwMinBitRate 81920000
|
| 535 |
+
dwMaxBitRate 163840000
|
| 536 |
+
dwMaxVideoFrameBufferSize 2048000
|
| 537 |
+
dwDefaultFrameInterval 2000000
|
| 538 |
+
bFrameIntervalType 2
|
| 539 |
+
dwFrameInterval( 0) 1333333
|
| 540 |
+
dwFrameInterval( 1) 2000000
|
| 541 |
+
VideoStreaming Interface Descriptor:
|
| 542 |
+
bLength 30
|
| 543 |
+
bDescriptorType 36
|
| 544 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 545 |
+
bFrameIndex 16
|
| 546 |
+
bmCapabilities 0x00
|
| 547 |
+
Still image unsupported
|
| 548 |
+
wWidth 1600
|
| 549 |
+
wHeight 904
|
| 550 |
+
dwMinBitRate 115712000
|
| 551 |
+
dwMaxBitRate 115712000
|
| 552 |
+
dwMaxVideoFrameBufferSize 2892800
|
| 553 |
+
dwDefaultFrameInterval 2000000
|
| 554 |
+
bFrameIntervalType 1
|
| 555 |
+
dwFrameInterval( 0) 2000000
|
| 556 |
+
VideoStreaming Interface Descriptor:
|
| 557 |
+
bLength 30
|
| 558 |
+
bDescriptorType 36
|
| 559 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 560 |
+
bFrameIndex 17
|
| 561 |
+
bmCapabilities 0x00
|
| 562 |
+
Still image unsupported
|
| 563 |
+
wWidth 1600
|
| 564 |
+
wHeight 1000
|
| 565 |
+
dwMinBitRate 128000000
|
| 566 |
+
dwMaxBitRate 128000000
|
| 567 |
+
dwMaxVideoFrameBufferSize 3200000
|
| 568 |
+
dwDefaultFrameInterval 2000000
|
| 569 |
+
bFrameIntervalType 1
|
| 570 |
+
dwFrameInterval( 0) 2000000
|
| 571 |
+
VideoStreaming Interface Descriptor:
|
| 572 |
+
bLength 30
|
| 573 |
+
bDescriptorType 36
|
| 574 |
+
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
|
| 575 |
+
bFrameIndex 18
|
| 576 |
+
bmCapabilities 0x00
|
| 577 |
+
Still image unsupported
|
| 578 |
+
wWidth 1600
|
| 579 |
+
wHeight 1200
|
| 580 |
+
dwMinBitRate 153600000
|
| 581 |
+
dwMaxBitRate 153600000
|
| 582 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 583 |
+
dwDefaultFrameInterval 2000000
|
| 584 |
+
bFrameIntervalType 1
|
| 585 |
+
dwFrameInterval( 0) 2000000
|
| 586 |
+
VideoStreaming Interface Descriptor:
|
| 587 |
+
bLength 79
|
| 588 |
+
bDescriptorType 36
|
| 589 |
+
bDescriptorSubtype 3 (STILL_IMAGE_FRAME)
|
| 590 |
+
bEndpointAddress 0
|
| 591 |
+
bNumImageSizePatterns 18
|
| 592 |
+
wWidth( 0) 640
|
| 593 |
+
wHeight( 0) 480
|
| 594 |
+
wWidth( 1) 160
|
| 595 |
+
wHeight( 1) 120
|
| 596 |
+
wWidth( 2) 176
|
| 597 |
+
wHeight( 2) 144
|
| 598 |
+
wWidth( 3) 320
|
| 599 |
+
wHeight( 3) 240
|
| 600 |
+
wWidth( 4) 352
|
| 601 |
+
wHeight( 4) 288
|
| 602 |
+
wWidth( 5) 640
|
| 603 |
+
wHeight( 5) 360
|
| 604 |
+
wWidth( 6) 640
|
| 605 |
+
wHeight( 6) 400
|
| 606 |
+
wWidth( 7) 768
|
| 607 |
+
wHeight( 7) 480
|
| 608 |
+
wWidth( 8) 800
|
| 609 |
+
wHeight( 8) 456
|
| 610 |
+
wWidth( 9) 800
|
| 611 |
+
wHeight( 9) 504
|
| 612 |
+
wWidth(10) 800
|
| 613 |
+
wHeight(10) 600
|
| 614 |
+
wWidth(11) 864
|
| 615 |
+
wHeight(11) 480
|
| 616 |
+
wWidth(12) 960
|
| 617 |
+
wHeight(12) 720
|
| 618 |
+
wWidth(13) 1280
|
| 619 |
+
wHeight(13) 720
|
| 620 |
+
wWidth(14) 1280
|
| 621 |
+
wHeight(14) 800
|
| 622 |
+
wWidth(15) 1600
|
| 623 |
+
wHeight(15) 904
|
| 624 |
+
wWidth(16) 1600
|
| 625 |
+
wHeight(16) 1000
|
| 626 |
+
wWidth(17) 1600
|
| 627 |
+
wHeight(17) 1200
|
| 628 |
+
bNumCompressionPatterns 18
|
| 629 |
+
bCompression( 0) 5
|
| 630 |
+
VideoStreaming Interface Descriptor:
|
| 631 |
+
bLength 6
|
| 632 |
+
bDescriptorType 36
|
| 633 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 634 |
+
bColorPrimaries 1 (BT.709,sRGB)
|
| 635 |
+
bTransferCharacteristics 1 (BT.709)
|
| 636 |
+
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
|
| 637 |
+
VideoStreaming Interface Descriptor:
|
| 638 |
+
bLength 11
|
| 639 |
+
bDescriptorType 36
|
| 640 |
+
bDescriptorSubtype 6 (FORMAT_MJPEG)
|
| 641 |
+
bFormatIndex 2
|
| 642 |
+
bNumFrameDescriptors 18
|
| 643 |
+
bFlags 1
|
| 644 |
+
Fixed-size samples: Yes
|
| 645 |
+
bDefaultFrameIndex 1
|
| 646 |
+
bAspectRatioX 0
|
| 647 |
+
bAspectRatioY 0
|
| 648 |
+
bmInterlaceFlags 0x00
|
| 649 |
+
Interlaced stream or variable: No
|
| 650 |
+
Fields per frame: 1 fields
|
| 651 |
+
Field 1 first: No
|
| 652 |
+
Field pattern: Field 1 only
|
| 653 |
+
bCopyProtect 0
|
| 654 |
+
VideoStreaming Interface Descriptor:
|
| 655 |
+
bLength 50
|
| 656 |
+
bDescriptorType 36
|
| 657 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 658 |
+
bFrameIndex 1
|
| 659 |
+
bmCapabilities 0x00
|
| 660 |
+
Still image unsupported
|
| 661 |
+
wWidth 640
|
| 662 |
+
wHeight 480
|
| 663 |
+
dwMinBitRate 24576000
|
| 664 |
+
dwMaxBitRate 147456000
|
| 665 |
+
dwMaxVideoFrameBufferSize 614400
|
| 666 |
+
dwDefaultFrameInterval 333333
|
| 667 |
+
bFrameIntervalType 6
|
| 668 |
+
dwFrameInterval( 0) 333333
|
| 669 |
+
dwFrameInterval( 1) 400000
|
| 670 |
+
dwFrameInterval( 2) 500000
|
| 671 |
+
dwFrameInterval( 3) 666666
|
| 672 |
+
dwFrameInterval( 4) 1000000
|
| 673 |
+
dwFrameInterval( 5) 2000000
|
| 674 |
+
VideoStreaming Interface Descriptor:
|
| 675 |
+
bLength 50
|
| 676 |
+
bDescriptorType 36
|
| 677 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 678 |
+
bFrameIndex 2
|
| 679 |
+
bmCapabilities 0x00
|
| 680 |
+
Still image unsupported
|
| 681 |
+
wWidth 160
|
| 682 |
+
wHeight 120
|
| 683 |
+
dwMinBitRate 1536000
|
| 684 |
+
dwMaxBitRate 9216000
|
| 685 |
+
dwMaxVideoFrameBufferSize 38400
|
| 686 |
+
dwDefaultFrameInterval 333333
|
| 687 |
+
bFrameIntervalType 6
|
| 688 |
+
dwFrameInterval( 0) 333333
|
| 689 |
+
dwFrameInterval( 1) 400000
|
| 690 |
+
dwFrameInterval( 2) 500000
|
| 691 |
+
dwFrameInterval( 3) 666666
|
| 692 |
+
dwFrameInterval( 4) 1000000
|
| 693 |
+
dwFrameInterval( 5) 2000000
|
| 694 |
+
VideoStreaming Interface Descriptor:
|
| 695 |
+
bLength 50
|
| 696 |
+
bDescriptorType 36
|
| 697 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 698 |
+
bFrameIndex 3
|
| 699 |
+
bmCapabilities 0x00
|
| 700 |
+
Still image unsupported
|
| 701 |
+
wWidth 176
|
| 702 |
+
wHeight 144
|
| 703 |
+
dwMinBitRate 2027520
|
| 704 |
+
dwMaxBitRate 12165120
|
| 705 |
+
dwMaxVideoFrameBufferSize 50688
|
| 706 |
+
dwDefaultFrameInterval 333333
|
| 707 |
+
bFrameIntervalType 6
|
| 708 |
+
dwFrameInterval( 0) 333333
|
| 709 |
+
dwFrameInterval( 1) 400000
|
| 710 |
+
dwFrameInterval( 2) 500000
|
| 711 |
+
dwFrameInterval( 3) 666666
|
| 712 |
+
dwFrameInterval( 4) 1000000
|
| 713 |
+
dwFrameInterval( 5) 2000000
|
| 714 |
+
VideoStreaming Interface Descriptor:
|
| 715 |
+
bLength 50
|
| 716 |
+
bDescriptorType 36
|
| 717 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 718 |
+
bFrameIndex 4
|
| 719 |
+
bmCapabilities 0x00
|
| 720 |
+
Still image unsupported
|
| 721 |
+
wWidth 320
|
| 722 |
+
wHeight 240
|
| 723 |
+
dwMinBitRate 6144000
|
| 724 |
+
dwMaxBitRate 36864000
|
| 725 |
+
dwMaxVideoFrameBufferSize 153600
|
| 726 |
+
dwDefaultFrameInterval 333333
|
| 727 |
+
bFrameIntervalType 6
|
| 728 |
+
dwFrameInterval( 0) 333333
|
| 729 |
+
dwFrameInterval( 1) 400000
|
| 730 |
+
dwFrameInterval( 2) 500000
|
| 731 |
+
dwFrameInterval( 3) 666666
|
| 732 |
+
dwFrameInterval( 4) 1000000
|
| 733 |
+
dwFrameInterval( 5) 2000000
|
| 734 |
+
VideoStreaming Interface Descriptor:
|
| 735 |
+
bLength 50
|
| 736 |
+
bDescriptorType 36
|
| 737 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 738 |
+
bFrameIndex 5
|
| 739 |
+
bmCapabilities 0x00
|
| 740 |
+
Still image unsupported
|
| 741 |
+
wWidth 352
|
| 742 |
+
wHeight 288
|
| 743 |
+
dwMinBitRate 8110080
|
| 744 |
+
dwMaxBitRate 48660480
|
| 745 |
+
dwMaxVideoFrameBufferSize 202752
|
| 746 |
+
dwDefaultFrameInterval 333333
|
| 747 |
+
bFrameIntervalType 6
|
| 748 |
+
dwFrameInterval( 0) 333333
|
| 749 |
+
dwFrameInterval( 1) 400000
|
| 750 |
+
dwFrameInterval( 2) 500000
|
| 751 |
+
dwFrameInterval( 3) 666666
|
| 752 |
+
dwFrameInterval( 4) 1000000
|
| 753 |
+
dwFrameInterval( 5) 2000000
|
| 754 |
+
VideoStreaming Interface Descriptor:
|
| 755 |
+
bLength 50
|
| 756 |
+
bDescriptorType 36
|
| 757 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 758 |
+
bFrameIndex 6
|
| 759 |
+
bmCapabilities 0x00
|
| 760 |
+
Still image unsupported
|
| 761 |
+
wWidth 640
|
| 762 |
+
wHeight 360
|
| 763 |
+
dwMinBitRate 18432000
|
| 764 |
+
dwMaxBitRate 110592000
|
| 765 |
+
dwMaxVideoFrameBufferSize 460800
|
| 766 |
+
dwDefaultFrameInterval 333333
|
| 767 |
+
bFrameIntervalType 6
|
| 768 |
+
dwFrameInterval( 0) 333333
|
| 769 |
+
dwFrameInterval( 1) 400000
|
| 770 |
+
dwFrameInterval( 2) 500000
|
| 771 |
+
dwFrameInterval( 3) 666666
|
| 772 |
+
dwFrameInterval( 4) 1000000
|
| 773 |
+
dwFrameInterval( 5) 2000000
|
| 774 |
+
VideoStreaming Interface Descriptor:
|
| 775 |
+
bLength 50
|
| 776 |
+
bDescriptorType 36
|
| 777 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 778 |
+
bFrameIndex 7
|
| 779 |
+
bmCapabilities 0x00
|
| 780 |
+
Still image unsupported
|
| 781 |
+
wWidth 640
|
| 782 |
+
wHeight 400
|
| 783 |
+
dwMinBitRate 20480000
|
| 784 |
+
dwMaxBitRate 122880000
|
| 785 |
+
dwMaxVideoFrameBufferSize 512000
|
| 786 |
+
dwDefaultFrameInterval 333333
|
| 787 |
+
bFrameIntervalType 6
|
| 788 |
+
dwFrameInterval( 0) 333333
|
| 789 |
+
dwFrameInterval( 1) 400000
|
| 790 |
+
dwFrameInterval( 2) 500000
|
| 791 |
+
dwFrameInterval( 3) 666666
|
| 792 |
+
dwFrameInterval( 4) 1000000
|
| 793 |
+
dwFrameInterval( 5) 2000000
|
| 794 |
+
VideoStreaming Interface Descriptor:
|
| 795 |
+
bLength 50
|
| 796 |
+
bDescriptorType 36
|
| 797 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 798 |
+
bFrameIndex 8
|
| 799 |
+
bmCapabilities 0x00
|
| 800 |
+
Still image unsupported
|
| 801 |
+
wWidth 768
|
| 802 |
+
wHeight 480
|
| 803 |
+
dwMinBitRate 29491200
|
| 804 |
+
dwMaxBitRate 176947200
|
| 805 |
+
dwMaxVideoFrameBufferSize 737280
|
| 806 |
+
dwDefaultFrameInterval 333333
|
| 807 |
+
bFrameIntervalType 6
|
| 808 |
+
dwFrameInterval( 0) 333333
|
| 809 |
+
dwFrameInterval( 1) 400000
|
| 810 |
+
dwFrameInterval( 2) 500000
|
| 811 |
+
dwFrameInterval( 3) 666666
|
| 812 |
+
dwFrameInterval( 4) 1000000
|
| 813 |
+
dwFrameInterval( 5) 2000000
|
| 814 |
+
VideoStreaming Interface Descriptor:
|
| 815 |
+
bLength 50
|
| 816 |
+
bDescriptorType 36
|
| 817 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 818 |
+
bFrameIndex 9
|
| 819 |
+
bmCapabilities 0x00
|
| 820 |
+
Still image unsupported
|
| 821 |
+
wWidth 800
|
| 822 |
+
wHeight 456
|
| 823 |
+
dwMinBitRate 29184000
|
| 824 |
+
dwMaxBitRate 175104000
|
| 825 |
+
dwMaxVideoFrameBufferSize 729600
|
| 826 |
+
dwDefaultFrameInterval 666666
|
| 827 |
+
bFrameIntervalType 6
|
| 828 |
+
dwFrameInterval( 0) 333333
|
| 829 |
+
dwFrameInterval( 1) 400000
|
| 830 |
+
dwFrameInterval( 2) 500000
|
| 831 |
+
dwFrameInterval( 3) 666666
|
| 832 |
+
dwFrameInterval( 4) 1000000
|
| 833 |
+
dwFrameInterval( 5) 2000000
|
| 834 |
+
VideoStreaming Interface Descriptor:
|
| 835 |
+
bLength 50
|
| 836 |
+
bDescriptorType 36
|
| 837 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 838 |
+
bFrameIndex 10
|
| 839 |
+
bmCapabilities 0x00
|
| 840 |
+
Still image unsupported
|
| 841 |
+
wWidth 800
|
| 842 |
+
wHeight 504
|
| 843 |
+
dwMinBitRate 32256000
|
| 844 |
+
dwMaxBitRate 193536000
|
| 845 |
+
dwMaxVideoFrameBufferSize 806400
|
| 846 |
+
dwDefaultFrameInterval 666666
|
| 847 |
+
bFrameIntervalType 6
|
| 848 |
+
dwFrameInterval( 0) 333333
|
| 849 |
+
dwFrameInterval( 1) 400000
|
| 850 |
+
dwFrameInterval( 2) 500000
|
| 851 |
+
dwFrameInterval( 3) 666666
|
| 852 |
+
dwFrameInterval( 4) 1000000
|
| 853 |
+
dwFrameInterval( 5) 2000000
|
| 854 |
+
VideoStreaming Interface Descriptor:
|
| 855 |
+
bLength 50
|
| 856 |
+
bDescriptorType 36
|
| 857 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 858 |
+
bFrameIndex 11
|
| 859 |
+
bmCapabilities 0x00
|
| 860 |
+
Still image unsupported
|
| 861 |
+
wWidth 800
|
| 862 |
+
wHeight 600
|
| 863 |
+
dwMinBitRate 38400000
|
| 864 |
+
dwMaxBitRate 230400000
|
| 865 |
+
dwMaxVideoFrameBufferSize 960000
|
| 866 |
+
dwDefaultFrameInterval 666666
|
| 867 |
+
bFrameIntervalType 6
|
| 868 |
+
dwFrameInterval( 0) 333333
|
| 869 |
+
dwFrameInterval( 1) 400000
|
| 870 |
+
dwFrameInterval( 2) 500000
|
| 871 |
+
dwFrameInterval( 3) 666666
|
| 872 |
+
dwFrameInterval( 4) 1000000
|
| 873 |
+
dwFrameInterval( 5) 2000000
|
| 874 |
+
VideoStreaming Interface Descriptor:
|
| 875 |
+
bLength 50
|
| 876 |
+
bDescriptorType 36
|
| 877 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 878 |
+
bFrameIndex 12
|
| 879 |
+
bmCapabilities 0x00
|
| 880 |
+
Still image unsupported
|
| 881 |
+
wWidth 864
|
| 882 |
+
wHeight 480
|
| 883 |
+
dwMinBitRate 33177600
|
| 884 |
+
dwMaxBitRate 199065600
|
| 885 |
+
dwMaxVideoFrameBufferSize 829440
|
| 886 |
+
dwDefaultFrameInterval 666666
|
| 887 |
+
bFrameIntervalType 6
|
| 888 |
+
dwFrameInterval( 0) 333333
|
| 889 |
+
dwFrameInterval( 1) 400000
|
| 890 |
+
dwFrameInterval( 2) 500000
|
| 891 |
+
dwFrameInterval( 3) 666666
|
| 892 |
+
dwFrameInterval( 4) 1000000
|
| 893 |
+
dwFrameInterval( 5) 2000000
|
| 894 |
+
VideoStreaming Interface Descriptor:
|
| 895 |
+
bLength 50
|
| 896 |
+
bDescriptorType 36
|
| 897 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 898 |
+
bFrameIndex 13
|
| 899 |
+
bmCapabilities 0x00
|
| 900 |
+
Still image unsupported
|
| 901 |
+
wWidth 960
|
| 902 |
+
wHeight 720
|
| 903 |
+
dwMinBitRate 55296000
|
| 904 |
+
dwMaxBitRate 331776000
|
| 905 |
+
dwMaxVideoFrameBufferSize 1382400
|
| 906 |
+
dwDefaultFrameInterval 1000000
|
| 907 |
+
bFrameIntervalType 6
|
| 908 |
+
dwFrameInterval( 0) 333333
|
| 909 |
+
dwFrameInterval( 1) 400000
|
| 910 |
+
dwFrameInterval( 2) 500000
|
| 911 |
+
dwFrameInterval( 3) 666666
|
| 912 |
+
dwFrameInterval( 4) 1000000
|
| 913 |
+
dwFrameInterval( 5) 2000000
|
| 914 |
+
VideoStreaming Interface Descriptor:
|
| 915 |
+
bLength 50
|
| 916 |
+
bDescriptorType 36
|
| 917 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 918 |
+
bFrameIndex 14
|
| 919 |
+
bmCapabilities 0x00
|
| 920 |
+
Still image unsupported
|
| 921 |
+
wWidth 1280
|
| 922 |
+
wHeight 720
|
| 923 |
+
dwMinBitRate 73728000
|
| 924 |
+
dwMaxBitRate 442368000
|
| 925 |
+
dwMaxVideoFrameBufferSize 1843200
|
| 926 |
+
dwDefaultFrameInterval 1000000
|
| 927 |
+
bFrameIntervalType 6
|
| 928 |
+
dwFrameInterval( 0) 333333
|
| 929 |
+
dwFrameInterval( 1) 400000
|
| 930 |
+
dwFrameInterval( 2) 500000
|
| 931 |
+
dwFrameInterval( 3) 666666
|
| 932 |
+
dwFrameInterval( 4) 1000000
|
| 933 |
+
dwFrameInterval( 5) 2000000
|
| 934 |
+
VideoStreaming Interface Descriptor:
|
| 935 |
+
bLength 46
|
| 936 |
+
bDescriptorType 36
|
| 937 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 938 |
+
bFrameIndex 15
|
| 939 |
+
bmCapabilities 0x00
|
| 940 |
+
Still image unsupported
|
| 941 |
+
wWidth 1280
|
| 942 |
+
wHeight 800
|
| 943 |
+
dwMinBitRate 81920000
|
| 944 |
+
dwMaxBitRate 409600000
|
| 945 |
+
dwMaxVideoFrameBufferSize 2048000
|
| 946 |
+
dwDefaultFrameInterval 1000000
|
| 947 |
+
bFrameIntervalType 5
|
| 948 |
+
dwFrameInterval( 0) 400000
|
| 949 |
+
dwFrameInterval( 1) 500000
|
| 950 |
+
dwFrameInterval( 2) 666666
|
| 951 |
+
dwFrameInterval( 3) 1000000
|
| 952 |
+
dwFrameInterval( 4) 2000000
|
| 953 |
+
VideoStreaming Interface Descriptor:
|
| 954 |
+
bLength 34
|
| 955 |
+
bDescriptorType 36
|
| 956 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 957 |
+
bFrameIndex 16
|
| 958 |
+
bmCapabilities 0x00
|
| 959 |
+
Still image unsupported
|
| 960 |
+
wWidth 1600
|
| 961 |
+
wHeight 904
|
| 962 |
+
dwMinBitRate 115712000
|
| 963 |
+
dwMaxBitRate 231424000
|
| 964 |
+
dwMaxVideoFrameBufferSize 2892800
|
| 965 |
+
dwDefaultFrameInterval 1000000
|
| 966 |
+
bFrameIntervalType 2
|
| 967 |
+
dwFrameInterval( 0) 1000000
|
| 968 |
+
dwFrameInterval( 1) 2000000
|
| 969 |
+
VideoStreaming Interface Descriptor:
|
| 970 |
+
bLength 34
|
| 971 |
+
bDescriptorType 36
|
| 972 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 973 |
+
bFrameIndex 17
|
| 974 |
+
bmCapabilities 0x00
|
| 975 |
+
Still image unsupported
|
| 976 |
+
wWidth 1600
|
| 977 |
+
wHeight 1000
|
| 978 |
+
dwMinBitRate 128000000
|
| 979 |
+
dwMaxBitRate 256000000
|
| 980 |
+
dwMaxVideoFrameBufferSize 3200000
|
| 981 |
+
dwDefaultFrameInterval 1000000
|
| 982 |
+
bFrameIntervalType 2
|
| 983 |
+
dwFrameInterval( 0) 1000000
|
| 984 |
+
dwFrameInterval( 1) 2000000
|
| 985 |
+
VideoStreaming Interface Descriptor:
|
| 986 |
+
bLength 34
|
| 987 |
+
bDescriptorType 36
|
| 988 |
+
bDescriptorSubtype 7 (FRAME_MJPEG)
|
| 989 |
+
bFrameIndex 18
|
| 990 |
+
bmCapabilities 0x00
|
| 991 |
+
Still image unsupported
|
| 992 |
+
wWidth 1600
|
| 993 |
+
wHeight 1200
|
| 994 |
+
dwMinBitRate 153600000
|
| 995 |
+
dwMaxBitRate 307200000
|
| 996 |
+
dwMaxVideoFrameBufferSize 3840000
|
| 997 |
+
dwDefaultFrameInterval 2000000
|
| 998 |
+
bFrameIntervalType 2
|
| 999 |
+
dwFrameInterval( 0) 1000000
|
| 1000 |
+
dwFrameInterval( 1) 2000000
|
| 1001 |
+
VideoStreaming Interface Descriptor:
|
| 1002 |
+
bLength 83
|
| 1003 |
+
bDescriptorType 36
|
| 1004 |
+
bDescriptorSubtype 3 (STILL_IMAGE_FRAME)
|
| 1005 |
+
bEndpointAddress 0
|
| 1006 |
+
bNumImageSizePatterns 18
|
| 1007 |
+
wWidth( 0) 640
|
| 1008 |
+
wHeight( 0) 480
|
| 1009 |
+
wWidth( 1) 160
|
| 1010 |
+
wHeight( 1) 120
|
| 1011 |
+
wWidth( 2) 176
|
| 1012 |
+
wHeight( 2) 144
|
| 1013 |
+
wWidth( 3) 320
|
| 1014 |
+
wHeight( 3) 240
|
| 1015 |
+
wWidth( 4) 352
|
| 1016 |
+
wHeight( 4) 288
|
| 1017 |
+
wWidth( 5) 640
|
| 1018 |
+
wHeight( 5) 360
|
| 1019 |
+
wWidth( 6) 640
|
| 1020 |
+
wHeight( 6) 400
|
| 1021 |
+
wWidth( 7) 768
|
| 1022 |
+
wHeight( 7) 480
|
| 1023 |
+
wWidth( 8) 800
|
| 1024 |
+
wHeight( 8) 456
|
| 1025 |
+
wWidth( 9) 800
|
| 1026 |
+
wHeight( 9) 504
|
| 1027 |
+
wWidth(10) 800
|
| 1028 |
+
wHeight(10) 600
|
| 1029 |
+
wWidth(11) 864
|
| 1030 |
+
wHeight(11) 480
|
| 1031 |
+
wWidth(12) 960
|
| 1032 |
+
wHeight(12) 720
|
| 1033 |
+
wWidth(13) 1280
|
| 1034 |
+
wHeight(13) 720
|
| 1035 |
+
wWidth(14) 1280
|
| 1036 |
+
wHeight(14) 800
|
| 1037 |
+
wWidth(15) 1600
|
| 1038 |
+
wHeight(15) 904
|
| 1039 |
+
wWidth(16) 1600
|
| 1040 |
+
wHeight(16) 1000
|
| 1041 |
+
wWidth(17) 1600
|
| 1042 |
+
wHeight(17) 1200
|
| 1043 |
+
bNumCompressionPatterns 18
|
| 1044 |
+
bCompression( 0) 5
|
| 1045 |
+
bCompression( 1) 10
|
| 1046 |
+
bCompression( 2) 15
|
| 1047 |
+
bCompression( 3) 20
|
| 1048 |
+
bCompression( 4) 25
|
| 1049 |
+
VideoStreaming Interface Descriptor:
|
| 1050 |
+
bLength 6
|
| 1051 |
+
bDescriptorType 36
|
| 1052 |
+
bDescriptorSubtype 13 (COLORFORMAT)
|
| 1053 |
+
bColorPrimaries 1 (BT.709,sRGB)
|
| 1054 |
+
bTransferCharacteristics 1 (BT.709)
|
| 1055 |
+
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
|
| 1056 |
+
Interface Descriptor:
|
| 1057 |
+
bLength 9
|
| 1058 |
+
bDescriptorType 4
|
| 1059 |
+
bInterfaceNumber 1
|
| 1060 |
+
bAlternateSetting 1
|
| 1061 |
+
bNumEndpoints 1
|
| 1062 |
+
bInterfaceClass 14 Video
|
| 1063 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1064 |
+
bInterfaceProtocol 0
|
| 1065 |
+
iInterface 0
|
| 1066 |
+
Endpoint Descriptor:
|
| 1067 |
+
bLength 7
|
| 1068 |
+
bDescriptorType 5
|
| 1069 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1070 |
+
bmAttributes 5
|
| 1071 |
+
Transfer Type Isochronous
|
| 1072 |
+
Synch Type Asynchronous
|
| 1073 |
+
Usage Type Data
|
| 1074 |
+
wMaxPacketSize 0x00c0 1x 192 bytes
|
| 1075 |
+
bInterval 1
|
| 1076 |
+
Interface Descriptor:
|
| 1077 |
+
bLength 9
|
| 1078 |
+
bDescriptorType 4
|
| 1079 |
+
bInterfaceNumber 1
|
| 1080 |
+
bAlternateSetting 2
|
| 1081 |
+
bNumEndpoints 1
|
| 1082 |
+
bInterfaceClass 14 Video
|
| 1083 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1084 |
+
bInterfaceProtocol 0
|
| 1085 |
+
iInterface 0
|
| 1086 |
+
Endpoint Descriptor:
|
| 1087 |
+
bLength 7
|
| 1088 |
+
bDescriptorType 5
|
| 1089 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1090 |
+
bmAttributes 5
|
| 1091 |
+
Transfer Type Isochronous
|
| 1092 |
+
Synch Type Asynchronous
|
| 1093 |
+
Usage Type Data
|
| 1094 |
+
wMaxPacketSize 0x0180 1x 384 bytes
|
| 1095 |
+
bInterval 1
|
| 1096 |
+
Interface Descriptor:
|
| 1097 |
+
bLength 9
|
| 1098 |
+
bDescriptorType 4
|
| 1099 |
+
bInterfaceNumber 1
|
| 1100 |
+
bAlternateSetting 3
|
| 1101 |
+
bNumEndpoints 1
|
| 1102 |
+
bInterfaceClass 14 Video
|
| 1103 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1104 |
+
bInterfaceProtocol 0
|
| 1105 |
+
iInterface 0
|
| 1106 |
+
Endpoint Descriptor:
|
| 1107 |
+
bLength 7
|
| 1108 |
+
bDescriptorType 5
|
| 1109 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1110 |
+
bmAttributes 5
|
| 1111 |
+
Transfer Type Isochronous
|
| 1112 |
+
Synch Type Asynchronous
|
| 1113 |
+
Usage Type Data
|
| 1114 |
+
wMaxPacketSize 0x0200 1x 512 bytes
|
| 1115 |
+
bInterval 1
|
| 1116 |
+
Interface Descriptor:
|
| 1117 |
+
bLength 9
|
| 1118 |
+
bDescriptorType 4
|
| 1119 |
+
bInterfaceNumber 1
|
| 1120 |
+
bAlternateSetting 4
|
| 1121 |
+
bNumEndpoints 1
|
| 1122 |
+
bInterfaceClass 14 Video
|
| 1123 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1124 |
+
bInterfaceProtocol 0
|
| 1125 |
+
iInterface 0
|
| 1126 |
+
Endpoint Descriptor:
|
| 1127 |
+
bLength 7
|
| 1128 |
+
bDescriptorType 5
|
| 1129 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1130 |
+
bmAttributes 5
|
| 1131 |
+
Transfer Type Isochronous
|
| 1132 |
+
Synch Type Asynchronous
|
| 1133 |
+
Usage Type Data
|
| 1134 |
+
wMaxPacketSize 0x0280 1x 640 bytes
|
| 1135 |
+
bInterval 1
|
| 1136 |
+
Interface Descriptor:
|
| 1137 |
+
bLength 9
|
| 1138 |
+
bDescriptorType 4
|
| 1139 |
+
bInterfaceNumber 1
|
| 1140 |
+
bAlternateSetting 5
|
| 1141 |
+
bNumEndpoints 1
|
| 1142 |
+
bInterfaceClass 14 Video
|
| 1143 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1144 |
+
bInterfaceProtocol 0
|
| 1145 |
+
iInterface 0
|
| 1146 |
+
Endpoint Descriptor:
|
| 1147 |
+
bLength 7
|
| 1148 |
+
bDescriptorType 5
|
| 1149 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1150 |
+
bmAttributes 5
|
| 1151 |
+
Transfer Type Isochronous
|
| 1152 |
+
Synch Type Asynchronous
|
| 1153 |
+
Usage Type Data
|
| 1154 |
+
wMaxPacketSize 0x0320 1x 800 bytes
|
| 1155 |
+
bInterval 1
|
| 1156 |
+
Interface Descriptor:
|
| 1157 |
+
bLength 9
|
| 1158 |
+
bDescriptorType 4
|
| 1159 |
+
bInterfaceNumber 1
|
| 1160 |
+
bAlternateSetting 6
|
| 1161 |
+
bNumEndpoints 1
|
| 1162 |
+
bInterfaceClass 14 Video
|
| 1163 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1164 |
+
bInterfaceProtocol 0
|
| 1165 |
+
iInterface 0
|
| 1166 |
+
Endpoint Descriptor:
|
| 1167 |
+
bLength 7
|
| 1168 |
+
bDescriptorType 5
|
| 1169 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1170 |
+
bmAttributes 5
|
| 1171 |
+
Transfer Type Isochronous
|
| 1172 |
+
Synch Type Asynchronous
|
| 1173 |
+
Usage Type Data
|
| 1174 |
+
wMaxPacketSize 0x03b0 1x 944 bytes
|
| 1175 |
+
bInterval 1
|
| 1176 |
+
Interface Descriptor:
|
| 1177 |
+
bLength 9
|
| 1178 |
+
bDescriptorType 4
|
| 1179 |
+
bInterfaceNumber 1
|
| 1180 |
+
bAlternateSetting 7
|
| 1181 |
+
bNumEndpoints 1
|
| 1182 |
+
bInterfaceClass 14 Video
|
| 1183 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1184 |
+
bInterfaceProtocol 0
|
| 1185 |
+
iInterface 0
|
| 1186 |
+
Endpoint Descriptor:
|
| 1187 |
+
bLength 7
|
| 1188 |
+
bDescriptorType 5
|
| 1189 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1190 |
+
bmAttributes 5
|
| 1191 |
+
Transfer Type Isochronous
|
| 1192 |
+
Synch Type Asynchronous
|
| 1193 |
+
Usage Type Data
|
| 1194 |
+
wMaxPacketSize 0x0a80 2x 640 bytes
|
| 1195 |
+
bInterval 1
|
| 1196 |
+
Interface Descriptor:
|
| 1197 |
+
bLength 9
|
| 1198 |
+
bDescriptorType 4
|
| 1199 |
+
bInterfaceNumber 1
|
| 1200 |
+
bAlternateSetting 8
|
| 1201 |
+
bNumEndpoints 1
|
| 1202 |
+
bInterfaceClass 14 Video
|
| 1203 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1204 |
+
bInterfaceProtocol 0
|
| 1205 |
+
iInterface 0
|
| 1206 |
+
Endpoint Descriptor:
|
| 1207 |
+
bLength 7
|
| 1208 |
+
bDescriptorType 5
|
| 1209 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1210 |
+
bmAttributes 5
|
| 1211 |
+
Transfer Type Isochronous
|
| 1212 |
+
Synch Type Asynchronous
|
| 1213 |
+
Usage Type Data
|
| 1214 |
+
wMaxPacketSize 0x0b20 2x 800 bytes
|
| 1215 |
+
bInterval 1
|
| 1216 |
+
Interface Descriptor:
|
| 1217 |
+
bLength 9
|
| 1218 |
+
bDescriptorType 4
|
| 1219 |
+
bInterfaceNumber 1
|
| 1220 |
+
bAlternateSetting 9
|
| 1221 |
+
bNumEndpoints 1
|
| 1222 |
+
bInterfaceClass 14 Video
|
| 1223 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1224 |
+
bInterfaceProtocol 0
|
| 1225 |
+
iInterface 0
|
| 1226 |
+
Endpoint Descriptor:
|
| 1227 |
+
bLength 7
|
| 1228 |
+
bDescriptorType 5
|
| 1229 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1230 |
+
bmAttributes 5
|
| 1231 |
+
Transfer Type Isochronous
|
| 1232 |
+
Synch Type Asynchronous
|
| 1233 |
+
Usage Type Data
|
| 1234 |
+
wMaxPacketSize 0x0be0 2x 992 bytes
|
| 1235 |
+
bInterval 1
|
| 1236 |
+
Interface Descriptor:
|
| 1237 |
+
bLength 9
|
| 1238 |
+
bDescriptorType 4
|
| 1239 |
+
bInterfaceNumber 1
|
| 1240 |
+
bAlternateSetting 10
|
| 1241 |
+
bNumEndpoints 1
|
| 1242 |
+
bInterfaceClass 14 Video
|
| 1243 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1244 |
+
bInterfaceProtocol 0
|
| 1245 |
+
iInterface 0
|
| 1246 |
+
Endpoint Descriptor:
|
| 1247 |
+
bLength 7
|
| 1248 |
+
bDescriptorType 5
|
| 1249 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1250 |
+
bmAttributes 5
|
| 1251 |
+
Transfer Type Isochronous
|
| 1252 |
+
Synch Type Asynchronous
|
| 1253 |
+
Usage Type Data
|
| 1254 |
+
wMaxPacketSize 0x1380 3x 896 bytes
|
| 1255 |
+
bInterval 1
|
| 1256 |
+
Interface Descriptor:
|
| 1257 |
+
bLength 9
|
| 1258 |
+
bDescriptorType 4
|
| 1259 |
+
bInterfaceNumber 1
|
| 1260 |
+
bAlternateSetting 11
|
| 1261 |
+
bNumEndpoints 1
|
| 1262 |
+
bInterfaceClass 14 Video
|
| 1263 |
+
bInterfaceSubClass 2 Video Streaming
|
| 1264 |
+
bInterfaceProtocol 0
|
| 1265 |
+
iInterface 0
|
| 1266 |
+
Endpoint Descriptor:
|
| 1267 |
+
bLength 7
|
| 1268 |
+
bDescriptorType 5
|
| 1269 |
+
bEndpointAddress 0x81 EP 1 IN
|
| 1270 |
+
bmAttributes 5
|
| 1271 |
+
Transfer Type Isochronous
|
| 1272 |
+
Synch Type Asynchronous
|
| 1273 |
+
Usage Type Data
|
| 1274 |
+
wMaxPacketSize 0x13fc 3x 1020 bytes
|
| 1275 |
+
bInterval 1
|
| 1276 |
+
Interface Association:
|
| 1277 |
+
bLength 8
|
| 1278 |
+
bDescriptorType 11
|
| 1279 |
+
bFirstInterface 2
|
| 1280 |
+
bInterfaceCount 2
|
| 1281 |
+
bFunctionClass 1 Audio
|
| 1282 |
+
bFunctionSubClass 2 Streaming
|
| 1283 |
+
bFunctionProtocol 0
|
| 1284 |
+
iFunction 0
|
| 1285 |
+
Interface Descriptor:
|
| 1286 |
+
bLength 9
|
| 1287 |
+
bDescriptorType 4
|
| 1288 |
+
bInterfaceNumber 2
|
| 1289 |
+
bAlternateSetting 0
|
| 1290 |
+
bNumEndpoints 0
|
| 1291 |
+
bInterfaceClass 1 Audio
|
| 1292 |
+
bInterfaceSubClass 1 Control Device
|
| 1293 |
+
bInterfaceProtocol 0
|
| 1294 |
+
iInterface 0
|
| 1295 |
+
AudioControl Interface Descriptor:
|
| 1296 |
+
bLength 9
|
| 1297 |
+
bDescriptorType 36
|
| 1298 |
+
bDescriptorSubtype 1 (HEADER)
|
| 1299 |
+
bcdADC 1.00
|
| 1300 |
+
wTotalLength 38
|
| 1301 |
+
bInCollection 1
|
| 1302 |
+
baInterfaceNr( 0) 3
|
| 1303 |
+
AudioControl Interface Descriptor:
|
| 1304 |
+
bLength 12
|
| 1305 |
+
bDescriptorType 36
|
| 1306 |
+
bDescriptorSubtype 2 (INPUT_TERMINAL)
|
| 1307 |
+
bTerminalID 1
|
| 1308 |
+
wTerminalType 0x0201 Microphone
|
| 1309 |
+
bAssocTerminal 0
|
| 1310 |
+
bNrChannels 1
|
| 1311 |
+
wChannelConfig 0x0000
|
| 1312 |
+
iChannelNames 0
|
| 1313 |
+
iTerminal 0
|
| 1314 |
+
AudioControl Interface Descriptor:
|
| 1315 |
+
bLength 9
|
| 1316 |
+
bDescriptorType 36
|
| 1317 |
+
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
|
| 1318 |
+
bTerminalID 3
|
| 1319 |
+
wTerminalType 0x0101 USB Streaming
|
| 1320 |
+
bAssocTerminal 1
|
| 1321 |
+
bSourceID 5
|
| 1322 |
+
iTerminal 0
|
| 1323 |
+
AudioControl Interface Descriptor:
|
| 1324 |
+
bLength 9
|
| 1325 |
+
bDescriptorType 36
|
| 1326 |
+
bDescriptorSubtype 6 (FEATURE_UNIT)
|
| 1327 |
+
bUnitID 5
|
| 1328 |
+
bSourceID 1
|
| 1329 |
+
bControlSize 1
|
| 1330 |
+
bmaControls( 0) 0x03
|
| 1331 |
+
Mute
|
| 1332 |
+
Volume
|
| 1333 |
+
bmaControls( 1) 0x00
|
| 1334 |
+
iFeature 0
|
| 1335 |
+
Interface Descriptor:
|
| 1336 |
+
bLength 9
|
| 1337 |
+
bDescriptorType 4
|
| 1338 |
+
bInterfaceNumber 3
|
| 1339 |
+
bAlternateSetting 0
|
| 1340 |
+
bNumEndpoints 0
|
| 1341 |
+
bInterfaceClass 1 Audio
|
| 1342 |
+
bInterfaceSubClass 2 Streaming
|
| 1343 |
+
bInterfaceProtocol 0
|
| 1344 |
+
iInterface 0
|
| 1345 |
+
Interface Descriptor:
|
| 1346 |
+
bLength 9
|
| 1347 |
+
bDescriptorType 4
|
| 1348 |
+
bInterfaceNumber 3
|
| 1349 |
+
bAlternateSetting 1
|
| 1350 |
+
bNumEndpoints 1
|
| 1351 |
+
bInterfaceClass 1 Audio
|
| 1352 |
+
bInterfaceSubClass 2 Streaming
|
| 1353 |
+
bInterfaceProtocol 0
|
| 1354 |
+
iInterface 0
|
| 1355 |
+
AudioStreaming Interface Descriptor:
|
| 1356 |
+
bLength 7
|
| 1357 |
+
bDescriptorType 36
|
| 1358 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1359 |
+
bTerminalLink 3
|
| 1360 |
+
bDelay 1 frames
|
| 1361 |
+
wFormatTag 1 PCM
|
| 1362 |
+
AudioStreaming Interface Descriptor:
|
| 1363 |
+
bLength 11
|
| 1364 |
+
bDescriptorType 36
|
| 1365 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1366 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1367 |
+
bNrChannels 1
|
| 1368 |
+
bSubframeSize 2
|
| 1369 |
+
bBitResolution 16
|
| 1370 |
+
bSamFreqType 1 Discrete
|
| 1371 |
+
tSamFreq[ 0] 16000
|
| 1372 |
+
Endpoint Descriptor:
|
| 1373 |
+
bLength 9
|
| 1374 |
+
bDescriptorType 5
|
| 1375 |
+
bEndpointAddress 0x86 EP 6 IN
|
| 1376 |
+
bmAttributes 5
|
| 1377 |
+
Transfer Type Isochronous
|
| 1378 |
+
Synch Type Asynchronous
|
| 1379 |
+
Usage Type Data
|
| 1380 |
+
wMaxPacketSize 0x0044 1x 68 bytes
|
| 1381 |
+
bInterval 4
|
| 1382 |
+
bRefresh 0
|
| 1383 |
+
bSynchAddress 0
|
| 1384 |
+
AudioControl Endpoint Descriptor:
|
| 1385 |
+
bLength 7
|
| 1386 |
+
bDescriptorType 37
|
| 1387 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1388 |
+
bmAttributes 0x01
|
| 1389 |
+
Sampling Frequency
|
| 1390 |
+
bLockDelayUnits 0 Undefined
|
| 1391 |
+
wLockDelay 0 Undefined
|
| 1392 |
+
Interface Descriptor:
|
| 1393 |
+
bLength 9
|
| 1394 |
+
bDescriptorType 4
|
| 1395 |
+
bInterfaceNumber 3
|
| 1396 |
+
bAlternateSetting 2
|
| 1397 |
+
bNumEndpoints 1
|
| 1398 |
+
bInterfaceClass 1 Audio
|
| 1399 |
+
bInterfaceSubClass 2 Streaming
|
| 1400 |
+
bInterfaceProtocol 0
|
| 1401 |
+
iInterface 0
|
| 1402 |
+
AudioStreaming Interface Descriptor:
|
| 1403 |
+
bLength 7
|
| 1404 |
+
bDescriptorType 36
|
| 1405 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1406 |
+
bTerminalLink 3
|
| 1407 |
+
bDelay 1 frames
|
| 1408 |
+
wFormatTag 1 PCM
|
| 1409 |
+
AudioStreaming Interface Descriptor:
|
| 1410 |
+
bLength 11
|
| 1411 |
+
bDescriptorType 36
|
| 1412 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1413 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1414 |
+
bNrChannels 1
|
| 1415 |
+
bSubframeSize 2
|
| 1416 |
+
bBitResolution 16
|
| 1417 |
+
bSamFreqType 1 Discrete
|
| 1418 |
+
tSamFreq[ 0] 24000
|
| 1419 |
+
Endpoint Descriptor:
|
| 1420 |
+
bLength 9
|
| 1421 |
+
bDescriptorType 5
|
| 1422 |
+
bEndpointAddress 0x86 EP 6 IN
|
| 1423 |
+
bmAttributes 5
|
| 1424 |
+
Transfer Type Isochronous
|
| 1425 |
+
Synch Type Asynchronous
|
| 1426 |
+
Usage Type Data
|
| 1427 |
+
wMaxPacketSize 0x0064 1x 100 bytes
|
| 1428 |
+
bInterval 4
|
| 1429 |
+
bRefresh 0
|
| 1430 |
+
bSynchAddress 0
|
| 1431 |
+
AudioControl Endpoint Descriptor:
|
| 1432 |
+
bLength 7
|
| 1433 |
+
bDescriptorType 37
|
| 1434 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1435 |
+
bmAttributes 0x01
|
| 1436 |
+
Sampling Frequency
|
| 1437 |
+
bLockDelayUnits 0 Undefined
|
| 1438 |
+
wLockDelay 0 Undefined
|
| 1439 |
+
Interface Descriptor:
|
| 1440 |
+
bLength 9
|
| 1441 |
+
bDescriptorType 4
|
| 1442 |
+
bInterfaceNumber 3
|
| 1443 |
+
bAlternateSetting 3
|
| 1444 |
+
bNumEndpoints 1
|
| 1445 |
+
bInterfaceClass 1 Audio
|
| 1446 |
+
bInterfaceSubClass 2 Streaming
|
| 1447 |
+
bInterfaceProtocol 0
|
| 1448 |
+
iInterface 0
|
| 1449 |
+
AudioStreaming Interface Descriptor:
|
| 1450 |
+
bLength 7
|
| 1451 |
+
bDescriptorType 36
|
| 1452 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1453 |
+
bTerminalLink 3
|
| 1454 |
+
bDelay 1 frames
|
| 1455 |
+
wFormatTag 1 PCM
|
| 1456 |
+
AudioStreaming Interface Descriptor:
|
| 1457 |
+
bLength 11
|
| 1458 |
+
bDescriptorType 36
|
| 1459 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1460 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1461 |
+
bNrChannels 1
|
| 1462 |
+
bSubframeSize 2
|
| 1463 |
+
bBitResolution 16
|
| 1464 |
+
bSamFreqType 1 Discrete
|
| 1465 |
+
tSamFreq[ 0] 32000
|
| 1466 |
+
Endpoint Descriptor:
|
| 1467 |
+
bLength 9
|
| 1468 |
+
bDescriptorType 5
|
| 1469 |
+
bEndpointAddress 0x86 EP 6 IN
|
| 1470 |
+
bmAttributes 5
|
| 1471 |
+
Transfer Type Isochronous
|
| 1472 |
+
Synch Type Asynchronous
|
| 1473 |
+
Usage Type Data
|
| 1474 |
+
wMaxPacketSize 0x0084 1x 132 bytes
|
| 1475 |
+
bInterval 4
|
| 1476 |
+
bRefresh 0
|
| 1477 |
+
bSynchAddress 0
|
| 1478 |
+
AudioControl Endpoint Descriptor:
|
| 1479 |
+
bLength 7
|
| 1480 |
+
bDescriptorType 37
|
| 1481 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1482 |
+
bmAttributes 0x01
|
| 1483 |
+
Sampling Frequency
|
| 1484 |
+
bLockDelayUnits 0 Undefined
|
| 1485 |
+
wLockDelay 0 Undefined
|
| 1486 |
+
Interface Descriptor:
|
| 1487 |
+
bLength 9
|
| 1488 |
+
bDescriptorType 4
|
| 1489 |
+
bInterfaceNumber 3
|
| 1490 |
+
bAlternateSetting 4
|
| 1491 |
+
bNumEndpoints 1
|
| 1492 |
+
bInterfaceClass 1 Audio
|
| 1493 |
+
bInterfaceSubClass 2 Streaming
|
| 1494 |
+
bInterfaceProtocol 0
|
| 1495 |
+
iInterface 0
|
| 1496 |
+
AudioStreaming Interface Descriptor:
|
| 1497 |
+
bLength 7
|
| 1498 |
+
bDescriptorType 36
|
| 1499 |
+
bDescriptorSubtype 1 (AS_GENERAL)
|
| 1500 |
+
bTerminalLink 3
|
| 1501 |
+
bDelay 1 frames
|
| 1502 |
+
wFormatTag 1 PCM
|
| 1503 |
+
AudioStreaming Interface Descriptor:
|
| 1504 |
+
bLength 11
|
| 1505 |
+
bDescriptorType 36
|
| 1506 |
+
bDescriptorSubtype 2 (FORMAT_TYPE)
|
| 1507 |
+
bFormatType 1 (FORMAT_TYPE_I)
|
| 1508 |
+
bNrChannels 1
|
| 1509 |
+
bSubframeSize 2
|
| 1510 |
+
bBitResolution 16
|
| 1511 |
+
bSamFreqType 1 Discrete
|
| 1512 |
+
tSamFreq[ 0] 48000
|
| 1513 |
+
Endpoint Descriptor:
|
| 1514 |
+
bLength 9
|
| 1515 |
+
bDescriptorType 5
|
| 1516 |
+
bEndpointAddress 0x86 EP 6 IN
|
| 1517 |
+
bmAttributes 5
|
| 1518 |
+
Transfer Type Isochronous
|
| 1519 |
+
Synch Type Asynchronous
|
| 1520 |
+
Usage Type Data
|
| 1521 |
+
wMaxPacketSize 0x00c4 1x 196 bytes
|
| 1522 |
+
bInterval 4
|
| 1523 |
+
bRefresh 0
|
| 1524 |
+
bSynchAddress 0
|
| 1525 |
+
AudioControl Endpoint Descriptor:
|
| 1526 |
+
bLength 7
|
| 1527 |
+
bDescriptorType 37
|
| 1528 |
+
bDescriptorSubtype 1 (EP_GENERAL)
|
| 1529 |
+
bmAttributes 0x01
|
| 1530 |
+
Sampling Frequency
|
| 1531 |
+
bLockDelayUnits 0 Undefined
|
| 1532 |
+
wLockDelay 0 Undefined
|
| 1533 |
+
Device Qualifier (for other device speed):
|
| 1534 |
+
bLength 10
|
| 1535 |
+
bDescriptorType 6
|
| 1536 |
+
bcdUSB 2.00
|
| 1537 |
+
bDeviceClass 239 Miscellaneous Device
|
| 1538 |
+
bDeviceSubClass 2 ?
|
| 1539 |
+
bDeviceProtocol 1 Interface Association
|
| 1540 |
+
bMaxPacketSize0 64
|
| 1541 |
+
bNumConfigurations 1
|
| 1542 |
+
Device Status: 0x0000
|
| 1543 |
+
(Bus Powered)
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/quickcampro9000_builtin_ctrls.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Listing available controls for device video0:
|
| 2 |
+
Exposure, Auto Priority
|
| 3 |
+
Exposure (Absolute)
|
| 4 |
+
Exposure, Auto
|
| 5 |
+
Backlight Compensation
|
| 6 |
+
Sharpness
|
| 7 |
+
White Balance Temperature
|
| 8 |
+
Power Line Frequency
|
| 9 |
+
Gain
|
| 10 |
+
White Balance Temperature, Auto
|
| 11 |
+
Saturation
|
| 12 |
+
Contrast
|
| 13 |
+
Brightness
|
collector/core/gripper/camera_service_src/third_party/libuvc/cameras/quickcampro9000_extra_ctrls.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Listing available controls for device video0:
|
| 2 |
+
Raw bits per pixel
|
| 3 |
+
Disable video processing
|
| 4 |
+
LED1 Frequency
|
| 5 |
+
LED1 Mode
|
| 6 |
+
Focus
|
| 7 |
+
Exposure, Auto Priority
|
| 8 |
+
Exposure (Absolute)
|
| 9 |
+
Exposure, Auto
|
| 10 |
+
Backlight Compensation
|
| 11 |
+
Sharpness
|
| 12 |
+
White Balance Temperature
|
| 13 |
+
Power Line Frequency
|
| 14 |
+
Gain
|
| 15 |
+
White Balance Temperature, Auto
|
| 16 |
+
Saturation
|
| 17 |
+
Contrast
|
| 18 |
+
Brightness
|
collector/core/gripper/camera_service_src/third_party/libuvc/changelog.txt
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Changes in 0.0.7 (2023-03-31)
|
| 2 |
+
----------------
|
| 3 |
+
|
| 4 |
+
New features:
|
| 5 |
+
- USB 3 support
|
| 6 |
+
- Several image/stream formats (incl. NV12, H264, BGR, RGB, P010)
|
| 7 |
+
- Format conversion functions
|
| 8 |
+
- Accept pre-opened libusb handles (used on Android)
|
| 9 |
+
- Library filename versioning (libuvc.0.0.7.dylib)
|
| 10 |
+
- Store system time upon receipt of new frame
|
| 11 |
+
- In-stream still-image-capture support
|
| 12 |
+
- Ensure successful stream parameter negotiation
|
| 13 |
+
- Improved build scripts
|
| 14 |
+
- Better logging on Android
|
| 15 |
+
|
| 16 |
+
Bug fixes:
|
| 17 |
+
- Many build issues
|
| 18 |
+
- Log spam in production builds
|
| 19 |
+
- A situation where libuvc could hang during shutdown
|
| 20 |
+
- A deadlock in get_frame()
|
| 21 |
+
- An issue that could cause the first frame to fail
|
| 22 |
+
- Incorrect settings usage in example code
|
| 23 |
+
- Unnecessary device reopening
|
| 24 |
+
- Excessive number of transfer buffers affecting Mac builds
|
| 25 |
+
- Freeing of transfers that may still be in progress
|
| 26 |
+
|
| 27 |
+
Changes in 0.0.6 (2017-09-21)
|
| 28 |
+
----------------
|
| 29 |
+
|
| 30 |
+
New features:
|
| 31 |
+
- Added support for pkg-config.
|
| 32 |
+
- Silence pkg-config when searching for libjpeg
|
| 33 |
+
- Resolve pkg-config conflict, update CONF_LIBRARY #11
|
| 34 |
+
- Actually use pkg-config to find libjpeg, and set cflags and ldflags for it. #78
|
| 35 |
+
- Add a pkg-config file to allow build systems to identify the flags needed for compiling and linking against this library #77
|
| 36 |
+
- Add support for 16-bit greyscale image format (Y16) #77
|
| 37 |
+
- Add support for 8-bit raw colour formats, specifically: BA81, GRBG, GBRG, RGGB and BGGR #77
|
| 38 |
+
- Improve detection of The Imaging Source Cameras that implement the UVC protocols but don't announce themselves as UVC cameras (and ignore similar cameras that appear to be UVC but are not). #77
|
| 39 |
+
- add routine for listing frame configs #76
|
| 40 |
+
- New Travis configuration
|
| 41 |
+
- Add uvc_find_devices function to support libuvc_ros index selection #36
|
| 42 |
+
- VideoStreaming status processing #30
|
| 43 |
+
- Add conversion from YUYV to Y (GRAY8) and UV (GRAY8) #27
|
| 44 |
+
- Add LICENSE.txt.
|
| 45 |
+
- Add Dw_clock_frequency field and populate acording to UVC1.0 or 1.1 spec. #19
|
| 46 |
+
- Changes to enable compilation with Windows MSVC #13
|
| 47 |
+
- Modify ABS_FMT macro to function correctly with MSVC.
|
| 48 |
+
- Implement gettimeofday for MSVC.
|
| 49 |
+
- Use portable memset instead of bzero for MSVC compatibility.
|
| 50 |
+
- Annotate libusb callback methods with LIBUSB_CALL for MSVC.
|
| 51 |
+
|
| 52 |
+
- Merge of bulk mode transfer support from Tridge, Mauricio and James (#8)
|
| 53 |
+
- Support for BY8 frames
|
| 54 |
+
- (Very basic) support for UVC1.1 camera data structures
|
| 55 |
+
- Support for frame-based video streams
|
| 56 |
+
- Support for TIS astro CCD cameras
|
| 57 |
+
- Squashed a few possible bugs
|
| 58 |
+
- Added section descriptions and control getter/setter documentation
|
| 59 |
+
|
| 60 |
+
Bug fixes:
|
| 61 |
+
- update CMakeLists.txt
|
| 62 |
+
- Add missing include (for ${CMAKE_INSTALL_LIBDIR}), fix static-/shared-only build #15
|
| 63 |
+
- Allow both static and dynamic versions of library to be built to work more neatly when building into runtime and development packages #77
|
| 64 |
+
|
| 65 |
+
- Update device.c, reduce salience of the alert which end user perceives as more important than it actually is. #79
|
| 66 |
+
- Clean up transfer buffers when an error occurs #77
|
| 67 |
+
- Fixed the issue that camera control functions can not work well with some cameras #41
|
| 68 |
+
UVC specification says that low byte of wIndex (for camera control) is interface number for camera control interface but current implementation of libuvc expects it is always zero and sometimes cause trouble.
|
| 69 |
+
Fixed camera control issue when using uvc_get_ctrl_len/uvc_get_ctrl/uvc_set_ctrl with camera that interface number of camera control interface is not zero.
|
| 70 |
+
- In some (as yet unidentified) circumstances it is possible for an interface to be claimed more than once. This can cause an error. #77
|
| 71 |
+
- A race allows queued transfers to arrive and be viable for resubmission even when a stream is no longer running. #77
|
| 72 |
+
- Querying stream controls before claiming the interface can fail, so claim the interface first, then do the query. #77
|
| 73 |
+
- Show error code in error message to help debugging #77
|
| 74 |
+
- Add specific error messages for unsupported frame formats to make failures clearer #77
|
| 75 |
+
- Clean up compiler warnings #77
|
| 76 |
+
- Remove libusb.h from the public header #62
|
| 77 |
+
- Fix the first captured image being dropped #51
|
| 78 |
+
The first frame generated by any camera was always lost in the libuvc layer, and not passed on to the listening application.
|
| 79 |
+
The start value of seq (thus hold_seq) could be arbitrary, but it must be non-zero. Both the callback listener and the polling function's last_seq / last_polled_seq start initialised to zero, which causes any image with hold_seq zero to be ignored.
|
| 80 |
+
- Build fix for Chromium #48, #58
|
| 81 |
+
Some platforms use different version of libusb. For instance, Chromium compiles libusb by itself. So hard coding of libuse version (i.e. <libusb-1.0/libusb.h>) causes compile failure.
|
| 82 |
+
Remove libusb.h from the public header, This should fix the include path problems seen when including libuvc.h into other programs
|
| 83 |
+
- pthread_cond_timedwait nanoseconds shouldn't be greater than 1 billion #47
|
| 84 |
+
While using the library and testing the timed poll with 500000 us wait time, I noticed that it often happened that the timedwait returned EINVAL.
|
| 85 |
+
According to http://stackoverflow.com/questions/11319063/pthread-cond-timedwait-root-causes-of-einval
|
| 86 |
+
this might happen for several reasons, and one of them is when the number of nanoseconds in the timespec is greater than 1 billion (1 billion = 1 second).
|
| 87 |
+
Thus, I added a check that correctly updates the seconds and nanoseconds, bringing the nanosecond values in the range 0-999999999.
|
| 88 |
+
In Issue #16 it is mentioned that code sometimes crashes on uvc_stream_get_frame with some specific timeout values, this might have been the cause.
|
| 89 |
+
Fix for wrong setting of pthread_cond_timedwait nanoseconds
|
| 90 |
+
- Fix control functions for processors and other devices #32
|
| 91 |
+
When using a control function that is pertinent to a particular device (such as a processing device), the id of that device needs to be passed to the camera, or the call will fail
|
| 92 |
+
- Added docs for some more controls and switched to yaml doc file
|
| 93 |
+
- Updated documentation index
|
| 94 |
+
- Enable uvc_mjpeg2rgb function, if JPEG is supported
|
| 95 |
+
|
| 96 |
+
Changes in 0.0.5 (2014-07-19)
|
| 97 |
+
----------------
|
| 98 |
+
|
| 99 |
+
New features:
|
| 100 |
+
- Added support for all of the camera terminal and processing unit controls, including the controls
|
| 101 |
+
that appeared in UVC 1.1 and 1.5.
|
| 102 |
+
- Added LIBUVC_VERSION_GTE(major, minor, patch) macro.
|
| 103 |
+
|
| 104 |
+
Bug fixes:
|
| 105 |
+
- Switching to explicit kernel driver detachment since auto_detach isn't available in libusb < 1.0.16.
|
| 106 |
+
- The cmake module now looks for libuvc.dylib instead of libuvc.so on OS X.
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
Changes in 0.0.4 (2014-06-26)
|
| 110 |
+
----------------
|
| 111 |
+
|
| 112 |
+
New features:
|
| 113 |
+
- Support devices with multiple streaming interfaces and multiple concurrent streams.
|
| 114 |
+
A new uvc_stream* API is added, along with a uvc_stream_handle type to encapsulate the
|
| 115 |
+
state of a single UVC stream. Multiple streams can run alongside each other, provided
|
| 116 |
+
your USB connection has enough bandwidth. Streams can be individually stopped and
|
| 117 |
+
resumed; the old uvc_start/stop_streaming API is still provided as a convenient way
|
| 118 |
+
to interact with the usual one-stream devices.
|
| 119 |
+
- Added support for MJPEG streams.
|
| 120 |
+
- Added functions for checking/setting autofocus mode.
|
| 121 |
+
- Added an interface to set/get arbitrary controls on units and terminals.
|
| 122 |
+
- Made the input, output, processing and extension units public.
|
| 123 |
+
- Implemented uvc_get_device and uvc_get_libusb_handle.
|
| 124 |
+
- Add a library-owned flag to uvc_frame_t so that users may allocate their own frame buffers.
|
| 125 |
+
|
| 126 |
+
Bug fixes:
|
| 127 |
+
- Send frames as soon as they're received, not when the following frame arrives
|
| 128 |
+
- Fixed call to NULL when no status callback is provided.
|
| 129 |
+
- Fixed crash that occurred during shutdown if the USB device was disconnected during streaming.
|
| 130 |
+
|
| 131 |
+
Miscellaneous improvements:
|
| 132 |
+
- Hid the transfer method (isochronous vs bulk) from the user. This was never really
|
| 133 |
+
selectable; the camera's streaming interface supports either bulk or isochronous
|
| 134 |
+
transfers, so now libuvc will figure out which one is appropriate. The `isochronous`
|
| 135 |
+
parameter has been converted to a `flags` parameter, which is currently unused but
|
| 136 |
+
could be used to convey up to 7 bits of stream mode information in the future.
|
| 137 |
+
- Improved the method for claiming the camera's interfaces.
|
| 138 |
+
- Renamed UVC_COLOR_FORMAT_* to UVC_FRAME_FORMAT_*. The old #defines are still available.
|
| 139 |
+
- Simplified format definition and lookup.
|
| 140 |
+
- Improved transfer status (error) handling.
|
collector/core/gripper/camera_service_src/third_party/libuvc/cmake/FindJpegPkg.cmake
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#[==============================================[
|
| 2 |
+
FindJpegPkg
|
| 3 |
+
-----------
|
| 4 |
+
|
| 5 |
+
Searching jpeg library library and creating imported
|
| 6 |
+
target JPEG::JPEG (this is different from shipped one with CMake)
|
| 7 |
+
|
| 8 |
+
#]==============================================]
|
| 9 |
+
|
| 10 |
+
# TODO Append parts for Version compasion and REQUIRED support
|
| 11 |
+
if (MSVC OR MINGW)
|
| 12 |
+
return()
|
| 13 |
+
endif()
|
| 14 |
+
|
| 15 |
+
# Try to find JPEG using a module or pkg-config. If that doesn't work, search for the header.
|
| 16 |
+
if (NOT TARGET JPEG::JPEG)
|
| 17 |
+
find_package(JPEG)
|
| 18 |
+
if (JPEG_FOUND)
|
| 19 |
+
message(STATUS "Found JPEG library using standard module")
|
| 20 |
+
set(IMPORTED_JPEG_INCLUDE_DIRS ${JPEG_INCLUDE_DIR})
|
| 21 |
+
set(IMPORTED_JPEG_LIBRARIES ${JPEG_LIBRARIES})
|
| 22 |
+
else()
|
| 23 |
+
find_package(PkgConfig)
|
| 24 |
+
pkg_check_modules(JPEG libjpeg)
|
| 25 |
+
if(JPEG_FOUND)
|
| 26 |
+
message(STATUS "JPEG found by pkgconfig")
|
| 27 |
+
|
| 28 |
+
if (DEFINED JPEG_INCLUDE_DIRS)
|
| 29 |
+
set(IMPORTED_JPEG_INCLUDE_DIRS ${JPEG_INCLUDE_DIRS})
|
| 30 |
+
endif()
|
| 31 |
+
|
| 32 |
+
if(JPEG_LIBRARIES)
|
| 33 |
+
find_library(JPEG_LIBRARY
|
| 34 |
+
NAMES ${JPEG_LIBRARIES}
|
| 35 |
+
PATHS ${JPEG_LIBDIR} ${JPEG_LIBRARY_DIRS}
|
| 36 |
+
)
|
| 37 |
+
if(JPEG_LIBRARY)
|
| 38 |
+
set(IMPORTED_JPEG_LIBRARIES ${JPEG_LIBRARY})
|
| 39 |
+
else()
|
| 40 |
+
message(WARNING "Could not found libjpeg library file")
|
| 41 |
+
endif()
|
| 42 |
+
endif()
|
| 43 |
+
else()
|
| 44 |
+
message(STATUS "Searching library manually")
|
| 45 |
+
find_path(IMPORTED_JPEG_INCLUDE_DIRS
|
| 46 |
+
NAMES jpeglib.h
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
if (NOT IMPORTED_JPEG_LIBRARIES)
|
| 50 |
+
find_library(IMPORTED_JPEG_LIBRARIES
|
| 51 |
+
NAMES jpeg
|
| 52 |
+
)
|
| 53 |
+
endif()
|
| 54 |
+
endif()
|
| 55 |
+
endif()
|
| 56 |
+
|
| 57 |
+
include(FindPackageHandleStandardArgs)
|
| 58 |
+
find_package_handle_standard_args(JPEG
|
| 59 |
+
REQUIRED_VARS IMPORTED_JPEG_LIBRARIES IMPORTED_JPEG_INCLUDE_DIRS
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
if(JPEG_FOUND)
|
| 63 |
+
set(JpegPkg_FOUND TRUE)
|
| 64 |
+
if (NOT TARGET JPEG::JPEG)
|
| 65 |
+
add_library(JPEG::JPEG
|
| 66 |
+
UNKNOWN IMPORTED
|
| 67 |
+
)
|
| 68 |
+
if(IMPORTED_JPEG_INCLUDE_DIRS)
|
| 69 |
+
set_target_properties(JPEG::JPEG PROPERTIES
|
| 70 |
+
INTERFACE_INCLUDE_DIRECTORIES ${IMPORTED_JPEG_INCLUDE_DIRS}
|
| 71 |
+
)
|
| 72 |
+
endif()
|
| 73 |
+
if(IMPORTED_JPEG_LIBRARIES)
|
| 74 |
+
set_target_properties(JPEG::JPEG PROPERTIES
|
| 75 |
+
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
| 76 |
+
IMPORTED_LOCATION ${IMPORTED_JPEG_LIBRARIES}
|
| 77 |
+
)
|
| 78 |
+
endif()
|
| 79 |
+
endif()
|
| 80 |
+
endif()
|
| 81 |
+
|
| 82 |
+
endif()
|
collector/core/gripper/camera_service_src/third_party/libuvc/cmake/FindLibUSB.cmake
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#[==============================================[
|
| 2 |
+
FindLibUSB
|
| 3 |
+
-----------
|
| 4 |
+
|
| 5 |
+
Searching libusb-1.0 library and creating imported
|
| 6 |
+
target LibUSB::LibUSB
|
| 7 |
+
|
| 8 |
+
#]==============================================]
|
| 9 |
+
|
| 10 |
+
# TODO Append parts for Version compasion and REQUIRED support
|
| 11 |
+
|
| 12 |
+
if (MSVC OR MINGW)
|
| 13 |
+
return()
|
| 14 |
+
endif()
|
| 15 |
+
|
| 16 |
+
if (NOT TARGET LibUSB::LibUSB)
|
| 17 |
+
find_package(PkgConfig)
|
| 18 |
+
pkg_check_modules(LibUSB REQUIRED
|
| 19 |
+
libusb-1.0
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
if(LibUSB_FOUND)
|
| 23 |
+
message(STATUS "libusb-1.0 found using pkgconfig")
|
| 24 |
+
|
| 25 |
+
add_library(LibUSB::LibUSB
|
| 26 |
+
UNKNOWN IMPORTED
|
| 27 |
+
)
|
| 28 |
+
if (DEFINED LibUSB_INCLUDE_DIRS AND NOT LibUSB_INCLUDE_DIRS STREQUAL "")
|
| 29 |
+
set_target_properties(LibUSB::LibUSB PROPERTIES
|
| 30 |
+
INTERFACE_INCLUDE_DIRECTORIES ${LibUSB_INCLUDE_DIRS}
|
| 31 |
+
)
|
| 32 |
+
endif()
|
| 33 |
+
|
| 34 |
+
if(LibUSB_LIBRARIES)
|
| 35 |
+
find_library(LibUSB_LIBRARY
|
| 36 |
+
NAMES ${LibUSB_LIBRARIES}
|
| 37 |
+
PATHS ${LibUSB_LIBDIR} ${LibUSB_LIBRARY_DIRS}
|
| 38 |
+
)
|
| 39 |
+
if(LibUSB_LIBRARY)
|
| 40 |
+
set_target_properties(LibUSB::LibUSB PROPERTIES
|
| 41 |
+
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
| 42 |
+
IMPORTED_LOCATION ${LibUSB_LIBRARY}
|
| 43 |
+
)
|
| 44 |
+
else()
|
| 45 |
+
message(WARNING "Could not found libusb-1.0 library file")
|
| 46 |
+
endif()
|
| 47 |
+
endif()
|
| 48 |
+
endif()
|
| 49 |
+
else()
|
| 50 |
+
message(WARNING "libusb-1.0 could not be found using pkgconfig")
|
| 51 |
+
endif()
|
collector/core/gripper/camera_service_src/third_party/libuvc/cmake/FindOpenCVPkg.cmake
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#[==============================================[
|
| 2 |
+
FindOpenCVPkg
|
| 3 |
+
-----------
|
| 4 |
+
|
| 5 |
+
Searching OpenCV components on systems which does not have fully installed OpenCV.
|
| 6 |
+
Such systems does not include OpenCVConfig.cmake and OpenCVConfigVersion.cmake files.
|
| 7 |
+
Example of such system is Ubuntu 18.04.
|
| 8 |
+
This package try to use OpenCV config file first if it exists.
|
| 9 |
+
|
| 10 |
+
#]==============================================]
|
| 11 |
+
|
| 12 |
+
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS)
|
| 13 |
+
set(comps ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS})
|
| 14 |
+
else()
|
| 15 |
+
set(comps)
|
| 16 |
+
endif()
|
| 17 |
+
|
| 18 |
+
list(LENGTH comps comps_count)
|
| 19 |
+
message(STATUS "Components requested: ${comps}")
|
| 20 |
+
|
| 21 |
+
# Try to find components using standard OpenCV config package
|
| 22 |
+
if(comps_count GREATER 0)
|
| 23 |
+
find_package(OpenCV QUIET
|
| 24 |
+
COMPONENTS ${comps}
|
| 25 |
+
)
|
| 26 |
+
if(OpenCV_FOUND)
|
| 27 |
+
set(OpenCVPkg_FOUND TRUE)
|
| 28 |
+
return()
|
| 29 |
+
endif()
|
| 30 |
+
else()
|
| 31 |
+
find_package(OpenCV QUIET)
|
| 32 |
+
if(OpenCV_FOUND)
|
| 33 |
+
set(OpenCVPkg_FOUND TRUE)
|
| 34 |
+
return()
|
| 35 |
+
endif()
|
| 36 |
+
endif()
|
| 37 |
+
|
| 38 |
+
if(comps_count EQUAL 0)
|
| 39 |
+
set(err_level)
|
| 40 |
+
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
| 41 |
+
set(err_level FATAL_ERROR)
|
| 42 |
+
endif()
|
| 43 |
+
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
|
| 44 |
+
set(err_level STATUS)
|
| 45 |
+
endif()
|
| 46 |
+
message(${err_level} "Without standart OpenCV config file you must list components you want")
|
| 47 |
+
return()
|
| 48 |
+
endif()
|
| 49 |
+
|
| 50 |
+
unset(required_comps_not_found)
|
| 51 |
+
foreach(comp IN LISTS comps)
|
| 52 |
+
string(REPLACE "opencv_" "" comp_name ${comp})
|
| 53 |
+
set(include_rel_path "opencv2/${comp_name}.hpp" "opencv2/${comp_name}/${comp_name}.hpp")
|
| 54 |
+
find_path(OpenCV_${comp}_INCLUDE_DIR
|
| 55 |
+
NAMES ${include_rel_path}
|
| 56 |
+
)
|
| 57 |
+
find_library(OpenCV_${comp}_LIBRARY
|
| 58 |
+
NAMES ${comp}
|
| 59 |
+
)
|
| 60 |
+
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED_${comp} AND
|
| 61 |
+
(NOT OpenCV_${comp}_INCLUDE_DIR OR NOT OpenCV_${comp}_LIBRARY))
|
| 62 |
+
list(APPEND required_comps_not_found ${comp})
|
| 63 |
+
else()
|
| 64 |
+
message(STATUS "Found component ${comp}: ${OpenCV_${comp}_LIBRARY}")
|
| 65 |
+
endif()
|
| 66 |
+
endforeach()
|
| 67 |
+
|
| 68 |
+
if(required_comps_not_found)
|
| 69 |
+
message(FATAL_ERROR "Following required components not found: ${required_comps_not_found}")
|
| 70 |
+
return()
|
| 71 |
+
endif()
|
| 72 |
+
|
| 73 |
+
foreach(comp IN LISTS comps)
|
| 74 |
+
if(NOT TARGET ${comp})
|
| 75 |
+
message(STATUS "No target ${comp} yet. Creating it")
|
| 76 |
+
add_library(${comp}
|
| 77 |
+
UNKNOWN IMPORTED
|
| 78 |
+
)
|
| 79 |
+
set_target_properties(${comp} PROPERTIES
|
| 80 |
+
INTERFACE_INCLUDE_DIRECTORIES ${OpenCV_${comp}_INCLUDE_DIR}
|
| 81 |
+
IMPORTED_LOCATION ${OpenCV_${comp}_LIBRARY}
|
| 82 |
+
)
|
| 83 |
+
endif()
|
| 84 |
+
endforeach()
|
| 85 |
+
|
| 86 |
+
set(OpenCVPkg_FOUND TRUE)
|
collector/core/gripper/camera_service_src/third_party/libuvc/doxygen.conf
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
collector/core/gripper/camera_service_src/third_party/libuvc/include/libuvc/libuvc.h
ADDED
|
@@ -0,0 +1,833 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef LIBUVC_H
|
| 2 |
+
#define LIBUVC_H
|
| 3 |
+
|
| 4 |
+
#ifdef __cplusplus
|
| 5 |
+
extern "C" {
|
| 6 |
+
#endif
|
| 7 |
+
|
| 8 |
+
#include <stdio.h> // FILE
|
| 9 |
+
#include <stdint.h>
|
| 10 |
+
#include <sys/time.h>
|
| 11 |
+
#include <libuvc/libuvc_config.h>
|
| 12 |
+
|
| 13 |
+
struct libusb_context;
|
| 14 |
+
struct libusb_device_handle;
|
| 15 |
+
|
| 16 |
+
/** UVC error types, based on libusb errors
|
| 17 |
+
* @ingroup diag
|
| 18 |
+
*/
|
| 19 |
+
typedef enum uvc_error {
|
| 20 |
+
/** Success (no error) */
|
| 21 |
+
UVC_SUCCESS = 0,
|
| 22 |
+
/** Input/output error */
|
| 23 |
+
UVC_ERROR_IO = -1,
|
| 24 |
+
/** Invalid parameter */
|
| 25 |
+
UVC_ERROR_INVALID_PARAM = -2,
|
| 26 |
+
/** Access denied */
|
| 27 |
+
UVC_ERROR_ACCESS = -3,
|
| 28 |
+
/** No such device */
|
| 29 |
+
UVC_ERROR_NO_DEVICE = -4,
|
| 30 |
+
/** Entity not found */
|
| 31 |
+
UVC_ERROR_NOT_FOUND = -5,
|
| 32 |
+
/** Resource busy */
|
| 33 |
+
UVC_ERROR_BUSY = -6,
|
| 34 |
+
/** Operation timed out */
|
| 35 |
+
UVC_ERROR_TIMEOUT = -7,
|
| 36 |
+
/** Overflow */
|
| 37 |
+
UVC_ERROR_OVERFLOW = -8,
|
| 38 |
+
/** Pipe error */
|
| 39 |
+
UVC_ERROR_PIPE = -9,
|
| 40 |
+
/** System call interrupted */
|
| 41 |
+
UVC_ERROR_INTERRUPTED = -10,
|
| 42 |
+
/** Insufficient memory */
|
| 43 |
+
UVC_ERROR_NO_MEM = -11,
|
| 44 |
+
/** Operation not supported */
|
| 45 |
+
UVC_ERROR_NOT_SUPPORTED = -12,
|
| 46 |
+
/** Device is not UVC-compliant */
|
| 47 |
+
UVC_ERROR_INVALID_DEVICE = -50,
|
| 48 |
+
/** Mode not supported */
|
| 49 |
+
UVC_ERROR_INVALID_MODE = -51,
|
| 50 |
+
/** Resource has a callback (can't use polling and async) */
|
| 51 |
+
UVC_ERROR_CALLBACK_EXISTS = -52,
|
| 52 |
+
/** Undefined error */
|
| 53 |
+
UVC_ERROR_OTHER = -99
|
| 54 |
+
} uvc_error_t;
|
| 55 |
+
|
| 56 |
+
/** Color coding of stream, transport-independent
|
| 57 |
+
* @ingroup streaming
|
| 58 |
+
*/
|
| 59 |
+
enum uvc_frame_format {
|
| 60 |
+
UVC_FRAME_FORMAT_UNKNOWN = 0,
|
| 61 |
+
/** Any supported format */
|
| 62 |
+
UVC_FRAME_FORMAT_ANY = 0,
|
| 63 |
+
UVC_FRAME_FORMAT_UNCOMPRESSED,
|
| 64 |
+
UVC_FRAME_FORMAT_COMPRESSED,
|
| 65 |
+
/** YUYV/YUV2/YUV422: YUV encoding with one luminance value per pixel and
|
| 66 |
+
* one UV (chrominance) pair for every two pixels.
|
| 67 |
+
*/
|
| 68 |
+
UVC_FRAME_FORMAT_YUYV,
|
| 69 |
+
UVC_FRAME_FORMAT_UYVY,
|
| 70 |
+
/** 24-bit RGB */
|
| 71 |
+
UVC_FRAME_FORMAT_RGB,
|
| 72 |
+
UVC_FRAME_FORMAT_BGR,
|
| 73 |
+
/** Motion-JPEG (or JPEG) encoded images */
|
| 74 |
+
UVC_FRAME_FORMAT_MJPEG,
|
| 75 |
+
UVC_FRAME_FORMAT_H264,
|
| 76 |
+
/** Greyscale images */
|
| 77 |
+
UVC_FRAME_FORMAT_GRAY8,
|
| 78 |
+
UVC_FRAME_FORMAT_GRAY16,
|
| 79 |
+
/* Raw colour mosaic images */
|
| 80 |
+
UVC_FRAME_FORMAT_BY8,
|
| 81 |
+
UVC_FRAME_FORMAT_BA81,
|
| 82 |
+
UVC_FRAME_FORMAT_SGRBG8,
|
| 83 |
+
UVC_FRAME_FORMAT_SGBRG8,
|
| 84 |
+
UVC_FRAME_FORMAT_SRGGB8,
|
| 85 |
+
UVC_FRAME_FORMAT_SBGGR8,
|
| 86 |
+
/** YUV420: NV12 */
|
| 87 |
+
UVC_FRAME_FORMAT_NV12,
|
| 88 |
+
/** YUV: P010 */
|
| 89 |
+
UVC_FRAME_FORMAT_P010,
|
| 90 |
+
/** Number of formats understood */
|
| 91 |
+
UVC_FRAME_FORMAT_COUNT,
|
| 92 |
+
};
|
| 93 |
+
|
| 94 |
+
/* UVC_COLOR_FORMAT_* have been replaced with UVC_FRAME_FORMAT_*. Please use
|
| 95 |
+
* UVC_FRAME_FORMAT_* instead of using these. */
|
| 96 |
+
#define UVC_COLOR_FORMAT_UNKNOWN UVC_FRAME_FORMAT_UNKNOWN
|
| 97 |
+
#define UVC_COLOR_FORMAT_UNCOMPRESSED UVC_FRAME_FORMAT_UNCOMPRESSED
|
| 98 |
+
#define UVC_COLOR_FORMAT_COMPRESSED UVC_FRAME_FORMAT_COMPRESSED
|
| 99 |
+
#define UVC_COLOR_FORMAT_YUYV UVC_FRAME_FORMAT_YUYV
|
| 100 |
+
#define UVC_COLOR_FORMAT_UYVY UVC_FRAME_FORMAT_UYVY
|
| 101 |
+
#define UVC_COLOR_FORMAT_RGB UVC_FRAME_FORMAT_RGB
|
| 102 |
+
#define UVC_COLOR_FORMAT_BGR UVC_FRAME_FORMAT_BGR
|
| 103 |
+
#define UVC_COLOR_FORMAT_MJPEG UVC_FRAME_FORMAT_MJPEG
|
| 104 |
+
#define UVC_COLOR_FORMAT_GRAY8 UVC_FRAME_FORMAT_GRAY8
|
| 105 |
+
#define UVC_COLOR_FORMAT_GRAY16 UVC_FRAME_FORMAT_GRAY16
|
| 106 |
+
#define UVC_COLOR_FORMAT_NV12 UVC_FRAME_FORMAT_NV12
|
| 107 |
+
|
| 108 |
+
/** VideoStreaming interface descriptor subtype (A.6) */
|
| 109 |
+
enum uvc_vs_desc_subtype {
|
| 110 |
+
UVC_VS_UNDEFINED = 0x00,
|
| 111 |
+
UVC_VS_INPUT_HEADER = 0x01,
|
| 112 |
+
UVC_VS_OUTPUT_HEADER = 0x02,
|
| 113 |
+
UVC_VS_STILL_IMAGE_FRAME = 0x03,
|
| 114 |
+
UVC_VS_FORMAT_UNCOMPRESSED = 0x04,
|
| 115 |
+
UVC_VS_FRAME_UNCOMPRESSED = 0x05,
|
| 116 |
+
UVC_VS_FORMAT_MJPEG = 0x06,
|
| 117 |
+
UVC_VS_FRAME_MJPEG = 0x07,
|
| 118 |
+
UVC_VS_FORMAT_MPEG2TS = 0x0a,
|
| 119 |
+
UVC_VS_FORMAT_DV = 0x0c,
|
| 120 |
+
UVC_VS_COLORFORMAT = 0x0d,
|
| 121 |
+
UVC_VS_FORMAT_FRAME_BASED = 0x10,
|
| 122 |
+
UVC_VS_FRAME_FRAME_BASED = 0x11,
|
| 123 |
+
UVC_VS_FORMAT_STREAM_BASED = 0x12
|
| 124 |
+
};
|
| 125 |
+
|
| 126 |
+
struct uvc_format_desc;
|
| 127 |
+
struct uvc_frame_desc;
|
| 128 |
+
|
| 129 |
+
typedef struct uvc_still_frame_res {
|
| 130 |
+
struct uvc_still_frame_res *prev, *next;
|
| 131 |
+
uint8_t bResolutionIndex;
|
| 132 |
+
/** Image width */
|
| 133 |
+
uint16_t wWidth;
|
| 134 |
+
/** Image height */
|
| 135 |
+
uint16_t wHeight;
|
| 136 |
+
} uvc_still_frame_res_t;
|
| 137 |
+
|
| 138 |
+
typedef struct uvc_still_frame_desc {
|
| 139 |
+
struct uvc_format_desc *parent;
|
| 140 |
+
struct uvc_still_frame_desc *prev, *next;
|
| 141 |
+
/** Type of frame, such as JPEG frame or uncompressed frme */
|
| 142 |
+
enum uvc_vs_desc_subtype bDescriptorSubtype;
|
| 143 |
+
/** Index of the frame within the list of specs available for this format */
|
| 144 |
+
uint8_t bEndPointAddress;
|
| 145 |
+
uvc_still_frame_res_t* imageSizePatterns;
|
| 146 |
+
uint8_t bNumCompressionPattern;
|
| 147 |
+
/* indication of compression level, the higher, the more compression is applied to image */
|
| 148 |
+
uint8_t* bCompression;
|
| 149 |
+
} uvc_still_frame_desc_t;
|
| 150 |
+
|
| 151 |
+
/** Frame descriptor
|
| 152 |
+
*
|
| 153 |
+
* A "frame" is a configuration of a streaming format
|
| 154 |
+
* for a particular image size at one of possibly several
|
| 155 |
+
* available frame rates.
|
| 156 |
+
*/
|
| 157 |
+
typedef struct uvc_frame_desc {
|
| 158 |
+
struct uvc_format_desc *parent;
|
| 159 |
+
struct uvc_frame_desc *prev, *next;
|
| 160 |
+
/** Type of frame, such as JPEG frame or uncompressed frme */
|
| 161 |
+
enum uvc_vs_desc_subtype bDescriptorSubtype;
|
| 162 |
+
/** Index of the frame within the list of specs available for this format */
|
| 163 |
+
uint8_t bFrameIndex;
|
| 164 |
+
uint8_t bmCapabilities;
|
| 165 |
+
/** Image width */
|
| 166 |
+
uint16_t wWidth;
|
| 167 |
+
/** Image height */
|
| 168 |
+
uint16_t wHeight;
|
| 169 |
+
/** Bitrate of corresponding stream at minimal frame rate */
|
| 170 |
+
uint32_t dwMinBitRate;
|
| 171 |
+
/** Bitrate of corresponding stream at maximal frame rate */
|
| 172 |
+
uint32_t dwMaxBitRate;
|
| 173 |
+
/** Maximum number of bytes for a video frame */
|
| 174 |
+
uint32_t dwMaxVideoFrameBufferSize;
|
| 175 |
+
/** Default frame interval (in 100ns units) */
|
| 176 |
+
uint32_t dwDefaultFrameInterval;
|
| 177 |
+
/** Minimum frame interval for continuous mode (100ns units) */
|
| 178 |
+
uint32_t dwMinFrameInterval;
|
| 179 |
+
/** Maximum frame interval for continuous mode (100ns units) */
|
| 180 |
+
uint32_t dwMaxFrameInterval;
|
| 181 |
+
/** Granularity of frame interval range for continuous mode (100ns) */
|
| 182 |
+
uint32_t dwFrameIntervalStep;
|
| 183 |
+
/** Frame intervals */
|
| 184 |
+
uint8_t bFrameIntervalType;
|
| 185 |
+
/** number of bytes per line */
|
| 186 |
+
uint32_t dwBytesPerLine;
|
| 187 |
+
/** Available frame rates, zero-terminated (in 100ns units) */
|
| 188 |
+
uint32_t *intervals;
|
| 189 |
+
} uvc_frame_desc_t;
|
| 190 |
+
|
| 191 |
+
/** Format descriptor
|
| 192 |
+
*
|
| 193 |
+
* A "format" determines a stream's image type (e.g., raw YUYV or JPEG)
|
| 194 |
+
* and includes many "frame" configurations.
|
| 195 |
+
*/
|
| 196 |
+
typedef struct uvc_format_desc {
|
| 197 |
+
struct uvc_streaming_interface *parent;
|
| 198 |
+
struct uvc_format_desc *prev, *next;
|
| 199 |
+
/** Type of image stream, such as JPEG or uncompressed. */
|
| 200 |
+
enum uvc_vs_desc_subtype bDescriptorSubtype;
|
| 201 |
+
/** Identifier of this format within the VS interface's format list */
|
| 202 |
+
uint8_t bFormatIndex;
|
| 203 |
+
uint8_t bNumFrameDescriptors;
|
| 204 |
+
/** Format specifier */
|
| 205 |
+
union {
|
| 206 |
+
uint8_t guidFormat[16];
|
| 207 |
+
uint8_t fourccFormat[4];
|
| 208 |
+
};
|
| 209 |
+
/** Format-specific data */
|
| 210 |
+
union {
|
| 211 |
+
/** BPP for uncompressed stream */
|
| 212 |
+
uint8_t bBitsPerPixel;
|
| 213 |
+
/** Flags for JPEG stream */
|
| 214 |
+
uint8_t bmFlags;
|
| 215 |
+
};
|
| 216 |
+
/** Default {uvc_frame_desc} to choose given this format */
|
| 217 |
+
uint8_t bDefaultFrameIndex;
|
| 218 |
+
uint8_t bAspectRatioX;
|
| 219 |
+
uint8_t bAspectRatioY;
|
| 220 |
+
uint8_t bmInterlaceFlags;
|
| 221 |
+
uint8_t bCopyProtect;
|
| 222 |
+
uint8_t bVariableSize;
|
| 223 |
+
/** Available frame specifications for this format */
|
| 224 |
+
struct uvc_frame_desc *frame_descs;
|
| 225 |
+
struct uvc_still_frame_desc *still_frame_desc;
|
| 226 |
+
} uvc_format_desc_t;
|
| 227 |
+
|
| 228 |
+
/** UVC request code (A.8) */
|
| 229 |
+
enum uvc_req_code {
|
| 230 |
+
UVC_RC_UNDEFINED = 0x00,
|
| 231 |
+
UVC_SET_CUR = 0x01,
|
| 232 |
+
UVC_GET_CUR = 0x81,
|
| 233 |
+
UVC_GET_MIN = 0x82,
|
| 234 |
+
UVC_GET_MAX = 0x83,
|
| 235 |
+
UVC_GET_RES = 0x84,
|
| 236 |
+
UVC_GET_LEN = 0x85,
|
| 237 |
+
UVC_GET_INFO = 0x86,
|
| 238 |
+
UVC_GET_DEF = 0x87
|
| 239 |
+
};
|
| 240 |
+
|
| 241 |
+
enum uvc_device_power_mode {
|
| 242 |
+
UVC_VC_VIDEO_POWER_MODE_FULL = 0x000b,
|
| 243 |
+
UVC_VC_VIDEO_POWER_MODE_DEVICE_DEPENDENT = 0x001b,
|
| 244 |
+
};
|
| 245 |
+
|
| 246 |
+
/** Camera terminal control selector (A.9.4) */
|
| 247 |
+
enum uvc_ct_ctrl_selector {
|
| 248 |
+
UVC_CT_CONTROL_UNDEFINED = 0x00,
|
| 249 |
+
UVC_CT_SCANNING_MODE_CONTROL = 0x01,
|
| 250 |
+
UVC_CT_AE_MODE_CONTROL = 0x02,
|
| 251 |
+
UVC_CT_AE_PRIORITY_CONTROL = 0x03,
|
| 252 |
+
UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL = 0x04,
|
| 253 |
+
UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL = 0x05,
|
| 254 |
+
UVC_CT_FOCUS_ABSOLUTE_CONTROL = 0x06,
|
| 255 |
+
UVC_CT_FOCUS_RELATIVE_CONTROL = 0x07,
|
| 256 |
+
UVC_CT_FOCUS_AUTO_CONTROL = 0x08,
|
| 257 |
+
UVC_CT_IRIS_ABSOLUTE_CONTROL = 0x09,
|
| 258 |
+
UVC_CT_IRIS_RELATIVE_CONTROL = 0x0a,
|
| 259 |
+
UVC_CT_ZOOM_ABSOLUTE_CONTROL = 0x0b,
|
| 260 |
+
UVC_CT_ZOOM_RELATIVE_CONTROL = 0x0c,
|
| 261 |
+
UVC_CT_PANTILT_ABSOLUTE_CONTROL = 0x0d,
|
| 262 |
+
UVC_CT_PANTILT_RELATIVE_CONTROL = 0x0e,
|
| 263 |
+
UVC_CT_ROLL_ABSOLUTE_CONTROL = 0x0f,
|
| 264 |
+
UVC_CT_ROLL_RELATIVE_CONTROL = 0x10,
|
| 265 |
+
UVC_CT_PRIVACY_CONTROL = 0x11,
|
| 266 |
+
UVC_CT_FOCUS_SIMPLE_CONTROL = 0x12,
|
| 267 |
+
UVC_CT_DIGITAL_WINDOW_CONTROL = 0x13,
|
| 268 |
+
UVC_CT_REGION_OF_INTEREST_CONTROL = 0x14
|
| 269 |
+
};
|
| 270 |
+
|
| 271 |
+
/** Processing unit control selector (A.9.5) */
|
| 272 |
+
enum uvc_pu_ctrl_selector {
|
| 273 |
+
UVC_PU_CONTROL_UNDEFINED = 0x00,
|
| 274 |
+
UVC_PU_BACKLIGHT_COMPENSATION_CONTROL = 0x01,
|
| 275 |
+
UVC_PU_BRIGHTNESS_CONTROL = 0x02,
|
| 276 |
+
UVC_PU_CONTRAST_CONTROL = 0x03,
|
| 277 |
+
UVC_PU_GAIN_CONTROL = 0x04,
|
| 278 |
+
UVC_PU_POWER_LINE_FREQUENCY_CONTROL = 0x05,
|
| 279 |
+
UVC_PU_HUE_CONTROL = 0x06,
|
| 280 |
+
UVC_PU_SATURATION_CONTROL = 0x07,
|
| 281 |
+
UVC_PU_SHARPNESS_CONTROL = 0x08,
|
| 282 |
+
UVC_PU_GAMMA_CONTROL = 0x09,
|
| 283 |
+
UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL = 0x0a,
|
| 284 |
+
UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL = 0x0b,
|
| 285 |
+
UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL = 0x0c,
|
| 286 |
+
UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL = 0x0d,
|
| 287 |
+
UVC_PU_DIGITAL_MULTIPLIER_CONTROL = 0x0e,
|
| 288 |
+
UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL = 0x0f,
|
| 289 |
+
UVC_PU_HUE_AUTO_CONTROL = 0x10,
|
| 290 |
+
UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL = 0x11,
|
| 291 |
+
UVC_PU_ANALOG_LOCK_STATUS_CONTROL = 0x12,
|
| 292 |
+
UVC_PU_CONTRAST_AUTO_CONTROL = 0x13
|
| 293 |
+
};
|
| 294 |
+
|
| 295 |
+
/** USB terminal type (B.1) */
|
| 296 |
+
enum uvc_term_type {
|
| 297 |
+
UVC_TT_VENDOR_SPECIFIC = 0x0100,
|
| 298 |
+
UVC_TT_STREAMING = 0x0101
|
| 299 |
+
};
|
| 300 |
+
|
| 301 |
+
/** Input terminal type (B.2) */
|
| 302 |
+
enum uvc_it_type {
|
| 303 |
+
UVC_ITT_VENDOR_SPECIFIC = 0x0200,
|
| 304 |
+
UVC_ITT_CAMERA = 0x0201,
|
| 305 |
+
UVC_ITT_MEDIA_TRANSPORT_INPUT = 0x0202
|
| 306 |
+
};
|
| 307 |
+
|
| 308 |
+
/** Output terminal type (B.3) */
|
| 309 |
+
enum uvc_ot_type {
|
| 310 |
+
UVC_OTT_VENDOR_SPECIFIC = 0x0300,
|
| 311 |
+
UVC_OTT_DISPLAY = 0x0301,
|
| 312 |
+
UVC_OTT_MEDIA_TRANSPORT_OUTPUT = 0x0302
|
| 313 |
+
};
|
| 314 |
+
|
| 315 |
+
/** External terminal type (B.4) */
|
| 316 |
+
enum uvc_et_type {
|
| 317 |
+
UVC_EXTERNAL_VENDOR_SPECIFIC = 0x0400,
|
| 318 |
+
UVC_COMPOSITE_CONNECTOR = 0x0401,
|
| 319 |
+
UVC_SVIDEO_CONNECTOR = 0x0402,
|
| 320 |
+
UVC_COMPONENT_CONNECTOR = 0x0403
|
| 321 |
+
};
|
| 322 |
+
|
| 323 |
+
/** Context, equivalent to libusb's contexts.
|
| 324 |
+
*
|
| 325 |
+
* May either own a libusb context or use one that's already made.
|
| 326 |
+
*
|
| 327 |
+
* Always create these with uvc_get_context.
|
| 328 |
+
*/
|
| 329 |
+
struct uvc_context;
|
| 330 |
+
typedef struct uvc_context uvc_context_t;
|
| 331 |
+
|
| 332 |
+
/** UVC device.
|
| 333 |
+
*
|
| 334 |
+
* Get this from uvc_get_device_list() or uvc_find_device().
|
| 335 |
+
*/
|
| 336 |
+
struct uvc_device;
|
| 337 |
+
typedef struct uvc_device uvc_device_t;
|
| 338 |
+
|
| 339 |
+
/** Handle on an open UVC device.
|
| 340 |
+
*
|
| 341 |
+
* Get one of these from uvc_open(). Once you uvc_close()
|
| 342 |
+
* it, it's no longer valid.
|
| 343 |
+
*/
|
| 344 |
+
struct uvc_device_handle;
|
| 345 |
+
typedef struct uvc_device_handle uvc_device_handle_t;
|
| 346 |
+
|
| 347 |
+
/** Handle on an open UVC stream.
|
| 348 |
+
*
|
| 349 |
+
* Get one of these from uvc_stream_open*().
|
| 350 |
+
* Once you uvc_stream_close() it, it will no longer be valid.
|
| 351 |
+
*/
|
| 352 |
+
struct uvc_stream_handle;
|
| 353 |
+
typedef struct uvc_stream_handle uvc_stream_handle_t;
|
| 354 |
+
|
| 355 |
+
/** Representation of the interface that brings data into the UVC device */
|
| 356 |
+
typedef struct uvc_input_terminal {
|
| 357 |
+
struct uvc_input_terminal *prev, *next;
|
| 358 |
+
/** Index of the terminal within the device */
|
| 359 |
+
uint8_t bTerminalID;
|
| 360 |
+
/** Type of terminal (e.g., camera) */
|
| 361 |
+
enum uvc_it_type wTerminalType;
|
| 362 |
+
uint16_t wObjectiveFocalLengthMin;
|
| 363 |
+
uint16_t wObjectiveFocalLengthMax;
|
| 364 |
+
uint16_t wOcularFocalLength;
|
| 365 |
+
/** Camera controls (meaning of bits given in {uvc_ct_ctrl_selector}) */
|
| 366 |
+
uint64_t bmControls;
|
| 367 |
+
} uvc_input_terminal_t;
|
| 368 |
+
|
| 369 |
+
typedef struct uvc_output_terminal {
|
| 370 |
+
struct uvc_output_terminal *prev, *next;
|
| 371 |
+
/** @todo */
|
| 372 |
+
} uvc_output_terminal_t;
|
| 373 |
+
|
| 374 |
+
/** Represents post-capture processing functions */
|
| 375 |
+
typedef struct uvc_processing_unit {
|
| 376 |
+
struct uvc_processing_unit *prev, *next;
|
| 377 |
+
/** Index of the processing unit within the device */
|
| 378 |
+
uint8_t bUnitID;
|
| 379 |
+
/** Index of the terminal from which the device accepts images */
|
| 380 |
+
uint8_t bSourceID;
|
| 381 |
+
/** Processing controls (meaning of bits given in {uvc_pu_ctrl_selector}) */
|
| 382 |
+
uint64_t bmControls;
|
| 383 |
+
} uvc_processing_unit_t;
|
| 384 |
+
|
| 385 |
+
/** Represents selector unit to connect other units */
|
| 386 |
+
typedef struct uvc_selector_unit {
|
| 387 |
+
struct uvc_selector_unit *prev, *next;
|
| 388 |
+
/** Index of the selector unit within the device */
|
| 389 |
+
uint8_t bUnitID;
|
| 390 |
+
} uvc_selector_unit_t;
|
| 391 |
+
|
| 392 |
+
/** Custom processing or camera-control functions */
|
| 393 |
+
typedef struct uvc_extension_unit {
|
| 394 |
+
struct uvc_extension_unit *prev, *next;
|
| 395 |
+
/** Index of the extension unit within the device */
|
| 396 |
+
uint8_t bUnitID;
|
| 397 |
+
/** GUID identifying the extension unit */
|
| 398 |
+
uint8_t guidExtensionCode[16];
|
| 399 |
+
/** Bitmap of available controls (manufacturer-dependent) */
|
| 400 |
+
uint64_t bmControls;
|
| 401 |
+
} uvc_extension_unit_t;
|
| 402 |
+
|
| 403 |
+
enum uvc_status_class {
|
| 404 |
+
UVC_STATUS_CLASS_CONTROL = 0x10,
|
| 405 |
+
UVC_STATUS_CLASS_CONTROL_CAMERA = 0x11,
|
| 406 |
+
UVC_STATUS_CLASS_CONTROL_PROCESSING = 0x12,
|
| 407 |
+
};
|
| 408 |
+
|
| 409 |
+
enum uvc_status_attribute {
|
| 410 |
+
UVC_STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00,
|
| 411 |
+
UVC_STATUS_ATTRIBUTE_INFO_CHANGE = 0x01,
|
| 412 |
+
UVC_STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02,
|
| 413 |
+
UVC_STATUS_ATTRIBUTE_UNKNOWN = 0xff
|
| 414 |
+
};
|
| 415 |
+
|
| 416 |
+
/** A callback function to accept status updates
|
| 417 |
+
* @ingroup device
|
| 418 |
+
*/
|
| 419 |
+
typedef void(uvc_status_callback_t)(enum uvc_status_class status_class,
|
| 420 |
+
int event,
|
| 421 |
+
int selector,
|
| 422 |
+
enum uvc_status_attribute status_attribute,
|
| 423 |
+
void *data, size_t data_len,
|
| 424 |
+
void *user_ptr);
|
| 425 |
+
|
| 426 |
+
/** A callback function to accept button events
|
| 427 |
+
* @ingroup device
|
| 428 |
+
*/
|
| 429 |
+
typedef void(uvc_button_callback_t)(int button,
|
| 430 |
+
int state,
|
| 431 |
+
void *user_ptr);
|
| 432 |
+
|
| 433 |
+
/** Structure representing a UVC device descriptor.
|
| 434 |
+
*
|
| 435 |
+
* (This isn't a standard structure.)
|
| 436 |
+
*/
|
| 437 |
+
typedef struct uvc_device_descriptor {
|
| 438 |
+
/** Vendor ID */
|
| 439 |
+
uint16_t idVendor;
|
| 440 |
+
/** Product ID */
|
| 441 |
+
uint16_t idProduct;
|
| 442 |
+
/** UVC compliance level, e.g. 0x0100 (1.0), 0x0110 */
|
| 443 |
+
uint16_t bcdUVC;
|
| 444 |
+
/** Serial number (null if unavailable) */
|
| 445 |
+
const char *serialNumber;
|
| 446 |
+
/** Device-reported manufacturer name (or null) */
|
| 447 |
+
const char *manufacturer;
|
| 448 |
+
/** Device-reporter product name (or null) */
|
| 449 |
+
const char *product;
|
| 450 |
+
} uvc_device_descriptor_t;
|
| 451 |
+
|
| 452 |
+
/** An image frame received from the UVC device
|
| 453 |
+
* @ingroup streaming
|
| 454 |
+
*/
|
| 455 |
+
typedef struct uvc_frame {
|
| 456 |
+
/** Image data for this frame */
|
| 457 |
+
void *data;
|
| 458 |
+
/** Size of image data buffer */
|
| 459 |
+
size_t data_bytes;
|
| 460 |
+
/** Width of image in pixels */
|
| 461 |
+
uint32_t width;
|
| 462 |
+
/** Height of image in pixels */
|
| 463 |
+
uint32_t height;
|
| 464 |
+
/** Pixel data format */
|
| 465 |
+
enum uvc_frame_format frame_format;
|
| 466 |
+
/** Number of bytes per horizontal line (undefined for compressed format) */
|
| 467 |
+
size_t step;
|
| 468 |
+
/** Frame number (may skip, but is strictly monotonically increasing) */
|
| 469 |
+
uint32_t sequence;
|
| 470 |
+
/** Estimate of system time when the device started capturing the image */
|
| 471 |
+
struct timeval capture_time;
|
| 472 |
+
/** Estimate of system time when the device finished receiving the image */
|
| 473 |
+
struct timespec capture_time_finished;
|
| 474 |
+
/** Handle on the device that produced the image.
|
| 475 |
+
* @warning You must not call any uvc_* functions during a callback. */
|
| 476 |
+
uvc_device_handle_t *source;
|
| 477 |
+
/** Is the data buffer owned by the library?
|
| 478 |
+
* If 1, the data buffer can be arbitrarily reallocated by frame conversion
|
| 479 |
+
* functions.
|
| 480 |
+
* If 0, the data buffer will not be reallocated or freed by the library.
|
| 481 |
+
* Set this field to zero if you are supplying the buffer.
|
| 482 |
+
*/
|
| 483 |
+
uint8_t library_owns_data;
|
| 484 |
+
/** Metadata for this frame if available */
|
| 485 |
+
void *metadata;
|
| 486 |
+
/** Size of metadata buffer */
|
| 487 |
+
size_t metadata_bytes;
|
| 488 |
+
} uvc_frame_t;
|
| 489 |
+
|
| 490 |
+
/** A callback function to handle incoming assembled UVC frames
|
| 491 |
+
* @ingroup streaming
|
| 492 |
+
*/
|
| 493 |
+
typedef void(uvc_frame_callback_t)(struct uvc_frame *frame, void *user_ptr);
|
| 494 |
+
|
| 495 |
+
/** Streaming mode, includes all information needed to select stream
|
| 496 |
+
* @ingroup streaming
|
| 497 |
+
*/
|
| 498 |
+
typedef struct uvc_stream_ctrl {
|
| 499 |
+
uint16_t bmHint;
|
| 500 |
+
uint8_t bFormatIndex;
|
| 501 |
+
uint8_t bFrameIndex;
|
| 502 |
+
uint32_t dwFrameInterval;
|
| 503 |
+
uint16_t wKeyFrameRate;
|
| 504 |
+
uint16_t wPFrameRate;
|
| 505 |
+
uint16_t wCompQuality;
|
| 506 |
+
uint16_t wCompWindowSize;
|
| 507 |
+
uint16_t wDelay;
|
| 508 |
+
uint32_t dwMaxVideoFrameSize;
|
| 509 |
+
uint32_t dwMaxPayloadTransferSize;
|
| 510 |
+
uint32_t dwClockFrequency;
|
| 511 |
+
uint8_t bmFramingInfo;
|
| 512 |
+
uint8_t bPreferredVersion;
|
| 513 |
+
uint8_t bMinVersion;
|
| 514 |
+
uint8_t bMaxVersion;
|
| 515 |
+
uint8_t bInterfaceNumber;
|
| 516 |
+
} uvc_stream_ctrl_t;
|
| 517 |
+
|
| 518 |
+
typedef struct uvc_still_ctrl {
|
| 519 |
+
/* Video format index from a format descriptor */
|
| 520 |
+
uint8_t bFormatIndex;
|
| 521 |
+
/* Video frame index from a frame descriptor */
|
| 522 |
+
uint8_t bFrameIndex;
|
| 523 |
+
/* Compression index from a frame descriptor */
|
| 524 |
+
uint8_t bCompressionIndex;
|
| 525 |
+
/* Maximum still image size in bytes. */
|
| 526 |
+
uint32_t dwMaxVideoFrameSize;
|
| 527 |
+
/* Maximum number of byte per payload*/
|
| 528 |
+
uint32_t dwMaxPayloadTransferSize;
|
| 529 |
+
uint8_t bInterfaceNumber;
|
| 530 |
+
} uvc_still_ctrl_t;
|
| 531 |
+
|
| 532 |
+
uvc_error_t uvc_init(uvc_context_t **ctx, struct libusb_context *usb_ctx);
|
| 533 |
+
void uvc_exit(uvc_context_t *ctx);
|
| 534 |
+
|
| 535 |
+
uvc_error_t uvc_get_device_list(
|
| 536 |
+
uvc_context_t *ctx,
|
| 537 |
+
uvc_device_t ***list);
|
| 538 |
+
void uvc_free_device_list(uvc_device_t **list, uint8_t unref_devices);
|
| 539 |
+
|
| 540 |
+
uvc_error_t uvc_get_device_descriptor(
|
| 541 |
+
uvc_device_t *dev,
|
| 542 |
+
uvc_device_descriptor_t **desc);
|
| 543 |
+
void uvc_free_device_descriptor(
|
| 544 |
+
uvc_device_descriptor_t *desc);
|
| 545 |
+
|
| 546 |
+
uint8_t uvc_get_bus_number(uvc_device_t *dev);
|
| 547 |
+
uint8_t uvc_get_device_address(uvc_device_t *dev);
|
| 548 |
+
/** Get the USB port path below the device's root hub. */
|
| 549 |
+
int uvc_get_port_numbers(uvc_device_t *dev, uint8_t *ports,
|
| 550 |
+
uint8_t port_count);
|
| 551 |
+
|
| 552 |
+
uvc_error_t uvc_find_device(
|
| 553 |
+
uvc_context_t *ctx,
|
| 554 |
+
uvc_device_t **dev,
|
| 555 |
+
int vid, int pid, const char *sn);
|
| 556 |
+
|
| 557 |
+
uvc_error_t uvc_find_devices(
|
| 558 |
+
uvc_context_t *ctx,
|
| 559 |
+
uvc_device_t ***devs,
|
| 560 |
+
int vid, int pid, const char *sn);
|
| 561 |
+
|
| 562 |
+
#if LIBUSB_API_VERSION >= 0x01000107
|
| 563 |
+
uvc_error_t uvc_wrap(
|
| 564 |
+
int sys_dev,
|
| 565 |
+
uvc_context_t *context,
|
| 566 |
+
uvc_device_handle_t **devh);
|
| 567 |
+
#endif
|
| 568 |
+
|
| 569 |
+
uvc_error_t uvc_open(
|
| 570 |
+
uvc_device_t *dev,
|
| 571 |
+
uvc_device_handle_t **devh);
|
| 572 |
+
void uvc_close(uvc_device_handle_t *devh);
|
| 573 |
+
|
| 574 |
+
uvc_device_t *uvc_get_device(uvc_device_handle_t *devh);
|
| 575 |
+
struct libusb_device_handle *uvc_get_libusb_handle(uvc_device_handle_t *devh);
|
| 576 |
+
|
| 577 |
+
/** Pin the altsetting (and hence the ISO payload) used for this device.
|
| 578 |
+
*
|
| 579 |
+
* The compile-time quirks table in stream.c is a per-model default. A caller
|
| 580 |
+
* that has measured which setting actually holds for a particular camera and
|
| 581 |
+
* host can override it per open device handle. The override is applied to every
|
| 582 |
+
* stream opened on this handle and wins over the table; pass altsetting <= 0 to
|
| 583 |
+
* clear it and fall back to the table. It takes effect on the next
|
| 584 |
+
* uvc_start_streaming()/uvc_stream_start(); it does not touch a running stream.
|
| 585 |
+
*
|
| 586 |
+
* @param[in] devh Device handle from uvc_open()
|
| 587 |
+
* @param[in] altsetting bAlternateSetting to force, or <= 0 to clear
|
| 588 |
+
* @param[in] payload Payload in bytes to commit and size the ISO transfers for
|
| 589 |
+
* @ingroup stream
|
| 590 |
+
*/
|
| 591 |
+
uvc_error_t uvc_set_altsetting_override(
|
| 592 |
+
uvc_device_handle_t *devh,
|
| 593 |
+
int altsetting,
|
| 594 |
+
unsigned int payload);
|
| 595 |
+
|
| 596 |
+
void uvc_ref_device(uvc_device_t *dev);
|
| 597 |
+
void uvc_unref_device(uvc_device_t *dev);
|
| 598 |
+
|
| 599 |
+
void uvc_set_status_callback(uvc_device_handle_t *devh,
|
| 600 |
+
uvc_status_callback_t cb,
|
| 601 |
+
void *user_ptr);
|
| 602 |
+
|
| 603 |
+
void uvc_set_button_callback(uvc_device_handle_t *devh,
|
| 604 |
+
uvc_button_callback_t cb,
|
| 605 |
+
void *user_ptr);
|
| 606 |
+
|
| 607 |
+
const uvc_input_terminal_t *uvc_get_camera_terminal(uvc_device_handle_t *devh);
|
| 608 |
+
const uvc_input_terminal_t *uvc_get_input_terminals(uvc_device_handle_t *devh);
|
| 609 |
+
const uvc_output_terminal_t *uvc_get_output_terminals(uvc_device_handle_t *devh);
|
| 610 |
+
const uvc_selector_unit_t *uvc_get_selector_units(uvc_device_handle_t *devh);
|
| 611 |
+
const uvc_processing_unit_t *uvc_get_processing_units(uvc_device_handle_t *devh);
|
| 612 |
+
const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh);
|
| 613 |
+
|
| 614 |
+
uvc_error_t uvc_get_stream_ctrl_format_size(
|
| 615 |
+
uvc_device_handle_t *devh,
|
| 616 |
+
uvc_stream_ctrl_t *ctrl,
|
| 617 |
+
enum uvc_frame_format format,
|
| 618 |
+
int width, int height,
|
| 619 |
+
int fps
|
| 620 |
+
);
|
| 621 |
+
|
| 622 |
+
uvc_error_t uvc_get_still_ctrl_format_size(
|
| 623 |
+
uvc_device_handle_t *devh,
|
| 624 |
+
uvc_stream_ctrl_t *ctrl,
|
| 625 |
+
uvc_still_ctrl_t *still_ctrl,
|
| 626 |
+
int width, int height);
|
| 627 |
+
|
| 628 |
+
uvc_error_t uvc_trigger_still(
|
| 629 |
+
uvc_device_handle_t *devh,
|
| 630 |
+
uvc_still_ctrl_t *still_ctrl);
|
| 631 |
+
|
| 632 |
+
const uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t* );
|
| 633 |
+
|
| 634 |
+
uvc_error_t uvc_probe_stream_ctrl(
|
| 635 |
+
uvc_device_handle_t *devh,
|
| 636 |
+
uvc_stream_ctrl_t *ctrl);
|
| 637 |
+
|
| 638 |
+
uvc_error_t uvc_probe_still_ctrl(
|
| 639 |
+
uvc_device_handle_t *devh,
|
| 640 |
+
uvc_still_ctrl_t *still_ctrl);
|
| 641 |
+
|
| 642 |
+
uvc_error_t uvc_start_streaming(
|
| 643 |
+
uvc_device_handle_t *devh,
|
| 644 |
+
uvc_stream_ctrl_t *ctrl,
|
| 645 |
+
uvc_frame_callback_t *cb,
|
| 646 |
+
void *user_ptr,
|
| 647 |
+
uint8_t flags);
|
| 648 |
+
|
| 649 |
+
uvc_error_t uvc_start_iso_streaming(
|
| 650 |
+
uvc_device_handle_t *devh,
|
| 651 |
+
uvc_stream_ctrl_t *ctrl,
|
| 652 |
+
uvc_frame_callback_t *cb,
|
| 653 |
+
void *user_ptr);
|
| 654 |
+
|
| 655 |
+
void uvc_stop_streaming(uvc_device_handle_t *devh);
|
| 656 |
+
|
| 657 |
+
uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t **strmh, uvc_stream_ctrl_t *ctrl);
|
| 658 |
+
uvc_error_t uvc_stream_ctrl(uvc_stream_handle_t *strmh, uvc_stream_ctrl_t *ctrl);
|
| 659 |
+
uvc_error_t uvc_stream_start(uvc_stream_handle_t *strmh,
|
| 660 |
+
uvc_frame_callback_t *cb,
|
| 661 |
+
void *user_ptr,
|
| 662 |
+
uint8_t flags);
|
| 663 |
+
uvc_error_t uvc_stream_start_iso(uvc_stream_handle_t *strmh,
|
| 664 |
+
uvc_frame_callback_t *cb,
|
| 665 |
+
void *user_ptr);
|
| 666 |
+
uvc_error_t uvc_stream_get_frame(
|
| 667 |
+
uvc_stream_handle_t *strmh,
|
| 668 |
+
uvc_frame_t **frame,
|
| 669 |
+
int32_t timeout_us
|
| 670 |
+
);
|
| 671 |
+
uvc_error_t uvc_stream_stop(uvc_stream_handle_t *strmh);
|
| 672 |
+
void uvc_stream_close(uvc_stream_handle_t *strmh);
|
| 673 |
+
|
| 674 |
+
int uvc_get_ctrl_len(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl);
|
| 675 |
+
int uvc_get_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len, enum uvc_req_code req_code);
|
| 676 |
+
int uvc_set_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len);
|
| 677 |
+
|
| 678 |
+
uvc_error_t uvc_get_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode *mode, enum uvc_req_code req_code);
|
| 679 |
+
uvc_error_t uvc_set_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode mode);
|
| 680 |
+
|
| 681 |
+
/* AUTO-GENERATED control accessors! Update them with the output of `ctrl-gen.py decl`. */
|
| 682 |
+
uvc_error_t uvc_get_scanning_mode(uvc_device_handle_t *devh, uint8_t* mode, enum uvc_req_code req_code);
|
| 683 |
+
uvc_error_t uvc_set_scanning_mode(uvc_device_handle_t *devh, uint8_t mode);
|
| 684 |
+
|
| 685 |
+
uvc_error_t uvc_get_ae_mode(uvc_device_handle_t *devh, uint8_t* mode, enum uvc_req_code req_code);
|
| 686 |
+
uvc_error_t uvc_set_ae_mode(uvc_device_handle_t *devh, uint8_t mode);
|
| 687 |
+
|
| 688 |
+
uvc_error_t uvc_get_ae_priority(uvc_device_handle_t *devh, uint8_t* priority, enum uvc_req_code req_code);
|
| 689 |
+
uvc_error_t uvc_set_ae_priority(uvc_device_handle_t *devh, uint8_t priority);
|
| 690 |
+
|
| 691 |
+
uvc_error_t uvc_get_exposure_abs(uvc_device_handle_t *devh, uint32_t* time, enum uvc_req_code req_code);
|
| 692 |
+
uvc_error_t uvc_set_exposure_abs(uvc_device_handle_t *devh, uint32_t time);
|
| 693 |
+
|
| 694 |
+
uvc_error_t uvc_get_exposure_rel(uvc_device_handle_t *devh, int8_t* step, enum uvc_req_code req_code);
|
| 695 |
+
uvc_error_t uvc_set_exposure_rel(uvc_device_handle_t *devh, int8_t step);
|
| 696 |
+
|
| 697 |
+
uvc_error_t uvc_get_focus_abs(uvc_device_handle_t *devh, uint16_t* focus, enum uvc_req_code req_code);
|
| 698 |
+
uvc_error_t uvc_set_focus_abs(uvc_device_handle_t *devh, uint16_t focus);
|
| 699 |
+
|
| 700 |
+
uvc_error_t uvc_get_focus_rel(uvc_device_handle_t *devh, int8_t* focus_rel, uint8_t* speed, enum uvc_req_code req_code);
|
| 701 |
+
uvc_error_t uvc_set_focus_rel(uvc_device_handle_t *devh, int8_t focus_rel, uint8_t speed);
|
| 702 |
+
|
| 703 |
+
uvc_error_t uvc_get_focus_simple_range(uvc_device_handle_t *devh, uint8_t* focus, enum uvc_req_code req_code);
|
| 704 |
+
uvc_error_t uvc_set_focus_simple_range(uvc_device_handle_t *devh, uint8_t focus);
|
| 705 |
+
|
| 706 |
+
uvc_error_t uvc_get_focus_auto(uvc_device_handle_t *devh, uint8_t* state, enum uvc_req_code req_code);
|
| 707 |
+
uvc_error_t uvc_set_focus_auto(uvc_device_handle_t *devh, uint8_t state);
|
| 708 |
+
|
| 709 |
+
uvc_error_t uvc_get_iris_abs(uvc_device_handle_t *devh, uint16_t* iris, enum uvc_req_code req_code);
|
| 710 |
+
uvc_error_t uvc_set_iris_abs(uvc_device_handle_t *devh, uint16_t iris);
|
| 711 |
+
|
| 712 |
+
uvc_error_t uvc_get_iris_rel(uvc_device_handle_t *devh, uint8_t* iris_rel, enum uvc_req_code req_code);
|
| 713 |
+
uvc_error_t uvc_set_iris_rel(uvc_device_handle_t *devh, uint8_t iris_rel);
|
| 714 |
+
|
| 715 |
+
uvc_error_t uvc_get_zoom_abs(uvc_device_handle_t *devh, uint16_t* focal_length, enum uvc_req_code req_code);
|
| 716 |
+
uvc_error_t uvc_set_zoom_abs(uvc_device_handle_t *devh, uint16_t focal_length);
|
| 717 |
+
|
| 718 |
+
uvc_error_t uvc_get_zoom_rel(uvc_device_handle_t *devh, int8_t* zoom_rel, uint8_t* digital_zoom, uint8_t* speed, enum uvc_req_code req_code);
|
| 719 |
+
uvc_error_t uvc_set_zoom_rel(uvc_device_handle_t *devh, int8_t zoom_rel, uint8_t digital_zoom, uint8_t speed);
|
| 720 |
+
|
| 721 |
+
uvc_error_t uvc_get_pantilt_abs(uvc_device_handle_t *devh, int32_t* pan, int32_t* tilt, enum uvc_req_code req_code);
|
| 722 |
+
uvc_error_t uvc_set_pantilt_abs(uvc_device_handle_t *devh, int32_t pan, int32_t tilt);
|
| 723 |
+
|
| 724 |
+
uvc_error_t uvc_get_pantilt_rel(uvc_device_handle_t *devh, int8_t* pan_rel, uint8_t* pan_speed, int8_t* tilt_rel, uint8_t* tilt_speed, enum uvc_req_code req_code);
|
| 725 |
+
uvc_error_t uvc_set_pantilt_rel(uvc_device_handle_t *devh, int8_t pan_rel, uint8_t pan_speed, int8_t tilt_rel, uint8_t tilt_speed);
|
| 726 |
+
|
| 727 |
+
uvc_error_t uvc_get_roll_abs(uvc_device_handle_t *devh, int16_t* roll, enum uvc_req_code req_code);
|
| 728 |
+
uvc_error_t uvc_set_roll_abs(uvc_device_handle_t *devh, int16_t roll);
|
| 729 |
+
|
| 730 |
+
uvc_error_t uvc_get_roll_rel(uvc_device_handle_t *devh, int8_t* roll_rel, uint8_t* speed, enum uvc_req_code req_code);
|
| 731 |
+
uvc_error_t uvc_set_roll_rel(uvc_device_handle_t *devh, int8_t roll_rel, uint8_t speed);
|
| 732 |
+
|
| 733 |
+
uvc_error_t uvc_get_privacy(uvc_device_handle_t *devh, uint8_t* privacy, enum uvc_req_code req_code);
|
| 734 |
+
uvc_error_t uvc_set_privacy(uvc_device_handle_t *devh, uint8_t privacy);
|
| 735 |
+
|
| 736 |
+
uvc_error_t uvc_get_digital_window(uvc_device_handle_t *devh, uint16_t* window_top, uint16_t* window_left, uint16_t* window_bottom, uint16_t* window_right, uint16_t* num_steps, uint16_t* num_steps_units, enum uvc_req_code req_code);
|
| 737 |
+
uvc_error_t uvc_set_digital_window(uvc_device_handle_t *devh, uint16_t window_top, uint16_t window_left, uint16_t window_bottom, uint16_t window_right, uint16_t num_steps, uint16_t num_steps_units);
|
| 738 |
+
|
| 739 |
+
uvc_error_t uvc_get_digital_roi(uvc_device_handle_t *devh, uint16_t* roi_top, uint16_t* roi_left, uint16_t* roi_bottom, uint16_t* roi_right, uint16_t* auto_controls, enum uvc_req_code req_code);
|
| 740 |
+
uvc_error_t uvc_set_digital_roi(uvc_device_handle_t *devh, uint16_t roi_top, uint16_t roi_left, uint16_t roi_bottom, uint16_t roi_right, uint16_t auto_controls);
|
| 741 |
+
|
| 742 |
+
uvc_error_t uvc_get_backlight_compensation(uvc_device_handle_t *devh, uint16_t* backlight_compensation, enum uvc_req_code req_code);
|
| 743 |
+
uvc_error_t uvc_set_backlight_compensation(uvc_device_handle_t *devh, uint16_t backlight_compensation);
|
| 744 |
+
|
| 745 |
+
uvc_error_t uvc_get_brightness(uvc_device_handle_t *devh, int16_t* brightness, enum uvc_req_code req_code);
|
| 746 |
+
uvc_error_t uvc_set_brightness(uvc_device_handle_t *devh, int16_t brightness);
|
| 747 |
+
|
| 748 |
+
uvc_error_t uvc_get_contrast(uvc_device_handle_t *devh, uint16_t* contrast, enum uvc_req_code req_code);
|
| 749 |
+
uvc_error_t uvc_set_contrast(uvc_device_handle_t *devh, uint16_t contrast);
|
| 750 |
+
|
| 751 |
+
uvc_error_t uvc_get_contrast_auto(uvc_device_handle_t *devh, uint8_t* contrast_auto, enum uvc_req_code req_code);
|
| 752 |
+
uvc_error_t uvc_set_contrast_auto(uvc_device_handle_t *devh, uint8_t contrast_auto);
|
| 753 |
+
|
| 754 |
+
uvc_error_t uvc_get_gain(uvc_device_handle_t *devh, uint16_t* gain, enum uvc_req_code req_code);
|
| 755 |
+
uvc_error_t uvc_set_gain(uvc_device_handle_t *devh, uint16_t gain);
|
| 756 |
+
|
| 757 |
+
uvc_error_t uvc_get_power_line_frequency(uvc_device_handle_t *devh, uint8_t* power_line_frequency, enum uvc_req_code req_code);
|
| 758 |
+
uvc_error_t uvc_set_power_line_frequency(uvc_device_handle_t *devh, uint8_t power_line_frequency);
|
| 759 |
+
|
| 760 |
+
uvc_error_t uvc_get_hue(uvc_device_handle_t *devh, int16_t* hue, enum uvc_req_code req_code);
|
| 761 |
+
uvc_error_t uvc_set_hue(uvc_device_handle_t *devh, int16_t hue);
|
| 762 |
+
|
| 763 |
+
uvc_error_t uvc_get_hue_auto(uvc_device_handle_t *devh, uint8_t* hue_auto, enum uvc_req_code req_code);
|
| 764 |
+
uvc_error_t uvc_set_hue_auto(uvc_device_handle_t *devh, uint8_t hue_auto);
|
| 765 |
+
|
| 766 |
+
uvc_error_t uvc_get_saturation(uvc_device_handle_t *devh, uint16_t* saturation, enum uvc_req_code req_code);
|
| 767 |
+
uvc_error_t uvc_set_saturation(uvc_device_handle_t *devh, uint16_t saturation);
|
| 768 |
+
|
| 769 |
+
uvc_error_t uvc_get_sharpness(uvc_device_handle_t *devh, uint16_t* sharpness, enum uvc_req_code req_code);
|
| 770 |
+
uvc_error_t uvc_set_sharpness(uvc_device_handle_t *devh, uint16_t sharpness);
|
| 771 |
+
|
| 772 |
+
uvc_error_t uvc_get_gamma(uvc_device_handle_t *devh, uint16_t* gamma, enum uvc_req_code req_code);
|
| 773 |
+
uvc_error_t uvc_set_gamma(uvc_device_handle_t *devh, uint16_t gamma);
|
| 774 |
+
|
| 775 |
+
uvc_error_t uvc_get_white_balance_temperature(uvc_device_handle_t *devh, uint16_t* temperature, enum uvc_req_code req_code);
|
| 776 |
+
uvc_error_t uvc_set_white_balance_temperature(uvc_device_handle_t *devh, uint16_t temperature);
|
| 777 |
+
|
| 778 |
+
uvc_error_t uvc_get_white_balance_temperature_auto(uvc_device_handle_t *devh, uint8_t* temperature_auto, enum uvc_req_code req_code);
|
| 779 |
+
uvc_error_t uvc_set_white_balance_temperature_auto(uvc_device_handle_t *devh, uint8_t temperature_auto);
|
| 780 |
+
|
| 781 |
+
uvc_error_t uvc_get_white_balance_component(uvc_device_handle_t *devh, uint16_t* blue, uint16_t* red, enum uvc_req_code req_code);
|
| 782 |
+
uvc_error_t uvc_set_white_balance_component(uvc_device_handle_t *devh, uint16_t blue, uint16_t red);
|
| 783 |
+
|
| 784 |
+
uvc_error_t uvc_get_white_balance_component_auto(uvc_device_handle_t *devh, uint8_t* white_balance_component_auto, enum uvc_req_code req_code);
|
| 785 |
+
uvc_error_t uvc_set_white_balance_component_auto(uvc_device_handle_t *devh, uint8_t white_balance_component_auto);
|
| 786 |
+
|
| 787 |
+
uvc_error_t uvc_get_digital_multiplier(uvc_device_handle_t *devh, uint16_t* multiplier_step, enum uvc_req_code req_code);
|
| 788 |
+
uvc_error_t uvc_set_digital_multiplier(uvc_device_handle_t *devh, uint16_t multiplier_step);
|
| 789 |
+
|
| 790 |
+
uvc_error_t uvc_get_digital_multiplier_limit(uvc_device_handle_t *devh, uint16_t* multiplier_step, enum uvc_req_code req_code);
|
| 791 |
+
uvc_error_t uvc_set_digital_multiplier_limit(uvc_device_handle_t *devh, uint16_t multiplier_step);
|
| 792 |
+
|
| 793 |
+
uvc_error_t uvc_get_analog_video_standard(uvc_device_handle_t *devh, uint8_t* video_standard, enum uvc_req_code req_code);
|
| 794 |
+
uvc_error_t uvc_set_analog_video_standard(uvc_device_handle_t *devh, uint8_t video_standard);
|
| 795 |
+
|
| 796 |
+
uvc_error_t uvc_get_analog_video_lock_status(uvc_device_handle_t *devh, uint8_t* status, enum uvc_req_code req_code);
|
| 797 |
+
uvc_error_t uvc_set_analog_video_lock_status(uvc_device_handle_t *devh, uint8_t status);
|
| 798 |
+
|
| 799 |
+
uvc_error_t uvc_get_input_select(uvc_device_handle_t *devh, uint8_t* selector, enum uvc_req_code req_code);
|
| 800 |
+
uvc_error_t uvc_set_input_select(uvc_device_handle_t *devh, uint8_t selector);
|
| 801 |
+
/* end AUTO-GENERATED control accessors */
|
| 802 |
+
|
| 803 |
+
void uvc_perror(uvc_error_t err, const char *msg);
|
| 804 |
+
const char* uvc_strerror(uvc_error_t err);
|
| 805 |
+
void uvc_print_diag(uvc_device_handle_t *devh, FILE *stream);
|
| 806 |
+
void uvc_print_stream_ctrl(uvc_stream_ctrl_t *ctrl, FILE *stream);
|
| 807 |
+
|
| 808 |
+
uvc_frame_t *uvc_allocate_frame(size_t data_bytes);
|
| 809 |
+
void uvc_free_frame(uvc_frame_t *frame);
|
| 810 |
+
|
| 811 |
+
uvc_error_t uvc_duplicate_frame(uvc_frame_t *in, uvc_frame_t *out);
|
| 812 |
+
|
| 813 |
+
uvc_error_t uvc_yuyv2rgb(uvc_frame_t *in, uvc_frame_t *out);
|
| 814 |
+
uvc_error_t uvc_uyvy2rgb(uvc_frame_t *in, uvc_frame_t *out);
|
| 815 |
+
uvc_error_t uvc_any2rgb(uvc_frame_t *in, uvc_frame_t *out);
|
| 816 |
+
|
| 817 |
+
uvc_error_t uvc_yuyv2bgr(uvc_frame_t *in, uvc_frame_t *out);
|
| 818 |
+
uvc_error_t uvc_uyvy2bgr(uvc_frame_t *in, uvc_frame_t *out);
|
| 819 |
+
uvc_error_t uvc_any2bgr(uvc_frame_t *in, uvc_frame_t *out);
|
| 820 |
+
|
| 821 |
+
uvc_error_t uvc_yuyv2y(uvc_frame_t *in, uvc_frame_t *out);
|
| 822 |
+
uvc_error_t uvc_yuyv2uv(uvc_frame_t *in, uvc_frame_t *out);
|
| 823 |
+
|
| 824 |
+
#ifdef LIBUVC_HAS_JPEG
|
| 825 |
+
uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out);
|
| 826 |
+
uvc_error_t uvc_mjpeg2gray(uvc_frame_t *in, uvc_frame_t *out);
|
| 827 |
+
#endif
|
| 828 |
+
|
| 829 |
+
#ifdef __cplusplus
|
| 830 |
+
}
|
| 831 |
+
#endif
|
| 832 |
+
|
| 833 |
+
#endif // !def(LIBUVC_H)
|
collector/core/gripper/camera_service_src/third_party/libuvc/include/libuvc/libuvc_config.h.in
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef LIBUVC_CONFIG_H
|
| 2 |
+
#define LIBUVC_CONFIG_H
|
| 3 |
+
|
| 4 |
+
#define LIBUVC_VERSION_MAJOR @libuvc_VERSION_MAJOR@
|
| 5 |
+
#define LIBUVC_VERSION_MINOR @libuvc_VERSION_MINOR@
|
| 6 |
+
#define LIBUVC_VERSION_PATCH @libuvc_VERSION_PATCH@
|
| 7 |
+
#define LIBUVC_VERSION_STR "@libuvc_VERSION@"
|
| 8 |
+
#define LIBUVC_VERSION_INT \
|
| 9 |
+
((@libuvc_VERSION_MAJOR@ << 16) | \
|
| 10 |
+
(@libuvc_VERSION_MINOR@ << 8) | \
|
| 11 |
+
(@libuvc_VERSION_PATCH@))
|
| 12 |
+
|
| 13 |
+
/** @brief Test whether libuvc is new enough
|
| 14 |
+
* This macro evaluates true if and only if the current version is
|
| 15 |
+
* at least as new as the version specified.
|
| 16 |
+
*/
|
| 17 |
+
#define LIBUVC_VERSION_GTE(major, minor, patch) \
|
| 18 |
+
(LIBUVC_VERSION_INT >= (((major) << 16) | ((minor) << 8) | (patch)))
|
| 19 |
+
|
| 20 |
+
#cmakedefine LIBUVC_HAS_JPEG 1
|
| 21 |
+
|
| 22 |
+
#endif // !def(LIBUVC_CONFIG_H)
|
collector/core/gripper/camera_service_src/third_party/libuvc/include/libuvc/libuvc_internal.h
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/** @file libuvc_internal.h
|
| 2 |
+
* @brief Implementation-specific UVC constants and structures.
|
| 3 |
+
* @cond include_hidden
|
| 4 |
+
*/
|
| 5 |
+
#ifndef LIBUVC_INTERNAL_H
|
| 6 |
+
#define LIBUVC_INTERNAL_H
|
| 7 |
+
|
| 8 |
+
#include <assert.h>
|
| 9 |
+
#include <stdlib.h>
|
| 10 |
+
#include <stdio.h>
|
| 11 |
+
#include <string.h>
|
| 12 |
+
#include <pthread.h>
|
| 13 |
+
#include <signal.h>
|
| 14 |
+
#include <libusb.h>
|
| 15 |
+
#include "utlist.h"
|
| 16 |
+
|
| 17 |
+
/** Converts an unaligned four-byte little-endian integer into an int32 */
|
| 18 |
+
#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
|
| 19 |
+
/** Converts an unaligned two-byte little-endian integer into an int16 */
|
| 20 |
+
#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8))
|
| 21 |
+
/** Converts an int16 into an unaligned two-byte little-endian integer */
|
| 22 |
+
#define SHORT_TO_SW(s, p) \
|
| 23 |
+
(p)[0] = (s); \
|
| 24 |
+
(p)[1] = (s) >> 8;
|
| 25 |
+
/** Converts an int32 into an unaligned four-byte little-endian integer */
|
| 26 |
+
#define INT_TO_DW(i, p) \
|
| 27 |
+
(p)[0] = (i); \
|
| 28 |
+
(p)[1] = (i) >> 8; \
|
| 29 |
+
(p)[2] = (i) >> 16; \
|
| 30 |
+
(p)[3] = (i) >> 24;
|
| 31 |
+
|
| 32 |
+
/** Selects the nth item in a doubly linked list. n=-1 selects the last item. */
|
| 33 |
+
#define DL_NTH(head, out, n) \
|
| 34 |
+
do { \
|
| 35 |
+
int dl_nth_i = 0; \
|
| 36 |
+
LDECLTYPE(head) dl_nth_p = (head); \
|
| 37 |
+
if ((n) < 0) { \
|
| 38 |
+
while (dl_nth_p && dl_nth_i > (n)) { \
|
| 39 |
+
dl_nth_p = dl_nth_p->prev; \
|
| 40 |
+
dl_nth_i--; \
|
| 41 |
+
} \
|
| 42 |
+
} else { \
|
| 43 |
+
while (dl_nth_p && dl_nth_i < (n)) { \
|
| 44 |
+
dl_nth_p = dl_nth_p->next; \
|
| 45 |
+
dl_nth_i++; \
|
| 46 |
+
} \
|
| 47 |
+
} \
|
| 48 |
+
(out) = dl_nth_p; \
|
| 49 |
+
} while (0);
|
| 50 |
+
|
| 51 |
+
#ifdef UVC_DEBUGGING
|
| 52 |
+
#include <libgen.h>
|
| 53 |
+
#ifdef __ANDROID__
|
| 54 |
+
#include <android/log.h>
|
| 55 |
+
#define UVC_DEBUG(format, ...) __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d/%s] " format "\n", basename(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__)
|
| 56 |
+
#define UVC_ENTER() __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d] begin %s\n", basename(__FILE__), __LINE__, __FUNCTION__)
|
| 57 |
+
#define UVC_EXIT(code) __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d] end %s (%d)\n", basename(__FILE__), __LINE__, __FUNCTION__, code)
|
| 58 |
+
#define UVC_EXIT_VOID() __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d] end %s\n", basename(__FILE__), __LINE__, __FUNCTION__)
|
| 59 |
+
#else
|
| 60 |
+
#define UVC_DEBUG(format, ...) fprintf(stderr, "[%s:%d/%s] " format "\n", basename(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__)
|
| 61 |
+
#define UVC_ENTER() fprintf(stderr, "[%s:%d] begin %s\n", basename(__FILE__), __LINE__, __FUNCTION__)
|
| 62 |
+
#define UVC_EXIT(code) fprintf(stderr, "[%s:%d] end %s (%d)\n", basename(__FILE__), __LINE__, __FUNCTION__, code)
|
| 63 |
+
#define UVC_EXIT_VOID() fprintf(stderr, "[%s:%d] end %s\n", basename(__FILE__), __LINE__, __FUNCTION__)
|
| 64 |
+
#endif
|
| 65 |
+
#else
|
| 66 |
+
#define UVC_DEBUG(format, ...)
|
| 67 |
+
#define UVC_ENTER()
|
| 68 |
+
#define UVC_EXIT_VOID()
|
| 69 |
+
#define UVC_EXIT(code)
|
| 70 |
+
#endif
|
| 71 |
+
|
| 72 |
+
/* http://stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers */
|
| 73 |
+
#define IS_INDEXABLE(arg) (sizeof(arg[0]))
|
| 74 |
+
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
|
| 75 |
+
#define ARRAYSIZE(arr) (sizeof(arr) / (IS_ARRAY(arr) ? sizeof(arr[0]) : 0))
|
| 76 |
+
|
| 77 |
+
/** Video interface subclass code (A.2) */
|
| 78 |
+
enum uvc_int_subclass_code {
|
| 79 |
+
UVC_SC_UNDEFINED = 0x00,
|
| 80 |
+
UVC_SC_VIDEOCONTROL = 0x01,
|
| 81 |
+
UVC_SC_VIDEOSTREAMING = 0x02,
|
| 82 |
+
UVC_SC_VIDEO_INTERFACE_COLLECTION = 0x03
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
/** Video interface protocol code (A.3) */
|
| 86 |
+
enum uvc_int_proto_code {
|
| 87 |
+
UVC_PC_PROTOCOL_UNDEFINED = 0x00
|
| 88 |
+
};
|
| 89 |
+
|
| 90 |
+
/** VideoControl interface descriptor subtype (A.5) */
|
| 91 |
+
enum uvc_vc_desc_subtype {
|
| 92 |
+
UVC_VC_DESCRIPTOR_UNDEFINED = 0x00,
|
| 93 |
+
UVC_VC_HEADER = 0x01,
|
| 94 |
+
UVC_VC_INPUT_TERMINAL = 0x02,
|
| 95 |
+
UVC_VC_OUTPUT_TERMINAL = 0x03,
|
| 96 |
+
UVC_VC_SELECTOR_UNIT = 0x04,
|
| 97 |
+
UVC_VC_PROCESSING_UNIT = 0x05,
|
| 98 |
+
UVC_VC_EXTENSION_UNIT = 0x06
|
| 99 |
+
};
|
| 100 |
+
|
| 101 |
+
/** UVC endpoint descriptor subtype (A.7) */
|
| 102 |
+
enum uvc_ep_desc_subtype {
|
| 103 |
+
UVC_EP_UNDEFINED = 0x00,
|
| 104 |
+
UVC_EP_GENERAL = 0x01,
|
| 105 |
+
UVC_EP_ENDPOINT = 0x02,
|
| 106 |
+
UVC_EP_INTERRUPT = 0x03
|
| 107 |
+
};
|
| 108 |
+
|
| 109 |
+
/** VideoControl interface control selector (A.9.1) */
|
| 110 |
+
enum uvc_vc_ctrl_selector {
|
| 111 |
+
UVC_VC_CONTROL_UNDEFINED = 0x00,
|
| 112 |
+
UVC_VC_VIDEO_POWER_MODE_CONTROL = 0x01,
|
| 113 |
+
UVC_VC_REQUEST_ERROR_CODE_CONTROL = 0x02
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
/** Terminal control selector (A.9.2) */
|
| 117 |
+
enum uvc_term_ctrl_selector {
|
| 118 |
+
UVC_TE_CONTROL_UNDEFINED = 0x00
|
| 119 |
+
};
|
| 120 |
+
|
| 121 |
+
/** Selector unit control selector (A.9.3) */
|
| 122 |
+
enum uvc_su_ctrl_selector {
|
| 123 |
+
UVC_SU_CONTROL_UNDEFINED = 0x00,
|
| 124 |
+
UVC_SU_INPUT_SELECT_CONTROL = 0x01
|
| 125 |
+
};
|
| 126 |
+
|
| 127 |
+
/** Extension unit control selector (A.9.6) */
|
| 128 |
+
enum uvc_xu_ctrl_selector {
|
| 129 |
+
UVC_XU_CONTROL_UNDEFINED = 0x00
|
| 130 |
+
};
|
| 131 |
+
|
| 132 |
+
/** VideoStreaming interface control selector (A.9.7) */
|
| 133 |
+
enum uvc_vs_ctrl_selector {
|
| 134 |
+
UVC_VS_CONTROL_UNDEFINED = 0x00,
|
| 135 |
+
UVC_VS_PROBE_CONTROL = 0x01,
|
| 136 |
+
UVC_VS_COMMIT_CONTROL = 0x02,
|
| 137 |
+
UVC_VS_STILL_PROBE_CONTROL = 0x03,
|
| 138 |
+
UVC_VS_STILL_COMMIT_CONTROL = 0x04,
|
| 139 |
+
UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05,
|
| 140 |
+
UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06,
|
| 141 |
+
UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07,
|
| 142 |
+
UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08,
|
| 143 |
+
UVC_VS_SYNC_DELAY_CONTROL = 0x09
|
| 144 |
+
};
|
| 145 |
+
|
| 146 |
+
/** Status packet type (2.4.2.2) */
|
| 147 |
+
enum uvc_status_type {
|
| 148 |
+
UVC_STATUS_TYPE_CONTROL = 1,
|
| 149 |
+
UVC_STATUS_TYPE_STREAMING = 2
|
| 150 |
+
};
|
| 151 |
+
|
| 152 |
+
/** Payload header flags (2.4.3.3) */
|
| 153 |
+
#define UVC_STREAM_EOH (1 << 7)
|
| 154 |
+
#define UVC_STREAM_ERR (1 << 6)
|
| 155 |
+
#define UVC_STREAM_STI (1 << 5)
|
| 156 |
+
#define UVC_STREAM_RES (1 << 4)
|
| 157 |
+
#define UVC_STREAM_SCR (1 << 3)
|
| 158 |
+
#define UVC_STREAM_PTS (1 << 2)
|
| 159 |
+
#define UVC_STREAM_EOF (1 << 1)
|
| 160 |
+
#define UVC_STREAM_FID (1 << 0)
|
| 161 |
+
|
| 162 |
+
/** Control capabilities (4.1.2) */
|
| 163 |
+
#define UVC_CONTROL_CAP_GET (1 << 0)
|
| 164 |
+
#define UVC_CONTROL_CAP_SET (1 << 1)
|
| 165 |
+
#define UVC_CONTROL_CAP_DISABLED (1 << 2)
|
| 166 |
+
#define UVC_CONTROL_CAP_AUTOUPDATE (1 << 3)
|
| 167 |
+
#define UVC_CONTROL_CAP_ASYNCHRONOUS (1 << 4)
|
| 168 |
+
|
| 169 |
+
struct uvc_streaming_interface;
|
| 170 |
+
struct uvc_device_info;
|
| 171 |
+
|
| 172 |
+
/** VideoStream interface */
|
| 173 |
+
typedef struct uvc_streaming_interface {
|
| 174 |
+
struct uvc_device_info *parent;
|
| 175 |
+
struct uvc_streaming_interface *prev, *next;
|
| 176 |
+
/** Interface number */
|
| 177 |
+
uint8_t bInterfaceNumber;
|
| 178 |
+
/** Video formats that this interface provides */
|
| 179 |
+
struct uvc_format_desc *format_descs;
|
| 180 |
+
/** USB endpoint to use when communicating with this interface */
|
| 181 |
+
uint8_t bEndpointAddress;
|
| 182 |
+
uint8_t bTerminalLink;
|
| 183 |
+
uint8_t bStillCaptureMethod;
|
| 184 |
+
} uvc_streaming_interface_t;
|
| 185 |
+
|
| 186 |
+
/** VideoControl interface */
|
| 187 |
+
typedef struct uvc_control_interface {
|
| 188 |
+
struct uvc_device_info *parent;
|
| 189 |
+
struct uvc_input_terminal *input_term_descs;
|
| 190 |
+
// struct uvc_output_terminal *output_term_descs;
|
| 191 |
+
struct uvc_selector_unit *selector_unit_descs;
|
| 192 |
+
struct uvc_processing_unit *processing_unit_descs;
|
| 193 |
+
struct uvc_extension_unit *extension_unit_descs;
|
| 194 |
+
uint16_t bcdUVC;
|
| 195 |
+
uint32_t dwClockFrequency;
|
| 196 |
+
uint8_t bEndpointAddress;
|
| 197 |
+
/** Interface number */
|
| 198 |
+
uint8_t bInterfaceNumber;
|
| 199 |
+
} uvc_control_interface_t;
|
| 200 |
+
|
| 201 |
+
struct uvc_stream_ctrl;
|
| 202 |
+
|
| 203 |
+
struct uvc_device {
|
| 204 |
+
struct uvc_context *ctx;
|
| 205 |
+
int ref;
|
| 206 |
+
libusb_device *usb_dev;
|
| 207 |
+
};
|
| 208 |
+
|
| 209 |
+
typedef struct uvc_device_info {
|
| 210 |
+
/** Configuration descriptor for USB device */
|
| 211 |
+
struct libusb_config_descriptor *config;
|
| 212 |
+
/** VideoControl interface provided by device */
|
| 213 |
+
uvc_control_interface_t ctrl_if;
|
| 214 |
+
/** VideoStreaming interfaces on the device */
|
| 215 |
+
uvc_streaming_interface_t *stream_ifs;
|
| 216 |
+
} uvc_device_info_t;
|
| 217 |
+
|
| 218 |
+
/*
|
| 219 |
+
The default Linux value of 100 is too deep for this service: six USB2
|
| 220 |
+
isochronous cameras would submit 600 transfers (up to 32 packets each),
|
| 221 |
+
exhausting the libusb/xHCI in-flight scheduling resources before all
|
| 222 |
+
endpoints receive callbacks. Eight transfers per camera provide 32 ms of
|
| 223 |
+
queued ISO work at the selected endpoint size and keep all six streams
|
| 224 |
+
active. This remains overridable at compile time for other applications.
|
| 225 |
+
*/
|
| 226 |
+
#ifndef LIBUVC_NUM_TRANSFER_BUFS
|
| 227 |
+
#if defined(__APPLE__) && defined(__MACH__)
|
| 228 |
+
#define LIBUVC_NUM_TRANSFER_BUFS 20
|
| 229 |
+
#else
|
| 230 |
+
#define LIBUVC_NUM_TRANSFER_BUFS 8
|
| 231 |
+
#endif
|
| 232 |
+
#endif
|
| 233 |
+
|
| 234 |
+
#define LIBUVC_XFER_META_BUF_SIZE ( 4 * 1024 )
|
| 235 |
+
|
| 236 |
+
struct uvc_stream_handle {
|
| 237 |
+
struct uvc_device_handle *devh;
|
| 238 |
+
struct uvc_stream_handle *prev, *next;
|
| 239 |
+
struct uvc_streaming_interface *stream_if;
|
| 240 |
+
|
| 241 |
+
/** if true, stream is running (streaming video to host) */
|
| 242 |
+
uint8_t running;
|
| 243 |
+
/** Current control block */
|
| 244 |
+
struct uvc_stream_ctrl cur_ctrl;
|
| 245 |
+
|
| 246 |
+
/* listeners may only access hold*, and only when holding a
|
| 247 |
+
* lock on cb_mutex (probably signaled with cb_cond) */
|
| 248 |
+
uint8_t fid;
|
| 249 |
+
uint32_t seq, hold_seq;
|
| 250 |
+
uint32_t pts, hold_pts;
|
| 251 |
+
uint32_t last_scr, hold_last_scr;
|
| 252 |
+
size_t got_bytes, hold_bytes;
|
| 253 |
+
uint8_t *outbuf, *holdbuf;
|
| 254 |
+
pthread_mutex_t cb_mutex;
|
| 255 |
+
pthread_cond_t cb_cond;
|
| 256 |
+
pthread_t cb_thread;
|
| 257 |
+
uint32_t last_polled_seq;
|
| 258 |
+
uvc_frame_callback_t *user_cb;
|
| 259 |
+
void *user_ptr;
|
| 260 |
+
struct libusb_transfer *transfers[LIBUVC_NUM_TRANSFER_BUFS];
|
| 261 |
+
uint8_t *transfer_bufs[LIBUVC_NUM_TRANSFER_BUFS];
|
| 262 |
+
struct uvc_frame frame;
|
| 263 |
+
enum uvc_frame_format frame_format;
|
| 264 |
+
struct timespec capture_time_finished;
|
| 265 |
+
|
| 266 |
+
/* raw metadata buffer if available */
|
| 267 |
+
uint8_t *meta_outbuf, *meta_holdbuf;
|
| 268 |
+
size_t meta_got_bytes, meta_hold_bytes;
|
| 269 |
+
};
|
| 270 |
+
|
| 271 |
+
/** Handle on an open UVC device
|
| 272 |
+
*
|
| 273 |
+
* @todo move most of this into a uvc_device struct?
|
| 274 |
+
*/
|
| 275 |
+
struct uvc_device_handle {
|
| 276 |
+
struct uvc_device *dev;
|
| 277 |
+
struct uvc_device_handle *prev, *next;
|
| 278 |
+
/** Underlying USB device handle */
|
| 279 |
+
libusb_device_handle *usb_devh;
|
| 280 |
+
struct uvc_device_info *info;
|
| 281 |
+
struct libusb_transfer *status_xfer;
|
| 282 |
+
uint8_t status_buf[32];
|
| 283 |
+
/** Function to call when we receive status updates from the camera */
|
| 284 |
+
uvc_status_callback_t *status_cb;
|
| 285 |
+
void *status_user_ptr;
|
| 286 |
+
/** Function to call when we receive button events from the camera */
|
| 287 |
+
uvc_button_callback_t *button_cb;
|
| 288 |
+
void *button_user_ptr;
|
| 289 |
+
|
| 290 |
+
uvc_stream_handle_t *streams;
|
| 291 |
+
/** Whether the camera is an iSight that sends one header per frame */
|
| 292 |
+
uint8_t is_isight;
|
| 293 |
+
uint32_t claimed;
|
| 294 |
+
|
| 295 |
+
/* Per-machine altsetting override set with uvc_set_altsetting_override().
|
| 296 |
+
* Zero-initialised by the calloc() in uvc_open_internal, so an untouched
|
| 297 |
+
* handle falls through to the compile-time quirks table in stream.c. */
|
| 298 |
+
uint8_t altsetting_override_valid;
|
| 299 |
+
uint8_t altsetting_override_altsetting;
|
| 300 |
+
uint16_t altsetting_override_payload;
|
| 301 |
+
};
|
| 302 |
+
|
| 303 |
+
/** Context within which we communicate with devices */
|
| 304 |
+
struct uvc_context {
|
| 305 |
+
/** Underlying context for USB communication */
|
| 306 |
+
struct libusb_context *usb_ctx;
|
| 307 |
+
/** True iff libuvc initialized the underlying USB context */
|
| 308 |
+
uint8_t own_usb_ctx;
|
| 309 |
+
/** List of open devices in this context */
|
| 310 |
+
uvc_device_handle_t *open_devices;
|
| 311 |
+
pthread_t handler_thread;
|
| 312 |
+
int kill_handler_thread;
|
| 313 |
+
};
|
| 314 |
+
|
| 315 |
+
uvc_error_t uvc_query_stream_ctrl(
|
| 316 |
+
uvc_device_handle_t *devh,
|
| 317 |
+
uvc_stream_ctrl_t *ctrl,
|
| 318 |
+
uint8_t probe,
|
| 319 |
+
enum uvc_req_code req);
|
| 320 |
+
|
| 321 |
+
void uvc_start_handler_thread(uvc_context_t *ctx);
|
| 322 |
+
uvc_error_t uvc_claim_if(uvc_device_handle_t *devh, int idx);
|
| 323 |
+
uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx);
|
| 324 |
+
|
| 325 |
+
#endif // !def(LIBUVC_INTERNAL_H)
|
| 326 |
+
/** @endcond */
|
collector/core/gripper/camera_service_src/third_party/libuvc/include/utlist.h
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
Copyright (c) 2007-2010, Troy D. Hanson http://uthash.sourceforge.net
|
| 3 |
+
All rights reserved.
|
| 4 |
+
|
| 5 |
+
Redistribution and use in source and binary forms, with or without
|
| 6 |
+
modification, are permitted provided that the following conditions are met:
|
| 7 |
+
|
| 8 |
+
* Redistributions of source code must retain the above copyright
|
| 9 |
+
notice, this list of conditions and the following disclaimer.
|
| 10 |
+
|
| 11 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
| 12 |
+
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
| 13 |
+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
| 14 |
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
| 15 |
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
| 16 |
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
| 17 |
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
| 18 |
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
| 19 |
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
| 20 |
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 21 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 22 |
+
*/
|
| 23 |
+
|
| 24 |
+
#ifndef UTLIST_H
|
| 25 |
+
#define UTLIST_H
|
| 26 |
+
|
| 27 |
+
#define UTLIST_VERSION 1.9.1
|
| 28 |
+
|
| 29 |
+
/*
|
| 30 |
+
* This file contains macros to manipulate singly and doubly-linked lists.
|
| 31 |
+
*
|
| 32 |
+
* 1. LL_ macros: singly-linked lists.
|
| 33 |
+
* 2. DL_ macros: doubly-linked lists.
|
| 34 |
+
* 3. CDL_ macros: circular doubly-linked lists.
|
| 35 |
+
*
|
| 36 |
+
* To use singly-linked lists, your structure must have a "next" pointer.
|
| 37 |
+
* To use doubly-linked lists, your structure must "prev" and "next" pointers.
|
| 38 |
+
* Either way, the pointer to the head of the list must be initialized to NULL.
|
| 39 |
+
*
|
| 40 |
+
* ----------------.EXAMPLE -------------------------
|
| 41 |
+
* struct item {
|
| 42 |
+
* int id;
|
| 43 |
+
* struct item *prev, *next;
|
| 44 |
+
* }
|
| 45 |
+
*
|
| 46 |
+
* struct item *list = NULL:
|
| 47 |
+
*
|
| 48 |
+
* int main() {
|
| 49 |
+
* struct item *item;
|
| 50 |
+
* ... allocate and populate item ...
|
| 51 |
+
* DL_APPEND(list, item);
|
| 52 |
+
* }
|
| 53 |
+
* --------------------------------------------------
|
| 54 |
+
*
|
| 55 |
+
* For doubly-linked lists, the append and delete macros are O(1)
|
| 56 |
+
* For singly-linked lists, append and delete are O(n) but prepend is O(1)
|
| 57 |
+
* The sort macro is O(n log(n)) for all types of single/double/circular lists.
|
| 58 |
+
*/
|
| 59 |
+
|
| 60 |
+
/* These macros use decltype or the earlier __typeof GNU extension.
|
| 61 |
+
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
|
| 62 |
+
when compiling c++ code), this code uses whatever method is needed
|
| 63 |
+
or, for VS2008 where neither is available, uses casting workarounds. */
|
| 64 |
+
#ifdef _MSC_VER /* MS compiler */
|
| 65 |
+
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
|
| 66 |
+
#define LDECLTYPE(x) decltype(x)
|
| 67 |
+
#else /* VS2008 or older (or VS2010 in C mode) */
|
| 68 |
+
#define NO_DECLTYPE
|
| 69 |
+
#define LDECLTYPE(x) char*
|
| 70 |
+
#endif
|
| 71 |
+
#else /* GNU, Sun and other compilers */
|
| 72 |
+
#define LDECLTYPE(x) __typeof(x)
|
| 73 |
+
#endif
|
| 74 |
+
|
| 75 |
+
/* for VS2008 we use some workarounds to get around the lack of decltype,
|
| 76 |
+
* namely, we always reassign our tmp variable to the list head if we need
|
| 77 |
+
* to dereference its prev/next pointers, and save/restore the real head.*/
|
| 78 |
+
#ifdef NO_DECLTYPE
|
| 79 |
+
#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
|
| 80 |
+
#define _NEXT(elt,list) ((char*)((list)->next))
|
| 81 |
+
#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
|
| 82 |
+
#define _PREV(elt,list) ((char*)((list)->prev))
|
| 83 |
+
#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
|
| 84 |
+
#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
|
| 85 |
+
#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
|
| 86 |
+
#else
|
| 87 |
+
#define _SV(elt,list)
|
| 88 |
+
#define _NEXT(elt,list) ((elt)->next)
|
| 89 |
+
#define _NEXTASGN(elt,list,to) ((elt)->next)=(to)
|
| 90 |
+
#define _PREV(elt,list) ((elt)->prev)
|
| 91 |
+
#define _PREVASGN(elt,list,to) ((elt)->prev)=(to)
|
| 92 |
+
#define _RS(list)
|
| 93 |
+
#define _CASTASGN(a,b) (a)=(b)
|
| 94 |
+
#endif
|
| 95 |
+
|
| 96 |
+
/******************************************************************************
|
| 97 |
+
* The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort *
|
| 98 |
+
* Unwieldy variable names used here to avoid shadowing passed-in variables. *
|
| 99 |
+
*****************************************************************************/
|
| 100 |
+
#define LL_SORT(list, cmp) \
|
| 101 |
+
do { \
|
| 102 |
+
LDECLTYPE(list) _ls_p; \
|
| 103 |
+
LDECLTYPE(list) _ls_q; \
|
| 104 |
+
LDECLTYPE(list) _ls_e; \
|
| 105 |
+
LDECLTYPE(list) _ls_tail; \
|
| 106 |
+
LDECLTYPE(list) _ls_oldhead; \
|
| 107 |
+
LDECLTYPE(list) _tmp; \
|
| 108 |
+
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
| 109 |
+
if (list) { \
|
| 110 |
+
_ls_insize = 1; \
|
| 111 |
+
_ls_looping = 1; \
|
| 112 |
+
while (_ls_looping) { \
|
| 113 |
+
_CASTASGN(_ls_p,list); \
|
| 114 |
+
_CASTASGN(_ls_oldhead,list); \
|
| 115 |
+
list = NULL; \
|
| 116 |
+
_ls_tail = NULL; \
|
| 117 |
+
_ls_nmerges = 0; \
|
| 118 |
+
while (_ls_p) { \
|
| 119 |
+
_ls_nmerges++; \
|
| 120 |
+
_ls_q = _ls_p; \
|
| 121 |
+
_ls_psize = 0; \
|
| 122 |
+
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
| 123 |
+
_ls_psize++; \
|
| 124 |
+
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \
|
| 125 |
+
if (!_ls_q) break; \
|
| 126 |
+
} \
|
| 127 |
+
_ls_qsize = _ls_insize; \
|
| 128 |
+
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
| 129 |
+
if (_ls_psize == 0) { \
|
| 130 |
+
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
|
| 131 |
+
} else if (_ls_qsize == 0 || !_ls_q) { \
|
| 132 |
+
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
|
| 133 |
+
} else if (cmp(_ls_p,_ls_q) <= 0) { \
|
| 134 |
+
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
|
| 135 |
+
} else { \
|
| 136 |
+
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
|
| 137 |
+
} \
|
| 138 |
+
if (_ls_tail) { \
|
| 139 |
+
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \
|
| 140 |
+
} else { \
|
| 141 |
+
_CASTASGN(list,_ls_e); \
|
| 142 |
+
} \
|
| 143 |
+
_ls_tail = _ls_e; \
|
| 144 |
+
} \
|
| 145 |
+
_ls_p = _ls_q; \
|
| 146 |
+
} \
|
| 147 |
+
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \
|
| 148 |
+
if (_ls_nmerges <= 1) { \
|
| 149 |
+
_ls_looping=0; \
|
| 150 |
+
} \
|
| 151 |
+
_ls_insize *= 2; \
|
| 152 |
+
} \
|
| 153 |
+
} else _tmp=NULL; /* quiet gcc unused variable warning */ \
|
| 154 |
+
} while (0)
|
| 155 |
+
|
| 156 |
+
#define DL_SORT(list, cmp) \
|
| 157 |
+
do { \
|
| 158 |
+
LDECLTYPE(list) _ls_p; \
|
| 159 |
+
LDECLTYPE(list) _ls_q; \
|
| 160 |
+
LDECLTYPE(list) _ls_e; \
|
| 161 |
+
LDECLTYPE(list) _ls_tail; \
|
| 162 |
+
LDECLTYPE(list) _ls_oldhead; \
|
| 163 |
+
LDECLTYPE(list) _tmp; \
|
| 164 |
+
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
| 165 |
+
if (list) { \
|
| 166 |
+
_ls_insize = 1; \
|
| 167 |
+
_ls_looping = 1; \
|
| 168 |
+
while (_ls_looping) { \
|
| 169 |
+
_CASTASGN(_ls_p,list); \
|
| 170 |
+
_CASTASGN(_ls_oldhead,list); \
|
| 171 |
+
list = NULL; \
|
| 172 |
+
_ls_tail = NULL; \
|
| 173 |
+
_ls_nmerges = 0; \
|
| 174 |
+
while (_ls_p) { \
|
| 175 |
+
_ls_nmerges++; \
|
| 176 |
+
_ls_q = _ls_p; \
|
| 177 |
+
_ls_psize = 0; \
|
| 178 |
+
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
| 179 |
+
_ls_psize++; \
|
| 180 |
+
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \
|
| 181 |
+
if (!_ls_q) break; \
|
| 182 |
+
} \
|
| 183 |
+
_ls_qsize = _ls_insize; \
|
| 184 |
+
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
| 185 |
+
if (_ls_psize == 0) { \
|
| 186 |
+
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
|
| 187 |
+
} else if (_ls_qsize == 0 || !_ls_q) { \
|
| 188 |
+
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
|
| 189 |
+
} else if (cmp(_ls_p,_ls_q) <= 0) { \
|
| 190 |
+
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
|
| 191 |
+
} else { \
|
| 192 |
+
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
|
| 193 |
+
} \
|
| 194 |
+
if (_ls_tail) { \
|
| 195 |
+
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \
|
| 196 |
+
} else { \
|
| 197 |
+
_CASTASGN(list,_ls_e); \
|
| 198 |
+
} \
|
| 199 |
+
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \
|
| 200 |
+
_ls_tail = _ls_e; \
|
| 201 |
+
} \
|
| 202 |
+
_ls_p = _ls_q; \
|
| 203 |
+
} \
|
| 204 |
+
_CASTASGN(list->prev, _ls_tail); \
|
| 205 |
+
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \
|
| 206 |
+
if (_ls_nmerges <= 1) { \
|
| 207 |
+
_ls_looping=0; \
|
| 208 |
+
} \
|
| 209 |
+
_ls_insize *= 2; \
|
| 210 |
+
} \
|
| 211 |
+
} else _tmp=NULL; /* quiet gcc unused variable warning */ \
|
| 212 |
+
} while (0)
|
| 213 |
+
|
| 214 |
+
#define CDL_SORT(list, cmp) \
|
| 215 |
+
do { \
|
| 216 |
+
LDECLTYPE(list) _ls_p; \
|
| 217 |
+
LDECLTYPE(list) _ls_q; \
|
| 218 |
+
LDECLTYPE(list) _ls_e; \
|
| 219 |
+
LDECLTYPE(list) _ls_tail; \
|
| 220 |
+
LDECLTYPE(list) _ls_oldhead; \
|
| 221 |
+
LDECLTYPE(list) _tmp; \
|
| 222 |
+
LDECLTYPE(list) _tmp2; \
|
| 223 |
+
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
| 224 |
+
if (list) { \
|
| 225 |
+
_ls_insize = 1; \
|
| 226 |
+
_ls_looping = 1; \
|
| 227 |
+
while (_ls_looping) { \
|
| 228 |
+
_CASTASGN(_ls_p,list); \
|
| 229 |
+
_CASTASGN(_ls_oldhead,list); \
|
| 230 |
+
list = NULL; \
|
| 231 |
+
_ls_tail = NULL; \
|
| 232 |
+
_ls_nmerges = 0; \
|
| 233 |
+
while (_ls_p) { \
|
| 234 |
+
_ls_nmerges++; \
|
| 235 |
+
_ls_q = _ls_p; \
|
| 236 |
+
_ls_psize = 0; \
|
| 237 |
+
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
| 238 |
+
_ls_psize++; \
|
| 239 |
+
_SV(_ls_q,list); \
|
| 240 |
+
if (_NEXT(_ls_q,list) == _ls_oldhead) { \
|
| 241 |
+
_ls_q = NULL; \
|
| 242 |
+
} else { \
|
| 243 |
+
_ls_q = _NEXT(_ls_q,list); \
|
| 244 |
+
} \
|
| 245 |
+
_RS(list); \
|
| 246 |
+
if (!_ls_q) break; \
|
| 247 |
+
} \
|
| 248 |
+
_ls_qsize = _ls_insize; \
|
| 249 |
+
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
| 250 |
+
if (_ls_psize == 0) { \
|
| 251 |
+
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
|
| 252 |
+
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
|
| 253 |
+
} else if (_ls_qsize == 0 || !_ls_q) { \
|
| 254 |
+
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
|
| 255 |
+
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
|
| 256 |
+
} else if (cmp(_ls_p,_ls_q) <= 0) { \
|
| 257 |
+
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
|
| 258 |
+
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
|
| 259 |
+
} else { \
|
| 260 |
+
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
|
| 261 |
+
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
|
| 262 |
+
} \
|
| 263 |
+
if (_ls_tail) { \
|
| 264 |
+
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \
|
| 265 |
+
} else { \
|
| 266 |
+
_CASTASGN(list,_ls_e); \
|
| 267 |
+
} \
|
| 268 |
+
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \
|
| 269 |
+
_ls_tail = _ls_e; \
|
| 270 |
+
} \
|
| 271 |
+
_ls_p = _ls_q; \
|
| 272 |
+
} \
|
| 273 |
+
_CASTASGN(list->prev,_ls_tail); \
|
| 274 |
+
_CASTASGN(_tmp2,list); \
|
| 275 |
+
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \
|
| 276 |
+
if (_ls_nmerges <= 1) { \
|
| 277 |
+
_ls_looping=0; \
|
| 278 |
+
} \
|
| 279 |
+
_ls_insize *= 2; \
|
| 280 |
+
} \
|
| 281 |
+
} else _tmp=NULL; /* quiet gcc unused variable warning */ \
|
| 282 |
+
} while (0)
|
| 283 |
+
|
| 284 |
+
/******************************************************************************
|
| 285 |
+
* singly linked list macros (non-circular) *
|
| 286 |
+
*****************************************************************************/
|
| 287 |
+
#define LL_PREPEND(head,add) \
|
| 288 |
+
do { \
|
| 289 |
+
(add)->next = head; \
|
| 290 |
+
head = add; \
|
| 291 |
+
} while (0)
|
| 292 |
+
|
| 293 |
+
#define LL_APPEND(head,add) \
|
| 294 |
+
do { \
|
| 295 |
+
LDECLTYPE(head) _tmp; \
|
| 296 |
+
(add)->next=NULL; \
|
| 297 |
+
if (head) { \
|
| 298 |
+
_tmp = head; \
|
| 299 |
+
while (_tmp->next) { _tmp = _tmp->next; } \
|
| 300 |
+
_tmp->next=(add); \
|
| 301 |
+
} else { \
|
| 302 |
+
(head)=(add); \
|
| 303 |
+
} \
|
| 304 |
+
} while (0)
|
| 305 |
+
|
| 306 |
+
#define LL_DELETE(head,del) \
|
| 307 |
+
do { \
|
| 308 |
+
LDECLTYPE(head) _tmp; \
|
| 309 |
+
if ((head) == (del)) { \
|
| 310 |
+
(head)=(head)->next; \
|
| 311 |
+
} else { \
|
| 312 |
+
_tmp = head; \
|
| 313 |
+
while (_tmp->next && (_tmp->next != (del))) { \
|
| 314 |
+
_tmp = _tmp->next; \
|
| 315 |
+
} \
|
| 316 |
+
if (_tmp->next) { \
|
| 317 |
+
_tmp->next = ((del)->next); \
|
| 318 |
+
} \
|
| 319 |
+
} \
|
| 320 |
+
} while (0)
|
| 321 |
+
|
| 322 |
+
/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
|
| 323 |
+
#define LL_APPEND_VS2008(head,add) \
|
| 324 |
+
do { \
|
| 325 |
+
if (head) { \
|
| 326 |
+
(add)->next = head; /* use add->next as a temp variable */ \
|
| 327 |
+
while ((add)->next->next) { (add)->next = (add)->next->next; } \
|
| 328 |
+
(add)->next->next=(add); \
|
| 329 |
+
} else { \
|
| 330 |
+
(head)=(add); \
|
| 331 |
+
} \
|
| 332 |
+
(add)->next=NULL; \
|
| 333 |
+
} while (0)
|
| 334 |
+
|
| 335 |
+
#define LL_DELETE_VS2008(head,del) \
|
| 336 |
+
do { \
|
| 337 |
+
if ((head) == (del)) { \
|
| 338 |
+
(head)=(head)->next; \
|
| 339 |
+
} else { \
|
| 340 |
+
char *_tmp = (char*)(head); \
|
| 341 |
+
while (head->next && (head->next != (del))) { \
|
| 342 |
+
head = head->next; \
|
| 343 |
+
} \
|
| 344 |
+
if (head->next) { \
|
| 345 |
+
head->next = ((del)->next); \
|
| 346 |
+
} \
|
| 347 |
+
{ \
|
| 348 |
+
char **_head_alias = (char**)&(head); \
|
| 349 |
+
*_head_alias = _tmp; \
|
| 350 |
+
} \
|
| 351 |
+
} \
|
| 352 |
+
} while (0)
|
| 353 |
+
#ifdef NO_DECLTYPE
|
| 354 |
+
#undef LL_APPEND
|
| 355 |
+
#define LL_APPEND LL_APPEND_VS2008
|
| 356 |
+
#undef LL_DELETE
|
| 357 |
+
#define LL_DELETE LL_DELETE_VS2008
|
| 358 |
+
#endif
|
| 359 |
+
/* end VS2008 replacements */
|
| 360 |
+
|
| 361 |
+
#define LL_FOREACH(head,el) \
|
| 362 |
+
for(el=head;el;el=el->next)
|
| 363 |
+
|
| 364 |
+
#define LL_FOREACH_SAFE(head,el,tmp) \
|
| 365 |
+
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
|
| 366 |
+
|
| 367 |
+
#define LL_SEARCH_SCALAR(head,out,field,val) \
|
| 368 |
+
do { \
|
| 369 |
+
LL_FOREACH(head,out) { \
|
| 370 |
+
if ((out)->field == (val)) break; \
|
| 371 |
+
} \
|
| 372 |
+
} while(0)
|
| 373 |
+
|
| 374 |
+
#define LL_SEARCH(head,out,elt,cmp) \
|
| 375 |
+
do { \
|
| 376 |
+
LL_FOREACH(head,out) { \
|
| 377 |
+
if ((cmp(out,elt))==0) break; \
|
| 378 |
+
} \
|
| 379 |
+
} while(0)
|
| 380 |
+
|
| 381 |
+
/******************************************************************************
|
| 382 |
+
* doubly linked list macros (non-circular) *
|
| 383 |
+
*****************************************************************************/
|
| 384 |
+
#define DL_PREPEND(head,add) \
|
| 385 |
+
do { \
|
| 386 |
+
(add)->next = head; \
|
| 387 |
+
if (head) { \
|
| 388 |
+
(add)->prev = (head)->prev; \
|
| 389 |
+
(head)->prev = (add); \
|
| 390 |
+
} else { \
|
| 391 |
+
(add)->prev = (add); \
|
| 392 |
+
} \
|
| 393 |
+
(head) = (add); \
|
| 394 |
+
} while (0)
|
| 395 |
+
|
| 396 |
+
#define DL_APPEND(head,add) \
|
| 397 |
+
do { \
|
| 398 |
+
if (head) { \
|
| 399 |
+
(add)->prev = (head)->prev; \
|
| 400 |
+
(head)->prev->next = (add); \
|
| 401 |
+
(head)->prev = (add); \
|
| 402 |
+
(add)->next = NULL; \
|
| 403 |
+
} else { \
|
| 404 |
+
(head)=(add); \
|
| 405 |
+
(head)->prev = (head); \
|
| 406 |
+
(head)->next = NULL; \
|
| 407 |
+
} \
|
| 408 |
+
} while (0);
|
| 409 |
+
|
| 410 |
+
#define DL_DELETE(head,del) \
|
| 411 |
+
do { \
|
| 412 |
+
if ((del)->prev == (del)) { \
|
| 413 |
+
(head)=NULL; \
|
| 414 |
+
} else if ((del)==(head)) { \
|
| 415 |
+
(del)->next->prev = (del)->prev; \
|
| 416 |
+
(head) = (del)->next; \
|
| 417 |
+
} else { \
|
| 418 |
+
(del)->prev->next = (del)->next; \
|
| 419 |
+
if ((del)->next) { \
|
| 420 |
+
(del)->next->prev = (del)->prev; \
|
| 421 |
+
} else { \
|
| 422 |
+
(head)->prev = (del)->prev; \
|
| 423 |
+
} \
|
| 424 |
+
} \
|
| 425 |
+
} while (0);
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
#define DL_FOREACH(head,el) \
|
| 429 |
+
for(el=head;el;el=el->next)
|
| 430 |
+
|
| 431 |
+
/* this version is safe for deleting the elements during iteration */
|
| 432 |
+
#define DL_FOREACH_SAFE(head,el,tmp) \
|
| 433 |
+
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
|
| 434 |
+
|
| 435 |
+
/* these are identical to their singly-linked list counterparts */
|
| 436 |
+
#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
|
| 437 |
+
#define DL_SEARCH LL_SEARCH
|
| 438 |
+
|
| 439 |
+
/******************************************************************************
|
| 440 |
+
* circular doubly linked list macros *
|
| 441 |
+
*****************************************************************************/
|
| 442 |
+
#define CDL_PREPEND(head,add) \
|
| 443 |
+
do { \
|
| 444 |
+
if (head) { \
|
| 445 |
+
(add)->prev = (head)->prev; \
|
| 446 |
+
(add)->next = (head); \
|
| 447 |
+
(head)->prev = (add); \
|
| 448 |
+
(add)->prev->next = (add); \
|
| 449 |
+
} else { \
|
| 450 |
+
(add)->prev = (add); \
|
| 451 |
+
(add)->next = (add); \
|
| 452 |
+
} \
|
| 453 |
+
(head)=(add); \
|
| 454 |
+
} while (0)
|
| 455 |
+
|
| 456 |
+
#define CDL_DELETE(head,del) \
|
| 457 |
+
do { \
|
| 458 |
+
if ( ((head)==(del)) && ((head)->next == (head))) { \
|
| 459 |
+
(head) = 0L; \
|
| 460 |
+
} else { \
|
| 461 |
+
(del)->next->prev = (del)->prev; \
|
| 462 |
+
(del)->prev->next = (del)->next; \
|
| 463 |
+
if ((del) == (head)) (head)=(del)->next; \
|
| 464 |
+
} \
|
| 465 |
+
} while (0);
|
| 466 |
+
|
| 467 |
+
#define CDL_FOREACH(head,el) \
|
| 468 |
+
for(el=head;el;el=(el->next==head ? 0L : el->next))
|
| 469 |
+
|
| 470 |
+
#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
|
| 471 |
+
for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \
|
| 472 |
+
(el) && ((tmp2)=(el)->next, 1); \
|
| 473 |
+
((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
|
| 474 |
+
|
| 475 |
+
#define CDL_SEARCH_SCALAR(head,out,field,val) \
|
| 476 |
+
do { \
|
| 477 |
+
CDL_FOREACH(head,out) { \
|
| 478 |
+
if ((out)->field == (val)) break; \
|
| 479 |
+
} \
|
| 480 |
+
} while(0)
|
| 481 |
+
|
| 482 |
+
#define CDL_SEARCH(head,out,elt,cmp) \
|
| 483 |
+
do { \
|
| 484 |
+
CDL_FOREACH(head,out) { \
|
| 485 |
+
if ((cmp(out,elt))==0) break; \
|
| 486 |
+
} \
|
| 487 |
+
} while(0)
|
| 488 |
+
|
| 489 |
+
#endif /* UTLIST_H */
|
| 490 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/libuvc.pc.in
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@
|
| 2 |
+
includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@
|
| 3 |
+
|
| 4 |
+
Name: libuvc
|
| 5 |
+
Description: @libuvc_DESCRIPTION@
|
| 6 |
+
URL: @libuvc_URL@
|
| 7 |
+
Version: @libuvc_VERSION@
|
| 8 |
+
Libs: -L${libdir} -luvc
|
| 9 |
+
Libs.private: -lusb-1.0 @PKGCONFIG_JPEG_LDFLAG@
|
| 10 |
+
Cflags: -I${includedir}
|
| 11 |
+
Requires: libusb-1.0
|
collector/core/gripper/camera_service_src/third_party/libuvc/libuvcConfig.cmake
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
| 2 |
+
|
| 3 |
+
# If find_package called with REQUIRED or QUIET we need to
|
| 4 |
+
# forward down it
|
| 5 |
+
unset(extraArgs)
|
| 6 |
+
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
|
| 7 |
+
list(APPEND extraArgs QUIET)
|
| 8 |
+
endif()
|
| 9 |
+
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
| 10 |
+
list(APPEND extraArgs REQUIRED)
|
| 11 |
+
endif()
|
| 12 |
+
|
| 13 |
+
find_package(JpegPkg ${extraArgs})
|
| 14 |
+
find_package(LibUSB ${extraArgs})
|
| 15 |
+
include("${CMAKE_CURRENT_LIST_DIR}/libuvcTargets.cmake")
|
| 16 |
+
|
| 17 |
+
set(libuvc_FOUND TRUE)
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/ctrl-gen.c
ADDED
|
@@ -0,0 +1,2259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* This is an AUTO-GENERATED file! Update it with the output of `ctrl-gen.py def`. */
|
| 2 |
+
#include "libuvc/libuvc.h"
|
| 3 |
+
#include "libuvc/libuvc_internal.h"
|
| 4 |
+
|
| 5 |
+
static const int REQ_TYPE_SET = 0x21;
|
| 6 |
+
static const int REQ_TYPE_GET = 0xa1;
|
| 7 |
+
|
| 8 |
+
/** @ingroup ctrl
|
| 9 |
+
* @brief Reads the SCANNING_MODE control.
|
| 10 |
+
* @param devh UVC device handle
|
| 11 |
+
* @param[out] mode 0: interlaced, 1: progressive
|
| 12 |
+
* @param req_code UVC_GET_* request to execute
|
| 13 |
+
*/
|
| 14 |
+
uvc_error_t uvc_get_scanning_mode(uvc_device_handle_t *devh, uint8_t* mode, enum uvc_req_code req_code) {
|
| 15 |
+
uint8_t data[1];
|
| 16 |
+
uvc_error_t ret;
|
| 17 |
+
|
| 18 |
+
ret = libusb_control_transfer(
|
| 19 |
+
devh->usb_devh,
|
| 20 |
+
REQ_TYPE_GET, req_code,
|
| 21 |
+
UVC_CT_SCANNING_MODE_CONTROL << 8,
|
| 22 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 23 |
+
data,
|
| 24 |
+
sizeof(data),
|
| 25 |
+
0);
|
| 26 |
+
|
| 27 |
+
if (ret == sizeof(data)) {
|
| 28 |
+
*mode = data[0];
|
| 29 |
+
return UVC_SUCCESS;
|
| 30 |
+
} else {
|
| 31 |
+
return ret;
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
/** @ingroup ctrl
|
| 37 |
+
* @brief Sets the SCANNING_MODE control.
|
| 38 |
+
* @param devh UVC device handle
|
| 39 |
+
* @param mode 0: interlaced, 1: progressive
|
| 40 |
+
*/
|
| 41 |
+
uvc_error_t uvc_set_scanning_mode(uvc_device_handle_t *devh, uint8_t mode) {
|
| 42 |
+
uint8_t data[1];
|
| 43 |
+
uvc_error_t ret;
|
| 44 |
+
|
| 45 |
+
data[0] = mode;
|
| 46 |
+
|
| 47 |
+
ret = libusb_control_transfer(
|
| 48 |
+
devh->usb_devh,
|
| 49 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 50 |
+
UVC_CT_SCANNING_MODE_CONTROL << 8,
|
| 51 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 52 |
+
data,
|
| 53 |
+
sizeof(data),
|
| 54 |
+
0);
|
| 55 |
+
|
| 56 |
+
if (ret == sizeof(data))
|
| 57 |
+
return UVC_SUCCESS;
|
| 58 |
+
else
|
| 59 |
+
return ret;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
/** @ingroup ctrl
|
| 63 |
+
* @brief Reads camera's auto-exposure mode.
|
| 64 |
+
*
|
| 65 |
+
* See uvc_set_ae_mode() for a description of the available modes.
|
| 66 |
+
* @param devh UVC device handle
|
| 67 |
+
* @param[out] mode 1: manual mode; 2: auto mode; 4: shutter priority mode; 8: aperture priority mode
|
| 68 |
+
* @param req_code UVC_GET_* request to execute
|
| 69 |
+
*/
|
| 70 |
+
uvc_error_t uvc_get_ae_mode(uvc_device_handle_t *devh, uint8_t* mode, enum uvc_req_code req_code) {
|
| 71 |
+
uint8_t data[1];
|
| 72 |
+
uvc_error_t ret;
|
| 73 |
+
|
| 74 |
+
ret = libusb_control_transfer(
|
| 75 |
+
devh->usb_devh,
|
| 76 |
+
REQ_TYPE_GET, req_code,
|
| 77 |
+
UVC_CT_AE_MODE_CONTROL << 8,
|
| 78 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 79 |
+
data,
|
| 80 |
+
sizeof(data),
|
| 81 |
+
0);
|
| 82 |
+
|
| 83 |
+
if (ret == sizeof(data)) {
|
| 84 |
+
*mode = data[0];
|
| 85 |
+
return UVC_SUCCESS;
|
| 86 |
+
} else {
|
| 87 |
+
return ret;
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
/** @ingroup ctrl
|
| 93 |
+
* @brief Sets camera's auto-exposure mode.
|
| 94 |
+
*
|
| 95 |
+
* Cameras may support any of the following AE modes:
|
| 96 |
+
* * UVC_AUTO_EXPOSURE_MODE_MANUAL (1) - manual exposure time, manual iris
|
| 97 |
+
* * UVC_AUTO_EXPOSURE_MODE_AUTO (2) - auto exposure time, auto iris
|
| 98 |
+
* * UVC_AUTO_EXPOSURE_MODE_SHUTTER_PRIORITY (4) - manual exposure time, auto iris
|
| 99 |
+
* * UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY (8) - auto exposure time, manual iris
|
| 100 |
+
*
|
| 101 |
+
* Most cameras provide manual mode and aperture priority mode.
|
| 102 |
+
* @param devh UVC device handle
|
| 103 |
+
* @param mode 1: manual mode; 2: auto mode; 4: shutter priority mode; 8: aperture priority mode
|
| 104 |
+
*/
|
| 105 |
+
uvc_error_t uvc_set_ae_mode(uvc_device_handle_t *devh, uint8_t mode) {
|
| 106 |
+
uint8_t data[1];
|
| 107 |
+
uvc_error_t ret;
|
| 108 |
+
|
| 109 |
+
data[0] = mode;
|
| 110 |
+
|
| 111 |
+
ret = libusb_control_transfer(
|
| 112 |
+
devh->usb_devh,
|
| 113 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 114 |
+
UVC_CT_AE_MODE_CONTROL << 8,
|
| 115 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 116 |
+
data,
|
| 117 |
+
sizeof(data),
|
| 118 |
+
0);
|
| 119 |
+
|
| 120 |
+
if (ret == sizeof(data))
|
| 121 |
+
return UVC_SUCCESS;
|
| 122 |
+
else
|
| 123 |
+
return ret;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/** @ingroup ctrl
|
| 127 |
+
* @brief Checks whether the camera may vary the frame rate for exposure control reasons.
|
| 128 |
+
* See uvc_set_ae_priority() for a description of the `priority` field.
|
| 129 |
+
* @param devh UVC device handle
|
| 130 |
+
* @param[out] priority 0: frame rate must remain constant; 1: frame rate may be varied for AE purposes
|
| 131 |
+
* @param req_code UVC_GET_* request to execute
|
| 132 |
+
*/
|
| 133 |
+
uvc_error_t uvc_get_ae_priority(uvc_device_handle_t *devh, uint8_t* priority, enum uvc_req_code req_code) {
|
| 134 |
+
uint8_t data[1];
|
| 135 |
+
uvc_error_t ret;
|
| 136 |
+
|
| 137 |
+
ret = libusb_control_transfer(
|
| 138 |
+
devh->usb_devh,
|
| 139 |
+
REQ_TYPE_GET, req_code,
|
| 140 |
+
UVC_CT_AE_PRIORITY_CONTROL << 8,
|
| 141 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 142 |
+
data,
|
| 143 |
+
sizeof(data),
|
| 144 |
+
0);
|
| 145 |
+
|
| 146 |
+
if (ret == sizeof(data)) {
|
| 147 |
+
*priority = data[0];
|
| 148 |
+
return UVC_SUCCESS;
|
| 149 |
+
} else {
|
| 150 |
+
return ret;
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
/** @ingroup ctrl
|
| 156 |
+
* @brief Chooses whether the camera may vary the frame rate for exposure control reasons.
|
| 157 |
+
* A `priority` value of zero means the camera may not vary its frame rate. A value of 1
|
| 158 |
+
* means the frame rate is variable. This setting has no effect outside of the `auto` and
|
| 159 |
+
* `shutter_priority` auto-exposure modes.
|
| 160 |
+
* @param devh UVC device handle
|
| 161 |
+
* @param priority 0: frame rate must remain constant; 1: frame rate may be varied for AE purposes
|
| 162 |
+
*/
|
| 163 |
+
uvc_error_t uvc_set_ae_priority(uvc_device_handle_t *devh, uint8_t priority) {
|
| 164 |
+
uint8_t data[1];
|
| 165 |
+
uvc_error_t ret;
|
| 166 |
+
|
| 167 |
+
data[0] = priority;
|
| 168 |
+
|
| 169 |
+
ret = libusb_control_transfer(
|
| 170 |
+
devh->usb_devh,
|
| 171 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 172 |
+
UVC_CT_AE_PRIORITY_CONTROL << 8,
|
| 173 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 174 |
+
data,
|
| 175 |
+
sizeof(data),
|
| 176 |
+
0);
|
| 177 |
+
|
| 178 |
+
if (ret == sizeof(data))
|
| 179 |
+
return UVC_SUCCESS;
|
| 180 |
+
else
|
| 181 |
+
return ret;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
/** @ingroup ctrl
|
| 185 |
+
* @brief Gets the absolute exposure time.
|
| 186 |
+
*
|
| 187 |
+
* See uvc_set_exposure_abs() for a description of the `time` field.
|
| 188 |
+
* @param devh UVC device handle
|
| 189 |
+
* @param[out] time
|
| 190 |
+
* @param req_code UVC_GET_* request to execute
|
| 191 |
+
*/
|
| 192 |
+
uvc_error_t uvc_get_exposure_abs(uvc_device_handle_t *devh, uint32_t* time, enum uvc_req_code req_code) {
|
| 193 |
+
uint8_t data[4];
|
| 194 |
+
uvc_error_t ret;
|
| 195 |
+
|
| 196 |
+
ret = libusb_control_transfer(
|
| 197 |
+
devh->usb_devh,
|
| 198 |
+
REQ_TYPE_GET, req_code,
|
| 199 |
+
UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL << 8,
|
| 200 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 201 |
+
data,
|
| 202 |
+
sizeof(data),
|
| 203 |
+
0);
|
| 204 |
+
|
| 205 |
+
if (ret == sizeof(data)) {
|
| 206 |
+
*time = DW_TO_INT(data + 0);
|
| 207 |
+
return UVC_SUCCESS;
|
| 208 |
+
} else {
|
| 209 |
+
return ret;
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
/** @ingroup ctrl
|
| 215 |
+
* @brief Sets the absolute exposure time.
|
| 216 |
+
*
|
| 217 |
+
* The `time` parameter should be provided in units of 0.0001 seconds (e.g., use the value 100
|
| 218 |
+
* for a 10ms exposure period). Auto exposure should be set to `manual` or `shutter_priority`
|
| 219 |
+
* before attempting to change this setting.
|
| 220 |
+
* @param devh UVC device handle
|
| 221 |
+
* @param time
|
| 222 |
+
*/
|
| 223 |
+
uvc_error_t uvc_set_exposure_abs(uvc_device_handle_t *devh, uint32_t time) {
|
| 224 |
+
uint8_t data[4];
|
| 225 |
+
uvc_error_t ret;
|
| 226 |
+
|
| 227 |
+
INT_TO_DW(time, data + 0);
|
| 228 |
+
|
| 229 |
+
ret = libusb_control_transfer(
|
| 230 |
+
devh->usb_devh,
|
| 231 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 232 |
+
UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL << 8,
|
| 233 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 234 |
+
data,
|
| 235 |
+
sizeof(data),
|
| 236 |
+
0);
|
| 237 |
+
|
| 238 |
+
if (ret == sizeof(data))
|
| 239 |
+
return UVC_SUCCESS;
|
| 240 |
+
else
|
| 241 |
+
return ret;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
/** @ingroup ctrl
|
| 245 |
+
* @brief Reads the exposure time relative to the current setting.
|
| 246 |
+
* @param devh UVC device handle
|
| 247 |
+
* @param[out] step number of steps by which to change the exposure time, or zero to set the default exposure time
|
| 248 |
+
* @param req_code UVC_GET_* request to execute
|
| 249 |
+
*/
|
| 250 |
+
uvc_error_t uvc_get_exposure_rel(uvc_device_handle_t *devh, int8_t* step, enum uvc_req_code req_code) {
|
| 251 |
+
uint8_t data[1];
|
| 252 |
+
uvc_error_t ret;
|
| 253 |
+
|
| 254 |
+
ret = libusb_control_transfer(
|
| 255 |
+
devh->usb_devh,
|
| 256 |
+
REQ_TYPE_GET, req_code,
|
| 257 |
+
UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL << 8,
|
| 258 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 259 |
+
data,
|
| 260 |
+
sizeof(data),
|
| 261 |
+
0);
|
| 262 |
+
|
| 263 |
+
if (ret == sizeof(data)) {
|
| 264 |
+
*step = data[0];
|
| 265 |
+
return UVC_SUCCESS;
|
| 266 |
+
} else {
|
| 267 |
+
return ret;
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
/** @ingroup ctrl
|
| 273 |
+
* @brief Sets the exposure time relative to the current setting.
|
| 274 |
+
* @param devh UVC device handle
|
| 275 |
+
* @param step number of steps by which to change the exposure time, or zero to set the default exposure time
|
| 276 |
+
*/
|
| 277 |
+
uvc_error_t uvc_set_exposure_rel(uvc_device_handle_t *devh, int8_t step) {
|
| 278 |
+
uint8_t data[1];
|
| 279 |
+
uvc_error_t ret;
|
| 280 |
+
|
| 281 |
+
data[0] = step;
|
| 282 |
+
|
| 283 |
+
ret = libusb_control_transfer(
|
| 284 |
+
devh->usb_devh,
|
| 285 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 286 |
+
UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL << 8,
|
| 287 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 288 |
+
data,
|
| 289 |
+
sizeof(data),
|
| 290 |
+
0);
|
| 291 |
+
|
| 292 |
+
if (ret == sizeof(data))
|
| 293 |
+
return UVC_SUCCESS;
|
| 294 |
+
else
|
| 295 |
+
return ret;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
/** @ingroup ctrl
|
| 299 |
+
* @brief Reads the distance at which an object is optimally focused.
|
| 300 |
+
* @param devh UVC device handle
|
| 301 |
+
* @param[out] focus focal target distance in millimeters
|
| 302 |
+
* @param req_code UVC_GET_* request to execute
|
| 303 |
+
*/
|
| 304 |
+
uvc_error_t uvc_get_focus_abs(uvc_device_handle_t *devh, uint16_t* focus, enum uvc_req_code req_code) {
|
| 305 |
+
uint8_t data[2];
|
| 306 |
+
uvc_error_t ret;
|
| 307 |
+
|
| 308 |
+
ret = libusb_control_transfer(
|
| 309 |
+
devh->usb_devh,
|
| 310 |
+
REQ_TYPE_GET, req_code,
|
| 311 |
+
UVC_CT_FOCUS_ABSOLUTE_CONTROL << 8,
|
| 312 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 313 |
+
data,
|
| 314 |
+
sizeof(data),
|
| 315 |
+
0);
|
| 316 |
+
|
| 317 |
+
if (ret == sizeof(data)) {
|
| 318 |
+
*focus = SW_TO_SHORT(data + 0);
|
| 319 |
+
return UVC_SUCCESS;
|
| 320 |
+
} else {
|
| 321 |
+
return ret;
|
| 322 |
+
}
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
/** @ingroup ctrl
|
| 327 |
+
* @brief Sets the distance at which an object is optimally focused.
|
| 328 |
+
* @param devh UVC device handle
|
| 329 |
+
* @param focus focal target distance in millimeters
|
| 330 |
+
*/
|
| 331 |
+
uvc_error_t uvc_set_focus_abs(uvc_device_handle_t *devh, uint16_t focus) {
|
| 332 |
+
uint8_t data[2];
|
| 333 |
+
uvc_error_t ret;
|
| 334 |
+
|
| 335 |
+
SHORT_TO_SW(focus, data + 0);
|
| 336 |
+
|
| 337 |
+
ret = libusb_control_transfer(
|
| 338 |
+
devh->usb_devh,
|
| 339 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 340 |
+
UVC_CT_FOCUS_ABSOLUTE_CONTROL << 8,
|
| 341 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 342 |
+
data,
|
| 343 |
+
sizeof(data),
|
| 344 |
+
0);
|
| 345 |
+
|
| 346 |
+
if (ret == sizeof(data))
|
| 347 |
+
return UVC_SUCCESS;
|
| 348 |
+
else
|
| 349 |
+
return ret;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
/** @ingroup ctrl
|
| 353 |
+
* @brief Reads the FOCUS_RELATIVE control.
|
| 354 |
+
* @param devh UVC device handle
|
| 355 |
+
* @param[out] focus_rel TODO
|
| 356 |
+
* @param[out] speed TODO
|
| 357 |
+
* @param req_code UVC_GET_* request to execute
|
| 358 |
+
*/
|
| 359 |
+
uvc_error_t uvc_get_focus_rel(uvc_device_handle_t *devh, int8_t* focus_rel, uint8_t* speed, enum uvc_req_code req_code) {
|
| 360 |
+
uint8_t data[2];
|
| 361 |
+
uvc_error_t ret;
|
| 362 |
+
|
| 363 |
+
ret = libusb_control_transfer(
|
| 364 |
+
devh->usb_devh,
|
| 365 |
+
REQ_TYPE_GET, req_code,
|
| 366 |
+
UVC_CT_FOCUS_RELATIVE_CONTROL << 8,
|
| 367 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 368 |
+
data,
|
| 369 |
+
sizeof(data),
|
| 370 |
+
0);
|
| 371 |
+
|
| 372 |
+
if (ret == sizeof(data)) {
|
| 373 |
+
*focus_rel = data[0];
|
| 374 |
+
*speed = data[1];
|
| 375 |
+
return UVC_SUCCESS;
|
| 376 |
+
} else {
|
| 377 |
+
return ret;
|
| 378 |
+
}
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
/** @ingroup ctrl
|
| 383 |
+
* @brief Sets the FOCUS_RELATIVE control.
|
| 384 |
+
* @param devh UVC device handle
|
| 385 |
+
* @param focus_rel TODO
|
| 386 |
+
* @param speed TODO
|
| 387 |
+
*/
|
| 388 |
+
uvc_error_t uvc_set_focus_rel(uvc_device_handle_t *devh, int8_t focus_rel, uint8_t speed) {
|
| 389 |
+
uint8_t data[2];
|
| 390 |
+
uvc_error_t ret;
|
| 391 |
+
|
| 392 |
+
data[0] = focus_rel;
|
| 393 |
+
data[1] = speed;
|
| 394 |
+
|
| 395 |
+
ret = libusb_control_transfer(
|
| 396 |
+
devh->usb_devh,
|
| 397 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 398 |
+
UVC_CT_FOCUS_RELATIVE_CONTROL << 8,
|
| 399 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 400 |
+
data,
|
| 401 |
+
sizeof(data),
|
| 402 |
+
0);
|
| 403 |
+
|
| 404 |
+
if (ret == sizeof(data))
|
| 405 |
+
return UVC_SUCCESS;
|
| 406 |
+
else
|
| 407 |
+
return ret;
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
/** @ingroup ctrl
|
| 411 |
+
* @brief Reads the FOCUS_SIMPLE control.
|
| 412 |
+
* @param devh UVC device handle
|
| 413 |
+
* @param[out] focus TODO
|
| 414 |
+
* @param req_code UVC_GET_* request to execute
|
| 415 |
+
*/
|
| 416 |
+
uvc_error_t uvc_get_focus_simple_range(uvc_device_handle_t *devh, uint8_t* focus, enum uvc_req_code req_code) {
|
| 417 |
+
uint8_t data[1];
|
| 418 |
+
uvc_error_t ret;
|
| 419 |
+
|
| 420 |
+
ret = libusb_control_transfer(
|
| 421 |
+
devh->usb_devh,
|
| 422 |
+
REQ_TYPE_GET, req_code,
|
| 423 |
+
UVC_CT_FOCUS_SIMPLE_CONTROL << 8,
|
| 424 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 425 |
+
data,
|
| 426 |
+
sizeof(data),
|
| 427 |
+
0);
|
| 428 |
+
|
| 429 |
+
if (ret == sizeof(data)) {
|
| 430 |
+
*focus = data[0];
|
| 431 |
+
return UVC_SUCCESS;
|
| 432 |
+
} else {
|
| 433 |
+
return ret;
|
| 434 |
+
}
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
/** @ingroup ctrl
|
| 439 |
+
* @brief Sets the FOCUS_SIMPLE control.
|
| 440 |
+
* @param devh UVC device handle
|
| 441 |
+
* @param focus TODO
|
| 442 |
+
*/
|
| 443 |
+
uvc_error_t uvc_set_focus_simple_range(uvc_device_handle_t *devh, uint8_t focus) {
|
| 444 |
+
uint8_t data[1];
|
| 445 |
+
uvc_error_t ret;
|
| 446 |
+
|
| 447 |
+
data[0] = focus;
|
| 448 |
+
|
| 449 |
+
ret = libusb_control_transfer(
|
| 450 |
+
devh->usb_devh,
|
| 451 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 452 |
+
UVC_CT_FOCUS_SIMPLE_CONTROL << 8,
|
| 453 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 454 |
+
data,
|
| 455 |
+
sizeof(data),
|
| 456 |
+
0);
|
| 457 |
+
|
| 458 |
+
if (ret == sizeof(data))
|
| 459 |
+
return UVC_SUCCESS;
|
| 460 |
+
else
|
| 461 |
+
return ret;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
/** @ingroup ctrl
|
| 465 |
+
* @brief Reads the FOCUS_AUTO control.
|
| 466 |
+
* @param devh UVC device handle
|
| 467 |
+
* @param[out] state TODO
|
| 468 |
+
* @param req_code UVC_GET_* request to execute
|
| 469 |
+
*/
|
| 470 |
+
uvc_error_t uvc_get_focus_auto(uvc_device_handle_t *devh, uint8_t* state, enum uvc_req_code req_code) {
|
| 471 |
+
uint8_t data[1];
|
| 472 |
+
uvc_error_t ret;
|
| 473 |
+
|
| 474 |
+
ret = libusb_control_transfer(
|
| 475 |
+
devh->usb_devh,
|
| 476 |
+
REQ_TYPE_GET, req_code,
|
| 477 |
+
UVC_CT_FOCUS_AUTO_CONTROL << 8,
|
| 478 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 479 |
+
data,
|
| 480 |
+
sizeof(data),
|
| 481 |
+
0);
|
| 482 |
+
|
| 483 |
+
if (ret == sizeof(data)) {
|
| 484 |
+
*state = data[0];
|
| 485 |
+
return UVC_SUCCESS;
|
| 486 |
+
} else {
|
| 487 |
+
return ret;
|
| 488 |
+
}
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
/** @ingroup ctrl
|
| 493 |
+
* @brief Sets the FOCUS_AUTO control.
|
| 494 |
+
* @param devh UVC device handle
|
| 495 |
+
* @param state TODO
|
| 496 |
+
*/
|
| 497 |
+
uvc_error_t uvc_set_focus_auto(uvc_device_handle_t *devh, uint8_t state) {
|
| 498 |
+
uint8_t data[1];
|
| 499 |
+
uvc_error_t ret;
|
| 500 |
+
|
| 501 |
+
data[0] = state;
|
| 502 |
+
|
| 503 |
+
ret = libusb_control_transfer(
|
| 504 |
+
devh->usb_devh,
|
| 505 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 506 |
+
UVC_CT_FOCUS_AUTO_CONTROL << 8,
|
| 507 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 508 |
+
data,
|
| 509 |
+
sizeof(data),
|
| 510 |
+
0);
|
| 511 |
+
|
| 512 |
+
if (ret == sizeof(data))
|
| 513 |
+
return UVC_SUCCESS;
|
| 514 |
+
else
|
| 515 |
+
return ret;
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
/** @ingroup ctrl
|
| 519 |
+
* @brief Reads the IRIS_ABSOLUTE control.
|
| 520 |
+
* @param devh UVC device handle
|
| 521 |
+
* @param[out] iris TODO
|
| 522 |
+
* @param req_code UVC_GET_* request to execute
|
| 523 |
+
*/
|
| 524 |
+
uvc_error_t uvc_get_iris_abs(uvc_device_handle_t *devh, uint16_t* iris, enum uvc_req_code req_code) {
|
| 525 |
+
uint8_t data[2];
|
| 526 |
+
uvc_error_t ret;
|
| 527 |
+
|
| 528 |
+
ret = libusb_control_transfer(
|
| 529 |
+
devh->usb_devh,
|
| 530 |
+
REQ_TYPE_GET, req_code,
|
| 531 |
+
UVC_CT_IRIS_ABSOLUTE_CONTROL << 8,
|
| 532 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 533 |
+
data,
|
| 534 |
+
sizeof(data),
|
| 535 |
+
0);
|
| 536 |
+
|
| 537 |
+
if (ret == sizeof(data)) {
|
| 538 |
+
*iris = SW_TO_SHORT(data + 0);
|
| 539 |
+
return UVC_SUCCESS;
|
| 540 |
+
} else {
|
| 541 |
+
return ret;
|
| 542 |
+
}
|
| 543 |
+
}
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
/** @ingroup ctrl
|
| 547 |
+
* @brief Sets the IRIS_ABSOLUTE control.
|
| 548 |
+
* @param devh UVC device handle
|
| 549 |
+
* @param iris TODO
|
| 550 |
+
*/
|
| 551 |
+
uvc_error_t uvc_set_iris_abs(uvc_device_handle_t *devh, uint16_t iris) {
|
| 552 |
+
uint8_t data[2];
|
| 553 |
+
uvc_error_t ret;
|
| 554 |
+
|
| 555 |
+
SHORT_TO_SW(iris, data + 0);
|
| 556 |
+
|
| 557 |
+
ret = libusb_control_transfer(
|
| 558 |
+
devh->usb_devh,
|
| 559 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 560 |
+
UVC_CT_IRIS_ABSOLUTE_CONTROL << 8,
|
| 561 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 562 |
+
data,
|
| 563 |
+
sizeof(data),
|
| 564 |
+
0);
|
| 565 |
+
|
| 566 |
+
if (ret == sizeof(data))
|
| 567 |
+
return UVC_SUCCESS;
|
| 568 |
+
else
|
| 569 |
+
return ret;
|
| 570 |
+
}
|
| 571 |
+
|
| 572 |
+
/** @ingroup ctrl
|
| 573 |
+
* @brief Reads the IRIS_RELATIVE control.
|
| 574 |
+
* @param devh UVC device handle
|
| 575 |
+
* @param[out] iris_rel TODO
|
| 576 |
+
* @param req_code UVC_GET_* request to execute
|
| 577 |
+
*/
|
| 578 |
+
uvc_error_t uvc_get_iris_rel(uvc_device_handle_t *devh, uint8_t* iris_rel, enum uvc_req_code req_code) {
|
| 579 |
+
uint8_t data[1];
|
| 580 |
+
uvc_error_t ret;
|
| 581 |
+
|
| 582 |
+
ret = libusb_control_transfer(
|
| 583 |
+
devh->usb_devh,
|
| 584 |
+
REQ_TYPE_GET, req_code,
|
| 585 |
+
UVC_CT_IRIS_RELATIVE_CONTROL << 8,
|
| 586 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 587 |
+
data,
|
| 588 |
+
sizeof(data),
|
| 589 |
+
0);
|
| 590 |
+
|
| 591 |
+
if (ret == sizeof(data)) {
|
| 592 |
+
*iris_rel = data[0];
|
| 593 |
+
return UVC_SUCCESS;
|
| 594 |
+
} else {
|
| 595 |
+
return ret;
|
| 596 |
+
}
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
/** @ingroup ctrl
|
| 601 |
+
* @brief Sets the IRIS_RELATIVE control.
|
| 602 |
+
* @param devh UVC device handle
|
| 603 |
+
* @param iris_rel TODO
|
| 604 |
+
*/
|
| 605 |
+
uvc_error_t uvc_set_iris_rel(uvc_device_handle_t *devh, uint8_t iris_rel) {
|
| 606 |
+
uint8_t data[1];
|
| 607 |
+
uvc_error_t ret;
|
| 608 |
+
|
| 609 |
+
data[0] = iris_rel;
|
| 610 |
+
|
| 611 |
+
ret = libusb_control_transfer(
|
| 612 |
+
devh->usb_devh,
|
| 613 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 614 |
+
UVC_CT_IRIS_RELATIVE_CONTROL << 8,
|
| 615 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 616 |
+
data,
|
| 617 |
+
sizeof(data),
|
| 618 |
+
0);
|
| 619 |
+
|
| 620 |
+
if (ret == sizeof(data))
|
| 621 |
+
return UVC_SUCCESS;
|
| 622 |
+
else
|
| 623 |
+
return ret;
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
/** @ingroup ctrl
|
| 627 |
+
* @brief Reads the ZOOM_ABSOLUTE control.
|
| 628 |
+
* @param devh UVC device handle
|
| 629 |
+
* @param[out] focal_length TODO
|
| 630 |
+
* @param req_code UVC_GET_* request to execute
|
| 631 |
+
*/
|
| 632 |
+
uvc_error_t uvc_get_zoom_abs(uvc_device_handle_t *devh, uint16_t* focal_length, enum uvc_req_code req_code) {
|
| 633 |
+
uint8_t data[2];
|
| 634 |
+
uvc_error_t ret;
|
| 635 |
+
|
| 636 |
+
ret = libusb_control_transfer(
|
| 637 |
+
devh->usb_devh,
|
| 638 |
+
REQ_TYPE_GET, req_code,
|
| 639 |
+
UVC_CT_ZOOM_ABSOLUTE_CONTROL << 8,
|
| 640 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 641 |
+
data,
|
| 642 |
+
sizeof(data),
|
| 643 |
+
0);
|
| 644 |
+
|
| 645 |
+
if (ret == sizeof(data)) {
|
| 646 |
+
*focal_length = SW_TO_SHORT(data + 0);
|
| 647 |
+
return UVC_SUCCESS;
|
| 648 |
+
} else {
|
| 649 |
+
return ret;
|
| 650 |
+
}
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
+
|
| 654 |
+
/** @ingroup ctrl
|
| 655 |
+
* @brief Sets the ZOOM_ABSOLUTE control.
|
| 656 |
+
* @param devh UVC device handle
|
| 657 |
+
* @param focal_length TODO
|
| 658 |
+
*/
|
| 659 |
+
uvc_error_t uvc_set_zoom_abs(uvc_device_handle_t *devh, uint16_t focal_length) {
|
| 660 |
+
uint8_t data[2];
|
| 661 |
+
uvc_error_t ret;
|
| 662 |
+
|
| 663 |
+
SHORT_TO_SW(focal_length, data + 0);
|
| 664 |
+
|
| 665 |
+
ret = libusb_control_transfer(
|
| 666 |
+
devh->usb_devh,
|
| 667 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 668 |
+
UVC_CT_ZOOM_ABSOLUTE_CONTROL << 8,
|
| 669 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 670 |
+
data,
|
| 671 |
+
sizeof(data),
|
| 672 |
+
0);
|
| 673 |
+
|
| 674 |
+
if (ret == sizeof(data))
|
| 675 |
+
return UVC_SUCCESS;
|
| 676 |
+
else
|
| 677 |
+
return ret;
|
| 678 |
+
}
|
| 679 |
+
|
| 680 |
+
/** @ingroup ctrl
|
| 681 |
+
* @brief Reads the ZOOM_RELATIVE control.
|
| 682 |
+
* @param devh UVC device handle
|
| 683 |
+
* @param[out] zoom_rel TODO
|
| 684 |
+
* @param[out] digital_zoom TODO
|
| 685 |
+
* @param[out] speed TODO
|
| 686 |
+
* @param req_code UVC_GET_* request to execute
|
| 687 |
+
*/
|
| 688 |
+
uvc_error_t uvc_get_zoom_rel(uvc_device_handle_t *devh, int8_t* zoom_rel, uint8_t* digital_zoom, uint8_t* speed, enum uvc_req_code req_code) {
|
| 689 |
+
uint8_t data[3];
|
| 690 |
+
uvc_error_t ret;
|
| 691 |
+
|
| 692 |
+
ret = libusb_control_transfer(
|
| 693 |
+
devh->usb_devh,
|
| 694 |
+
REQ_TYPE_GET, req_code,
|
| 695 |
+
UVC_CT_ZOOM_RELATIVE_CONTROL << 8,
|
| 696 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 697 |
+
data,
|
| 698 |
+
sizeof(data),
|
| 699 |
+
0);
|
| 700 |
+
|
| 701 |
+
if (ret == sizeof(data)) {
|
| 702 |
+
*zoom_rel = data[0];
|
| 703 |
+
*digital_zoom = data[1];
|
| 704 |
+
*speed = data[2];
|
| 705 |
+
return UVC_SUCCESS;
|
| 706 |
+
} else {
|
| 707 |
+
return ret;
|
| 708 |
+
}
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
|
| 712 |
+
/** @ingroup ctrl
|
| 713 |
+
* @brief Sets the ZOOM_RELATIVE control.
|
| 714 |
+
* @param devh UVC device handle
|
| 715 |
+
* @param zoom_rel TODO
|
| 716 |
+
* @param digital_zoom TODO
|
| 717 |
+
* @param speed TODO
|
| 718 |
+
*/
|
| 719 |
+
uvc_error_t uvc_set_zoom_rel(uvc_device_handle_t *devh, int8_t zoom_rel, uint8_t digital_zoom, uint8_t speed) {
|
| 720 |
+
uint8_t data[3];
|
| 721 |
+
uvc_error_t ret;
|
| 722 |
+
|
| 723 |
+
data[0] = zoom_rel;
|
| 724 |
+
data[1] = digital_zoom;
|
| 725 |
+
data[2] = speed;
|
| 726 |
+
|
| 727 |
+
ret = libusb_control_transfer(
|
| 728 |
+
devh->usb_devh,
|
| 729 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 730 |
+
UVC_CT_ZOOM_RELATIVE_CONTROL << 8,
|
| 731 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 732 |
+
data,
|
| 733 |
+
sizeof(data),
|
| 734 |
+
0);
|
| 735 |
+
|
| 736 |
+
if (ret == sizeof(data))
|
| 737 |
+
return UVC_SUCCESS;
|
| 738 |
+
else
|
| 739 |
+
return ret;
|
| 740 |
+
}
|
| 741 |
+
|
| 742 |
+
/** @ingroup ctrl
|
| 743 |
+
* @brief Reads the PANTILT_ABSOLUTE control.
|
| 744 |
+
* @param devh UVC device handle
|
| 745 |
+
* @param[out] pan TODO
|
| 746 |
+
* @param[out] tilt TODO
|
| 747 |
+
* @param req_code UVC_GET_* request to execute
|
| 748 |
+
*/
|
| 749 |
+
uvc_error_t uvc_get_pantilt_abs(uvc_device_handle_t *devh, int32_t* pan, int32_t* tilt, enum uvc_req_code req_code) {
|
| 750 |
+
uint8_t data[8];
|
| 751 |
+
uvc_error_t ret;
|
| 752 |
+
|
| 753 |
+
ret = libusb_control_transfer(
|
| 754 |
+
devh->usb_devh,
|
| 755 |
+
REQ_TYPE_GET, req_code,
|
| 756 |
+
UVC_CT_PANTILT_ABSOLUTE_CONTROL << 8,
|
| 757 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 758 |
+
data,
|
| 759 |
+
sizeof(data),
|
| 760 |
+
0);
|
| 761 |
+
|
| 762 |
+
if (ret == sizeof(data)) {
|
| 763 |
+
*pan = DW_TO_INT(data + 0);
|
| 764 |
+
*tilt = DW_TO_INT(data + 4);
|
| 765 |
+
return UVC_SUCCESS;
|
| 766 |
+
} else {
|
| 767 |
+
return ret;
|
| 768 |
+
}
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
/** @ingroup ctrl
|
| 773 |
+
* @brief Sets the PANTILT_ABSOLUTE control.
|
| 774 |
+
* @param devh UVC device handle
|
| 775 |
+
* @param pan TODO
|
| 776 |
+
* @param tilt TODO
|
| 777 |
+
*/
|
| 778 |
+
uvc_error_t uvc_set_pantilt_abs(uvc_device_handle_t *devh, int32_t pan, int32_t tilt) {
|
| 779 |
+
uint8_t data[8];
|
| 780 |
+
uvc_error_t ret;
|
| 781 |
+
|
| 782 |
+
INT_TO_DW(pan, data + 0);
|
| 783 |
+
INT_TO_DW(tilt, data + 4);
|
| 784 |
+
|
| 785 |
+
ret = libusb_control_transfer(
|
| 786 |
+
devh->usb_devh,
|
| 787 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 788 |
+
UVC_CT_PANTILT_ABSOLUTE_CONTROL << 8,
|
| 789 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 790 |
+
data,
|
| 791 |
+
sizeof(data),
|
| 792 |
+
0);
|
| 793 |
+
|
| 794 |
+
if (ret == sizeof(data))
|
| 795 |
+
return UVC_SUCCESS;
|
| 796 |
+
else
|
| 797 |
+
return ret;
|
| 798 |
+
}
|
| 799 |
+
|
| 800 |
+
/** @ingroup ctrl
|
| 801 |
+
* @brief Reads the PANTILT_RELATIVE control.
|
| 802 |
+
* @param devh UVC device handle
|
| 803 |
+
* @param[out] pan_rel TODO
|
| 804 |
+
* @param[out] pan_speed TODO
|
| 805 |
+
* @param[out] tilt_rel TODO
|
| 806 |
+
* @param[out] tilt_speed TODO
|
| 807 |
+
* @param req_code UVC_GET_* request to execute
|
| 808 |
+
*/
|
| 809 |
+
uvc_error_t uvc_get_pantilt_rel(uvc_device_handle_t *devh, int8_t* pan_rel, uint8_t* pan_speed, int8_t* tilt_rel, uint8_t* tilt_speed, enum uvc_req_code req_code) {
|
| 810 |
+
uint8_t data[4];
|
| 811 |
+
uvc_error_t ret;
|
| 812 |
+
|
| 813 |
+
ret = libusb_control_transfer(
|
| 814 |
+
devh->usb_devh,
|
| 815 |
+
REQ_TYPE_GET, req_code,
|
| 816 |
+
UVC_CT_PANTILT_RELATIVE_CONTROL << 8,
|
| 817 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 818 |
+
data,
|
| 819 |
+
sizeof(data),
|
| 820 |
+
0);
|
| 821 |
+
|
| 822 |
+
if (ret == sizeof(data)) {
|
| 823 |
+
*pan_rel = data[0];
|
| 824 |
+
*pan_speed = data[1];
|
| 825 |
+
*tilt_rel = data[2];
|
| 826 |
+
*tilt_speed = data[3];
|
| 827 |
+
return UVC_SUCCESS;
|
| 828 |
+
} else {
|
| 829 |
+
return ret;
|
| 830 |
+
}
|
| 831 |
+
}
|
| 832 |
+
|
| 833 |
+
|
| 834 |
+
/** @ingroup ctrl
|
| 835 |
+
* @brief Sets the PANTILT_RELATIVE control.
|
| 836 |
+
* @param devh UVC device handle
|
| 837 |
+
* @param pan_rel TODO
|
| 838 |
+
* @param pan_speed TODO
|
| 839 |
+
* @param tilt_rel TODO
|
| 840 |
+
* @param tilt_speed TODO
|
| 841 |
+
*/
|
| 842 |
+
uvc_error_t uvc_set_pantilt_rel(uvc_device_handle_t *devh, int8_t pan_rel, uint8_t pan_speed, int8_t tilt_rel, uint8_t tilt_speed) {
|
| 843 |
+
uint8_t data[4];
|
| 844 |
+
uvc_error_t ret;
|
| 845 |
+
|
| 846 |
+
data[0] = pan_rel;
|
| 847 |
+
data[1] = pan_speed;
|
| 848 |
+
data[2] = tilt_rel;
|
| 849 |
+
data[3] = tilt_speed;
|
| 850 |
+
|
| 851 |
+
ret = libusb_control_transfer(
|
| 852 |
+
devh->usb_devh,
|
| 853 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 854 |
+
UVC_CT_PANTILT_RELATIVE_CONTROL << 8,
|
| 855 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 856 |
+
data,
|
| 857 |
+
sizeof(data),
|
| 858 |
+
0);
|
| 859 |
+
|
| 860 |
+
if (ret == sizeof(data))
|
| 861 |
+
return UVC_SUCCESS;
|
| 862 |
+
else
|
| 863 |
+
return ret;
|
| 864 |
+
}
|
| 865 |
+
|
| 866 |
+
/** @ingroup ctrl
|
| 867 |
+
* @brief Reads the ROLL_ABSOLUTE control.
|
| 868 |
+
* @param devh UVC device handle
|
| 869 |
+
* @param[out] roll TODO
|
| 870 |
+
* @param req_code UVC_GET_* request to execute
|
| 871 |
+
*/
|
| 872 |
+
uvc_error_t uvc_get_roll_abs(uvc_device_handle_t *devh, int16_t* roll, enum uvc_req_code req_code) {
|
| 873 |
+
uint8_t data[2];
|
| 874 |
+
uvc_error_t ret;
|
| 875 |
+
|
| 876 |
+
ret = libusb_control_transfer(
|
| 877 |
+
devh->usb_devh,
|
| 878 |
+
REQ_TYPE_GET, req_code,
|
| 879 |
+
UVC_CT_ROLL_ABSOLUTE_CONTROL << 8,
|
| 880 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 881 |
+
data,
|
| 882 |
+
sizeof(data),
|
| 883 |
+
0);
|
| 884 |
+
|
| 885 |
+
if (ret == sizeof(data)) {
|
| 886 |
+
*roll = SW_TO_SHORT(data + 0);
|
| 887 |
+
return UVC_SUCCESS;
|
| 888 |
+
} else {
|
| 889 |
+
return ret;
|
| 890 |
+
}
|
| 891 |
+
}
|
| 892 |
+
|
| 893 |
+
|
| 894 |
+
/** @ingroup ctrl
|
| 895 |
+
* @brief Sets the ROLL_ABSOLUTE control.
|
| 896 |
+
* @param devh UVC device handle
|
| 897 |
+
* @param roll TODO
|
| 898 |
+
*/
|
| 899 |
+
uvc_error_t uvc_set_roll_abs(uvc_device_handle_t *devh, int16_t roll) {
|
| 900 |
+
uint8_t data[2];
|
| 901 |
+
uvc_error_t ret;
|
| 902 |
+
|
| 903 |
+
SHORT_TO_SW(roll, data + 0);
|
| 904 |
+
|
| 905 |
+
ret = libusb_control_transfer(
|
| 906 |
+
devh->usb_devh,
|
| 907 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 908 |
+
UVC_CT_ROLL_ABSOLUTE_CONTROL << 8,
|
| 909 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 910 |
+
data,
|
| 911 |
+
sizeof(data),
|
| 912 |
+
0);
|
| 913 |
+
|
| 914 |
+
if (ret == sizeof(data))
|
| 915 |
+
return UVC_SUCCESS;
|
| 916 |
+
else
|
| 917 |
+
return ret;
|
| 918 |
+
}
|
| 919 |
+
|
| 920 |
+
/** @ingroup ctrl
|
| 921 |
+
* @brief Reads the ROLL_RELATIVE control.
|
| 922 |
+
* @param devh UVC device handle
|
| 923 |
+
* @param[out] roll_rel TODO
|
| 924 |
+
* @param[out] speed TODO
|
| 925 |
+
* @param req_code UVC_GET_* request to execute
|
| 926 |
+
*/
|
| 927 |
+
uvc_error_t uvc_get_roll_rel(uvc_device_handle_t *devh, int8_t* roll_rel, uint8_t* speed, enum uvc_req_code req_code) {
|
| 928 |
+
uint8_t data[2];
|
| 929 |
+
uvc_error_t ret;
|
| 930 |
+
|
| 931 |
+
ret = libusb_control_transfer(
|
| 932 |
+
devh->usb_devh,
|
| 933 |
+
REQ_TYPE_GET, req_code,
|
| 934 |
+
UVC_CT_ROLL_RELATIVE_CONTROL << 8,
|
| 935 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 936 |
+
data,
|
| 937 |
+
sizeof(data),
|
| 938 |
+
0);
|
| 939 |
+
|
| 940 |
+
if (ret == sizeof(data)) {
|
| 941 |
+
*roll_rel = data[0];
|
| 942 |
+
*speed = data[1];
|
| 943 |
+
return UVC_SUCCESS;
|
| 944 |
+
} else {
|
| 945 |
+
return ret;
|
| 946 |
+
}
|
| 947 |
+
}
|
| 948 |
+
|
| 949 |
+
|
| 950 |
+
/** @ingroup ctrl
|
| 951 |
+
* @brief Sets the ROLL_RELATIVE control.
|
| 952 |
+
* @param devh UVC device handle
|
| 953 |
+
* @param roll_rel TODO
|
| 954 |
+
* @param speed TODO
|
| 955 |
+
*/
|
| 956 |
+
uvc_error_t uvc_set_roll_rel(uvc_device_handle_t *devh, int8_t roll_rel, uint8_t speed) {
|
| 957 |
+
uint8_t data[2];
|
| 958 |
+
uvc_error_t ret;
|
| 959 |
+
|
| 960 |
+
data[0] = roll_rel;
|
| 961 |
+
data[1] = speed;
|
| 962 |
+
|
| 963 |
+
ret = libusb_control_transfer(
|
| 964 |
+
devh->usb_devh,
|
| 965 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 966 |
+
UVC_CT_ROLL_RELATIVE_CONTROL << 8,
|
| 967 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 968 |
+
data,
|
| 969 |
+
sizeof(data),
|
| 970 |
+
0);
|
| 971 |
+
|
| 972 |
+
if (ret == sizeof(data))
|
| 973 |
+
return UVC_SUCCESS;
|
| 974 |
+
else
|
| 975 |
+
return ret;
|
| 976 |
+
}
|
| 977 |
+
|
| 978 |
+
/** @ingroup ctrl
|
| 979 |
+
* @brief Reads the PRIVACY control.
|
| 980 |
+
* @param devh UVC device handle
|
| 981 |
+
* @param[out] privacy TODO
|
| 982 |
+
* @param req_code UVC_GET_* request to execute
|
| 983 |
+
*/
|
| 984 |
+
uvc_error_t uvc_get_privacy(uvc_device_handle_t *devh, uint8_t* privacy, enum uvc_req_code req_code) {
|
| 985 |
+
uint8_t data[1];
|
| 986 |
+
uvc_error_t ret;
|
| 987 |
+
|
| 988 |
+
ret = libusb_control_transfer(
|
| 989 |
+
devh->usb_devh,
|
| 990 |
+
REQ_TYPE_GET, req_code,
|
| 991 |
+
UVC_CT_PRIVACY_CONTROL << 8,
|
| 992 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 993 |
+
data,
|
| 994 |
+
sizeof(data),
|
| 995 |
+
0);
|
| 996 |
+
|
| 997 |
+
if (ret == sizeof(data)) {
|
| 998 |
+
*privacy = data[0];
|
| 999 |
+
return UVC_SUCCESS;
|
| 1000 |
+
} else {
|
| 1001 |
+
return ret;
|
| 1002 |
+
}
|
| 1003 |
+
}
|
| 1004 |
+
|
| 1005 |
+
|
| 1006 |
+
/** @ingroup ctrl
|
| 1007 |
+
* @brief Sets the PRIVACY control.
|
| 1008 |
+
* @param devh UVC device handle
|
| 1009 |
+
* @param privacy TODO
|
| 1010 |
+
*/
|
| 1011 |
+
uvc_error_t uvc_set_privacy(uvc_device_handle_t *devh, uint8_t privacy) {
|
| 1012 |
+
uint8_t data[1];
|
| 1013 |
+
uvc_error_t ret;
|
| 1014 |
+
|
| 1015 |
+
data[0] = privacy;
|
| 1016 |
+
|
| 1017 |
+
ret = libusb_control_transfer(
|
| 1018 |
+
devh->usb_devh,
|
| 1019 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1020 |
+
UVC_CT_PRIVACY_CONTROL << 8,
|
| 1021 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1022 |
+
data,
|
| 1023 |
+
sizeof(data),
|
| 1024 |
+
0);
|
| 1025 |
+
|
| 1026 |
+
if (ret == sizeof(data))
|
| 1027 |
+
return UVC_SUCCESS;
|
| 1028 |
+
else
|
| 1029 |
+
return ret;
|
| 1030 |
+
}
|
| 1031 |
+
|
| 1032 |
+
/** @ingroup ctrl
|
| 1033 |
+
* @brief Reads the DIGITAL_WINDOW control.
|
| 1034 |
+
* @param devh UVC device handle
|
| 1035 |
+
* @param[out] window_top TODO
|
| 1036 |
+
* @param[out] window_left TODO
|
| 1037 |
+
* @param[out] window_bottom TODO
|
| 1038 |
+
* @param[out] window_right TODO
|
| 1039 |
+
* @param[out] num_steps TODO
|
| 1040 |
+
* @param[out] num_steps_units TODO
|
| 1041 |
+
* @param req_code UVC_GET_* request to execute
|
| 1042 |
+
*/
|
| 1043 |
+
uvc_error_t uvc_get_digital_window(uvc_device_handle_t *devh, uint16_t* window_top, uint16_t* window_left, uint16_t* window_bottom, uint16_t* window_right, uint16_t* num_steps, uint16_t* num_steps_units, enum uvc_req_code req_code) {
|
| 1044 |
+
uint8_t data[12];
|
| 1045 |
+
uvc_error_t ret;
|
| 1046 |
+
|
| 1047 |
+
ret = libusb_control_transfer(
|
| 1048 |
+
devh->usb_devh,
|
| 1049 |
+
REQ_TYPE_GET, req_code,
|
| 1050 |
+
UVC_CT_DIGITAL_WINDOW_CONTROL << 8,
|
| 1051 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1052 |
+
data,
|
| 1053 |
+
sizeof(data),
|
| 1054 |
+
0);
|
| 1055 |
+
|
| 1056 |
+
if (ret == sizeof(data)) {
|
| 1057 |
+
*window_top = SW_TO_SHORT(data + 0);
|
| 1058 |
+
*window_left = SW_TO_SHORT(data + 2);
|
| 1059 |
+
*window_bottom = SW_TO_SHORT(data + 4);
|
| 1060 |
+
*window_right = SW_TO_SHORT(data + 6);
|
| 1061 |
+
*num_steps = SW_TO_SHORT(data + 8);
|
| 1062 |
+
*num_steps_units = SW_TO_SHORT(data + 10);
|
| 1063 |
+
return UVC_SUCCESS;
|
| 1064 |
+
} else {
|
| 1065 |
+
return ret;
|
| 1066 |
+
}
|
| 1067 |
+
}
|
| 1068 |
+
|
| 1069 |
+
|
| 1070 |
+
/** @ingroup ctrl
|
| 1071 |
+
* @brief Sets the DIGITAL_WINDOW control.
|
| 1072 |
+
* @param devh UVC device handle
|
| 1073 |
+
* @param window_top TODO
|
| 1074 |
+
* @param window_left TODO
|
| 1075 |
+
* @param window_bottom TODO
|
| 1076 |
+
* @param window_right TODO
|
| 1077 |
+
* @param num_steps TODO
|
| 1078 |
+
* @param num_steps_units TODO
|
| 1079 |
+
*/
|
| 1080 |
+
uvc_error_t uvc_set_digital_window(uvc_device_handle_t *devh, uint16_t window_top, uint16_t window_left, uint16_t window_bottom, uint16_t window_right, uint16_t num_steps, uint16_t num_steps_units) {
|
| 1081 |
+
uint8_t data[12];
|
| 1082 |
+
uvc_error_t ret;
|
| 1083 |
+
|
| 1084 |
+
SHORT_TO_SW(window_top, data + 0);
|
| 1085 |
+
SHORT_TO_SW(window_left, data + 2);
|
| 1086 |
+
SHORT_TO_SW(window_bottom, data + 4);
|
| 1087 |
+
SHORT_TO_SW(window_right, data + 6);
|
| 1088 |
+
SHORT_TO_SW(num_steps, data + 8);
|
| 1089 |
+
SHORT_TO_SW(num_steps_units, data + 10);
|
| 1090 |
+
|
| 1091 |
+
ret = libusb_control_transfer(
|
| 1092 |
+
devh->usb_devh,
|
| 1093 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1094 |
+
UVC_CT_DIGITAL_WINDOW_CONTROL << 8,
|
| 1095 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1096 |
+
data,
|
| 1097 |
+
sizeof(data),
|
| 1098 |
+
0);
|
| 1099 |
+
|
| 1100 |
+
if (ret == sizeof(data))
|
| 1101 |
+
return UVC_SUCCESS;
|
| 1102 |
+
else
|
| 1103 |
+
return ret;
|
| 1104 |
+
}
|
| 1105 |
+
|
| 1106 |
+
/** @ingroup ctrl
|
| 1107 |
+
* @brief Reads the REGION_OF_INTEREST control.
|
| 1108 |
+
* @param devh UVC device handle
|
| 1109 |
+
* @param[out] roi_top TODO
|
| 1110 |
+
* @param[out] roi_left TODO
|
| 1111 |
+
* @param[out] roi_bottom TODO
|
| 1112 |
+
* @param[out] roi_right TODO
|
| 1113 |
+
* @param[out] auto_controls TODO
|
| 1114 |
+
* @param req_code UVC_GET_* request to execute
|
| 1115 |
+
*/
|
| 1116 |
+
uvc_error_t uvc_get_digital_roi(uvc_device_handle_t *devh, uint16_t* roi_top, uint16_t* roi_left, uint16_t* roi_bottom, uint16_t* roi_right, uint16_t* auto_controls, enum uvc_req_code req_code) {
|
| 1117 |
+
uint8_t data[10];
|
| 1118 |
+
uvc_error_t ret;
|
| 1119 |
+
|
| 1120 |
+
ret = libusb_control_transfer(
|
| 1121 |
+
devh->usb_devh,
|
| 1122 |
+
REQ_TYPE_GET, req_code,
|
| 1123 |
+
UVC_CT_REGION_OF_INTEREST_CONTROL << 8,
|
| 1124 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1125 |
+
data,
|
| 1126 |
+
sizeof(data),
|
| 1127 |
+
0);
|
| 1128 |
+
|
| 1129 |
+
if (ret == sizeof(data)) {
|
| 1130 |
+
*roi_top = SW_TO_SHORT(data + 0);
|
| 1131 |
+
*roi_left = SW_TO_SHORT(data + 2);
|
| 1132 |
+
*roi_bottom = SW_TO_SHORT(data + 4);
|
| 1133 |
+
*roi_right = SW_TO_SHORT(data + 6);
|
| 1134 |
+
*auto_controls = SW_TO_SHORT(data + 8);
|
| 1135 |
+
return UVC_SUCCESS;
|
| 1136 |
+
} else {
|
| 1137 |
+
return ret;
|
| 1138 |
+
}
|
| 1139 |
+
}
|
| 1140 |
+
|
| 1141 |
+
|
| 1142 |
+
/** @ingroup ctrl
|
| 1143 |
+
* @brief Sets the REGION_OF_INTEREST control.
|
| 1144 |
+
* @param devh UVC device handle
|
| 1145 |
+
* @param roi_top TODO
|
| 1146 |
+
* @param roi_left TODO
|
| 1147 |
+
* @param roi_bottom TODO
|
| 1148 |
+
* @param roi_right TODO
|
| 1149 |
+
* @param auto_controls TODO
|
| 1150 |
+
*/
|
| 1151 |
+
uvc_error_t uvc_set_digital_roi(uvc_device_handle_t *devh, uint16_t roi_top, uint16_t roi_left, uint16_t roi_bottom, uint16_t roi_right, uint16_t auto_controls) {
|
| 1152 |
+
uint8_t data[10];
|
| 1153 |
+
uvc_error_t ret;
|
| 1154 |
+
|
| 1155 |
+
SHORT_TO_SW(roi_top, data + 0);
|
| 1156 |
+
SHORT_TO_SW(roi_left, data + 2);
|
| 1157 |
+
SHORT_TO_SW(roi_bottom, data + 4);
|
| 1158 |
+
SHORT_TO_SW(roi_right, data + 6);
|
| 1159 |
+
SHORT_TO_SW(auto_controls, data + 8);
|
| 1160 |
+
|
| 1161 |
+
ret = libusb_control_transfer(
|
| 1162 |
+
devh->usb_devh,
|
| 1163 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1164 |
+
UVC_CT_REGION_OF_INTEREST_CONTROL << 8,
|
| 1165 |
+
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1166 |
+
data,
|
| 1167 |
+
sizeof(data),
|
| 1168 |
+
0);
|
| 1169 |
+
|
| 1170 |
+
if (ret == sizeof(data))
|
| 1171 |
+
return UVC_SUCCESS;
|
| 1172 |
+
else
|
| 1173 |
+
return ret;
|
| 1174 |
+
}
|
| 1175 |
+
|
| 1176 |
+
/** @ingroup ctrl
|
| 1177 |
+
* @brief Reads the BACKLIGHT_COMPENSATION control.
|
| 1178 |
+
* @param devh UVC device handle
|
| 1179 |
+
* @param[out] backlight_compensation device-dependent backlight compensation mode; zero means backlight compensation is disabled
|
| 1180 |
+
* @param req_code UVC_GET_* request to execute
|
| 1181 |
+
*/
|
| 1182 |
+
uvc_error_t uvc_get_backlight_compensation(uvc_device_handle_t *devh, uint16_t* backlight_compensation, enum uvc_req_code req_code) {
|
| 1183 |
+
uint8_t data[2];
|
| 1184 |
+
uvc_error_t ret;
|
| 1185 |
+
|
| 1186 |
+
ret = libusb_control_transfer(
|
| 1187 |
+
devh->usb_devh,
|
| 1188 |
+
REQ_TYPE_GET, req_code,
|
| 1189 |
+
UVC_PU_BACKLIGHT_COMPENSATION_CONTROL << 8,
|
| 1190 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1191 |
+
data,
|
| 1192 |
+
sizeof(data),
|
| 1193 |
+
0);
|
| 1194 |
+
|
| 1195 |
+
if (ret == sizeof(data)) {
|
| 1196 |
+
*backlight_compensation = SW_TO_SHORT(data + 0);
|
| 1197 |
+
return UVC_SUCCESS;
|
| 1198 |
+
} else {
|
| 1199 |
+
return ret;
|
| 1200 |
+
}
|
| 1201 |
+
}
|
| 1202 |
+
|
| 1203 |
+
|
| 1204 |
+
/** @ingroup ctrl
|
| 1205 |
+
* @brief Sets the BACKLIGHT_COMPENSATION control.
|
| 1206 |
+
* @param devh UVC device handle
|
| 1207 |
+
* @param backlight_compensation device-dependent backlight compensation mode; zero means backlight compensation is disabled
|
| 1208 |
+
*/
|
| 1209 |
+
uvc_error_t uvc_set_backlight_compensation(uvc_device_handle_t *devh, uint16_t backlight_compensation) {
|
| 1210 |
+
uint8_t data[2];
|
| 1211 |
+
uvc_error_t ret;
|
| 1212 |
+
|
| 1213 |
+
SHORT_TO_SW(backlight_compensation, data + 0);
|
| 1214 |
+
|
| 1215 |
+
ret = libusb_control_transfer(
|
| 1216 |
+
devh->usb_devh,
|
| 1217 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1218 |
+
UVC_PU_BACKLIGHT_COMPENSATION_CONTROL << 8,
|
| 1219 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1220 |
+
data,
|
| 1221 |
+
sizeof(data),
|
| 1222 |
+
0);
|
| 1223 |
+
|
| 1224 |
+
if (ret == sizeof(data))
|
| 1225 |
+
return UVC_SUCCESS;
|
| 1226 |
+
else
|
| 1227 |
+
return ret;
|
| 1228 |
+
}
|
| 1229 |
+
|
| 1230 |
+
/** @ingroup ctrl
|
| 1231 |
+
* @brief Reads the BRIGHTNESS control.
|
| 1232 |
+
* @param devh UVC device handle
|
| 1233 |
+
* @param[out] brightness TODO
|
| 1234 |
+
* @param req_code UVC_GET_* request to execute
|
| 1235 |
+
*/
|
| 1236 |
+
uvc_error_t uvc_get_brightness(uvc_device_handle_t *devh, int16_t* brightness, enum uvc_req_code req_code) {
|
| 1237 |
+
uint8_t data[2];
|
| 1238 |
+
uvc_error_t ret;
|
| 1239 |
+
|
| 1240 |
+
ret = libusb_control_transfer(
|
| 1241 |
+
devh->usb_devh,
|
| 1242 |
+
REQ_TYPE_GET, req_code,
|
| 1243 |
+
UVC_PU_BRIGHTNESS_CONTROL << 8,
|
| 1244 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1245 |
+
data,
|
| 1246 |
+
sizeof(data),
|
| 1247 |
+
0);
|
| 1248 |
+
|
| 1249 |
+
if (ret == sizeof(data)) {
|
| 1250 |
+
*brightness = SW_TO_SHORT(data + 0);
|
| 1251 |
+
return UVC_SUCCESS;
|
| 1252 |
+
} else {
|
| 1253 |
+
return ret;
|
| 1254 |
+
}
|
| 1255 |
+
}
|
| 1256 |
+
|
| 1257 |
+
|
| 1258 |
+
/** @ingroup ctrl
|
| 1259 |
+
* @brief Sets the BRIGHTNESS control.
|
| 1260 |
+
* @param devh UVC device handle
|
| 1261 |
+
* @param brightness TODO
|
| 1262 |
+
*/
|
| 1263 |
+
uvc_error_t uvc_set_brightness(uvc_device_handle_t *devh, int16_t brightness) {
|
| 1264 |
+
uint8_t data[2];
|
| 1265 |
+
uvc_error_t ret;
|
| 1266 |
+
|
| 1267 |
+
SHORT_TO_SW(brightness, data + 0);
|
| 1268 |
+
|
| 1269 |
+
ret = libusb_control_transfer(
|
| 1270 |
+
devh->usb_devh,
|
| 1271 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1272 |
+
UVC_PU_BRIGHTNESS_CONTROL << 8,
|
| 1273 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1274 |
+
data,
|
| 1275 |
+
sizeof(data),
|
| 1276 |
+
0);
|
| 1277 |
+
|
| 1278 |
+
if (ret == sizeof(data))
|
| 1279 |
+
return UVC_SUCCESS;
|
| 1280 |
+
else
|
| 1281 |
+
return ret;
|
| 1282 |
+
}
|
| 1283 |
+
|
| 1284 |
+
/** @ingroup ctrl
|
| 1285 |
+
* @brief Reads the CONTRAST control.
|
| 1286 |
+
* @param devh UVC device handle
|
| 1287 |
+
* @param[out] contrast TODO
|
| 1288 |
+
* @param req_code UVC_GET_* request to execute
|
| 1289 |
+
*/
|
| 1290 |
+
uvc_error_t uvc_get_contrast(uvc_device_handle_t *devh, uint16_t* contrast, enum uvc_req_code req_code) {
|
| 1291 |
+
uint8_t data[2];
|
| 1292 |
+
uvc_error_t ret;
|
| 1293 |
+
|
| 1294 |
+
ret = libusb_control_transfer(
|
| 1295 |
+
devh->usb_devh,
|
| 1296 |
+
REQ_TYPE_GET, req_code,
|
| 1297 |
+
UVC_PU_CONTRAST_CONTROL << 8,
|
| 1298 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1299 |
+
data,
|
| 1300 |
+
sizeof(data),
|
| 1301 |
+
0);
|
| 1302 |
+
|
| 1303 |
+
if (ret == sizeof(data)) {
|
| 1304 |
+
*contrast = SW_TO_SHORT(data + 0);
|
| 1305 |
+
return UVC_SUCCESS;
|
| 1306 |
+
} else {
|
| 1307 |
+
return ret;
|
| 1308 |
+
}
|
| 1309 |
+
}
|
| 1310 |
+
|
| 1311 |
+
|
| 1312 |
+
/** @ingroup ctrl
|
| 1313 |
+
* @brief Sets the CONTRAST control.
|
| 1314 |
+
* @param devh UVC device handle
|
| 1315 |
+
* @param contrast TODO
|
| 1316 |
+
*/
|
| 1317 |
+
uvc_error_t uvc_set_contrast(uvc_device_handle_t *devh, uint16_t contrast) {
|
| 1318 |
+
uint8_t data[2];
|
| 1319 |
+
uvc_error_t ret;
|
| 1320 |
+
|
| 1321 |
+
SHORT_TO_SW(contrast, data + 0);
|
| 1322 |
+
|
| 1323 |
+
ret = libusb_control_transfer(
|
| 1324 |
+
devh->usb_devh,
|
| 1325 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1326 |
+
UVC_PU_CONTRAST_CONTROL << 8,
|
| 1327 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1328 |
+
data,
|
| 1329 |
+
sizeof(data),
|
| 1330 |
+
0);
|
| 1331 |
+
|
| 1332 |
+
if (ret == sizeof(data))
|
| 1333 |
+
return UVC_SUCCESS;
|
| 1334 |
+
else
|
| 1335 |
+
return ret;
|
| 1336 |
+
}
|
| 1337 |
+
|
| 1338 |
+
/** @ingroup ctrl
|
| 1339 |
+
* @brief Reads the CONTRAST_AUTO control.
|
| 1340 |
+
* @param devh UVC device handle
|
| 1341 |
+
* @param[out] contrast_auto TODO
|
| 1342 |
+
* @param req_code UVC_GET_* request to execute
|
| 1343 |
+
*/
|
| 1344 |
+
uvc_error_t uvc_get_contrast_auto(uvc_device_handle_t *devh, uint8_t* contrast_auto, enum uvc_req_code req_code) {
|
| 1345 |
+
uint8_t data[1];
|
| 1346 |
+
uvc_error_t ret;
|
| 1347 |
+
|
| 1348 |
+
ret = libusb_control_transfer(
|
| 1349 |
+
devh->usb_devh,
|
| 1350 |
+
REQ_TYPE_GET, req_code,
|
| 1351 |
+
UVC_PU_CONTRAST_AUTO_CONTROL << 8,
|
| 1352 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1353 |
+
data,
|
| 1354 |
+
sizeof(data),
|
| 1355 |
+
0);
|
| 1356 |
+
|
| 1357 |
+
if (ret == sizeof(data)) {
|
| 1358 |
+
*contrast_auto = data[0];
|
| 1359 |
+
return UVC_SUCCESS;
|
| 1360 |
+
} else {
|
| 1361 |
+
return ret;
|
| 1362 |
+
}
|
| 1363 |
+
}
|
| 1364 |
+
|
| 1365 |
+
|
| 1366 |
+
/** @ingroup ctrl
|
| 1367 |
+
* @brief Sets the CONTRAST_AUTO control.
|
| 1368 |
+
* @param devh UVC device handle
|
| 1369 |
+
* @param contrast_auto TODO
|
| 1370 |
+
*/
|
| 1371 |
+
uvc_error_t uvc_set_contrast_auto(uvc_device_handle_t *devh, uint8_t contrast_auto) {
|
| 1372 |
+
uint8_t data[1];
|
| 1373 |
+
uvc_error_t ret;
|
| 1374 |
+
|
| 1375 |
+
data[0] = contrast_auto;
|
| 1376 |
+
|
| 1377 |
+
ret = libusb_control_transfer(
|
| 1378 |
+
devh->usb_devh,
|
| 1379 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1380 |
+
UVC_PU_CONTRAST_AUTO_CONTROL << 8,
|
| 1381 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1382 |
+
data,
|
| 1383 |
+
sizeof(data),
|
| 1384 |
+
0);
|
| 1385 |
+
|
| 1386 |
+
if (ret == sizeof(data))
|
| 1387 |
+
return UVC_SUCCESS;
|
| 1388 |
+
else
|
| 1389 |
+
return ret;
|
| 1390 |
+
}
|
| 1391 |
+
|
| 1392 |
+
/** @ingroup ctrl
|
| 1393 |
+
* @brief Reads the GAIN control.
|
| 1394 |
+
* @param devh UVC device handle
|
| 1395 |
+
* @param[out] gain TODO
|
| 1396 |
+
* @param req_code UVC_GET_* request to execute
|
| 1397 |
+
*/
|
| 1398 |
+
uvc_error_t uvc_get_gain(uvc_device_handle_t *devh, uint16_t* gain, enum uvc_req_code req_code) {
|
| 1399 |
+
uint8_t data[2];
|
| 1400 |
+
uvc_error_t ret;
|
| 1401 |
+
|
| 1402 |
+
ret = libusb_control_transfer(
|
| 1403 |
+
devh->usb_devh,
|
| 1404 |
+
REQ_TYPE_GET, req_code,
|
| 1405 |
+
UVC_PU_GAIN_CONTROL << 8,
|
| 1406 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1407 |
+
data,
|
| 1408 |
+
sizeof(data),
|
| 1409 |
+
0);
|
| 1410 |
+
|
| 1411 |
+
if (ret == sizeof(data)) {
|
| 1412 |
+
*gain = SW_TO_SHORT(data + 0);
|
| 1413 |
+
return UVC_SUCCESS;
|
| 1414 |
+
} else {
|
| 1415 |
+
return ret;
|
| 1416 |
+
}
|
| 1417 |
+
}
|
| 1418 |
+
|
| 1419 |
+
|
| 1420 |
+
/** @ingroup ctrl
|
| 1421 |
+
* @brief Sets the GAIN control.
|
| 1422 |
+
* @param devh UVC device handle
|
| 1423 |
+
* @param gain TODO
|
| 1424 |
+
*/
|
| 1425 |
+
uvc_error_t uvc_set_gain(uvc_device_handle_t *devh, uint16_t gain) {
|
| 1426 |
+
uint8_t data[2];
|
| 1427 |
+
uvc_error_t ret;
|
| 1428 |
+
|
| 1429 |
+
SHORT_TO_SW(gain, data + 0);
|
| 1430 |
+
|
| 1431 |
+
ret = libusb_control_transfer(
|
| 1432 |
+
devh->usb_devh,
|
| 1433 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1434 |
+
UVC_PU_GAIN_CONTROL << 8,
|
| 1435 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1436 |
+
data,
|
| 1437 |
+
sizeof(data),
|
| 1438 |
+
0);
|
| 1439 |
+
|
| 1440 |
+
if (ret == sizeof(data))
|
| 1441 |
+
return UVC_SUCCESS;
|
| 1442 |
+
else
|
| 1443 |
+
return ret;
|
| 1444 |
+
}
|
| 1445 |
+
|
| 1446 |
+
/** @ingroup ctrl
|
| 1447 |
+
* @brief Reads the POWER_LINE_FREQUENCY control.
|
| 1448 |
+
* @param devh UVC device handle
|
| 1449 |
+
* @param[out] power_line_frequency TODO
|
| 1450 |
+
* @param req_code UVC_GET_* request to execute
|
| 1451 |
+
*/
|
| 1452 |
+
uvc_error_t uvc_get_power_line_frequency(uvc_device_handle_t *devh, uint8_t* power_line_frequency, enum uvc_req_code req_code) {
|
| 1453 |
+
uint8_t data[1];
|
| 1454 |
+
uvc_error_t ret;
|
| 1455 |
+
|
| 1456 |
+
ret = libusb_control_transfer(
|
| 1457 |
+
devh->usb_devh,
|
| 1458 |
+
REQ_TYPE_GET, req_code,
|
| 1459 |
+
UVC_PU_POWER_LINE_FREQUENCY_CONTROL << 8,
|
| 1460 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1461 |
+
data,
|
| 1462 |
+
sizeof(data),
|
| 1463 |
+
0);
|
| 1464 |
+
|
| 1465 |
+
if (ret == sizeof(data)) {
|
| 1466 |
+
*power_line_frequency = data[0];
|
| 1467 |
+
return UVC_SUCCESS;
|
| 1468 |
+
} else {
|
| 1469 |
+
return ret;
|
| 1470 |
+
}
|
| 1471 |
+
}
|
| 1472 |
+
|
| 1473 |
+
|
| 1474 |
+
/** @ingroup ctrl
|
| 1475 |
+
* @brief Sets the POWER_LINE_FREQUENCY control.
|
| 1476 |
+
* @param devh UVC device handle
|
| 1477 |
+
* @param power_line_frequency TODO
|
| 1478 |
+
*/
|
| 1479 |
+
uvc_error_t uvc_set_power_line_frequency(uvc_device_handle_t *devh, uint8_t power_line_frequency) {
|
| 1480 |
+
uint8_t data[1];
|
| 1481 |
+
uvc_error_t ret;
|
| 1482 |
+
|
| 1483 |
+
data[0] = power_line_frequency;
|
| 1484 |
+
|
| 1485 |
+
ret = libusb_control_transfer(
|
| 1486 |
+
devh->usb_devh,
|
| 1487 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1488 |
+
UVC_PU_POWER_LINE_FREQUENCY_CONTROL << 8,
|
| 1489 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1490 |
+
data,
|
| 1491 |
+
sizeof(data),
|
| 1492 |
+
0);
|
| 1493 |
+
|
| 1494 |
+
if (ret == sizeof(data))
|
| 1495 |
+
return UVC_SUCCESS;
|
| 1496 |
+
else
|
| 1497 |
+
return ret;
|
| 1498 |
+
}
|
| 1499 |
+
|
| 1500 |
+
/** @ingroup ctrl
|
| 1501 |
+
* @brief Reads the HUE control.
|
| 1502 |
+
* @param devh UVC device handle
|
| 1503 |
+
* @param[out] hue TODO
|
| 1504 |
+
* @param req_code UVC_GET_* request to execute
|
| 1505 |
+
*/
|
| 1506 |
+
uvc_error_t uvc_get_hue(uvc_device_handle_t *devh, int16_t* hue, enum uvc_req_code req_code) {
|
| 1507 |
+
uint8_t data[2];
|
| 1508 |
+
uvc_error_t ret;
|
| 1509 |
+
|
| 1510 |
+
ret = libusb_control_transfer(
|
| 1511 |
+
devh->usb_devh,
|
| 1512 |
+
REQ_TYPE_GET, req_code,
|
| 1513 |
+
UVC_PU_HUE_CONTROL << 8,
|
| 1514 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1515 |
+
data,
|
| 1516 |
+
sizeof(data),
|
| 1517 |
+
0);
|
| 1518 |
+
|
| 1519 |
+
if (ret == sizeof(data)) {
|
| 1520 |
+
*hue = SW_TO_SHORT(data + 0);
|
| 1521 |
+
return UVC_SUCCESS;
|
| 1522 |
+
} else {
|
| 1523 |
+
return ret;
|
| 1524 |
+
}
|
| 1525 |
+
}
|
| 1526 |
+
|
| 1527 |
+
|
| 1528 |
+
/** @ingroup ctrl
|
| 1529 |
+
* @brief Sets the HUE control.
|
| 1530 |
+
* @param devh UVC device handle
|
| 1531 |
+
* @param hue TODO
|
| 1532 |
+
*/
|
| 1533 |
+
uvc_error_t uvc_set_hue(uvc_device_handle_t *devh, int16_t hue) {
|
| 1534 |
+
uint8_t data[2];
|
| 1535 |
+
uvc_error_t ret;
|
| 1536 |
+
|
| 1537 |
+
SHORT_TO_SW(hue, data + 0);
|
| 1538 |
+
|
| 1539 |
+
ret = libusb_control_transfer(
|
| 1540 |
+
devh->usb_devh,
|
| 1541 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1542 |
+
UVC_PU_HUE_CONTROL << 8,
|
| 1543 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1544 |
+
data,
|
| 1545 |
+
sizeof(data),
|
| 1546 |
+
0);
|
| 1547 |
+
|
| 1548 |
+
if (ret == sizeof(data))
|
| 1549 |
+
return UVC_SUCCESS;
|
| 1550 |
+
else
|
| 1551 |
+
return ret;
|
| 1552 |
+
}
|
| 1553 |
+
|
| 1554 |
+
/** @ingroup ctrl
|
| 1555 |
+
* @brief Reads the HUE_AUTO control.
|
| 1556 |
+
* @param devh UVC device handle
|
| 1557 |
+
* @param[out] hue_auto TODO
|
| 1558 |
+
* @param req_code UVC_GET_* request to execute
|
| 1559 |
+
*/
|
| 1560 |
+
uvc_error_t uvc_get_hue_auto(uvc_device_handle_t *devh, uint8_t* hue_auto, enum uvc_req_code req_code) {
|
| 1561 |
+
uint8_t data[1];
|
| 1562 |
+
uvc_error_t ret;
|
| 1563 |
+
|
| 1564 |
+
ret = libusb_control_transfer(
|
| 1565 |
+
devh->usb_devh,
|
| 1566 |
+
REQ_TYPE_GET, req_code,
|
| 1567 |
+
UVC_PU_HUE_AUTO_CONTROL << 8,
|
| 1568 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1569 |
+
data,
|
| 1570 |
+
sizeof(data),
|
| 1571 |
+
0);
|
| 1572 |
+
|
| 1573 |
+
if (ret == sizeof(data)) {
|
| 1574 |
+
*hue_auto = data[0];
|
| 1575 |
+
return UVC_SUCCESS;
|
| 1576 |
+
} else {
|
| 1577 |
+
return ret;
|
| 1578 |
+
}
|
| 1579 |
+
}
|
| 1580 |
+
|
| 1581 |
+
|
| 1582 |
+
/** @ingroup ctrl
|
| 1583 |
+
* @brief Sets the HUE_AUTO control.
|
| 1584 |
+
* @param devh UVC device handle
|
| 1585 |
+
* @param hue_auto TODO
|
| 1586 |
+
*/
|
| 1587 |
+
uvc_error_t uvc_set_hue_auto(uvc_device_handle_t *devh, uint8_t hue_auto) {
|
| 1588 |
+
uint8_t data[1];
|
| 1589 |
+
uvc_error_t ret;
|
| 1590 |
+
|
| 1591 |
+
data[0] = hue_auto;
|
| 1592 |
+
|
| 1593 |
+
ret = libusb_control_transfer(
|
| 1594 |
+
devh->usb_devh,
|
| 1595 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1596 |
+
UVC_PU_HUE_AUTO_CONTROL << 8,
|
| 1597 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1598 |
+
data,
|
| 1599 |
+
sizeof(data),
|
| 1600 |
+
0);
|
| 1601 |
+
|
| 1602 |
+
if (ret == sizeof(data))
|
| 1603 |
+
return UVC_SUCCESS;
|
| 1604 |
+
else
|
| 1605 |
+
return ret;
|
| 1606 |
+
}
|
| 1607 |
+
|
| 1608 |
+
/** @ingroup ctrl
|
| 1609 |
+
* @brief Reads the SATURATION control.
|
| 1610 |
+
* @param devh UVC device handle
|
| 1611 |
+
* @param[out] saturation TODO
|
| 1612 |
+
* @param req_code UVC_GET_* request to execute
|
| 1613 |
+
*/
|
| 1614 |
+
uvc_error_t uvc_get_saturation(uvc_device_handle_t *devh, uint16_t* saturation, enum uvc_req_code req_code) {
|
| 1615 |
+
uint8_t data[2];
|
| 1616 |
+
uvc_error_t ret;
|
| 1617 |
+
|
| 1618 |
+
ret = libusb_control_transfer(
|
| 1619 |
+
devh->usb_devh,
|
| 1620 |
+
REQ_TYPE_GET, req_code,
|
| 1621 |
+
UVC_PU_SATURATION_CONTROL << 8,
|
| 1622 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1623 |
+
data,
|
| 1624 |
+
sizeof(data),
|
| 1625 |
+
0);
|
| 1626 |
+
|
| 1627 |
+
if (ret == sizeof(data)) {
|
| 1628 |
+
*saturation = SW_TO_SHORT(data + 0);
|
| 1629 |
+
return UVC_SUCCESS;
|
| 1630 |
+
} else {
|
| 1631 |
+
return ret;
|
| 1632 |
+
}
|
| 1633 |
+
}
|
| 1634 |
+
|
| 1635 |
+
|
| 1636 |
+
/** @ingroup ctrl
|
| 1637 |
+
* @brief Sets the SATURATION control.
|
| 1638 |
+
* @param devh UVC device handle
|
| 1639 |
+
* @param saturation TODO
|
| 1640 |
+
*/
|
| 1641 |
+
uvc_error_t uvc_set_saturation(uvc_device_handle_t *devh, uint16_t saturation) {
|
| 1642 |
+
uint8_t data[2];
|
| 1643 |
+
uvc_error_t ret;
|
| 1644 |
+
|
| 1645 |
+
SHORT_TO_SW(saturation, data + 0);
|
| 1646 |
+
|
| 1647 |
+
ret = libusb_control_transfer(
|
| 1648 |
+
devh->usb_devh,
|
| 1649 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1650 |
+
UVC_PU_SATURATION_CONTROL << 8,
|
| 1651 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1652 |
+
data,
|
| 1653 |
+
sizeof(data),
|
| 1654 |
+
0);
|
| 1655 |
+
|
| 1656 |
+
if (ret == sizeof(data))
|
| 1657 |
+
return UVC_SUCCESS;
|
| 1658 |
+
else
|
| 1659 |
+
return ret;
|
| 1660 |
+
}
|
| 1661 |
+
|
| 1662 |
+
/** @ingroup ctrl
|
| 1663 |
+
* @brief Reads the SHARPNESS control.
|
| 1664 |
+
* @param devh UVC device handle
|
| 1665 |
+
* @param[out] sharpness TODO
|
| 1666 |
+
* @param req_code UVC_GET_* request to execute
|
| 1667 |
+
*/
|
| 1668 |
+
uvc_error_t uvc_get_sharpness(uvc_device_handle_t *devh, uint16_t* sharpness, enum uvc_req_code req_code) {
|
| 1669 |
+
uint8_t data[2];
|
| 1670 |
+
uvc_error_t ret;
|
| 1671 |
+
|
| 1672 |
+
ret = libusb_control_transfer(
|
| 1673 |
+
devh->usb_devh,
|
| 1674 |
+
REQ_TYPE_GET, req_code,
|
| 1675 |
+
UVC_PU_SHARPNESS_CONTROL << 8,
|
| 1676 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1677 |
+
data,
|
| 1678 |
+
sizeof(data),
|
| 1679 |
+
0);
|
| 1680 |
+
|
| 1681 |
+
if (ret == sizeof(data)) {
|
| 1682 |
+
*sharpness = SW_TO_SHORT(data + 0);
|
| 1683 |
+
return UVC_SUCCESS;
|
| 1684 |
+
} else {
|
| 1685 |
+
return ret;
|
| 1686 |
+
}
|
| 1687 |
+
}
|
| 1688 |
+
|
| 1689 |
+
|
| 1690 |
+
/** @ingroup ctrl
|
| 1691 |
+
* @brief Sets the SHARPNESS control.
|
| 1692 |
+
* @param devh UVC device handle
|
| 1693 |
+
* @param sharpness TODO
|
| 1694 |
+
*/
|
| 1695 |
+
uvc_error_t uvc_set_sharpness(uvc_device_handle_t *devh, uint16_t sharpness) {
|
| 1696 |
+
uint8_t data[2];
|
| 1697 |
+
uvc_error_t ret;
|
| 1698 |
+
|
| 1699 |
+
SHORT_TO_SW(sharpness, data + 0);
|
| 1700 |
+
|
| 1701 |
+
ret = libusb_control_transfer(
|
| 1702 |
+
devh->usb_devh,
|
| 1703 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1704 |
+
UVC_PU_SHARPNESS_CONTROL << 8,
|
| 1705 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1706 |
+
data,
|
| 1707 |
+
sizeof(data),
|
| 1708 |
+
0);
|
| 1709 |
+
|
| 1710 |
+
if (ret == sizeof(data))
|
| 1711 |
+
return UVC_SUCCESS;
|
| 1712 |
+
else
|
| 1713 |
+
return ret;
|
| 1714 |
+
}
|
| 1715 |
+
|
| 1716 |
+
/** @ingroup ctrl
|
| 1717 |
+
* @brief Reads the GAMMA control.
|
| 1718 |
+
* @param devh UVC device handle
|
| 1719 |
+
* @param[out] gamma TODO
|
| 1720 |
+
* @param req_code UVC_GET_* request to execute
|
| 1721 |
+
*/
|
| 1722 |
+
uvc_error_t uvc_get_gamma(uvc_device_handle_t *devh, uint16_t* gamma, enum uvc_req_code req_code) {
|
| 1723 |
+
uint8_t data[2];
|
| 1724 |
+
uvc_error_t ret;
|
| 1725 |
+
|
| 1726 |
+
ret = libusb_control_transfer(
|
| 1727 |
+
devh->usb_devh,
|
| 1728 |
+
REQ_TYPE_GET, req_code,
|
| 1729 |
+
UVC_PU_GAMMA_CONTROL << 8,
|
| 1730 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1731 |
+
data,
|
| 1732 |
+
sizeof(data),
|
| 1733 |
+
0);
|
| 1734 |
+
|
| 1735 |
+
if (ret == sizeof(data)) {
|
| 1736 |
+
*gamma = SW_TO_SHORT(data + 0);
|
| 1737 |
+
return UVC_SUCCESS;
|
| 1738 |
+
} else {
|
| 1739 |
+
return ret;
|
| 1740 |
+
}
|
| 1741 |
+
}
|
| 1742 |
+
|
| 1743 |
+
|
| 1744 |
+
/** @ingroup ctrl
|
| 1745 |
+
* @brief Sets the GAMMA control.
|
| 1746 |
+
* @param devh UVC device handle
|
| 1747 |
+
* @param gamma TODO
|
| 1748 |
+
*/
|
| 1749 |
+
uvc_error_t uvc_set_gamma(uvc_device_handle_t *devh, uint16_t gamma) {
|
| 1750 |
+
uint8_t data[2];
|
| 1751 |
+
uvc_error_t ret;
|
| 1752 |
+
|
| 1753 |
+
SHORT_TO_SW(gamma, data + 0);
|
| 1754 |
+
|
| 1755 |
+
ret = libusb_control_transfer(
|
| 1756 |
+
devh->usb_devh,
|
| 1757 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1758 |
+
UVC_PU_GAMMA_CONTROL << 8,
|
| 1759 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1760 |
+
data,
|
| 1761 |
+
sizeof(data),
|
| 1762 |
+
0);
|
| 1763 |
+
|
| 1764 |
+
if (ret == sizeof(data))
|
| 1765 |
+
return UVC_SUCCESS;
|
| 1766 |
+
else
|
| 1767 |
+
return ret;
|
| 1768 |
+
}
|
| 1769 |
+
|
| 1770 |
+
/** @ingroup ctrl
|
| 1771 |
+
* @brief Reads the WHITE_BALANCE_TEMPERATURE control.
|
| 1772 |
+
* @param devh UVC device handle
|
| 1773 |
+
* @param[out] temperature TODO
|
| 1774 |
+
* @param req_code UVC_GET_* request to execute
|
| 1775 |
+
*/
|
| 1776 |
+
uvc_error_t uvc_get_white_balance_temperature(uvc_device_handle_t *devh, uint16_t* temperature, enum uvc_req_code req_code) {
|
| 1777 |
+
uint8_t data[2];
|
| 1778 |
+
uvc_error_t ret;
|
| 1779 |
+
|
| 1780 |
+
ret = libusb_control_transfer(
|
| 1781 |
+
devh->usb_devh,
|
| 1782 |
+
REQ_TYPE_GET, req_code,
|
| 1783 |
+
UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL << 8,
|
| 1784 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1785 |
+
data,
|
| 1786 |
+
sizeof(data),
|
| 1787 |
+
0);
|
| 1788 |
+
|
| 1789 |
+
if (ret == sizeof(data)) {
|
| 1790 |
+
*temperature = SW_TO_SHORT(data + 0);
|
| 1791 |
+
return UVC_SUCCESS;
|
| 1792 |
+
} else {
|
| 1793 |
+
return ret;
|
| 1794 |
+
}
|
| 1795 |
+
}
|
| 1796 |
+
|
| 1797 |
+
|
| 1798 |
+
/** @ingroup ctrl
|
| 1799 |
+
* @brief Sets the WHITE_BALANCE_TEMPERATURE control.
|
| 1800 |
+
* @param devh UVC device handle
|
| 1801 |
+
* @param temperature TODO
|
| 1802 |
+
*/
|
| 1803 |
+
uvc_error_t uvc_set_white_balance_temperature(uvc_device_handle_t *devh, uint16_t temperature) {
|
| 1804 |
+
uint8_t data[2];
|
| 1805 |
+
uvc_error_t ret;
|
| 1806 |
+
|
| 1807 |
+
SHORT_TO_SW(temperature, data + 0);
|
| 1808 |
+
|
| 1809 |
+
ret = libusb_control_transfer(
|
| 1810 |
+
devh->usb_devh,
|
| 1811 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1812 |
+
UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL << 8,
|
| 1813 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1814 |
+
data,
|
| 1815 |
+
sizeof(data),
|
| 1816 |
+
0);
|
| 1817 |
+
|
| 1818 |
+
if (ret == sizeof(data))
|
| 1819 |
+
return UVC_SUCCESS;
|
| 1820 |
+
else
|
| 1821 |
+
return ret;
|
| 1822 |
+
}
|
| 1823 |
+
|
| 1824 |
+
/** @ingroup ctrl
|
| 1825 |
+
* @brief Reads the WHITE_BALANCE_TEMPERATURE_AUTO control.
|
| 1826 |
+
* @param devh UVC device handle
|
| 1827 |
+
* @param[out] temperature_auto TODO
|
| 1828 |
+
* @param req_code UVC_GET_* request to execute
|
| 1829 |
+
*/
|
| 1830 |
+
uvc_error_t uvc_get_white_balance_temperature_auto(uvc_device_handle_t *devh, uint8_t* temperature_auto, enum uvc_req_code req_code) {
|
| 1831 |
+
uint8_t data[1];
|
| 1832 |
+
uvc_error_t ret;
|
| 1833 |
+
|
| 1834 |
+
ret = libusb_control_transfer(
|
| 1835 |
+
devh->usb_devh,
|
| 1836 |
+
REQ_TYPE_GET, req_code,
|
| 1837 |
+
UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL << 8,
|
| 1838 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1839 |
+
data,
|
| 1840 |
+
sizeof(data),
|
| 1841 |
+
0);
|
| 1842 |
+
|
| 1843 |
+
if (ret == sizeof(data)) {
|
| 1844 |
+
*temperature_auto = data[0];
|
| 1845 |
+
return UVC_SUCCESS;
|
| 1846 |
+
} else {
|
| 1847 |
+
return ret;
|
| 1848 |
+
}
|
| 1849 |
+
}
|
| 1850 |
+
|
| 1851 |
+
|
| 1852 |
+
/** @ingroup ctrl
|
| 1853 |
+
* @brief Sets the WHITE_BALANCE_TEMPERATURE_AUTO control.
|
| 1854 |
+
* @param devh UVC device handle
|
| 1855 |
+
* @param temperature_auto TODO
|
| 1856 |
+
*/
|
| 1857 |
+
uvc_error_t uvc_set_white_balance_temperature_auto(uvc_device_handle_t *devh, uint8_t temperature_auto) {
|
| 1858 |
+
uint8_t data[1];
|
| 1859 |
+
uvc_error_t ret;
|
| 1860 |
+
|
| 1861 |
+
data[0] = temperature_auto;
|
| 1862 |
+
|
| 1863 |
+
ret = libusb_control_transfer(
|
| 1864 |
+
devh->usb_devh,
|
| 1865 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1866 |
+
UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL << 8,
|
| 1867 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1868 |
+
data,
|
| 1869 |
+
sizeof(data),
|
| 1870 |
+
0);
|
| 1871 |
+
|
| 1872 |
+
if (ret == sizeof(data))
|
| 1873 |
+
return UVC_SUCCESS;
|
| 1874 |
+
else
|
| 1875 |
+
return ret;
|
| 1876 |
+
}
|
| 1877 |
+
|
| 1878 |
+
/** @ingroup ctrl
|
| 1879 |
+
* @brief Reads the WHITE_BALANCE_COMPONENT control.
|
| 1880 |
+
* @param devh UVC device handle
|
| 1881 |
+
* @param[out] blue TODO
|
| 1882 |
+
* @param[out] red TODO
|
| 1883 |
+
* @param req_code UVC_GET_* request to execute
|
| 1884 |
+
*/
|
| 1885 |
+
uvc_error_t uvc_get_white_balance_component(uvc_device_handle_t *devh, uint16_t* blue, uint16_t* red, enum uvc_req_code req_code) {
|
| 1886 |
+
uint8_t data[4];
|
| 1887 |
+
uvc_error_t ret;
|
| 1888 |
+
|
| 1889 |
+
ret = libusb_control_transfer(
|
| 1890 |
+
devh->usb_devh,
|
| 1891 |
+
REQ_TYPE_GET, req_code,
|
| 1892 |
+
UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL << 8,
|
| 1893 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1894 |
+
data,
|
| 1895 |
+
sizeof(data),
|
| 1896 |
+
0);
|
| 1897 |
+
|
| 1898 |
+
if (ret == sizeof(data)) {
|
| 1899 |
+
*blue = SW_TO_SHORT(data + 0);
|
| 1900 |
+
*red = SW_TO_SHORT(data + 2);
|
| 1901 |
+
return UVC_SUCCESS;
|
| 1902 |
+
} else {
|
| 1903 |
+
return ret;
|
| 1904 |
+
}
|
| 1905 |
+
}
|
| 1906 |
+
|
| 1907 |
+
|
| 1908 |
+
/** @ingroup ctrl
|
| 1909 |
+
* @brief Sets the WHITE_BALANCE_COMPONENT control.
|
| 1910 |
+
* @param devh UVC device handle
|
| 1911 |
+
* @param blue TODO
|
| 1912 |
+
* @param red TODO
|
| 1913 |
+
*/
|
| 1914 |
+
uvc_error_t uvc_set_white_balance_component(uvc_device_handle_t *devh, uint16_t blue, uint16_t red) {
|
| 1915 |
+
uint8_t data[4];
|
| 1916 |
+
uvc_error_t ret;
|
| 1917 |
+
|
| 1918 |
+
SHORT_TO_SW(blue, data + 0);
|
| 1919 |
+
SHORT_TO_SW(red, data + 2);
|
| 1920 |
+
|
| 1921 |
+
ret = libusb_control_transfer(
|
| 1922 |
+
devh->usb_devh,
|
| 1923 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1924 |
+
UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL << 8,
|
| 1925 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1926 |
+
data,
|
| 1927 |
+
sizeof(data),
|
| 1928 |
+
0);
|
| 1929 |
+
|
| 1930 |
+
if (ret == sizeof(data))
|
| 1931 |
+
return UVC_SUCCESS;
|
| 1932 |
+
else
|
| 1933 |
+
return ret;
|
| 1934 |
+
}
|
| 1935 |
+
|
| 1936 |
+
/** @ingroup ctrl
|
| 1937 |
+
* @brief Reads the WHITE_BALANCE_COMPONENT_AUTO control.
|
| 1938 |
+
* @param devh UVC device handle
|
| 1939 |
+
* @param[out] white_balance_component_auto TODO
|
| 1940 |
+
* @param req_code UVC_GET_* request to execute
|
| 1941 |
+
*/
|
| 1942 |
+
uvc_error_t uvc_get_white_balance_component_auto(uvc_device_handle_t *devh, uint8_t* white_balance_component_auto, enum uvc_req_code req_code) {
|
| 1943 |
+
uint8_t data[1];
|
| 1944 |
+
uvc_error_t ret;
|
| 1945 |
+
|
| 1946 |
+
ret = libusb_control_transfer(
|
| 1947 |
+
devh->usb_devh,
|
| 1948 |
+
REQ_TYPE_GET, req_code,
|
| 1949 |
+
UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL << 8,
|
| 1950 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1951 |
+
data,
|
| 1952 |
+
sizeof(data),
|
| 1953 |
+
0);
|
| 1954 |
+
|
| 1955 |
+
if (ret == sizeof(data)) {
|
| 1956 |
+
*white_balance_component_auto = data[0];
|
| 1957 |
+
return UVC_SUCCESS;
|
| 1958 |
+
} else {
|
| 1959 |
+
return ret;
|
| 1960 |
+
}
|
| 1961 |
+
}
|
| 1962 |
+
|
| 1963 |
+
|
| 1964 |
+
/** @ingroup ctrl
|
| 1965 |
+
* @brief Sets the WHITE_BALANCE_COMPONENT_AUTO control.
|
| 1966 |
+
* @param devh UVC device handle
|
| 1967 |
+
* @param white_balance_component_auto TODO
|
| 1968 |
+
*/
|
| 1969 |
+
uvc_error_t uvc_set_white_balance_component_auto(uvc_device_handle_t *devh, uint8_t white_balance_component_auto) {
|
| 1970 |
+
uint8_t data[1];
|
| 1971 |
+
uvc_error_t ret;
|
| 1972 |
+
|
| 1973 |
+
data[0] = white_balance_component_auto;
|
| 1974 |
+
|
| 1975 |
+
ret = libusb_control_transfer(
|
| 1976 |
+
devh->usb_devh,
|
| 1977 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 1978 |
+
UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL << 8,
|
| 1979 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 1980 |
+
data,
|
| 1981 |
+
sizeof(data),
|
| 1982 |
+
0);
|
| 1983 |
+
|
| 1984 |
+
if (ret == sizeof(data))
|
| 1985 |
+
return UVC_SUCCESS;
|
| 1986 |
+
else
|
| 1987 |
+
return ret;
|
| 1988 |
+
}
|
| 1989 |
+
|
| 1990 |
+
/** @ingroup ctrl
|
| 1991 |
+
* @brief Reads the DIGITAL_MULTIPLIER control.
|
| 1992 |
+
* @param devh UVC device handle
|
| 1993 |
+
* @param[out] multiplier_step TODO
|
| 1994 |
+
* @param req_code UVC_GET_* request to execute
|
| 1995 |
+
*/
|
| 1996 |
+
uvc_error_t uvc_get_digital_multiplier(uvc_device_handle_t *devh, uint16_t* multiplier_step, enum uvc_req_code req_code) {
|
| 1997 |
+
uint8_t data[2];
|
| 1998 |
+
uvc_error_t ret;
|
| 1999 |
+
|
| 2000 |
+
ret = libusb_control_transfer(
|
| 2001 |
+
devh->usb_devh,
|
| 2002 |
+
REQ_TYPE_GET, req_code,
|
| 2003 |
+
UVC_PU_DIGITAL_MULTIPLIER_CONTROL << 8,
|
| 2004 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2005 |
+
data,
|
| 2006 |
+
sizeof(data),
|
| 2007 |
+
0);
|
| 2008 |
+
|
| 2009 |
+
if (ret == sizeof(data)) {
|
| 2010 |
+
*multiplier_step = SW_TO_SHORT(data + 0);
|
| 2011 |
+
return UVC_SUCCESS;
|
| 2012 |
+
} else {
|
| 2013 |
+
return ret;
|
| 2014 |
+
}
|
| 2015 |
+
}
|
| 2016 |
+
|
| 2017 |
+
|
| 2018 |
+
/** @ingroup ctrl
|
| 2019 |
+
* @brief Sets the DIGITAL_MULTIPLIER control.
|
| 2020 |
+
* @param devh UVC device handle
|
| 2021 |
+
* @param multiplier_step TODO
|
| 2022 |
+
*/
|
| 2023 |
+
uvc_error_t uvc_set_digital_multiplier(uvc_device_handle_t *devh, uint16_t multiplier_step) {
|
| 2024 |
+
uint8_t data[2];
|
| 2025 |
+
uvc_error_t ret;
|
| 2026 |
+
|
| 2027 |
+
SHORT_TO_SW(multiplier_step, data + 0);
|
| 2028 |
+
|
| 2029 |
+
ret = libusb_control_transfer(
|
| 2030 |
+
devh->usb_devh,
|
| 2031 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 2032 |
+
UVC_PU_DIGITAL_MULTIPLIER_CONTROL << 8,
|
| 2033 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2034 |
+
data,
|
| 2035 |
+
sizeof(data),
|
| 2036 |
+
0);
|
| 2037 |
+
|
| 2038 |
+
if (ret == sizeof(data))
|
| 2039 |
+
return UVC_SUCCESS;
|
| 2040 |
+
else
|
| 2041 |
+
return ret;
|
| 2042 |
+
}
|
| 2043 |
+
|
| 2044 |
+
/** @ingroup ctrl
|
| 2045 |
+
* @brief Reads the DIGITAL_MULTIPLIER_LIMIT control.
|
| 2046 |
+
* @param devh UVC device handle
|
| 2047 |
+
* @param[out] multiplier_step TODO
|
| 2048 |
+
* @param req_code UVC_GET_* request to execute
|
| 2049 |
+
*/
|
| 2050 |
+
uvc_error_t uvc_get_digital_multiplier_limit(uvc_device_handle_t *devh, uint16_t* multiplier_step, enum uvc_req_code req_code) {
|
| 2051 |
+
uint8_t data[2];
|
| 2052 |
+
uvc_error_t ret;
|
| 2053 |
+
|
| 2054 |
+
ret = libusb_control_transfer(
|
| 2055 |
+
devh->usb_devh,
|
| 2056 |
+
REQ_TYPE_GET, req_code,
|
| 2057 |
+
UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL << 8,
|
| 2058 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2059 |
+
data,
|
| 2060 |
+
sizeof(data),
|
| 2061 |
+
0);
|
| 2062 |
+
|
| 2063 |
+
if (ret == sizeof(data)) {
|
| 2064 |
+
*multiplier_step = SW_TO_SHORT(data + 0);
|
| 2065 |
+
return UVC_SUCCESS;
|
| 2066 |
+
} else {
|
| 2067 |
+
return ret;
|
| 2068 |
+
}
|
| 2069 |
+
}
|
| 2070 |
+
|
| 2071 |
+
|
| 2072 |
+
/** @ingroup ctrl
|
| 2073 |
+
* @brief Sets the DIGITAL_MULTIPLIER_LIMIT control.
|
| 2074 |
+
* @param devh UVC device handle
|
| 2075 |
+
* @param multiplier_step TODO
|
| 2076 |
+
*/
|
| 2077 |
+
uvc_error_t uvc_set_digital_multiplier_limit(uvc_device_handle_t *devh, uint16_t multiplier_step) {
|
| 2078 |
+
uint8_t data[2];
|
| 2079 |
+
uvc_error_t ret;
|
| 2080 |
+
|
| 2081 |
+
SHORT_TO_SW(multiplier_step, data + 0);
|
| 2082 |
+
|
| 2083 |
+
ret = libusb_control_transfer(
|
| 2084 |
+
devh->usb_devh,
|
| 2085 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 2086 |
+
UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL << 8,
|
| 2087 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2088 |
+
data,
|
| 2089 |
+
sizeof(data),
|
| 2090 |
+
0);
|
| 2091 |
+
|
| 2092 |
+
if (ret == sizeof(data))
|
| 2093 |
+
return UVC_SUCCESS;
|
| 2094 |
+
else
|
| 2095 |
+
return ret;
|
| 2096 |
+
}
|
| 2097 |
+
|
| 2098 |
+
/** @ingroup ctrl
|
| 2099 |
+
* @brief Reads the ANALOG_VIDEO_STANDARD control.
|
| 2100 |
+
* @param devh UVC device handle
|
| 2101 |
+
* @param[out] video_standard TODO
|
| 2102 |
+
* @param req_code UVC_GET_* request to execute
|
| 2103 |
+
*/
|
| 2104 |
+
uvc_error_t uvc_get_analog_video_standard(uvc_device_handle_t *devh, uint8_t* video_standard, enum uvc_req_code req_code) {
|
| 2105 |
+
uint8_t data[1];
|
| 2106 |
+
uvc_error_t ret;
|
| 2107 |
+
|
| 2108 |
+
ret = libusb_control_transfer(
|
| 2109 |
+
devh->usb_devh,
|
| 2110 |
+
REQ_TYPE_GET, req_code,
|
| 2111 |
+
UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL << 8,
|
| 2112 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2113 |
+
data,
|
| 2114 |
+
sizeof(data),
|
| 2115 |
+
0);
|
| 2116 |
+
|
| 2117 |
+
if (ret == sizeof(data)) {
|
| 2118 |
+
*video_standard = data[0];
|
| 2119 |
+
return UVC_SUCCESS;
|
| 2120 |
+
} else {
|
| 2121 |
+
return ret;
|
| 2122 |
+
}
|
| 2123 |
+
}
|
| 2124 |
+
|
| 2125 |
+
|
| 2126 |
+
/** @ingroup ctrl
|
| 2127 |
+
* @brief Sets the ANALOG_VIDEO_STANDARD control.
|
| 2128 |
+
* @param devh UVC device handle
|
| 2129 |
+
* @param video_standard TODO
|
| 2130 |
+
*/
|
| 2131 |
+
uvc_error_t uvc_set_analog_video_standard(uvc_device_handle_t *devh, uint8_t video_standard) {
|
| 2132 |
+
uint8_t data[1];
|
| 2133 |
+
uvc_error_t ret;
|
| 2134 |
+
|
| 2135 |
+
data[0] = video_standard;
|
| 2136 |
+
|
| 2137 |
+
ret = libusb_control_transfer(
|
| 2138 |
+
devh->usb_devh,
|
| 2139 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 2140 |
+
UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL << 8,
|
| 2141 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2142 |
+
data,
|
| 2143 |
+
sizeof(data),
|
| 2144 |
+
0);
|
| 2145 |
+
|
| 2146 |
+
if (ret == sizeof(data))
|
| 2147 |
+
return UVC_SUCCESS;
|
| 2148 |
+
else
|
| 2149 |
+
return ret;
|
| 2150 |
+
}
|
| 2151 |
+
|
| 2152 |
+
/** @ingroup ctrl
|
| 2153 |
+
* @brief Reads the ANALOG_LOCK_STATUS control.
|
| 2154 |
+
* @param devh UVC device handle
|
| 2155 |
+
* @param[out] status TODO
|
| 2156 |
+
* @param req_code UVC_GET_* request to execute
|
| 2157 |
+
*/
|
| 2158 |
+
uvc_error_t uvc_get_analog_video_lock_status(uvc_device_handle_t *devh, uint8_t* status, enum uvc_req_code req_code) {
|
| 2159 |
+
uint8_t data[1];
|
| 2160 |
+
uvc_error_t ret;
|
| 2161 |
+
|
| 2162 |
+
ret = libusb_control_transfer(
|
| 2163 |
+
devh->usb_devh,
|
| 2164 |
+
REQ_TYPE_GET, req_code,
|
| 2165 |
+
UVC_PU_ANALOG_LOCK_STATUS_CONTROL << 8,
|
| 2166 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2167 |
+
data,
|
| 2168 |
+
sizeof(data),
|
| 2169 |
+
0);
|
| 2170 |
+
|
| 2171 |
+
if (ret == sizeof(data)) {
|
| 2172 |
+
*status = data[0];
|
| 2173 |
+
return UVC_SUCCESS;
|
| 2174 |
+
} else {
|
| 2175 |
+
return ret;
|
| 2176 |
+
}
|
| 2177 |
+
}
|
| 2178 |
+
|
| 2179 |
+
|
| 2180 |
+
/** @ingroup ctrl
|
| 2181 |
+
* @brief Sets the ANALOG_LOCK_STATUS control.
|
| 2182 |
+
* @param devh UVC device handle
|
| 2183 |
+
* @param status TODO
|
| 2184 |
+
*/
|
| 2185 |
+
uvc_error_t uvc_set_analog_video_lock_status(uvc_device_handle_t *devh, uint8_t status) {
|
| 2186 |
+
uint8_t data[1];
|
| 2187 |
+
uvc_error_t ret;
|
| 2188 |
+
|
| 2189 |
+
data[0] = status;
|
| 2190 |
+
|
| 2191 |
+
ret = libusb_control_transfer(
|
| 2192 |
+
devh->usb_devh,
|
| 2193 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 2194 |
+
UVC_PU_ANALOG_LOCK_STATUS_CONTROL << 8,
|
| 2195 |
+
uvc_get_processing_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2196 |
+
data,
|
| 2197 |
+
sizeof(data),
|
| 2198 |
+
0);
|
| 2199 |
+
|
| 2200 |
+
if (ret == sizeof(data))
|
| 2201 |
+
return UVC_SUCCESS;
|
| 2202 |
+
else
|
| 2203 |
+
return ret;
|
| 2204 |
+
}
|
| 2205 |
+
|
| 2206 |
+
/** @ingroup ctrl
|
| 2207 |
+
* @brief Reads the INPUT_SELECT control.
|
| 2208 |
+
* @param devh UVC device handle
|
| 2209 |
+
* @param[out] selector TODO
|
| 2210 |
+
* @param req_code UVC_GET_* request to execute
|
| 2211 |
+
*/
|
| 2212 |
+
uvc_error_t uvc_get_input_select(uvc_device_handle_t *devh, uint8_t* selector, enum uvc_req_code req_code) {
|
| 2213 |
+
uint8_t data[1];
|
| 2214 |
+
uvc_error_t ret;
|
| 2215 |
+
|
| 2216 |
+
ret = libusb_control_transfer(
|
| 2217 |
+
devh->usb_devh,
|
| 2218 |
+
REQ_TYPE_GET, req_code,
|
| 2219 |
+
UVC_SU_INPUT_SELECT_CONTROL << 8,
|
| 2220 |
+
uvc_get_selector_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2221 |
+
data,
|
| 2222 |
+
sizeof(data),
|
| 2223 |
+
0);
|
| 2224 |
+
|
| 2225 |
+
if (ret == sizeof(data)) {
|
| 2226 |
+
*selector = data[0];
|
| 2227 |
+
return UVC_SUCCESS;
|
| 2228 |
+
} else {
|
| 2229 |
+
return ret;
|
| 2230 |
+
}
|
| 2231 |
+
}
|
| 2232 |
+
|
| 2233 |
+
|
| 2234 |
+
/** @ingroup ctrl
|
| 2235 |
+
* @brief Sets the INPUT_SELECT control.
|
| 2236 |
+
* @param devh UVC device handle
|
| 2237 |
+
* @param selector TODO
|
| 2238 |
+
*/
|
| 2239 |
+
uvc_error_t uvc_set_input_select(uvc_device_handle_t *devh, uint8_t selector) {
|
| 2240 |
+
uint8_t data[1];
|
| 2241 |
+
uvc_error_t ret;
|
| 2242 |
+
|
| 2243 |
+
data[0] = selector;
|
| 2244 |
+
|
| 2245 |
+
ret = libusb_control_transfer(
|
| 2246 |
+
devh->usb_devh,
|
| 2247 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 2248 |
+
UVC_SU_INPUT_SELECT_CONTROL << 8,
|
| 2249 |
+
uvc_get_selector_units(devh)->bUnitID << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 2250 |
+
data,
|
| 2251 |
+
sizeof(data),
|
| 2252 |
+
0);
|
| 2253 |
+
|
| 2254 |
+
if (ret == sizeof(data))
|
| 2255 |
+
return UVC_SUCCESS;
|
| 2256 |
+
else
|
| 2257 |
+
return ret;
|
| 2258 |
+
}
|
| 2259 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/ctrl-gen.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
from __future__ import print_function
|
| 3 |
+
from collections import OrderedDict
|
| 4 |
+
import getopt
|
| 5 |
+
import sys
|
| 6 |
+
import yaml
|
| 7 |
+
|
| 8 |
+
class quoted(str): pass
|
| 9 |
+
|
| 10 |
+
def quoted_presenter(dumper, data):
|
| 11 |
+
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"')
|
| 12 |
+
yaml.add_representer(quoted, quoted_presenter)
|
| 13 |
+
|
| 14 |
+
class literal(str): pass
|
| 15 |
+
|
| 16 |
+
def literal_presenter(dumper, data):
|
| 17 |
+
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
|
| 18 |
+
yaml.add_representer(literal, literal_presenter)
|
| 19 |
+
|
| 20 |
+
def ordered_dict_presenter(dumper, data):
|
| 21 |
+
return dumper.represent_dict(data.items())
|
| 22 |
+
yaml.add_representer(OrderedDict, ordered_dict_presenter)
|
| 23 |
+
|
| 24 |
+
def dict_constructor(loader, node):
|
| 25 |
+
return OrderedDict(loader.construct_pairs(node))
|
| 26 |
+
_mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
|
| 27 |
+
yaml.add_constructor(_mapping_tag, dict_constructor)
|
| 28 |
+
|
| 29 |
+
class IntField(object):
|
| 30 |
+
def __init__(self, name, position, length, signed):
|
| 31 |
+
self.name = name
|
| 32 |
+
self.position = position
|
| 33 |
+
self.length = length
|
| 34 |
+
self.signed = signed
|
| 35 |
+
|
| 36 |
+
if not self.length in [1, 2, 4]:
|
| 37 |
+
raise Exception("bad length " + str(self.length))
|
| 38 |
+
|
| 39 |
+
self.user_type = ('u' if not signed else '') + 'int' + str(length * 8) + '_t'
|
| 40 |
+
|
| 41 |
+
def getter_sig(self):
|
| 42 |
+
return "{0}* {1}".format(self.user_type, self.name)
|
| 43 |
+
|
| 44 |
+
def unpack(self):
|
| 45 |
+
if self.length == 1:
|
| 46 |
+
return "*{0} = data[{1}];".format(self.name, self.position)
|
| 47 |
+
elif self.length == 2:
|
| 48 |
+
return "*{0} = SW_TO_SHORT(data + {1});".format(self.name, self.position)
|
| 49 |
+
elif self.length == 4:
|
| 50 |
+
return "*{0} = DW_TO_INT(data + {1});".format(self.name, self.position)
|
| 51 |
+
|
| 52 |
+
def setter_sig(self):
|
| 53 |
+
return "{0} {1}".format(self.user_type, self.name)
|
| 54 |
+
|
| 55 |
+
def pack(self):
|
| 56 |
+
if self.length == 1:
|
| 57 |
+
return "data[{0}] = {1};".format(self.position, self.name)
|
| 58 |
+
elif self.length == 2:
|
| 59 |
+
return "SHORT_TO_SW({0}, data + {1});".format(self.name, self.position)
|
| 60 |
+
elif self.length == 4:
|
| 61 |
+
return "INT_TO_DW({0}, data + {1});".format(self.name, self.position)
|
| 62 |
+
|
| 63 |
+
def spec(self):
|
| 64 |
+
rep = [('position', self.position), ('length', self.length)]
|
| 65 |
+
if self.signed:
|
| 66 |
+
rep.append(('signed', True))
|
| 67 |
+
return rep
|
| 68 |
+
|
| 69 |
+
@staticmethod
|
| 70 |
+
def load(spec):
|
| 71 |
+
return IntField(spec['name'], spec['position'], spec['length'], spec['signed'] if signed in spec else False)
|
| 72 |
+
|
| 73 |
+
def load_field(name, spec):
|
| 74 |
+
if spec['type'] == 'int':
|
| 75 |
+
return IntField(name, spec['position'], spec['length'], spec.get('signed', False))
|
| 76 |
+
else:
|
| 77 |
+
raise Exception("unknown field type '{0}'".format(spec['type']))
|
| 78 |
+
|
| 79 |
+
GETTER_TEMPLATE = """/** @ingroup ctrl
|
| 80 |
+
* {gen_doc}
|
| 81 |
+
* @param devh UVC device handle
|
| 82 |
+
* {args_doc}
|
| 83 |
+
* @param req_code UVC_GET_* request to execute
|
| 84 |
+
*/
|
| 85 |
+
uvc_error_t uvc_get_{control_name}(uvc_device_handle_t *devh, {args_signature}, enum uvc_req_code req_code) {{
|
| 86 |
+
uint8_t data[{control_length}];
|
| 87 |
+
uvc_error_t ret;
|
| 88 |
+
|
| 89 |
+
ret = libusb_control_transfer(
|
| 90 |
+
devh->usb_devh,
|
| 91 |
+
REQ_TYPE_GET, req_code,
|
| 92 |
+
{control_code} << 8,
|
| 93 |
+
{unit_fn} << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 94 |
+
data,
|
| 95 |
+
sizeof(data),
|
| 96 |
+
0);
|
| 97 |
+
|
| 98 |
+
if (ret == sizeof(data)) {{
|
| 99 |
+
{unpack}
|
| 100 |
+
return UVC_SUCCESS;
|
| 101 |
+
}} else {{
|
| 102 |
+
return ret;
|
| 103 |
+
}}
|
| 104 |
+
}}
|
| 105 |
+
"""
|
| 106 |
+
|
| 107 |
+
SETTER_TEMPLATE = """/** @ingroup ctrl
|
| 108 |
+
* {gen_doc}
|
| 109 |
+
* @param devh UVC device handle
|
| 110 |
+
* {args_doc}
|
| 111 |
+
*/
|
| 112 |
+
uvc_error_t uvc_set_{control_name}(uvc_device_handle_t *devh, {args_signature}) {{
|
| 113 |
+
uint8_t data[{control_length}];
|
| 114 |
+
uvc_error_t ret;
|
| 115 |
+
|
| 116 |
+
{pack}
|
| 117 |
+
|
| 118 |
+
ret = libusb_control_transfer(
|
| 119 |
+
devh->usb_devh,
|
| 120 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 121 |
+
{control_code} << 8,
|
| 122 |
+
{unit_fn} << 8 | devh->info->ctrl_if.bInterfaceNumber,
|
| 123 |
+
data,
|
| 124 |
+
sizeof(data),
|
| 125 |
+
0);
|
| 126 |
+
|
| 127 |
+
if (ret == sizeof(data))
|
| 128 |
+
return UVC_SUCCESS;
|
| 129 |
+
else
|
| 130 |
+
return ret;
|
| 131 |
+
}}
|
| 132 |
+
"""
|
| 133 |
+
|
| 134 |
+
def gen_decl(unit_name, unit, control_name, control):
|
| 135 |
+
fields = [(load_field(field_name, field_details), field_details['doc']) for field_name, field_details in control['fields'].items()] if 'fields' in control else []
|
| 136 |
+
|
| 137 |
+
get_args_signature = ', '.join([field.getter_sig() for (field, desc) in fields])
|
| 138 |
+
set_args_signature = ', '.join([field.setter_sig() for (field, desc) in fields])
|
| 139 |
+
|
| 140 |
+
return "uvc_error_t uvc_get_{function_name}(uvc_device_handle_t *devh, {args_signature}, enum uvc_req_code req_code);\n".format(**{
|
| 141 |
+
"function_name": control_name,
|
| 142 |
+
"args_signature": get_args_signature
|
| 143 |
+
}) + "uvc_error_t uvc_set_{function_name}(uvc_device_handle_t *devh, {args_signature});\n".format(**{
|
| 144 |
+
"function_name": control_name,
|
| 145 |
+
"args_signature": set_args_signature
|
| 146 |
+
})
|
| 147 |
+
|
| 148 |
+
def gen_ctrl(unit_name, unit, control_name, control):
|
| 149 |
+
fields = [(load_field(field_name, field_details), field_details['doc']) for field_name, field_details in control['fields'].items()] if 'fields' in control else []
|
| 150 |
+
|
| 151 |
+
get_args_signature = ', '.join([field.getter_sig() for (field, desc) in fields])
|
| 152 |
+
set_args_signature = ', '.join([field.setter_sig() for (field, desc) in fields])
|
| 153 |
+
unpack = "\n ".join([field.unpack() for (field, desc) in fields])
|
| 154 |
+
pack = "\n ".join([field.pack() for (field, desc) in fields])
|
| 155 |
+
|
| 156 |
+
get_gen_doc_raw = None
|
| 157 |
+
set_gen_doc_raw = None
|
| 158 |
+
|
| 159 |
+
if 'doc' in control:
|
| 160 |
+
doc = control['doc']
|
| 161 |
+
|
| 162 |
+
if isinstance(doc, str):
|
| 163 |
+
get_gen_doc_raw = "\n * ".join(doc.splitlines())
|
| 164 |
+
set_gen_doc_raw = get_gen_doc_raw
|
| 165 |
+
else:
|
| 166 |
+
if 'get' in doc:
|
| 167 |
+
get_gen_doc_raw = "\n * ".join(doc['get'].splitlines())
|
| 168 |
+
if 'set' in doc:
|
| 169 |
+
set_gen_doc_raw = "\n * ".join(doc['set'].splitlines())
|
| 170 |
+
|
| 171 |
+
if get_gen_doc_raw is not None:
|
| 172 |
+
get_gen_doc = get_gen_doc_raw.format(gets_sets='Reads')
|
| 173 |
+
else:
|
| 174 |
+
get_gen_doc = '@brief Reads the ' + control['control'] + ' control.'
|
| 175 |
+
|
| 176 |
+
if set_gen_doc_raw is not None:
|
| 177 |
+
set_gen_doc = set_gen_doc_raw.format(gets_sets='Sets')
|
| 178 |
+
else:
|
| 179 |
+
set_gen_doc = '@brief Sets the ' + control['control'] + ' control.'
|
| 180 |
+
|
| 181 |
+
get_args_doc = "\n * ".join(["@param[out] {0} {1}".format(field.name, desc) for (field, desc) in fields])
|
| 182 |
+
set_args_doc = "\n * ".join(["@param {0} {1}".format(field.name, desc) for (field, desc) in fields])
|
| 183 |
+
|
| 184 |
+
control_code = 'UVC_' + unit['control_prefix'] + '_' + control['control'] + '_CONTROL'
|
| 185 |
+
|
| 186 |
+
unit_fn = "uvc_get_camera_terminal(devh)->bTerminalID" if (unit_name == "camera_terminal") else ("uvc_get_" + unit_name + "s(devh)->bUnitID")
|
| 187 |
+
|
| 188 |
+
return GETTER_TEMPLATE.format(
|
| 189 |
+
unit=unit,
|
| 190 |
+
unit_fn=unit_fn,
|
| 191 |
+
control_name=control_name,
|
| 192 |
+
control_code=control_code,
|
| 193 |
+
control_length=control['length'],
|
| 194 |
+
args_signature=get_args_signature,
|
| 195 |
+
args_doc=get_args_doc,
|
| 196 |
+
gen_doc=get_gen_doc,
|
| 197 |
+
unpack=unpack) + "\n\n" + SETTER_TEMPLATE.format(
|
| 198 |
+
unit=unit,
|
| 199 |
+
unit_fn=unit_fn,
|
| 200 |
+
control_name=control_name,
|
| 201 |
+
control_code=control_code,
|
| 202 |
+
control_length=control['length'],
|
| 203 |
+
args_signature=set_args_signature,
|
| 204 |
+
args_doc=set_args_doc,
|
| 205 |
+
gen_doc=set_gen_doc,
|
| 206 |
+
pack=pack
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
def export_unit(unit):
|
| 210 |
+
def fmt_doc(doc):
|
| 211 |
+
def wrap_doc_entry(entry):
|
| 212 |
+
if "\n" in entry:
|
| 213 |
+
return literal(entry)
|
| 214 |
+
else:
|
| 215 |
+
return entry
|
| 216 |
+
|
| 217 |
+
if isinstance(doc, str):
|
| 218 |
+
return wrap_doc_entry(doc)
|
| 219 |
+
else:
|
| 220 |
+
return OrderedDict([(mode, wrap_doc_entry(text)) for mode, text in doc.items()])
|
| 221 |
+
|
| 222 |
+
def fmt_ctrl(control_name, control_details):
|
| 223 |
+
contents = OrderedDict()
|
| 224 |
+
contents['control'] = control_details['control']
|
| 225 |
+
contents['length'] = control_details['length']
|
| 226 |
+
contents['fields'] = control_details['fields']
|
| 227 |
+
|
| 228 |
+
if 'doc' in control_details:
|
| 229 |
+
contents['doc'] = fmt_doc(control_details['doc'])
|
| 230 |
+
|
| 231 |
+
return (control_name, contents)
|
| 232 |
+
|
| 233 |
+
unit_out = OrderedDict()
|
| 234 |
+
unit_out['type'] = unit['type']
|
| 235 |
+
if 'guid' in unit:
|
| 236 |
+
unit_out['guid'] = unit['guid']
|
| 237 |
+
if 'description' in unit:
|
| 238 |
+
unit_out['description'] = unit['description']
|
| 239 |
+
if 'control_prefix' in unit:
|
| 240 |
+
unit_out['control_prefix'] = unit['control_prefix']
|
| 241 |
+
unit_out['controls'] = OrderedDict([fmt_ctrl(ctrl_name, ctrl_details) for ctrl_name, ctrl_details in unit['controls'].items()])
|
| 242 |
+
return unit_out
|
| 243 |
+
|
| 244 |
+
if __name__ == '__main__':
|
| 245 |
+
try:
|
| 246 |
+
opts, args = getopt.getopt(sys.argv[1:], "hi:", ["help", "input="])
|
| 247 |
+
except getopt.GetoptError as err:
|
| 248 |
+
print(str(err))
|
| 249 |
+
usage()
|
| 250 |
+
sys.exit(-1)
|
| 251 |
+
|
| 252 |
+
inputs = []
|
| 253 |
+
|
| 254 |
+
for opt, val in opts:
|
| 255 |
+
if opt in ('-h', '--help'):
|
| 256 |
+
usage()
|
| 257 |
+
sys.exit(0)
|
| 258 |
+
elif opt in ('-i', '--input'):
|
| 259 |
+
inputs.append(val)
|
| 260 |
+
|
| 261 |
+
mode = None
|
| 262 |
+
for arg in args:
|
| 263 |
+
if arg in ('def', 'decl', 'yaml'):
|
| 264 |
+
if mode is None:
|
| 265 |
+
mode = arg
|
| 266 |
+
else:
|
| 267 |
+
print("Can't specify more than one mode")
|
| 268 |
+
sys.exit(-1)
|
| 269 |
+
else:
|
| 270 |
+
print("Invalid mode '{0}'".format(arg))
|
| 271 |
+
sys.exit(-1)
|
| 272 |
+
|
| 273 |
+
def iterunits():
|
| 274 |
+
for input_file in inputs:
|
| 275 |
+
with open(input_file, "r") as fp:
|
| 276 |
+
units = yaml.load(fp)['units']
|
| 277 |
+
for unit_name, unit_details in units.iteritems():
|
| 278 |
+
yield unit_name, unit_details
|
| 279 |
+
|
| 280 |
+
if mode == 'def':
|
| 281 |
+
print("""/* This is an AUTO-GENERATED file! Update it with the output of `ctrl-gen.py def`. */
|
| 282 |
+
#include "libuvc/libuvc.h"
|
| 283 |
+
#include "libuvc/libuvc_internal.h"
|
| 284 |
+
|
| 285 |
+
static const int REQ_TYPE_SET = 0x21;
|
| 286 |
+
static const int REQ_TYPE_GET = 0xa1;
|
| 287 |
+
""")
|
| 288 |
+
fun = gen_ctrl
|
| 289 |
+
elif mode == 'decl':
|
| 290 |
+
fun = gen_decl
|
| 291 |
+
elif mode == 'yaml':
|
| 292 |
+
exported_units = OrderedDict()
|
| 293 |
+
for unit_name, unit_details in iterunits():
|
| 294 |
+
exported_units[unit_name] = export_unit(unit_details)
|
| 295 |
+
|
| 296 |
+
yaml.dump({'units': exported_units}, sys.stdout, default_flow_style=False)
|
| 297 |
+
sys.exit(0)
|
| 298 |
+
|
| 299 |
+
for unit_name, unit_details in iterunits():
|
| 300 |
+
for control_name, control_details in unit_details['controls'].iteritems():
|
| 301 |
+
code = fun(unit_name, unit_details, control_name, control_details)
|
| 302 |
+
print(code)
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/ctrl.c
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
/**
|
| 35 |
+
* @defgroup ctrl Video capture and processing controls
|
| 36 |
+
* @brief Functions for manipulating device settings and stream parameters
|
| 37 |
+
*
|
| 38 |
+
* The `uvc_get_*` and `uvc_set_*` functions are used to read and write the settings associated
|
| 39 |
+
* with the device's input, processing and output units.
|
| 40 |
+
*/
|
| 41 |
+
|
| 42 |
+
#include "libuvc/libuvc.h"
|
| 43 |
+
#include "libuvc/libuvc_internal.h"
|
| 44 |
+
|
| 45 |
+
static const int REQ_TYPE_SET = 0x21;
|
| 46 |
+
static const int REQ_TYPE_GET = 0xa1;
|
| 47 |
+
|
| 48 |
+
/***** GENERIC CONTROLS *****/
|
| 49 |
+
/**
|
| 50 |
+
* @brief Get the length of a control on a terminal or unit.
|
| 51 |
+
*
|
| 52 |
+
* @param devh UVC device handle
|
| 53 |
+
* @param unit Unit or Terminal ID; obtain this from the uvc_extension_unit_t describing the extension unit
|
| 54 |
+
* @param ctrl Vendor-specific control number to query
|
| 55 |
+
* @return On success, the length of the control as reported by the device. Otherwise,
|
| 56 |
+
* a uvc_error_t error describing the error encountered.
|
| 57 |
+
* @ingroup ctrl
|
| 58 |
+
*/
|
| 59 |
+
int uvc_get_ctrl_len(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl) {
|
| 60 |
+
unsigned char buf[2];
|
| 61 |
+
|
| 62 |
+
int ret = libusb_control_transfer(
|
| 63 |
+
devh->usb_devh,
|
| 64 |
+
REQ_TYPE_GET, UVC_GET_LEN,
|
| 65 |
+
ctrl << 8,
|
| 66 |
+
unit << 8 | devh->info->ctrl_if.bInterfaceNumber, // XXX saki
|
| 67 |
+
buf,
|
| 68 |
+
2,
|
| 69 |
+
0 /* timeout */);
|
| 70 |
+
|
| 71 |
+
if (ret < 0)
|
| 72 |
+
return ret;
|
| 73 |
+
else
|
| 74 |
+
return (unsigned short)SW_TO_SHORT(buf);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/**
|
| 78 |
+
* @brief Perform a GET_* request from an extension unit.
|
| 79 |
+
*
|
| 80 |
+
* @param devh UVC device handle
|
| 81 |
+
* @param unit Unit ID; obtain this from the uvc_extension_unit_t describing the extension unit
|
| 82 |
+
* @param ctrl Control number to query
|
| 83 |
+
* @param data Data buffer to be filled by the device
|
| 84 |
+
* @param len Size of data buffer
|
| 85 |
+
* @param req_code GET_* request to execute
|
| 86 |
+
* @return On success, the number of bytes actually transferred. Otherwise,
|
| 87 |
+
* a uvc_error_t error describing the error encountered.
|
| 88 |
+
* @ingroup ctrl
|
| 89 |
+
*/
|
| 90 |
+
int uvc_get_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len, enum uvc_req_code req_code) {
|
| 91 |
+
return libusb_control_transfer(
|
| 92 |
+
devh->usb_devh,
|
| 93 |
+
REQ_TYPE_GET, req_code,
|
| 94 |
+
ctrl << 8,
|
| 95 |
+
unit << 8 | devh->info->ctrl_if.bInterfaceNumber, // XXX saki
|
| 96 |
+
data,
|
| 97 |
+
len,
|
| 98 |
+
0 /* timeout */);
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
/**
|
| 102 |
+
* @brief Perform a SET_CUR request to a terminal or unit.
|
| 103 |
+
*
|
| 104 |
+
* @param devh UVC device handle
|
| 105 |
+
* @param unit Unit or Terminal ID
|
| 106 |
+
* @param ctrl Control number to set
|
| 107 |
+
* @param data Data buffer to be sent to the device
|
| 108 |
+
* @param len Size of data buffer
|
| 109 |
+
* @return On success, the number of bytes actually transferred. Otherwise,
|
| 110 |
+
* a uvc_error_t error describing the error encountered.
|
| 111 |
+
* @ingroup ctrl
|
| 112 |
+
*/
|
| 113 |
+
int uvc_set_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len) {
|
| 114 |
+
return libusb_control_transfer(
|
| 115 |
+
devh->usb_devh,
|
| 116 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 117 |
+
ctrl << 8,
|
| 118 |
+
unit << 8 | devh->info->ctrl_if.bInterfaceNumber, // XXX saki
|
| 119 |
+
data,
|
| 120 |
+
len,
|
| 121 |
+
0 /* timeout */);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
/***** INTERFACE CONTROLS *****/
|
| 125 |
+
uvc_error_t uvc_get_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode *mode, enum uvc_req_code req_code) {
|
| 126 |
+
uint8_t mode_char;
|
| 127 |
+
uvc_error_t ret;
|
| 128 |
+
|
| 129 |
+
ret = libusb_control_transfer(
|
| 130 |
+
devh->usb_devh,
|
| 131 |
+
REQ_TYPE_GET, req_code,
|
| 132 |
+
UVC_VC_VIDEO_POWER_MODE_CONTROL << 8,
|
| 133 |
+
devh->info->ctrl_if.bInterfaceNumber, // XXX saki
|
| 134 |
+
&mode_char,
|
| 135 |
+
sizeof(mode_char),
|
| 136 |
+
0);
|
| 137 |
+
|
| 138 |
+
if (ret == 1) {
|
| 139 |
+
*mode = mode_char;
|
| 140 |
+
return UVC_SUCCESS;
|
| 141 |
+
} else {
|
| 142 |
+
return ret;
|
| 143 |
+
}
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
uvc_error_t uvc_set_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode mode) {
|
| 147 |
+
uint8_t mode_char = mode;
|
| 148 |
+
uvc_error_t ret;
|
| 149 |
+
|
| 150 |
+
ret = libusb_control_transfer(
|
| 151 |
+
devh->usb_devh,
|
| 152 |
+
REQ_TYPE_SET, UVC_SET_CUR,
|
| 153 |
+
UVC_VC_VIDEO_POWER_MODE_CONTROL << 8,
|
| 154 |
+
devh->info->ctrl_if.bInterfaceNumber, // XXX saki
|
| 155 |
+
&mode_char,
|
| 156 |
+
sizeof(mode_char),
|
| 157 |
+
0);
|
| 158 |
+
|
| 159 |
+
if (ret == 1)
|
| 160 |
+
return UVC_SUCCESS;
|
| 161 |
+
else
|
| 162 |
+
return ret;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
/** @todo Request Error Code Control (UVC 1.5, 4.2.1.2) */
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/device.c
ADDED
|
@@ -0,0 +1,2005 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
/**
|
| 35 |
+
* @defgroup device Device handling and enumeration
|
| 36 |
+
* @brief Support for finding, inspecting and opening UVC devices
|
| 37 |
+
*/
|
| 38 |
+
|
| 39 |
+
#include "libuvc/libuvc.h"
|
| 40 |
+
#include "libuvc/libuvc_internal.h"
|
| 41 |
+
|
| 42 |
+
int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev);
|
| 43 |
+
void uvc_free_devh(uvc_device_handle_t *devh);
|
| 44 |
+
|
| 45 |
+
uvc_error_t uvc_get_device_info(uvc_device_handle_t *devh, uvc_device_info_t **info);
|
| 46 |
+
void uvc_free_device_info(uvc_device_info_t *info);
|
| 47 |
+
|
| 48 |
+
uvc_error_t uvc_scan_control(uvc_device_handle_t *devh, uvc_device_info_t *info);
|
| 49 |
+
uvc_error_t uvc_parse_vc(uvc_device_t *dev,
|
| 50 |
+
uvc_device_info_t *info,
|
| 51 |
+
const unsigned char *block, size_t block_size);
|
| 52 |
+
uvc_error_t uvc_parse_vc_selector_unit(uvc_device_t *dev,
|
| 53 |
+
uvc_device_info_t *info,
|
| 54 |
+
const unsigned char *block, size_t block_size);
|
| 55 |
+
uvc_error_t uvc_parse_vc_extension_unit(uvc_device_t *dev,
|
| 56 |
+
uvc_device_info_t *info,
|
| 57 |
+
const unsigned char *block,
|
| 58 |
+
size_t block_size);
|
| 59 |
+
uvc_error_t uvc_parse_vc_header(uvc_device_t *dev,
|
| 60 |
+
uvc_device_info_t *info,
|
| 61 |
+
const unsigned char *block, size_t block_size);
|
| 62 |
+
uvc_error_t uvc_parse_vc_input_terminal(uvc_device_t *dev,
|
| 63 |
+
uvc_device_info_t *info,
|
| 64 |
+
const unsigned char *block,
|
| 65 |
+
size_t block_size);
|
| 66 |
+
uvc_error_t uvc_parse_vc_processing_unit(uvc_device_t *dev,
|
| 67 |
+
uvc_device_info_t *info,
|
| 68 |
+
const unsigned char *block,
|
| 69 |
+
size_t block_size);
|
| 70 |
+
|
| 71 |
+
uvc_error_t uvc_scan_streaming(uvc_device_t *dev,
|
| 72 |
+
uvc_device_info_t *info,
|
| 73 |
+
int interface_idx);
|
| 74 |
+
uvc_error_t uvc_parse_vs(uvc_device_t *dev,
|
| 75 |
+
uvc_device_info_t *info,
|
| 76 |
+
uvc_streaming_interface_t *stream_if,
|
| 77 |
+
const unsigned char *block, size_t block_size);
|
| 78 |
+
uvc_error_t uvc_parse_vs_format_uncompressed(uvc_streaming_interface_t *stream_if,
|
| 79 |
+
const unsigned char *block,
|
| 80 |
+
size_t block_size);
|
| 81 |
+
uvc_error_t uvc_parse_vs_format_mjpeg(uvc_streaming_interface_t *stream_if,
|
| 82 |
+
const unsigned char *block,
|
| 83 |
+
size_t block_size);
|
| 84 |
+
uvc_error_t uvc_parse_vs_frame_uncompressed(uvc_streaming_interface_t *stream_if,
|
| 85 |
+
const unsigned char *block,
|
| 86 |
+
size_t block_size);
|
| 87 |
+
uvc_error_t uvc_parse_vs_frame_format(uvc_streaming_interface_t *stream_if,
|
| 88 |
+
const unsigned char *block,
|
| 89 |
+
size_t block_size);
|
| 90 |
+
uvc_error_t uvc_parse_vs_frame_frame(uvc_streaming_interface_t *stream_if,
|
| 91 |
+
const unsigned char *block,
|
| 92 |
+
size_t block_size);
|
| 93 |
+
uvc_error_t uvc_parse_vs_input_header(uvc_streaming_interface_t *stream_if,
|
| 94 |
+
const unsigned char *block,
|
| 95 |
+
size_t block_size);
|
| 96 |
+
|
| 97 |
+
void LIBUSB_CALL _uvc_status_callback(struct libusb_transfer *transfer);
|
| 98 |
+
|
| 99 |
+
/** @internal
|
| 100 |
+
* @brief Test whether the specified USB device has been opened as a UVC device
|
| 101 |
+
* @ingroup device
|
| 102 |
+
*
|
| 103 |
+
* @param ctx Context in which to search for the UVC device
|
| 104 |
+
* @param usb_dev USB device to find
|
| 105 |
+
* @return true if the device is open in this context
|
| 106 |
+
*/
|
| 107 |
+
int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev) {
|
| 108 |
+
uvc_device_handle_t *devh;
|
| 109 |
+
|
| 110 |
+
DL_FOREACH(ctx->open_devices, devh) {
|
| 111 |
+
if (usb_dev == devh->dev->usb_dev)
|
| 112 |
+
return 1;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
return 0;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/** @brief Finds a camera identified by vendor, product and/or serial number
|
| 119 |
+
* @ingroup device
|
| 120 |
+
*
|
| 121 |
+
* @param[in] ctx UVC context in which to search for the camera
|
| 122 |
+
* @param[out] dev Reference to the camera, or NULL if not found
|
| 123 |
+
* @param[in] vid Vendor ID number, optional
|
| 124 |
+
* @param[in] pid Product ID number, optional
|
| 125 |
+
* @param[in] sn Serial number or NULL
|
| 126 |
+
* @return Error finding device or UVC_SUCCESS
|
| 127 |
+
*/
|
| 128 |
+
uvc_error_t uvc_find_device(
|
| 129 |
+
uvc_context_t *ctx, uvc_device_t **dev,
|
| 130 |
+
int vid, int pid, const char *sn) {
|
| 131 |
+
uvc_error_t ret = UVC_SUCCESS;
|
| 132 |
+
|
| 133 |
+
uvc_device_t **list;
|
| 134 |
+
uvc_device_t *test_dev;
|
| 135 |
+
int dev_idx;
|
| 136 |
+
int found_dev;
|
| 137 |
+
|
| 138 |
+
UVC_ENTER();
|
| 139 |
+
|
| 140 |
+
ret = uvc_get_device_list(ctx, &list);
|
| 141 |
+
|
| 142 |
+
if (ret != UVC_SUCCESS) {
|
| 143 |
+
UVC_EXIT(ret);
|
| 144 |
+
return ret;
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
dev_idx = 0;
|
| 148 |
+
found_dev = 0;
|
| 149 |
+
|
| 150 |
+
while (!found_dev && (test_dev = list[dev_idx++]) != NULL) {
|
| 151 |
+
uvc_device_descriptor_t *desc;
|
| 152 |
+
|
| 153 |
+
if (uvc_get_device_descriptor(test_dev, &desc) != UVC_SUCCESS)
|
| 154 |
+
continue;
|
| 155 |
+
|
| 156 |
+
if ((!vid || desc->idVendor == vid)
|
| 157 |
+
&& (!pid || desc->idProduct == pid)
|
| 158 |
+
&& (!sn || (desc->serialNumber && !strcmp(desc->serialNumber, sn))))
|
| 159 |
+
found_dev = 1;
|
| 160 |
+
|
| 161 |
+
uvc_free_device_descriptor(desc);
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
if (found_dev)
|
| 165 |
+
uvc_ref_device(test_dev);
|
| 166 |
+
|
| 167 |
+
uvc_free_device_list(list, 1);
|
| 168 |
+
|
| 169 |
+
if (found_dev) {
|
| 170 |
+
*dev = test_dev;
|
| 171 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 172 |
+
return UVC_SUCCESS;
|
| 173 |
+
} else {
|
| 174 |
+
UVC_EXIT(UVC_ERROR_NO_DEVICE);
|
| 175 |
+
return UVC_ERROR_NO_DEVICE;
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/** @brief Finds all cameras identified by vendor, product and/or serial number
|
| 180 |
+
* @ingroup device
|
| 181 |
+
*
|
| 182 |
+
* @param[in] ctx UVC context in which to search for the camera
|
| 183 |
+
* @param[out] devs List of matching cameras
|
| 184 |
+
* @param[in] vid Vendor ID number, optional
|
| 185 |
+
* @param[in] pid Product ID number, optional
|
| 186 |
+
* @param[in] sn Serial number or NULL
|
| 187 |
+
* @return Error finding device or UVC_SUCCESS
|
| 188 |
+
*/
|
| 189 |
+
uvc_error_t uvc_find_devices(
|
| 190 |
+
uvc_context_t *ctx, uvc_device_t ***devs,
|
| 191 |
+
int vid, int pid, const char *sn) {
|
| 192 |
+
uvc_error_t ret = UVC_SUCCESS;
|
| 193 |
+
|
| 194 |
+
uvc_device_t **list;
|
| 195 |
+
uvc_device_t *test_dev;
|
| 196 |
+
int dev_idx;
|
| 197 |
+
int found_dev;
|
| 198 |
+
|
| 199 |
+
uvc_device_t **list_internal;
|
| 200 |
+
int num_uvc_devices;
|
| 201 |
+
|
| 202 |
+
UVC_ENTER();
|
| 203 |
+
|
| 204 |
+
ret = uvc_get_device_list(ctx, &list);
|
| 205 |
+
|
| 206 |
+
if (ret != UVC_SUCCESS) {
|
| 207 |
+
UVC_EXIT(ret);
|
| 208 |
+
return ret;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
num_uvc_devices = 0;
|
| 212 |
+
dev_idx = 0;
|
| 213 |
+
found_dev = 0;
|
| 214 |
+
|
| 215 |
+
list_internal = malloc(sizeof(*list_internal));
|
| 216 |
+
*list_internal = NULL;
|
| 217 |
+
|
| 218 |
+
while ((test_dev = list[dev_idx++]) != NULL) {
|
| 219 |
+
uvc_device_descriptor_t *desc;
|
| 220 |
+
|
| 221 |
+
if (uvc_get_device_descriptor(test_dev, &desc) != UVC_SUCCESS)
|
| 222 |
+
continue;
|
| 223 |
+
|
| 224 |
+
if ((!vid || desc->idVendor == vid)
|
| 225 |
+
&& (!pid || desc->idProduct == pid)
|
| 226 |
+
&& (!sn || (desc->serialNumber && !strcmp(desc->serialNumber, sn)))) {
|
| 227 |
+
found_dev = 1;
|
| 228 |
+
uvc_ref_device(test_dev);
|
| 229 |
+
|
| 230 |
+
num_uvc_devices++;
|
| 231 |
+
list_internal = realloc(list_internal, (num_uvc_devices + 1) * sizeof(*list_internal));
|
| 232 |
+
|
| 233 |
+
list_internal[num_uvc_devices - 1] = test_dev;
|
| 234 |
+
list_internal[num_uvc_devices] = NULL;
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
uvc_free_device_descriptor(desc);
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
uvc_free_device_list(list, 1);
|
| 241 |
+
|
| 242 |
+
if (found_dev) {
|
| 243 |
+
*devs = list_internal;
|
| 244 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 245 |
+
return UVC_SUCCESS;
|
| 246 |
+
} else {
|
| 247 |
+
UVC_EXIT(UVC_ERROR_NO_DEVICE);
|
| 248 |
+
return UVC_ERROR_NO_DEVICE;
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
/** @brief Get the number of the bus to which the device is attached
|
| 253 |
+
* @ingroup device
|
| 254 |
+
*/
|
| 255 |
+
uint8_t uvc_get_bus_number(uvc_device_t *dev) {
|
| 256 |
+
return libusb_get_bus_number(dev->usb_dev);
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
/** @brief Get the number assigned to the device within its bus
|
| 260 |
+
* @ingroup device
|
| 261 |
+
*/
|
| 262 |
+
uint8_t uvc_get_device_address(uvc_device_t *dev) {
|
| 263 |
+
return libusb_get_device_address(dev->usb_dev);
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
int uvc_get_port_numbers(uvc_device_t *dev, uint8_t *ports,
|
| 267 |
+
uint8_t port_count) {
|
| 268 |
+
if (dev == NULL || ports == NULL || port_count == 0)
|
| 269 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 270 |
+
return libusb_get_port_numbers(dev->usb_dev, ports, port_count);
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
static uvc_error_t uvc_open_internal(uvc_device_t *dev, struct libusb_device_handle *usb_devh, uvc_device_handle_t **devh);
|
| 274 |
+
|
| 275 |
+
#if LIBUSB_API_VERSION >= 0x01000107
|
| 276 |
+
/** @brief Wrap a platform-specific system device handle and obtain a UVC device handle.
|
| 277 |
+
* The handle allows you to use libusb to perform I/O on the device in question.
|
| 278 |
+
*
|
| 279 |
+
* On Linux, the system device handle must be a valid file descriptor opened on the device node.
|
| 280 |
+
*
|
| 281 |
+
* The system device handle must remain open until uvc_close() is called. The system device handle will not be closed by uvc_close().
|
| 282 |
+
* @ingroup device
|
| 283 |
+
*
|
| 284 |
+
* @param sys_dev the platform-specific system device handle
|
| 285 |
+
* @param context UVC context to prepare the device
|
| 286 |
+
* @param[out] devh Handle on opened device
|
| 287 |
+
* @return Error opening device or SUCCESS
|
| 288 |
+
*/
|
| 289 |
+
uvc_error_t uvc_wrap(
|
| 290 |
+
int sys_dev,
|
| 291 |
+
uvc_context_t *context,
|
| 292 |
+
uvc_device_handle_t **devh) {
|
| 293 |
+
uvc_error_t ret;
|
| 294 |
+
struct libusb_device_handle *usb_devh;
|
| 295 |
+
|
| 296 |
+
UVC_ENTER();
|
| 297 |
+
|
| 298 |
+
uvc_device_t *dev = NULL;
|
| 299 |
+
int err = libusb_wrap_sys_device(context->usb_ctx, sys_dev, &usb_devh);
|
| 300 |
+
UVC_DEBUG("libusb_wrap_sys_device() = %d", err);
|
| 301 |
+
if (err != LIBUSB_SUCCESS) {
|
| 302 |
+
UVC_EXIT(err);
|
| 303 |
+
return err;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
dev = calloc(1, sizeof(uvc_device_t));
|
| 307 |
+
dev->ctx = context;
|
| 308 |
+
dev->usb_dev = libusb_get_device(usb_devh);
|
| 309 |
+
|
| 310 |
+
ret = uvc_open_internal(dev, usb_devh, devh);
|
| 311 |
+
UVC_EXIT(ret);
|
| 312 |
+
return ret;
|
| 313 |
+
}
|
| 314 |
+
#endif
|
| 315 |
+
|
| 316 |
+
/** @brief Open a UVC device
|
| 317 |
+
* @ingroup device
|
| 318 |
+
*
|
| 319 |
+
* @param dev Device to open
|
| 320 |
+
* @param[out] devh Handle on opened device
|
| 321 |
+
* @return Error opening device or SUCCESS
|
| 322 |
+
*/
|
| 323 |
+
uvc_error_t uvc_open(
|
| 324 |
+
uvc_device_t *dev,
|
| 325 |
+
uvc_device_handle_t **devh) {
|
| 326 |
+
uvc_error_t ret;
|
| 327 |
+
struct libusb_device_handle *usb_devh;
|
| 328 |
+
|
| 329 |
+
UVC_ENTER();
|
| 330 |
+
|
| 331 |
+
ret = libusb_open(dev->usb_dev, &usb_devh);
|
| 332 |
+
UVC_DEBUG("libusb_open() = %d", ret);
|
| 333 |
+
|
| 334 |
+
if (ret != UVC_SUCCESS) {
|
| 335 |
+
UVC_EXIT(ret);
|
| 336 |
+
return ret;
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
ret = uvc_open_internal(dev, usb_devh, devh);
|
| 340 |
+
UVC_EXIT(ret);
|
| 341 |
+
return ret;
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
static uvc_error_t uvc_open_internal(
|
| 345 |
+
uvc_device_t *dev,
|
| 346 |
+
struct libusb_device_handle *usb_devh,
|
| 347 |
+
uvc_device_handle_t **devh) {
|
| 348 |
+
uvc_error_t ret;
|
| 349 |
+
uvc_device_handle_t *internal_devh;
|
| 350 |
+
struct libusb_device_descriptor desc;
|
| 351 |
+
|
| 352 |
+
UVC_ENTER();
|
| 353 |
+
|
| 354 |
+
uvc_ref_device(dev);
|
| 355 |
+
|
| 356 |
+
internal_devh = calloc(1, sizeof(*internal_devh));
|
| 357 |
+
internal_devh->dev = dev;
|
| 358 |
+
internal_devh->usb_devh = usb_devh;
|
| 359 |
+
|
| 360 |
+
ret = uvc_get_device_info(internal_devh, &(internal_devh->info));
|
| 361 |
+
|
| 362 |
+
if (ret != UVC_SUCCESS)
|
| 363 |
+
goto fail;
|
| 364 |
+
|
| 365 |
+
UVC_DEBUG("claiming control interface %d", internal_devh->info->ctrl_if.bInterfaceNumber);
|
| 366 |
+
ret = uvc_claim_if(internal_devh, internal_devh->info->ctrl_if.bInterfaceNumber);
|
| 367 |
+
if (ret != UVC_SUCCESS)
|
| 368 |
+
goto fail;
|
| 369 |
+
|
| 370 |
+
libusb_get_device_descriptor(dev->usb_dev, &desc);
|
| 371 |
+
internal_devh->is_isight = (desc.idVendor == 0x05ac && desc.idProduct == 0x8501);
|
| 372 |
+
|
| 373 |
+
if (internal_devh->info->ctrl_if.bEndpointAddress) {
|
| 374 |
+
internal_devh->status_xfer = libusb_alloc_transfer(0);
|
| 375 |
+
if (!internal_devh->status_xfer) {
|
| 376 |
+
ret = UVC_ERROR_NO_MEM;
|
| 377 |
+
goto fail;
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
libusb_fill_interrupt_transfer(internal_devh->status_xfer,
|
| 381 |
+
usb_devh,
|
| 382 |
+
internal_devh->info->ctrl_if.bEndpointAddress,
|
| 383 |
+
internal_devh->status_buf,
|
| 384 |
+
sizeof(internal_devh->status_buf),
|
| 385 |
+
_uvc_status_callback,
|
| 386 |
+
internal_devh,
|
| 387 |
+
0);
|
| 388 |
+
ret = libusb_submit_transfer(internal_devh->status_xfer);
|
| 389 |
+
UVC_DEBUG("libusb_submit_transfer() = %d", ret);
|
| 390 |
+
|
| 391 |
+
if (ret) {
|
| 392 |
+
fprintf(stderr,
|
| 393 |
+
"uvc: device has a status interrupt endpoint, but unable to read from it\n");
|
| 394 |
+
goto fail;
|
| 395 |
+
}
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
if (dev->ctx->own_usb_ctx && dev->ctx->open_devices == NULL) {
|
| 399 |
+
/* Since this is our first device, we need to spawn the event handler thread */
|
| 400 |
+
uvc_start_handler_thread(dev->ctx);
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
DL_APPEND(dev->ctx->open_devices, internal_devh);
|
| 404 |
+
*devh = internal_devh;
|
| 405 |
+
|
| 406 |
+
UVC_EXIT(ret);
|
| 407 |
+
|
| 408 |
+
return ret;
|
| 409 |
+
|
| 410 |
+
fail:
|
| 411 |
+
if ( internal_devh->info ) {
|
| 412 |
+
uvc_release_if(internal_devh, internal_devh->info->ctrl_if.bInterfaceNumber);
|
| 413 |
+
}
|
| 414 |
+
libusb_close(usb_devh);
|
| 415 |
+
uvc_unref_device(dev);
|
| 416 |
+
uvc_free_devh(internal_devh);
|
| 417 |
+
|
| 418 |
+
UVC_EXIT(ret);
|
| 419 |
+
|
| 420 |
+
return ret;
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
/**
|
| 424 |
+
* @internal
|
| 425 |
+
* @brief Parses the complete device descriptor for a device
|
| 426 |
+
* @ingroup device
|
| 427 |
+
* @note Free *info with uvc_free_device_info when you're done
|
| 428 |
+
*
|
| 429 |
+
* @param dev Device to parse descriptor for
|
| 430 |
+
* @param info Where to store a pointer to the new info struct
|
| 431 |
+
*/
|
| 432 |
+
uvc_error_t uvc_get_device_info(uvc_device_handle_t *devh,
|
| 433 |
+
uvc_device_info_t **info) {
|
| 434 |
+
uvc_error_t ret;
|
| 435 |
+
uvc_device_info_t *internal_info;
|
| 436 |
+
|
| 437 |
+
UVC_ENTER();
|
| 438 |
+
|
| 439 |
+
internal_info = calloc(1, sizeof(*internal_info));
|
| 440 |
+
if (!internal_info) {
|
| 441 |
+
UVC_EXIT(UVC_ERROR_NO_MEM);
|
| 442 |
+
return UVC_ERROR_NO_MEM;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
if (libusb_get_config_descriptor(devh->dev->usb_dev,
|
| 446 |
+
0,
|
| 447 |
+
&(internal_info->config)) != 0) {
|
| 448 |
+
free(internal_info);
|
| 449 |
+
UVC_EXIT(UVC_ERROR_IO);
|
| 450 |
+
return UVC_ERROR_IO;
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
ret = uvc_scan_control(devh, internal_info);
|
| 454 |
+
if (ret != UVC_SUCCESS) {
|
| 455 |
+
uvc_free_device_info(internal_info);
|
| 456 |
+
UVC_EXIT(ret);
|
| 457 |
+
return ret;
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
*info = internal_info;
|
| 461 |
+
|
| 462 |
+
UVC_EXIT(ret);
|
| 463 |
+
return ret;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
/**
|
| 467 |
+
* @internal
|
| 468 |
+
* @brief Frees the device descriptor for a device
|
| 469 |
+
* @ingroup device
|
| 470 |
+
*
|
| 471 |
+
* @param info Which device info block to free
|
| 472 |
+
*/
|
| 473 |
+
void uvc_free_device_info(uvc_device_info_t *info) {
|
| 474 |
+
uvc_input_terminal_t *input_term, *input_term_tmp;
|
| 475 |
+
uvc_processing_unit_t *proc_unit, *proc_unit_tmp;
|
| 476 |
+
uvc_extension_unit_t *ext_unit, *ext_unit_tmp;
|
| 477 |
+
|
| 478 |
+
uvc_streaming_interface_t *stream_if, *stream_if_tmp;
|
| 479 |
+
uvc_format_desc_t *format, *format_tmp;
|
| 480 |
+
uvc_frame_desc_t *frame, *frame_tmp;
|
| 481 |
+
uvc_still_frame_desc_t *still_frame, *still_frame_tmp;
|
| 482 |
+
uvc_still_frame_res_t *still_res, *still_res_tmp;
|
| 483 |
+
|
| 484 |
+
UVC_ENTER();
|
| 485 |
+
|
| 486 |
+
DL_FOREACH_SAFE(info->ctrl_if.input_term_descs, input_term, input_term_tmp) {
|
| 487 |
+
DL_DELETE(info->ctrl_if.input_term_descs, input_term);
|
| 488 |
+
free(input_term);
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
DL_FOREACH_SAFE(info->ctrl_if.processing_unit_descs, proc_unit, proc_unit_tmp) {
|
| 492 |
+
DL_DELETE(info->ctrl_if.processing_unit_descs, proc_unit);
|
| 493 |
+
free(proc_unit);
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
DL_FOREACH_SAFE(info->ctrl_if.extension_unit_descs, ext_unit, ext_unit_tmp) {
|
| 497 |
+
DL_DELETE(info->ctrl_if.extension_unit_descs, ext_unit);
|
| 498 |
+
free(ext_unit);
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
DL_FOREACH_SAFE(info->stream_ifs, stream_if, stream_if_tmp) {
|
| 502 |
+
DL_FOREACH_SAFE(stream_if->format_descs, format, format_tmp) {
|
| 503 |
+
DL_FOREACH_SAFE(format->frame_descs, frame, frame_tmp) {
|
| 504 |
+
if (frame->intervals)
|
| 505 |
+
free(frame->intervals);
|
| 506 |
+
|
| 507 |
+
DL_DELETE(format->frame_descs, frame);
|
| 508 |
+
free(frame);
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
if(format->still_frame_desc) {
|
| 512 |
+
DL_FOREACH_SAFE(format->still_frame_desc, still_frame, still_frame_tmp) {
|
| 513 |
+
DL_FOREACH_SAFE(still_frame->imageSizePatterns, still_res, still_res_tmp) {
|
| 514 |
+
free(still_res);
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
if(still_frame->bCompression) {
|
| 518 |
+
free(still_frame->bCompression);
|
| 519 |
+
}
|
| 520 |
+
free(still_frame);
|
| 521 |
+
}
|
| 522 |
+
}
|
| 523 |
+
|
| 524 |
+
DL_DELETE(stream_if->format_descs, format);
|
| 525 |
+
free(format);
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
DL_DELETE(info->stream_ifs, stream_if);
|
| 529 |
+
free(stream_if);
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
if (info->config)
|
| 533 |
+
libusb_free_config_descriptor(info->config);
|
| 534 |
+
|
| 535 |
+
free(info);
|
| 536 |
+
|
| 537 |
+
UVC_EXIT_VOID();
|
| 538 |
+
}
|
| 539 |
+
|
| 540 |
+
static uvc_error_t get_device_descriptor(
|
| 541 |
+
uvc_device_handle_t *devh,
|
| 542 |
+
uvc_device_descriptor_t **desc) {
|
| 543 |
+
uvc_device_descriptor_t *desc_internal;
|
| 544 |
+
struct libusb_device_descriptor usb_desc;
|
| 545 |
+
struct libusb_device_handle *usb_devh = devh->usb_devh;
|
| 546 |
+
uvc_error_t ret;
|
| 547 |
+
|
| 548 |
+
UVC_ENTER();
|
| 549 |
+
|
| 550 |
+
ret = libusb_get_device_descriptor(devh->dev->usb_dev, &usb_desc);
|
| 551 |
+
|
| 552 |
+
if (ret != UVC_SUCCESS) {
|
| 553 |
+
UVC_EXIT(ret);
|
| 554 |
+
return ret;
|
| 555 |
+
}
|
| 556 |
+
|
| 557 |
+
desc_internal = calloc(1, sizeof(*desc_internal));
|
| 558 |
+
desc_internal->idVendor = usb_desc.idVendor;
|
| 559 |
+
desc_internal->idProduct = usb_desc.idProduct;
|
| 560 |
+
|
| 561 |
+
unsigned char buf[64];
|
| 562 |
+
|
| 563 |
+
int bytes = libusb_get_string_descriptor_ascii(
|
| 564 |
+
usb_devh, usb_desc.iSerialNumber, buf, sizeof(buf));
|
| 565 |
+
|
| 566 |
+
if (bytes > 0)
|
| 567 |
+
desc_internal->serialNumber = strdup((const char*) buf);
|
| 568 |
+
|
| 569 |
+
bytes = libusb_get_string_descriptor_ascii(
|
| 570 |
+
usb_devh, usb_desc.iManufacturer, buf, sizeof(buf));
|
| 571 |
+
|
| 572 |
+
if (bytes > 0)
|
| 573 |
+
desc_internal->manufacturer = strdup((const char*) buf);
|
| 574 |
+
|
| 575 |
+
bytes = libusb_get_string_descriptor_ascii(
|
| 576 |
+
usb_devh, usb_desc.iProduct, buf, sizeof(buf));
|
| 577 |
+
|
| 578 |
+
if (bytes > 0)
|
| 579 |
+
desc_internal->product = strdup((const char*) buf);
|
| 580 |
+
|
| 581 |
+
*desc = desc_internal;
|
| 582 |
+
|
| 583 |
+
UVC_EXIT(ret);
|
| 584 |
+
return ret;
|
| 585 |
+
}
|
| 586 |
+
|
| 587 |
+
/**
|
| 588 |
+
* @brief Get a descriptor that contains the general information about
|
| 589 |
+
* a device
|
| 590 |
+
* @ingroup device
|
| 591 |
+
*
|
| 592 |
+
* Free *desc with uvc_free_device_descriptor when you're done.
|
| 593 |
+
*
|
| 594 |
+
* @param dev Device to fetch information about
|
| 595 |
+
* @param[out] desc Descriptor structure
|
| 596 |
+
* @return Error if unable to fetch information, else SUCCESS
|
| 597 |
+
*/
|
| 598 |
+
uvc_error_t uvc_get_device_descriptor(
|
| 599 |
+
uvc_device_t *dev,
|
| 600 |
+
uvc_device_descriptor_t **desc) {
|
| 601 |
+
uvc_device_descriptor_t *desc_internal;
|
| 602 |
+
struct libusb_device_descriptor usb_desc;
|
| 603 |
+
struct libusb_device_handle *usb_devh;
|
| 604 |
+
uvc_error_t ret;
|
| 605 |
+
|
| 606 |
+
UVC_ENTER();
|
| 607 |
+
|
| 608 |
+
ret = libusb_get_device_descriptor(dev->usb_dev, &usb_desc);
|
| 609 |
+
|
| 610 |
+
if (ret != UVC_SUCCESS) {
|
| 611 |
+
UVC_EXIT(ret);
|
| 612 |
+
return ret;
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
desc_internal = calloc(1, sizeof(*desc_internal));
|
| 616 |
+
desc_internal->idVendor = usb_desc.idVendor;
|
| 617 |
+
desc_internal->idProduct = usb_desc.idProduct;
|
| 618 |
+
|
| 619 |
+
if (libusb_open(dev->usb_dev, &usb_devh) == 0) {
|
| 620 |
+
unsigned char buf[64];
|
| 621 |
+
|
| 622 |
+
int bytes = libusb_get_string_descriptor_ascii(
|
| 623 |
+
usb_devh, usb_desc.iSerialNumber, buf, sizeof(buf));
|
| 624 |
+
|
| 625 |
+
if (bytes > 0)
|
| 626 |
+
desc_internal->serialNumber = strdup((const char*) buf);
|
| 627 |
+
|
| 628 |
+
bytes = libusb_get_string_descriptor_ascii(
|
| 629 |
+
usb_devh, usb_desc.iManufacturer, buf, sizeof(buf));
|
| 630 |
+
|
| 631 |
+
if (bytes > 0)
|
| 632 |
+
desc_internal->manufacturer = strdup((const char*) buf);
|
| 633 |
+
|
| 634 |
+
bytes = libusb_get_string_descriptor_ascii(
|
| 635 |
+
usb_devh, usb_desc.iProduct, buf, sizeof(buf));
|
| 636 |
+
|
| 637 |
+
if (bytes > 0)
|
| 638 |
+
desc_internal->product = strdup((const char*) buf);
|
| 639 |
+
|
| 640 |
+
libusb_close(usb_devh);
|
| 641 |
+
} else {
|
| 642 |
+
UVC_DEBUG("can't open device %04x:%04x, not fetching serial etc.",
|
| 643 |
+
usb_desc.idVendor, usb_desc.idProduct);
|
| 644 |
+
}
|
| 645 |
+
|
| 646 |
+
*desc = desc_internal;
|
| 647 |
+
|
| 648 |
+
UVC_EXIT(ret);
|
| 649 |
+
return ret;
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
/**
|
| 653 |
+
* @brief Frees a device descriptor created with uvc_get_device_descriptor
|
| 654 |
+
* @ingroup device
|
| 655 |
+
*
|
| 656 |
+
* @param desc Descriptor to free
|
| 657 |
+
*/
|
| 658 |
+
void uvc_free_device_descriptor(
|
| 659 |
+
uvc_device_descriptor_t *desc) {
|
| 660 |
+
UVC_ENTER();
|
| 661 |
+
|
| 662 |
+
if (desc->serialNumber)
|
| 663 |
+
free((void*) desc->serialNumber);
|
| 664 |
+
|
| 665 |
+
if (desc->manufacturer)
|
| 666 |
+
free((void*) desc->manufacturer);
|
| 667 |
+
|
| 668 |
+
if (desc->product)
|
| 669 |
+
free((void*) desc->product);
|
| 670 |
+
|
| 671 |
+
free(desc);
|
| 672 |
+
|
| 673 |
+
UVC_EXIT_VOID();
|
| 674 |
+
}
|
| 675 |
+
|
| 676 |
+
/**
|
| 677 |
+
* @brief Get a list of the UVC devices attached to the system
|
| 678 |
+
* @ingroup device
|
| 679 |
+
*
|
| 680 |
+
* @note Free the list with uvc_free_device_list when you're done.
|
| 681 |
+
*
|
| 682 |
+
* @param ctx UVC context in which to list devices
|
| 683 |
+
* @param list List of uvc_device structures
|
| 684 |
+
* @return Error if unable to list devices, else SUCCESS
|
| 685 |
+
*/
|
| 686 |
+
uvc_error_t uvc_get_device_list(
|
| 687 |
+
uvc_context_t *ctx,
|
| 688 |
+
uvc_device_t ***list) {
|
| 689 |
+
struct libusb_device **usb_dev_list;
|
| 690 |
+
struct libusb_device *usb_dev;
|
| 691 |
+
int num_usb_devices;
|
| 692 |
+
|
| 693 |
+
uvc_device_t **list_internal;
|
| 694 |
+
int num_uvc_devices;
|
| 695 |
+
|
| 696 |
+
/* per device */
|
| 697 |
+
int dev_idx;
|
| 698 |
+
struct libusb_config_descriptor *config;
|
| 699 |
+
struct libusb_device_descriptor desc;
|
| 700 |
+
uint8_t got_interface;
|
| 701 |
+
|
| 702 |
+
/* per interface */
|
| 703 |
+
int interface_idx;
|
| 704 |
+
const struct libusb_interface *interface;
|
| 705 |
+
|
| 706 |
+
/* per altsetting */
|
| 707 |
+
int altsetting_idx;
|
| 708 |
+
const struct libusb_interface_descriptor *if_desc;
|
| 709 |
+
|
| 710 |
+
UVC_ENTER();
|
| 711 |
+
|
| 712 |
+
num_usb_devices = libusb_get_device_list(ctx->usb_ctx, &usb_dev_list);
|
| 713 |
+
|
| 714 |
+
if (num_usb_devices < 0) {
|
| 715 |
+
UVC_EXIT(UVC_ERROR_IO);
|
| 716 |
+
return UVC_ERROR_IO;
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
list_internal = malloc(sizeof(*list_internal));
|
| 720 |
+
*list_internal = NULL;
|
| 721 |
+
|
| 722 |
+
num_uvc_devices = 0;
|
| 723 |
+
dev_idx = -1;
|
| 724 |
+
|
| 725 |
+
while ((usb_dev = usb_dev_list[++dev_idx]) != NULL) {
|
| 726 |
+
got_interface = 0;
|
| 727 |
+
|
| 728 |
+
if (libusb_get_config_descriptor(usb_dev, 0, &config) != 0)
|
| 729 |
+
continue;
|
| 730 |
+
|
| 731 |
+
if ( libusb_get_device_descriptor ( usb_dev, &desc ) != LIBUSB_SUCCESS )
|
| 732 |
+
continue;
|
| 733 |
+
|
| 734 |
+
for (interface_idx = 0;
|
| 735 |
+
!got_interface && interface_idx < config->bNumInterfaces;
|
| 736 |
+
++interface_idx) {
|
| 737 |
+
interface = &config->interface[interface_idx];
|
| 738 |
+
|
| 739 |
+
for (altsetting_idx = 0;
|
| 740 |
+
!got_interface && altsetting_idx < interface->num_altsetting;
|
| 741 |
+
++altsetting_idx) {
|
| 742 |
+
if_desc = &interface->altsetting[altsetting_idx];
|
| 743 |
+
|
| 744 |
+
// Skip TIS cameras that definitely aren't UVC even though they might
|
| 745 |
+
// look that way
|
| 746 |
+
|
| 747 |
+
if ( 0x199e == desc.idVendor && desc.idProduct >= 0x8201 &&
|
| 748 |
+
desc.idProduct <= 0x8208 ) {
|
| 749 |
+
continue;
|
| 750 |
+
}
|
| 751 |
+
|
| 752 |
+
// Special case for Imaging Source cameras
|
| 753 |
+
/* Video, Streaming */
|
| 754 |
+
if ( 0x199e == desc.idVendor && ( 0x8101 == desc.idProduct ||
|
| 755 |
+
0x8102 == desc.idProduct ) &&
|
| 756 |
+
if_desc->bInterfaceClass == 255 &&
|
| 757 |
+
if_desc->bInterfaceSubClass == 2 ) {
|
| 758 |
+
got_interface = 1;
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
/* Video, Streaming */
|
| 762 |
+
if (if_desc->bInterfaceClass == 14 && if_desc->bInterfaceSubClass == 2) {
|
| 763 |
+
got_interface = 1;
|
| 764 |
+
}
|
| 765 |
+
}
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
libusb_free_config_descriptor(config);
|
| 769 |
+
|
| 770 |
+
if (got_interface) {
|
| 771 |
+
uvc_device_t *uvc_dev = malloc(sizeof(*uvc_dev));
|
| 772 |
+
uvc_dev->ctx = ctx;
|
| 773 |
+
uvc_dev->ref = 0;
|
| 774 |
+
uvc_dev->usb_dev = usb_dev;
|
| 775 |
+
uvc_ref_device(uvc_dev);
|
| 776 |
+
|
| 777 |
+
num_uvc_devices++;
|
| 778 |
+
list_internal = realloc(list_internal, (num_uvc_devices + 1) * sizeof(*list_internal));
|
| 779 |
+
|
| 780 |
+
list_internal[num_uvc_devices - 1] = uvc_dev;
|
| 781 |
+
list_internal[num_uvc_devices] = NULL;
|
| 782 |
+
|
| 783 |
+
UVC_DEBUG(" UVC: %d", dev_idx);
|
| 784 |
+
} else {
|
| 785 |
+
UVC_DEBUG("non-UVC: %d", dev_idx);
|
| 786 |
+
}
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
libusb_free_device_list(usb_dev_list, 1);
|
| 790 |
+
|
| 791 |
+
*list = list_internal;
|
| 792 |
+
|
| 793 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 794 |
+
return UVC_SUCCESS;
|
| 795 |
+
}
|
| 796 |
+
|
| 797 |
+
/**
|
| 798 |
+
* @brief Frees a list of device structures created with uvc_get_device_list.
|
| 799 |
+
* @ingroup device
|
| 800 |
+
*
|
| 801 |
+
* @param list Device list to free
|
| 802 |
+
* @param unref_devices Decrement the reference counter for each device
|
| 803 |
+
* in the list, and destroy any entries that end up with zero references
|
| 804 |
+
*/
|
| 805 |
+
void uvc_free_device_list(uvc_device_t **list, uint8_t unref_devices) {
|
| 806 |
+
uvc_device_t *dev;
|
| 807 |
+
int dev_idx = 0;
|
| 808 |
+
|
| 809 |
+
UVC_ENTER();
|
| 810 |
+
|
| 811 |
+
if (unref_devices) {
|
| 812 |
+
while ((dev = list[dev_idx++]) != NULL) {
|
| 813 |
+
uvc_unref_device(dev);
|
| 814 |
+
}
|
| 815 |
+
}
|
| 816 |
+
|
| 817 |
+
free(list);
|
| 818 |
+
|
| 819 |
+
UVC_EXIT_VOID();
|
| 820 |
+
}
|
| 821 |
+
|
| 822 |
+
/**
|
| 823 |
+
* @brief Get the uvc_device_t corresponding to an open device
|
| 824 |
+
* @ingroup device
|
| 825 |
+
*
|
| 826 |
+
* @note Unref the uvc_device_t when you're done with it
|
| 827 |
+
*
|
| 828 |
+
* @param devh Device handle to an open UVC device
|
| 829 |
+
*/
|
| 830 |
+
uvc_device_t *uvc_get_device(uvc_device_handle_t *devh) {
|
| 831 |
+
uvc_ref_device(devh->dev);
|
| 832 |
+
return devh->dev;
|
| 833 |
+
}
|
| 834 |
+
|
| 835 |
+
uvc_error_t uvc_set_altsetting_override(
|
| 836 |
+
uvc_device_handle_t *devh,
|
| 837 |
+
int altsetting,
|
| 838 |
+
unsigned int payload) {
|
| 839 |
+
if (devh == NULL) {
|
| 840 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 841 |
+
}
|
| 842 |
+
if (altsetting <= 0) {
|
| 843 |
+
devh->altsetting_override_valid = 0;
|
| 844 |
+
devh->altsetting_override_altsetting = 0;
|
| 845 |
+
devh->altsetting_override_payload = 0;
|
| 846 |
+
return UVC_SUCCESS;
|
| 847 |
+
}
|
| 848 |
+
if (altsetting > 0xff || payload == 0 || payload > 0xffff) {
|
| 849 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 850 |
+
}
|
| 851 |
+
devh->altsetting_override_altsetting = (uint8_t)altsetting;
|
| 852 |
+
devh->altsetting_override_payload = (uint16_t)payload;
|
| 853 |
+
devh->altsetting_override_valid = 1;
|
| 854 |
+
UVC_DEBUG("altsetting override: alt=%d payload=%u", altsetting, payload);
|
| 855 |
+
return UVC_SUCCESS;
|
| 856 |
+
}
|
| 857 |
+
|
| 858 |
+
/**
|
| 859 |
+
* @brief Get the underlying libusb device handle for an open device
|
| 860 |
+
* @ingroup device
|
| 861 |
+
*
|
| 862 |
+
* This can be used to access other interfaces on the same device, e.g.
|
| 863 |
+
* a webcam microphone.
|
| 864 |
+
*
|
| 865 |
+
* @note The libusb device handle is only valid while the UVC device is open;
|
| 866 |
+
* it will be invalidated upon calling uvc_close.
|
| 867 |
+
*
|
| 868 |
+
* @param devh UVC device handle to an open device
|
| 869 |
+
*/
|
| 870 |
+
libusb_device_handle *uvc_get_libusb_handle(uvc_device_handle_t *devh) {
|
| 871 |
+
return devh->usb_devh;
|
| 872 |
+
}
|
| 873 |
+
|
| 874 |
+
/**
|
| 875 |
+
* @brief Get camera terminal descriptor for the open device.
|
| 876 |
+
*
|
| 877 |
+
* @note Do not modify the returned structure.
|
| 878 |
+
* @note The returned structure is part of a linked list, but iterating through
|
| 879 |
+
* it will make it no longer the camera terminal
|
| 880 |
+
*
|
| 881 |
+
* @param devh Device handle to an open UVC device
|
| 882 |
+
*/
|
| 883 |
+
const uvc_input_terminal_t *uvc_get_camera_terminal(uvc_device_handle_t *devh) {
|
| 884 |
+
const uvc_input_terminal_t *term = uvc_get_input_terminals(devh);
|
| 885 |
+
while(term != NULL) {
|
| 886 |
+
if (term->wTerminalType == UVC_ITT_CAMERA) {
|
| 887 |
+
break;
|
| 888 |
+
}
|
| 889 |
+
else {
|
| 890 |
+
term = term->next;
|
| 891 |
+
}
|
| 892 |
+
}
|
| 893 |
+
return term;
|
| 894 |
+
}
|
| 895 |
+
|
| 896 |
+
|
| 897 |
+
/**
|
| 898 |
+
* @brief Get input terminal descriptors for the open device.
|
| 899 |
+
*
|
| 900 |
+
* @note Do not modify the returned structure.
|
| 901 |
+
* @note The returned structure is part of a linked list. Iterate through
|
| 902 |
+
* it by using the 'next' pointers.
|
| 903 |
+
*
|
| 904 |
+
* @param devh Device handle to an open UVC device
|
| 905 |
+
*/
|
| 906 |
+
const uvc_input_terminal_t *uvc_get_input_terminals(uvc_device_handle_t *devh) {
|
| 907 |
+
return devh->info->ctrl_if.input_term_descs;
|
| 908 |
+
}
|
| 909 |
+
|
| 910 |
+
/**
|
| 911 |
+
* @brief Get output terminal descriptors for the open device.
|
| 912 |
+
*
|
| 913 |
+
* @note Do not modify the returned structure.
|
| 914 |
+
* @note The returned structure is part of a linked list. Iterate through
|
| 915 |
+
* it by using the 'next' pointers.
|
| 916 |
+
*
|
| 917 |
+
* @param devh Device handle to an open UVC device
|
| 918 |
+
*/
|
| 919 |
+
const uvc_output_terminal_t *uvc_get_output_terminals(uvc_device_handle_t *devh) {
|
| 920 |
+
return NULL; /* @todo */
|
| 921 |
+
}
|
| 922 |
+
|
| 923 |
+
/**
|
| 924 |
+
* @brief Get selector unit descriptors for the open device.
|
| 925 |
+
*
|
| 926 |
+
* @note Do not modify the returned structure.
|
| 927 |
+
* @note The returned structure is part of a linked list. Iterate through
|
| 928 |
+
* it by using the 'next' pointers.
|
| 929 |
+
*
|
| 930 |
+
* @param devh Device handle to an open UVC device
|
| 931 |
+
*/
|
| 932 |
+
const uvc_selector_unit_t *uvc_get_selector_units(uvc_device_handle_t *devh) {
|
| 933 |
+
return devh->info->ctrl_if.selector_unit_descs;
|
| 934 |
+
}
|
| 935 |
+
|
| 936 |
+
/**
|
| 937 |
+
* @brief Get processing unit descriptors for the open device.
|
| 938 |
+
*
|
| 939 |
+
* @note Do not modify the returned structure.
|
| 940 |
+
* @note The returned structure is part of a linked list. Iterate through
|
| 941 |
+
* it by using the 'next' pointers.
|
| 942 |
+
*
|
| 943 |
+
* @param devh Device handle to an open UVC device
|
| 944 |
+
*/
|
| 945 |
+
const uvc_processing_unit_t *uvc_get_processing_units(uvc_device_handle_t *devh) {
|
| 946 |
+
return devh->info->ctrl_if.processing_unit_descs;
|
| 947 |
+
}
|
| 948 |
+
|
| 949 |
+
/**
|
| 950 |
+
* @brief Get extension unit descriptors for the open device.
|
| 951 |
+
*
|
| 952 |
+
* @note Do not modify the returned structure.
|
| 953 |
+
* @note The returned structure is part of a linked list. Iterate through
|
| 954 |
+
* it by using the 'next' pointers.
|
| 955 |
+
*
|
| 956 |
+
* @param devh Device handle to an open UVC device
|
| 957 |
+
*/
|
| 958 |
+
const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh) {
|
| 959 |
+
return devh->info->ctrl_if.extension_unit_descs;
|
| 960 |
+
}
|
| 961 |
+
|
| 962 |
+
/**
|
| 963 |
+
* @brief Increment the reference count for a device
|
| 964 |
+
* @ingroup device
|
| 965 |
+
*
|
| 966 |
+
* @param dev Device to reference
|
| 967 |
+
*/
|
| 968 |
+
void uvc_ref_device(uvc_device_t *dev) {
|
| 969 |
+
UVC_ENTER();
|
| 970 |
+
|
| 971 |
+
dev->ref++;
|
| 972 |
+
libusb_ref_device(dev->usb_dev);
|
| 973 |
+
|
| 974 |
+
UVC_EXIT_VOID();
|
| 975 |
+
}
|
| 976 |
+
|
| 977 |
+
/**
|
| 978 |
+
* @brief Decrement the reference count for a device
|
| 979 |
+
* @ingropu device
|
| 980 |
+
* @note If the count reaches zero, the device will be discarded
|
| 981 |
+
*
|
| 982 |
+
* @param dev Device to unreference
|
| 983 |
+
*/
|
| 984 |
+
void uvc_unref_device(uvc_device_t *dev) {
|
| 985 |
+
UVC_ENTER();
|
| 986 |
+
|
| 987 |
+
libusb_unref_device(dev->usb_dev);
|
| 988 |
+
dev->ref--;
|
| 989 |
+
|
| 990 |
+
if (dev->ref == 0)
|
| 991 |
+
free(dev);
|
| 992 |
+
|
| 993 |
+
UVC_EXIT_VOID();
|
| 994 |
+
}
|
| 995 |
+
|
| 996 |
+
/** @internal
|
| 997 |
+
* Claim a UVC interface, detaching the kernel driver if necessary.
|
| 998 |
+
* @ingroup device
|
| 999 |
+
*
|
| 1000 |
+
* @param devh UVC device handle
|
| 1001 |
+
* @param idx UVC interface index
|
| 1002 |
+
*/
|
| 1003 |
+
uvc_error_t uvc_claim_if(uvc_device_handle_t *devh, int idx) {
|
| 1004 |
+
int ret = UVC_SUCCESS;
|
| 1005 |
+
|
| 1006 |
+
UVC_ENTER();
|
| 1007 |
+
|
| 1008 |
+
if ( devh->claimed & ( 1 << idx )) {
|
| 1009 |
+
UVC_DEBUG("attempt to claim already-claimed interface %d\n", idx );
|
| 1010 |
+
UVC_EXIT(ret);
|
| 1011 |
+
return ret;
|
| 1012 |
+
}
|
| 1013 |
+
|
| 1014 |
+
/* Tell libusb to detach any active kernel drivers. libusb will keep track of whether
|
| 1015 |
+
* it found a kernel driver for this interface. */
|
| 1016 |
+
ret = libusb_detach_kernel_driver(devh->usb_devh, idx);
|
| 1017 |
+
|
| 1018 |
+
if (ret == UVC_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND || ret == LIBUSB_ERROR_NOT_SUPPORTED) {
|
| 1019 |
+
UVC_DEBUG("claiming interface %d", idx);
|
| 1020 |
+
if (!( ret = libusb_claim_interface(devh->usb_devh, idx))) {
|
| 1021 |
+
devh->claimed |= ( 1 << idx );
|
| 1022 |
+
}
|
| 1023 |
+
} else {
|
| 1024 |
+
UVC_DEBUG("not claiming interface %d: unable to detach kernel driver (%s)",
|
| 1025 |
+
idx, uvc_strerror(ret));
|
| 1026 |
+
}
|
| 1027 |
+
|
| 1028 |
+
UVC_EXIT(ret);
|
| 1029 |
+
return ret;
|
| 1030 |
+
}
|
| 1031 |
+
|
| 1032 |
+
/** @internal
|
| 1033 |
+
* Release a UVC interface.
|
| 1034 |
+
* @ingroup device
|
| 1035 |
+
*
|
| 1036 |
+
* @param devh UVC device handle
|
| 1037 |
+
* @param idx UVC interface index
|
| 1038 |
+
*/
|
| 1039 |
+
uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx) {
|
| 1040 |
+
int ret = UVC_SUCCESS;
|
| 1041 |
+
|
| 1042 |
+
UVC_ENTER();
|
| 1043 |
+
UVC_DEBUG("releasing interface %d", idx);
|
| 1044 |
+
if (!( devh->claimed & ( 1 << idx ))) {
|
| 1045 |
+
UVC_DEBUG("attempt to release unclaimed interface %d\n", idx );
|
| 1046 |
+
UVC_EXIT(ret);
|
| 1047 |
+
return ret;
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
/* libusb_release_interface *should* reset the alternate setting to the first available,
|
| 1051 |
+
but sometimes (e.g. on Darwin) it doesn't. Thus, we do it explicitly here.
|
| 1052 |
+
This is needed to de-initialize certain cameras. */
|
| 1053 |
+
libusb_set_interface_alt_setting(devh->usb_devh, idx, 0);
|
| 1054 |
+
ret = libusb_release_interface(devh->usb_devh, idx);
|
| 1055 |
+
|
| 1056 |
+
if (UVC_SUCCESS == ret) {
|
| 1057 |
+
devh->claimed &= ~( 1 << idx );
|
| 1058 |
+
/* Reattach any kernel drivers that were disabled when we claimed this interface */
|
| 1059 |
+
ret = libusb_attach_kernel_driver(devh->usb_devh, idx);
|
| 1060 |
+
|
| 1061 |
+
if (ret == UVC_SUCCESS) {
|
| 1062 |
+
UVC_DEBUG("reattached kernel driver to interface %d", idx);
|
| 1063 |
+
} else if (ret == LIBUSB_ERROR_NOT_FOUND || ret == LIBUSB_ERROR_NOT_SUPPORTED) {
|
| 1064 |
+
ret = UVC_SUCCESS; /* NOT_FOUND and NOT_SUPPORTED are OK: nothing to do */
|
| 1065 |
+
} else {
|
| 1066 |
+
UVC_DEBUG("error reattaching kernel driver to interface %d: %s",
|
| 1067 |
+
idx, uvc_strerror(ret));
|
| 1068 |
+
}
|
| 1069 |
+
}
|
| 1070 |
+
|
| 1071 |
+
UVC_EXIT(ret);
|
| 1072 |
+
return ret;
|
| 1073 |
+
}
|
| 1074 |
+
|
| 1075 |
+
/** @internal
|
| 1076 |
+
* Find a device's VideoControl interface and process its descriptor
|
| 1077 |
+
* @ingroup device
|
| 1078 |
+
*/
|
| 1079 |
+
uvc_error_t uvc_scan_control(uvc_device_handle_t *devh, uvc_device_info_t *info) {
|
| 1080 |
+
const struct libusb_interface_descriptor *if_desc;
|
| 1081 |
+
uvc_error_t parse_ret, ret;
|
| 1082 |
+
int interface_idx;
|
| 1083 |
+
const unsigned char *buffer;
|
| 1084 |
+
size_t buffer_left, block_size;
|
| 1085 |
+
|
| 1086 |
+
UVC_ENTER();
|
| 1087 |
+
|
| 1088 |
+
ret = UVC_SUCCESS;
|
| 1089 |
+
if_desc = NULL;
|
| 1090 |
+
|
| 1091 |
+
uvc_device_descriptor_t* dev_desc;
|
| 1092 |
+
int haveTISCamera = 0;
|
| 1093 |
+
if ( get_device_descriptor ( devh, &dev_desc ) == UVC_SUCCESS ) {
|
| 1094 |
+
if ( 0x199e == dev_desc->idVendor && ( 0x8101 == dev_desc->idProduct ||
|
| 1095 |
+
0x8102 == dev_desc->idProduct )) {
|
| 1096 |
+
haveTISCamera = 1;
|
| 1097 |
+
}
|
| 1098 |
+
uvc_free_device_descriptor ( dev_desc );
|
| 1099 |
+
}
|
| 1100 |
+
|
| 1101 |
+
for (interface_idx = 0; interface_idx < info->config->bNumInterfaces; ++interface_idx) {
|
| 1102 |
+
if_desc = &info->config->interface[interface_idx].altsetting[0];
|
| 1103 |
+
|
| 1104 |
+
if ( haveTISCamera && if_desc->bInterfaceClass == 255 && if_desc->bInterfaceSubClass == 1) // Video, Control
|
| 1105 |
+
break;
|
| 1106 |
+
|
| 1107 |
+
if (if_desc->bInterfaceClass == 14 && if_desc->bInterfaceSubClass == 1) // Video, Control
|
| 1108 |
+
break;
|
| 1109 |
+
|
| 1110 |
+
if_desc = NULL;
|
| 1111 |
+
}
|
| 1112 |
+
|
| 1113 |
+
if (if_desc == NULL) {
|
| 1114 |
+
UVC_EXIT(UVC_ERROR_INVALID_DEVICE);
|
| 1115 |
+
return UVC_ERROR_INVALID_DEVICE;
|
| 1116 |
+
}
|
| 1117 |
+
|
| 1118 |
+
info->ctrl_if.bInterfaceNumber = interface_idx;
|
| 1119 |
+
if (if_desc->bNumEndpoints != 0) {
|
| 1120 |
+
info->ctrl_if.bEndpointAddress = if_desc->endpoint[0].bEndpointAddress;
|
| 1121 |
+
}
|
| 1122 |
+
|
| 1123 |
+
buffer = if_desc->extra;
|
| 1124 |
+
buffer_left = if_desc->extra_length;
|
| 1125 |
+
|
| 1126 |
+
while (buffer_left >= 3) { // parseX needs to see buf[0,2] = length,type
|
| 1127 |
+
block_size = buffer[0];
|
| 1128 |
+
parse_ret = uvc_parse_vc(devh->dev, info, buffer, block_size);
|
| 1129 |
+
|
| 1130 |
+
if (parse_ret != UVC_SUCCESS) {
|
| 1131 |
+
ret = parse_ret;
|
| 1132 |
+
break;
|
| 1133 |
+
}
|
| 1134 |
+
|
| 1135 |
+
buffer_left -= block_size;
|
| 1136 |
+
buffer += block_size;
|
| 1137 |
+
}
|
| 1138 |
+
|
| 1139 |
+
UVC_EXIT(ret);
|
| 1140 |
+
return ret;
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
+
/** @internal
|
| 1144 |
+
* @brief Parse a VideoControl header.
|
| 1145 |
+
* @ingroup device
|
| 1146 |
+
*/
|
| 1147 |
+
uvc_error_t uvc_parse_vc_header(uvc_device_t *dev,
|
| 1148 |
+
uvc_device_info_t *info,
|
| 1149 |
+
const unsigned char *block, size_t block_size) {
|
| 1150 |
+
size_t i;
|
| 1151 |
+
uvc_error_t scan_ret, ret = UVC_SUCCESS;
|
| 1152 |
+
|
| 1153 |
+
UVC_ENTER();
|
| 1154 |
+
|
| 1155 |
+
/*
|
| 1156 |
+
int uvc_version;
|
| 1157 |
+
uvc_version = (block[4] >> 4) * 1000 + (block[4] & 0x0f) * 100
|
| 1158 |
+
+ (block[3] >> 4) * 10 + (block[3] & 0x0f);
|
| 1159 |
+
*/
|
| 1160 |
+
|
| 1161 |
+
info->ctrl_if.bcdUVC = SW_TO_SHORT(&block[3]);
|
| 1162 |
+
|
| 1163 |
+
switch (info->ctrl_if.bcdUVC) {
|
| 1164 |
+
case 0x0100:
|
| 1165 |
+
info->ctrl_if.dwClockFrequency = DW_TO_INT(block + 7);
|
| 1166 |
+
break;
|
| 1167 |
+
case 0x010a:
|
| 1168 |
+
info->ctrl_if.dwClockFrequency = DW_TO_INT(block + 7);
|
| 1169 |
+
break;
|
| 1170 |
+
case 0x0110:
|
| 1171 |
+
break;
|
| 1172 |
+
default:
|
| 1173 |
+
UVC_EXIT(UVC_ERROR_NOT_SUPPORTED);
|
| 1174 |
+
return UVC_ERROR_NOT_SUPPORTED;
|
| 1175 |
+
}
|
| 1176 |
+
|
| 1177 |
+
for (i = 12; i < block_size; ++i) {
|
| 1178 |
+
scan_ret = uvc_scan_streaming(dev, info, block[i]);
|
| 1179 |
+
if (scan_ret != UVC_SUCCESS) {
|
| 1180 |
+
ret = scan_ret;
|
| 1181 |
+
break;
|
| 1182 |
+
}
|
| 1183 |
+
}
|
| 1184 |
+
|
| 1185 |
+
UVC_EXIT(ret);
|
| 1186 |
+
return ret;
|
| 1187 |
+
}
|
| 1188 |
+
|
| 1189 |
+
/** @internal
|
| 1190 |
+
* @brief Parse a VideoControl input terminal.
|
| 1191 |
+
* @ingroup device
|
| 1192 |
+
*/
|
| 1193 |
+
uvc_error_t uvc_parse_vc_input_terminal(uvc_device_t *dev,
|
| 1194 |
+
uvc_device_info_t *info,
|
| 1195 |
+
const unsigned char *block, size_t block_size) {
|
| 1196 |
+
uvc_input_terminal_t *term;
|
| 1197 |
+
size_t i;
|
| 1198 |
+
|
| 1199 |
+
UVC_ENTER();
|
| 1200 |
+
|
| 1201 |
+
/* only supporting camera-type input terminals */
|
| 1202 |
+
if (SW_TO_SHORT(&block[4]) != UVC_ITT_CAMERA) {
|
| 1203 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1204 |
+
return UVC_SUCCESS;
|
| 1205 |
+
}
|
| 1206 |
+
|
| 1207 |
+
term = calloc(1, sizeof(*term));
|
| 1208 |
+
|
| 1209 |
+
term->bTerminalID = block[3];
|
| 1210 |
+
term->wTerminalType = SW_TO_SHORT(&block[4]);
|
| 1211 |
+
term->wObjectiveFocalLengthMin = SW_TO_SHORT(&block[8]);
|
| 1212 |
+
term->wObjectiveFocalLengthMax = SW_TO_SHORT(&block[10]);
|
| 1213 |
+
term->wOcularFocalLength = SW_TO_SHORT(&block[12]);
|
| 1214 |
+
|
| 1215 |
+
for (i = 14 + block[14]; i >= 15; --i)
|
| 1216 |
+
term->bmControls = block[i] + (term->bmControls << 8);
|
| 1217 |
+
|
| 1218 |
+
DL_APPEND(info->ctrl_if.input_term_descs, term);
|
| 1219 |
+
|
| 1220 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1221 |
+
return UVC_SUCCESS;
|
| 1222 |
+
}
|
| 1223 |
+
|
| 1224 |
+
/** @internal
|
| 1225 |
+
* @brief Parse a VideoControl processing unit.
|
| 1226 |
+
* @ingroup device
|
| 1227 |
+
*/
|
| 1228 |
+
uvc_error_t uvc_parse_vc_processing_unit(uvc_device_t *dev,
|
| 1229 |
+
uvc_device_info_t *info,
|
| 1230 |
+
const unsigned char *block, size_t block_size) {
|
| 1231 |
+
uvc_processing_unit_t *unit;
|
| 1232 |
+
size_t i;
|
| 1233 |
+
|
| 1234 |
+
UVC_ENTER();
|
| 1235 |
+
|
| 1236 |
+
unit = calloc(1, sizeof(*unit));
|
| 1237 |
+
unit->bUnitID = block[3];
|
| 1238 |
+
unit->bSourceID = block[4];
|
| 1239 |
+
|
| 1240 |
+
for (i = 7 + block[7]; i >= 8; --i)
|
| 1241 |
+
unit->bmControls = block[i] + (unit->bmControls << 8);
|
| 1242 |
+
|
| 1243 |
+
DL_APPEND(info->ctrl_if.processing_unit_descs, unit);
|
| 1244 |
+
|
| 1245 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1246 |
+
return UVC_SUCCESS;
|
| 1247 |
+
}
|
| 1248 |
+
|
| 1249 |
+
/** @internal
|
| 1250 |
+
* @brief Parse a VideoControl selector unit.
|
| 1251 |
+
* @ingroup device
|
| 1252 |
+
*/
|
| 1253 |
+
uvc_error_t uvc_parse_vc_selector_unit(uvc_device_t *dev,
|
| 1254 |
+
uvc_device_info_t *info,
|
| 1255 |
+
const unsigned char *block, size_t block_size) {
|
| 1256 |
+
uvc_selector_unit_t *unit;
|
| 1257 |
+
|
| 1258 |
+
UVC_ENTER();
|
| 1259 |
+
|
| 1260 |
+
unit = calloc(1, sizeof(*unit));
|
| 1261 |
+
unit->bUnitID = block[3];
|
| 1262 |
+
|
| 1263 |
+
DL_APPEND(info->ctrl_if.selector_unit_descs, unit);
|
| 1264 |
+
|
| 1265 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1266 |
+
return UVC_SUCCESS;
|
| 1267 |
+
}
|
| 1268 |
+
|
| 1269 |
+
/** @internal
|
| 1270 |
+
* @brief Parse a VideoControl extension unit.
|
| 1271 |
+
* @ingroup device
|
| 1272 |
+
*/
|
| 1273 |
+
uvc_error_t uvc_parse_vc_extension_unit(uvc_device_t *dev,
|
| 1274 |
+
uvc_device_info_t *info,
|
| 1275 |
+
const unsigned char *block, size_t block_size) {
|
| 1276 |
+
uvc_extension_unit_t *unit = calloc(1, sizeof(*unit));
|
| 1277 |
+
const uint8_t *start_of_controls;
|
| 1278 |
+
int size_of_controls, num_in_pins;
|
| 1279 |
+
int i;
|
| 1280 |
+
|
| 1281 |
+
UVC_ENTER();
|
| 1282 |
+
|
| 1283 |
+
unit->bUnitID = block[3];
|
| 1284 |
+
memcpy(unit->guidExtensionCode, &block[4], 16);
|
| 1285 |
+
|
| 1286 |
+
num_in_pins = block[21];
|
| 1287 |
+
size_of_controls = block[22 + num_in_pins];
|
| 1288 |
+
start_of_controls = &block[23 + num_in_pins];
|
| 1289 |
+
|
| 1290 |
+
for (i = size_of_controls - 1; i >= 0; --i)
|
| 1291 |
+
unit->bmControls = start_of_controls[i] + (unit->bmControls << 8);
|
| 1292 |
+
|
| 1293 |
+
DL_APPEND(info->ctrl_if.extension_unit_descs, unit);
|
| 1294 |
+
|
| 1295 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1296 |
+
return UVC_SUCCESS;
|
| 1297 |
+
}
|
| 1298 |
+
|
| 1299 |
+
/** @internal
|
| 1300 |
+
* Process a single VideoControl descriptor block
|
| 1301 |
+
* @ingroup device
|
| 1302 |
+
*/
|
| 1303 |
+
uvc_error_t uvc_parse_vc(
|
| 1304 |
+
uvc_device_t *dev,
|
| 1305 |
+
uvc_device_info_t *info,
|
| 1306 |
+
const unsigned char *block, size_t block_size) {
|
| 1307 |
+
int descriptor_subtype;
|
| 1308 |
+
uvc_error_t ret = UVC_SUCCESS;
|
| 1309 |
+
|
| 1310 |
+
UVC_ENTER();
|
| 1311 |
+
|
| 1312 |
+
if (block[1] != 36) { // not a CS_INTERFACE descriptor??
|
| 1313 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1314 |
+
return UVC_SUCCESS; // UVC_ERROR_INVALID_DEVICE;
|
| 1315 |
+
}
|
| 1316 |
+
|
| 1317 |
+
descriptor_subtype = block[2];
|
| 1318 |
+
|
| 1319 |
+
switch (descriptor_subtype) {
|
| 1320 |
+
case UVC_VC_HEADER:
|
| 1321 |
+
ret = uvc_parse_vc_header(dev, info, block, block_size);
|
| 1322 |
+
break;
|
| 1323 |
+
case UVC_VC_INPUT_TERMINAL:
|
| 1324 |
+
ret = uvc_parse_vc_input_terminal(dev, info, block, block_size);
|
| 1325 |
+
break;
|
| 1326 |
+
case UVC_VC_OUTPUT_TERMINAL:
|
| 1327 |
+
break;
|
| 1328 |
+
case UVC_VC_SELECTOR_UNIT:
|
| 1329 |
+
ret = uvc_parse_vc_selector_unit(dev, info, block, block_size);
|
| 1330 |
+
break;
|
| 1331 |
+
case UVC_VC_PROCESSING_UNIT:
|
| 1332 |
+
ret = uvc_parse_vc_processing_unit(dev, info, block, block_size);
|
| 1333 |
+
break;
|
| 1334 |
+
case UVC_VC_EXTENSION_UNIT:
|
| 1335 |
+
ret = uvc_parse_vc_extension_unit(dev, info, block, block_size);
|
| 1336 |
+
break;
|
| 1337 |
+
default:
|
| 1338 |
+
ret = UVC_ERROR_INVALID_DEVICE;
|
| 1339 |
+
}
|
| 1340 |
+
|
| 1341 |
+
UVC_EXIT(ret);
|
| 1342 |
+
return ret;
|
| 1343 |
+
}
|
| 1344 |
+
|
| 1345 |
+
/** @internal
|
| 1346 |
+
* Process a VideoStreaming interface
|
| 1347 |
+
* @ingroup device
|
| 1348 |
+
*/
|
| 1349 |
+
uvc_error_t uvc_scan_streaming(uvc_device_t *dev,
|
| 1350 |
+
uvc_device_info_t *info,
|
| 1351 |
+
int interface_idx) {
|
| 1352 |
+
const struct libusb_interface_descriptor *if_desc;
|
| 1353 |
+
const unsigned char *buffer;
|
| 1354 |
+
size_t buffer_left, block_size;
|
| 1355 |
+
uvc_error_t ret, parse_ret;
|
| 1356 |
+
uvc_streaming_interface_t *stream_if;
|
| 1357 |
+
|
| 1358 |
+
UVC_ENTER();
|
| 1359 |
+
|
| 1360 |
+
ret = UVC_SUCCESS;
|
| 1361 |
+
|
| 1362 |
+
if_desc = &(info->config->interface[interface_idx].altsetting[0]);
|
| 1363 |
+
buffer = if_desc->extra;
|
| 1364 |
+
buffer_left = if_desc->extra_length;
|
| 1365 |
+
|
| 1366 |
+
stream_if = calloc(1, sizeof(*stream_if));
|
| 1367 |
+
stream_if->parent = info;
|
| 1368 |
+
stream_if->bInterfaceNumber = if_desc->bInterfaceNumber;
|
| 1369 |
+
DL_APPEND(info->stream_ifs, stream_if);
|
| 1370 |
+
|
| 1371 |
+
while (buffer_left >= 3) {
|
| 1372 |
+
block_size = buffer[0];
|
| 1373 |
+
parse_ret = uvc_parse_vs(dev, info, stream_if, buffer, block_size);
|
| 1374 |
+
|
| 1375 |
+
if (parse_ret != UVC_SUCCESS) {
|
| 1376 |
+
ret = parse_ret;
|
| 1377 |
+
break;
|
| 1378 |
+
}
|
| 1379 |
+
|
| 1380 |
+
buffer_left -= block_size;
|
| 1381 |
+
buffer += block_size;
|
| 1382 |
+
}
|
| 1383 |
+
|
| 1384 |
+
UVC_EXIT(ret);
|
| 1385 |
+
return ret;
|
| 1386 |
+
}
|
| 1387 |
+
|
| 1388 |
+
/** @internal
|
| 1389 |
+
* @brief Parse a VideoStreaming header block.
|
| 1390 |
+
* @ingroup device
|
| 1391 |
+
*/
|
| 1392 |
+
uvc_error_t uvc_parse_vs_input_header(uvc_streaming_interface_t *stream_if,
|
| 1393 |
+
const unsigned char *block,
|
| 1394 |
+
size_t block_size) {
|
| 1395 |
+
UVC_ENTER();
|
| 1396 |
+
|
| 1397 |
+
stream_if->bEndpointAddress = block[6] & 0x8f;
|
| 1398 |
+
stream_if->bTerminalLink = block[8];
|
| 1399 |
+
stream_if->bStillCaptureMethod = block[9];
|
| 1400 |
+
|
| 1401 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1402 |
+
return UVC_SUCCESS;
|
| 1403 |
+
}
|
| 1404 |
+
|
| 1405 |
+
/** @internal
|
| 1406 |
+
* @brief Parse a VideoStreaming uncompressed format block.
|
| 1407 |
+
* @ingroup device
|
| 1408 |
+
*/
|
| 1409 |
+
uvc_error_t uvc_parse_vs_format_uncompressed(uvc_streaming_interface_t *stream_if,
|
| 1410 |
+
const unsigned char *block,
|
| 1411 |
+
size_t block_size) {
|
| 1412 |
+
UVC_ENTER();
|
| 1413 |
+
|
| 1414 |
+
uvc_format_desc_t *format = calloc(1, sizeof(*format));
|
| 1415 |
+
|
| 1416 |
+
format->parent = stream_if;
|
| 1417 |
+
format->bDescriptorSubtype = block[2];
|
| 1418 |
+
format->bFormatIndex = block[3];
|
| 1419 |
+
//format->bmCapabilities = block[4];
|
| 1420 |
+
//format->bmFlags = block[5];
|
| 1421 |
+
memcpy(format->guidFormat, &block[5], 16);
|
| 1422 |
+
format->bBitsPerPixel = block[21];
|
| 1423 |
+
format->bDefaultFrameIndex = block[22];
|
| 1424 |
+
format->bAspectRatioX = block[23];
|
| 1425 |
+
format->bAspectRatioY = block[24];
|
| 1426 |
+
format->bmInterlaceFlags = block[25];
|
| 1427 |
+
format->bCopyProtect = block[26];
|
| 1428 |
+
|
| 1429 |
+
DL_APPEND(stream_if->format_descs, format);
|
| 1430 |
+
|
| 1431 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1432 |
+
return UVC_SUCCESS;
|
| 1433 |
+
}
|
| 1434 |
+
|
| 1435 |
+
/** @internal
|
| 1436 |
+
* @brief Parse a VideoStreaming frame format block.
|
| 1437 |
+
* @ingroup device
|
| 1438 |
+
*/
|
| 1439 |
+
uvc_error_t uvc_parse_vs_frame_format(uvc_streaming_interface_t *stream_if,
|
| 1440 |
+
const unsigned char *block,
|
| 1441 |
+
size_t block_size) {
|
| 1442 |
+
UVC_ENTER();
|
| 1443 |
+
|
| 1444 |
+
uvc_format_desc_t *format = calloc(1, sizeof(*format));
|
| 1445 |
+
|
| 1446 |
+
format->parent = stream_if;
|
| 1447 |
+
format->bDescriptorSubtype = block[2];
|
| 1448 |
+
format->bFormatIndex = block[3];
|
| 1449 |
+
format->bNumFrameDescriptors = block[4];
|
| 1450 |
+
memcpy(format->guidFormat, &block[5], 16);
|
| 1451 |
+
format->bBitsPerPixel = block[21];
|
| 1452 |
+
format->bDefaultFrameIndex = block[22];
|
| 1453 |
+
format->bAspectRatioX = block[23];
|
| 1454 |
+
format->bAspectRatioY = block[24];
|
| 1455 |
+
format->bmInterlaceFlags = block[25];
|
| 1456 |
+
format->bCopyProtect = block[26];
|
| 1457 |
+
format->bVariableSize = block[27];
|
| 1458 |
+
|
| 1459 |
+
DL_APPEND(stream_if->format_descs, format);
|
| 1460 |
+
|
| 1461 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1462 |
+
return UVC_SUCCESS;
|
| 1463 |
+
}
|
| 1464 |
+
|
| 1465 |
+
/** @internal
|
| 1466 |
+
* @brief Parse a VideoStreaming MJPEG format block.
|
| 1467 |
+
* @ingroup device
|
| 1468 |
+
*/
|
| 1469 |
+
uvc_error_t uvc_parse_vs_format_mjpeg(uvc_streaming_interface_t *stream_if,
|
| 1470 |
+
const unsigned char *block,
|
| 1471 |
+
size_t block_size) {
|
| 1472 |
+
UVC_ENTER();
|
| 1473 |
+
|
| 1474 |
+
uvc_format_desc_t *format = calloc(1, sizeof(*format));
|
| 1475 |
+
|
| 1476 |
+
format->parent = stream_if;
|
| 1477 |
+
format->bDescriptorSubtype = block[2];
|
| 1478 |
+
format->bFormatIndex = block[3];
|
| 1479 |
+
memcpy(format->fourccFormat, "MJPG", 4);
|
| 1480 |
+
format->bmFlags = block[5];
|
| 1481 |
+
format->bBitsPerPixel = 0;
|
| 1482 |
+
format->bDefaultFrameIndex = block[6];
|
| 1483 |
+
format->bAspectRatioX = block[7];
|
| 1484 |
+
format->bAspectRatioY = block[8];
|
| 1485 |
+
format->bmInterlaceFlags = block[9];
|
| 1486 |
+
format->bCopyProtect = block[10];
|
| 1487 |
+
|
| 1488 |
+
DL_APPEND(stream_if->format_descs, format);
|
| 1489 |
+
|
| 1490 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1491 |
+
return UVC_SUCCESS;
|
| 1492 |
+
}
|
| 1493 |
+
|
| 1494 |
+
/** @internal
|
| 1495 |
+
* @brief Parse a VideoStreaming uncompressed frame block.
|
| 1496 |
+
* @ingroup device
|
| 1497 |
+
*/
|
| 1498 |
+
uvc_error_t uvc_parse_vs_frame_frame(uvc_streaming_interface_t *stream_if,
|
| 1499 |
+
const unsigned char *block,
|
| 1500 |
+
size_t block_size) {
|
| 1501 |
+
uvc_format_desc_t *format;
|
| 1502 |
+
uvc_frame_desc_t *frame;
|
| 1503 |
+
|
| 1504 |
+
const unsigned char *p;
|
| 1505 |
+
int i;
|
| 1506 |
+
|
| 1507 |
+
UVC_ENTER();
|
| 1508 |
+
|
| 1509 |
+
format = stream_if->format_descs->prev;
|
| 1510 |
+
frame = calloc(1, sizeof(*frame));
|
| 1511 |
+
|
| 1512 |
+
frame->parent = format;
|
| 1513 |
+
|
| 1514 |
+
frame->bDescriptorSubtype = block[2];
|
| 1515 |
+
frame->bFrameIndex = block[3];
|
| 1516 |
+
frame->bmCapabilities = block[4];
|
| 1517 |
+
frame->wWidth = block[5] + (block[6] << 8);
|
| 1518 |
+
frame->wHeight = block[7] + (block[8] << 8);
|
| 1519 |
+
frame->dwMinBitRate = DW_TO_INT(&block[9]);
|
| 1520 |
+
frame->dwMaxBitRate = DW_TO_INT(&block[13]);
|
| 1521 |
+
frame->dwDefaultFrameInterval = DW_TO_INT(&block[17]);
|
| 1522 |
+
frame->bFrameIntervalType = block[21];
|
| 1523 |
+
frame->dwBytesPerLine = DW_TO_INT(&block[22]);
|
| 1524 |
+
|
| 1525 |
+
if (block[21] == 0) {
|
| 1526 |
+
frame->dwMinFrameInterval = DW_TO_INT(&block[26]);
|
| 1527 |
+
frame->dwMaxFrameInterval = DW_TO_INT(&block[30]);
|
| 1528 |
+
frame->dwFrameIntervalStep = DW_TO_INT(&block[34]);
|
| 1529 |
+
} else {
|
| 1530 |
+
frame->intervals = calloc(block[21] + 1, sizeof(frame->intervals[0]));
|
| 1531 |
+
p = &block[26];
|
| 1532 |
+
|
| 1533 |
+
for (i = 0; i < block[21]; ++i) {
|
| 1534 |
+
frame->intervals[i] = DW_TO_INT(p);
|
| 1535 |
+
p += 4;
|
| 1536 |
+
}
|
| 1537 |
+
frame->intervals[block[21]] = 0;
|
| 1538 |
+
}
|
| 1539 |
+
|
| 1540 |
+
DL_APPEND(format->frame_descs, frame);
|
| 1541 |
+
|
| 1542 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1543 |
+
return UVC_SUCCESS;
|
| 1544 |
+
}
|
| 1545 |
+
|
| 1546 |
+
/** @internal
|
| 1547 |
+
* @brief Parse a VideoStreaming uncompressed frame block.
|
| 1548 |
+
* @ingroup device
|
| 1549 |
+
*/
|
| 1550 |
+
uvc_error_t uvc_parse_vs_frame_uncompressed(uvc_streaming_interface_t *stream_if,
|
| 1551 |
+
const unsigned char *block,
|
| 1552 |
+
size_t block_size) {
|
| 1553 |
+
uvc_format_desc_t *format;
|
| 1554 |
+
uvc_frame_desc_t *frame;
|
| 1555 |
+
|
| 1556 |
+
const unsigned char *p;
|
| 1557 |
+
int i;
|
| 1558 |
+
|
| 1559 |
+
UVC_ENTER();
|
| 1560 |
+
|
| 1561 |
+
format = stream_if->format_descs->prev;
|
| 1562 |
+
frame = calloc(1, sizeof(*frame));
|
| 1563 |
+
|
| 1564 |
+
frame->parent = format;
|
| 1565 |
+
|
| 1566 |
+
frame->bDescriptorSubtype = block[2];
|
| 1567 |
+
frame->bFrameIndex = block[3];
|
| 1568 |
+
frame->bmCapabilities = block[4];
|
| 1569 |
+
frame->wWidth = block[5] + (block[6] << 8);
|
| 1570 |
+
frame->wHeight = block[7] + (block[8] << 8);
|
| 1571 |
+
frame->dwMinBitRate = DW_TO_INT(&block[9]);
|
| 1572 |
+
frame->dwMaxBitRate = DW_TO_INT(&block[13]);
|
| 1573 |
+
frame->dwMaxVideoFrameBufferSize = DW_TO_INT(&block[17]);
|
| 1574 |
+
frame->dwDefaultFrameInterval = DW_TO_INT(&block[21]);
|
| 1575 |
+
frame->bFrameIntervalType = block[25];
|
| 1576 |
+
|
| 1577 |
+
if (block[25] == 0) {
|
| 1578 |
+
frame->dwMinFrameInterval = DW_TO_INT(&block[26]);
|
| 1579 |
+
frame->dwMaxFrameInterval = DW_TO_INT(&block[30]);
|
| 1580 |
+
frame->dwFrameIntervalStep = DW_TO_INT(&block[34]);
|
| 1581 |
+
} else {
|
| 1582 |
+
frame->intervals = calloc(block[25] + 1, sizeof(frame->intervals[0]));
|
| 1583 |
+
p = &block[26];
|
| 1584 |
+
|
| 1585 |
+
for (i = 0; i < block[25]; ++i) {
|
| 1586 |
+
frame->intervals[i] = DW_TO_INT(p);
|
| 1587 |
+
p += 4;
|
| 1588 |
+
}
|
| 1589 |
+
frame->intervals[block[25]] = 0;
|
| 1590 |
+
}
|
| 1591 |
+
|
| 1592 |
+
DL_APPEND(format->frame_descs, frame);
|
| 1593 |
+
|
| 1594 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1595 |
+
return UVC_SUCCESS;
|
| 1596 |
+
}
|
| 1597 |
+
|
| 1598 |
+
/** @internal
|
| 1599 |
+
* @brief Parse a VideoStreaming still iamge frame
|
| 1600 |
+
* @ingroup device
|
| 1601 |
+
*/
|
| 1602 |
+
uvc_error_t uvc_parse_vs_still_image_frame(uvc_streaming_interface_t *stream_if,
|
| 1603 |
+
const unsigned char *block,
|
| 1604 |
+
size_t block_size) {
|
| 1605 |
+
|
| 1606 |
+
struct uvc_still_frame_desc* frame;
|
| 1607 |
+
uvc_format_desc_t *format;
|
| 1608 |
+
|
| 1609 |
+
const unsigned char *p;
|
| 1610 |
+
int i;
|
| 1611 |
+
|
| 1612 |
+
UVC_ENTER();
|
| 1613 |
+
|
| 1614 |
+
format = stream_if->format_descs->prev;
|
| 1615 |
+
frame = calloc(1, sizeof(*frame));
|
| 1616 |
+
|
| 1617 |
+
frame->parent = format;
|
| 1618 |
+
|
| 1619 |
+
frame->bDescriptorSubtype = block[2];
|
| 1620 |
+
frame->bEndPointAddress = block[3];
|
| 1621 |
+
uint8_t numImageSizePatterns = block[4];
|
| 1622 |
+
|
| 1623 |
+
frame->imageSizePatterns = NULL;
|
| 1624 |
+
|
| 1625 |
+
p = &block[5];
|
| 1626 |
+
|
| 1627 |
+
for (i = 1; i <= numImageSizePatterns; ++i) {
|
| 1628 |
+
uvc_still_frame_res_t* res = calloc(1, sizeof(uvc_still_frame_res_t));
|
| 1629 |
+
res->bResolutionIndex = i;
|
| 1630 |
+
res->wWidth = SW_TO_SHORT(p);
|
| 1631 |
+
p += 2;
|
| 1632 |
+
res->wHeight = SW_TO_SHORT(p);
|
| 1633 |
+
p += 2;
|
| 1634 |
+
|
| 1635 |
+
DL_APPEND(frame->imageSizePatterns, res);
|
| 1636 |
+
}
|
| 1637 |
+
|
| 1638 |
+
p = &block[5+4*numImageSizePatterns];
|
| 1639 |
+
frame->bNumCompressionPattern = *p;
|
| 1640 |
+
|
| 1641 |
+
if(frame->bNumCompressionPattern)
|
| 1642 |
+
{
|
| 1643 |
+
frame->bCompression = calloc(frame->bNumCompressionPattern, sizeof(frame->bCompression[0]));
|
| 1644 |
+
for(i = 0; i < frame->bNumCompressionPattern; ++i)
|
| 1645 |
+
{
|
| 1646 |
+
++p;
|
| 1647 |
+
frame->bCompression[i] = *p;
|
| 1648 |
+
}
|
| 1649 |
+
}
|
| 1650 |
+
else
|
| 1651 |
+
{
|
| 1652 |
+
frame->bCompression = NULL;
|
| 1653 |
+
}
|
| 1654 |
+
|
| 1655 |
+
DL_APPEND(format->still_frame_desc, frame);
|
| 1656 |
+
|
| 1657 |
+
UVC_EXIT(UVC_SUCCESS);
|
| 1658 |
+
return UVC_SUCCESS;
|
| 1659 |
+
}
|
| 1660 |
+
|
| 1661 |
+
/** @internal
|
| 1662 |
+
* Process a single VideoStreaming descriptor block
|
| 1663 |
+
* @ingroup device
|
| 1664 |
+
*/
|
| 1665 |
+
uvc_error_t uvc_parse_vs(
|
| 1666 |
+
uvc_device_t *dev,
|
| 1667 |
+
uvc_device_info_t *info,
|
| 1668 |
+
uvc_streaming_interface_t *stream_if,
|
| 1669 |
+
const unsigned char *block, size_t block_size) {
|
| 1670 |
+
uvc_error_t ret;
|
| 1671 |
+
int descriptor_subtype;
|
| 1672 |
+
|
| 1673 |
+
UVC_ENTER();
|
| 1674 |
+
|
| 1675 |
+
ret = UVC_SUCCESS;
|
| 1676 |
+
descriptor_subtype = block[2];
|
| 1677 |
+
|
| 1678 |
+
switch (descriptor_subtype) {
|
| 1679 |
+
case UVC_VS_INPUT_HEADER:
|
| 1680 |
+
ret = uvc_parse_vs_input_header(stream_if, block, block_size);
|
| 1681 |
+
break;
|
| 1682 |
+
case UVC_VS_OUTPUT_HEADER:
|
| 1683 |
+
UVC_DEBUG("unsupported descriptor subtype VS_OUTPUT_HEADER");
|
| 1684 |
+
break;
|
| 1685 |
+
case UVC_VS_STILL_IMAGE_FRAME:
|
| 1686 |
+
ret = uvc_parse_vs_still_image_frame(stream_if, block, block_size);
|
| 1687 |
+
break;
|
| 1688 |
+
case UVC_VS_FORMAT_UNCOMPRESSED:
|
| 1689 |
+
ret = uvc_parse_vs_format_uncompressed(stream_if, block, block_size);
|
| 1690 |
+
break;
|
| 1691 |
+
case UVC_VS_FORMAT_MJPEG:
|
| 1692 |
+
ret = uvc_parse_vs_format_mjpeg(stream_if, block, block_size);
|
| 1693 |
+
break;
|
| 1694 |
+
case UVC_VS_FRAME_UNCOMPRESSED:
|
| 1695 |
+
case UVC_VS_FRAME_MJPEG:
|
| 1696 |
+
ret = uvc_parse_vs_frame_uncompressed(stream_if, block, block_size);
|
| 1697 |
+
break;
|
| 1698 |
+
case UVC_VS_FORMAT_MPEG2TS:
|
| 1699 |
+
UVC_DEBUG("unsupported descriptor subtype VS_FORMAT_MPEG2TS");
|
| 1700 |
+
break;
|
| 1701 |
+
case UVC_VS_FORMAT_DV:
|
| 1702 |
+
UVC_DEBUG("unsupported descriptor subtype VS_FORMAT_DV");
|
| 1703 |
+
break;
|
| 1704 |
+
case UVC_VS_COLORFORMAT:
|
| 1705 |
+
UVC_DEBUG("unsupported descriptor subtype VS_COLORFORMAT");
|
| 1706 |
+
break;
|
| 1707 |
+
case UVC_VS_FORMAT_FRAME_BASED:
|
| 1708 |
+
ret = uvc_parse_vs_frame_format ( stream_if, block, block_size );
|
| 1709 |
+
break;
|
| 1710 |
+
case UVC_VS_FRAME_FRAME_BASED:
|
| 1711 |
+
ret = uvc_parse_vs_frame_frame ( stream_if, block, block_size );
|
| 1712 |
+
break;
|
| 1713 |
+
case UVC_VS_FORMAT_STREAM_BASED:
|
| 1714 |
+
UVC_DEBUG("unsupported descriptor subtype VS_FORMAT_STREAM_BASED");
|
| 1715 |
+
break;
|
| 1716 |
+
default:
|
| 1717 |
+
/** @todo handle JPEG and maybe still frames or even DV... */
|
| 1718 |
+
//UVC_DEBUG("unsupported descriptor subtype: %d",descriptor_subtype);
|
| 1719 |
+
break;
|
| 1720 |
+
}
|
| 1721 |
+
|
| 1722 |
+
UVC_EXIT(ret);
|
| 1723 |
+
return ret;
|
| 1724 |
+
}
|
| 1725 |
+
|
| 1726 |
+
/** @internal
|
| 1727 |
+
* @brief Free memory associated with a UVC device
|
| 1728 |
+
* @pre Streaming must be stopped, and threads must have died
|
| 1729 |
+
*/
|
| 1730 |
+
void uvc_free_devh(uvc_device_handle_t *devh) {
|
| 1731 |
+
UVC_ENTER();
|
| 1732 |
+
|
| 1733 |
+
if (devh->info)
|
| 1734 |
+
uvc_free_device_info(devh->info);
|
| 1735 |
+
|
| 1736 |
+
if (devh->status_xfer)
|
| 1737 |
+
libusb_free_transfer(devh->status_xfer);
|
| 1738 |
+
|
| 1739 |
+
free(devh);
|
| 1740 |
+
|
| 1741 |
+
UVC_EXIT_VOID();
|
| 1742 |
+
}
|
| 1743 |
+
|
| 1744 |
+
/** @brief Close a device
|
| 1745 |
+
*
|
| 1746 |
+
* @ingroup device
|
| 1747 |
+
*
|
| 1748 |
+
* Ends any stream that's in progress.
|
| 1749 |
+
*
|
| 1750 |
+
* The device handle and frame structures will be invalidated.
|
| 1751 |
+
*/
|
| 1752 |
+
void uvc_close(uvc_device_handle_t *devh) {
|
| 1753 |
+
UVC_ENTER();
|
| 1754 |
+
uvc_context_t *ctx = devh->dev->ctx;
|
| 1755 |
+
|
| 1756 |
+
if (devh->streams)
|
| 1757 |
+
uvc_stop_streaming(devh);
|
| 1758 |
+
|
| 1759 |
+
uvc_release_if(devh, devh->info->ctrl_if.bInterfaceNumber);
|
| 1760 |
+
|
| 1761 |
+
/* If we are managing the libusb context and this is the last open device,
|
| 1762 |
+
* then we need to cancel the handler thread. When we call libusb_close,
|
| 1763 |
+
* it'll cause a return from the thread's libusb_handle_events call, after
|
| 1764 |
+
* which the handler thread will check the flag we set and then exit. */
|
| 1765 |
+
if (ctx->own_usb_ctx && ctx->open_devices == devh && devh->next == NULL) {
|
| 1766 |
+
ctx->kill_handler_thread = 1;
|
| 1767 |
+
libusb_close(devh->usb_devh);
|
| 1768 |
+
pthread_join(ctx->handler_thread, NULL);
|
| 1769 |
+
} else {
|
| 1770 |
+
libusb_close(devh->usb_devh);
|
| 1771 |
+
}
|
| 1772 |
+
|
| 1773 |
+
DL_DELETE(ctx->open_devices, devh);
|
| 1774 |
+
|
| 1775 |
+
uvc_unref_device(devh->dev);
|
| 1776 |
+
|
| 1777 |
+
uvc_free_devh(devh);
|
| 1778 |
+
|
| 1779 |
+
UVC_EXIT_VOID();
|
| 1780 |
+
}
|
| 1781 |
+
|
| 1782 |
+
/** @internal
|
| 1783 |
+
* @brief Get number of open devices
|
| 1784 |
+
*/
|
| 1785 |
+
size_t uvc_num_devices(uvc_context_t *ctx) {
|
| 1786 |
+
size_t count = 0;
|
| 1787 |
+
|
| 1788 |
+
uvc_device_handle_t *devh;
|
| 1789 |
+
|
| 1790 |
+
UVC_ENTER();
|
| 1791 |
+
|
| 1792 |
+
DL_FOREACH(ctx->open_devices, devh) {
|
| 1793 |
+
count++;
|
| 1794 |
+
}
|
| 1795 |
+
|
| 1796 |
+
UVC_EXIT((int) count);
|
| 1797 |
+
return count;
|
| 1798 |
+
}
|
| 1799 |
+
|
| 1800 |
+
void uvc_process_control_status(uvc_device_handle_t *devh, unsigned char *data, int len) {
|
| 1801 |
+
enum uvc_status_class status_class;
|
| 1802 |
+
uint8_t originator = 0, selector = 0, event = 0;
|
| 1803 |
+
enum uvc_status_attribute attribute = UVC_STATUS_ATTRIBUTE_UNKNOWN;
|
| 1804 |
+
void *content = NULL;
|
| 1805 |
+
size_t content_len = 0;
|
| 1806 |
+
int found_entity = 0;
|
| 1807 |
+
struct uvc_input_terminal *input_terminal;
|
| 1808 |
+
struct uvc_processing_unit *processing_unit;
|
| 1809 |
+
|
| 1810 |
+
UVC_ENTER();
|
| 1811 |
+
|
| 1812 |
+
if (len < 5) {
|
| 1813 |
+
UVC_DEBUG("Short read of VideoControl status update (%d bytes)", len);
|
| 1814 |
+
UVC_EXIT_VOID();
|
| 1815 |
+
return;
|
| 1816 |
+
}
|
| 1817 |
+
|
| 1818 |
+
originator = data[1];
|
| 1819 |
+
event = data[2];
|
| 1820 |
+
selector = data[3];
|
| 1821 |
+
|
| 1822 |
+
if (originator == 0) {
|
| 1823 |
+
UVC_DEBUG("Unhandled update from VC interface");
|
| 1824 |
+
UVC_EXIT_VOID();
|
| 1825 |
+
return; /* @todo VideoControl virtual entity interface updates */
|
| 1826 |
+
}
|
| 1827 |
+
|
| 1828 |
+
if (event != 0) {
|
| 1829 |
+
UVC_DEBUG("Unhandled VC event %d", (int) event);
|
| 1830 |
+
UVC_EXIT_VOID();
|
| 1831 |
+
return;
|
| 1832 |
+
}
|
| 1833 |
+
|
| 1834 |
+
/* printf("bSelector: %d\n", selector); */
|
| 1835 |
+
|
| 1836 |
+
DL_FOREACH(devh->info->ctrl_if.input_term_descs, input_terminal) {
|
| 1837 |
+
if (input_terminal->bTerminalID == originator) {
|
| 1838 |
+
status_class = UVC_STATUS_CLASS_CONTROL_CAMERA;
|
| 1839 |
+
found_entity = 1;
|
| 1840 |
+
break;
|
| 1841 |
+
}
|
| 1842 |
+
}
|
| 1843 |
+
|
| 1844 |
+
if (!found_entity) {
|
| 1845 |
+
DL_FOREACH(devh->info->ctrl_if.processing_unit_descs, processing_unit) {
|
| 1846 |
+
if (processing_unit->bUnitID == originator) {
|
| 1847 |
+
status_class = UVC_STATUS_CLASS_CONTROL_PROCESSING;
|
| 1848 |
+
found_entity = 1;
|
| 1849 |
+
break;
|
| 1850 |
+
}
|
| 1851 |
+
}
|
| 1852 |
+
}
|
| 1853 |
+
|
| 1854 |
+
if (!found_entity) {
|
| 1855 |
+
UVC_DEBUG("Got status update for unknown VideoControl entity %d",
|
| 1856 |
+
(int) originator);
|
| 1857 |
+
UVC_EXIT_VOID();
|
| 1858 |
+
return;
|
| 1859 |
+
}
|
| 1860 |
+
|
| 1861 |
+
attribute = data[4];
|
| 1862 |
+
content = data + 5;
|
| 1863 |
+
content_len = len - 5;
|
| 1864 |
+
|
| 1865 |
+
UVC_DEBUG("Event: class=%d, event=%d, selector=%d, attribute=%d, content_len=%zd",
|
| 1866 |
+
status_class, event, selector, attribute, content_len);
|
| 1867 |
+
|
| 1868 |
+
if(devh->status_cb) {
|
| 1869 |
+
UVC_DEBUG("Running user-supplied status callback");
|
| 1870 |
+
devh->status_cb(status_class,
|
| 1871 |
+
event,
|
| 1872 |
+
selector,
|
| 1873 |
+
attribute,
|
| 1874 |
+
content, content_len,
|
| 1875 |
+
devh->status_user_ptr);
|
| 1876 |
+
}
|
| 1877 |
+
|
| 1878 |
+
UVC_EXIT_VOID();
|
| 1879 |
+
}
|
| 1880 |
+
|
| 1881 |
+
void uvc_process_streaming_status(uvc_device_handle_t *devh, unsigned char *data, int len) {
|
| 1882 |
+
|
| 1883 |
+
UVC_ENTER();
|
| 1884 |
+
|
| 1885 |
+
if (len < 3) {
|
| 1886 |
+
UVC_DEBUG("Invalid streaming status event received.\n");
|
| 1887 |
+
UVC_EXIT_VOID();
|
| 1888 |
+
return;
|
| 1889 |
+
}
|
| 1890 |
+
|
| 1891 |
+
if (data[2] == 0) {
|
| 1892 |
+
if (len < 4) {
|
| 1893 |
+
UVC_DEBUG("Short read of status update (%d bytes)", len);
|
| 1894 |
+
UVC_EXIT_VOID();
|
| 1895 |
+
return;
|
| 1896 |
+
}
|
| 1897 |
+
UVC_DEBUG("Button (intf %u) %s len %d\n", data[1], data[3] ? "pressed" : "released", len);
|
| 1898 |
+
|
| 1899 |
+
if(devh->button_cb) {
|
| 1900 |
+
UVC_DEBUG("Running user-supplied button callback");
|
| 1901 |
+
devh->button_cb(data[1],
|
| 1902 |
+
data[3],
|
| 1903 |
+
devh->button_user_ptr);
|
| 1904 |
+
}
|
| 1905 |
+
} else {
|
| 1906 |
+
UVC_DEBUG("Stream %u error event %02x %02x len %d.\n", data[1], data[2], data[3], len);
|
| 1907 |
+
}
|
| 1908 |
+
|
| 1909 |
+
UVC_EXIT_VOID();
|
| 1910 |
+
}
|
| 1911 |
+
|
| 1912 |
+
void uvc_process_status_xfer(uvc_device_handle_t *devh, struct libusb_transfer *transfer) {
|
| 1913 |
+
|
| 1914 |
+
UVC_ENTER();
|
| 1915 |
+
|
| 1916 |
+
/* printf("Got transfer of aLen = %d\n", transfer->actual_length); */
|
| 1917 |
+
|
| 1918 |
+
if (transfer->actual_length > 0) {
|
| 1919 |
+
switch (transfer->buffer[0] & 0x0f) {
|
| 1920 |
+
case 1: /* VideoControl interface */
|
| 1921 |
+
uvc_process_control_status(devh, transfer->buffer, transfer->actual_length);
|
| 1922 |
+
break;
|
| 1923 |
+
case 2: /* VideoStreaming interface */
|
| 1924 |
+
uvc_process_streaming_status(devh, transfer->buffer, transfer->actual_length);
|
| 1925 |
+
break;
|
| 1926 |
+
}
|
| 1927 |
+
}
|
| 1928 |
+
|
| 1929 |
+
UVC_EXIT_VOID();
|
| 1930 |
+
}
|
| 1931 |
+
|
| 1932 |
+
/** @internal
|
| 1933 |
+
* @brief Process asynchronous status updates from the device.
|
| 1934 |
+
*/
|
| 1935 |
+
void LIBUSB_CALL _uvc_status_callback(struct libusb_transfer *transfer) {
|
| 1936 |
+
UVC_ENTER();
|
| 1937 |
+
|
| 1938 |
+
uvc_device_handle_t *devh = (uvc_device_handle_t *) transfer->user_data;
|
| 1939 |
+
|
| 1940 |
+
switch (transfer->status) {
|
| 1941 |
+
case LIBUSB_TRANSFER_ERROR:
|
| 1942 |
+
case LIBUSB_TRANSFER_CANCELLED:
|
| 1943 |
+
case LIBUSB_TRANSFER_NO_DEVICE:
|
| 1944 |
+
UVC_DEBUG("not processing/resubmitting, status = %d", transfer->status);
|
| 1945 |
+
UVC_EXIT_VOID();
|
| 1946 |
+
return;
|
| 1947 |
+
case LIBUSB_TRANSFER_COMPLETED:
|
| 1948 |
+
uvc_process_status_xfer(devh, transfer);
|
| 1949 |
+
break;
|
| 1950 |
+
case LIBUSB_TRANSFER_TIMED_OUT:
|
| 1951 |
+
case LIBUSB_TRANSFER_STALL:
|
| 1952 |
+
case LIBUSB_TRANSFER_OVERFLOW:
|
| 1953 |
+
UVC_DEBUG("retrying transfer, status = %d", transfer->status);
|
| 1954 |
+
break;
|
| 1955 |
+
}
|
| 1956 |
+
|
| 1957 |
+
#ifdef UVC_DEBUGGING
|
| 1958 |
+
uvc_error_t ret =
|
| 1959 |
+
#endif
|
| 1960 |
+
libusb_submit_transfer(transfer);
|
| 1961 |
+
UVC_DEBUG("libusb_submit_transfer() = %d", ret);
|
| 1962 |
+
|
| 1963 |
+
UVC_EXIT_VOID();
|
| 1964 |
+
}
|
| 1965 |
+
|
| 1966 |
+
/** @brief Set a callback function to receive status updates
|
| 1967 |
+
*
|
| 1968 |
+
* @ingroup device
|
| 1969 |
+
*/
|
| 1970 |
+
void uvc_set_status_callback(uvc_device_handle_t *devh,
|
| 1971 |
+
uvc_status_callback_t cb,
|
| 1972 |
+
void *user_ptr) {
|
| 1973 |
+
UVC_ENTER();
|
| 1974 |
+
|
| 1975 |
+
devh->status_cb = cb;
|
| 1976 |
+
devh->status_user_ptr = user_ptr;
|
| 1977 |
+
|
| 1978 |
+
UVC_EXIT_VOID();
|
| 1979 |
+
}
|
| 1980 |
+
|
| 1981 |
+
/** @brief Set a callback function to receive button events
|
| 1982 |
+
*
|
| 1983 |
+
* @ingroup device
|
| 1984 |
+
*/
|
| 1985 |
+
void uvc_set_button_callback(uvc_device_handle_t *devh,
|
| 1986 |
+
uvc_button_callback_t cb,
|
| 1987 |
+
void *user_ptr) {
|
| 1988 |
+
UVC_ENTER();
|
| 1989 |
+
|
| 1990 |
+
devh->button_cb = cb;
|
| 1991 |
+
devh->button_user_ptr = user_ptr;
|
| 1992 |
+
|
| 1993 |
+
UVC_EXIT_VOID();
|
| 1994 |
+
}
|
| 1995 |
+
|
| 1996 |
+
/**
|
| 1997 |
+
* @brief Get format descriptions for the open device.
|
| 1998 |
+
*
|
| 1999 |
+
* @note Do not modify the returned structure.
|
| 2000 |
+
*
|
| 2001 |
+
* @param devh Device handle to an open UVC device
|
| 2002 |
+
*/
|
| 2003 |
+
const uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t *devh) {
|
| 2004 |
+
return devh->info->stream_ifs->format_descs;
|
| 2005 |
+
}
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/diag.c
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
/**
|
| 35 |
+
* @defgroup diag Diagnostics
|
| 36 |
+
* @brief Interpretation of devices, error codes and negotiated stream parameters
|
| 37 |
+
*/
|
| 38 |
+
|
| 39 |
+
#include "libuvc/libuvc.h"
|
| 40 |
+
#include "libuvc/libuvc_internal.h"
|
| 41 |
+
|
| 42 |
+
/** @internal */
|
| 43 |
+
typedef struct _uvc_error_msg {
|
| 44 |
+
uvc_error_t err;
|
| 45 |
+
const char *msg;
|
| 46 |
+
} _uvc_error_msg_t;
|
| 47 |
+
|
| 48 |
+
static const _uvc_error_msg_t uvc_error_msgs[] = {
|
| 49 |
+
{UVC_SUCCESS, "Success"},
|
| 50 |
+
{UVC_ERROR_IO, "I/O error"},
|
| 51 |
+
{UVC_ERROR_INVALID_PARAM, "Invalid parameter"},
|
| 52 |
+
{UVC_ERROR_ACCESS, "Access denied"},
|
| 53 |
+
{UVC_ERROR_NO_DEVICE, "No such device"},
|
| 54 |
+
{UVC_ERROR_NOT_FOUND, "Not found"},
|
| 55 |
+
{UVC_ERROR_BUSY, "Busy"},
|
| 56 |
+
{UVC_ERROR_TIMEOUT, "Timeout"},
|
| 57 |
+
{UVC_ERROR_OVERFLOW, "Overflow"},
|
| 58 |
+
{UVC_ERROR_PIPE, "Pipe"},
|
| 59 |
+
{UVC_ERROR_INTERRUPTED, "Interrupted"},
|
| 60 |
+
{UVC_ERROR_NO_MEM, "Out of memory"},
|
| 61 |
+
{UVC_ERROR_NOT_SUPPORTED, "Not supported"},
|
| 62 |
+
{UVC_ERROR_INVALID_DEVICE, "Invalid device"},
|
| 63 |
+
{UVC_ERROR_INVALID_MODE, "Invalid mode"},
|
| 64 |
+
{UVC_ERROR_CALLBACK_EXISTS, "Callback exists"}
|
| 65 |
+
};
|
| 66 |
+
|
| 67 |
+
/** @brief Print a message explaining an error in the UVC driver
|
| 68 |
+
* @ingroup diag
|
| 69 |
+
*
|
| 70 |
+
* @param err UVC error code
|
| 71 |
+
* @param msg Optional custom message, prepended to output
|
| 72 |
+
*/
|
| 73 |
+
void uvc_perror(uvc_error_t err, const char *msg) {
|
| 74 |
+
if (msg && *msg) {
|
| 75 |
+
fputs(msg, stderr);
|
| 76 |
+
fputs(": ", stderr);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
fprintf(stderr, "%s (%d)\n", uvc_strerror(err), err);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/** @brief Return a string explaining an error in the UVC driver
|
| 83 |
+
* @ingroup diag
|
| 84 |
+
*
|
| 85 |
+
* @param err UVC error code
|
| 86 |
+
* @return error message
|
| 87 |
+
*/
|
| 88 |
+
const char* uvc_strerror(uvc_error_t err) {
|
| 89 |
+
size_t idx;
|
| 90 |
+
|
| 91 |
+
for (idx = 0; idx < sizeof(uvc_error_msgs) / sizeof(*uvc_error_msgs); ++idx) {
|
| 92 |
+
if (uvc_error_msgs[idx].err == err) {
|
| 93 |
+
return uvc_error_msgs[idx].msg;
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
return "Unknown error";
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/** @brief Print the values in a stream control block
|
| 101 |
+
* @ingroup diag
|
| 102 |
+
*
|
| 103 |
+
* @param devh UVC device
|
| 104 |
+
* @param stream Output stream (stderr if NULL)
|
| 105 |
+
*/
|
| 106 |
+
void uvc_print_stream_ctrl(uvc_stream_ctrl_t *ctrl, FILE *stream) {
|
| 107 |
+
if (stream == NULL)
|
| 108 |
+
stream = stderr;
|
| 109 |
+
|
| 110 |
+
fprintf(stream, "bmHint: %04x\n", ctrl->bmHint);
|
| 111 |
+
fprintf(stream, "bFormatIndex: %d\n", ctrl->bFormatIndex);
|
| 112 |
+
fprintf(stream, "bFrameIndex: %d\n", ctrl->bFrameIndex);
|
| 113 |
+
fprintf(stream, "dwFrameInterval: %u\n", ctrl->dwFrameInterval);
|
| 114 |
+
fprintf(stream, "wKeyFrameRate: %d\n", ctrl->wKeyFrameRate);
|
| 115 |
+
fprintf(stream, "wPFrameRate: %d\n", ctrl->wPFrameRate);
|
| 116 |
+
fprintf(stream, "wCompQuality: %d\n", ctrl->wCompQuality);
|
| 117 |
+
fprintf(stream, "wCompWindowSize: %d\n", ctrl->wCompWindowSize);
|
| 118 |
+
fprintf(stream, "wDelay: %d\n", ctrl->wDelay);
|
| 119 |
+
fprintf(stream, "dwMaxVideoFrameSize: %u\n", ctrl->dwMaxVideoFrameSize);
|
| 120 |
+
fprintf(stream, "dwMaxPayloadTransferSize: %u\n", ctrl->dwMaxPayloadTransferSize);
|
| 121 |
+
fprintf(stream, "bInterfaceNumber: %d\n", ctrl->bInterfaceNumber);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
static const char *_uvc_name_for_format_subtype(uint8_t subtype) {
|
| 125 |
+
switch (subtype) {
|
| 126 |
+
case UVC_VS_FORMAT_UNCOMPRESSED:
|
| 127 |
+
return "UncompressedFormat";
|
| 128 |
+
case UVC_VS_FORMAT_MJPEG:
|
| 129 |
+
return "MJPEGFormat";
|
| 130 |
+
case UVC_VS_FORMAT_FRAME_BASED:
|
| 131 |
+
return "FrameFormat";
|
| 132 |
+
default:
|
| 133 |
+
return "Unknown";
|
| 134 |
+
}
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
/** @brief Print camera capabilities and configuration.
|
| 138 |
+
* @ingroup diag
|
| 139 |
+
*
|
| 140 |
+
* @param devh UVC device
|
| 141 |
+
* @param stream Output stream (stderr if NULL)
|
| 142 |
+
*/
|
| 143 |
+
void uvc_print_diag(uvc_device_handle_t *devh, FILE *stream) {
|
| 144 |
+
if (stream == NULL)
|
| 145 |
+
stream = stderr;
|
| 146 |
+
|
| 147 |
+
if (devh->info->ctrl_if.bcdUVC) {
|
| 148 |
+
uvc_streaming_interface_t *stream_if;
|
| 149 |
+
int stream_idx = 0;
|
| 150 |
+
|
| 151 |
+
uvc_device_descriptor_t *desc;
|
| 152 |
+
uvc_get_device_descriptor(devh->dev, &desc);
|
| 153 |
+
|
| 154 |
+
fprintf(stream, "DEVICE CONFIGURATION (%04x:%04x/%s) ---\n",
|
| 155 |
+
desc->idVendor, desc->idProduct,
|
| 156 |
+
desc->serialNumber ? desc->serialNumber : "[none]");
|
| 157 |
+
|
| 158 |
+
uvc_free_device_descriptor(desc);
|
| 159 |
+
|
| 160 |
+
fprintf(stream, "Status: %s\n", devh->streams ? "streaming" : "idle");
|
| 161 |
+
|
| 162 |
+
fprintf(stream, "VideoControl:\n"
|
| 163 |
+
"\tbcdUVC: 0x%04x\n",
|
| 164 |
+
devh->info->ctrl_if.bcdUVC);
|
| 165 |
+
|
| 166 |
+
DL_FOREACH(devh->info->stream_ifs, stream_if) {
|
| 167 |
+
uvc_format_desc_t *fmt_desc;
|
| 168 |
+
|
| 169 |
+
++stream_idx;
|
| 170 |
+
|
| 171 |
+
fprintf(stream, "VideoStreaming(%d):\n"
|
| 172 |
+
"\tbEndpointAddress: %d\n\tFormats:\n",
|
| 173 |
+
stream_idx, stream_if->bEndpointAddress);
|
| 174 |
+
|
| 175 |
+
DL_FOREACH(stream_if->format_descs, fmt_desc) {
|
| 176 |
+
uvc_frame_desc_t *frame_desc;
|
| 177 |
+
int i;
|
| 178 |
+
|
| 179 |
+
switch (fmt_desc->bDescriptorSubtype) {
|
| 180 |
+
case UVC_VS_FORMAT_UNCOMPRESSED:
|
| 181 |
+
case UVC_VS_FORMAT_MJPEG:
|
| 182 |
+
case UVC_VS_FORMAT_FRAME_BASED:
|
| 183 |
+
fprintf(stream,
|
| 184 |
+
"\t\%s(%d)\n"
|
| 185 |
+
"\t\t bits per pixel: %d\n"
|
| 186 |
+
"\t\t GUID: ",
|
| 187 |
+
_uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype),
|
| 188 |
+
fmt_desc->bFormatIndex,
|
| 189 |
+
fmt_desc->bBitsPerPixel);
|
| 190 |
+
|
| 191 |
+
for (i = 0; i < 16; ++i)
|
| 192 |
+
fprintf(stream, "%02x", fmt_desc->guidFormat[i]);
|
| 193 |
+
|
| 194 |
+
fprintf(stream, " (%4s)\n", fmt_desc->fourccFormat );
|
| 195 |
+
|
| 196 |
+
fprintf(stream,
|
| 197 |
+
"\t\t default frame: %d\n"
|
| 198 |
+
"\t\t aspect ratio: %dx%d\n"
|
| 199 |
+
"\t\t interlace flags: %02x\n"
|
| 200 |
+
"\t\t copy protect: %02x\n",
|
| 201 |
+
fmt_desc->bDefaultFrameIndex,
|
| 202 |
+
fmt_desc->bAspectRatioX,
|
| 203 |
+
fmt_desc->bAspectRatioY,
|
| 204 |
+
fmt_desc->bmInterlaceFlags,
|
| 205 |
+
fmt_desc->bCopyProtect);
|
| 206 |
+
|
| 207 |
+
DL_FOREACH(fmt_desc->frame_descs, frame_desc) {
|
| 208 |
+
uint32_t *interval_ptr;
|
| 209 |
+
|
| 210 |
+
fprintf(stream,
|
| 211 |
+
"\t\t\tFrameDescriptor(%d)\n"
|
| 212 |
+
"\t\t\t capabilities: %02x\n"
|
| 213 |
+
"\t\t\t size: %dx%d\n"
|
| 214 |
+
"\t\t\t bit rate: %d-%d\n"
|
| 215 |
+
"\t\t\t max frame size: %d\n"
|
| 216 |
+
"\t\t\t default interval: 1/%d\n",
|
| 217 |
+
frame_desc->bFrameIndex,
|
| 218 |
+
frame_desc->bmCapabilities,
|
| 219 |
+
frame_desc->wWidth,
|
| 220 |
+
frame_desc->wHeight,
|
| 221 |
+
frame_desc->dwMinBitRate,
|
| 222 |
+
frame_desc->dwMaxBitRate,
|
| 223 |
+
frame_desc->dwMaxVideoFrameBufferSize,
|
| 224 |
+
10000000 / frame_desc->dwDefaultFrameInterval);
|
| 225 |
+
if (frame_desc->intervals) {
|
| 226 |
+
for (interval_ptr = frame_desc->intervals;
|
| 227 |
+
*interval_ptr;
|
| 228 |
+
++interval_ptr) {
|
| 229 |
+
fprintf(stream,
|
| 230 |
+
"\t\t\t interval[%d]: 1/%d\n",
|
| 231 |
+
(int) (interval_ptr - frame_desc->intervals),
|
| 232 |
+
10000000 / *interval_ptr);
|
| 233 |
+
}
|
| 234 |
+
} else {
|
| 235 |
+
fprintf(stream,
|
| 236 |
+
"\t\t\t min interval[%d] = 1/%d\n"
|
| 237 |
+
"\t\t\t max interval[%d] = 1/%d\n",
|
| 238 |
+
frame_desc->dwMinFrameInterval,
|
| 239 |
+
10000000 / frame_desc->dwMinFrameInterval,
|
| 240 |
+
frame_desc->dwMaxFrameInterval,
|
| 241 |
+
10000000 / frame_desc->dwMaxFrameInterval);
|
| 242 |
+
if (frame_desc->dwFrameIntervalStep)
|
| 243 |
+
fprintf(stream,
|
| 244 |
+
"\t\t\t interval step[%d] = 1/%d\n",
|
| 245 |
+
frame_desc->dwFrameIntervalStep,
|
| 246 |
+
10000000 / frame_desc->dwFrameIntervalStep);
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
if(fmt_desc->still_frame_desc)
|
| 250 |
+
{
|
| 251 |
+
uvc_still_frame_desc_t* still_frame_desc;
|
| 252 |
+
DL_FOREACH(fmt_desc->still_frame_desc, still_frame_desc)
|
| 253 |
+
{
|
| 254 |
+
fprintf(stream,
|
| 255 |
+
"\t\t\tStillFrameDescriptor\n"
|
| 256 |
+
"\t\t\t bEndPointAddress: %02x\n",
|
| 257 |
+
still_frame_desc->bEndPointAddress);
|
| 258 |
+
uvc_still_frame_res_t* imageSizePattern;
|
| 259 |
+
DL_FOREACH(still_frame_desc->imageSizePatterns, imageSizePattern) {
|
| 260 |
+
fprintf(stream,
|
| 261 |
+
"\t\t\t wWidth(%d) = %d\n"
|
| 262 |
+
"\t\t\t wHeight(%d) = %d\n",
|
| 263 |
+
imageSizePattern->bResolutionIndex,
|
| 264 |
+
imageSizePattern->wWidth,
|
| 265 |
+
imageSizePattern->bResolutionIndex,
|
| 266 |
+
imageSizePattern->wHeight);
|
| 267 |
+
}
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
break;
|
| 271 |
+
default:
|
| 272 |
+
fprintf(stream, "\t-UnknownFormat (%d)\n",
|
| 273 |
+
fmt_desc->bDescriptorSubtype );
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
fprintf(stream, "END DEVICE CONFIGURATION\n");
|
| 279 |
+
} else {
|
| 280 |
+
fprintf(stream, "uvc_print_diag: Device not configured!\n");
|
| 281 |
+
}
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
/** @brief Print all possible frame configuration.
|
| 285 |
+
* @ingroup diag
|
| 286 |
+
*
|
| 287 |
+
* @param devh UVC device
|
| 288 |
+
* @param stream Output stream (stderr if NULL)
|
| 289 |
+
*/
|
| 290 |
+
void uvc_print_frameformats(uvc_device_handle_t *devh) {
|
| 291 |
+
|
| 292 |
+
if (devh->info->ctrl_if.bcdUVC) {
|
| 293 |
+
uvc_streaming_interface_t *stream_if;
|
| 294 |
+
int stream_idx = 0;
|
| 295 |
+
DL_FOREACH(devh->info->stream_ifs, stream_if) {
|
| 296 |
+
uvc_format_desc_t *fmt_desc;
|
| 297 |
+
++stream_idx;
|
| 298 |
+
|
| 299 |
+
DL_FOREACH(stream_if->format_descs, fmt_desc) {
|
| 300 |
+
uvc_frame_desc_t *frame_desc;
|
| 301 |
+
int i;
|
| 302 |
+
|
| 303 |
+
switch (fmt_desc->bDescriptorSubtype) {
|
| 304 |
+
case UVC_VS_FORMAT_UNCOMPRESSED:
|
| 305 |
+
case UVC_VS_FORMAT_MJPEG:
|
| 306 |
+
case UVC_VS_FORMAT_FRAME_BASED:
|
| 307 |
+
printf(" \%s(%d)\n"
|
| 308 |
+
" bits per pixel: %d\n"
|
| 309 |
+
" GUID: ",
|
| 310 |
+
_uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype),
|
| 311 |
+
fmt_desc->bFormatIndex,
|
| 312 |
+
fmt_desc->bBitsPerPixel);
|
| 313 |
+
|
| 314 |
+
for (i = 0; i < 16; ++i)
|
| 315 |
+
printf("%02x", fmt_desc->guidFormat[i]);
|
| 316 |
+
|
| 317 |
+
printf(" (%4s)\n", fmt_desc->fourccFormat );
|
| 318 |
+
|
| 319 |
+
printf(" default frame: %d\n"
|
| 320 |
+
" aspect ratio: %dx%d\n"
|
| 321 |
+
" interlace flags: %02x\n"
|
| 322 |
+
" copy protect: %02x\n",
|
| 323 |
+
fmt_desc->bDefaultFrameIndex,
|
| 324 |
+
fmt_desc->bAspectRatioX,
|
| 325 |
+
fmt_desc->bAspectRatioY,
|
| 326 |
+
fmt_desc->bmInterlaceFlags,
|
| 327 |
+
fmt_desc->bCopyProtect);
|
| 328 |
+
|
| 329 |
+
DL_FOREACH(fmt_desc->frame_descs, frame_desc) {
|
| 330 |
+
uint32_t *interval_ptr;
|
| 331 |
+
|
| 332 |
+
printf(" FrameDescriptor(%d)\n"
|
| 333 |
+
" capabilities: %02x\n"
|
| 334 |
+
" size: %dx%d\n"
|
| 335 |
+
" bit rate: %d-%d\n"
|
| 336 |
+
" max frame size: %d\n"
|
| 337 |
+
" default interval: 1/%d\n",
|
| 338 |
+
frame_desc->bFrameIndex,
|
| 339 |
+
frame_desc->bmCapabilities,
|
| 340 |
+
frame_desc->wWidth,
|
| 341 |
+
frame_desc->wHeight,
|
| 342 |
+
frame_desc->dwMinBitRate,
|
| 343 |
+
frame_desc->dwMaxBitRate,
|
| 344 |
+
frame_desc->dwMaxVideoFrameBufferSize,
|
| 345 |
+
10000000 / frame_desc->dwDefaultFrameInterval);
|
| 346 |
+
if (frame_desc->intervals) {
|
| 347 |
+
for (interval_ptr = frame_desc->intervals;
|
| 348 |
+
*interval_ptr;
|
| 349 |
+
++interval_ptr) {
|
| 350 |
+
printf(" interval[%d]: 1/%d\n",
|
| 351 |
+
(int) (interval_ptr - frame_desc->intervals),
|
| 352 |
+
10000000 / *interval_ptr);
|
| 353 |
+
}
|
| 354 |
+
} else {
|
| 355 |
+
printf(" min interval[%d] = 1/%d\n"
|
| 356 |
+
" max interval[%d] = 1/%d\n",
|
| 357 |
+
frame_desc->dwMinFrameInterval,
|
| 358 |
+
10000000 / frame_desc->dwMinFrameInterval,
|
| 359 |
+
frame_desc->dwMaxFrameInterval,
|
| 360 |
+
10000000 / frame_desc->dwMaxFrameInterval);
|
| 361 |
+
if (frame_desc->dwFrameIntervalStep)
|
| 362 |
+
printf(" interval step[%d] = 1/%d\n",
|
| 363 |
+
frame_desc->dwFrameIntervalStep,
|
| 364 |
+
10000000 / frame_desc->dwFrameIntervalStep);
|
| 365 |
+
}
|
| 366 |
+
}
|
| 367 |
+
break;
|
| 368 |
+
default:
|
| 369 |
+
printf("\t-UnknownFormat (%d)\n",fmt_desc->bDescriptorSubtype );
|
| 370 |
+
}
|
| 371 |
+
}
|
| 372 |
+
}
|
| 373 |
+
} else {
|
| 374 |
+
printf("uvc_print_frameformats: Device not configured!\n");
|
| 375 |
+
}
|
| 376 |
+
}
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/example.c
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "libuvc/libuvc.h"
|
| 2 |
+
#include <stdio.h>
|
| 3 |
+
#include <unistd.h>
|
| 4 |
+
|
| 5 |
+
/* This callback function runs once per frame. Use it to perform any
|
| 6 |
+
* quick processing you need, or have it put the frame into your application's
|
| 7 |
+
* input queue. If this function takes too long, you'll start losing frames. */
|
| 8 |
+
void cb(uvc_frame_t *frame, void *ptr) {
|
| 9 |
+
uvc_frame_t *bgr;
|
| 10 |
+
uvc_error_t ret;
|
| 11 |
+
enum uvc_frame_format *frame_format = (enum uvc_frame_format *)ptr;
|
| 12 |
+
/* FILE *fp;
|
| 13 |
+
* static int jpeg_count = 0;
|
| 14 |
+
* static const char *H264_FILE = "iOSDevLog.h264";
|
| 15 |
+
* static const char *MJPEG_FILE = ".jpeg";
|
| 16 |
+
* char filename[16]; */
|
| 17 |
+
|
| 18 |
+
/* We'll convert the image from YUV/JPEG to BGR, so allocate space */
|
| 19 |
+
bgr = uvc_allocate_frame(frame->width * frame->height * 3);
|
| 20 |
+
if (!bgr) {
|
| 21 |
+
printf("unable to allocate bgr frame!\n");
|
| 22 |
+
return;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
printf("callback! frame_format = %d, width = %d, height = %d, length = %lu, ptr = %p\n",
|
| 26 |
+
frame->frame_format, frame->width, frame->height, frame->data_bytes, ptr);
|
| 27 |
+
|
| 28 |
+
switch (frame->frame_format) {
|
| 29 |
+
case UVC_FRAME_FORMAT_H264:
|
| 30 |
+
/* use `ffplay H264_FILE` to play */
|
| 31 |
+
/* fp = fopen(H264_FILE, "a");
|
| 32 |
+
* fwrite(frame->data, 1, frame->data_bytes, fp);
|
| 33 |
+
* fclose(fp); */
|
| 34 |
+
break;
|
| 35 |
+
case UVC_COLOR_FORMAT_MJPEG:
|
| 36 |
+
/* sprintf(filename, "%d%s", jpeg_count++, MJPEG_FILE);
|
| 37 |
+
* fp = fopen(filename, "w");
|
| 38 |
+
* fwrite(frame->data, 1, frame->data_bytes, fp);
|
| 39 |
+
* fclose(fp); */
|
| 40 |
+
break;
|
| 41 |
+
case UVC_COLOR_FORMAT_YUYV:
|
| 42 |
+
/* Do the BGR conversion */
|
| 43 |
+
ret = uvc_any2bgr(frame, bgr);
|
| 44 |
+
if (ret) {
|
| 45 |
+
uvc_perror(ret, "uvc_any2bgr");
|
| 46 |
+
uvc_free_frame(bgr);
|
| 47 |
+
return;
|
| 48 |
+
}
|
| 49 |
+
break;
|
| 50 |
+
default:
|
| 51 |
+
break;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
if (frame->sequence % 30 == 0) {
|
| 55 |
+
printf(" * got image %u\n", frame->sequence);
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
/* Call a user function:
|
| 59 |
+
*
|
| 60 |
+
* my_type *my_obj = (*my_type) ptr;
|
| 61 |
+
* my_user_function(ptr, bgr);
|
| 62 |
+
* my_other_function(ptr, bgr->data, bgr->width, bgr->height);
|
| 63 |
+
*/
|
| 64 |
+
|
| 65 |
+
/* Call a C++ method:
|
| 66 |
+
*
|
| 67 |
+
* my_type *my_obj = (*my_type) ptr;
|
| 68 |
+
* my_obj->my_func(bgr);
|
| 69 |
+
*/
|
| 70 |
+
|
| 71 |
+
/* Use opencv.highgui to display the image:
|
| 72 |
+
*
|
| 73 |
+
* cvImg = cvCreateImageHeader(
|
| 74 |
+
* cvSize(bgr->width, bgr->height),
|
| 75 |
+
* IPL_DEPTH_8U,
|
| 76 |
+
* 3);
|
| 77 |
+
*
|
| 78 |
+
* cvSetData(cvImg, bgr->data, bgr->width * 3);
|
| 79 |
+
*
|
| 80 |
+
* cvNamedWindow("Test", CV_WINDOW_AUTOSIZE);
|
| 81 |
+
* cvShowImage("Test", cvImg);
|
| 82 |
+
* cvWaitKey(10);
|
| 83 |
+
*
|
| 84 |
+
* cvReleaseImageHeader(&cvImg);
|
| 85 |
+
*/
|
| 86 |
+
|
| 87 |
+
uvc_free_frame(bgr);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
int main(int argc, char **argv) {
|
| 91 |
+
uvc_context_t *ctx;
|
| 92 |
+
uvc_device_t *dev;
|
| 93 |
+
uvc_device_handle_t *devh;
|
| 94 |
+
uvc_stream_ctrl_t ctrl;
|
| 95 |
+
uvc_error_t res;
|
| 96 |
+
|
| 97 |
+
/* Initialize a UVC service context. Libuvc will set up its own libusb
|
| 98 |
+
* context. Replace NULL with a libusb_context pointer to run libuvc
|
| 99 |
+
* from an existing libusb context. */
|
| 100 |
+
res = uvc_init(&ctx, NULL);
|
| 101 |
+
|
| 102 |
+
if (res < 0) {
|
| 103 |
+
uvc_perror(res, "uvc_init");
|
| 104 |
+
return res;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
puts("UVC initialized");
|
| 108 |
+
|
| 109 |
+
/* Locates the first attached UVC device, stores in dev */
|
| 110 |
+
res = uvc_find_device(
|
| 111 |
+
ctx, &dev,
|
| 112 |
+
0, 0, NULL); /* filter devices: vendor_id, product_id, "serial_num" */
|
| 113 |
+
|
| 114 |
+
if (res < 0) {
|
| 115 |
+
uvc_perror(res, "uvc_find_device"); /* no devices found */
|
| 116 |
+
} else {
|
| 117 |
+
puts("Device found");
|
| 118 |
+
|
| 119 |
+
/* Try to open the device: requires exclusive access */
|
| 120 |
+
res = uvc_open(dev, &devh);
|
| 121 |
+
|
| 122 |
+
if (res < 0) {
|
| 123 |
+
uvc_perror(res, "uvc_open"); /* unable to open device */
|
| 124 |
+
} else {
|
| 125 |
+
puts("Device opened");
|
| 126 |
+
|
| 127 |
+
/* Print out a message containing all the information that libuvc
|
| 128 |
+
* knows about the device */
|
| 129 |
+
uvc_print_diag(devh, stderr);
|
| 130 |
+
|
| 131 |
+
const uvc_format_desc_t *format_desc = uvc_get_format_descs(devh);
|
| 132 |
+
const uvc_frame_desc_t *frame_desc = format_desc->frame_descs;
|
| 133 |
+
enum uvc_frame_format frame_format;
|
| 134 |
+
int width = 640;
|
| 135 |
+
int height = 480;
|
| 136 |
+
int fps = 30;
|
| 137 |
+
|
| 138 |
+
switch (format_desc->bDescriptorSubtype) {
|
| 139 |
+
case UVC_VS_FORMAT_MJPEG:
|
| 140 |
+
frame_format = UVC_COLOR_FORMAT_MJPEG;
|
| 141 |
+
break;
|
| 142 |
+
case UVC_VS_FORMAT_FRAME_BASED:
|
| 143 |
+
frame_format = UVC_FRAME_FORMAT_H264;
|
| 144 |
+
break;
|
| 145 |
+
default:
|
| 146 |
+
frame_format = UVC_FRAME_FORMAT_YUYV;
|
| 147 |
+
break;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
if (frame_desc) {
|
| 151 |
+
width = frame_desc->wWidth;
|
| 152 |
+
height = frame_desc->wHeight;
|
| 153 |
+
fps = 10000000 / frame_desc->dwDefaultFrameInterval;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
printf("\nFirst format: (%4s) %dx%d %dfps\n", format_desc->fourccFormat, width, height, fps);
|
| 157 |
+
|
| 158 |
+
/* Try to negotiate first stream profile */
|
| 159 |
+
res = uvc_get_stream_ctrl_format_size(
|
| 160 |
+
devh, &ctrl, /* result stored in ctrl */
|
| 161 |
+
frame_format,
|
| 162 |
+
width, height, fps /* width, height, fps */
|
| 163 |
+
);
|
| 164 |
+
|
| 165 |
+
/* Print out the result */
|
| 166 |
+
uvc_print_stream_ctrl(&ctrl, stderr);
|
| 167 |
+
|
| 168 |
+
if (res < 0) {
|
| 169 |
+
uvc_perror(res, "get_mode"); /* device doesn't provide a matching stream */
|
| 170 |
+
} else {
|
| 171 |
+
/* Start the video stream. The library will call user function cb:
|
| 172 |
+
* cb(frame, (void *) 12345)
|
| 173 |
+
*/
|
| 174 |
+
res = uvc_start_streaming(devh, &ctrl, cb, (void *) 12345, 0);
|
| 175 |
+
|
| 176 |
+
if (res < 0) {
|
| 177 |
+
uvc_perror(res, "start_streaming"); /* unable to start stream */
|
| 178 |
+
} else {
|
| 179 |
+
puts("Streaming...");
|
| 180 |
+
|
| 181 |
+
/* enable auto exposure - see uvc_set_ae_mode documentation */
|
| 182 |
+
puts("Enabling auto exposure ...");
|
| 183 |
+
const uint8_t UVC_AUTO_EXPOSURE_MODE_AUTO = 2;
|
| 184 |
+
res = uvc_set_ae_mode(devh, UVC_AUTO_EXPOSURE_MODE_AUTO);
|
| 185 |
+
if (res == UVC_SUCCESS) {
|
| 186 |
+
puts(" ... enabled auto exposure");
|
| 187 |
+
} else if (res == UVC_ERROR_PIPE) {
|
| 188 |
+
/* this error indicates that the camera does not support the full AE mode;
|
| 189 |
+
* try again, using aperture priority mode (fixed aperture, variable exposure time) */
|
| 190 |
+
puts(" ... full AE not supported, trying aperture priority mode");
|
| 191 |
+
const uint8_t UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY = 8;
|
| 192 |
+
res = uvc_set_ae_mode(devh, UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY);
|
| 193 |
+
if (res < 0) {
|
| 194 |
+
uvc_perror(res, " ... uvc_set_ae_mode failed to enable aperture priority mode");
|
| 195 |
+
} else {
|
| 196 |
+
puts(" ... enabled aperture priority auto exposure mode");
|
| 197 |
+
}
|
| 198 |
+
} else {
|
| 199 |
+
uvc_perror(res, " ... uvc_set_ae_mode failed to enable auto exposure mode");
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
sleep(10); /* stream for 10 seconds */
|
| 203 |
+
|
| 204 |
+
/* End the stream. Blocks until last callback is serviced */
|
| 205 |
+
uvc_stop_streaming(devh);
|
| 206 |
+
puts("Done streaming.");
|
| 207 |
+
}
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
/* Release our handle on the device */
|
| 211 |
+
uvc_close(devh);
|
| 212 |
+
puts("Device closed");
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
/* Release the device descriptor */
|
| 216 |
+
uvc_unref_device(dev);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
/* Close the UVC context. This closes and cleans up any existing device handles,
|
| 220 |
+
* and it closes the libusb context if one was not provided. */
|
| 221 |
+
uvc_exit(ctx);
|
| 222 |
+
puts("UVC exited");
|
| 223 |
+
|
| 224 |
+
return 0;
|
| 225 |
+
}
|
| 226 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/frame-mjpeg.c
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2014 Robert Xiao
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
|
| 35 |
+
/**
|
| 36 |
+
* @defgroup frame Frame processing
|
| 37 |
+
*/
|
| 38 |
+
#include "libuvc/libuvc.h"
|
| 39 |
+
#include "libuvc/libuvc_internal.h"
|
| 40 |
+
#include <jpeglib.h>
|
| 41 |
+
#include <setjmp.h>
|
| 42 |
+
|
| 43 |
+
extern uvc_error_t uvc_ensure_frame_size(uvc_frame_t *frame, size_t need_bytes);
|
| 44 |
+
|
| 45 |
+
struct error_mgr {
|
| 46 |
+
struct jpeg_error_mgr super;
|
| 47 |
+
jmp_buf jmp;
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
static void _error_exit(j_common_ptr dinfo) {
|
| 51 |
+
struct error_mgr *myerr = (struct error_mgr *)dinfo->err;
|
| 52 |
+
(*dinfo->err->output_message)(dinfo);
|
| 53 |
+
longjmp(myerr->jmp, 1);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/* ISO/IEC 10918-1:1993(E) K.3.3. Default Huffman tables used by MJPEG UVC devices
|
| 57 |
+
which don't specify a Huffman table in the JPEG stream. */
|
| 58 |
+
static const unsigned char dc_lumi_len[] =
|
| 59 |
+
{0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
|
| 60 |
+
static const unsigned char dc_lumi_val[] =
|
| 61 |
+
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
| 62 |
+
|
| 63 |
+
static const unsigned char dc_chromi_len[] =
|
| 64 |
+
{0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
|
| 65 |
+
static const unsigned char dc_chromi_val[] =
|
| 66 |
+
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
| 67 |
+
|
| 68 |
+
static const unsigned char ac_lumi_len[] =
|
| 69 |
+
{0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d};
|
| 70 |
+
static const unsigned char ac_lumi_val[] =
|
| 71 |
+
{0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
|
| 72 |
+
0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71,
|
| 73 |
+
0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
|
| 74 |
+
0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
|
| 75 |
+
0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
|
| 76 |
+
0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
|
| 77 |
+
0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
| 78 |
+
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
| 79 |
+
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
|
| 80 |
+
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
|
| 81 |
+
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
|
| 82 |
+
0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
|
| 83 |
+
0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
|
| 84 |
+
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
| 85 |
+
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
|
| 86 |
+
0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
| 87 |
+
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
|
| 88 |
+
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
|
| 89 |
+
static const unsigned char ac_chromi_len[] =
|
| 90 |
+
{0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77};
|
| 91 |
+
static const unsigned char ac_chromi_val[] =
|
| 92 |
+
{0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
|
| 93 |
+
0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
|
| 94 |
+
0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
|
| 95 |
+
0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
|
| 96 |
+
0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
|
| 97 |
+
0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36,
|
| 98 |
+
0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47,
|
| 99 |
+
0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
| 100 |
+
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
| 101 |
+
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
|
| 102 |
+
0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
| 103 |
+
0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
|
| 104 |
+
0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
|
| 105 |
+
0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
|
| 106 |
+
0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
|
| 107 |
+
0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
| 108 |
+
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
| 109 |
+
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
|
| 110 |
+
|
| 111 |
+
#define COPY_HUFF_TABLE(dinfo,tbl,name) do { \
|
| 112 |
+
if(dinfo->tbl == NULL) dinfo->tbl = jpeg_alloc_huff_table((j_common_ptr)dinfo); \
|
| 113 |
+
memcpy(dinfo->tbl->bits, name##_len, sizeof(name##_len)); \
|
| 114 |
+
memset(dinfo->tbl->huffval, 0, sizeof(dinfo->tbl->huffval)); \
|
| 115 |
+
memcpy(dinfo->tbl->huffval, name##_val, sizeof(name##_val)); \
|
| 116 |
+
} while(0)
|
| 117 |
+
|
| 118 |
+
static void insert_huff_tables(j_decompress_ptr dinfo) {
|
| 119 |
+
COPY_HUFF_TABLE(dinfo, dc_huff_tbl_ptrs[0], dc_lumi);
|
| 120 |
+
COPY_HUFF_TABLE(dinfo, dc_huff_tbl_ptrs[1], dc_chromi);
|
| 121 |
+
COPY_HUFF_TABLE(dinfo, ac_huff_tbl_ptrs[0], ac_lumi);
|
| 122 |
+
COPY_HUFF_TABLE(dinfo, ac_huff_tbl_ptrs[1], ac_chromi);
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
static uvc_error_t uvc_mjpeg_convert(uvc_frame_t *in, uvc_frame_t *out) {
|
| 126 |
+
struct jpeg_decompress_struct dinfo;
|
| 127 |
+
struct error_mgr jerr;
|
| 128 |
+
size_t lines_read;
|
| 129 |
+
dinfo.err = jpeg_std_error(&jerr.super);
|
| 130 |
+
jerr.super.error_exit = _error_exit;
|
| 131 |
+
|
| 132 |
+
if (setjmp(jerr.jmp)) {
|
| 133 |
+
goto fail;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
jpeg_create_decompress(&dinfo);
|
| 137 |
+
jpeg_mem_src(&dinfo, in->data, in->data_bytes);
|
| 138 |
+
jpeg_read_header(&dinfo, TRUE);
|
| 139 |
+
|
| 140 |
+
if (dinfo.dc_huff_tbl_ptrs[0] == NULL) {
|
| 141 |
+
/* This frame is missing the Huffman tables: fill in the standard ones */
|
| 142 |
+
insert_huff_tables(&dinfo);
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
if (out->frame_format == UVC_FRAME_FORMAT_RGB)
|
| 146 |
+
dinfo.out_color_space = JCS_RGB;
|
| 147 |
+
else if (out->frame_format == UVC_FRAME_FORMAT_GRAY8)
|
| 148 |
+
dinfo.out_color_space = JCS_GRAYSCALE;
|
| 149 |
+
else
|
| 150 |
+
goto fail;
|
| 151 |
+
|
| 152 |
+
dinfo.dct_method = JDCT_IFAST;
|
| 153 |
+
|
| 154 |
+
jpeg_start_decompress(&dinfo);
|
| 155 |
+
|
| 156 |
+
lines_read = 0;
|
| 157 |
+
while (dinfo.output_scanline < dinfo.output_height) {
|
| 158 |
+
unsigned char *buffer[1] = {( unsigned char*) out->data + lines_read * out->step };
|
| 159 |
+
int num_scanlines;
|
| 160 |
+
|
| 161 |
+
num_scanlines = jpeg_read_scanlines(&dinfo, buffer, 1);
|
| 162 |
+
lines_read += num_scanlines;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
jpeg_finish_decompress(&dinfo);
|
| 166 |
+
jpeg_destroy_decompress(&dinfo);
|
| 167 |
+
return 0;
|
| 168 |
+
|
| 169 |
+
fail:
|
| 170 |
+
jpeg_destroy_decompress(&dinfo);
|
| 171 |
+
return UVC_ERROR_OTHER;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/** @brief Convert an MJPEG frame to RGB
|
| 175 |
+
* @ingroup frame
|
| 176 |
+
*
|
| 177 |
+
* @param in MJPEG frame
|
| 178 |
+
* @param out RGB frame
|
| 179 |
+
*/
|
| 180 |
+
uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out) {
|
| 181 |
+
if (in->frame_format != UVC_FRAME_FORMAT_MJPEG)
|
| 182 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 183 |
+
|
| 184 |
+
if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0)
|
| 185 |
+
return UVC_ERROR_NO_MEM;
|
| 186 |
+
|
| 187 |
+
out->width = in->width;
|
| 188 |
+
out->height = in->height;
|
| 189 |
+
out->frame_format = UVC_FRAME_FORMAT_RGB;
|
| 190 |
+
out->step = in->width * 3;
|
| 191 |
+
out->sequence = in->sequence;
|
| 192 |
+
out->capture_time = in->capture_time;
|
| 193 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 194 |
+
out->source = in->source;
|
| 195 |
+
|
| 196 |
+
return uvc_mjpeg_convert(in, out);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
/** @brief Convert an MJPEG frame to GRAY8
|
| 200 |
+
* @ingroup frame
|
| 201 |
+
*
|
| 202 |
+
* @param in MJPEG frame
|
| 203 |
+
* @param out GRAY8 frame
|
| 204 |
+
*/
|
| 205 |
+
uvc_error_t uvc_mjpeg2gray(uvc_frame_t *in, uvc_frame_t *out) {
|
| 206 |
+
if (in->frame_format != UVC_FRAME_FORMAT_MJPEG)
|
| 207 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 208 |
+
|
| 209 |
+
if (uvc_ensure_frame_size(out, in->width * in->height) < 0)
|
| 210 |
+
return UVC_ERROR_NO_MEM;
|
| 211 |
+
|
| 212 |
+
out->width = in->width;
|
| 213 |
+
out->height = in->height;
|
| 214 |
+
out->frame_format = UVC_FRAME_FORMAT_GRAY8;
|
| 215 |
+
out->step = in->width;
|
| 216 |
+
out->sequence = in->sequence;
|
| 217 |
+
out->capture_time = in->capture_time;
|
| 218 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 219 |
+
out->source = in->source;
|
| 220 |
+
|
| 221 |
+
return uvc_mjpeg_convert(in, out);
|
| 222 |
+
}
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/frame.c
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
/**
|
| 35 |
+
* @defgroup frame Frame processing
|
| 36 |
+
* @brief Tools for managing frame buffers and converting between image formats
|
| 37 |
+
*/
|
| 38 |
+
#include "libuvc/libuvc.h"
|
| 39 |
+
#include "libuvc/libuvc_internal.h"
|
| 40 |
+
|
| 41 |
+
/** @internal */
|
| 42 |
+
uvc_error_t uvc_ensure_frame_size(uvc_frame_t *frame, size_t need_bytes) {
|
| 43 |
+
if (frame->library_owns_data) {
|
| 44 |
+
if (!frame->data || frame->data_bytes != need_bytes) {
|
| 45 |
+
frame->data_bytes = need_bytes;
|
| 46 |
+
frame->data = realloc(frame->data, frame->data_bytes);
|
| 47 |
+
}
|
| 48 |
+
if (!frame->data)
|
| 49 |
+
return UVC_ERROR_NO_MEM;
|
| 50 |
+
return UVC_SUCCESS;
|
| 51 |
+
} else {
|
| 52 |
+
if (!frame->data || frame->data_bytes < need_bytes)
|
| 53 |
+
return UVC_ERROR_NO_MEM;
|
| 54 |
+
return UVC_SUCCESS;
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
/** @brief Allocate a frame structure
|
| 59 |
+
* @ingroup frame
|
| 60 |
+
*
|
| 61 |
+
* @param data_bytes Number of bytes to allocate, or zero
|
| 62 |
+
* @return New frame, or NULL on error
|
| 63 |
+
*/
|
| 64 |
+
uvc_frame_t *uvc_allocate_frame(size_t data_bytes) {
|
| 65 |
+
uvc_frame_t *frame = malloc(sizeof(*frame));
|
| 66 |
+
|
| 67 |
+
if (!frame)
|
| 68 |
+
return NULL;
|
| 69 |
+
|
| 70 |
+
memset(frame, 0, sizeof(*frame));
|
| 71 |
+
|
| 72 |
+
frame->library_owns_data = 1;
|
| 73 |
+
|
| 74 |
+
if (data_bytes > 0) {
|
| 75 |
+
frame->data_bytes = data_bytes;
|
| 76 |
+
frame->data = malloc(data_bytes);
|
| 77 |
+
|
| 78 |
+
if (!frame->data) {
|
| 79 |
+
free(frame);
|
| 80 |
+
return NULL;
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
return frame;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
/** @brief Free a frame structure
|
| 88 |
+
* @ingroup frame
|
| 89 |
+
*
|
| 90 |
+
* @param frame Frame to destroy
|
| 91 |
+
*/
|
| 92 |
+
void uvc_free_frame(uvc_frame_t *frame) {
|
| 93 |
+
if (frame->library_owns_data)
|
| 94 |
+
{
|
| 95 |
+
if (frame->data_bytes > 0)
|
| 96 |
+
free(frame->data);
|
| 97 |
+
if (frame->metadata_bytes > 0)
|
| 98 |
+
free(frame->metadata);
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
free(frame);
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
static inline unsigned char sat(int i) {
|
| 105 |
+
return (unsigned char)( i >= 255 ? 255 : (i < 0 ? 0 : i));
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
/** @brief Duplicate a frame, preserving color format
|
| 109 |
+
* @ingroup frame
|
| 110 |
+
*
|
| 111 |
+
* @param in Original frame
|
| 112 |
+
* @param out Duplicate frame
|
| 113 |
+
*/
|
| 114 |
+
uvc_error_t uvc_duplicate_frame(uvc_frame_t *in, uvc_frame_t *out) {
|
| 115 |
+
if (uvc_ensure_frame_size(out, in->data_bytes) < 0)
|
| 116 |
+
return UVC_ERROR_NO_MEM;
|
| 117 |
+
|
| 118 |
+
out->width = in->width;
|
| 119 |
+
out->height = in->height;
|
| 120 |
+
out->frame_format = in->frame_format;
|
| 121 |
+
out->step = in->step;
|
| 122 |
+
out->sequence = in->sequence;
|
| 123 |
+
out->capture_time = in->capture_time;
|
| 124 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 125 |
+
out->source = in->source;
|
| 126 |
+
|
| 127 |
+
memcpy(out->data, in->data, in->data_bytes);
|
| 128 |
+
|
| 129 |
+
if (in->metadata && in->metadata_bytes > 0)
|
| 130 |
+
{
|
| 131 |
+
if (out->metadata_bytes < in->metadata_bytes)
|
| 132 |
+
{
|
| 133 |
+
out->metadata = realloc(out->metadata, in->metadata_bytes);
|
| 134 |
+
}
|
| 135 |
+
out->metadata_bytes = in->metadata_bytes;
|
| 136 |
+
memcpy(out->metadata, in->metadata, in->metadata_bytes);
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
return UVC_SUCCESS;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
#define YUYV2RGB_2(pyuv, prgb) { \
|
| 143 |
+
float r = 1.402f * ((pyuv)[3]-128); \
|
| 144 |
+
float g = -0.34414f * ((pyuv)[1]-128) - 0.71414f * ((pyuv)[3]-128); \
|
| 145 |
+
float b = 1.772f * ((pyuv)[1]-128); \
|
| 146 |
+
(prgb)[0] = sat(pyuv[0] + r); \
|
| 147 |
+
(prgb)[1] = sat(pyuv[0] + g); \
|
| 148 |
+
(prgb)[2] = sat(pyuv[0] + b); \
|
| 149 |
+
(prgb)[3] = sat(pyuv[2] + r); \
|
| 150 |
+
(prgb)[4] = sat(pyuv[2] + g); \
|
| 151 |
+
(prgb)[5] = sat(pyuv[2] + b); \
|
| 152 |
+
}
|
| 153 |
+
#define IYUYV2RGB_2(pyuv, prgb) { \
|
| 154 |
+
int r = (22987 * ((pyuv)[3] - 128)) >> 14; \
|
| 155 |
+
int g = (-5636 * ((pyuv)[1] - 128) - 11698 * ((pyuv)[3] - 128)) >> 14; \
|
| 156 |
+
int b = (29049 * ((pyuv)[1] - 128)) >> 14; \
|
| 157 |
+
(prgb)[0] = sat(*(pyuv) + r); \
|
| 158 |
+
(prgb)[1] = sat(*(pyuv) + g); \
|
| 159 |
+
(prgb)[2] = sat(*(pyuv) + b); \
|
| 160 |
+
(prgb)[3] = sat((pyuv)[2] + r); \
|
| 161 |
+
(prgb)[4] = sat((pyuv)[2] + g); \
|
| 162 |
+
(prgb)[5] = sat((pyuv)[2] + b); \
|
| 163 |
+
}
|
| 164 |
+
#define IYUYV2RGB_16(pyuv, prgb) IYUYV2RGB_8(pyuv, prgb); IYUYV2RGB_8(pyuv + 16, prgb + 24);
|
| 165 |
+
#define IYUYV2RGB_8(pyuv, prgb) IYUYV2RGB_4(pyuv, prgb); IYUYV2RGB_4(pyuv + 8, prgb + 12);
|
| 166 |
+
#define IYUYV2RGB_4(pyuv, prgb) IYUYV2RGB_2(pyuv, prgb); IYUYV2RGB_2(pyuv + 4, prgb + 6);
|
| 167 |
+
|
| 168 |
+
/** @brief Convert a frame from YUYV to RGB
|
| 169 |
+
* @ingroup frame
|
| 170 |
+
*
|
| 171 |
+
* @param in YUYV frame
|
| 172 |
+
* @param out RGB frame
|
| 173 |
+
*/
|
| 174 |
+
uvc_error_t uvc_yuyv2rgb(uvc_frame_t *in, uvc_frame_t *out) {
|
| 175 |
+
if (in->frame_format != UVC_FRAME_FORMAT_YUYV)
|
| 176 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 177 |
+
|
| 178 |
+
if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0)
|
| 179 |
+
return UVC_ERROR_NO_MEM;
|
| 180 |
+
|
| 181 |
+
out->width = in->width;
|
| 182 |
+
out->height = in->height;
|
| 183 |
+
out->frame_format = UVC_FRAME_FORMAT_RGB;
|
| 184 |
+
out->step = in->width * 3;
|
| 185 |
+
out->sequence = in->sequence;
|
| 186 |
+
out->capture_time = in->capture_time;
|
| 187 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 188 |
+
out->source = in->source;
|
| 189 |
+
|
| 190 |
+
uint8_t *pyuv = in->data;
|
| 191 |
+
uint8_t *prgb = out->data;
|
| 192 |
+
uint8_t *prgb_end = prgb + out->data_bytes;
|
| 193 |
+
|
| 194 |
+
while (prgb < prgb_end) {
|
| 195 |
+
IYUYV2RGB_8(pyuv, prgb);
|
| 196 |
+
|
| 197 |
+
prgb += 3 * 8;
|
| 198 |
+
pyuv += 2 * 8;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
return UVC_SUCCESS;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
#define IYUYV2BGR_2(pyuv, pbgr) { \
|
| 205 |
+
int r = (22987 * ((pyuv)[3] - 128)) >> 14; \
|
| 206 |
+
int g = (-5636 * ((pyuv)[1] - 128) - 11698 * ((pyuv)[3] - 128)) >> 14; \
|
| 207 |
+
int b = (29049 * ((pyuv)[1] - 128)) >> 14; \
|
| 208 |
+
(pbgr)[0] = sat(*(pyuv) + b); \
|
| 209 |
+
(pbgr)[1] = sat(*(pyuv) + g); \
|
| 210 |
+
(pbgr)[2] = sat(*(pyuv) + r); \
|
| 211 |
+
(pbgr)[3] = sat((pyuv)[2] + b); \
|
| 212 |
+
(pbgr)[4] = sat((pyuv)[2] + g); \
|
| 213 |
+
(pbgr)[5] = sat((pyuv)[2] + r); \
|
| 214 |
+
}
|
| 215 |
+
#define IYUYV2BGR_16(pyuv, pbgr) IYUYV2BGR_8(pyuv, pbgr); IYUYV2BGR_8(pyuv + 16, pbgr + 24);
|
| 216 |
+
#define IYUYV2BGR_8(pyuv, pbgr) IYUYV2BGR_4(pyuv, pbgr); IYUYV2BGR_4(pyuv + 8, pbgr + 12);
|
| 217 |
+
#define IYUYV2BGR_4(pyuv, pbgr) IYUYV2BGR_2(pyuv, pbgr); IYUYV2BGR_2(pyuv + 4, pbgr + 6);
|
| 218 |
+
|
| 219 |
+
/** @brief Convert a frame from YUYV to BGR
|
| 220 |
+
* @ingroup frame
|
| 221 |
+
*
|
| 222 |
+
* @param in YUYV frame
|
| 223 |
+
* @param out BGR frame
|
| 224 |
+
*/
|
| 225 |
+
uvc_error_t uvc_yuyv2bgr(uvc_frame_t *in, uvc_frame_t *out) {
|
| 226 |
+
if (in->frame_format != UVC_FRAME_FORMAT_YUYV)
|
| 227 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 228 |
+
|
| 229 |
+
if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0)
|
| 230 |
+
return UVC_ERROR_NO_MEM;
|
| 231 |
+
|
| 232 |
+
out->width = in->width;
|
| 233 |
+
out->height = in->height;
|
| 234 |
+
out->frame_format = UVC_FRAME_FORMAT_BGR;
|
| 235 |
+
out->step = in->width * 3;
|
| 236 |
+
out->sequence = in->sequence;
|
| 237 |
+
out->capture_time = in->capture_time;
|
| 238 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 239 |
+
out->source = in->source;
|
| 240 |
+
|
| 241 |
+
uint8_t *pyuv = in->data;
|
| 242 |
+
uint8_t *pbgr = out->data;
|
| 243 |
+
uint8_t *pbgr_end = pbgr + out->data_bytes;
|
| 244 |
+
|
| 245 |
+
while (pbgr < pbgr_end) {
|
| 246 |
+
IYUYV2BGR_8(pyuv, pbgr);
|
| 247 |
+
|
| 248 |
+
pbgr += 3 * 8;
|
| 249 |
+
pyuv += 2 * 8;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
return UVC_SUCCESS;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
#define IYUYV2Y(pyuv, py) { \
|
| 256 |
+
(py)[0] = (pyuv[0]); \
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
/** @brief Convert a frame from YUYV to Y (GRAY8)
|
| 260 |
+
* @ingroup frame
|
| 261 |
+
*
|
| 262 |
+
* @param in YUYV frame
|
| 263 |
+
* @param out GRAY8 frame
|
| 264 |
+
*/
|
| 265 |
+
uvc_error_t uvc_yuyv2y(uvc_frame_t *in, uvc_frame_t *out) {
|
| 266 |
+
if (in->frame_format != UVC_FRAME_FORMAT_YUYV)
|
| 267 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 268 |
+
|
| 269 |
+
if (uvc_ensure_frame_size(out, in->width * in->height) < 0)
|
| 270 |
+
return UVC_ERROR_NO_MEM;
|
| 271 |
+
|
| 272 |
+
out->width = in->width;
|
| 273 |
+
out->height = in->height;
|
| 274 |
+
out->frame_format = UVC_FRAME_FORMAT_GRAY8;
|
| 275 |
+
out->step = in->width;
|
| 276 |
+
out->sequence = in->sequence;
|
| 277 |
+
out->capture_time = in->capture_time;
|
| 278 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 279 |
+
out->source = in->source;
|
| 280 |
+
|
| 281 |
+
uint8_t *pyuv = in->data;
|
| 282 |
+
uint8_t *py = out->data;
|
| 283 |
+
uint8_t *py_end = py + out->data_bytes;
|
| 284 |
+
|
| 285 |
+
while (py < py_end) {
|
| 286 |
+
IYUYV2Y(pyuv, py);
|
| 287 |
+
|
| 288 |
+
py += 1;
|
| 289 |
+
pyuv += 2;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
return UVC_SUCCESS;
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
#define IYUYV2UV(pyuv, puv) { \
|
| 296 |
+
(puv)[0] = (pyuv[1]); \
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
/** @brief Convert a frame from YUYV to UV (GRAY8)
|
| 300 |
+
* @ingroup frame
|
| 301 |
+
*
|
| 302 |
+
* @param in YUYV frame
|
| 303 |
+
* @param out GRAY8 frame
|
| 304 |
+
*/
|
| 305 |
+
uvc_error_t uvc_yuyv2uv(uvc_frame_t *in, uvc_frame_t *out) {
|
| 306 |
+
if (in->frame_format != UVC_FRAME_FORMAT_YUYV)
|
| 307 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 308 |
+
|
| 309 |
+
if (uvc_ensure_frame_size(out, in->width * in->height) < 0)
|
| 310 |
+
return UVC_ERROR_NO_MEM;
|
| 311 |
+
|
| 312 |
+
out->width = in->width;
|
| 313 |
+
out->height = in->height;
|
| 314 |
+
out->frame_format = UVC_FRAME_FORMAT_GRAY8;
|
| 315 |
+
out->step = in->width;
|
| 316 |
+
out->sequence = in->sequence;
|
| 317 |
+
out->capture_time = in->capture_time;
|
| 318 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 319 |
+
out->source = in->source;
|
| 320 |
+
|
| 321 |
+
uint8_t *pyuv = in->data;
|
| 322 |
+
uint8_t *puv = out->data;
|
| 323 |
+
uint8_t *puv_end = puv + out->data_bytes;
|
| 324 |
+
|
| 325 |
+
while (puv < puv_end) {
|
| 326 |
+
IYUYV2UV(pyuv, puv);
|
| 327 |
+
|
| 328 |
+
puv += 1;
|
| 329 |
+
pyuv += 2;
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
return UVC_SUCCESS;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
#define IUYVY2RGB_2(pyuv, prgb) { \
|
| 336 |
+
int r = (22987 * ((pyuv)[2] - 128)) >> 14; \
|
| 337 |
+
int g = (-5636 * ((pyuv)[0] - 128) - 11698 * ((pyuv)[2] - 128)) >> 14; \
|
| 338 |
+
int b = (29049 * ((pyuv)[0] - 128)) >> 14; \
|
| 339 |
+
(prgb)[0] = sat((pyuv)[1] + r); \
|
| 340 |
+
(prgb)[1] = sat((pyuv)[1] + g); \
|
| 341 |
+
(prgb)[2] = sat((pyuv)[1] + b); \
|
| 342 |
+
(prgb)[3] = sat((pyuv)[3] + r); \
|
| 343 |
+
(prgb)[4] = sat((pyuv)[3] + g); \
|
| 344 |
+
(prgb)[5] = sat((pyuv)[3] + b); \
|
| 345 |
+
}
|
| 346 |
+
#define IUYVY2RGB_16(pyuv, prgb) IUYVY2RGB_8(pyuv, prgb); IUYVY2RGB_8(pyuv + 16, prgb + 24);
|
| 347 |
+
#define IUYVY2RGB_8(pyuv, prgb) IUYVY2RGB_4(pyuv, prgb); IUYVY2RGB_4(pyuv + 8, prgb + 12);
|
| 348 |
+
#define IUYVY2RGB_4(pyuv, prgb) IUYVY2RGB_2(pyuv, prgb); IUYVY2RGB_2(pyuv + 4, prgb + 6);
|
| 349 |
+
|
| 350 |
+
/** @brief Convert a frame from UYVY to RGB
|
| 351 |
+
* @ingroup frame
|
| 352 |
+
* @param ini UYVY frame
|
| 353 |
+
* @param out RGB frame
|
| 354 |
+
*/
|
| 355 |
+
uvc_error_t uvc_uyvy2rgb(uvc_frame_t *in, uvc_frame_t *out) {
|
| 356 |
+
if (in->frame_format != UVC_FRAME_FORMAT_UYVY)
|
| 357 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 358 |
+
|
| 359 |
+
if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0)
|
| 360 |
+
return UVC_ERROR_NO_MEM;
|
| 361 |
+
|
| 362 |
+
out->width = in->width;
|
| 363 |
+
out->height = in->height;
|
| 364 |
+
out->frame_format = UVC_FRAME_FORMAT_RGB;
|
| 365 |
+
out->step = in->width *3;
|
| 366 |
+
out->sequence = in->sequence;
|
| 367 |
+
out->capture_time = in->capture_time;
|
| 368 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 369 |
+
out->source = in->source;
|
| 370 |
+
|
| 371 |
+
uint8_t *pyuv = in->data;
|
| 372 |
+
uint8_t *prgb = out->data;
|
| 373 |
+
uint8_t *prgb_end = prgb + out->data_bytes;
|
| 374 |
+
|
| 375 |
+
while (prgb < prgb_end) {
|
| 376 |
+
IUYVY2RGB_8(pyuv, prgb);
|
| 377 |
+
|
| 378 |
+
prgb += 3 * 8;
|
| 379 |
+
pyuv += 2 * 8;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
return UVC_SUCCESS;
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
#define IUYVY2BGR_2(pyuv, pbgr) { \
|
| 386 |
+
int r = (22987 * ((pyuv)[2] - 128)) >> 14; \
|
| 387 |
+
int g = (-5636 * ((pyuv)[0] - 128) - 11698 * ((pyuv)[2] - 128)) >> 14; \
|
| 388 |
+
int b = (29049 * ((pyuv)[0] - 128)) >> 14; \
|
| 389 |
+
(pbgr)[0] = sat((pyuv)[1] + b); \
|
| 390 |
+
(pbgr)[1] = sat((pyuv)[1] + g); \
|
| 391 |
+
(pbgr)[2] = sat((pyuv)[1] + r); \
|
| 392 |
+
(pbgr)[3] = sat((pyuv)[3] + b); \
|
| 393 |
+
(pbgr)[4] = sat((pyuv)[3] + g); \
|
| 394 |
+
(pbgr)[5] = sat((pyuv)[3] + r); \
|
| 395 |
+
}
|
| 396 |
+
#define IUYVY2BGR_16(pyuv, pbgr) IUYVY2BGR_8(pyuv, pbgr); IUYVY2BGR_8(pyuv + 16, pbgr + 24);
|
| 397 |
+
#define IUYVY2BGR_8(pyuv, pbgr) IUYVY2BGR_4(pyuv, pbgr); IUYVY2BGR_4(pyuv + 8, pbgr + 12);
|
| 398 |
+
#define IUYVY2BGR_4(pyuv, pbgr) IUYVY2BGR_2(pyuv, pbgr); IUYVY2BGR_2(pyuv + 4, pbgr + 6);
|
| 399 |
+
|
| 400 |
+
/** @brief Convert a frame from UYVY to BGR
|
| 401 |
+
* @ingroup frame
|
| 402 |
+
* @param ini UYVY frame
|
| 403 |
+
* @param out BGR frame
|
| 404 |
+
*/
|
| 405 |
+
uvc_error_t uvc_uyvy2bgr(uvc_frame_t *in, uvc_frame_t *out) {
|
| 406 |
+
if (in->frame_format != UVC_FRAME_FORMAT_UYVY)
|
| 407 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 408 |
+
|
| 409 |
+
if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0)
|
| 410 |
+
return UVC_ERROR_NO_MEM;
|
| 411 |
+
|
| 412 |
+
out->width = in->width;
|
| 413 |
+
out->height = in->height;
|
| 414 |
+
out->frame_format = UVC_FRAME_FORMAT_BGR;
|
| 415 |
+
out->step = in->width *3;
|
| 416 |
+
out->sequence = in->sequence;
|
| 417 |
+
out->capture_time = in->capture_time;
|
| 418 |
+
out->capture_time_finished = in->capture_time_finished;
|
| 419 |
+
out->source = in->source;
|
| 420 |
+
|
| 421 |
+
uint8_t *pyuv = in->data;
|
| 422 |
+
uint8_t *pbgr = out->data;
|
| 423 |
+
uint8_t *pbgr_end = pbgr + out->data_bytes;
|
| 424 |
+
|
| 425 |
+
while (pbgr < pbgr_end) {
|
| 426 |
+
IUYVY2BGR_8(pyuv, pbgr);
|
| 427 |
+
|
| 428 |
+
pbgr += 3 * 8;
|
| 429 |
+
pyuv += 2 * 8;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
return UVC_SUCCESS;
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
/** @brief Convert a frame to RGB
|
| 436 |
+
* @ingroup frame
|
| 437 |
+
*
|
| 438 |
+
* @param in non-RGB frame
|
| 439 |
+
* @param out RGB frame
|
| 440 |
+
*/
|
| 441 |
+
uvc_error_t uvc_any2rgb(uvc_frame_t *in, uvc_frame_t *out) {
|
| 442 |
+
switch (in->frame_format) {
|
| 443 |
+
#ifdef LIBUVC_HAS_JPEG
|
| 444 |
+
case UVC_FRAME_FORMAT_MJPEG:
|
| 445 |
+
return uvc_mjpeg2rgb(in, out);
|
| 446 |
+
#endif
|
| 447 |
+
case UVC_FRAME_FORMAT_YUYV:
|
| 448 |
+
return uvc_yuyv2rgb(in, out);
|
| 449 |
+
case UVC_FRAME_FORMAT_UYVY:
|
| 450 |
+
return uvc_uyvy2rgb(in, out);
|
| 451 |
+
case UVC_FRAME_FORMAT_RGB:
|
| 452 |
+
return uvc_duplicate_frame(in, out);
|
| 453 |
+
default:
|
| 454 |
+
return UVC_ERROR_NOT_SUPPORTED;
|
| 455 |
+
}
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
/** @brief Convert a frame to BGR
|
| 459 |
+
* @ingroup frame
|
| 460 |
+
*
|
| 461 |
+
* @param in non-BGR frame
|
| 462 |
+
* @param out BGR frame
|
| 463 |
+
*/
|
| 464 |
+
uvc_error_t uvc_any2bgr(uvc_frame_t *in, uvc_frame_t *out) {
|
| 465 |
+
switch (in->frame_format) {
|
| 466 |
+
case UVC_FRAME_FORMAT_YUYV:
|
| 467 |
+
return uvc_yuyv2bgr(in, out);
|
| 468 |
+
case UVC_FRAME_FORMAT_UYVY:
|
| 469 |
+
return uvc_uyvy2bgr(in, out);
|
| 470 |
+
case UVC_FRAME_FORMAT_BGR:
|
| 471 |
+
return uvc_duplicate_frame(in, out);
|
| 472 |
+
default:
|
| 473 |
+
return UVC_ERROR_NOT_SUPPORTED;
|
| 474 |
+
}
|
| 475 |
+
}
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/init.c
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
/**
|
| 35 |
+
\mainpage libuvc: a cross-platform library for USB video devices
|
| 36 |
+
|
| 37 |
+
\b libuvc is a library that supports enumeration, control and streaming
|
| 38 |
+
for USB Video Class (UVC) devices, such as consumer webcams.
|
| 39 |
+
|
| 40 |
+
\section features Features
|
| 41 |
+
\li UVC device \ref device "discovery and management" API
|
| 42 |
+
\li \ref streaming "Video streaming" (device to host) with asynchronous/callback and synchronous/polling modes
|
| 43 |
+
\li Read/write access to standard \ref ctrl "device settings"
|
| 44 |
+
\li \ref frame "Conversion" between various formats: RGB, YUV, JPEG, etc.
|
| 45 |
+
\li Tested on Mac and Linux, portable to Windows and some BSDs
|
| 46 |
+
|
| 47 |
+
\section roadmap Roadmap
|
| 48 |
+
\li Bulk-mode image capture
|
| 49 |
+
\li One-shot image capture
|
| 50 |
+
\li Improved support for standard settings
|
| 51 |
+
\li Support for "extended" (vendor-defined) settings
|
| 52 |
+
|
| 53 |
+
\section misc Misc.
|
| 54 |
+
\p The source code can be found at https://github.com/libuvc/libuvc/. To build
|
| 55 |
+
the library, install <a href="https://libusb.info/">libusb</a> 1.0+ and run:
|
| 56 |
+
|
| 57 |
+
\code
|
| 58 |
+
$ git clone https://github.com/libuvc/libuvc.git
|
| 59 |
+
$ cd libuvc
|
| 60 |
+
$ mkdir build
|
| 61 |
+
$ cd build
|
| 62 |
+
$ cmake -DCMAKE_BUILD_TYPE=Release ..
|
| 63 |
+
$ make && make install
|
| 64 |
+
\endcode
|
| 65 |
+
|
| 66 |
+
\section Example
|
| 67 |
+
In this example, libuvc is used to acquire images in a 30 fps, 640x480
|
| 68 |
+
YUV stream from a UVC device such as a standard webcam.
|
| 69 |
+
|
| 70 |
+
\include example.c
|
| 71 |
+
|
| 72 |
+
*/
|
| 73 |
+
|
| 74 |
+
/**
|
| 75 |
+
* @defgroup init Library initialization/deinitialization
|
| 76 |
+
* @brief Setup routines used to construct UVC access contexts
|
| 77 |
+
*/
|
| 78 |
+
#include "libuvc/libuvc.h"
|
| 79 |
+
#include "libuvc/libuvc_internal.h"
|
| 80 |
+
|
| 81 |
+
/** @internal
|
| 82 |
+
* @brief Event handler thread
|
| 83 |
+
* There's one of these per UVC context.
|
| 84 |
+
* @todo We shouldn't run this if we don't own the USB context
|
| 85 |
+
*/
|
| 86 |
+
void *_uvc_handle_events(void *arg) {
|
| 87 |
+
uvc_context_t *ctx = (uvc_context_t *) arg;
|
| 88 |
+
|
| 89 |
+
while (!ctx->kill_handler_thread)
|
| 90 |
+
libusb_handle_events_completed(ctx->usb_ctx, &ctx->kill_handler_thread);
|
| 91 |
+
return NULL;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
/** @brief Initializes the UVC context
|
| 95 |
+
* @ingroup init
|
| 96 |
+
*
|
| 97 |
+
* @note If you provide your own USB context, you must handle
|
| 98 |
+
* libusb event processing using a function such as libusb_handle_events.
|
| 99 |
+
*
|
| 100 |
+
* @param[out] pctx The location where the context reference should be stored.
|
| 101 |
+
* @param[in] usb_ctx Optional USB context to use
|
| 102 |
+
* @return Error opening context or UVC_SUCCESS
|
| 103 |
+
*/
|
| 104 |
+
uvc_error_t uvc_init(uvc_context_t **pctx, struct libusb_context *usb_ctx) {
|
| 105 |
+
uvc_error_t ret = UVC_SUCCESS;
|
| 106 |
+
uvc_context_t *ctx = calloc(1, sizeof(*ctx));
|
| 107 |
+
|
| 108 |
+
if (usb_ctx == NULL) {
|
| 109 |
+
ret = libusb_init(&ctx->usb_ctx);
|
| 110 |
+
ctx->own_usb_ctx = 1;
|
| 111 |
+
if (ret != UVC_SUCCESS) {
|
| 112 |
+
free(ctx);
|
| 113 |
+
ctx = NULL;
|
| 114 |
+
}
|
| 115 |
+
} else {
|
| 116 |
+
ctx->own_usb_ctx = 0;
|
| 117 |
+
ctx->usb_ctx = usb_ctx;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
if (ctx != NULL)
|
| 121 |
+
*pctx = ctx;
|
| 122 |
+
|
| 123 |
+
return ret;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/**
|
| 127 |
+
* @brief Closes the UVC context, shutting down any active cameras.
|
| 128 |
+
* @ingroup init
|
| 129 |
+
*
|
| 130 |
+
* @note This function invalides any existing references to the context's
|
| 131 |
+
* cameras.
|
| 132 |
+
*
|
| 133 |
+
* If no USB context was provided to #uvc_init, the UVC-specific USB
|
| 134 |
+
* context will be destroyed.
|
| 135 |
+
*
|
| 136 |
+
* @param ctx UVC context to shut down
|
| 137 |
+
*/
|
| 138 |
+
void uvc_exit(uvc_context_t *ctx) {
|
| 139 |
+
uvc_device_handle_t *devh;
|
| 140 |
+
|
| 141 |
+
DL_FOREACH(ctx->open_devices, devh) {
|
| 142 |
+
uvc_close(devh);
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
if (ctx->own_usb_ctx)
|
| 146 |
+
libusb_exit(ctx->usb_ctx);
|
| 147 |
+
|
| 148 |
+
free(ctx);
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
/**
|
| 152 |
+
* @internal
|
| 153 |
+
* @brief Spawns a handler thread for the context
|
| 154 |
+
* @ingroup init
|
| 155 |
+
*
|
| 156 |
+
* This should be called at the end of a successful uvc_open if no devices
|
| 157 |
+
* are already open (and being handled).
|
| 158 |
+
*/
|
| 159 |
+
void uvc_start_handler_thread(uvc_context_t *ctx) {
|
| 160 |
+
if (ctx->own_usb_ctx)
|
| 161 |
+
pthread_create(&ctx->handler_thread, NULL, _uvc_handle_events, (void*) ctx);
|
| 162 |
+
}
|
| 163 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/misc.c
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
#include <string.h>
|
| 35 |
+
#include <stdlib.h>
|
| 36 |
+
|
| 37 |
+
#if __APPLE__
|
| 38 |
+
char *strndup(const char *s, size_t n) {
|
| 39 |
+
size_t src_n = 0;
|
| 40 |
+
const char *sp = s;
|
| 41 |
+
char *d;
|
| 42 |
+
|
| 43 |
+
while (*sp++)
|
| 44 |
+
src_n++;
|
| 45 |
+
|
| 46 |
+
if (src_n < n)
|
| 47 |
+
n = src_n;
|
| 48 |
+
|
| 49 |
+
d = malloc(n + 1);
|
| 50 |
+
|
| 51 |
+
memcpy(d, s, n);
|
| 52 |
+
|
| 53 |
+
d[n] = '\0';
|
| 54 |
+
|
| 55 |
+
return d;
|
| 56 |
+
}
|
| 57 |
+
#endif
|
| 58 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/stream.c
ADDED
|
@@ -0,0 +1,1752 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
/**
|
| 35 |
+
* @defgroup streaming Streaming control functions
|
| 36 |
+
* @brief Tools for creating, managing and consuming video streams
|
| 37 |
+
*/
|
| 38 |
+
|
| 39 |
+
#include "libuvc/libuvc.h"
|
| 40 |
+
#include "libuvc/libuvc_internal.h"
|
| 41 |
+
#include "errno.h"
|
| 42 |
+
|
| 43 |
+
#ifdef _MSC_VER
|
| 44 |
+
|
| 45 |
+
#define DELTA_EPOCH_IN_MICROSECS 116444736000000000Ui64
|
| 46 |
+
|
| 47 |
+
// gettimeofday - get time of day for Windows;
|
| 48 |
+
// A gettimeofday implementation for Microsoft Windows;
|
| 49 |
+
// Public domain code, author "ponnada";
|
| 50 |
+
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
| 51 |
+
{
|
| 52 |
+
FILETIME ft;
|
| 53 |
+
unsigned __int64 tmpres = 0;
|
| 54 |
+
static int tzflag = 0;
|
| 55 |
+
if (NULL != tv)
|
| 56 |
+
{
|
| 57 |
+
GetSystemTimeAsFileTime(&ft);
|
| 58 |
+
tmpres |= ft.dwHighDateTime;
|
| 59 |
+
tmpres <<= 32;
|
| 60 |
+
tmpres |= ft.dwLowDateTime;
|
| 61 |
+
tmpres /= 10;
|
| 62 |
+
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
| 63 |
+
tv->tv_sec = (long)(tmpres / 1000000UL);
|
| 64 |
+
tv->tv_usec = (long)(tmpres % 1000000UL);
|
| 65 |
+
}
|
| 66 |
+
return 0;
|
| 67 |
+
}
|
| 68 |
+
#endif // _MSC_VER
|
| 69 |
+
uvc_frame_desc_t *uvc_find_frame_desc_stream(uvc_stream_handle_t *strmh,
|
| 70 |
+
uint16_t format_id, uint16_t frame_id);
|
| 71 |
+
uvc_frame_desc_t *uvc_find_frame_desc(uvc_device_handle_t *devh,
|
| 72 |
+
uint16_t format_id, uint16_t frame_id);
|
| 73 |
+
void *_uvc_user_caller(void *arg);
|
| 74 |
+
void _uvc_populate_frame(uvc_stream_handle_t *strmh);
|
| 75 |
+
|
| 76 |
+
static uvc_streaming_interface_t *_uvc_get_stream_if(uvc_device_handle_t *devh, int interface_idx);
|
| 77 |
+
static uvc_stream_handle_t *_uvc_get_stream_by_interface(uvc_device_handle_t *devh, int interface_idx);
|
| 78 |
+
static enum uvc_frame_format uvc_frame_format_for_guid(uint8_t guid[16]);
|
| 79 |
+
|
| 80 |
+
/*
|
| 81 |
+
* The project cameras advertise a larger UVC payload than they need for the
|
| 82 |
+
* production MJPEG modes. The workaround must update the UVC COMMIT control
|
| 83 |
+
* before the host selects an altsetting; changing only libusb's transfer
|
| 84 |
+
* geometry leaves the camera's negotiated payload at its oversized value.
|
| 85 |
+
* This is deliberately an exact VID/PID + format + size + frame-rate match.
|
| 86 |
+
*/
|
| 87 |
+
struct ksq_altsetting_quirk {
|
| 88 |
+
uint16_t vid;
|
| 89 |
+
uint16_t pid;
|
| 90 |
+
uint8_t streaming_interface;
|
| 91 |
+
uint8_t forced_altsetting;
|
| 92 |
+
uint16_t payload;
|
| 93 |
+
uint16_t width;
|
| 94 |
+
uint16_t height;
|
| 95 |
+
};
|
| 96 |
+
|
| 97 |
+
static const struct ksq_altsetting_quirk ksq_altsetting_quirks[] = {
|
| 98 |
+
/* 2026-08-28 alt-3 verification: Sightac alt4/1600 -> alt3/800 (5-cam USB
|
| 99 |
+
* budget). Revert this line to {0x0c45, 0x636f, 1, 4, 1600, 640, 480} to
|
| 100 |
+
* restore the production 2-cam-per-set mode. */
|
| 101 |
+
{0x0c45, 0x636f, 1, 3, 800, 640, 480},
|
| 102 |
+
/* 2026-09-15 DECXIN default is alt6/944, the largest single-transaction
|
| 103 |
+
* setting. It is the safe fallback, not the preferred mode: it only fits
|
| 104 |
+
* 8 x 32 x 944 = 241664 B per frame against a measured worst case of
|
| 105 |
+
* 234148 B, so a busier scene truncates frames, while alt7/1280 fits
|
| 106 |
+
* 327680 B. The service therefore pins alt7/1280 per device through
|
| 107 |
+
* uvc_set_altsetting_override() on controllers that can carry it, and
|
| 108 |
+
* drops back to this entry when it cannot. alt7 is a high-bandwidth
|
| 109 |
+
* isochronous setting (2 transactions per microframe) that stalls the AMD
|
| 110 |
+
* 1022:15b7 controller within 45..130 s, every time. */
|
| 111 |
+
{0x1bcf, 0x2d4f, 1, 6, 944, 1280, 960},
|
| 112 |
+
};
|
| 113 |
+
|
| 114 |
+
/* The altsetting/payload pair a stream should be opened with, either from the
|
| 115 |
+
* caller's per-device override or, failing that, from the table above. */
|
| 116 |
+
struct ksq_altsetting_choice {
|
| 117 |
+
uint16_t vid;
|
| 118 |
+
uint16_t pid;
|
| 119 |
+
uint8_t altsetting;
|
| 120 |
+
uint16_t payload;
|
| 121 |
+
int from_override;
|
| 122 |
+
};
|
| 123 |
+
|
| 124 |
+
/* Returns 1 and fills @p out when a setting was resolved, 0 when the stream
|
| 125 |
+
* should be opened with libuvc's own altsetting search. */
|
| 126 |
+
static int _ksq_resolve_altsetting_for_ctrl(
|
| 127 |
+
uvc_device_handle_t *devh, const uvc_stream_ctrl_t *ctrl,
|
| 128 |
+
enum uvc_frame_format frame_format, uvc_frame_desc_t *frame_desc,
|
| 129 |
+
struct ksq_altsetting_choice *out) {
|
| 130 |
+
struct libusb_device_descriptor descriptor;
|
| 131 |
+
int have_descriptor;
|
| 132 |
+
size_t index;
|
| 133 |
+
|
| 134 |
+
if (devh == NULL || ctrl == NULL || frame_desc == NULL || out == NULL) {
|
| 135 |
+
return 0;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
/* An explicit override wins over the table: the table is a per-model
|
| 139 |
+
* default, the override is a per-machine measurement of what this host
|
| 140 |
+
* controller can carry. It is deliberately not gated on format/size/
|
| 141 |
+
* interval -- the caller already validated the mode it asked for, and the
|
| 142 |
+
* endpoint check in uvc_stream_start() still rejects an altsetting that the
|
| 143 |
+
* streaming interface does not actually have. */
|
| 144 |
+
if (devh->altsetting_override_valid) {
|
| 145 |
+
have_descriptor =
|
| 146 |
+
libusb_get_device_descriptor(devh->dev->usb_dev, &descriptor) == 0;
|
| 147 |
+
out->vid = have_descriptor ? descriptor.idVendor : 0;
|
| 148 |
+
out->pid = have_descriptor ? descriptor.idProduct : 0;
|
| 149 |
+
out->altsetting = devh->altsetting_override_altsetting;
|
| 150 |
+
out->payload = devh->altsetting_override_payload;
|
| 151 |
+
out->from_override = 1;
|
| 152 |
+
return 1;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
if (frame_format != UVC_FRAME_FORMAT_MJPEG ||
|
| 156 |
+
ctrl->dwFrameInterval < 330000 || ctrl->dwFrameInterval > 340000 ||
|
| 157 |
+
frame_desc->parent == NULL || frame_desc->parent->parent == NULL ||
|
| 158 |
+
libusb_get_device_descriptor(devh->dev->usb_dev, &descriptor) != 0) {
|
| 159 |
+
return 0;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
for (index = 0;
|
| 163 |
+
index < sizeof(ksq_altsetting_quirks) /
|
| 164 |
+
sizeof(ksq_altsetting_quirks[0]);
|
| 165 |
+
++index) {
|
| 166 |
+
const struct ksq_altsetting_quirk *quirk = &ksq_altsetting_quirks[index];
|
| 167 |
+
if (descriptor.idVendor == quirk->vid &&
|
| 168 |
+
descriptor.idProduct == quirk->pid &&
|
| 169 |
+
frame_desc->wWidth == quirk->width &&
|
| 170 |
+
frame_desc->wHeight == quirk->height &&
|
| 171 |
+
frame_desc->parent->parent->bInterfaceNumber ==
|
| 172 |
+
quirk->streaming_interface) {
|
| 173 |
+
out->vid = quirk->vid;
|
| 174 |
+
out->pid = quirk->pid;
|
| 175 |
+
out->altsetting = quirk->forced_altsetting;
|
| 176 |
+
out->payload = quirk->payload;
|
| 177 |
+
out->from_override = 0;
|
| 178 |
+
return 1;
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
return 0;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
struct format_table_entry {
|
| 185 |
+
enum uvc_frame_format format;
|
| 186 |
+
uint8_t abstract_fmt;
|
| 187 |
+
uint8_t guid[16];
|
| 188 |
+
int children_count;
|
| 189 |
+
enum uvc_frame_format *children;
|
| 190 |
+
};
|
| 191 |
+
|
| 192 |
+
struct format_table_entry *_get_format_entry(enum uvc_frame_format format) {
|
| 193 |
+
#define ABS_FMT(_fmt, _num, ...) \
|
| 194 |
+
case _fmt: { \
|
| 195 |
+
static enum uvc_frame_format _fmt##_children[] = __VA_ARGS__; \
|
| 196 |
+
static struct format_table_entry _fmt##_entry = { \
|
| 197 |
+
_fmt, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, _num, _fmt##_children }; \
|
| 198 |
+
return &_fmt##_entry; }
|
| 199 |
+
|
| 200 |
+
#define FMT(_fmt, ...) \
|
| 201 |
+
case _fmt: { \
|
| 202 |
+
static struct format_table_entry _fmt##_entry = { \
|
| 203 |
+
_fmt, 0, __VA_ARGS__, 0, NULL }; \
|
| 204 |
+
return &_fmt##_entry; }
|
| 205 |
+
|
| 206 |
+
switch(format) {
|
| 207 |
+
/* Define new formats here */
|
| 208 |
+
ABS_FMT(UVC_FRAME_FORMAT_ANY, 2,
|
| 209 |
+
{UVC_FRAME_FORMAT_UNCOMPRESSED, UVC_FRAME_FORMAT_COMPRESSED})
|
| 210 |
+
|
| 211 |
+
ABS_FMT(UVC_FRAME_FORMAT_UNCOMPRESSED, 8,
|
| 212 |
+
{UVC_FRAME_FORMAT_YUYV, UVC_FRAME_FORMAT_UYVY, UVC_FRAME_FORMAT_GRAY8,
|
| 213 |
+
UVC_FRAME_FORMAT_GRAY16, UVC_FRAME_FORMAT_NV12, UVC_FRAME_FORMAT_P010,
|
| 214 |
+
UVC_FRAME_FORMAT_BGR, UVC_FRAME_FORMAT_RGB})
|
| 215 |
+
FMT(UVC_FRAME_FORMAT_YUYV,
|
| 216 |
+
{'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 217 |
+
FMT(UVC_FRAME_FORMAT_UYVY,
|
| 218 |
+
{'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 219 |
+
FMT(UVC_FRAME_FORMAT_GRAY8,
|
| 220 |
+
{'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 221 |
+
FMT(UVC_FRAME_FORMAT_GRAY16,
|
| 222 |
+
{'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 223 |
+
FMT(UVC_FRAME_FORMAT_NV12,
|
| 224 |
+
{'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 225 |
+
FMT(UVC_FRAME_FORMAT_P010,
|
| 226 |
+
{'P', '0', '1', '0', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 227 |
+
FMT(UVC_FRAME_FORMAT_BGR,
|
| 228 |
+
{0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70})
|
| 229 |
+
FMT(UVC_FRAME_FORMAT_RGB,
|
| 230 |
+
{0x7e, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70})
|
| 231 |
+
FMT(UVC_FRAME_FORMAT_BY8,
|
| 232 |
+
{'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 233 |
+
FMT(UVC_FRAME_FORMAT_BA81,
|
| 234 |
+
{'B', 'A', '8', '1', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 235 |
+
FMT(UVC_FRAME_FORMAT_SGRBG8,
|
| 236 |
+
{'G', 'R', 'B', 'G', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 237 |
+
FMT(UVC_FRAME_FORMAT_SGBRG8,
|
| 238 |
+
{'G', 'B', 'R', 'G', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 239 |
+
FMT(UVC_FRAME_FORMAT_SRGGB8,
|
| 240 |
+
{'R', 'G', 'G', 'B', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 241 |
+
FMT(UVC_FRAME_FORMAT_SBGGR8,
|
| 242 |
+
{'B', 'G', 'G', 'R', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 243 |
+
ABS_FMT(UVC_FRAME_FORMAT_COMPRESSED, 2,
|
| 244 |
+
{UVC_FRAME_FORMAT_MJPEG, UVC_FRAME_FORMAT_H264})
|
| 245 |
+
FMT(UVC_FRAME_FORMAT_MJPEG,
|
| 246 |
+
{'M', 'J', 'P', 'G'})
|
| 247 |
+
FMT(UVC_FRAME_FORMAT_H264,
|
| 248 |
+
{'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
|
| 249 |
+
|
| 250 |
+
default:
|
| 251 |
+
return NULL;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
#undef ABS_FMT
|
| 255 |
+
#undef FMT
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
static uint8_t _uvc_frame_format_matches_guid(enum uvc_frame_format fmt, uint8_t guid[16]) {
|
| 259 |
+
struct format_table_entry *format;
|
| 260 |
+
int child_idx;
|
| 261 |
+
|
| 262 |
+
format = _get_format_entry(fmt);
|
| 263 |
+
if (!format)
|
| 264 |
+
return 0;
|
| 265 |
+
|
| 266 |
+
if (!format->abstract_fmt && !memcmp(guid, format->guid, 16))
|
| 267 |
+
return 1;
|
| 268 |
+
|
| 269 |
+
for (child_idx = 0; child_idx < format->children_count; child_idx++) {
|
| 270 |
+
if (_uvc_frame_format_matches_guid(format->children[child_idx], guid))
|
| 271 |
+
return 1;
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
return 0;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
static enum uvc_frame_format uvc_frame_format_for_guid(uint8_t guid[16]) {
|
| 278 |
+
struct format_table_entry *format;
|
| 279 |
+
enum uvc_frame_format fmt;
|
| 280 |
+
|
| 281 |
+
for (fmt = 0; fmt < UVC_FRAME_FORMAT_COUNT; ++fmt) {
|
| 282 |
+
format = _get_format_entry(fmt);
|
| 283 |
+
if (!format || format->abstract_fmt)
|
| 284 |
+
continue;
|
| 285 |
+
if (!memcmp(format->guid, guid, 16))
|
| 286 |
+
return format->format;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
return UVC_FRAME_FORMAT_UNKNOWN;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
/** @internal
|
| 293 |
+
* Run a streaming control query
|
| 294 |
+
* @param[in] devh UVC device
|
| 295 |
+
* @param[in,out] ctrl Control block
|
| 296 |
+
* @param[in] probe Whether this is a probe query or a commit query
|
| 297 |
+
* @param[in] req Query type
|
| 298 |
+
*/
|
| 299 |
+
uvc_error_t uvc_query_stream_ctrl(
|
| 300 |
+
uvc_device_handle_t *devh,
|
| 301 |
+
uvc_stream_ctrl_t *ctrl,
|
| 302 |
+
uint8_t probe,
|
| 303 |
+
enum uvc_req_code req) {
|
| 304 |
+
uint8_t buf[34];
|
| 305 |
+
size_t len;
|
| 306 |
+
uvc_error_t err;
|
| 307 |
+
|
| 308 |
+
memset(buf, 0, sizeof(buf));
|
| 309 |
+
|
| 310 |
+
if (devh->info->ctrl_if.bcdUVC >= 0x0110)
|
| 311 |
+
len = 34;
|
| 312 |
+
else
|
| 313 |
+
len = 26;
|
| 314 |
+
|
| 315 |
+
/* prepare for a SET transfer */
|
| 316 |
+
if (req == UVC_SET_CUR) {
|
| 317 |
+
SHORT_TO_SW(ctrl->bmHint, buf);
|
| 318 |
+
buf[2] = ctrl->bFormatIndex;
|
| 319 |
+
buf[3] = ctrl->bFrameIndex;
|
| 320 |
+
INT_TO_DW(ctrl->dwFrameInterval, buf + 4);
|
| 321 |
+
SHORT_TO_SW(ctrl->wKeyFrameRate, buf + 8);
|
| 322 |
+
SHORT_TO_SW(ctrl->wPFrameRate, buf + 10);
|
| 323 |
+
SHORT_TO_SW(ctrl->wCompQuality, buf + 12);
|
| 324 |
+
SHORT_TO_SW(ctrl->wCompWindowSize, buf + 14);
|
| 325 |
+
SHORT_TO_SW(ctrl->wDelay, buf + 16);
|
| 326 |
+
INT_TO_DW(ctrl->dwMaxVideoFrameSize, buf + 18);
|
| 327 |
+
INT_TO_DW(ctrl->dwMaxPayloadTransferSize, buf + 22);
|
| 328 |
+
|
| 329 |
+
if (len == 34) {
|
| 330 |
+
INT_TO_DW ( ctrl->dwClockFrequency, buf + 26 );
|
| 331 |
+
buf[30] = ctrl->bmFramingInfo;
|
| 332 |
+
buf[31] = ctrl->bPreferredVersion;
|
| 333 |
+
buf[32] = ctrl->bMinVersion;
|
| 334 |
+
buf[33] = ctrl->bMaxVersion;
|
| 335 |
+
/** @todo support UVC 1.1 */
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
/* do the transfer */
|
| 340 |
+
err = libusb_control_transfer(
|
| 341 |
+
devh->usb_devh,
|
| 342 |
+
req == UVC_SET_CUR ? 0x21 : 0xA1,
|
| 343 |
+
req,
|
| 344 |
+
probe ? (UVC_VS_PROBE_CONTROL << 8) : (UVC_VS_COMMIT_CONTROL << 8),
|
| 345 |
+
ctrl->bInterfaceNumber,
|
| 346 |
+
buf, len, 0
|
| 347 |
+
);
|
| 348 |
+
|
| 349 |
+
if (err <= 0) {
|
| 350 |
+
return err;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/* now decode following a GET transfer */
|
| 354 |
+
if (req != UVC_SET_CUR) {
|
| 355 |
+
ctrl->bmHint = SW_TO_SHORT(buf);
|
| 356 |
+
ctrl->bFormatIndex = buf[2];
|
| 357 |
+
ctrl->bFrameIndex = buf[3];
|
| 358 |
+
ctrl->dwFrameInterval = DW_TO_INT(buf + 4);
|
| 359 |
+
ctrl->wKeyFrameRate = SW_TO_SHORT(buf + 8);
|
| 360 |
+
ctrl->wPFrameRate = SW_TO_SHORT(buf + 10);
|
| 361 |
+
ctrl->wCompQuality = SW_TO_SHORT(buf + 12);
|
| 362 |
+
ctrl->wCompWindowSize = SW_TO_SHORT(buf + 14);
|
| 363 |
+
ctrl->wDelay = SW_TO_SHORT(buf + 16);
|
| 364 |
+
ctrl->dwMaxVideoFrameSize = DW_TO_INT(buf + 18);
|
| 365 |
+
ctrl->dwMaxPayloadTransferSize = DW_TO_INT(buf + 22);
|
| 366 |
+
|
| 367 |
+
if (len == 34) {
|
| 368 |
+
ctrl->dwClockFrequency = DW_TO_INT ( buf + 26 );
|
| 369 |
+
ctrl->bmFramingInfo = buf[30];
|
| 370 |
+
ctrl->bPreferredVersion = buf[31];
|
| 371 |
+
ctrl->bMinVersion = buf[32];
|
| 372 |
+
ctrl->bMaxVersion = buf[33];
|
| 373 |
+
/** @todo support UVC 1.1 */
|
| 374 |
+
}
|
| 375 |
+
else
|
| 376 |
+
ctrl->dwClockFrequency = devh->info->ctrl_if.dwClockFrequency;
|
| 377 |
+
|
| 378 |
+
/* fix up block for cameras that fail to set dwMax* */
|
| 379 |
+
if (ctrl->dwMaxVideoFrameSize == 0) {
|
| 380 |
+
uvc_frame_desc_t *frame = uvc_find_frame_desc(devh, ctrl->bFormatIndex, ctrl->bFrameIndex);
|
| 381 |
+
|
| 382 |
+
if (frame) {
|
| 383 |
+
ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize;
|
| 384 |
+
}
|
| 385 |
+
}
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
return UVC_SUCCESS;
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
/** @internal
|
| 392 |
+
* Run a streaming control query
|
| 393 |
+
* @param[in] devh UVC device
|
| 394 |
+
* @param[in,out] ctrl Control block
|
| 395 |
+
* @param[in] probe Whether this is a probe query or a commit query
|
| 396 |
+
* @param[in] req Query type
|
| 397 |
+
*/
|
| 398 |
+
uvc_error_t uvc_query_still_ctrl(
|
| 399 |
+
uvc_device_handle_t *devh,
|
| 400 |
+
uvc_still_ctrl_t *still_ctrl,
|
| 401 |
+
uint8_t probe,
|
| 402 |
+
enum uvc_req_code req) {
|
| 403 |
+
|
| 404 |
+
uint8_t buf[11];
|
| 405 |
+
const size_t len = 11;
|
| 406 |
+
uvc_error_t err;
|
| 407 |
+
|
| 408 |
+
memset(buf, 0, sizeof(buf));
|
| 409 |
+
|
| 410 |
+
if (req == UVC_SET_CUR) {
|
| 411 |
+
/* prepare for a SET transfer */
|
| 412 |
+
buf[0] = still_ctrl->bFormatIndex;
|
| 413 |
+
buf[1] = still_ctrl->bFrameIndex;
|
| 414 |
+
buf[2] = still_ctrl->bCompressionIndex;
|
| 415 |
+
INT_TO_DW(still_ctrl->dwMaxVideoFrameSize, buf + 3);
|
| 416 |
+
INT_TO_DW(still_ctrl->dwMaxPayloadTransferSize, buf + 7);
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
/* do the transfer */
|
| 420 |
+
err = libusb_control_transfer(
|
| 421 |
+
devh->usb_devh,
|
| 422 |
+
req == UVC_SET_CUR ? 0x21 : 0xA1,
|
| 423 |
+
req,
|
| 424 |
+
probe ? (UVC_VS_STILL_PROBE_CONTROL << 8) : (UVC_VS_STILL_COMMIT_CONTROL << 8),
|
| 425 |
+
still_ctrl->bInterfaceNumber,
|
| 426 |
+
buf, len, 0
|
| 427 |
+
);
|
| 428 |
+
|
| 429 |
+
if (err <= 0) {
|
| 430 |
+
return err;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
/* now decode following a GET transfer */
|
| 434 |
+
if (req != UVC_SET_CUR) {
|
| 435 |
+
still_ctrl->bFormatIndex = buf[0];
|
| 436 |
+
still_ctrl->bFrameIndex = buf[1];
|
| 437 |
+
still_ctrl->bCompressionIndex = buf[2];
|
| 438 |
+
still_ctrl->dwMaxVideoFrameSize = DW_TO_INT(buf + 3);
|
| 439 |
+
still_ctrl->dwMaxPayloadTransferSize = DW_TO_INT(buf + 7);
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
return UVC_SUCCESS;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
/** Initiate a method 2 (in stream) still capture
|
| 446 |
+
* @ingroup streaming
|
| 447 |
+
*
|
| 448 |
+
* @param[in] devh Device handle
|
| 449 |
+
* @param[in] still_ctrl Still capture control block
|
| 450 |
+
*/
|
| 451 |
+
uvc_error_t uvc_trigger_still(
|
| 452 |
+
uvc_device_handle_t *devh,
|
| 453 |
+
uvc_still_ctrl_t *still_ctrl) {
|
| 454 |
+
uvc_stream_handle_t* stream;
|
| 455 |
+
uvc_streaming_interface_t* stream_if;
|
| 456 |
+
uint8_t buf;
|
| 457 |
+
uvc_error_t err;
|
| 458 |
+
|
| 459 |
+
/* Stream must be running for method 2 to work */
|
| 460 |
+
stream = _uvc_get_stream_by_interface(devh, still_ctrl->bInterfaceNumber);
|
| 461 |
+
if (!stream || !stream->running)
|
| 462 |
+
return UVC_ERROR_NOT_SUPPORTED;
|
| 463 |
+
|
| 464 |
+
/* Only method 2 is supported */
|
| 465 |
+
stream_if = _uvc_get_stream_if(devh, still_ctrl->bInterfaceNumber);
|
| 466 |
+
if(!stream_if || stream_if->bStillCaptureMethod != 2)
|
| 467 |
+
return UVC_ERROR_NOT_SUPPORTED;
|
| 468 |
+
|
| 469 |
+
/* prepare for a SET transfer */
|
| 470 |
+
buf = 1;
|
| 471 |
+
|
| 472 |
+
/* do the transfer */
|
| 473 |
+
err = libusb_control_transfer(
|
| 474 |
+
devh->usb_devh,
|
| 475 |
+
0x21, //type set
|
| 476 |
+
UVC_SET_CUR,
|
| 477 |
+
(UVC_VS_STILL_IMAGE_TRIGGER_CONTROL << 8),
|
| 478 |
+
still_ctrl->bInterfaceNumber,
|
| 479 |
+
&buf, 1, 0);
|
| 480 |
+
|
| 481 |
+
if (err <= 0) {
|
| 482 |
+
return err;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
return UVC_SUCCESS;
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
/** @brief Reconfigure stream with a new stream format.
|
| 489 |
+
* @ingroup streaming
|
| 490 |
+
*
|
| 491 |
+
* This may be executed whether or not the stream is running.
|
| 492 |
+
*
|
| 493 |
+
* @param[in] strmh Stream handle
|
| 494 |
+
* @param[in] ctrl Control block, processed using {uvc_probe_stream_ctrl} or
|
| 495 |
+
* {uvc_get_stream_ctrl_format_size}
|
| 496 |
+
*/
|
| 497 |
+
uvc_error_t uvc_stream_ctrl(uvc_stream_handle_t *strmh, uvc_stream_ctrl_t *ctrl) {
|
| 498 |
+
uvc_error_t ret;
|
| 499 |
+
uvc_frame_desc_t *frame_desc;
|
| 500 |
+
enum uvc_frame_format frame_format = UVC_FRAME_FORMAT_UNKNOWN;
|
| 501 |
+
struct ksq_altsetting_choice choice;
|
| 502 |
+
|
| 503 |
+
if (strmh->stream_if->bInterfaceNumber != ctrl->bInterfaceNumber)
|
| 504 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 505 |
+
|
| 506 |
+
/* @todo Allow the stream to be modified without restarting the stream */
|
| 507 |
+
if (strmh->running)
|
| 508 |
+
return UVC_ERROR_BUSY;
|
| 509 |
+
|
| 510 |
+
/*
|
| 511 |
+
* Apply the device-specific limit before COMMIT_CONTROL is sent. The
|
| 512 |
+
* later altsetting override uses the same value, so the camera's negotiated
|
| 513 |
+
* payload and libusb's ISO packet geometry remain consistent.
|
| 514 |
+
*/
|
| 515 |
+
frame_desc = uvc_find_frame_desc_stream(strmh, ctrl->bFormatIndex,
|
| 516 |
+
ctrl->bFrameIndex);
|
| 517 |
+
if (frame_desc != NULL && frame_desc->parent != NULL) {
|
| 518 |
+
frame_format = uvc_frame_format_for_guid(frame_desc->parent->guidFormat);
|
| 519 |
+
}
|
| 520 |
+
if (_ksq_resolve_altsetting_for_ctrl(strmh->devh, ctrl, frame_format,
|
| 521 |
+
frame_desc, &choice) &&
|
| 522 |
+
ctrl->dwMaxPayloadTransferSize > choice.payload) {
|
| 523 |
+
fprintf(stderr,
|
| 524 |
+
"[LIBUVC-QUIRK] COMMIT payload %u -> %u for VID:PID %04x:%04x%s\n",
|
| 525 |
+
ctrl->dwMaxPayloadTransferSize, choice.payload,
|
| 526 |
+
choice.vid, choice.pid,
|
| 527 |
+
choice.from_override ? " (altsetting override)" : "");
|
| 528 |
+
ctrl->dwMaxPayloadTransferSize = choice.payload;
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
ret = uvc_query_stream_ctrl(strmh->devh, ctrl, 0, UVC_SET_CUR);
|
| 532 |
+
if (ret != UVC_SUCCESS)
|
| 533 |
+
return ret;
|
| 534 |
+
|
| 535 |
+
strmh->cur_ctrl = *ctrl;
|
| 536 |
+
return UVC_SUCCESS;
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
/** @internal
|
| 540 |
+
* @brief Find the descriptor for a specific frame configuration
|
| 541 |
+
* @param stream_if Stream interface
|
| 542 |
+
* @param format_id Index of format class descriptor
|
| 543 |
+
* @param frame_id Index of frame descriptor
|
| 544 |
+
*/
|
| 545 |
+
static uvc_frame_desc_t *_uvc_find_frame_desc_stream_if(uvc_streaming_interface_t *stream_if,
|
| 546 |
+
uint16_t format_id, uint16_t frame_id) {
|
| 547 |
+
|
| 548 |
+
uvc_format_desc_t *format = NULL;
|
| 549 |
+
uvc_frame_desc_t *frame = NULL;
|
| 550 |
+
|
| 551 |
+
DL_FOREACH(stream_if->format_descs, format) {
|
| 552 |
+
if (format->bFormatIndex == format_id) {
|
| 553 |
+
DL_FOREACH(format->frame_descs, frame) {
|
| 554 |
+
if (frame->bFrameIndex == frame_id)
|
| 555 |
+
return frame;
|
| 556 |
+
}
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
return NULL;
|
| 561 |
+
}
|
| 562 |
+
|
| 563 |
+
uvc_frame_desc_t *uvc_find_frame_desc_stream(uvc_stream_handle_t *strmh,
|
| 564 |
+
uint16_t format_id, uint16_t frame_id) {
|
| 565 |
+
return _uvc_find_frame_desc_stream_if(strmh->stream_if, format_id, frame_id);
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
/** @internal
|
| 569 |
+
* @brief Find the descriptor for a specific frame configuration
|
| 570 |
+
* @param devh UVC device
|
| 571 |
+
* @param format_id Index of format class descriptor
|
| 572 |
+
* @param frame_id Index of frame descriptor
|
| 573 |
+
*/
|
| 574 |
+
uvc_frame_desc_t *uvc_find_frame_desc(uvc_device_handle_t *devh,
|
| 575 |
+
uint16_t format_id, uint16_t frame_id) {
|
| 576 |
+
|
| 577 |
+
uvc_streaming_interface_t *stream_if;
|
| 578 |
+
uvc_frame_desc_t *frame;
|
| 579 |
+
|
| 580 |
+
DL_FOREACH(devh->info->stream_ifs, stream_if) {
|
| 581 |
+
frame = _uvc_find_frame_desc_stream_if(stream_if, format_id, frame_id);
|
| 582 |
+
if (frame)
|
| 583 |
+
return frame;
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
return NULL;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
/** Get a negotiated streaming control block for some common parameters.
|
| 590 |
+
* @ingroup streaming
|
| 591 |
+
*
|
| 592 |
+
* @param[in] devh Device handle
|
| 593 |
+
* @param[in,out] ctrl Control block
|
| 594 |
+
* @param[in] format_class Type of streaming format
|
| 595 |
+
* @param[in] width Desired frame width
|
| 596 |
+
* @param[in] height Desired frame height
|
| 597 |
+
* @param[in] fps Frame rate, frames per second
|
| 598 |
+
*/
|
| 599 |
+
uvc_error_t uvc_get_stream_ctrl_format_size(
|
| 600 |
+
uvc_device_handle_t *devh,
|
| 601 |
+
uvc_stream_ctrl_t *ctrl,
|
| 602 |
+
enum uvc_frame_format cf,
|
| 603 |
+
int width, int height,
|
| 604 |
+
int fps) {
|
| 605 |
+
uvc_streaming_interface_t *stream_if;
|
| 606 |
+
|
| 607 |
+
/* find a matching frame descriptor and interval */
|
| 608 |
+
DL_FOREACH(devh->info->stream_ifs, stream_if) {
|
| 609 |
+
uvc_format_desc_t *format;
|
| 610 |
+
|
| 611 |
+
DL_FOREACH(stream_if->format_descs, format) {
|
| 612 |
+
uvc_frame_desc_t *frame;
|
| 613 |
+
|
| 614 |
+
if (!_uvc_frame_format_matches_guid(cf, format->guidFormat))
|
| 615 |
+
continue;
|
| 616 |
+
|
| 617 |
+
DL_FOREACH(format->frame_descs, frame) {
|
| 618 |
+
if (frame->wWidth != width || frame->wHeight != height)
|
| 619 |
+
continue;
|
| 620 |
+
|
| 621 |
+
uint32_t *interval;
|
| 622 |
+
|
| 623 |
+
ctrl->bInterfaceNumber = stream_if->bInterfaceNumber;
|
| 624 |
+
UVC_DEBUG("claiming streaming interface %d", stream_if->bInterfaceNumber );
|
| 625 |
+
uvc_claim_if(devh, ctrl->bInterfaceNumber);
|
| 626 |
+
/* get the max values */
|
| 627 |
+
uvc_query_stream_ctrl( devh, ctrl, 1, UVC_GET_MAX);
|
| 628 |
+
|
| 629 |
+
if (frame->intervals) {
|
| 630 |
+
for (interval = frame->intervals; *interval; ++interval) {
|
| 631 |
+
// allow a fps rate of zero to mean "accept first rate available"
|
| 632 |
+
if (10000000 / *interval == (unsigned int) fps || fps == 0) {
|
| 633 |
+
|
| 634 |
+
ctrl->bmHint = (1 << 0); /* don't negotiate interval */
|
| 635 |
+
ctrl->bFormatIndex = format->bFormatIndex;
|
| 636 |
+
ctrl->bFrameIndex = frame->bFrameIndex;
|
| 637 |
+
ctrl->dwFrameInterval = *interval;
|
| 638 |
+
|
| 639 |
+
goto found;
|
| 640 |
+
}
|
| 641 |
+
}
|
| 642 |
+
} else {
|
| 643 |
+
uint32_t interval_100ns = 10000000 / fps;
|
| 644 |
+
uint32_t interval_offset = interval_100ns - frame->dwMinFrameInterval;
|
| 645 |
+
|
| 646 |
+
if (interval_100ns >= frame->dwMinFrameInterval
|
| 647 |
+
&& interval_100ns <= frame->dwMaxFrameInterval
|
| 648 |
+
&& !(interval_offset
|
| 649 |
+
&& (interval_offset % frame->dwFrameIntervalStep))) {
|
| 650 |
+
|
| 651 |
+
ctrl->bmHint = (1 << 0);
|
| 652 |
+
ctrl->bFormatIndex = format->bFormatIndex;
|
| 653 |
+
ctrl->bFrameIndex = frame->bFrameIndex;
|
| 654 |
+
ctrl->dwFrameInterval = interval_100ns;
|
| 655 |
+
|
| 656 |
+
goto found;
|
| 657 |
+
}
|
| 658 |
+
}
|
| 659 |
+
}
|
| 660 |
+
}
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
return UVC_ERROR_INVALID_MODE;
|
| 664 |
+
|
| 665 |
+
found:
|
| 666 |
+
return uvc_probe_stream_ctrl(devh, ctrl);
|
| 667 |
+
}
|
| 668 |
+
|
| 669 |
+
/** Get a negotiated still control block for some common parameters.
|
| 670 |
+
* @ingroup streaming
|
| 671 |
+
*
|
| 672 |
+
* @param[in] devh Device handle
|
| 673 |
+
* @param[in] ctrl Control block
|
| 674 |
+
* @param[in, out] still_ctrl Still capture control block
|
| 675 |
+
* @param[in] width Desired frame width
|
| 676 |
+
* @param[in] height Desired frame height
|
| 677 |
+
*/
|
| 678 |
+
uvc_error_t uvc_get_still_ctrl_format_size(
|
| 679 |
+
uvc_device_handle_t *devh,
|
| 680 |
+
uvc_stream_ctrl_t *ctrl,
|
| 681 |
+
uvc_still_ctrl_t *still_ctrl,
|
| 682 |
+
int width, int height) {
|
| 683 |
+
uvc_streaming_interface_t *stream_if;
|
| 684 |
+
uvc_still_frame_desc_t *still;
|
| 685 |
+
uvc_format_desc_t *format;
|
| 686 |
+
uvc_still_frame_res_t *sizePattern;
|
| 687 |
+
|
| 688 |
+
stream_if = _uvc_get_stream_if(devh, ctrl->bInterfaceNumber);
|
| 689 |
+
|
| 690 |
+
/* Only method 2 is supported */
|
| 691 |
+
if(!stream_if || stream_if->bStillCaptureMethod != 2)
|
| 692 |
+
return UVC_ERROR_NOT_SUPPORTED;
|
| 693 |
+
|
| 694 |
+
DL_FOREACH(stream_if->format_descs, format) {
|
| 695 |
+
|
| 696 |
+
if (ctrl->bFormatIndex != format->bFormatIndex)
|
| 697 |
+
continue;
|
| 698 |
+
|
| 699 |
+
/* get the max values */
|
| 700 |
+
uvc_query_still_ctrl(devh, still_ctrl, 1, UVC_GET_MAX);
|
| 701 |
+
|
| 702 |
+
//look for still format
|
| 703 |
+
DL_FOREACH(format->still_frame_desc, still) {
|
| 704 |
+
DL_FOREACH(still->imageSizePatterns, sizePattern) {
|
| 705 |
+
|
| 706 |
+
if (sizePattern->wWidth != width || sizePattern->wHeight != height)
|
| 707 |
+
continue;
|
| 708 |
+
|
| 709 |
+
still_ctrl->bInterfaceNumber = ctrl->bInterfaceNumber;
|
| 710 |
+
still_ctrl->bFormatIndex = format->bFormatIndex;
|
| 711 |
+
still_ctrl->bFrameIndex = sizePattern->bResolutionIndex;
|
| 712 |
+
still_ctrl->bCompressionIndex = 0; //TODO support compression index
|
| 713 |
+
goto found;
|
| 714 |
+
}
|
| 715 |
+
}
|
| 716 |
+
}
|
| 717 |
+
|
| 718 |
+
return UVC_ERROR_INVALID_MODE;
|
| 719 |
+
|
| 720 |
+
found:
|
| 721 |
+
return uvc_probe_still_ctrl(devh, still_ctrl);
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
static int _uvc_stream_params_negotiated(
|
| 725 |
+
uvc_stream_ctrl_t *required,
|
| 726 |
+
uvc_stream_ctrl_t *actual) {
|
| 727 |
+
return required->bFormatIndex == actual->bFormatIndex &&
|
| 728 |
+
required->bFrameIndex == actual->bFrameIndex &&
|
| 729 |
+
required->dwMaxPayloadTransferSize >= actual->dwMaxPayloadTransferSize;
|
| 730 |
+
}
|
| 731 |
+
|
| 732 |
+
/** @internal
|
| 733 |
+
* Negotiate streaming parameters with the device
|
| 734 |
+
*
|
| 735 |
+
* @param[in] devh UVC device
|
| 736 |
+
* @param[in,out] ctrl Control block
|
| 737 |
+
*/
|
| 738 |
+
uvc_error_t uvc_probe_stream_ctrl(
|
| 739 |
+
uvc_device_handle_t *devh,
|
| 740 |
+
uvc_stream_ctrl_t *ctrl) {
|
| 741 |
+
uvc_stream_ctrl_t required_ctrl = *ctrl;
|
| 742 |
+
|
| 743 |
+
uvc_query_stream_ctrl( devh, ctrl, 1, UVC_SET_CUR );
|
| 744 |
+
uvc_query_stream_ctrl( devh, ctrl, 1, UVC_GET_CUR );
|
| 745 |
+
|
| 746 |
+
if(!_uvc_stream_params_negotiated(&required_ctrl, ctrl)) {
|
| 747 |
+
UVC_DEBUG("Unable to negotiate streaming format");
|
| 748 |
+
return UVC_ERROR_INVALID_MODE;
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
return UVC_SUCCESS;
|
| 752 |
+
}
|
| 753 |
+
|
| 754 |
+
/** @internal
|
| 755 |
+
* Negotiate still parameters with the device
|
| 756 |
+
*
|
| 757 |
+
* @param[in] devh UVC device
|
| 758 |
+
* @param[in,out] still_ctrl Still capture control block
|
| 759 |
+
*/
|
| 760 |
+
uvc_error_t uvc_probe_still_ctrl(
|
| 761 |
+
uvc_device_handle_t *devh,
|
| 762 |
+
uvc_still_ctrl_t *still_ctrl) {
|
| 763 |
+
|
| 764 |
+
int res = uvc_query_still_ctrl(
|
| 765 |
+
devh, still_ctrl, 1, UVC_SET_CUR
|
| 766 |
+
);
|
| 767 |
+
|
| 768 |
+
if(res == UVC_SUCCESS) {
|
| 769 |
+
res = uvc_query_still_ctrl(
|
| 770 |
+
devh, still_ctrl, 1, UVC_GET_CUR
|
| 771 |
+
);
|
| 772 |
+
|
| 773 |
+
if(res == UVC_SUCCESS) {
|
| 774 |
+
res = uvc_query_still_ctrl(
|
| 775 |
+
devh, still_ctrl, 0, UVC_SET_CUR
|
| 776 |
+
);
|
| 777 |
+
}
|
| 778 |
+
}
|
| 779 |
+
|
| 780 |
+
return res;
|
| 781 |
+
}
|
| 782 |
+
|
| 783 |
+
/** @internal
|
| 784 |
+
* @brief Swap the working buffer with the presented buffer and notify consumers
|
| 785 |
+
*/
|
| 786 |
+
void _uvc_swap_buffers(uvc_stream_handle_t *strmh) {
|
| 787 |
+
uint8_t *tmp_buf;
|
| 788 |
+
|
| 789 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 790 |
+
|
| 791 |
+
(void)clock_gettime(CLOCK_MONOTONIC, &strmh->capture_time_finished);
|
| 792 |
+
|
| 793 |
+
/* swap the buffers */
|
| 794 |
+
tmp_buf = strmh->holdbuf;
|
| 795 |
+
strmh->hold_bytes = strmh->got_bytes;
|
| 796 |
+
strmh->holdbuf = strmh->outbuf;
|
| 797 |
+
strmh->outbuf = tmp_buf;
|
| 798 |
+
strmh->hold_last_scr = strmh->last_scr;
|
| 799 |
+
strmh->hold_pts = strmh->pts;
|
| 800 |
+
strmh->hold_seq = strmh->seq;
|
| 801 |
+
|
| 802 |
+
/* swap metadata buffer */
|
| 803 |
+
tmp_buf = strmh->meta_holdbuf;
|
| 804 |
+
strmh->meta_holdbuf = strmh->meta_outbuf;
|
| 805 |
+
strmh->meta_outbuf = tmp_buf;
|
| 806 |
+
strmh->meta_hold_bytes = strmh->meta_got_bytes;
|
| 807 |
+
|
| 808 |
+
pthread_cond_broadcast(&strmh->cb_cond);
|
| 809 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 810 |
+
|
| 811 |
+
strmh->seq++;
|
| 812 |
+
strmh->got_bytes = 0;
|
| 813 |
+
strmh->meta_got_bytes = 0;
|
| 814 |
+
strmh->last_scr = 0;
|
| 815 |
+
strmh->pts = 0;
|
| 816 |
+
}
|
| 817 |
+
|
| 818 |
+
/** @internal
|
| 819 |
+
* @brief Process a payload transfer
|
| 820 |
+
*
|
| 821 |
+
* Processes stream, places frames into buffer, signals listeners
|
| 822 |
+
* (such as user callback thread and any polling thread) on new frame
|
| 823 |
+
*
|
| 824 |
+
* @param payload Contents of the payload transfer, either a packet (isochronous) or a full
|
| 825 |
+
* transfer (bulk mode)
|
| 826 |
+
* @param payload_len Length of the payload transfer
|
| 827 |
+
*/
|
| 828 |
+
void _uvc_process_payload(uvc_stream_handle_t *strmh, uint8_t *payload, size_t payload_len) {
|
| 829 |
+
size_t header_len;
|
| 830 |
+
uint8_t header_info;
|
| 831 |
+
size_t data_len;
|
| 832 |
+
|
| 833 |
+
/* magic numbers for identifying header packets from some iSight cameras */
|
| 834 |
+
static uint8_t isight_tag[] = {
|
| 835 |
+
0x11, 0x22, 0x33, 0x44,
|
| 836 |
+
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xfa, 0xce
|
| 837 |
+
};
|
| 838 |
+
|
| 839 |
+
/* ignore empty payload transfers */
|
| 840 |
+
if (payload_len == 0)
|
| 841 |
+
return;
|
| 842 |
+
|
| 843 |
+
/* Certain iSight cameras have strange behavior: They send header
|
| 844 |
+
* information in a packet with no image data, and then the following
|
| 845 |
+
* packets have only image data, with no more headers until the next frame.
|
| 846 |
+
*
|
| 847 |
+
* The iSight header: len(1), flags(1 or 2), 0x11223344(4),
|
| 848 |
+
* 0xdeadbeefdeadface(8), ??(16)
|
| 849 |
+
*/
|
| 850 |
+
|
| 851 |
+
if (strmh->devh->is_isight &&
|
| 852 |
+
(payload_len < 14 || memcmp(isight_tag, payload + 2, sizeof(isight_tag))) &&
|
| 853 |
+
(payload_len < 15 || memcmp(isight_tag, payload + 3, sizeof(isight_tag)))) {
|
| 854 |
+
/* The payload transfer doesn't have any iSight magic, so it's all image data */
|
| 855 |
+
header_len = 0;
|
| 856 |
+
data_len = payload_len;
|
| 857 |
+
} else {
|
| 858 |
+
header_len = payload[0];
|
| 859 |
+
|
| 860 |
+
if (header_len > payload_len) {
|
| 861 |
+
UVC_DEBUG("bogus packet: actual_len=%zd, header_len=%zd\n", payload_len, header_len);
|
| 862 |
+
return;
|
| 863 |
+
}
|
| 864 |
+
|
| 865 |
+
if (strmh->devh->is_isight)
|
| 866 |
+
data_len = 0;
|
| 867 |
+
else
|
| 868 |
+
data_len = payload_len - header_len;
|
| 869 |
+
}
|
| 870 |
+
|
| 871 |
+
if (header_len < 2) {
|
| 872 |
+
header_info = 0;
|
| 873 |
+
} else {
|
| 874 |
+
/** @todo we should be checking the end-of-header bit */
|
| 875 |
+
size_t variable_offset = 2;
|
| 876 |
+
|
| 877 |
+
header_info = payload[1];
|
| 878 |
+
|
| 879 |
+
if (header_info & 0x40) {
|
| 880 |
+
UVC_DEBUG("bad packet: error bit set");
|
| 881 |
+
return;
|
| 882 |
+
}
|
| 883 |
+
|
| 884 |
+
if (strmh->fid != (header_info & 1) && strmh->got_bytes != 0) {
|
| 885 |
+
/* The frame ID bit was flipped, but we have image data sitting
|
| 886 |
+
around from prior transfers. This means the camera didn't send
|
| 887 |
+
an EOF for the last transfer of the previous frame. */
|
| 888 |
+
_uvc_swap_buffers(strmh);
|
| 889 |
+
}
|
| 890 |
+
|
| 891 |
+
strmh->fid = header_info & 1;
|
| 892 |
+
|
| 893 |
+
if (header_info & (1 << 2)) {
|
| 894 |
+
strmh->pts = DW_TO_INT(payload + variable_offset);
|
| 895 |
+
variable_offset += 4;
|
| 896 |
+
}
|
| 897 |
+
|
| 898 |
+
if (header_info & (1 << 3)) {
|
| 899 |
+
/** @todo read the SOF token counter */
|
| 900 |
+
strmh->last_scr = DW_TO_INT(payload + variable_offset);
|
| 901 |
+
variable_offset += 6;
|
| 902 |
+
}
|
| 903 |
+
|
| 904 |
+
if (header_len > variable_offset) {
|
| 905 |
+
// Metadata is attached to header
|
| 906 |
+
size_t meta_len = header_len - variable_offset;
|
| 907 |
+
if (strmh->meta_got_bytes + meta_len > LIBUVC_XFER_META_BUF_SIZE)
|
| 908 |
+
meta_len = LIBUVC_XFER_META_BUF_SIZE - strmh->meta_got_bytes; /* Avoid overflow. */
|
| 909 |
+
memcpy(strmh->meta_outbuf + strmh->meta_got_bytes, payload + variable_offset, meta_len);
|
| 910 |
+
strmh->meta_got_bytes += meta_len;
|
| 911 |
+
}
|
| 912 |
+
}
|
| 913 |
+
|
| 914 |
+
if (data_len > 0) {
|
| 915 |
+
if (strmh->got_bytes + data_len > strmh->cur_ctrl.dwMaxVideoFrameSize)
|
| 916 |
+
data_len = strmh->cur_ctrl.dwMaxVideoFrameSize - strmh->got_bytes; /* Avoid overflow. */
|
| 917 |
+
memcpy(strmh->outbuf + strmh->got_bytes, payload + header_len, data_len);
|
| 918 |
+
strmh->got_bytes += data_len;
|
| 919 |
+
if (header_info & (1 << 1) || strmh->got_bytes == strmh->cur_ctrl.dwMaxVideoFrameSize) {
|
| 920 |
+
/* The EOF bit is set, so publish the complete frame */
|
| 921 |
+
_uvc_swap_buffers(strmh);
|
| 922 |
+
}
|
| 923 |
+
}
|
| 924 |
+
}
|
| 925 |
+
|
| 926 |
+
/** @internal
|
| 927 |
+
* @brief Stream transfer callback
|
| 928 |
+
*
|
| 929 |
+
* Processes stream, places frames into buffer, signals listeners
|
| 930 |
+
* (such as user callback thread and any polling thread) on new frame
|
| 931 |
+
*
|
| 932 |
+
* @param transfer Active transfer
|
| 933 |
+
*/
|
| 934 |
+
void LIBUSB_CALL _uvc_stream_callback(struct libusb_transfer *transfer) {
|
| 935 |
+
uvc_stream_handle_t *strmh = transfer->user_data;
|
| 936 |
+
|
| 937 |
+
int resubmit = 1;
|
| 938 |
+
|
| 939 |
+
switch (transfer->status) {
|
| 940 |
+
case LIBUSB_TRANSFER_COMPLETED:
|
| 941 |
+
if (transfer->num_iso_packets == 0) {
|
| 942 |
+
/* This is a bulk mode transfer, so it just has one payload transfer */
|
| 943 |
+
_uvc_process_payload(strmh, transfer->buffer, transfer->actual_length);
|
| 944 |
+
} else {
|
| 945 |
+
/* This is an isochronous mode transfer, so each packet has a payload transfer */
|
| 946 |
+
int packet_id;
|
| 947 |
+
|
| 948 |
+
for (packet_id = 0; packet_id < transfer->num_iso_packets; ++packet_id) {
|
| 949 |
+
uint8_t *pktbuf;
|
| 950 |
+
struct libusb_iso_packet_descriptor *pkt;
|
| 951 |
+
|
| 952 |
+
pkt = transfer->iso_packet_desc + packet_id;
|
| 953 |
+
|
| 954 |
+
if (pkt->status != 0) {
|
| 955 |
+
UVC_DEBUG("bad packet (isochronous transfer); status: %d", pkt->status);
|
| 956 |
+
continue;
|
| 957 |
+
}
|
| 958 |
+
|
| 959 |
+
pktbuf = libusb_get_iso_packet_buffer_simple(transfer, packet_id);
|
| 960 |
+
|
| 961 |
+
_uvc_process_payload(strmh, pktbuf, pkt->actual_length);
|
| 962 |
+
|
| 963 |
+
}
|
| 964 |
+
}
|
| 965 |
+
break;
|
| 966 |
+
case LIBUSB_TRANSFER_CANCELLED:
|
| 967 |
+
case LIBUSB_TRANSFER_ERROR:
|
| 968 |
+
case LIBUSB_TRANSFER_NO_DEVICE: {
|
| 969 |
+
int i;
|
| 970 |
+
UVC_DEBUG("not retrying transfer, status = %d", transfer->status);
|
| 971 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 972 |
+
|
| 973 |
+
/* Mark transfer as deleted. */
|
| 974 |
+
for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) {
|
| 975 |
+
if(strmh->transfers[i] == transfer) {
|
| 976 |
+
UVC_DEBUG("Freeing transfer %d (%p)", i, transfer);
|
| 977 |
+
free(transfer->buffer);
|
| 978 |
+
libusb_free_transfer(transfer);
|
| 979 |
+
strmh->transfers[i] = NULL;
|
| 980 |
+
break;
|
| 981 |
+
}
|
| 982 |
+
}
|
| 983 |
+
if(i == LIBUVC_NUM_TRANSFER_BUFS ) {
|
| 984 |
+
UVC_DEBUG("transfer %p not found; not freeing!", transfer);
|
| 985 |
+
}
|
| 986 |
+
|
| 987 |
+
resubmit = 0;
|
| 988 |
+
|
| 989 |
+
pthread_cond_broadcast(&strmh->cb_cond);
|
| 990 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 991 |
+
|
| 992 |
+
break;
|
| 993 |
+
}
|
| 994 |
+
case LIBUSB_TRANSFER_TIMED_OUT:
|
| 995 |
+
case LIBUSB_TRANSFER_STALL:
|
| 996 |
+
case LIBUSB_TRANSFER_OVERFLOW:
|
| 997 |
+
UVC_DEBUG("retrying transfer, status = %d", transfer->status);
|
| 998 |
+
break;
|
| 999 |
+
}
|
| 1000 |
+
|
| 1001 |
+
if ( resubmit ) {
|
| 1002 |
+
if ( strmh->running ) {
|
| 1003 |
+
int libusbRet = libusb_submit_transfer(transfer);
|
| 1004 |
+
if (libusbRet < 0)
|
| 1005 |
+
{
|
| 1006 |
+
int i;
|
| 1007 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 1008 |
+
|
| 1009 |
+
/* Mark transfer as deleted. */
|
| 1010 |
+
for (i = 0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) {
|
| 1011 |
+
if (strmh->transfers[i] == transfer) {
|
| 1012 |
+
UVC_DEBUG("Freeing failed transfer %d (%p)", i, transfer);
|
| 1013 |
+
free(transfer->buffer);
|
| 1014 |
+
libusb_free_transfer(transfer);
|
| 1015 |
+
strmh->transfers[i] = NULL;
|
| 1016 |
+
break;
|
| 1017 |
+
}
|
| 1018 |
+
}
|
| 1019 |
+
if (i == LIBUVC_NUM_TRANSFER_BUFS) {
|
| 1020 |
+
UVC_DEBUG("failed transfer %p not found; not freeing!", transfer);
|
| 1021 |
+
}
|
| 1022 |
+
|
| 1023 |
+
pthread_cond_broadcast(&strmh->cb_cond);
|
| 1024 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1025 |
+
}
|
| 1026 |
+
} else {
|
| 1027 |
+
int i;
|
| 1028 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 1029 |
+
|
| 1030 |
+
/* Mark transfer as deleted. */
|
| 1031 |
+
for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) {
|
| 1032 |
+
if(strmh->transfers[i] == transfer) {
|
| 1033 |
+
UVC_DEBUG("Freeing orphan transfer %d (%p)", i, transfer);
|
| 1034 |
+
free(transfer->buffer);
|
| 1035 |
+
libusb_free_transfer(transfer);
|
| 1036 |
+
strmh->transfers[i] = NULL;
|
| 1037 |
+
break;
|
| 1038 |
+
}
|
| 1039 |
+
}
|
| 1040 |
+
if(i == LIBUVC_NUM_TRANSFER_BUFS ) {
|
| 1041 |
+
UVC_DEBUG("orphan transfer %p not found; not freeing!", transfer);
|
| 1042 |
+
}
|
| 1043 |
+
|
| 1044 |
+
pthread_cond_broadcast(&strmh->cb_cond);
|
| 1045 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1046 |
+
}
|
| 1047 |
+
}
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
/** Begin streaming video from the camera into the callback function.
|
| 1051 |
+
* @ingroup streaming
|
| 1052 |
+
*
|
| 1053 |
+
* @param devh UVC device
|
| 1054 |
+
* @param ctrl Control block, processed using {uvc_probe_stream_ctrl} or
|
| 1055 |
+
* {uvc_get_stream_ctrl_format_size}
|
| 1056 |
+
* @param cb User callback function. See {uvc_frame_callback_t} for restrictions.
|
| 1057 |
+
* @param flags Stream setup flags, currently undefined. Set this to zero. The lower bit
|
| 1058 |
+
* is reserved for backward compatibility.
|
| 1059 |
+
*/
|
| 1060 |
+
uvc_error_t uvc_start_streaming(
|
| 1061 |
+
uvc_device_handle_t *devh,
|
| 1062 |
+
uvc_stream_ctrl_t *ctrl,
|
| 1063 |
+
uvc_frame_callback_t *cb,
|
| 1064 |
+
void *user_ptr,
|
| 1065 |
+
uint8_t flags
|
| 1066 |
+
) {
|
| 1067 |
+
uvc_error_t ret;
|
| 1068 |
+
uvc_stream_handle_t *strmh;
|
| 1069 |
+
|
| 1070 |
+
ret = uvc_stream_open_ctrl(devh, &strmh, ctrl);
|
| 1071 |
+
if (ret != UVC_SUCCESS)
|
| 1072 |
+
return ret;
|
| 1073 |
+
|
| 1074 |
+
ret = uvc_stream_start(strmh, cb, user_ptr, flags);
|
| 1075 |
+
if (ret != UVC_SUCCESS) {
|
| 1076 |
+
uvc_stream_close(strmh);
|
| 1077 |
+
return ret;
|
| 1078 |
+
}
|
| 1079 |
+
|
| 1080 |
+
return UVC_SUCCESS;
|
| 1081 |
+
}
|
| 1082 |
+
|
| 1083 |
+
/** Begin streaming video from the camera into the callback function.
|
| 1084 |
+
* @ingroup streaming
|
| 1085 |
+
*
|
| 1086 |
+
* @deprecated The stream type (bulk vs. isochronous) will be determined by the
|
| 1087 |
+
* type of interface associated with the uvc_stream_ctrl_t parameter, regardless
|
| 1088 |
+
* of whether the caller requests isochronous streaming. Please switch to
|
| 1089 |
+
* uvc_start_streaming().
|
| 1090 |
+
*
|
| 1091 |
+
* @param devh UVC device
|
| 1092 |
+
* @param ctrl Control block, processed using {uvc_probe_stream_ctrl} or
|
| 1093 |
+
* {uvc_get_stream_ctrl_format_size}
|
| 1094 |
+
* @param cb User callback function. See {uvc_frame_callback_t} for restrictions.
|
| 1095 |
+
*/
|
| 1096 |
+
uvc_error_t uvc_start_iso_streaming(
|
| 1097 |
+
uvc_device_handle_t *devh,
|
| 1098 |
+
uvc_stream_ctrl_t *ctrl,
|
| 1099 |
+
uvc_frame_callback_t *cb,
|
| 1100 |
+
void *user_ptr
|
| 1101 |
+
) {
|
| 1102 |
+
return uvc_start_streaming(devh, ctrl, cb, user_ptr, 0);
|
| 1103 |
+
}
|
| 1104 |
+
|
| 1105 |
+
static uvc_stream_handle_t *_uvc_get_stream_by_interface(uvc_device_handle_t *devh, int interface_idx) {
|
| 1106 |
+
uvc_stream_handle_t *strmh;
|
| 1107 |
+
|
| 1108 |
+
DL_FOREACH(devh->streams, strmh) {
|
| 1109 |
+
if (strmh->stream_if->bInterfaceNumber == interface_idx)
|
| 1110 |
+
return strmh;
|
| 1111 |
+
}
|
| 1112 |
+
|
| 1113 |
+
return NULL;
|
| 1114 |
+
}
|
| 1115 |
+
|
| 1116 |
+
static uvc_streaming_interface_t *_uvc_get_stream_if(uvc_device_handle_t *devh, int interface_idx) {
|
| 1117 |
+
uvc_streaming_interface_t *stream_if;
|
| 1118 |
+
|
| 1119 |
+
DL_FOREACH(devh->info->stream_ifs, stream_if) {
|
| 1120 |
+
if (stream_if->bInterfaceNumber == interface_idx)
|
| 1121 |
+
return stream_if;
|
| 1122 |
+
}
|
| 1123 |
+
|
| 1124 |
+
return NULL;
|
| 1125 |
+
}
|
| 1126 |
+
|
| 1127 |
+
/** Open a new video stream.
|
| 1128 |
+
* @ingroup streaming
|
| 1129 |
+
*
|
| 1130 |
+
* @param devh UVC device
|
| 1131 |
+
* @param ctrl Control block, processed using {uvc_probe_stream_ctrl} or
|
| 1132 |
+
* {uvc_get_stream_ctrl_format_size}
|
| 1133 |
+
*/
|
| 1134 |
+
uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t **strmhp, uvc_stream_ctrl_t *ctrl) {
|
| 1135 |
+
/* Chosen frame and format descriptors */
|
| 1136 |
+
uvc_stream_handle_t *strmh = NULL;
|
| 1137 |
+
uvc_streaming_interface_t *stream_if;
|
| 1138 |
+
uvc_error_t ret;
|
| 1139 |
+
|
| 1140 |
+
UVC_ENTER();
|
| 1141 |
+
|
| 1142 |
+
if (_uvc_get_stream_by_interface(devh, ctrl->bInterfaceNumber) != NULL) {
|
| 1143 |
+
ret = UVC_ERROR_BUSY; /* Stream is already opened */
|
| 1144 |
+
goto fail;
|
| 1145 |
+
}
|
| 1146 |
+
|
| 1147 |
+
stream_if = _uvc_get_stream_if(devh, ctrl->bInterfaceNumber);
|
| 1148 |
+
if (!stream_if) {
|
| 1149 |
+
ret = UVC_ERROR_INVALID_PARAM;
|
| 1150 |
+
goto fail;
|
| 1151 |
+
}
|
| 1152 |
+
|
| 1153 |
+
strmh = calloc(1, sizeof(*strmh));
|
| 1154 |
+
if (!strmh) {
|
| 1155 |
+
ret = UVC_ERROR_NO_MEM;
|
| 1156 |
+
goto fail;
|
| 1157 |
+
}
|
| 1158 |
+
strmh->devh = devh;
|
| 1159 |
+
strmh->stream_if = stream_if;
|
| 1160 |
+
strmh->frame.library_owns_data = 1;
|
| 1161 |
+
|
| 1162 |
+
ret = uvc_claim_if(strmh->devh, strmh->stream_if->bInterfaceNumber);
|
| 1163 |
+
if (ret != UVC_SUCCESS)
|
| 1164 |
+
goto fail;
|
| 1165 |
+
|
| 1166 |
+
ret = uvc_stream_ctrl(strmh, ctrl);
|
| 1167 |
+
if (ret != UVC_SUCCESS)
|
| 1168 |
+
goto fail;
|
| 1169 |
+
|
| 1170 |
+
// Set up the streaming status and data space
|
| 1171 |
+
strmh->running = 0;
|
| 1172 |
+
|
| 1173 |
+
strmh->outbuf = malloc( ctrl->dwMaxVideoFrameSize );
|
| 1174 |
+
strmh->holdbuf = malloc( ctrl->dwMaxVideoFrameSize );
|
| 1175 |
+
|
| 1176 |
+
strmh->meta_outbuf = malloc( LIBUVC_XFER_META_BUF_SIZE );
|
| 1177 |
+
strmh->meta_holdbuf = malloc( LIBUVC_XFER_META_BUF_SIZE );
|
| 1178 |
+
|
| 1179 |
+
pthread_mutex_init(&strmh->cb_mutex, NULL);
|
| 1180 |
+
pthread_cond_init(&strmh->cb_cond, NULL);
|
| 1181 |
+
|
| 1182 |
+
DL_APPEND(devh->streams, strmh);
|
| 1183 |
+
|
| 1184 |
+
*strmhp = strmh;
|
| 1185 |
+
|
| 1186 |
+
UVC_EXIT(0);
|
| 1187 |
+
return UVC_SUCCESS;
|
| 1188 |
+
|
| 1189 |
+
fail:
|
| 1190 |
+
if(strmh)
|
| 1191 |
+
free(strmh);
|
| 1192 |
+
UVC_EXIT(ret);
|
| 1193 |
+
return ret;
|
| 1194 |
+
}
|
| 1195 |
+
|
| 1196 |
+
/** Begin streaming video from the stream into the callback function.
|
| 1197 |
+
* @ingroup streaming
|
| 1198 |
+
*
|
| 1199 |
+
* @param strmh UVC stream
|
| 1200 |
+
* @param cb User callback function. See {uvc_frame_callback_t} for restrictions.
|
| 1201 |
+
* @param flags Stream setup flags, currently undefined. Set this to zero. The lower bit
|
| 1202 |
+
* is reserved for backward compatibility.
|
| 1203 |
+
*/
|
| 1204 |
+
uvc_error_t uvc_stream_start(
|
| 1205 |
+
uvc_stream_handle_t *strmh,
|
| 1206 |
+
uvc_frame_callback_t *cb,
|
| 1207 |
+
void *user_ptr,
|
| 1208 |
+
uint8_t flags
|
| 1209 |
+
) {
|
| 1210 |
+
/* USB interface we'll be using */
|
| 1211 |
+
const struct libusb_interface *interface;
|
| 1212 |
+
int interface_id;
|
| 1213 |
+
char isochronous;
|
| 1214 |
+
uvc_frame_desc_t *frame_desc;
|
| 1215 |
+
uvc_format_desc_t *format_desc;
|
| 1216 |
+
uvc_stream_ctrl_t *ctrl;
|
| 1217 |
+
uvc_error_t ret;
|
| 1218 |
+
struct ksq_altsetting_choice choice;
|
| 1219 |
+
/* Total amount of data per transfer */
|
| 1220 |
+
size_t total_transfer_size = 0;
|
| 1221 |
+
struct libusb_transfer *transfer;
|
| 1222 |
+
int transfer_id;
|
| 1223 |
+
|
| 1224 |
+
ctrl = &strmh->cur_ctrl;
|
| 1225 |
+
|
| 1226 |
+
UVC_ENTER();
|
| 1227 |
+
|
| 1228 |
+
if (strmh->running) {
|
| 1229 |
+
UVC_EXIT(UVC_ERROR_BUSY);
|
| 1230 |
+
return UVC_ERROR_BUSY;
|
| 1231 |
+
}
|
| 1232 |
+
|
| 1233 |
+
strmh->running = 1;
|
| 1234 |
+
strmh->seq = 1;
|
| 1235 |
+
strmh->fid = 0;
|
| 1236 |
+
strmh->pts = 0;
|
| 1237 |
+
strmh->last_scr = 0;
|
| 1238 |
+
|
| 1239 |
+
frame_desc = uvc_find_frame_desc_stream(strmh, ctrl->bFormatIndex, ctrl->bFrameIndex);
|
| 1240 |
+
if (!frame_desc) {
|
| 1241 |
+
ret = UVC_ERROR_INVALID_PARAM;
|
| 1242 |
+
goto fail;
|
| 1243 |
+
}
|
| 1244 |
+
format_desc = frame_desc->parent;
|
| 1245 |
+
|
| 1246 |
+
strmh->frame_format = uvc_frame_format_for_guid(format_desc->guidFormat);
|
| 1247 |
+
if (strmh->frame_format == UVC_FRAME_FORMAT_UNKNOWN) {
|
| 1248 |
+
ret = UVC_ERROR_NOT_SUPPORTED;
|
| 1249 |
+
goto fail;
|
| 1250 |
+
}
|
| 1251 |
+
|
| 1252 |
+
// Get the interface that provides the chosen format and frame configuration
|
| 1253 |
+
interface_id = strmh->stream_if->bInterfaceNumber;
|
| 1254 |
+
interface = &strmh->devh->info->config->interface[interface_id];
|
| 1255 |
+
|
| 1256 |
+
/* A VS interface uses isochronous transfers iff it has multiple altsettings.
|
| 1257 |
+
* (UVC 1.5: 2.4.3. VideoStreaming Interface) */
|
| 1258 |
+
isochronous = interface->num_altsetting > 1;
|
| 1259 |
+
|
| 1260 |
+
if (isochronous) {
|
| 1261 |
+
/* For isochronous streaming, we choose an appropriate altsetting for the endpoint
|
| 1262 |
+
* and set up several transfers */
|
| 1263 |
+
const struct libusb_interface_descriptor *altsetting = 0;
|
| 1264 |
+
const struct libusb_endpoint_descriptor *endpoint = 0;
|
| 1265 |
+
/* The greatest number of bytes that the device might provide, per packet, in this
|
| 1266 |
+
* configuration */
|
| 1267 |
+
size_t config_bytes_per_packet;
|
| 1268 |
+
/* Number of packets per transfer */
|
| 1269 |
+
size_t packets_per_transfer = 0;
|
| 1270 |
+
/* Size of packet transferable from the chosen endpoint */
|
| 1271 |
+
size_t endpoint_bytes_per_packet = 0;
|
| 1272 |
+
/* Index of the altsetting */
|
| 1273 |
+
int alt_idx, ep_idx;
|
| 1274 |
+
const struct ksq_altsetting_quirk *quirk;
|
| 1275 |
+
|
| 1276 |
+
config_bytes_per_packet = strmh->cur_ctrl.dwMaxPayloadTransferSize;
|
| 1277 |
+
|
| 1278 |
+
/* Go through the altsettings and find one whose packets are at least
|
| 1279 |
+
* as big as our format's maximum per-packet usage. Assume that the
|
| 1280 |
+
* packet sizes are increasing. */
|
| 1281 |
+
for (alt_idx = 0; alt_idx < interface->num_altsetting; alt_idx++) {
|
| 1282 |
+
altsetting = interface->altsetting + alt_idx;
|
| 1283 |
+
endpoint_bytes_per_packet = 0;
|
| 1284 |
+
|
| 1285 |
+
/* Find the endpoint with the number specified in the VS header */
|
| 1286 |
+
for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
|
| 1287 |
+
endpoint = altsetting->endpoint + ep_idx;
|
| 1288 |
+
|
| 1289 |
+
struct libusb_ss_endpoint_companion_descriptor *ep_comp = 0;
|
| 1290 |
+
libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp);
|
| 1291 |
+
if (ep_comp)
|
| 1292 |
+
{
|
| 1293 |
+
endpoint_bytes_per_packet = ep_comp->wBytesPerInterval;
|
| 1294 |
+
libusb_free_ss_endpoint_companion_descriptor(ep_comp);
|
| 1295 |
+
break;
|
| 1296 |
+
}
|
| 1297 |
+
else
|
| 1298 |
+
{
|
| 1299 |
+
if (endpoint->bEndpointAddress == format_desc->parent->bEndpointAddress) {
|
| 1300 |
+
endpoint_bytes_per_packet = endpoint->wMaxPacketSize;
|
| 1301 |
+
// wMaxPacketSize: [unused:2 (multiplier-1):3 size:11]
|
| 1302 |
+
endpoint_bytes_per_packet = (endpoint_bytes_per_packet & 0x07ff) *
|
| 1303 |
+
(((endpoint_bytes_per_packet >> 11) & 3) + 1);
|
| 1304 |
+
break;
|
| 1305 |
+
}
|
| 1306 |
+
}
|
| 1307 |
+
}
|
| 1308 |
+
|
| 1309 |
+
if (endpoint_bytes_per_packet >= config_bytes_per_packet) {
|
| 1310 |
+
/* Transfers will be at most one frame long: Divide the maximum frame size
|
| 1311 |
+
* by the size of the endpoint and round up */
|
| 1312 |
+
packets_per_transfer = (ctrl->dwMaxVideoFrameSize +
|
| 1313 |
+
endpoint_bytes_per_packet - 1) / endpoint_bytes_per_packet;
|
| 1314 |
+
|
| 1315 |
+
/* But keep a reasonable limit: Otherwise we start dropping data */
|
| 1316 |
+
if (packets_per_transfer > 32)
|
| 1317 |
+
packets_per_transfer = 32;
|
| 1318 |
+
|
| 1319 |
+
total_transfer_size = packets_per_transfer * endpoint_bytes_per_packet;
|
| 1320 |
+
break;
|
| 1321 |
+
}
|
| 1322 |
+
}
|
| 1323 |
+
|
| 1324 |
+
if (_ksq_resolve_altsetting_for_ctrl(strmh->devh, ctrl, strmh->frame_format,
|
| 1325 |
+
frame_desc, &choice)) {
|
| 1326 |
+
int forced_alt_idx = -1;
|
| 1327 |
+
|
| 1328 |
+
for (alt_idx = 0; alt_idx < interface->num_altsetting; ++alt_idx) {
|
| 1329 |
+
const struct libusb_interface_descriptor *candidate =
|
| 1330 |
+
interface->altsetting + alt_idx;
|
| 1331 |
+
if (candidate->bAlternateSetting == choice.altsetting) {
|
| 1332 |
+
forced_alt_idx = alt_idx;
|
| 1333 |
+
altsetting = candidate;
|
| 1334 |
+
endpoint_bytes_per_packet = 0;
|
| 1335 |
+
for (ep_idx = 0; ep_idx < candidate->bNumEndpoints; ++ep_idx) {
|
| 1336 |
+
endpoint = candidate->endpoint + ep_idx;
|
| 1337 |
+
if (endpoint->bEndpointAddress ==
|
| 1338 |
+
format_desc->parent->bEndpointAddress) {
|
| 1339 |
+
endpoint_bytes_per_packet = endpoint->wMaxPacketSize;
|
| 1340 |
+
endpoint_bytes_per_packet =
|
| 1341 |
+
(endpoint_bytes_per_packet & 0x07ff) *
|
| 1342 |
+
(((endpoint_bytes_per_packet >> 11) & 3) + 1);
|
| 1343 |
+
break;
|
| 1344 |
+
}
|
| 1345 |
+
}
|
| 1346 |
+
break;
|
| 1347 |
+
}
|
| 1348 |
+
}
|
| 1349 |
+
|
| 1350 |
+
if (forced_alt_idx < 0 ||
|
| 1351 |
+
endpoint_bytes_per_packet < choice.payload) {
|
| 1352 |
+
fprintf(stderr,
|
| 1353 |
+
"[LIBUVC-QUIRK] VID:PID %04x:%04x alt=%u "
|
| 1354 |
+
"payload=%u unavailable endpoint=%zu%s\n",
|
| 1355 |
+
choice.vid, choice.pid,
|
| 1356 |
+
choice.altsetting, choice.payload,
|
| 1357 |
+
endpoint_bytes_per_packet,
|
| 1358 |
+
choice.from_override ? " (altsetting override)" : "");
|
| 1359 |
+
ret = UVC_ERROR_INVALID_MODE;
|
| 1360 |
+
goto fail;
|
| 1361 |
+
}
|
| 1362 |
+
|
| 1363 |
+
/* Recompute transfer geometry for the forced endpoint. */
|
| 1364 |
+
packets_per_transfer = (ctrl->dwMaxVideoFrameSize +
|
| 1365 |
+
endpoint_bytes_per_packet - 1) /
|
| 1366 |
+
endpoint_bytes_per_packet;
|
| 1367 |
+
if (packets_per_transfer > 32)
|
| 1368 |
+
packets_per_transfer = 32;
|
| 1369 |
+
total_transfer_size = packets_per_transfer * endpoint_bytes_per_packet;
|
| 1370 |
+
fprintf(stderr,
|
| 1371 |
+
"[LIBUVC-QUIRK] VID:PID %04x:%04x alt=%u "
|
| 1372 |
+
"payload=%u endpoint=%zu frame=%ux%u interval=%u"
|
| 1373 |
+
" xfers=%d xfer_bytes=%zu%s\n",
|
| 1374 |
+
choice.vid, choice.pid,
|
| 1375 |
+
choice.altsetting, choice.payload,
|
| 1376 |
+
endpoint_bytes_per_packet, frame_desc->wWidth,
|
| 1377 |
+
frame_desc->wHeight, ctrl->dwFrameInterval,
|
| 1378 |
+
packets_per_transfer, total_transfer_size,
|
| 1379 |
+
choice.from_override ? " (altsetting override)" : "");
|
| 1380 |
+
alt_idx = forced_alt_idx;
|
| 1381 |
+
}
|
| 1382 |
+
|
| 1383 |
+
/* If we searched through all the altsettings and found nothing usable */
|
| 1384 |
+
if (alt_idx == interface->num_altsetting) {
|
| 1385 |
+
ret = UVC_ERROR_INVALID_MODE;
|
| 1386 |
+
goto fail;
|
| 1387 |
+
}
|
| 1388 |
+
|
| 1389 |
+
/* Select the altsetting */
|
| 1390 |
+
ret = libusb_set_interface_alt_setting(strmh->devh->usb_devh,
|
| 1391 |
+
altsetting->bInterfaceNumber,
|
| 1392 |
+
altsetting->bAlternateSetting);
|
| 1393 |
+
if (ret != UVC_SUCCESS) {
|
| 1394 |
+
UVC_DEBUG("libusb_set_interface_alt_setting failed");
|
| 1395 |
+
goto fail;
|
| 1396 |
+
}
|
| 1397 |
+
|
| 1398 |
+
/* Set up the transfers */
|
| 1399 |
+
for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; ++transfer_id) {
|
| 1400 |
+
transfer = libusb_alloc_transfer(packets_per_transfer);
|
| 1401 |
+
strmh->transfers[transfer_id] = transfer;
|
| 1402 |
+
strmh->transfer_bufs[transfer_id] = malloc(total_transfer_size);
|
| 1403 |
+
|
| 1404 |
+
libusb_fill_iso_transfer(
|
| 1405 |
+
transfer, strmh->devh->usb_devh, format_desc->parent->bEndpointAddress,
|
| 1406 |
+
strmh->transfer_bufs[transfer_id],
|
| 1407 |
+
total_transfer_size, packets_per_transfer, _uvc_stream_callback, (void*) strmh, 5000);
|
| 1408 |
+
|
| 1409 |
+
libusb_set_iso_packet_lengths(transfer, endpoint_bytes_per_packet);
|
| 1410 |
+
}
|
| 1411 |
+
} else {
|
| 1412 |
+
for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS;
|
| 1413 |
+
++transfer_id) {
|
| 1414 |
+
transfer = libusb_alloc_transfer(0);
|
| 1415 |
+
strmh->transfers[transfer_id] = transfer;
|
| 1416 |
+
strmh->transfer_bufs[transfer_id] = malloc (
|
| 1417 |
+
strmh->cur_ctrl.dwMaxPayloadTransferSize );
|
| 1418 |
+
libusb_fill_bulk_transfer ( transfer, strmh->devh->usb_devh,
|
| 1419 |
+
format_desc->parent->bEndpointAddress,
|
| 1420 |
+
strmh->transfer_bufs[transfer_id],
|
| 1421 |
+
strmh->cur_ctrl.dwMaxPayloadTransferSize, _uvc_stream_callback,
|
| 1422 |
+
( void* ) strmh, 5000 );
|
| 1423 |
+
}
|
| 1424 |
+
}
|
| 1425 |
+
|
| 1426 |
+
strmh->user_cb = cb;
|
| 1427 |
+
strmh->user_ptr = user_ptr;
|
| 1428 |
+
|
| 1429 |
+
/* If the user wants it, set up a thread that calls the user's function
|
| 1430 |
+
* with the contents of each frame.
|
| 1431 |
+
*/
|
| 1432 |
+
if (cb) {
|
| 1433 |
+
pthread_create(&strmh->cb_thread, NULL, _uvc_user_caller, (void*) strmh);
|
| 1434 |
+
}
|
| 1435 |
+
|
| 1436 |
+
for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS;
|
| 1437 |
+
transfer_id++) {
|
| 1438 |
+
ret = libusb_submit_transfer(strmh->transfers[transfer_id]);
|
| 1439 |
+
if (ret != UVC_SUCCESS) {
|
| 1440 |
+
UVC_DEBUG("libusb_submit_transfer failed: %d",ret);
|
| 1441 |
+
break;
|
| 1442 |
+
}
|
| 1443 |
+
}
|
| 1444 |
+
|
| 1445 |
+
if ( ret != UVC_SUCCESS && transfer_id >= 0 ) {
|
| 1446 |
+
for ( ; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; transfer_id++) {
|
| 1447 |
+
free ( strmh->transfers[transfer_id]->buffer );
|
| 1448 |
+
libusb_free_transfer ( strmh->transfers[transfer_id]);
|
| 1449 |
+
strmh->transfers[transfer_id] = 0;
|
| 1450 |
+
}
|
| 1451 |
+
ret = UVC_SUCCESS;
|
| 1452 |
+
}
|
| 1453 |
+
|
| 1454 |
+
UVC_EXIT(ret);
|
| 1455 |
+
return ret;
|
| 1456 |
+
fail:
|
| 1457 |
+
strmh->running = 0;
|
| 1458 |
+
UVC_EXIT(ret);
|
| 1459 |
+
return ret;
|
| 1460 |
+
}
|
| 1461 |
+
|
| 1462 |
+
/** Begin streaming video from the stream into the callback function.
|
| 1463 |
+
* @ingroup streaming
|
| 1464 |
+
*
|
| 1465 |
+
* @deprecated The stream type (bulk vs. isochronous) will be determined by the
|
| 1466 |
+
* type of interface associated with the uvc_stream_ctrl_t parameter, regardless
|
| 1467 |
+
* of whether the caller requests isochronous streaming. Please switch to
|
| 1468 |
+
* uvc_stream_start().
|
| 1469 |
+
*
|
| 1470 |
+
* @param strmh UVC stream
|
| 1471 |
+
* @param cb User callback function. See {uvc_frame_callback_t} for restrictions.
|
| 1472 |
+
*/
|
| 1473 |
+
uvc_error_t uvc_stream_start_iso(
|
| 1474 |
+
uvc_stream_handle_t *strmh,
|
| 1475 |
+
uvc_frame_callback_t *cb,
|
| 1476 |
+
void *user_ptr
|
| 1477 |
+
) {
|
| 1478 |
+
return uvc_stream_start(strmh, cb, user_ptr, 0);
|
| 1479 |
+
}
|
| 1480 |
+
|
| 1481 |
+
/** @internal
|
| 1482 |
+
* @brief User callback runner thread
|
| 1483 |
+
* @note There should be at most one of these per currently streaming device
|
| 1484 |
+
* @param arg Device handle
|
| 1485 |
+
*/
|
| 1486 |
+
void *_uvc_user_caller(void *arg) {
|
| 1487 |
+
uvc_stream_handle_t *strmh = (uvc_stream_handle_t *) arg;
|
| 1488 |
+
|
| 1489 |
+
uint32_t last_seq = 0;
|
| 1490 |
+
|
| 1491 |
+
do {
|
| 1492 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 1493 |
+
|
| 1494 |
+
while (strmh->running && last_seq == strmh->hold_seq) {
|
| 1495 |
+
pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex);
|
| 1496 |
+
}
|
| 1497 |
+
|
| 1498 |
+
if (!strmh->running) {
|
| 1499 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1500 |
+
break;
|
| 1501 |
+
}
|
| 1502 |
+
|
| 1503 |
+
last_seq = strmh->hold_seq;
|
| 1504 |
+
_uvc_populate_frame(strmh);
|
| 1505 |
+
|
| 1506 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1507 |
+
|
| 1508 |
+
strmh->user_cb(&strmh->frame, strmh->user_ptr);
|
| 1509 |
+
} while(1);
|
| 1510 |
+
|
| 1511 |
+
return NULL; // return value ignored
|
| 1512 |
+
}
|
| 1513 |
+
|
| 1514 |
+
/** @internal
|
| 1515 |
+
* @brief Populate the fields of a frame to be handed to user code
|
| 1516 |
+
* must be called with stream cb lock held!
|
| 1517 |
+
*/
|
| 1518 |
+
void _uvc_populate_frame(uvc_stream_handle_t *strmh) {
|
| 1519 |
+
uvc_frame_t *frame = &strmh->frame;
|
| 1520 |
+
uvc_frame_desc_t *frame_desc;
|
| 1521 |
+
|
| 1522 |
+
/** @todo this stuff that hits the main config cache should really happen
|
| 1523 |
+
* in start() so that only one thread hits these data. all of this stuff
|
| 1524 |
+
* is going to be reopen_on_change anyway
|
| 1525 |
+
*/
|
| 1526 |
+
|
| 1527 |
+
frame_desc = uvc_find_frame_desc(strmh->devh, strmh->cur_ctrl.bFormatIndex,
|
| 1528 |
+
strmh->cur_ctrl.bFrameIndex);
|
| 1529 |
+
|
| 1530 |
+
frame->frame_format = strmh->frame_format;
|
| 1531 |
+
|
| 1532 |
+
frame->width = frame_desc->wWidth;
|
| 1533 |
+
frame->height = frame_desc->wHeight;
|
| 1534 |
+
|
| 1535 |
+
switch (frame->frame_format) {
|
| 1536 |
+
case UVC_FRAME_FORMAT_BGR:
|
| 1537 |
+
frame->step = frame->width * 3;
|
| 1538 |
+
break;
|
| 1539 |
+
case UVC_FRAME_FORMAT_YUYV:
|
| 1540 |
+
frame->step = frame->width * 2;
|
| 1541 |
+
break;
|
| 1542 |
+
case UVC_FRAME_FORMAT_NV12:
|
| 1543 |
+
frame->step = frame->width;
|
| 1544 |
+
break;
|
| 1545 |
+
case UVC_FRAME_FORMAT_P010:
|
| 1546 |
+
frame->step = frame->width * 2;
|
| 1547 |
+
break;
|
| 1548 |
+
case UVC_FRAME_FORMAT_MJPEG:
|
| 1549 |
+
frame->step = 0;
|
| 1550 |
+
break;
|
| 1551 |
+
case UVC_FRAME_FORMAT_H264:
|
| 1552 |
+
frame->step = 0;
|
| 1553 |
+
break;
|
| 1554 |
+
default:
|
| 1555 |
+
frame->step = 0;
|
| 1556 |
+
break;
|
| 1557 |
+
}
|
| 1558 |
+
|
| 1559 |
+
frame->sequence = strmh->hold_seq;
|
| 1560 |
+
frame->capture_time_finished = strmh->capture_time_finished;
|
| 1561 |
+
|
| 1562 |
+
/* copy the image data from the hold buffer to the frame (unnecessary extra buf?) */
|
| 1563 |
+
if (frame->data_bytes < strmh->hold_bytes) {
|
| 1564 |
+
frame->data = realloc(frame->data, strmh->hold_bytes);
|
| 1565 |
+
}
|
| 1566 |
+
frame->data_bytes = strmh->hold_bytes;
|
| 1567 |
+
memcpy(frame->data, strmh->holdbuf, frame->data_bytes);
|
| 1568 |
+
|
| 1569 |
+
if (strmh->meta_hold_bytes > 0)
|
| 1570 |
+
{
|
| 1571 |
+
if (frame->metadata_bytes < strmh->meta_hold_bytes)
|
| 1572 |
+
{
|
| 1573 |
+
frame->metadata = realloc(frame->metadata, strmh->meta_hold_bytes);
|
| 1574 |
+
}
|
| 1575 |
+
frame->metadata_bytes = strmh->meta_hold_bytes;
|
| 1576 |
+
memcpy(frame->metadata, strmh->meta_holdbuf, frame->metadata_bytes);
|
| 1577 |
+
}
|
| 1578 |
+
}
|
| 1579 |
+
|
| 1580 |
+
/** Poll for a frame
|
| 1581 |
+
* @ingroup streaming
|
| 1582 |
+
*
|
| 1583 |
+
* @param devh UVC device
|
| 1584 |
+
* @param[out] frame Location to store pointer to captured frame (NULL on error)
|
| 1585 |
+
* @param timeout_us >0: Wait at most N microseconds; 0: Wait indefinitely; -1: return immediately
|
| 1586 |
+
*/
|
| 1587 |
+
uvc_error_t uvc_stream_get_frame(uvc_stream_handle_t *strmh,
|
| 1588 |
+
uvc_frame_t **frame,
|
| 1589 |
+
int32_t timeout_us) {
|
| 1590 |
+
time_t add_secs;
|
| 1591 |
+
time_t add_nsecs;
|
| 1592 |
+
struct timespec ts;
|
| 1593 |
+
|
| 1594 |
+
if (!strmh->running)
|
| 1595 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 1596 |
+
|
| 1597 |
+
if (strmh->user_cb)
|
| 1598 |
+
return UVC_ERROR_CALLBACK_EXISTS;
|
| 1599 |
+
|
| 1600 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 1601 |
+
|
| 1602 |
+
if (strmh->last_polled_seq < strmh->hold_seq) {
|
| 1603 |
+
_uvc_populate_frame(strmh);
|
| 1604 |
+
*frame = &strmh->frame;
|
| 1605 |
+
strmh->last_polled_seq = strmh->hold_seq;
|
| 1606 |
+
} else if (timeout_us != -1) {
|
| 1607 |
+
if (timeout_us == 0) {
|
| 1608 |
+
pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex);
|
| 1609 |
+
} else {
|
| 1610 |
+
add_secs = timeout_us / 1000000;
|
| 1611 |
+
add_nsecs = (timeout_us % 1000000) * 1000;
|
| 1612 |
+
ts.tv_sec = 0;
|
| 1613 |
+
ts.tv_nsec = 0;
|
| 1614 |
+
|
| 1615 |
+
#if _POSIX_TIMERS > 0
|
| 1616 |
+
clock_gettime(CLOCK_REALTIME, &ts);
|
| 1617 |
+
#else
|
| 1618 |
+
struct timeval tv;
|
| 1619 |
+
gettimeofday(&tv, NULL);
|
| 1620 |
+
ts.tv_sec = tv.tv_sec;
|
| 1621 |
+
ts.tv_nsec = tv.tv_usec * 1000;
|
| 1622 |
+
#endif
|
| 1623 |
+
|
| 1624 |
+
ts.tv_sec += add_secs;
|
| 1625 |
+
ts.tv_nsec += add_nsecs;
|
| 1626 |
+
|
| 1627 |
+
/* pthread_cond_timedwait FAILS with EINVAL if ts.tv_nsec > 1000000000 (1 billion)
|
| 1628 |
+
* Since we are just adding values to the timespec, we have to increment the seconds if nanoseconds is greater than 1 billion,
|
| 1629 |
+
* and then re-adjust the nanoseconds in the correct range.
|
| 1630 |
+
* */
|
| 1631 |
+
ts.tv_sec += ts.tv_nsec / 1000000000;
|
| 1632 |
+
ts.tv_nsec = ts.tv_nsec % 1000000000;
|
| 1633 |
+
|
| 1634 |
+
int err = pthread_cond_timedwait(&strmh->cb_cond, &strmh->cb_mutex, &ts);
|
| 1635 |
+
|
| 1636 |
+
//TODO: How should we handle EINVAL?
|
| 1637 |
+
if (err) {
|
| 1638 |
+
*frame = NULL;
|
| 1639 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1640 |
+
return err == ETIMEDOUT ? UVC_ERROR_TIMEOUT : UVC_ERROR_OTHER;
|
| 1641 |
+
}
|
| 1642 |
+
}
|
| 1643 |
+
|
| 1644 |
+
if (strmh->last_polled_seq < strmh->hold_seq) {
|
| 1645 |
+
_uvc_populate_frame(strmh);
|
| 1646 |
+
*frame = &strmh->frame;
|
| 1647 |
+
strmh->last_polled_seq = strmh->hold_seq;
|
| 1648 |
+
} else {
|
| 1649 |
+
*frame = NULL;
|
| 1650 |
+
}
|
| 1651 |
+
} else {
|
| 1652 |
+
*frame = NULL;
|
| 1653 |
+
}
|
| 1654 |
+
|
| 1655 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1656 |
+
|
| 1657 |
+
return UVC_SUCCESS;
|
| 1658 |
+
}
|
| 1659 |
+
|
| 1660 |
+
/** @brief Stop streaming video
|
| 1661 |
+
* @ingroup streaming
|
| 1662 |
+
*
|
| 1663 |
+
* Closes all streams, ends threads and cancels pollers
|
| 1664 |
+
*
|
| 1665 |
+
* @param devh UVC device
|
| 1666 |
+
*/
|
| 1667 |
+
void uvc_stop_streaming(uvc_device_handle_t *devh) {
|
| 1668 |
+
uvc_stream_handle_t *strmh, *strmh_tmp;
|
| 1669 |
+
|
| 1670 |
+
DL_FOREACH_SAFE(devh->streams, strmh, strmh_tmp) {
|
| 1671 |
+
uvc_stream_close(strmh);
|
| 1672 |
+
}
|
| 1673 |
+
}
|
| 1674 |
+
|
| 1675 |
+
/** @brief Stop stream.
|
| 1676 |
+
* @ingroup streaming
|
| 1677 |
+
*
|
| 1678 |
+
* Stops stream, ends threads and cancels pollers
|
| 1679 |
+
*
|
| 1680 |
+
* @param devh UVC device
|
| 1681 |
+
*/
|
| 1682 |
+
uvc_error_t uvc_stream_stop(uvc_stream_handle_t *strmh) {
|
| 1683 |
+
int i;
|
| 1684 |
+
|
| 1685 |
+
if (!strmh->running)
|
| 1686 |
+
return UVC_ERROR_INVALID_PARAM;
|
| 1687 |
+
|
| 1688 |
+
strmh->running = 0;
|
| 1689 |
+
|
| 1690 |
+
pthread_mutex_lock(&strmh->cb_mutex);
|
| 1691 |
+
|
| 1692 |
+
/* Attempt to cancel any running transfers, we can't free them just yet because they aren't
|
| 1693 |
+
* necessarily completed but they will be free'd in _uvc_stream_callback().
|
| 1694 |
+
*/
|
| 1695 |
+
for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) {
|
| 1696 |
+
if(strmh->transfers[i] != NULL)
|
| 1697 |
+
libusb_cancel_transfer(strmh->transfers[i]);
|
| 1698 |
+
}
|
| 1699 |
+
|
| 1700 |
+
/* Wait for transfers to complete/cancel */
|
| 1701 |
+
do {
|
| 1702 |
+
for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) {
|
| 1703 |
+
if(strmh->transfers[i] != NULL)
|
| 1704 |
+
break;
|
| 1705 |
+
}
|
| 1706 |
+
if(i == LIBUVC_NUM_TRANSFER_BUFS )
|
| 1707 |
+
break;
|
| 1708 |
+
pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex);
|
| 1709 |
+
} while(1);
|
| 1710 |
+
// Kick the user thread awake
|
| 1711 |
+
pthread_cond_broadcast(&strmh->cb_cond);
|
| 1712 |
+
pthread_mutex_unlock(&strmh->cb_mutex);
|
| 1713 |
+
|
| 1714 |
+
/** @todo stop the actual stream, camera side? */
|
| 1715 |
+
|
| 1716 |
+
if (strmh->user_cb) {
|
| 1717 |
+
/* wait for the thread to stop (triggered by
|
| 1718 |
+
* LIBUSB_TRANSFER_CANCELLED transfer) */
|
| 1719 |
+
pthread_join(strmh->cb_thread, NULL);
|
| 1720 |
+
}
|
| 1721 |
+
|
| 1722 |
+
return UVC_SUCCESS;
|
| 1723 |
+
}
|
| 1724 |
+
|
| 1725 |
+
/** @brief Close stream.
|
| 1726 |
+
* @ingroup streaming
|
| 1727 |
+
*
|
| 1728 |
+
* Closes stream, frees handle and all streaming resources.
|
| 1729 |
+
*
|
| 1730 |
+
* @param strmh UVC stream handle
|
| 1731 |
+
*/
|
| 1732 |
+
void uvc_stream_close(uvc_stream_handle_t *strmh) {
|
| 1733 |
+
if (strmh->running)
|
| 1734 |
+
uvc_stream_stop(strmh);
|
| 1735 |
+
|
| 1736 |
+
uvc_release_if(strmh->devh, strmh->stream_if->bInterfaceNumber);
|
| 1737 |
+
|
| 1738 |
+
if (strmh->frame.data)
|
| 1739 |
+
free(strmh->frame.data);
|
| 1740 |
+
|
| 1741 |
+
free(strmh->outbuf);
|
| 1742 |
+
free(strmh->holdbuf);
|
| 1743 |
+
|
| 1744 |
+
free(strmh->meta_outbuf);
|
| 1745 |
+
free(strmh->meta_holdbuf);
|
| 1746 |
+
|
| 1747 |
+
pthread_cond_destroy(&strmh->cb_cond);
|
| 1748 |
+
pthread_mutex_destroy(&strmh->cb_mutex);
|
| 1749 |
+
|
| 1750 |
+
DL_DELETE(strmh->devh->streams, strmh);
|
| 1751 |
+
free(strmh);
|
| 1752 |
+
}
|
collector/core/gripper/camera_service_src/third_party/libuvc/src/test.c
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright (C) 2010-2012 Ken Tossell
|
| 5 |
+
* All rights reserved.
|
| 6 |
+
*
|
| 7 |
+
* Redistribution and use in source and binary forms, with or without
|
| 8 |
+
* modification, are permitted provided that the following conditions
|
| 9 |
+
* are met:
|
| 10 |
+
*
|
| 11 |
+
* * Redistributions of source code must retain the above copyright
|
| 12 |
+
* notice, this list of conditions and the following disclaimer.
|
| 13 |
+
* * Redistributions in binary form must reproduce the above
|
| 14 |
+
* copyright notice, this list of conditions and the following
|
| 15 |
+
* disclaimer in the documentation and/or other materials provided
|
| 16 |
+
* with the distribution.
|
| 17 |
+
* * Neither the name of the author nor other contributors may be
|
| 18 |
+
* used to endorse or promote products derived from this software
|
| 19 |
+
* without specific prior written permission.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
* POSSIBILITY OF SUCH DAMAGE.
|
| 33 |
+
*********************************************************************/
|
| 34 |
+
#include <stdio.h>
|
| 35 |
+
#include <opencv2/highgui/highgui_c.h>
|
| 36 |
+
|
| 37 |
+
#include "libuvc/libuvc.h"
|
| 38 |
+
|
| 39 |
+
void cb(uvc_frame_t *frame, void *ptr) {
|
| 40 |
+
uvc_frame_t *bgr;
|
| 41 |
+
uvc_error_t ret;
|
| 42 |
+
IplImage* cvImg;
|
| 43 |
+
|
| 44 |
+
printf("callback! length = %u, ptr = %d\n", frame->data_bytes, (int) ptr);
|
| 45 |
+
|
| 46 |
+
bgr = uvc_allocate_frame(frame->width * frame->height * 3);
|
| 47 |
+
if (!bgr) {
|
| 48 |
+
printf("unable to allocate bgr frame!");
|
| 49 |
+
return;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
ret = uvc_any2bgr(frame, bgr);
|
| 53 |
+
if (ret) {
|
| 54 |
+
uvc_perror(ret, "uvc_any2bgr");
|
| 55 |
+
uvc_free_frame(bgr);
|
| 56 |
+
return;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
cvImg = cvCreateImageHeader(
|
| 60 |
+
cvSize(bgr->width, bgr->height),
|
| 61 |
+
IPL_DEPTH_8U,
|
| 62 |
+
3);
|
| 63 |
+
|
| 64 |
+
cvSetData(cvImg, bgr->data, bgr->width * 3);
|
| 65 |
+
|
| 66 |
+
cvNamedWindow("Test", CV_WINDOW_AUTOSIZE);
|
| 67 |
+
cvShowImage("Test", cvImg);
|
| 68 |
+
cvWaitKey(10);
|
| 69 |
+
|
| 70 |
+
cvReleaseImageHeader(&cvImg);
|
| 71 |
+
|
| 72 |
+
uvc_free_frame(bgr);
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
int main(int argc, char **argv) {
|
| 76 |
+
uvc_context_t *ctx;
|
| 77 |
+
uvc_error_t res;
|
| 78 |
+
uvc_device_t *dev;
|
| 79 |
+
uvc_device_handle_t *devh;
|
| 80 |
+
uvc_stream_ctrl_t ctrl;
|
| 81 |
+
|
| 82 |
+
res = uvc_init(&ctx, NULL);
|
| 83 |
+
|
| 84 |
+
if (res < 0) {
|
| 85 |
+
uvc_perror(res, "uvc_init");
|
| 86 |
+
return res;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
puts("UVC initialized");
|
| 90 |
+
|
| 91 |
+
res = uvc_find_device(
|
| 92 |
+
ctx, &dev,
|
| 93 |
+
0, 0, NULL);
|
| 94 |
+
|
| 95 |
+
if (res < 0) {
|
| 96 |
+
uvc_perror(res, "uvc_find_device");
|
| 97 |
+
} else {
|
| 98 |
+
puts("Device found");
|
| 99 |
+
|
| 100 |
+
res = uvc_open(dev, &devh);
|
| 101 |
+
|
| 102 |
+
if (res < 0) {
|
| 103 |
+
uvc_perror(res, "uvc_open");
|
| 104 |
+
} else {
|
| 105 |
+
puts("Device opened");
|
| 106 |
+
|
| 107 |
+
uvc_print_diag(devh, stderr);
|
| 108 |
+
|
| 109 |
+
res = uvc_get_stream_ctrl_format_size(
|
| 110 |
+
devh, &ctrl, UVC_FRAME_FORMAT_YUYV, 640, 480, 30
|
| 111 |
+
);
|
| 112 |
+
|
| 113 |
+
uvc_print_stream_ctrl(&ctrl, stderr);
|
| 114 |
+
|
| 115 |
+
if (res < 0) {
|
| 116 |
+
uvc_perror(res, "get_mode");
|
| 117 |
+
} else {
|
| 118 |
+
res = uvc_start_streaming(devh, &ctrl, cb, 12345, 0);
|
| 119 |
+
|
| 120 |
+
if (res < 0) {
|
| 121 |
+
uvc_perror(res, "start_streaming");
|
| 122 |
+
} else {
|
| 123 |
+
puts("Streaming for 10 seconds...");
|
| 124 |
+
uvc_error_t resAEMODE = uvc_set_ae_mode(devh, 1);
|
| 125 |
+
uvc_perror(resAEMODE, "set_ae_mode");
|
| 126 |
+
int i;
|
| 127 |
+
for (i = 1; i <= 10; i++) {
|
| 128 |
+
/* uvc_error_t resPT = uvc_set_pantilt_abs(devh, i * 20 * 3600, 0); */
|
| 129 |
+
/* uvc_perror(resPT, "set_pt_abs"); */
|
| 130 |
+
uvc_error_t resEXP = uvc_set_exposure_abs(devh, 20 + i * 5);
|
| 131 |
+
uvc_perror(resEXP, "set_exp_abs");
|
| 132 |
+
|
| 133 |
+
sleep(1);
|
| 134 |
+
}
|
| 135 |
+
sleep(10);
|
| 136 |
+
uvc_stop_streaming(devh);
|
| 137 |
+
puts("Done streaming.");
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
uvc_close(devh);
|
| 142 |
+
puts("Device closed");
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
uvc_unref_device(dev);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
uvc_exit(ctx);
|
| 149 |
+
puts("UVC exited");
|
| 150 |
+
|
| 151 |
+
return 0;
|
| 152 |
+
}
|
| 153 |
+
|
collector/core/gripper/camera_service_src/third_party/libuvc/standard-units.yaml
ADDED
|
@@ -0,0 +1,518 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
units:
|
| 2 |
+
camera_terminal:
|
| 3 |
+
type: standard
|
| 4 |
+
description: Standard camera input terminal (captures images from sensor)
|
| 5 |
+
control_prefix: CT
|
| 6 |
+
controls:
|
| 7 |
+
scanning_mode:
|
| 8 |
+
control: SCANNING_MODE
|
| 9 |
+
length: 1
|
| 10 |
+
fields:
|
| 11 |
+
mode:
|
| 12 |
+
type: int
|
| 13 |
+
position: 0
|
| 14 |
+
length: 1
|
| 15 |
+
doc: '0: interlaced, 1: progressive'
|
| 16 |
+
ae_mode:
|
| 17 |
+
control: AE_MODE
|
| 18 |
+
length: 1
|
| 19 |
+
fields:
|
| 20 |
+
mode:
|
| 21 |
+
type: int
|
| 22 |
+
position: 0
|
| 23 |
+
length: 1
|
| 24 |
+
doc: '1: manual mode; 2: auto mode; 4: shutter priority mode; 8: aperture
|
| 25 |
+
priority mode'
|
| 26 |
+
doc:
|
| 27 |
+
get: |-
|
| 28 |
+
@brief Reads camera's auto-exposure mode.
|
| 29 |
+
|
| 30 |
+
See uvc_set_ae_mode() for a description of the available modes.
|
| 31 |
+
set: |-
|
| 32 |
+
@brief Sets camera's auto-exposure mode.
|
| 33 |
+
|
| 34 |
+
Cameras may support any of the following AE modes:
|
| 35 |
+
* UVC_AUTO_EXPOSURE_MODE_MANUAL (1) - manual exposure time, manual iris
|
| 36 |
+
* UVC_AUTO_EXPOSURE_MODE_AUTO (2) - auto exposure time, auto iris
|
| 37 |
+
* UVC_AUTO_EXPOSURE_MODE_SHUTTER_PRIORITY (4) - manual exposure time, auto iris
|
| 38 |
+
* UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY (8) - auto exposure time, manual iris
|
| 39 |
+
|
| 40 |
+
Most cameras provide manual mode and aperture priority mode.
|
| 41 |
+
ae_priority:
|
| 42 |
+
control: AE_PRIORITY
|
| 43 |
+
length: 1
|
| 44 |
+
fields:
|
| 45 |
+
priority:
|
| 46 |
+
type: int
|
| 47 |
+
position: 0
|
| 48 |
+
length: 1
|
| 49 |
+
doc: '0: frame rate must remain constant; 1: frame rate may be varied
|
| 50 |
+
for AE purposes'
|
| 51 |
+
doc:
|
| 52 |
+
get: |-
|
| 53 |
+
@brief Checks whether the camera may vary the frame rate for exposure control reasons.
|
| 54 |
+
See uvc_set_ae_priority() for a description of the `priority` field.
|
| 55 |
+
set: |-
|
| 56 |
+
@brief Chooses whether the camera may vary the frame rate for exposure control reasons.
|
| 57 |
+
A `priority` value of zero means the camera may not vary its frame rate. A value of 1
|
| 58 |
+
means the frame rate is variable. This setting has no effect outside of the `auto` and
|
| 59 |
+
`shutter_priority` auto-exposure modes.
|
| 60 |
+
exposure_abs:
|
| 61 |
+
control: EXPOSURE_TIME_ABSOLUTE
|
| 62 |
+
length: 4
|
| 63 |
+
fields:
|
| 64 |
+
time:
|
| 65 |
+
type: int
|
| 66 |
+
position: 0
|
| 67 |
+
length: 4
|
| 68 |
+
doc: ''
|
| 69 |
+
doc:
|
| 70 |
+
get: |-
|
| 71 |
+
@brief Gets the absolute exposure time.
|
| 72 |
+
|
| 73 |
+
See uvc_set_exposure_abs() for a description of the `time` field.
|
| 74 |
+
set: |-
|
| 75 |
+
@brief Sets the absolute exposure time.
|
| 76 |
+
|
| 77 |
+
The `time` parameter should be provided in units of 0.0001 seconds (e.g., use the value 100
|
| 78 |
+
for a 10ms exposure period). Auto exposure should be set to `manual` or `shutter_priority`
|
| 79 |
+
before attempting to change this setting.
|
| 80 |
+
exposure_rel:
|
| 81 |
+
control: EXPOSURE_TIME_RELATIVE
|
| 82 |
+
length: 1
|
| 83 |
+
fields:
|
| 84 |
+
step:
|
| 85 |
+
type: int
|
| 86 |
+
position: 0
|
| 87 |
+
length: 1
|
| 88 |
+
signed: true
|
| 89 |
+
doc: number of steps by which to change the exposure time, or zero to
|
| 90 |
+
set the default exposure time
|
| 91 |
+
doc: '@brief {gets_sets} the exposure time relative to the current setting.'
|
| 92 |
+
focus_abs:
|
| 93 |
+
control: FOCUS_ABSOLUTE
|
| 94 |
+
length: 2
|
| 95 |
+
fields:
|
| 96 |
+
focus:
|
| 97 |
+
type: int
|
| 98 |
+
position: 0
|
| 99 |
+
length: 2
|
| 100 |
+
doc: focal target distance in millimeters
|
| 101 |
+
doc: '@brief {gets_sets} the distance at which an object is optimally focused.'
|
| 102 |
+
focus_rel:
|
| 103 |
+
control: FOCUS_RELATIVE
|
| 104 |
+
length: 2
|
| 105 |
+
fields:
|
| 106 |
+
focus_rel:
|
| 107 |
+
type: int
|
| 108 |
+
position: 0
|
| 109 |
+
length: 1
|
| 110 |
+
signed: true
|
| 111 |
+
doc: TODO
|
| 112 |
+
speed:
|
| 113 |
+
type: int
|
| 114 |
+
position: 1
|
| 115 |
+
length: 1
|
| 116 |
+
doc: TODO
|
| 117 |
+
focus_simple_range:
|
| 118 |
+
control: FOCUS_SIMPLE
|
| 119 |
+
length: 1
|
| 120 |
+
fields:
|
| 121 |
+
focus:
|
| 122 |
+
type: int
|
| 123 |
+
position: 0
|
| 124 |
+
length: 1
|
| 125 |
+
doc: TODO
|
| 126 |
+
focus_auto:
|
| 127 |
+
control: FOCUS_AUTO
|
| 128 |
+
length: 1
|
| 129 |
+
fields:
|
| 130 |
+
state:
|
| 131 |
+
type: int
|
| 132 |
+
position: 0
|
| 133 |
+
length: 1
|
| 134 |
+
doc: TODO
|
| 135 |
+
iris_abs:
|
| 136 |
+
control: IRIS_ABSOLUTE
|
| 137 |
+
length: 2
|
| 138 |
+
fields:
|
| 139 |
+
iris:
|
| 140 |
+
type: int
|
| 141 |
+
position: 0
|
| 142 |
+
length: 2
|
| 143 |
+
doc: TODO
|
| 144 |
+
iris_rel:
|
| 145 |
+
control: IRIS_RELATIVE
|
| 146 |
+
length: 1
|
| 147 |
+
fields:
|
| 148 |
+
iris_rel:
|
| 149 |
+
type: int
|
| 150 |
+
position: 0
|
| 151 |
+
length: 1
|
| 152 |
+
doc: TODO
|
| 153 |
+
zoom_abs:
|
| 154 |
+
control: ZOOM_ABSOLUTE
|
| 155 |
+
length: 2
|
| 156 |
+
fields:
|
| 157 |
+
focal_length:
|
| 158 |
+
type: int
|
| 159 |
+
position: 0
|
| 160 |
+
length: 2
|
| 161 |
+
doc: TODO
|
| 162 |
+
zoom_rel:
|
| 163 |
+
control: ZOOM_RELATIVE
|
| 164 |
+
length: 3
|
| 165 |
+
fields:
|
| 166 |
+
zoom_rel:
|
| 167 |
+
type: int
|
| 168 |
+
position: 0
|
| 169 |
+
length: 1
|
| 170 |
+
signed: true
|
| 171 |
+
doc: TODO
|
| 172 |
+
digital_zoom:
|
| 173 |
+
type: int
|
| 174 |
+
position: 1
|
| 175 |
+
length: 1
|
| 176 |
+
doc: TODO
|
| 177 |
+
speed:
|
| 178 |
+
type: int
|
| 179 |
+
position: 2
|
| 180 |
+
length: 1
|
| 181 |
+
doc: TODO
|
| 182 |
+
pantilt_abs:
|
| 183 |
+
control: PANTILT_ABSOLUTE
|
| 184 |
+
length: 8
|
| 185 |
+
fields:
|
| 186 |
+
pan:
|
| 187 |
+
type: int
|
| 188 |
+
position: 0
|
| 189 |
+
length: 4
|
| 190 |
+
signed: true
|
| 191 |
+
doc: TODO
|
| 192 |
+
tilt:
|
| 193 |
+
type: int
|
| 194 |
+
position: 4
|
| 195 |
+
length: 4
|
| 196 |
+
signed: true
|
| 197 |
+
doc: TODO
|
| 198 |
+
pantilt_rel:
|
| 199 |
+
control: PANTILT_RELATIVE
|
| 200 |
+
length: 4
|
| 201 |
+
fields:
|
| 202 |
+
pan_rel:
|
| 203 |
+
type: int
|
| 204 |
+
position: 0
|
| 205 |
+
length: 1
|
| 206 |
+
signed: true
|
| 207 |
+
doc: TODO
|
| 208 |
+
pan_speed:
|
| 209 |
+
type: int
|
| 210 |
+
position: 1
|
| 211 |
+
length: 1
|
| 212 |
+
doc: TODO
|
| 213 |
+
tilt_rel:
|
| 214 |
+
type: int
|
| 215 |
+
position: 2
|
| 216 |
+
length: 1
|
| 217 |
+
signed: true
|
| 218 |
+
doc: TODO
|
| 219 |
+
tilt_speed:
|
| 220 |
+
type: int
|
| 221 |
+
position: 3
|
| 222 |
+
length: 1
|
| 223 |
+
doc: TODO
|
| 224 |
+
roll_abs:
|
| 225 |
+
control: ROLL_ABSOLUTE
|
| 226 |
+
length: 2
|
| 227 |
+
fields:
|
| 228 |
+
roll:
|
| 229 |
+
type: int
|
| 230 |
+
position: 0
|
| 231 |
+
length: 2
|
| 232 |
+
signed: true
|
| 233 |
+
doc: TODO
|
| 234 |
+
roll_rel:
|
| 235 |
+
control: ROLL_RELATIVE
|
| 236 |
+
length: 2
|
| 237 |
+
fields:
|
| 238 |
+
roll_rel:
|
| 239 |
+
type: int
|
| 240 |
+
position: 0
|
| 241 |
+
length: 1
|
| 242 |
+
signed: true
|
| 243 |
+
doc: TODO
|
| 244 |
+
speed:
|
| 245 |
+
type: int
|
| 246 |
+
position: 1
|
| 247 |
+
length: 1
|
| 248 |
+
doc: TODO
|
| 249 |
+
privacy:
|
| 250 |
+
control: PRIVACY
|
| 251 |
+
length: 1
|
| 252 |
+
fields:
|
| 253 |
+
privacy:
|
| 254 |
+
type: int
|
| 255 |
+
position: 0
|
| 256 |
+
length: 1
|
| 257 |
+
doc: TODO
|
| 258 |
+
digital_window:
|
| 259 |
+
control: DIGITAL_WINDOW
|
| 260 |
+
length: 12
|
| 261 |
+
fields:
|
| 262 |
+
window_top:
|
| 263 |
+
type: int
|
| 264 |
+
position: 0
|
| 265 |
+
length: 2
|
| 266 |
+
doc: TODO
|
| 267 |
+
window_left:
|
| 268 |
+
type: int
|
| 269 |
+
position: 2
|
| 270 |
+
length: 2
|
| 271 |
+
doc: TODO
|
| 272 |
+
window_bottom:
|
| 273 |
+
type: int
|
| 274 |
+
position: 4
|
| 275 |
+
length: 2
|
| 276 |
+
doc: TODO
|
| 277 |
+
window_right:
|
| 278 |
+
type: int
|
| 279 |
+
position: 6
|
| 280 |
+
length: 2
|
| 281 |
+
doc: TODO
|
| 282 |
+
num_steps:
|
| 283 |
+
type: int
|
| 284 |
+
position: 8
|
| 285 |
+
length: 2
|
| 286 |
+
doc: TODO
|
| 287 |
+
num_steps_units:
|
| 288 |
+
type: int
|
| 289 |
+
position: 10
|
| 290 |
+
length: 2
|
| 291 |
+
doc: TODO
|
| 292 |
+
digital_roi:
|
| 293 |
+
control: REGION_OF_INTEREST
|
| 294 |
+
length: 10
|
| 295 |
+
fields:
|
| 296 |
+
roi_top:
|
| 297 |
+
type: int
|
| 298 |
+
position: 0
|
| 299 |
+
length: 2
|
| 300 |
+
doc: TODO
|
| 301 |
+
roi_left:
|
| 302 |
+
type: int
|
| 303 |
+
position: 2
|
| 304 |
+
length: 2
|
| 305 |
+
doc: TODO
|
| 306 |
+
roi_bottom:
|
| 307 |
+
type: int
|
| 308 |
+
position: 4
|
| 309 |
+
length: 2
|
| 310 |
+
doc: TODO
|
| 311 |
+
roi_right:
|
| 312 |
+
type: int
|
| 313 |
+
position: 6
|
| 314 |
+
length: 2
|
| 315 |
+
doc: TODO
|
| 316 |
+
auto_controls:
|
| 317 |
+
type: int
|
| 318 |
+
position: 8
|
| 319 |
+
length: 2
|
| 320 |
+
doc: TODO
|
| 321 |
+
processing_unit:
|
| 322 |
+
type: standard
|
| 323 |
+
description: Standard processing unit (processes images between other units)
|
| 324 |
+
control_prefix: PU
|
| 325 |
+
controls:
|
| 326 |
+
backlight_compensation:
|
| 327 |
+
control: BACKLIGHT_COMPENSATION
|
| 328 |
+
length: 2
|
| 329 |
+
fields:
|
| 330 |
+
backlight_compensation:
|
| 331 |
+
type: int
|
| 332 |
+
position: 0
|
| 333 |
+
length: 2
|
| 334 |
+
doc: device-dependent backlight compensation mode; zero means backlight
|
| 335 |
+
compensation is disabled
|
| 336 |
+
brightness:
|
| 337 |
+
control: BRIGHTNESS
|
| 338 |
+
length: 2
|
| 339 |
+
fields:
|
| 340 |
+
brightness:
|
| 341 |
+
type: int
|
| 342 |
+
position: 0
|
| 343 |
+
length: 2
|
| 344 |
+
signed: true
|
| 345 |
+
doc: TODO
|
| 346 |
+
contrast:
|
| 347 |
+
control: CONTRAST
|
| 348 |
+
length: 2
|
| 349 |
+
fields:
|
| 350 |
+
contrast:
|
| 351 |
+
type: int
|
| 352 |
+
position: 0
|
| 353 |
+
length: 2
|
| 354 |
+
doc: TODO
|
| 355 |
+
contrast_auto:
|
| 356 |
+
control: CONTRAST_AUTO
|
| 357 |
+
length: 1
|
| 358 |
+
fields:
|
| 359 |
+
contrast_auto:
|
| 360 |
+
type: int
|
| 361 |
+
position: 0
|
| 362 |
+
length: 1
|
| 363 |
+
doc: TODO
|
| 364 |
+
gain:
|
| 365 |
+
control: GAIN
|
| 366 |
+
length: 2
|
| 367 |
+
fields:
|
| 368 |
+
gain:
|
| 369 |
+
type: int
|
| 370 |
+
position: 0
|
| 371 |
+
length: 2
|
| 372 |
+
doc: TODO
|
| 373 |
+
power_line_frequency:
|
| 374 |
+
control: POWER_LINE_FREQUENCY
|
| 375 |
+
length: 1
|
| 376 |
+
fields:
|
| 377 |
+
power_line_frequency:
|
| 378 |
+
type: int
|
| 379 |
+
position: 0
|
| 380 |
+
length: 1
|
| 381 |
+
doc: TODO
|
| 382 |
+
hue:
|
| 383 |
+
control: HUE
|
| 384 |
+
length: 2
|
| 385 |
+
fields:
|
| 386 |
+
hue:
|
| 387 |
+
type: int
|
| 388 |
+
position: 0
|
| 389 |
+
length: 2
|
| 390 |
+
signed: true
|
| 391 |
+
doc: TODO
|
| 392 |
+
hue_auto:
|
| 393 |
+
control: HUE_AUTO
|
| 394 |
+
length: 1
|
| 395 |
+
fields:
|
| 396 |
+
hue_auto:
|
| 397 |
+
type: int
|
| 398 |
+
position: 0
|
| 399 |
+
length: 1
|
| 400 |
+
doc: TODO
|
| 401 |
+
saturation:
|
| 402 |
+
control: SATURATION
|
| 403 |
+
length: 2
|
| 404 |
+
fields:
|
| 405 |
+
saturation:
|
| 406 |
+
type: int
|
| 407 |
+
position: 0
|
| 408 |
+
length: 2
|
| 409 |
+
doc: TODO
|
| 410 |
+
sharpness:
|
| 411 |
+
control: SHARPNESS
|
| 412 |
+
length: 2
|
| 413 |
+
fields:
|
| 414 |
+
sharpness:
|
| 415 |
+
type: int
|
| 416 |
+
position: 0
|
| 417 |
+
length: 2
|
| 418 |
+
doc: TODO
|
| 419 |
+
gamma:
|
| 420 |
+
control: GAMMA
|
| 421 |
+
length: 2
|
| 422 |
+
fields:
|
| 423 |
+
gamma:
|
| 424 |
+
type: int
|
| 425 |
+
position: 0
|
| 426 |
+
length: 2
|
| 427 |
+
doc: TODO
|
| 428 |
+
white_balance_temperature:
|
| 429 |
+
control: WHITE_BALANCE_TEMPERATURE
|
| 430 |
+
length: 2
|
| 431 |
+
fields:
|
| 432 |
+
temperature:
|
| 433 |
+
type: int
|
| 434 |
+
position: 0
|
| 435 |
+
length: 2
|
| 436 |
+
doc: TODO
|
| 437 |
+
white_balance_temperature_auto:
|
| 438 |
+
control: WHITE_BALANCE_TEMPERATURE_AUTO
|
| 439 |
+
length: 1
|
| 440 |
+
fields:
|
| 441 |
+
temperature_auto:
|
| 442 |
+
type: int
|
| 443 |
+
position: 0
|
| 444 |
+
length: 1
|
| 445 |
+
doc: TODO
|
| 446 |
+
white_balance_component:
|
| 447 |
+
control: WHITE_BALANCE_COMPONENT
|
| 448 |
+
length: 4
|
| 449 |
+
fields:
|
| 450 |
+
blue:
|
| 451 |
+
type: int
|
| 452 |
+
position: 0
|
| 453 |
+
length: 2
|
| 454 |
+
doc: TODO
|
| 455 |
+
red:
|
| 456 |
+
type: int
|
| 457 |
+
position: 2
|
| 458 |
+
length: 2
|
| 459 |
+
doc: TODO
|
| 460 |
+
white_balance_component_auto:
|
| 461 |
+
control: WHITE_BALANCE_COMPONENT_AUTO
|
| 462 |
+
length: 1
|
| 463 |
+
fields:
|
| 464 |
+
white_balance_component_auto:
|
| 465 |
+
type: int
|
| 466 |
+
position: 0
|
| 467 |
+
length: 1
|
| 468 |
+
doc: TODO
|
| 469 |
+
digital_multiplier:
|
| 470 |
+
control: DIGITAL_MULTIPLIER
|
| 471 |
+
length: 2
|
| 472 |
+
fields:
|
| 473 |
+
multiplier_step:
|
| 474 |
+
type: int
|
| 475 |
+
position: 0
|
| 476 |
+
length: 2
|
| 477 |
+
doc: TODO
|
| 478 |
+
digital_multiplier_limit:
|
| 479 |
+
control: DIGITAL_MULTIPLIER_LIMIT
|
| 480 |
+
length: 2
|
| 481 |
+
fields:
|
| 482 |
+
multiplier_step:
|
| 483 |
+
type: int
|
| 484 |
+
position: 0
|
| 485 |
+
length: 2
|
| 486 |
+
doc: TODO
|
| 487 |
+
analog_video_standard:
|
| 488 |
+
control: ANALOG_VIDEO_STANDARD
|
| 489 |
+
length: 1
|
| 490 |
+
fields:
|
| 491 |
+
video_standard:
|
| 492 |
+
type: int
|
| 493 |
+
position: 0
|
| 494 |
+
length: 1
|
| 495 |
+
doc: TODO
|
| 496 |
+
analog_video_lock_status:
|
| 497 |
+
control: ANALOG_LOCK_STATUS
|
| 498 |
+
length: 1
|
| 499 |
+
fields:
|
| 500 |
+
status:
|
| 501 |
+
type: int
|
| 502 |
+
position: 0
|
| 503 |
+
length: 1
|
| 504 |
+
doc: TODO
|
| 505 |
+
selector_unit:
|
| 506 |
+
type: standard
|
| 507 |
+
description: Standard selector unit (controls connectivity between other units)
|
| 508 |
+
control_prefix: SU
|
| 509 |
+
controls:
|
| 510 |
+
input_select:
|
| 511 |
+
control: INPUT_SELECT
|
| 512 |
+
length: 1
|
| 513 |
+
fields:
|
| 514 |
+
selector:
|
| 515 |
+
type: int
|
| 516 |
+
position: 0
|
| 517 |
+
length: 1
|
| 518 |
+
doc: TODO
|
collector/core/gripper/camera_service_src/tools/discover_uvc_config.c
ADDED
|
@@ -0,0 +1,1463 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Minimal user-space discovery demo for Sightac + DECXIN.
|
| 3 |
+
*
|
| 4 |
+
* Scope is intentionally limited to:
|
| 5 |
+
* Sightac: 0c45:636f, 640x480 MJPEG @ 30
|
| 6 |
+
* DECXIN : 1bcf:2d4f, 1280x960 MJPEG @ 30
|
| 7 |
+
*
|
| 8 |
+
* Fays is not enumerated as a supported candidate and is never opened by
|
| 9 |
+
* this program. The demo uses libuvc for UVC enumeration/negotiation and
|
| 10 |
+
* libusb for USB topology/descriptors and the read-only Sonix XU6 probe.
|
| 11 |
+
* It does not start a video stream and it does not write/erase Flash.
|
| 12 |
+
*/
|
| 13 |
+
#include <ctype.h>
|
| 14 |
+
#include <errno.h>
|
| 15 |
+
#include <libusb-1.0/libusb.h>
|
| 16 |
+
#include <libuvc/libuvc.h>
|
| 17 |
+
#include <limits.h>
|
| 18 |
+
#include <math.h>
|
| 19 |
+
#include <stdint.h>
|
| 20 |
+
#include <stdio.h>
|
| 21 |
+
#include <stdlib.h>
|
| 22 |
+
#include <string.h>
|
| 23 |
+
#include <unistd.h>
|
| 24 |
+
|
| 25 |
+
#define MAX_CANDIDATES 32
|
| 26 |
+
#define MAX_GROUPS 16
|
| 27 |
+
#define MAX_PORT_PATH 64
|
| 28 |
+
#define MAX_TEXT 128
|
| 29 |
+
|
| 30 |
+
#define SIGHTAC_VID 0x0c45
|
| 31 |
+
#define SIGHTAC_PID 0x636f
|
| 32 |
+
#define DECXIN_VID 0x1bcf
|
| 33 |
+
#define DECXIN_PID 0x2d4f
|
| 34 |
+
|
| 35 |
+
#define XU_GET_CUR 0x81
|
| 36 |
+
#define XU_SET_CUR 0x01
|
| 37 |
+
#define XU_CTRL_GET 0xa1
|
| 38 |
+
#define XU_CTRL_SET 0x21
|
| 39 |
+
#define XU_ASIC_SELECTOR 0x01
|
| 40 |
+
#define XU_FLASH_XU6_SELECTOR 0x06
|
| 41 |
+
#define CTRL_TIMEOUT_MS 2000
|
| 42 |
+
#define FLASH_PROBE_ATTEMPTS 3
|
| 43 |
+
#define FLASH_RETRY_DELAY_US 25000
|
| 44 |
+
#define FLASH_MAX_ADDRESS 0x4ffff
|
| 45 |
+
#define SF_XU64_EU_ADDR 0x335
|
| 46 |
+
#define SF_XU64_RW_START_ADDR 0x337
|
| 47 |
+
#define SF_XU64_RW_END_ADDR 0x33b
|
| 48 |
+
|
| 49 |
+
typedef struct {
|
| 50 |
+
int vid;
|
| 51 |
+
int pid;
|
| 52 |
+
int width;
|
| 53 |
+
int height;
|
| 54 |
+
int fps;
|
| 55 |
+
int expected_altsetting;
|
| 56 |
+
int forced_payload;
|
| 57 |
+
const char *kind;
|
| 58 |
+
} mode_profile_t;
|
| 59 |
+
|
| 60 |
+
typedef struct {
|
| 61 |
+
uvc_device_t *uvc_device;
|
| 62 |
+
libusb_device *usb_device;
|
| 63 |
+
int vid;
|
| 64 |
+
int pid;
|
| 65 |
+
int bus;
|
| 66 |
+
char port[MAX_PORT_PATH];
|
| 67 |
+
char usb_serial[MAX_TEXT];
|
| 68 |
+
char device_type[MAX_TEXT];
|
| 69 |
+
char side[16];
|
| 70 |
+
char flash_serial[MAX_TEXT];
|
| 71 |
+
int control_interface;
|
| 72 |
+
int streaming_interface;
|
| 73 |
+
int altsetting;
|
| 74 |
+
unsigned int descriptor_payload;
|
| 75 |
+
unsigned int negotiated_payload;
|
| 76 |
+
int mode_ok;
|
| 77 |
+
int flash_ok;
|
| 78 |
+
int flash_params_ok;
|
| 79 |
+
double fx_params[5];
|
| 80 |
+
double fy_params[5];
|
| 81 |
+
double fz_params[21];
|
| 82 |
+
double ft_params[20];
|
| 83 |
+
double fw_params[9];
|
| 84 |
+
int opened_ok;
|
| 85 |
+
char error[MAX_TEXT];
|
| 86 |
+
} camera_candidate_t;
|
| 87 |
+
|
| 88 |
+
typedef struct {
|
| 89 |
+
int sightac[2];
|
| 90 |
+
int decxin;
|
| 91 |
+
int bus;
|
| 92 |
+
char outer_path[MAX_PORT_PATH];
|
| 93 |
+
} camera_group_t;
|
| 94 |
+
|
| 95 |
+
typedef enum {
|
| 96 |
+
FLASH_MODE_FULL = 0,
|
| 97 |
+
FLASH_MODE_IDENTITY = 1,
|
| 98 |
+
FLASH_MODE_NONE = 2,
|
| 99 |
+
FLASH_MODE_TOPOLOGY_ONLY = 3,
|
| 100 |
+
} flash_mode_t;
|
| 101 |
+
|
| 102 |
+
static const mode_profile_t *profile_for(int vid, int pid) {
|
| 103 |
+
static const mode_profile_t sightac = {
|
| 104 |
+
SIGHTAC_VID, SIGHTAC_PID, 640, 480, 30, 3, 800, "sightac"
|
| 105 |
+
};
|
| 106 |
+
/* The scanner reports the camera's *preferred* altsetting -- the one the
|
| 107 |
+
* descriptor advertises, which gives frames the most room: alt7/1280
|
| 108 |
+
* (2 transactions per microframe) fits 8 x 32 x 1280 = 327680 B per frame,
|
| 109 |
+
* while alt6/944 fits only 241664 B against a measured worst case of
|
| 110 |
+
* 234148 B.
|
| 111 |
+
*
|
| 112 |
+
* This value is *reported*, not used as the starting rung. Measured
|
| 113 |
+
* 2026-09-15: alt7 stalls every 45-130 s while all three cameras stream,
|
| 114 |
+
* on both host controllers tried, and it delivers the same 30.00 fps as
|
| 115 |
+
* alt6 -- so the extra per-frame headroom buys nothing and the app starts
|
| 116 |
+
* at the most conservative pair instead (uvc_camera_service.py,
|
| 117 |
+
* _starting_mode). Keeping the scanner honest about the descriptor matters
|
| 118 |
+
* even so: the app checks this pair against the service's mode table, and a
|
| 119 |
+
* mismatch means scanner and service were built apart from each other. */
|
| 120 |
+
static const mode_profile_t decxin = {
|
| 121 |
+
DECXIN_VID, DECXIN_PID, 1280, 960, 30, 7, 1280, "decxin"
|
| 122 |
+
};
|
| 123 |
+
if (vid == SIGHTAC_VID && pid == SIGHTAC_PID) {
|
| 124 |
+
return &sightac;
|
| 125 |
+
}
|
| 126 |
+
if (vid == DECXIN_VID && pid == DECXIN_PID) {
|
| 127 |
+
return &decxin;
|
| 128 |
+
}
|
| 129 |
+
return NULL;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
static const char *usb_error_text(int result) {
|
| 133 |
+
return result < 0 ? libusb_strerror((enum libusb_error)result) :
|
| 134 |
+
libusb_strerror((enum libusb_error)result);
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
static void copy_text(char *destination, size_t size, const char *source) {
|
| 138 |
+
size_t length;
|
| 139 |
+
if (size == 0) {
|
| 140 |
+
return;
|
| 141 |
+
}
|
| 142 |
+
if (source == NULL) {
|
| 143 |
+
destination[0] = '\0';
|
| 144 |
+
return;
|
| 145 |
+
}
|
| 146 |
+
length = strlen(source);
|
| 147 |
+
if (length >= size) {
|
| 148 |
+
length = size - 1;
|
| 149 |
+
}
|
| 150 |
+
memcpy(destination, source, length);
|
| 151 |
+
destination[length] = '\0';
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
static void write_json_string(FILE *out, const char *value) {
|
| 155 |
+
fputc('"', out);
|
| 156 |
+
for (const unsigned char *cursor = (const unsigned char *)(value ? value : "");
|
| 157 |
+
*cursor != '\0'; ++cursor) {
|
| 158 |
+
switch (*cursor) {
|
| 159 |
+
case '"':
|
| 160 |
+
fputs("\\\"", out);
|
| 161 |
+
break;
|
| 162 |
+
case '\\':
|
| 163 |
+
fputs("\\\\", out);
|
| 164 |
+
break;
|
| 165 |
+
case '\b':
|
| 166 |
+
fputs("\\b", out);
|
| 167 |
+
break;
|
| 168 |
+
case '\f':
|
| 169 |
+
fputs("\\f", out);
|
| 170 |
+
break;
|
| 171 |
+
case '\n':
|
| 172 |
+
fputs("\\n", out);
|
| 173 |
+
break;
|
| 174 |
+
case '\r':
|
| 175 |
+
fputs("\\r", out);
|
| 176 |
+
break;
|
| 177 |
+
case '\t':
|
| 178 |
+
fputs("\\t", out);
|
| 179 |
+
break;
|
| 180 |
+
default:
|
| 181 |
+
if (*cursor < 0x20) {
|
| 182 |
+
fprintf(out, "\\u%04x", (unsigned int)*cursor);
|
| 183 |
+
} else {
|
| 184 |
+
fputc(*cursor, out);
|
| 185 |
+
}
|
| 186 |
+
break;
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
fputc('"', out);
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
static int format_uvc_port_path(uvc_device_t *device, char *out,
|
| 193 |
+
size_t out_size) {
|
| 194 |
+
uint8_t ports[8];
|
| 195 |
+
int count = uvc_get_port_numbers(device, ports, sizeof(ports));
|
| 196 |
+
size_t used = 0;
|
| 197 |
+
if (count < 0 || out_size == 0) {
|
| 198 |
+
return count < 0 ? count : LIBUSB_ERROR_INVALID_PARAM;
|
| 199 |
+
}
|
| 200 |
+
out[0] = '\0';
|
| 201 |
+
for (int index = 0; index < count; ++index) {
|
| 202 |
+
int written = snprintf(out + used, out_size - used, "%s%u",
|
| 203 |
+
index == 0 ? "" : ".", ports[index]);
|
| 204 |
+
if (written < 0 || (size_t)written >= out_size - used) {
|
| 205 |
+
return LIBUSB_ERROR_OVERFLOW;
|
| 206 |
+
}
|
| 207 |
+
used += (size_t)written;
|
| 208 |
+
}
|
| 209 |
+
return 0;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
static int format_libusb_port_path(libusb_device *device, char *out,
|
| 213 |
+
size_t out_size) {
|
| 214 |
+
uint8_t ports[8];
|
| 215 |
+
int count = libusb_get_port_numbers(device, ports, sizeof(ports));
|
| 216 |
+
size_t used = 0;
|
| 217 |
+
if (count < 0 || out_size == 0) {
|
| 218 |
+
return count < 0 ? count : LIBUSB_ERROR_INVALID_PARAM;
|
| 219 |
+
}
|
| 220 |
+
out[0] = '\0';
|
| 221 |
+
for (int index = 0; index < count; ++index) {
|
| 222 |
+
int written = snprintf(out + used, out_size - used, "%s%u",
|
| 223 |
+
index == 0 ? "" : ".", ports[index]);
|
| 224 |
+
if (written < 0 || (size_t)written >= out_size - used) {
|
| 225 |
+
return LIBUSB_ERROR_OVERFLOW;
|
| 226 |
+
}
|
| 227 |
+
used += (size_t)written;
|
| 228 |
+
}
|
| 229 |
+
return 0;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
static int find_control_interface(libusb_device *device) {
|
| 233 |
+
struct libusb_config_descriptor *config = NULL;
|
| 234 |
+
int result = libusb_get_active_config_descriptor(device, &config);
|
| 235 |
+
if (result != 0) {
|
| 236 |
+
return result;
|
| 237 |
+
}
|
| 238 |
+
int found = LIBUSB_ERROR_NOT_FOUND;
|
| 239 |
+
for (int i = 0; i < config->bNumInterfaces && found < 0; ++i) {
|
| 240 |
+
for (int j = 0; j < config->interface[i].num_altsetting; ++j) {
|
| 241 |
+
const struct libusb_interface_descriptor *alt =
|
| 242 |
+
&config->interface[i].altsetting[j];
|
| 243 |
+
if (alt->bInterfaceClass == LIBUSB_CLASS_VIDEO &&
|
| 244 |
+
alt->bInterfaceSubClass == 1) {
|
| 245 |
+
found = alt->bInterfaceNumber;
|
| 246 |
+
break;
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
libusb_free_config_descriptor(config);
|
| 251 |
+
return found;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
static unsigned int endpoint_payload(const struct libusb_endpoint_descriptor *ep) {
|
| 255 |
+
unsigned int payload = ep->wMaxPacketSize & 0x07ffu;
|
| 256 |
+
payload *= 1u + ((ep->wMaxPacketSize >> 11) & 0x03u);
|
| 257 |
+
return payload;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
static int find_stream_mode(libusb_device *device, int expected_altsetting,
|
| 261 |
+
unsigned int minimum_payload,
|
| 262 |
+
int *interface_number, int *altsetting,
|
| 263 |
+
unsigned int *payload) {
|
| 264 |
+
struct libusb_config_descriptor *config = NULL;
|
| 265 |
+
int result = libusb_get_active_config_descriptor(device, &config);
|
| 266 |
+
if (result != 0) {
|
| 267 |
+
return result;
|
| 268 |
+
}
|
| 269 |
+
int found = LIBUSB_ERROR_NOT_FOUND;
|
| 270 |
+
unsigned int selected_payload = 0;
|
| 271 |
+
int selected_interface = -1;
|
| 272 |
+
for (int i = 0; i < config->bNumInterfaces; ++i) {
|
| 273 |
+
for (int j = 0; j < config->interface[i].num_altsetting; ++j) {
|
| 274 |
+
const struct libusb_interface_descriptor *alt =
|
| 275 |
+
&config->interface[i].altsetting[j];
|
| 276 |
+
if (alt->bInterfaceClass != LIBUSB_CLASS_VIDEO ||
|
| 277 |
+
alt->bInterfaceSubClass != 2) {
|
| 278 |
+
continue;
|
| 279 |
+
}
|
| 280 |
+
if (alt->bAlternateSetting != expected_altsetting) {
|
| 281 |
+
continue;
|
| 282 |
+
}
|
| 283 |
+
for (int k = 0; k < alt->bNumEndpoints; ++k) {
|
| 284 |
+
const struct libusb_endpoint_descriptor *ep = &alt->endpoint[k];
|
| 285 |
+
unsigned int transfer_type = ep->bmAttributes &
|
| 286 |
+
LIBUSB_TRANSFER_TYPE_MASK;
|
| 287 |
+
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) !=
|
| 288 |
+
LIBUSB_ENDPOINT_IN ||
|
| 289 |
+
(transfer_type != LIBUSB_TRANSFER_TYPE_ISOCHRONOUS &&
|
| 290 |
+
transfer_type != LIBUSB_TRANSFER_TYPE_BULK)) {
|
| 291 |
+
continue;
|
| 292 |
+
}
|
| 293 |
+
unsigned int current_payload = endpoint_payload(ep);
|
| 294 |
+
if (current_payload >= minimum_payload &&
|
| 295 |
+
current_payload > selected_payload) {
|
| 296 |
+
selected_payload = current_payload;
|
| 297 |
+
selected_interface = alt->bInterfaceNumber;
|
| 298 |
+
found = 0;
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
}
|
| 302 |
+
}
|
| 303 |
+
libusb_free_config_descriptor(config);
|
| 304 |
+
if (found == 0) {
|
| 305 |
+
*interface_number = selected_interface;
|
| 306 |
+
*altsetting = expected_altsetting;
|
| 307 |
+
*payload = selected_payload;
|
| 308 |
+
}
|
| 309 |
+
if (found == 0 && *interface_number < 0) {
|
| 310 |
+
found = LIBUSB_ERROR_NOT_FOUND;
|
| 311 |
+
}
|
| 312 |
+
return found;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
static libusb_device *find_raw_device(libusb_device **devices, ssize_t count,
|
| 316 |
+
int vid, int pid, int bus,
|
| 317 |
+
const char *wanted_port) {
|
| 318 |
+
for (ssize_t index = 0; index < count; ++index) {
|
| 319 |
+
struct libusb_device_descriptor descriptor;
|
| 320 |
+
char port[MAX_PORT_PATH];
|
| 321 |
+
if (libusb_get_device_descriptor(devices[index], &descriptor) != 0 ||
|
| 322 |
+
descriptor.idVendor != (uint16_t)vid ||
|
| 323 |
+
descriptor.idProduct != (uint16_t)pid ||
|
| 324 |
+
(int)libusb_get_bus_number(devices[index]) != bus ||
|
| 325 |
+
format_libusb_port_path(devices[index], port, sizeof(port)) != 0 ||
|
| 326 |
+
strcmp(port, wanted_port) != 0) {
|
| 327 |
+
continue;
|
| 328 |
+
}
|
| 329 |
+
return devices[index];
|
| 330 |
+
}
|
| 331 |
+
return NULL;
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
static int xu_control(libusb_device_handle *handle, int interface_number,
|
| 335 |
+
uint8_t unit, uint8_t selector, int get,
|
| 336 |
+
uint8_t *data, uint16_t size) {
|
| 337 |
+
uint8_t request_type = get ? XU_CTRL_GET : XU_CTRL_SET;
|
| 338 |
+
uint8_t request = get ? XU_GET_CUR : XU_SET_CUR;
|
| 339 |
+
uint16_t value = (uint16_t)selector << 8;
|
| 340 |
+
uint16_t index = ((uint16_t)unit << 8) | (uint16_t)interface_number;
|
| 341 |
+
return libusb_control_transfer(handle, request_type, request, value, index,
|
| 342 |
+
data, size, CTRL_TIMEOUT_MS);
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
static int asic_read(libusb_device_handle *handle, int interface_number,
|
| 346 |
+
uint8_t unit, uint16_t address, uint8_t *value) {
|
| 347 |
+
uint8_t data[4] = {
|
| 348 |
+
(uint8_t)(address & 0xff), (uint8_t)(address >> 8), 0, 0xff
|
| 349 |
+
};
|
| 350 |
+
int result = xu_control(handle, interface_number, unit,
|
| 351 |
+
XU_ASIC_SELECTOR, 0, data, sizeof(data));
|
| 352 |
+
if (result != (int)sizeof(data)) {
|
| 353 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 354 |
+
}
|
| 355 |
+
data[3] = 0;
|
| 356 |
+
result = xu_control(handle, interface_number, unit, XU_ASIC_SELECTOR, 1,
|
| 357 |
+
data, sizeof(data));
|
| 358 |
+
if (result != (int)sizeof(data)) {
|
| 359 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 360 |
+
}
|
| 361 |
+
*value = data[2];
|
| 362 |
+
return 0;
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
static int asic_write(libusb_device_handle *handle, int interface_number,
|
| 366 |
+
uint8_t unit, uint16_t address, uint8_t value) {
|
| 367 |
+
uint8_t data[4] = {
|
| 368 |
+
(uint8_t)(address & 0xff), (uint8_t)(address >> 8), value, 0
|
| 369 |
+
};
|
| 370 |
+
int result = xu_control(handle, interface_number, unit,
|
| 371 |
+
XU_ASIC_SELECTOR, 0, data, sizeof(data));
|
| 372 |
+
if (result != (int)sizeof(data)) {
|
| 373 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 374 |
+
}
|
| 375 |
+
return 0;
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
+
static int known_chip(uint8_t chip) {
|
| 379 |
+
switch (chip) {
|
| 380 |
+
case 0x15: case 0x16: case 0x22: case 0x23: case 0x25:
|
| 381 |
+
case 0x32: case 0x33: case 0x56: case 0x70: case 0x71:
|
| 382 |
+
case 0x75: case 0x76: case 0x83: case 0x85: case 0x86:
|
| 383 |
+
case 0x87: case 0x88: case 0x89: case 0x90: case 0x92:
|
| 384 |
+
case 0x98: case 0x99:
|
| 385 |
+
return 1;
|
| 386 |
+
default:
|
| 387 |
+
return 0;
|
| 388 |
+
}
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
static int find_xu_unit(libusb_device_handle *handle, int interface_number,
|
| 392 |
+
uint8_t *unit) {
|
| 393 |
+
for (uint8_t candidate = 3; candidate <= 4; ++candidate) {
|
| 394 |
+
uint8_t raw[4] = {0x1f, 0x10, 0, 0xff};
|
| 395 |
+
int set_result = xu_control(handle, interface_number, candidate,
|
| 396 |
+
XU_ASIC_SELECTOR, 0, raw, sizeof(raw));
|
| 397 |
+
int get_result = -1;
|
| 398 |
+
if (set_result == (int)sizeof(raw)) {
|
| 399 |
+
raw[3] = 0;
|
| 400 |
+
get_result = xu_control(handle, interface_number, candidate,
|
| 401 |
+
XU_ASIC_SELECTOR, 1, raw, sizeof(raw));
|
| 402 |
+
}
|
| 403 |
+
uint8_t value = get_result == (int)sizeof(raw) ? raw[2] : 0;
|
| 404 |
+
if (value == 0xaa && get_result == (int)sizeof(raw)) {
|
| 405 |
+
uint8_t id80f0 = 0;
|
| 406 |
+
uint8_t id80f1 = 0;
|
| 407 |
+
uint8_t id80f3 = 0;
|
| 408 |
+
int r0 = asic_read(handle, interface_number, candidate, 0x80f0,
|
| 409 |
+
&id80f0);
|
| 410 |
+
int r1 = asic_read(handle, interface_number, candidate, 0x80f1,
|
| 411 |
+
&id80f1);
|
| 412 |
+
int r3 = asic_read(handle, interface_number, candidate, 0x80f3,
|
| 413 |
+
&id80f3);
|
| 414 |
+
if (r0 == 0 && r1 == 0 && r3 == 0 &&
|
| 415 |
+
(known_chip(id80f0) ||
|
| 416 |
+
(id80f0 == 0x00 && id80f1 == 0x02 && id80f3 == 0x24) ||
|
| 417 |
+
(id80f0 == 0x01 && id80f1 == 0x02 && id80f3 == 0x23) ||
|
| 418 |
+
(id80f0 == 0x04 && id80f1 == 0x02 && id80f3 == 0x24))) {
|
| 419 |
+
value = id80f0;
|
| 420 |
+
}
|
| 421 |
+
}
|
| 422 |
+
if (set_result == (int)sizeof(raw) &&
|
| 423 |
+
get_result == (int)sizeof(raw) &&
|
| 424 |
+
(known_chip(value) || value == 0x00 || value == 0x01 ||
|
| 425 |
+
value == 0x04)) {
|
| 426 |
+
*unit = candidate;
|
| 427 |
+
return 0;
|
| 428 |
+
}
|
| 429 |
+
}
|
| 430 |
+
return LIBUSB_ERROR_NOT_FOUND;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
static int sf_xu6_write_config(libusb_device_handle *handle,
|
| 434 |
+
int interface_number, uint8_t unit,
|
| 435 |
+
uint32_t start_address, uint32_t data_size,
|
| 436 |
+
uint8_t buffer_size) {
|
| 437 |
+
uint8_t data[64] = {0};
|
| 438 |
+
data[0] = (uint8_t)(start_address & 0xff);
|
| 439 |
+
data[1] = (uint8_t)((start_address >> 8) & 0xff);
|
| 440 |
+
data[2] = (uint8_t)((start_address >> 16) & 0xff);
|
| 441 |
+
data[3] = (uint8_t)((start_address >> 24) & 0xff);
|
| 442 |
+
data[4] = 2u << 6;
|
| 443 |
+
data[5] = buffer_size;
|
| 444 |
+
uint32_t end_address = start_address + data_size;
|
| 445 |
+
data[6] = (uint8_t)(end_address & 0xff);
|
| 446 |
+
data[7] = (uint8_t)((end_address >> 8) & 0xff);
|
| 447 |
+
data[8] = (uint8_t)((end_address >> 16) & 0xff);
|
| 448 |
+
data[9] = (uint8_t)((end_address >> 24) & 0xff);
|
| 449 |
+
data[10] = 0x0b;
|
| 450 |
+
data[11] = 0x01;
|
| 451 |
+
int result = xu_control(handle, interface_number, unit,
|
| 452 |
+
XU_FLASH_XU6_SELECTOR, 0, data, sizeof(data));
|
| 453 |
+
return result == (int)sizeof(data) ? 0 :
|
| 454 |
+
(result < 0 ? result : LIBUSB_ERROR_IO);
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
static int sf_xu6_stop(libusb_device_handle *handle, int interface_number,
|
| 458 |
+
uint8_t unit) {
|
| 459 |
+
for (uint16_t i = 0; i < 4; ++i) {
|
| 460 |
+
int result = asic_write(handle, interface_number, unit,
|
| 461 |
+
SF_XU64_RW_START_ADDR + i, 0);
|
| 462 |
+
if (result != 0) {
|
| 463 |
+
return result;
|
| 464 |
+
}
|
| 465 |
+
result = asic_write(handle, interface_number, unit,
|
| 466 |
+
SF_XU64_RW_END_ADDR + i, 0);
|
| 467 |
+
if (result != 0) {
|
| 468 |
+
return result;
|
| 469 |
+
}
|
| 470 |
+
}
|
| 471 |
+
return asic_write(handle, interface_number, unit, SF_XU64_EU_ADDR, 0);
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
static int sf_xu6_read_block(libusb_device_handle *handle, int interface_number,
|
| 475 |
+
uint8_t unit, uint32_t address, uint8_t *out,
|
| 476 |
+
uint8_t length) {
|
| 477 |
+
if (length == 0 || length > 64 || address > FLASH_MAX_ADDRESS ||
|
| 478 |
+
address + length < address) {
|
| 479 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 480 |
+
}
|
| 481 |
+
int result = sf_xu6_stop(handle, interface_number, unit);
|
| 482 |
+
if (result != 0) {
|
| 483 |
+
return result;
|
| 484 |
+
}
|
| 485 |
+
result = sf_xu6_write_config(handle, interface_number, unit, address,
|
| 486 |
+
length, length);
|
| 487 |
+
if (result != 0) {
|
| 488 |
+
return result;
|
| 489 |
+
}
|
| 490 |
+
uint8_t data[64] = {0};
|
| 491 |
+
result = xu_control(handle, interface_number, unit,
|
| 492 |
+
XU_FLASH_XU6_SELECTOR, 1, data, sizeof(data));
|
| 493 |
+
if (result != (int)sizeof(data)) {
|
| 494 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 495 |
+
}
|
| 496 |
+
memcpy(out, data, length);
|
| 497 |
+
return 0;
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
static int sf_xu6_read(libusb_device_handle *handle, int interface_number,
|
| 501 |
+
uint8_t unit, uint32_t address, uint8_t *out,
|
| 502 |
+
size_t length) {
|
| 503 |
+
while (length > 0) {
|
| 504 |
+
uint8_t chunk = length > 64 ? 64 : (uint8_t)length;
|
| 505 |
+
int result = sf_xu6_read_block(handle, interface_number, unit, address,
|
| 506 |
+
out, chunk);
|
| 507 |
+
if (result != 0) {
|
| 508 |
+
return result;
|
| 509 |
+
}
|
| 510 |
+
address += chunk;
|
| 511 |
+
out += chunk;
|
| 512 |
+
length -= chunk;
|
| 513 |
+
}
|
| 514 |
+
return 0;
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
static int sf_xu6_prepare_290(libusb_device_handle *handle,
|
| 518 |
+
int interface_number, uint8_t unit) {
|
| 519 |
+
int result = asic_write(handle, interface_number, unit, 0x8045, 10);
|
| 520 |
+
if (result != 0) {
|
| 521 |
+
return result;
|
| 522 |
+
}
|
| 523 |
+
return asic_write(handle, interface_number, unit, 0x8e10, 1);
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
static void sleep_for_probe(size_t attempt) {
|
| 527 |
+
useconds_t delay = FLASH_RETRY_DELAY_US *
|
| 528 |
+
(useconds_t)(attempt + 1U);
|
| 529 |
+
usleep(delay);
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
static void decode_flash_text(const uint8_t *raw, size_t length,
|
| 533 |
+
char *out, size_t out_size) {
|
| 534 |
+
size_t used = 0;
|
| 535 |
+
if (out_size == 0) {
|
| 536 |
+
return;
|
| 537 |
+
}
|
| 538 |
+
for (size_t i = 0; i < length && used + 1 < out_size; ++i) {
|
| 539 |
+
if (raw[i] == 0 || raw[i] == 0xff) {
|
| 540 |
+
break;
|
| 541 |
+
}
|
| 542 |
+
out[used++] = isprint(raw[i]) ? (char)raw[i] : '.';
|
| 543 |
+
}
|
| 544 |
+
while (used > 0 && isspace((unsigned char)out[used - 1])) {
|
| 545 |
+
--used;
|
| 546 |
+
}
|
| 547 |
+
out[used] = '\0';
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
static void decode_flash_serial(const uint8_t *raw, size_t length,
|
| 551 |
+
char *out, size_t out_size) {
|
| 552 |
+
size_t used = 0;
|
| 553 |
+
if (out_size == 0) {
|
| 554 |
+
return;
|
| 555 |
+
}
|
| 556 |
+
/* Sonix parameter-table strings use the same UTF-16LE-like layout as
|
| 557 |
+
* the vendor SDK: length in bytes at [0], characters at [2], [4]... . */
|
| 558 |
+
if (length >= 2 && raw[0] >= 2 && raw[0] <= length * 2 &&
|
| 559 |
+
(raw[0] & 1u) == 0) {
|
| 560 |
+
size_t chars = (raw[0] - 2) / 2;
|
| 561 |
+
for (size_t index = 0; index < chars &&
|
| 562 |
+
2 + index * 2 < length && used + 1 < out_size; ++index) {
|
| 563 |
+
uint8_t value = raw[2 + index * 2];
|
| 564 |
+
if (value == 0 || value == 0xff) {
|
| 565 |
+
break;
|
| 566 |
+
}
|
| 567 |
+
out[used++] = isprint(value) ? (char)value : '.';
|
| 568 |
+
}
|
| 569 |
+
} else {
|
| 570 |
+
decode_flash_text(raw, length, out, out_size);
|
| 571 |
+
return;
|
| 572 |
+
}
|
| 573 |
+
while (used > 0 && isspace((unsigned char)out[used - 1])) {
|
| 574 |
+
--used;
|
| 575 |
+
}
|
| 576 |
+
out[used] = '\0';
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
static void reset_flash_identity(camera_candidate_t *candidate) {
|
| 580 |
+
memset(candidate->device_type, 0, sizeof(candidate->device_type));
|
| 581 |
+
memset(candidate->side, 0, sizeof(candidate->side));
|
| 582 |
+
memset(candidate->flash_serial, 0, sizeof(candidate->flash_serial));
|
| 583 |
+
memset(candidate->fx_params, 0, sizeof(candidate->fx_params));
|
| 584 |
+
memset(candidate->fy_params, 0, sizeof(candidate->fy_params));
|
| 585 |
+
memset(candidate->fz_params, 0, sizeof(candidate->fz_params));
|
| 586 |
+
memset(candidate->fw_params, 0, sizeof(candidate->fw_params));
|
| 587 |
+
memset(candidate->ft_params, 0, sizeof(candidate->ft_params));
|
| 588 |
+
candidate->flash_ok = 0;
|
| 589 |
+
candidate->flash_params_ok = 0;
|
| 590 |
+
candidate->error[0] = '\0';
|
| 591 |
+
}
|
| 592 |
+
|
| 593 |
+
static int read_flash_identity_once(camera_candidate_t *candidate,
|
| 594 |
+
libusb_device_handle *handle,
|
| 595 |
+
int read_calibration) {
|
| 596 |
+
uint8_t unit = 0;
|
| 597 |
+
int result = find_xu_unit(handle, candidate->control_interface, &unit);
|
| 598 |
+
if (result != 0) {
|
| 599 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 600 |
+
"XU unit: %s", usb_error_text(result));
|
| 601 |
+
return result;
|
| 602 |
+
}
|
| 603 |
+
result = sf_xu6_prepare_290(handle, candidate->control_interface, unit);
|
| 604 |
+
if (result != 0) {
|
| 605 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 606 |
+
"XU6 prepare: %s", usb_error_text(result));
|
| 607 |
+
return result;
|
| 608 |
+
}
|
| 609 |
+
|
| 610 |
+
char params[5][MAX_TEXT];
|
| 611 |
+
memset(params, 0, sizeof(params));
|
| 612 |
+
for (size_t index = 0; index < 5; ++index) {
|
| 613 |
+
uint8_t raw[8] = {0};
|
| 614 |
+
result = sf_xu6_read(handle, candidate->control_interface, unit,
|
| 615 |
+
0x45000 + (uint32_t)(index * sizeof(raw)), raw,
|
| 616 |
+
sizeof(raw));
|
| 617 |
+
if (result != 0) {
|
| 618 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 619 |
+
"Flash device_params[%zu]: %s", index,
|
| 620 |
+
usb_error_text(result));
|
| 621 |
+
return result;
|
| 622 |
+
}
|
| 623 |
+
decode_flash_text(raw, sizeof(raw), params[index], sizeof(params[index]));
|
| 624 |
+
}
|
| 625 |
+
copy_text(candidate->device_type, sizeof(candidate->device_type), params[0]);
|
| 626 |
+
copy_text(candidate->side, sizeof(candidate->side), params[1]);
|
| 627 |
+
copy_text(candidate->flash_serial, sizeof(candidate->flash_serial), params[2]);
|
| 628 |
+
if (!(strcmp(candidate->device_type, "planar") == 0 ||
|
| 629 |
+
strcmp(candidate->device_type, "curved") == 0 ||
|
| 630 |
+
strcmp(candidate->device_type, "bevel") == 0)) {
|
| 631 |
+
char message[MAX_TEXT + 32];
|
| 632 |
+
snprintf(message, sizeof(message), "Flash device_type is %s",
|
| 633 |
+
candidate->device_type[0] ? candidate->device_type : "empty");
|
| 634 |
+
copy_text(candidate->error, sizeof(candidate->error), message);
|
| 635 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 636 |
+
}
|
| 637 |
+
uint32_t parameter_start = 0x8000;
|
| 638 |
+
uint8_t table[0x2b] = {0};
|
| 639 |
+
int table_result = sf_xu6_read(handle, candidate->control_interface,
|
| 640 |
+
unit, 0x160, table, sizeof(table));
|
| 641 |
+
if (table_result != 0) {
|
| 642 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 643 |
+
"Flash parameter_table: %s", usb_error_text(table_result));
|
| 644 |
+
return table_result;
|
| 645 |
+
}
|
| 646 |
+
if (table[0x0f] == 0xff) {
|
| 647 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 648 |
+
"Flash parameter start address is blank");
|
| 649 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 650 |
+
}
|
| 651 |
+
parameter_start = ((uint32_t)table[0x0f] << 24) |
|
| 652 |
+
((uint32_t)table[0x10] << 16) |
|
| 653 |
+
((uint32_t)table[0x11] << 8) | table[0x12];
|
| 654 |
+
|
| 655 |
+
uint8_t serial_raw[64] = {0};
|
| 656 |
+
int serial_result = sf_xu6_read(handle, candidate->control_interface,
|
| 657 |
+
unit, parameter_start + 0xc0, serial_raw,
|
| 658 |
+
sizeof(serial_raw));
|
| 659 |
+
if (serial_result != 0) {
|
| 660 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 661 |
+
"Flash device_serial: %s", usb_error_text(serial_result));
|
| 662 |
+
return serial_result;
|
| 663 |
+
}
|
| 664 |
+
char flash_serial[MAX_TEXT] = {0};
|
| 665 |
+
decode_flash_serial(serial_raw, sizeof(serial_raw), flash_serial,
|
| 666 |
+
sizeof(flash_serial));
|
| 667 |
+
if (flash_serial[0] == '\0') {
|
| 668 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 669 |
+
"Flash device_serial is empty");
|
| 670 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 671 |
+
}
|
| 672 |
+
copy_text(candidate->flash_serial, sizeof(candidate->flash_serial),
|
| 673 |
+
flash_serial);
|
| 674 |
+
for (char *cursor = candidate->side; *cursor != '\0'; ++cursor) {
|
| 675 |
+
*cursor = (char)tolower((unsigned char)*cursor);
|
| 676 |
+
}
|
| 677 |
+
if (strcmp(candidate->side, "left") != 0 &&
|
| 678 |
+
strcmp(candidate->side, "right") != 0) {
|
| 679 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 680 |
+
"Flash side is %s", candidate->side[0] ? candidate->side : "empty");
|
| 681 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 682 |
+
}
|
| 683 |
+
candidate->flash_ok = 1;
|
| 684 |
+
if (!read_calibration) {
|
| 685 |
+
return 0;
|
| 686 |
+
}
|
| 687 |
+
|
| 688 |
+
struct {
|
| 689 |
+
uint32_t address;
|
| 690 |
+
double *values;
|
| 691 |
+
size_t count;
|
| 692 |
+
const char *name;
|
| 693 |
+
} calibration[] = {
|
| 694 |
+
{0x41000, candidate->fx_params, 5, "fx"},
|
| 695 |
+
{0x42000, candidate->fy_params, 5, "fy"},
|
| 696 |
+
{0x43000, candidate->fz_params, 21, "fz"},
|
| 697 |
+
{0x47000, candidate->ft_params, 20, "ft"},
|
| 698 |
+
{0x44000, candidate->fw_params, 9, "fw"},
|
| 699 |
+
};
|
| 700 |
+
for (size_t table_index = 0;
|
| 701 |
+
table_index < sizeof(calibration) / sizeof(calibration[0]);
|
| 702 |
+
++table_index) {
|
| 703 |
+
/* Each XU6 read pays the setup cost of stop/config/control transfers.
|
| 704 |
+
* Read each contiguous calibration table in 64-byte blocks instead
|
| 705 |
+
* of issuing one transfer per double. */
|
| 706 |
+
uint8_t raw[21 * sizeof(double)] = {0};
|
| 707 |
+
size_t raw_size = calibration[table_index].count * sizeof(double);
|
| 708 |
+
result = sf_xu6_read(
|
| 709 |
+
handle, candidate->control_interface, unit,
|
| 710 |
+
calibration[table_index].address, raw, raw_size);
|
| 711 |
+
if (result != 0) {
|
| 712 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 713 |
+
"Flash %s: %s", calibration[table_index].name,
|
| 714 |
+
usb_error_text(result));
|
| 715 |
+
return result;
|
| 716 |
+
}
|
| 717 |
+
for (size_t value_index = 0;
|
| 718 |
+
value_index < calibration[table_index].count; ++value_index) {
|
| 719 |
+
const uint8_t *slot = raw + value_index * sizeof(double);
|
| 720 |
+
int erased = 1;
|
| 721 |
+
for (size_t byte = 0; byte < sizeof(double); ++byte) {
|
| 722 |
+
if (slot[byte] != 0xff) erased = 0;
|
| 723 |
+
}
|
| 724 |
+
if (erased && (strcmp(calibration[table_index].name, "ft") == 0 ||
|
| 725 |
+
(strcmp(calibration[table_index].name, "fz") == 0 && value_index >= 8))) {
|
| 726 |
+
/* Match 0902 _default_force_calibration_params for erased
|
| 727 |
+
* extension slots only. Required coefficients and I/O errors
|
| 728 |
+
* remain fail-closed. FT's first/fourth defaults are 1, not 0. */
|
| 729 |
+
calibration[table_index].values[value_index] =
|
| 730 |
+
(strcmp(calibration[table_index].name, "ft") == 0 &&
|
| 731 |
+
(value_index == 0 || value_index == 3)) ? 1.0 : 0.0;
|
| 732 |
+
continue;
|
| 733 |
+
}
|
| 734 |
+
memcpy(&calibration[table_index].values[value_index], slot,
|
| 735 |
+
sizeof(double));
|
| 736 |
+
if (!isfinite(calibration[table_index].values[value_index]) &&
|
| 737 |
+
!(strcmp(calibration[table_index].name, "fw") == 0 &&
|
| 738 |
+
value_index >= 5)) {
|
| 739 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 740 |
+
"Flash %s[%zu] is not finite",
|
| 741 |
+
calibration[table_index].name, value_index);
|
| 742 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 743 |
+
}
|
| 744 |
+
}
|
| 745 |
+
}
|
| 746 |
+
candidate->flash_params_ok = 1;
|
| 747 |
+
candidate->flash_ok = 1;
|
| 748 |
+
return 0;
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
static int flash_double_equal(double first, double second) {
|
| 752 |
+
if (isnan(first) && isnan(second)) {
|
| 753 |
+
return 1;
|
| 754 |
+
}
|
| 755 |
+
return first == second;
|
| 756 |
+
}
|
| 757 |
+
|
| 758 |
+
static int flash_double_array_equal(const double *first, const double *second,
|
| 759 |
+
size_t count) {
|
| 760 |
+
for (size_t index = 0; index < count; ++index) {
|
| 761 |
+
if (!flash_double_equal(first[index], second[index])) {
|
| 762 |
+
return 0;
|
| 763 |
+
}
|
| 764 |
+
}
|
| 765 |
+
return 1;
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
static int flash_readout_equal(const camera_candidate_t *first,
|
| 769 |
+
const camera_candidate_t *second,
|
| 770 |
+
int compare_calibration) {
|
| 771 |
+
if (strcmp(first->device_type, second->device_type) != 0 ||
|
| 772 |
+
strcmp(first->side, second->side) != 0 ||
|
| 773 |
+
strcmp(first->flash_serial, second->flash_serial) != 0) {
|
| 774 |
+
return 0;
|
| 775 |
+
}
|
| 776 |
+
if (!compare_calibration) {
|
| 777 |
+
return 1;
|
| 778 |
+
}
|
| 779 |
+
return
|
| 780 |
+
strcmp(first->side, second->side) == 0 &&
|
| 781 |
+
strcmp(first->flash_serial, second->flash_serial) == 0 &&
|
| 782 |
+
flash_double_array_equal(first->fx_params, second->fx_params, 5) &&
|
| 783 |
+
flash_double_array_equal(first->fy_params, second->fy_params, 5) &&
|
| 784 |
+
flash_double_array_equal(first->fz_params, second->fz_params, 21) &&
|
| 785 |
+
flash_double_array_equal(first->ft_params, second->ft_params, 20) &&
|
| 786 |
+
flash_double_array_equal(first->fw_params, second->fw_params, 9);
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
static int read_flash_identity_verified(camera_candidate_t *candidate,
|
| 790 |
+
libusb_device_handle *handle,
|
| 791 |
+
int read_calibration) {
|
| 792 |
+
char last_error[MAX_TEXT] = "";
|
| 793 |
+
int last_result = LIBUSB_ERROR_IO;
|
| 794 |
+
|
| 795 |
+
for (int attempt = 0; attempt < FLASH_PROBE_ATTEMPTS; ++attempt) {
|
| 796 |
+
camera_candidate_t verified;
|
| 797 |
+
int result;
|
| 798 |
+
|
| 799 |
+
reset_flash_identity(candidate);
|
| 800 |
+
result = read_flash_identity_once(candidate, handle,
|
| 801 |
+
read_calibration);
|
| 802 |
+
if (result != 0) {
|
| 803 |
+
copy_text(last_error, sizeof(last_error), candidate->error);
|
| 804 |
+
last_result = result;
|
| 805 |
+
if (attempt + 1 < FLASH_PROBE_ATTEMPTS) {
|
| 806 |
+
sleep_for_probe((size_t)attempt);
|
| 807 |
+
}
|
| 808 |
+
continue;
|
| 809 |
+
}
|
| 810 |
+
|
| 811 |
+
/* A single XU6 transfer can complete while its small DMA/control
|
| 812 |
+
* state was disturbed by USB arbitration. Read the immutable tables
|
| 813 |
+
* into an independent structure and require bit-stable identity and
|
| 814 |
+
* calibration values before accepting them as complete. */
|
| 815 |
+
memset(&verified, 0, sizeof(verified));
|
| 816 |
+
verified.control_interface = candidate->control_interface;
|
| 817 |
+
result = read_flash_identity_once(&verified, handle,
|
| 818 |
+
read_calibration);
|
| 819 |
+
if (result == 0) {
|
| 820 |
+
if (flash_readout_equal(candidate, &verified,
|
| 821 |
+
read_calibration)) {
|
| 822 |
+
return 0;
|
| 823 |
+
}
|
| 824 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 825 |
+
"Flash readback verification mismatch");
|
| 826 |
+
} else {
|
| 827 |
+
char message[MAX_TEXT + 40];
|
| 828 |
+
snprintf(message, sizeof(message),
|
| 829 |
+
"Flash readback verification: %s", verified.error);
|
| 830 |
+
copy_text(candidate->error, sizeof(candidate->error), message);
|
| 831 |
+
}
|
| 832 |
+
copy_text(last_error, sizeof(last_error), candidate->error);
|
| 833 |
+
last_result = result == 0 ? LIBUSB_ERROR_IO : result;
|
| 834 |
+
if (attempt + 1 < FLASH_PROBE_ATTEMPTS) {
|
| 835 |
+
sleep_for_probe((size_t)attempt);
|
| 836 |
+
}
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
candidate->error[0] = '\0';
|
| 840 |
+
copy_text(candidate->error, sizeof(candidate->error),
|
| 841 |
+
last_error[0] ? last_error : "Flash probe exhausted retries");
|
| 842 |
+
candidate->flash_ok = 0;
|
| 843 |
+
candidate->flash_params_ok = 0;
|
| 844 |
+
return last_result;
|
| 845 |
+
}
|
| 846 |
+
|
| 847 |
+
static int inspect_candidate(camera_candidate_t *candidate,
|
| 848 |
+
flash_mode_t flash_mode) {
|
| 849 |
+
const mode_profile_t *profile = profile_for(candidate->vid, candidate->pid);
|
| 850 |
+
if (candidate->usb_device == NULL) {
|
| 851 |
+
copy_text(candidate->error, sizeof(candidate->error),
|
| 852 |
+
"libusb device disappeared");
|
| 853 |
+
candidate->mode_ok = 0;
|
| 854 |
+
return -1;
|
| 855 |
+
}
|
| 856 |
+
candidate->control_interface = find_control_interface(candidate->usb_device);
|
| 857 |
+
if (candidate->control_interface < 0) {
|
| 858 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 859 |
+
"control interface: %s", usb_error_text(candidate->control_interface));
|
| 860 |
+
candidate->mode_ok = 0;
|
| 861 |
+
return -1;
|
| 862 |
+
}
|
| 863 |
+
int descriptor_interface = -1;
|
| 864 |
+
int descriptor_alt = -1;
|
| 865 |
+
unsigned int descriptor_payload = 0;
|
| 866 |
+
int descriptor_result = find_stream_mode(
|
| 867 |
+
candidate->usb_device, profile->expected_altsetting,
|
| 868 |
+
(unsigned int)profile->forced_payload, &descriptor_interface,
|
| 869 |
+
&descriptor_alt, &descriptor_payload);
|
| 870 |
+
if (descriptor_result != 0) {
|
| 871 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 872 |
+
"stream altsetting: %s", usb_error_text(descriptor_result));
|
| 873 |
+
candidate->mode_ok = 0;
|
| 874 |
+
return -1;
|
| 875 |
+
}
|
| 876 |
+
candidate->streaming_interface = descriptor_interface;
|
| 877 |
+
candidate->altsetting = descriptor_alt;
|
| 878 |
+
candidate->descriptor_payload = descriptor_payload;
|
| 879 |
+
|
| 880 |
+
uvc_device_handle_t *uvc_handle = NULL;
|
| 881 |
+
uvc_error_t uvc_result = uvc_open(candidate->uvc_device, &uvc_handle);
|
| 882 |
+
if (uvc_result != UVC_SUCCESS || uvc_handle == NULL) {
|
| 883 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 884 |
+
"uvc_open: %s", uvc_strerror(uvc_result));
|
| 885 |
+
candidate->mode_ok = -1;
|
| 886 |
+
return -1;
|
| 887 |
+
}
|
| 888 |
+
candidate->opened_ok = 1;
|
| 889 |
+
libusb_device_handle *usb_handle = uvc_get_libusb_handle(uvc_handle);
|
| 890 |
+
|
| 891 |
+
uvc_stream_ctrl_t control;
|
| 892 |
+
memset(&control, 0, sizeof(control));
|
| 893 |
+
uvc_result = uvc_get_stream_ctrl_format_size(
|
| 894 |
+
uvc_handle, &control, UVC_FRAME_FORMAT_MJPEG,
|
| 895 |
+
profile->width, profile->height, profile->fps);
|
| 896 |
+
if (uvc_result != UVC_SUCCESS) {
|
| 897 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 898 |
+
"MJPEG %dx%d@%d: %s", profile->width, profile->height,
|
| 899 |
+
profile->fps, uvc_strerror(uvc_result));
|
| 900 |
+
candidate->mode_ok = 0;
|
| 901 |
+
uvc_close(uvc_handle);
|
| 902 |
+
return -1;
|
| 903 |
+
}
|
| 904 |
+
candidate->streaming_interface = control.bInterfaceNumber;
|
| 905 |
+
candidate->negotiated_payload = control.dwMaxPayloadTransferSize;
|
| 906 |
+
candidate->mode_ok = candidate->altsetting == profile->expected_altsetting;
|
| 907 |
+
int result = 0;
|
| 908 |
+
if (!candidate->mode_ok) {
|
| 909 |
+
snprintf(candidate->error, sizeof(candidate->error),
|
| 910 |
+
"alt=%d expected=%d", candidate->altsetting,
|
| 911 |
+
profile->expected_altsetting);
|
| 912 |
+
uvc_close(uvc_handle);
|
| 913 |
+
return -1;
|
| 914 |
+
}
|
| 915 |
+
if ((flash_mode == FLASH_MODE_FULL ||
|
| 916 |
+
flash_mode == FLASH_MODE_IDENTITY) &&
|
| 917 |
+
candidate->vid == SIGHTAC_VID &&
|
| 918 |
+
candidate->pid == SIGHTAC_PID) {
|
| 919 |
+
result = read_flash_identity_verified(
|
| 920 |
+
candidate, usb_handle, flash_mode == FLASH_MODE_FULL);
|
| 921 |
+
if (result != 0) {
|
| 922 |
+
uvc_close(uvc_handle);
|
| 923 |
+
return -1;
|
| 924 |
+
}
|
| 925 |
+
}
|
| 926 |
+
uvc_close(uvc_handle);
|
| 927 |
+
return 0;
|
| 928 |
+
}
|
| 929 |
+
|
| 930 |
+
static int same_outer_parent(const camera_candidate_t *left,
|
| 931 |
+
const camera_candidate_t *right) {
|
| 932 |
+
char left_parent[MAX_PORT_PATH];
|
| 933 |
+
char right_parent[MAX_PORT_PATH];
|
| 934 |
+
const char *left_dot = strrchr(left->port, '.');
|
| 935 |
+
const char *right_dot = strrchr(right->port, '.');
|
| 936 |
+
if (left_dot == NULL || right_dot == NULL || left->bus != right->bus) {
|
| 937 |
+
return 0;
|
| 938 |
+
}
|
| 939 |
+
size_t left_size = (size_t)(left_dot - left->port);
|
| 940 |
+
size_t right_size = (size_t)(right_dot - right->port);
|
| 941 |
+
if (left_size == 0 || left_size >= sizeof(left_parent) ||
|
| 942 |
+
right_size == 0 || right_size >= sizeof(right_parent)) {
|
| 943 |
+
return 0;
|
| 944 |
+
}
|
| 945 |
+
memcpy(left_parent, left->port, left_size);
|
| 946 |
+
left_parent[left_size] = '\0';
|
| 947 |
+
memcpy(right_parent, right->port, right_size);
|
| 948 |
+
right_parent[right_size] = '\0';
|
| 949 |
+
return strcmp(left_parent, right_parent) == 0;
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
static int port_has_prefix(const char *port, const char *prefix) {
|
| 953 |
+
size_t prefix_size = strlen(prefix);
|
| 954 |
+
return strncmp(port, prefix, prefix_size) == 0 &&
|
| 955 |
+
port[prefix_size] == '.';
|
| 956 |
+
}
|
| 957 |
+
|
| 958 |
+
static int build_groups(const camera_candidate_t *candidates, size_t count,
|
| 959 |
+
camera_group_t *groups, size_t *group_count) {
|
| 960 |
+
int used[MAX_CANDIDATES] = {0};
|
| 961 |
+
size_t output_count = 0;
|
| 962 |
+
for (size_t i = 0; i < count; ++i) {
|
| 963 |
+
if (candidates[i].vid != SIGHTAC_VID || used[i]) {
|
| 964 |
+
continue;
|
| 965 |
+
}
|
| 966 |
+
int matches[2] = {-1, -1};
|
| 967 |
+
size_t match_count = 0;
|
| 968 |
+
for (size_t j = i; j < count; ++j) {
|
| 969 |
+
if (candidates[j].vid == SIGHTAC_VID &&
|
| 970 |
+
same_outer_parent(&candidates[i], &candidates[j])) {
|
| 971 |
+
if (match_count < 2) {
|
| 972 |
+
matches[match_count] = (int)j;
|
| 973 |
+
}
|
| 974 |
+
++match_count;
|
| 975 |
+
}
|
| 976 |
+
}
|
| 977 |
+
if (match_count != 2) {
|
| 978 |
+
fprintf(stderr, "[DISCOVERY] ambiguous Sightac group bus=%d parent of %s: %zu node(s)\n",
|
| 979 |
+
candidates[i].bus, candidates[i].port, match_count);
|
| 980 |
+
continue;
|
| 981 |
+
}
|
| 982 |
+
char outer_path[MAX_PORT_PATH];
|
| 983 |
+
const char *dot = strrchr(candidates[matches[0]].port, '.');
|
| 984 |
+
size_t outer_size = (size_t)(dot - candidates[matches[0]].port);
|
| 985 |
+
if (outer_size == 0 || outer_size >= sizeof(outer_path)) {
|
| 986 |
+
continue;
|
| 987 |
+
}
|
| 988 |
+
memcpy(outer_path, candidates[matches[0]].port, outer_size);
|
| 989 |
+
outer_path[outer_size] = '\0';
|
| 990 |
+
|
| 991 |
+
int decxin = -1;
|
| 992 |
+
size_t decxin_count = 0;
|
| 993 |
+
for (size_t j = 0; j < count; ++j) {
|
| 994 |
+
if (candidates[j].vid == DECXIN_VID &&
|
| 995 |
+
candidates[j].pid == DECXIN_PID &&
|
| 996 |
+
candidates[j].bus == candidates[i].bus &&
|
| 997 |
+
port_has_prefix(candidates[j].port, outer_path)) {
|
| 998 |
+
decxin = (int)j;
|
| 999 |
+
++decxin_count;
|
| 1000 |
+
}
|
| 1001 |
+
}
|
| 1002 |
+
if (decxin_count != 1 || output_count >= MAX_GROUPS) {
|
| 1003 |
+
fprintf(stderr, "[DISCOVERY] bus=%d outer=%s DECXIN match count=%zu; group rejected\n",
|
| 1004 |
+
candidates[i].bus, outer_path, decxin_count);
|
| 1005 |
+
continue;
|
| 1006 |
+
}
|
| 1007 |
+
groups[output_count].sightac[0] = matches[0];
|
| 1008 |
+
groups[output_count].sightac[1] = matches[1];
|
| 1009 |
+
groups[output_count].decxin = decxin;
|
| 1010 |
+
groups[output_count].bus = candidates[i].bus;
|
| 1011 |
+
copy_text(groups[output_count].outer_path,
|
| 1012 |
+
sizeof(groups[output_count].outer_path), outer_path);
|
| 1013 |
+
used[matches[0]] = 1;
|
| 1014 |
+
used[matches[1]] = 1;
|
| 1015 |
+
used[decxin] = 1;
|
| 1016 |
+
++output_count;
|
| 1017 |
+
}
|
| 1018 |
+
*group_count = output_count;
|
| 1019 |
+
for (size_t i = 0; i < count; ++i) {
|
| 1020 |
+
if (!used[i]) {
|
| 1021 |
+
fprintf(stderr, "[DISCOVERY] ungrouped %s %04x:%04x bus=%d port=%s\n",
|
| 1022 |
+
profile_for(candidates[i].vid, candidates[i].pid)->kind,
|
| 1023 |
+
candidates[i].vid, candidates[i].pid, candidates[i].bus,
|
| 1024 |
+
candidates[i].port);
|
| 1025 |
+
}
|
| 1026 |
+
}
|
| 1027 |
+
return output_count > 0 ? 0 : -1;
|
| 1028 |
+
}
|
| 1029 |
+
|
| 1030 |
+
static const camera_candidate_t *candidate_by_side(
|
| 1031 |
+
const camera_candidate_t *candidates, const camera_group_t *group,
|
| 1032 |
+
const char *side) {
|
| 1033 |
+
for (size_t i = 0; i < 2; ++i) {
|
| 1034 |
+
const camera_candidate_t *candidate = &candidates[group->sightac[i]];
|
| 1035 |
+
if (strcmp(candidate->side, side) == 0) {
|
| 1036 |
+
return candidate;
|
| 1037 |
+
}
|
| 1038 |
+
}
|
| 1039 |
+
return NULL;
|
| 1040 |
+
}
|
| 1041 |
+
|
| 1042 |
+
static int group_sides_ok(const camera_candidate_t *candidates,
|
| 1043 |
+
const camera_group_t *group,
|
| 1044 |
+
int require_calibration) {
|
| 1045 |
+
const camera_candidate_t *left = candidate_by_side(candidates, group, "left");
|
| 1046 |
+
const camera_candidate_t *right = candidate_by_side(candidates, group, "right");
|
| 1047 |
+
const camera_candidate_t *decxin = &candidates[group->decxin];
|
| 1048 |
+
return left != NULL && right != NULL && left != right &&
|
| 1049 |
+
candidates[group->sightac[0]].flash_ok &&
|
| 1050 |
+
candidates[group->sightac[1]].flash_ok &&
|
| 1051 |
+
(!require_calibration ||
|
| 1052 |
+
(candidates[group->sightac[0]].flash_params_ok &&
|
| 1053 |
+
candidates[group->sightac[1]].flash_params_ok)) &&
|
| 1054 |
+
left->mode_ok > 0 && right->mode_ok > 0 &&
|
| 1055 |
+
decxin->mode_ok > 0;
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
static void print_candidate(const camera_candidate_t *candidate,
|
| 1059 |
+
flash_mode_t flash_mode) {
|
| 1060 |
+
const mode_profile_t *profile = profile_for(candidate->vid, candidate->pid);
|
| 1061 |
+
const char *mode_status = candidate->mode_ok > 0 ? "ok" :
|
| 1062 |
+
(candidate->mode_ok < 0 ? "unknown" : "failed");
|
| 1063 |
+
fprintf(stderr,
|
| 1064 |
+
"[DISCOVERY] %-7s vidpid=%04x:%04x bus=%d port=%s usb_serial=%s "
|
| 1065 |
+
"control_if=%d stream_if=%d alt=%d descriptor_payload=%u "
|
| 1066 |
+
"negotiated_payload=%u mode=%s",
|
| 1067 |
+
profile->kind, candidate->vid, candidate->pid, candidate->bus,
|
| 1068 |
+
candidate->port, candidate->usb_serial[0] ? candidate->usb_serial : "<none>",
|
| 1069 |
+
candidate->control_interface, candidate->streaming_interface,
|
| 1070 |
+
candidate->altsetting, candidate->descriptor_payload,
|
| 1071 |
+
candidate->negotiated_payload, mode_status);
|
| 1072 |
+
if (candidate->vid == SIGHTAC_VID &&
|
| 1073 |
+
(flash_mode == FLASH_MODE_FULL ||
|
| 1074 |
+
flash_mode == FLASH_MODE_IDENTITY)) {
|
| 1075 |
+
const char *calibration = flash_mode == FLASH_MODE_IDENTITY
|
| 1076 |
+
? "skipped"
|
| 1077 |
+
: (candidate->flash_params_ok
|
| 1078 |
+
? "ok" : "failed");
|
| 1079 |
+
fprintf(stderr, " flash=%s calibration=%s side=%s serial=%s type=%s",
|
| 1080 |
+
candidate->flash_ok ? "ok" : "failed",
|
| 1081 |
+
calibration,
|
| 1082 |
+
candidate->side[0] ? candidate->side : "<none>",
|
| 1083 |
+
candidate->flash_serial[0] ? candidate->flash_serial : "<none>",
|
| 1084 |
+
candidate->device_type[0] ? candidate->device_type : "<none>");
|
| 1085 |
+
}
|
| 1086 |
+
if (candidate->error[0]) {
|
| 1087 |
+
fprintf(stderr, " error=%s", candidate->error);
|
| 1088 |
+
}
|
| 1089 |
+
fputc('\n', stderr);
|
| 1090 |
+
}
|
| 1091 |
+
|
| 1092 |
+
static void write_json_candidate(FILE *out,
|
| 1093 |
+
const camera_candidate_t *candidate,
|
| 1094 |
+
int include_flash,
|
| 1095 |
+
int include_calibration) {
|
| 1096 |
+
const mode_profile_t *profile = profile_for(candidate->vid, candidate->pid);
|
| 1097 |
+
fprintf(out,
|
| 1098 |
+
"{\"kind\":");
|
| 1099 |
+
write_json_string(out, profile->kind);
|
| 1100 |
+
fprintf(out,
|
| 1101 |
+
",\"vid\":%d,\"pid\":%d,\"bus\":%d,\"port\":",
|
| 1102 |
+
candidate->vid, candidate->pid, candidate->bus);
|
| 1103 |
+
write_json_string(out, candidate->port);
|
| 1104 |
+
fprintf(out,
|
| 1105 |
+
",\"usb_serial\":");
|
| 1106 |
+
write_json_string(out, candidate->usb_serial);
|
| 1107 |
+
fprintf(out,
|
| 1108 |
+
",\"control_interface\":%d,\"streaming_interface\":%d,"
|
| 1109 |
+
"\"altsetting\":%d,\"descriptor_payload\":%u,"
|
| 1110 |
+
"\"negotiated_payload\":%u,\"mode_ok\":%s",
|
| 1111 |
+
candidate->control_interface, candidate->streaming_interface,
|
| 1112 |
+
candidate->altsetting, candidate->descriptor_payload,
|
| 1113 |
+
candidate->negotiated_payload,
|
| 1114 |
+
candidate->mode_ok > 0 ? "true" : "false");
|
| 1115 |
+
if (include_flash) {
|
| 1116 |
+
fprintf(out, ",\"flash_ok\":%s,\"flash_params_ok\":%s,\"side\":",
|
| 1117 |
+
candidate->flash_ok ? "true" : "false",
|
| 1118 |
+
candidate->flash_params_ok ? "true" : "false");
|
| 1119 |
+
write_json_string(out, candidate->side);
|
| 1120 |
+
fprintf(out, ",\"flash_serial\":");
|
| 1121 |
+
write_json_string(out, candidate->flash_serial);
|
| 1122 |
+
fprintf(out, ",\"device_type\":");
|
| 1123 |
+
write_json_string(out, candidate->device_type);
|
| 1124 |
+
if (!include_calibration) {
|
| 1125 |
+
goto candidate_error;
|
| 1126 |
+
}
|
| 1127 |
+
fprintf(out, ",\"flash_params\":{\"device_type\":");
|
| 1128 |
+
write_json_string(out, candidate->device_type);
|
| 1129 |
+
fprintf(out, ",\"fx_params\":[");
|
| 1130 |
+
for (size_t i = 0; i < 5; ++i) {
|
| 1131 |
+
if (i != 0) fputc(',', out);
|
| 1132 |
+
fprintf(out, "%.17g", candidate->fx_params[i]);
|
| 1133 |
+
}
|
| 1134 |
+
fprintf(out, "],\"fy_params\":[");
|
| 1135 |
+
for (size_t i = 0; i < 5; ++i) {
|
| 1136 |
+
if (i != 0) fputc(',', out);
|
| 1137 |
+
fprintf(out, "%.17g", candidate->fy_params[i]);
|
| 1138 |
+
}
|
| 1139 |
+
fprintf(out, "],\"fz_params\":[");
|
| 1140 |
+
for (size_t i = 0; i < 21; ++i) {
|
| 1141 |
+
if (i != 0) fputc(',', out);
|
| 1142 |
+
fprintf(out, "%.17g", candidate->fz_params[i]);
|
| 1143 |
+
}
|
| 1144 |
+
fprintf(out, "],\"ft_params\":[");
|
| 1145 |
+
for (size_t i = 0; i < 20; ++i) {
|
| 1146 |
+
if (i != 0) fputc(',', out);
|
| 1147 |
+
fprintf(out, "%.17g", candidate->ft_params[i]);
|
| 1148 |
+
}
|
| 1149 |
+
fprintf(out, "],\"fw_params\":[");
|
| 1150 |
+
for (size_t i = 0; i < 9; ++i) {
|
| 1151 |
+
if (i != 0) fputc(',', out);
|
| 1152 |
+
if (isfinite(candidate->fw_params[i])) {
|
| 1153 |
+
fprintf(out, "%.17g", candidate->fw_params[i]);
|
| 1154 |
+
} else {
|
| 1155 |
+
/* The bevel firmware reserves fw[5..8]; current ROI/line
|
| 1156 |
+
* code consumes only fw[0..4]. Keep the JSON valid. */
|
| 1157 |
+
fputs("null", out);
|
| 1158 |
+
}
|
| 1159 |
+
}
|
| 1160 |
+
fprintf(out, "],\"flash_state\":\"programmed\","
|
| 1161 |
+
"\"calibration_source\":\"hardware\"}");
|
| 1162 |
+
}
|
| 1163 |
+
candidate_error:
|
| 1164 |
+
fprintf(out, ",\"error\":");
|
| 1165 |
+
write_json_string(out, candidate->error);
|
| 1166 |
+
fputc('}', out);
|
| 1167 |
+
}
|
| 1168 |
+
|
| 1169 |
+
static void write_camera_section(FILE *out, const camera_candidate_t *candidate,
|
| 1170 |
+
int group_number, const char *name,
|
| 1171 |
+
const char *socket_name) {
|
| 1172 |
+
const mode_profile_t *profile = profile_for(candidate->vid, candidate->pid);
|
| 1173 |
+
fprintf(out,
|
| 1174 |
+
"[%s]\n"
|
| 1175 |
+
"vid=0x%04x\n"
|
| 1176 |
+
"pid=0x%04x\n"
|
| 1177 |
+
"usb_bus=%d\n"
|
| 1178 |
+
"usb_port_path=%s\n"
|
| 1179 |
+
"streaming_interface=%d\n"
|
| 1180 |
+
"forced_altsetting=%d\n"
|
| 1181 |
+
"forced_payload=%d\n"
|
| 1182 |
+
"width=%d\n"
|
| 1183 |
+
"height=%d\n"
|
| 1184 |
+
"fps=%d\n"
|
| 1185 |
+
"format=MJPEG\n"
|
| 1186 |
+
"socket_path=/tmp/ksq-camera-service-unit-%d-%s.sock\n\n",
|
| 1187 |
+
name, candidate->vid, candidate->pid, candidate->bus, candidate->port,
|
| 1188 |
+
candidate->streaming_interface, candidate->altsetting,
|
| 1189 |
+
profile->forced_payload, profile->width, profile->height,
|
| 1190 |
+
profile->fps, group_number, socket_name);
|
| 1191 |
+
}
|
| 1192 |
+
|
| 1193 |
+
static int write_generated_config(FILE *out, const camera_candidate_t *candidates,
|
| 1194 |
+
const camera_group_t *groups,
|
| 1195 |
+
size_t group_count) {
|
| 1196 |
+
for (size_t index = 0; index < group_count; ++index) {
|
| 1197 |
+
if (!group_sides_ok(candidates, &groups[index], 1)) {
|
| 1198 |
+
fprintf(stderr, "[DISCOVERY] group %zu rejected: mode or left/right Flash identity is incomplete\n",
|
| 1199 |
+
index + 1);
|
| 1200 |
+
return -1;
|
| 1201 |
+
}
|
| 1202 |
+
}
|
| 1203 |
+
fprintf(out,
|
| 1204 |
+
"# Generated by discover-uvc-config.\n"
|
| 1205 |
+
"# Scope: Sightac + DECXIN only; Fays is intentionally excluded.\n"
|
| 1206 |
+
"# Bus/port/interface/alt are from the current libusb/libuvc scan.\n\n");
|
| 1207 |
+
for (size_t index = 0; index < group_count; ++index) {
|
| 1208 |
+
const camera_candidate_t *left =
|
| 1209 |
+
candidate_by_side(candidates, &groups[index], "left");
|
| 1210 |
+
const camera_candidate_t *right =
|
| 1211 |
+
candidate_by_side(candidates, &groups[index], "right");
|
| 1212 |
+
const camera_candidate_t *decxin = &candidates[groups[index].decxin];
|
| 1213 |
+
char name[64];
|
| 1214 |
+
snprintf(name, sizeof(name), "unit_%zu_sightac_left", index + 1);
|
| 1215 |
+
write_camera_section(out, left, (int)(index + 1), name,
|
| 1216 |
+
"sightac-left");
|
| 1217 |
+
snprintf(name, sizeof(name), "unit_%zu_sightac_right", index + 1);
|
| 1218 |
+
write_camera_section(out, right, (int)(index + 1), name,
|
| 1219 |
+
"sightac-right");
|
| 1220 |
+
snprintf(name, sizeof(name), "unit_%zu_decxin", index + 1);
|
| 1221 |
+
write_camera_section(out, decxin, (int)(index + 1), name, "decxin");
|
| 1222 |
+
}
|
| 1223 |
+
return 0;
|
| 1224 |
+
}
|
| 1225 |
+
|
| 1226 |
+
static int write_json_report(FILE *out, const camera_candidate_t *candidates,
|
| 1227 |
+
const camera_group_t *groups, size_t group_count,
|
| 1228 |
+
flash_mode_t flash_mode) {
|
| 1229 |
+
const int include_flash = flash_mode == FLASH_MODE_FULL ||
|
| 1230 |
+
flash_mode == FLASH_MODE_IDENTITY;
|
| 1231 |
+
const int include_calibration = flash_mode == FLASH_MODE_FULL;
|
| 1232 |
+
const int require_calibration = flash_mode == FLASH_MODE_FULL;
|
| 1233 |
+
fprintf(out, "{\"groups\":[");
|
| 1234 |
+
for (size_t index = 0; index < group_count; ++index) {
|
| 1235 |
+
const camera_group_t *group = &groups[index];
|
| 1236 |
+
if (index != 0) {
|
| 1237 |
+
fputc(',', out);
|
| 1238 |
+
}
|
| 1239 |
+
fprintf(out, "{\"bus\":%d,\"outer_path\":", group->bus);
|
| 1240 |
+
write_json_string(out, group->outer_path);
|
| 1241 |
+
fprintf(out, ",\"sightac\":[");
|
| 1242 |
+
write_json_candidate(out, &candidates[group->sightac[0]],
|
| 1243 |
+
include_flash, include_calibration);
|
| 1244 |
+
fputc(',', out);
|
| 1245 |
+
write_json_candidate(out, &candidates[group->sightac[1]],
|
| 1246 |
+
include_flash, include_calibration);
|
| 1247 |
+
fprintf(out, "],\"decxin\":");
|
| 1248 |
+
write_json_candidate(out, &candidates[group->decxin], 0, 0);
|
| 1249 |
+
fprintf(out, ",\"valid\":%s}",
|
| 1250 |
+
group_sides_ok(candidates, group,
|
| 1251 |
+
require_calibration) ? "true" : "false");
|
| 1252 |
+
}
|
| 1253 |
+
fprintf(out, "]}\n");
|
| 1254 |
+
return ferror(out) ? -1 : 0;
|
| 1255 |
+
}
|
| 1256 |
+
|
| 1257 |
+
static void print_usage(const char *program) {
|
| 1258 |
+
fprintf(stderr,
|
| 1259 |
+
"usage: %s [--emit-config PATH] [--json] [--skip-flash|--identity-only|--topology-only] [--bus N --port-prefix P]\n"
|
| 1260 |
+
" default: scan Sightac/DECXIN and read Sightac Flash side/serial\n"
|
| 1261 |
+
" --emit-config PATH: write a generated camera-service INI\n"
|
| 1262 |
+
" --json: write machine-readable UVC groups to stdout\n"
|
| 1263 |
+
" --skip-flash: topology/mode report only; never emits a usable INI\n"
|
| 1264 |
+
" --identity-only: read Flash side/serial without calibration tables\n"
|
| 1265 |
+
" --topology-only: USB topology only; does not open UVC streams\n"
|
| 1266 |
+
" --bus N --port-prefix P: inspect only this physical USB group\n",
|
| 1267 |
+
program);
|
| 1268 |
+
}
|
| 1269 |
+
|
| 1270 |
+
int main(int argc, char **argv) {
|
| 1271 |
+
const char *config_path = NULL;
|
| 1272 |
+
int json_output = 0;
|
| 1273 |
+
flash_mode_t flash_mode = FLASH_MODE_FULL;
|
| 1274 |
+
int only_bus = -1;
|
| 1275 |
+
const char *only_port = NULL;
|
| 1276 |
+
for (int index = 1; index < argc; ++index) {
|
| 1277 |
+
if (strcmp(argv[index], "--emit-config") == 0 && index + 1 < argc) {
|
| 1278 |
+
config_path = argv[++index];
|
| 1279 |
+
} else if (strcmp(argv[index], "--bus") == 0 && index + 1 < argc) {
|
| 1280 |
+
const char *value = argv[++index];
|
| 1281 |
+
if (!*value || strlen(value) > 3 || strspn(value, "0123456789") != strlen(value)) return 2;
|
| 1282 |
+
only_bus = atoi(value);
|
| 1283 |
+
if (only_bus <= 0 || only_bus > 255) return 2;
|
| 1284 |
+
} else if (strcmp(argv[index], "--port-prefix") == 0 && index + 1 < argc) {
|
| 1285 |
+
only_port = argv[++index];
|
| 1286 |
+
if (!*only_port || strspn(only_port, "0123456789.") != strlen(only_port)) return 2;
|
| 1287 |
+
} else if (strcmp(argv[index], "--json") == 0) {
|
| 1288 |
+
json_output = 1;
|
| 1289 |
+
} else if (strcmp(argv[index], "--skip-flash") == 0) {
|
| 1290 |
+
flash_mode = FLASH_MODE_NONE;
|
| 1291 |
+
} else if (strcmp(argv[index], "--identity-only") == 0) {
|
| 1292 |
+
flash_mode = FLASH_MODE_IDENTITY;
|
| 1293 |
+
} else if (strcmp(argv[index], "--topology-only") == 0) {
|
| 1294 |
+
flash_mode = FLASH_MODE_TOPOLOGY_ONLY;
|
| 1295 |
+
} else {
|
| 1296 |
+
print_usage(argv[0]);
|
| 1297 |
+
return 2;
|
| 1298 |
+
}
|
| 1299 |
+
}
|
| 1300 |
+
|
| 1301 |
+
if ((only_bus > 0) != (only_port != NULL)) return 2;
|
| 1302 |
+
|
| 1303 |
+
uvc_context_t *uvc_context = NULL;
|
| 1304 |
+
uvc_error_t uvc_result = uvc_init(&uvc_context, NULL);
|
| 1305 |
+
if (uvc_result != UVC_SUCCESS) {
|
| 1306 |
+
fprintf(stderr, "[DISCOVERY] uvc_init failed: %s\n",
|
| 1307 |
+
uvc_strerror(uvc_result));
|
| 1308 |
+
return 1;
|
| 1309 |
+
}
|
| 1310 |
+
uvc_device_t **devices = NULL;
|
| 1311 |
+
uvc_result = uvc_get_device_list(uvc_context, &devices);
|
| 1312 |
+
if (uvc_result != UVC_SUCCESS) {
|
| 1313 |
+
fprintf(stderr, "[DISCOVERY] uvc_get_device_list failed: %s\n",
|
| 1314 |
+
uvc_strerror(uvc_result));
|
| 1315 |
+
uvc_exit(uvc_context);
|
| 1316 |
+
return 1;
|
| 1317 |
+
}
|
| 1318 |
+
libusb_context *usb_context = NULL;
|
| 1319 |
+
int usb_result = libusb_init(&usb_context);
|
| 1320 |
+
if (usb_result != 0) {
|
| 1321 |
+
fprintf(stderr, "[DISCOVERY] libusb_init failed: %s\n",
|
| 1322 |
+
usb_error_text(usb_result));
|
| 1323 |
+
uvc_free_device_list(devices, 1);
|
| 1324 |
+
uvc_exit(uvc_context);
|
| 1325 |
+
return 1;
|
| 1326 |
+
}
|
| 1327 |
+
libusb_device **raw_devices = NULL;
|
| 1328 |
+
ssize_t raw_count = libusb_get_device_list(usb_context, &raw_devices);
|
| 1329 |
+
if (raw_count < 0) {
|
| 1330 |
+
fprintf(stderr, "[DISCOVERY] libusb_get_device_list failed: %s\n",
|
| 1331 |
+
usb_error_text((int)raw_count));
|
| 1332 |
+
libusb_exit(usb_context);
|
| 1333 |
+
uvc_free_device_list(devices, 1);
|
| 1334 |
+
uvc_exit(uvc_context);
|
| 1335 |
+
return 1;
|
| 1336 |
+
}
|
| 1337 |
+
|
| 1338 |
+
camera_candidate_t candidates[MAX_CANDIDATES];
|
| 1339 |
+
memset(candidates, 0, sizeof(candidates));
|
| 1340 |
+
size_t candidate_count = 0;
|
| 1341 |
+
for (size_t index = 0; devices[index] != NULL; ++index) {
|
| 1342 |
+
/* Filter physical scope BEFORE descriptors/Flash/open operations.
|
| 1343 |
+
* A second rig must never open cameras already streaming for the first. */
|
| 1344 |
+
if (only_port) {
|
| 1345 |
+
char port[MAX_TEXT] = {0};
|
| 1346 |
+
size_t length = strlen(only_port);
|
| 1347 |
+
if ((int)uvc_get_bus_number(devices[index]) != only_bus ||
|
| 1348 |
+
format_uvc_port_path(devices[index], port, sizeof(port)) != 0 ||
|
| 1349 |
+
strncmp(port, only_port, length) != 0 ||
|
| 1350 |
+
(port[length] != '\0' && port[length] != '.')) continue;
|
| 1351 |
+
}
|
| 1352 |
+
uvc_device_descriptor_t *descriptor = NULL;
|
| 1353 |
+
if (uvc_get_device_descriptor(devices[index], &descriptor) != UVC_SUCCESS ||
|
| 1354 |
+
descriptor == NULL) {
|
| 1355 |
+
continue;
|
| 1356 |
+
}
|
| 1357 |
+
const mode_profile_t *profile =
|
| 1358 |
+
profile_for(descriptor->idVendor, descriptor->idProduct);
|
| 1359 |
+
if (profile == NULL || candidate_count >= MAX_CANDIDATES) {
|
| 1360 |
+
uvc_free_device_descriptor(descriptor);
|
| 1361 |
+
continue;
|
| 1362 |
+
}
|
| 1363 |
+
camera_candidate_t *candidate = &candidates[candidate_count++];
|
| 1364 |
+
candidate->uvc_device = devices[index];
|
| 1365 |
+
candidate->control_interface = -1;
|
| 1366 |
+
candidate->streaming_interface = -1;
|
| 1367 |
+
candidate->altsetting = -1;
|
| 1368 |
+
candidate->mode_ok = -1;
|
| 1369 |
+
candidate->vid = descriptor->idVendor;
|
| 1370 |
+
candidate->pid = descriptor->idProduct;
|
| 1371 |
+
copy_text(candidate->usb_serial, sizeof(candidate->usb_serial),
|
| 1372 |
+
descriptor->serialNumber);
|
| 1373 |
+
if (format_uvc_port_path(devices[index], candidate->port,
|
| 1374 |
+
sizeof(candidate->port)) != 0) {
|
| 1375 |
+
copy_text(candidate->error, sizeof(candidate->error),
|
| 1376 |
+
"cannot read USB port path");
|
| 1377 |
+
}
|
| 1378 |
+
candidate->bus = uvc_get_bus_number(devices[index]);
|
| 1379 |
+
candidate->usb_device = find_raw_device(
|
| 1380 |
+
raw_devices, raw_count, candidate->vid, candidate->pid,
|
| 1381 |
+
candidate->bus, candidate->port);
|
| 1382 |
+
int status = 0;
|
| 1383 |
+
if (flash_mode != FLASH_MODE_TOPOLOGY_ONLY) {
|
| 1384 |
+
status = inspect_candidate(candidate, flash_mode);
|
| 1385 |
+
}
|
| 1386 |
+
if (status != 0 && candidate->error[0] == '\0') {
|
| 1387 |
+
copy_text(candidate->error, sizeof(candidate->error), "inspection failed");
|
| 1388 |
+
}
|
| 1389 |
+
print_candidate(candidate, flash_mode);
|
| 1390 |
+
uvc_free_device_descriptor(descriptor);
|
| 1391 |
+
}
|
| 1392 |
+
|
| 1393 |
+
camera_group_t groups[MAX_GROUPS];
|
| 1394 |
+
memset(groups, 0, sizeof(groups));
|
| 1395 |
+
size_t group_count = 0;
|
| 1396 |
+
if (candidate_count == 0 ||
|
| 1397 |
+
build_groups(candidates, candidate_count, groups, &group_count) != 0) {
|
| 1398 |
+
fprintf(stderr, "[DISCOVERY] no complete Sightac + DECXIN topology group\n");
|
| 1399 |
+
libusb_free_device_list(raw_devices, 1);
|
| 1400 |
+
libusb_exit(usb_context);
|
| 1401 |
+
uvc_free_device_list(devices, 1);
|
| 1402 |
+
uvc_exit(uvc_context);
|
| 1403 |
+
return 3;
|
| 1404 |
+
}
|
| 1405 |
+
fprintf(stderr, "[DISCOVERY] complete group(s)=%zu\n", group_count);
|
| 1406 |
+
|
| 1407 |
+
int result = 0;
|
| 1408 |
+
if (config_path != NULL && json_output) {
|
| 1409 |
+
fprintf(stderr, "[DISCOVERY] refusing --emit-config with --json\n");
|
| 1410 |
+
result = 2;
|
| 1411 |
+
} else if (json_output) {
|
| 1412 |
+
if (write_json_report(stdout, candidates, groups, group_count,
|
| 1413 |
+
flash_mode) != 0) {
|
| 1414 |
+
result = 1;
|
| 1415 |
+
}
|
| 1416 |
+
} else if (config_path != NULL) {
|
| 1417 |
+
if (flash_mode != FLASH_MODE_FULL) {
|
| 1418 |
+
fprintf(stderr, "[DISCOVERY] refusing --emit-config without full Flash mode\n");
|
| 1419 |
+
result = 2;
|
| 1420 |
+
} else {
|
| 1421 |
+
char temporary_path[PATH_MAX];
|
| 1422 |
+
int path_result = snprintf(temporary_path, sizeof(temporary_path),
|
| 1423 |
+
"%s.tmp.%ld", config_path,
|
| 1424 |
+
(long)getpid());
|
| 1425 |
+
if (path_result < 0 || (size_t)path_result >= sizeof(temporary_path)) {
|
| 1426 |
+
fprintf(stderr, "[DISCOVERY] config path is too long: %s\n",
|
| 1427 |
+
config_path);
|
| 1428 |
+
result = 1;
|
| 1429 |
+
goto cleanup;
|
| 1430 |
+
}
|
| 1431 |
+
FILE *out = fopen(temporary_path, "w");
|
| 1432 |
+
if (out == NULL) {
|
| 1433 |
+
fprintf(stderr, "[DISCOVERY] cannot write %s: %s\n",
|
| 1434 |
+
config_path, strerror(errno));
|
| 1435 |
+
result = 1;
|
| 1436 |
+
} else {
|
| 1437 |
+
int write_result = write_generated_config(
|
| 1438 |
+
out, candidates, groups, group_count);
|
| 1439 |
+
int close_result = fclose(out);
|
| 1440 |
+
if (write_result != 0 || close_result != 0 ||
|
| 1441 |
+
rename(temporary_path, config_path) != 0) {
|
| 1442 |
+
fprintf(stderr, "[DISCOVERY] failed to generate %s\n",
|
| 1443 |
+
config_path);
|
| 1444 |
+
unlink(temporary_path);
|
| 1445 |
+
result = 1;
|
| 1446 |
+
} else {
|
| 1447 |
+
fprintf(stderr, "[DISCOVERY] generated config: %s\n",
|
| 1448 |
+
config_path);
|
| 1449 |
+
}
|
| 1450 |
+
}
|
| 1451 |
+
}
|
| 1452 |
+
} else if (flash_mode == FLASH_MODE_FULL) {
|
| 1453 |
+
if (write_generated_config(stdout, candidates, groups, group_count) != 0) {
|
| 1454 |
+
result = 1;
|
| 1455 |
+
}
|
| 1456 |
+
}
|
| 1457 |
+
cleanup:
|
| 1458 |
+
libusb_free_device_list(raw_devices, 1);
|
| 1459 |
+
libusb_exit(usb_context);
|
| 1460 |
+
uvc_free_device_list(devices, 1);
|
| 1461 |
+
uvc_exit(uvc_context);
|
| 1462 |
+
return result;
|
| 1463 |
+
}
|
collector/core/gripper/camera_service_src/tools/sonix_flash_probe.c
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Read-only Sonix XU/Serial-Flash probe.
|
| 3 |
+
*
|
| 4 |
+
* This is the libusb equivalent of the source SDK's
|
| 5 |
+
* UVCIOC_CTRL_QUERY(UVC_SET_CUR/UVC_GET_CUR) path. It never issues a
|
| 6 |
+
* serial-flash write or erase command. The SET_CUR requests below only set
|
| 7 |
+
* the address for the following read, exactly as XU_ReadDataFormFlash().
|
| 8 |
+
*/
|
| 9 |
+
#include <libusb-1.0/libusb.h>
|
| 10 |
+
|
| 11 |
+
#include <ctype.h>
|
| 12 |
+
#include <errno.h>
|
| 13 |
+
#include <math.h>
|
| 14 |
+
#include <stdint.h>
|
| 15 |
+
#include <stdio.h>
|
| 16 |
+
#include <stdlib.h>
|
| 17 |
+
#include <string.h>
|
| 18 |
+
|
| 19 |
+
#define SONIX_VID 0x0c45
|
| 20 |
+
#define SONIX_PID 0x636f
|
| 21 |
+
#define XU_GET_CUR 0x81
|
| 22 |
+
#define XU_SET_CUR 0x01
|
| 23 |
+
#define XU_CTRL_GET 0xa1
|
| 24 |
+
#define XU_CTRL_SET 0x21
|
| 25 |
+
#define XU_ASIC_SELECTOR 0x01
|
| 26 |
+
#define XU_FLASH_SELECTOR 0x03
|
| 27 |
+
#define XU_FLASH_XU6_SELECTOR 0x06
|
| 28 |
+
#define CTRL_TIMEOUT_MS 2000
|
| 29 |
+
#define FLASH_MAX_ADDRESS 0x4ffff
|
| 30 |
+
#define SF_XU64_EU_ADDR 0x335
|
| 31 |
+
#define SF_XU64_RW_START_ADDR 0x337
|
| 32 |
+
#define SF_XU64_RW_END_ADDR 0x33b
|
| 33 |
+
|
| 34 |
+
static void print_hex(const uint8_t *data, size_t length) {
|
| 35 |
+
for (size_t i = 0; i < length; ++i) {
|
| 36 |
+
printf("%02x%s", data[i], (i + 1 == length) ? "" : " ");
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
static void print_ascii(const uint8_t *data, size_t length) {
|
| 41 |
+
putchar('"');
|
| 42 |
+
for (size_t i = 0; i < length; ++i) {
|
| 43 |
+
putchar(isprint(data[i]) ? (int)data[i] : '.');
|
| 44 |
+
}
|
| 45 |
+
puts("\"");
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
static int port_path(libusb_device *device, char *out, size_t out_size) {
|
| 49 |
+
uint8_t ports[8];
|
| 50 |
+
int count = libusb_get_port_numbers(device, ports, sizeof(ports));
|
| 51 |
+
size_t used = 0;
|
| 52 |
+
if (count < 0) {
|
| 53 |
+
return count;
|
| 54 |
+
}
|
| 55 |
+
out[0] = '\0';
|
| 56 |
+
for (int i = 0; i < count; ++i) {
|
| 57 |
+
int written = snprintf(out + used, out_size - used, "%s%u",
|
| 58 |
+
i == 0 ? "" : ".", ports[i]);
|
| 59 |
+
if (written < 0 || (size_t)written >= out_size - used) {
|
| 60 |
+
return LIBUSB_ERROR_OVERFLOW;
|
| 61 |
+
}
|
| 62 |
+
used += (size_t)written;
|
| 63 |
+
}
|
| 64 |
+
return 0;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
static int control_interface(libusb_device *device) {
|
| 68 |
+
struct libusb_config_descriptor *config = NULL;
|
| 69 |
+
int result = libusb_get_active_config_descriptor(device, &config);
|
| 70 |
+
if (result != 0) {
|
| 71 |
+
return result;
|
| 72 |
+
}
|
| 73 |
+
int found = LIBUSB_ERROR_NOT_FOUND;
|
| 74 |
+
for (int i = 0; i < config->bNumInterfaces && found < 0; ++i) {
|
| 75 |
+
for (int j = 0; j < config->interface[i].num_altsetting; ++j) {
|
| 76 |
+
const struct libusb_interface_descriptor *alt =
|
| 77 |
+
&config->interface[i].altsetting[j];
|
| 78 |
+
if (alt->bInterfaceClass == LIBUSB_CLASS_VIDEO &&
|
| 79 |
+
alt->bInterfaceSubClass == 1) {
|
| 80 |
+
found = alt->bInterfaceNumber;
|
| 81 |
+
break;
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
libusb_free_config_descriptor(config);
|
| 86 |
+
return found;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
static int xu_control(libusb_device_handle *handle, int interface_number,
|
| 90 |
+
uint8_t unit, uint8_t selector, int get,
|
| 91 |
+
uint8_t *data, uint16_t size) {
|
| 92 |
+
uint8_t request_type = get ? XU_CTRL_GET : XU_CTRL_SET;
|
| 93 |
+
uint8_t request = get ? XU_GET_CUR : XU_SET_CUR;
|
| 94 |
+
uint16_t value = (uint16_t)selector << 8;
|
| 95 |
+
uint16_t index = ((uint16_t)unit << 8) | (uint16_t)interface_number;
|
| 96 |
+
return libusb_control_transfer(handle, request_type, request, value, index,
|
| 97 |
+
data, size, CTRL_TIMEOUT_MS);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
static int asic_read(libusb_device_handle *handle, int interface_number,
|
| 101 |
+
uint8_t unit, uint16_t address, uint8_t *value) {
|
| 102 |
+
uint8_t data[4] = {
|
| 103 |
+
(uint8_t)(address & 0xff), (uint8_t)(address >> 8), 0, 0xff
|
| 104 |
+
};
|
| 105 |
+
int result = xu_control(handle, interface_number, unit, XU_ASIC_SELECTOR,
|
| 106 |
+
0, data, sizeof(data));
|
| 107 |
+
if (result != (int)sizeof(data)) {
|
| 108 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 109 |
+
}
|
| 110 |
+
data[3] = 0;
|
| 111 |
+
result = xu_control(handle, interface_number, unit, XU_ASIC_SELECTOR,
|
| 112 |
+
1, data, sizeof(data));
|
| 113 |
+
if (result != (int)sizeof(data)) {
|
| 114 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 115 |
+
}
|
| 116 |
+
*value = data[2];
|
| 117 |
+
return 0;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
static int asic_write(libusb_device_handle *handle, int interface_number,
|
| 121 |
+
uint8_t unit, uint16_t address, uint8_t value) {
|
| 122 |
+
uint8_t data[4] = {
|
| 123 |
+
(uint8_t)(address & 0xff), (uint8_t)(address >> 8), value, 0
|
| 124 |
+
};
|
| 125 |
+
int result = xu_control(handle, interface_number, unit, XU_ASIC_SELECTOR,
|
| 126 |
+
0, data, sizeof(data));
|
| 127 |
+
if (result != (int)sizeof(data)) {
|
| 128 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 129 |
+
}
|
| 130 |
+
return 0;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
static int sf_xu6_write_config(libusb_device_handle *handle,
|
| 134 |
+
int interface_number, uint8_t unit,
|
| 135 |
+
uint32_t start_address, uint32_t data_size,
|
| 136 |
+
uint8_t buffer_size) {
|
| 137 |
+
/* This is the packed XU6Data structure used by SetReadInfo_SFXU6(). */
|
| 138 |
+
uint8_t data[64] = {0};
|
| 139 |
+
data[0] = (uint8_t)(start_address & 0xff);
|
| 140 |
+
data[1] = (uint8_t)((start_address >> 8) & 0xff);
|
| 141 |
+
data[2] = (uint8_t)((start_address >> 16) & 0xff);
|
| 142 |
+
data[3] = (uint8_t)((start_address >> 24) & 0xff);
|
| 143 |
+
data[4] = 2u << 6; /* dummy write */
|
| 144 |
+
data[5] = buffer_size;
|
| 145 |
+
uint32_t end_address = start_address + data_size;
|
| 146 |
+
data[6] = (uint8_t)(end_address & 0xff);
|
| 147 |
+
data[7] = (uint8_t)((end_address >> 8) & 0xff);
|
| 148 |
+
data[8] = (uint8_t)((end_address >> 16) & 0xff);
|
| 149 |
+
data[9] = (uint8_t)((end_address >> 24) & 0xff);
|
| 150 |
+
data[10] = 0x0b; /* SDK default read command */
|
| 151 |
+
data[11] = 0x01; /* SF output mode */
|
| 152 |
+
int result = xu_control(handle, interface_number, unit,
|
| 153 |
+
XU_FLASH_XU6_SELECTOR, 0, data, sizeof(data));
|
| 154 |
+
if (result != (int)sizeof(data)) {
|
| 155 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 156 |
+
}
|
| 157 |
+
return 0;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
static int sf_xu6_stop(libusb_device_handle *handle, int interface_number,
|
| 161 |
+
uint8_t unit) {
|
| 162 |
+
int result;
|
| 163 |
+
for (uint16_t i = 0; i < 4; ++i) {
|
| 164 |
+
result = asic_write(handle, interface_number, unit,
|
| 165 |
+
SF_XU64_RW_START_ADDR + i, 0);
|
| 166 |
+
if (result != 0) {
|
| 167 |
+
return result;
|
| 168 |
+
}
|
| 169 |
+
result = asic_write(handle, interface_number, unit,
|
| 170 |
+
SF_XU64_RW_END_ADDR + i, 0);
|
| 171 |
+
if (result != 0) {
|
| 172 |
+
return result;
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
return asic_write(handle, interface_number, unit, SF_XU64_EU_ADDR, 0);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
static int sf_xu6_read_block(libusb_device_handle *handle, int interface_number,
|
| 179 |
+
uint8_t unit, uint32_t address, uint8_t *out,
|
| 180 |
+
uint8_t length) {
|
| 181 |
+
if (length == 0 || length > 64 || address > FLASH_MAX_ADDRESS ||
|
| 182 |
+
address + length < address) {
|
| 183 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 184 |
+
}
|
| 185 |
+
int result = sf_xu6_stop(handle, interface_number, unit);
|
| 186 |
+
if (result != 0) {
|
| 187 |
+
return result;
|
| 188 |
+
}
|
| 189 |
+
result = sf_xu6_write_config(handle, interface_number, unit, address,
|
| 190 |
+
length, length);
|
| 191 |
+
if (result != 0) {
|
| 192 |
+
return result;
|
| 193 |
+
}
|
| 194 |
+
uint8_t data[64] = {0};
|
| 195 |
+
result = xu_control(handle, interface_number, unit,
|
| 196 |
+
XU_FLASH_XU6_SELECTOR, 1, data, sizeof(data));
|
| 197 |
+
if (result != (int)sizeof(data)) {
|
| 198 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 199 |
+
}
|
| 200 |
+
memcpy(out, data, length);
|
| 201 |
+
return 0;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
static int sf_xu6_read(libusb_device_handle *handle, int interface_number,
|
| 205 |
+
uint8_t unit, uint32_t address, uint8_t *out,
|
| 206 |
+
size_t length) {
|
| 207 |
+
while (length > 0) {
|
| 208 |
+
uint8_t chunk = length > 64 ? 64 : (uint8_t)length;
|
| 209 |
+
int result = sf_xu6_read_block(handle, interface_number, unit, address,
|
| 210 |
+
out, chunk);
|
| 211 |
+
if (result != 0) {
|
| 212 |
+
return result;
|
| 213 |
+
}
|
| 214 |
+
address += chunk;
|
| 215 |
+
out += chunk;
|
| 216 |
+
length -= chunk;
|
| 217 |
+
}
|
| 218 |
+
return 0;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
static int sf_xu6_prepare_290(libusb_device_handle *handle,
|
| 222 |
+
int interface_number, uint8_t unit) {
|
| 223 |
+
/* SetSFControllerSCK() for ROM290 in the vendor source. */
|
| 224 |
+
int result = asic_write(handle, interface_number, unit, 0x8045, 10);
|
| 225 |
+
if (result != 0) {
|
| 226 |
+
return result;
|
| 227 |
+
}
|
| 228 |
+
return asic_write(handle, interface_number, unit, 0x8e10, 1);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
static int known_chip(uint8_t chip) {
|
| 232 |
+
switch (chip) {
|
| 233 |
+
case 0x15: case 0x16: case 0x22: case 0x23: case 0x25:
|
| 234 |
+
case 0x32: case 0x33: case 0x56: case 0x70: case 0x71:
|
| 235 |
+
case 0x75: case 0x76: case 0x83: case 0x85: case 0x86:
|
| 236 |
+
case 0x87: case 0x88: case 0x89: case 0x90: case 0x92:
|
| 237 |
+
case 0x98: case 0x99:
|
| 238 |
+
return 1;
|
| 239 |
+
default:
|
| 240 |
+
return 0;
|
| 241 |
+
}
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
static int find_xu_unit(libusb_device_handle *handle, int interface_number,
|
| 245 |
+
uint8_t *unit, uint8_t *chip) {
|
| 246 |
+
/* The vendor source deliberately probes units 3 and 4 in this order. */
|
| 247 |
+
for (uint8_t candidate = 3; candidate <= 4; ++candidate) {
|
| 248 |
+
uint8_t value = 0;
|
| 249 |
+
uint8_t raw[4] = {
|
| 250 |
+
0x1f, 0x10, 0, 0xff
|
| 251 |
+
};
|
| 252 |
+
int set_result = xu_control(handle, interface_number, candidate,
|
| 253 |
+
XU_ASIC_SELECTOR, 0, raw, sizeof(raw));
|
| 254 |
+
int get_result = -1;
|
| 255 |
+
if (set_result == (int)sizeof(raw)) {
|
| 256 |
+
raw[3] = 0;
|
| 257 |
+
get_result = xu_control(handle, interface_number, candidate,
|
| 258 |
+
XU_ASIC_SELECTOR, 1, raw, sizeof(raw));
|
| 259 |
+
if (get_result == (int)sizeof(raw)) {
|
| 260 |
+
value = raw[2];
|
| 261 |
+
}
|
| 262 |
+
}
|
| 263 |
+
fprintf(stderr, " xu_probe unit=%u set=%d get=%d data=",
|
| 264 |
+
candidate, set_result, get_result);
|
| 265 |
+
print_hex(raw, sizeof(raw));
|
| 266 |
+
fputc('\n', stderr);
|
| 267 |
+
if (value == 0xaa && get_result == (int)sizeof(raw)) {
|
| 268 |
+
uint8_t id80f0 = 0, id80f1 = 0, id80f3 = 0;
|
| 269 |
+
int r0 = asic_read(handle, interface_number, candidate, 0x80f0,
|
| 270 |
+
&id80f0);
|
| 271 |
+
int r1 = asic_read(handle, interface_number, candidate, 0x80f1,
|
| 272 |
+
&id80f1);
|
| 273 |
+
int r3 = asic_read(handle, interface_number, candidate, 0x80f3,
|
| 274 |
+
&id80f3);
|
| 275 |
+
fprintf(stderr, " xu_probe fallback 80f0=%02x(%d) 80f1=%02x(%d) "
|
| 276 |
+
"80f3=%02x(%d)\n",
|
| 277 |
+
id80f0, r0, id80f1, r1, id80f3, r3);
|
| 278 |
+
if (r0 == 0 && r1 == 0 && r3 == 0 &&
|
| 279 |
+
(known_chip(id80f0) ||
|
| 280 |
+
(id80f0 == 0x00 && id80f1 == 0x02 && id80f3 == 0x24) ||
|
| 281 |
+
(id80f0 == 0x01 && id80f1 == 0x02 && id80f3 == 0x23) ||
|
| 282 |
+
(id80f0 == 0x04 && id80f1 == 0x02 && id80f3 == 0x24))) {
|
| 283 |
+
value = id80f0;
|
| 284 |
+
}
|
| 285 |
+
}
|
| 286 |
+
if (set_result == (int)sizeof(raw) &&
|
| 287 |
+
get_result == (int)sizeof(raw) &&
|
| 288 |
+
(known_chip(value) || value == 0x00 || value == 0x01 ||
|
| 289 |
+
value == 0x04)) {
|
| 290 |
+
*unit = candidate;
|
| 291 |
+
*chip = value;
|
| 292 |
+
return 0;
|
| 293 |
+
}
|
| 294 |
+
}
|
| 295 |
+
return LIBUSB_ERROR_NOT_FOUND;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
static int flash_read_block(libusb_device_handle *handle, int interface_number,
|
| 299 |
+
uint8_t unit, uint32_t address, uint8_t *out,
|
| 300 |
+
uint8_t length) {
|
| 301 |
+
if (length == 0 || length > 8 || address > FLASH_MAX_ADDRESS) {
|
| 302 |
+
return LIBUSB_ERROR_INVALID_PARAM;
|
| 303 |
+
}
|
| 304 |
+
uint8_t data[11] = {0};
|
| 305 |
+
data[0] = (uint8_t)address;
|
| 306 |
+
data[1] = (uint8_t)(address >> 8);
|
| 307 |
+
uint8_t command = address < 0x10000 ? 0x88 :
|
| 308 |
+
address < 0x20000 ? 0x98 :
|
| 309 |
+
address < 0x30000 ? 0xa8 : 0xb8;
|
| 310 |
+
data[2] = (uint8_t)((command & 0xf0) | length);
|
| 311 |
+
int result = xu_control(handle, interface_number, unit,
|
| 312 |
+
XU_FLASH_SELECTOR, 0, data, sizeof(data));
|
| 313 |
+
if (result != (int)sizeof(data)) {
|
| 314 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 315 |
+
}
|
| 316 |
+
result = xu_control(handle, interface_number, unit, XU_FLASH_SELECTOR, 1,
|
| 317 |
+
data, sizeof(data));
|
| 318 |
+
if (result != (int)sizeof(data)) {
|
| 319 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 320 |
+
}
|
| 321 |
+
memcpy(out, data + 3, length);
|
| 322 |
+
return 0;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
static int flash_read(libusb_device_handle *handle, int interface_number,
|
| 326 |
+
uint8_t unit, uint32_t address, uint8_t *out,
|
| 327 |
+
size_t length) {
|
| 328 |
+
while (length > 0) {
|
| 329 |
+
uint8_t chunk = length > 8 ? 8 : (uint8_t)length;
|
| 330 |
+
int result = flash_read_block(handle, interface_number, unit, address,
|
| 331 |
+
out, chunk);
|
| 332 |
+
if (result != 0) {
|
| 333 |
+
return result;
|
| 334 |
+
}
|
| 335 |
+
address += chunk;
|
| 336 |
+
out += chunk;
|
| 337 |
+
length -= chunk;
|
| 338 |
+
}
|
| 339 |
+
return 0;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
static int rom_read(libusb_device_handle *handle, int interface_number,
|
| 343 |
+
uint8_t unit, uint16_t address, uint8_t *out) {
|
| 344 |
+
uint8_t data[11] = {0};
|
| 345 |
+
data[0] = (uint8_t)address;
|
| 346 |
+
data[1] = (uint8_t)(address >> 8);
|
| 347 |
+
data[2] = 8;
|
| 348 |
+
int result = xu_control(handle, interface_number, unit, 0x04, 0, data,
|
| 349 |
+
sizeof(data));
|
| 350 |
+
if (result != (int)sizeof(data)) {
|
| 351 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 352 |
+
}
|
| 353 |
+
result = xu_control(handle, interface_number, unit, 0x04, 1, data,
|
| 354 |
+
sizeof(data));
|
| 355 |
+
if (result != (int)sizeof(data)) {
|
| 356 |
+
return result < 0 ? result : LIBUSB_ERROR_IO;
|
| 357 |
+
}
|
| 358 |
+
memcpy(out, data + 3, 8);
|
| 359 |
+
return 0;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
static int looks_like_rom(const uint8_t *data) {
|
| 363 |
+
return isdigit(data[0]) && isdigit(data[1]) && isdigit(data[2]) &&
|
| 364 |
+
data[3] == 'R' && data[4] == '0';
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
static void print_setting(const char *label, const uint8_t *data, size_t size) {
|
| 368 |
+
printf(" %s raw=", label);
|
| 369 |
+
print_hex(data, size);
|
| 370 |
+
printf(" ascii=");
|
| 371 |
+
print_ascii(data, size);
|
| 372 |
+
if (data[0] >= 2 && (data[0] & 1) == 0 && data[0] <= size * 2) {
|
| 373 |
+
size_t chars = (data[0] - 2) / 2;
|
| 374 |
+
printf(" %s decoded=\"", label);
|
| 375 |
+
for (size_t i = 0; i < chars; ++i) {
|
| 376 |
+
putchar(isprint(data[2 + i * 2]) ? data[2 + i * 2] : '.');
|
| 377 |
+
}
|
| 378 |
+
puts("\"");
|
| 379 |
+
}
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
typedef int (*flash_reader_fn)(libusb_device_handle *, int, uint8_t,
|
| 383 |
+
uint32_t, uint8_t *, size_t);
|
| 384 |
+
|
| 385 |
+
static void print_double_group(libusb_device_handle *handle, int interface_number,
|
| 386 |
+
uint8_t unit, const char *label,
|
| 387 |
+
uint32_t address, size_t count,
|
| 388 |
+
flash_reader_fn reader) {
|
| 389 |
+
printf(" %s @0x%05x:", label, address);
|
| 390 |
+
for (size_t i = 0; i < count; ++i) {
|
| 391 |
+
uint8_t raw[8] = {0};
|
| 392 |
+
int result = reader(handle, interface_number, unit,
|
| 393 |
+
address + (uint32_t)(i * sizeof(raw)), raw,
|
| 394 |
+
sizeof(raw));
|
| 395 |
+
if (result != 0) {
|
| 396 |
+
printf(" [%zu]=<read:%s>", i, libusb_strerror(result));
|
| 397 |
+
continue;
|
| 398 |
+
}
|
| 399 |
+
if (memcmp(raw, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0) {
|
| 400 |
+
printf(" [%zu]=<unburned>", i);
|
| 401 |
+
continue;
|
| 402 |
+
}
|
| 403 |
+
double value = 0.0;
|
| 404 |
+
memcpy(&value, raw, sizeof(value));
|
| 405 |
+
if (isfinite(value)) {
|
| 406 |
+
printf(" [%zu]=%.12g", i, value);
|
| 407 |
+
} else {
|
| 408 |
+
printf(" [%zu]=<nonfinite>", i);
|
| 409 |
+
}
|
| 410 |
+
}
|
| 411 |
+
putchar('\n');
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
static void print_device_params(libusb_device_handle *handle,
|
| 415 |
+
int interface_number, uint8_t unit,
|
| 416 |
+
flash_reader_fn reader) {
|
| 417 |
+
printf(" device_params @0x45000:");
|
| 418 |
+
for (size_t i = 0; i < 5; ++i) {
|
| 419 |
+
uint8_t raw[8] = {0};
|
| 420 |
+
int result = reader(handle, interface_number, unit,
|
| 421 |
+
0x45000 + (uint32_t)(i * sizeof(raw)), raw,
|
| 422 |
+
sizeof(raw));
|
| 423 |
+
if (result != 0) {
|
| 424 |
+
printf(" [%zu]=<read:%s>", i, libusb_strerror(result));
|
| 425 |
+
continue;
|
| 426 |
+
}
|
| 427 |
+
char text[9];
|
| 428 |
+
memcpy(text, raw, sizeof(raw));
|
| 429 |
+
text[8] = '\0';
|
| 430 |
+
for (size_t j = 0; j < sizeof(raw); ++j) {
|
| 431 |
+
if (text[j] == '\0' || text[j] == (char)0xff) {
|
| 432 |
+
text[j] = '\0';
|
| 433 |
+
break;
|
| 434 |
+
}
|
| 435 |
+
if (!isprint((unsigned char)text[j])) {
|
| 436 |
+
text[j] = '.';
|
| 437 |
+
}
|
| 438 |
+
}
|
| 439 |
+
printf(" [%zu]=%s", i, text[0] == '\0' ? "<empty>" : text);
|
| 440 |
+
}
|
| 441 |
+
putchar('\n');
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
static int probe_device(libusb_device *device, const char *wanted_port) {
|
| 445 |
+
struct libusb_device_descriptor descriptor;
|
| 446 |
+
char port[64];
|
| 447 |
+
if (libusb_get_device_descriptor(device, &descriptor) != 0 ||
|
| 448 |
+
descriptor.idVendor != SONIX_VID || descriptor.idProduct != SONIX_PID ||
|
| 449 |
+
port_path(device, port, sizeof(port)) != 0 ||
|
| 450 |
+
(wanted_port != NULL && strcmp(port, wanted_port) != 0)) {
|
| 451 |
+
return 0;
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
libusb_device_handle *handle = NULL;
|
| 455 |
+
int result = libusb_open(device, &handle);
|
| 456 |
+
if (result != 0) {
|
| 457 |
+
fprintf(stderr, "port=%s open failed: %s\n", port,
|
| 458 |
+
libusb_strerror(result));
|
| 459 |
+
return -1;
|
| 460 |
+
}
|
| 461 |
+
int interface_number = control_interface(device);
|
| 462 |
+
if (interface_number < 0) {
|
| 463 |
+
fprintf(stderr, "port=%s control interface lookup failed: %s\n", port,
|
| 464 |
+
libusb_strerror(interface_number));
|
| 465 |
+
libusb_close(handle);
|
| 466 |
+
return -1;
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
int was_attached = libusb_kernel_driver_active(handle, interface_number);
|
| 470 |
+
if (was_attached == 1) {
|
| 471 |
+
result = libusb_detach_kernel_driver(handle, interface_number);
|
| 472 |
+
if (result != 0) {
|
| 473 |
+
fprintf(stderr, "port=%s detach interface=%d failed: %s\n", port,
|
| 474 |
+
interface_number, libusb_strerror(result));
|
| 475 |
+
libusb_close(handle);
|
| 476 |
+
return -1;
|
| 477 |
+
}
|
| 478 |
+
}
|
| 479 |
+
result = libusb_claim_interface(handle, interface_number);
|
| 480 |
+
if (result != 0) {
|
| 481 |
+
fprintf(stderr, "port=%s claim interface=%d failed: %s\n", port,
|
| 482 |
+
interface_number, libusb_strerror(result));
|
| 483 |
+
if (was_attached == 1) {
|
| 484 |
+
libusb_attach_kernel_driver(handle, interface_number);
|
| 485 |
+
}
|
| 486 |
+
libusb_close(handle);
|
| 487 |
+
return -1;
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
uint8_t unit = 0, chip = 0;
|
| 491 |
+
result = find_xu_unit(handle, interface_number, &unit, &chip);
|
| 492 |
+
printf("port=%s interface=%d xu_unit=%s", port, interface_number,
|
| 493 |
+
result == 0 ? "found" : "not-found");
|
| 494 |
+
if (result == 0) {
|
| 495 |
+
printf("(%u) chip=0x%02x\n", unit, chip);
|
| 496 |
+
uint8_t roms[6][8];
|
| 497 |
+
const uint16_t addresses[6] = {
|
| 498 |
+
0x9ff8, 0xd2b2, 0xdff8, 0xbff8, 0xc7f8, 0xaff8
|
| 499 |
+
};
|
| 500 |
+
int rom_found = 0;
|
| 501 |
+
for (size_t i = 0; i < 6; ++i) {
|
| 502 |
+
memset(roms[i], 0xff, sizeof(roms[i]));
|
| 503 |
+
int read_result = rom_read(handle, interface_number, unit,
|
| 504 |
+
addresses[i], roms[i]);
|
| 505 |
+
if (read_result == 0) {
|
| 506 |
+
printf(" ROM[0x%04x]=", addresses[i]);
|
| 507 |
+
print_hex(roms[i], sizeof(roms[i]));
|
| 508 |
+
printf(" ascii=");
|
| 509 |
+
print_ascii(roms[i], sizeof(roms[i]));
|
| 510 |
+
if (looks_like_rom(roms[i])) {
|
| 511 |
+
rom_found = 1;
|
| 512 |
+
}
|
| 513 |
+
} else {
|
| 514 |
+
printf(" ROM[0x%04x] read failed: %s\n", addresses[i],
|
| 515 |
+
libusb_strerror(read_result));
|
| 516 |
+
}
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
uint8_t raw[64];
|
| 520 |
+
if (flash_read(handle, interface_number, unit, 0, raw, sizeof(raw)) == 0) {
|
| 521 |
+
printf(" FLASH[0x0000..0x003f]=");
|
| 522 |
+
print_hex(raw, sizeof(raw));
|
| 523 |
+
putchar('\n');
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
/* The source SDK's default table fallback is 0x8000. */
|
| 527 |
+
uint32_t parameter_start = 0x8000;
|
| 528 |
+
uint8_t rom[8] = {0};
|
| 529 |
+
for (size_t i = 0; i < 6; ++i) {
|
| 530 |
+
if (looks_like_rom(roms[i])) {
|
| 531 |
+
memcpy(rom, roms[i], sizeof(rom));
|
| 532 |
+
break;
|
| 533 |
+
}
|
| 534 |
+
}
|
| 535 |
+
if (memcmp(rom, "232R0", 4) == 0 && rom[5] == 1) {
|
| 536 |
+
parameter_start = 0xc000;
|
| 537 |
+
} else if (memcmp(rom, "232R0", 4) == 0 && rom[5] == 2) {
|
| 538 |
+
parameter_start = 0xc000;
|
| 539 |
+
} else if (memcmp(rom, "276R0", 4) == 0 && rom[5] == 1) {
|
| 540 |
+
parameter_start = 0xc000;
|
| 541 |
+
} else if (memcmp(rom, "216R0", 4) == 0) {
|
| 542 |
+
parameter_start = 0x5800;
|
| 543 |
+
} else if (rom[0] >= '2' && rom[0] <= '9') {
|
| 544 |
+
/* Newer Sonix layouts store the table address in sectorTable. */
|
| 545 |
+
uint8_t table[0x2b];
|
| 546 |
+
if (flash_read(handle, interface_number, unit, 0x160, table,
|
| 547 |
+
sizeof(table)) == 0 && table[0x0f] != 0xff) {
|
| 548 |
+
parameter_start = ((uint32_t)table[0x0f] << 24) |
|
| 549 |
+
((uint32_t)table[0x10] << 16) |
|
| 550 |
+
((uint32_t)table[0x11] << 8) | table[0x12];
|
| 551 |
+
}
|
| 552 |
+
}
|
| 553 |
+
printf(" parameter_table_start=0x%08x (rom_probe=%s)\n",
|
| 554 |
+
parameter_start, rom_found ? "matched" : "unknown");
|
| 555 |
+
uint8_t setting[64];
|
| 556 |
+
if (flash_read(handle, interface_number, unit, parameter_start + 0xc0,
|
| 557 |
+
setting, sizeof(setting)) == 0) {
|
| 558 |
+
print_setting("serial", setting, sizeof(setting));
|
| 559 |
+
} else {
|
| 560 |
+
printf(" serial read failed at 0x%08x\n",
|
| 561 |
+
parameter_start + 0xc0);
|
| 562 |
+
}
|
| 563 |
+
int xu6_prepare = sf_xu6_prepare_290(handle, interface_number, unit);
|
| 564 |
+
printf(" XU6 calibration channel prepare=%s\n",
|
| 565 |
+
xu6_prepare == 0 ? "ok" : libusb_strerror(xu6_prepare));
|
| 566 |
+
if (xu6_prepare == 0) {
|
| 567 |
+
print_double_group(handle, interface_number, unit, "fx_params",
|
| 568 |
+
0x41000, 5, sf_xu6_read);
|
| 569 |
+
print_double_group(handle, interface_number, unit, "fy_params",
|
| 570 |
+
0x42000, 5, sf_xu6_read);
|
| 571 |
+
print_double_group(handle, interface_number, unit, "fz_params",
|
| 572 |
+
0x43000, 8, sf_xu6_read);
|
| 573 |
+
print_double_group(handle, interface_number, unit, "fw_params",
|
| 574 |
+
0x44000, 9, sf_xu6_read);
|
| 575 |
+
print_device_params(handle, interface_number, unit, sf_xu6_read);
|
| 576 |
+
}
|
| 577 |
+
} else {
|
| 578 |
+
printf("\n");
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
+
libusb_release_interface(handle, interface_number);
|
| 582 |
+
if (was_attached == 1) {
|
| 583 |
+
libusb_attach_kernel_driver(handle, interface_number);
|
| 584 |
+
}
|
| 585 |
+
libusb_close(handle);
|
| 586 |
+
return 1;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
int main(int argc, char **argv) {
|
| 590 |
+
const char *wanted_port = argc > 1 ? argv[1] : NULL;
|
| 591 |
+
libusb_context *context = NULL;
|
| 592 |
+
int result = libusb_init(&context);
|
| 593 |
+
if (result != 0) {
|
| 594 |
+
fprintf(stderr, "libusb_init failed: %s\n", libusb_strerror(result));
|
| 595 |
+
return 1;
|
| 596 |
+
}
|
| 597 |
+
libusb_device **devices = NULL;
|
| 598 |
+
ssize_t count = libusb_get_device_list(context, &devices);
|
| 599 |
+
if (count < 0) {
|
| 600 |
+
fprintf(stderr, "get device list failed: %s\n", libusb_strerror((int)count));
|
| 601 |
+
libusb_exit(context);
|
| 602 |
+
return 1;
|
| 603 |
+
}
|
| 604 |
+
int matched = 0;
|
| 605 |
+
for (ssize_t i = 0; i < count; ++i) {
|
| 606 |
+
int status = probe_device(devices[i], wanted_port);
|
| 607 |
+
if (status > 0) {
|
| 608 |
+
matched++;
|
| 609 |
+
}
|
| 610 |
+
}
|
| 611 |
+
libusb_free_device_list(devices, 1);
|
| 612 |
+
libusb_exit(context);
|
| 613 |
+
if (wanted_port != NULL && matched == 0) {
|
| 614 |
+
fprintf(stderr, "no Sonix 0c45:636f device at port %s\n", wanted_port);
|
| 615 |
+
return 2;
|
| 616 |
+
}
|
| 617 |
+
return matched > 0 ? 0 : 2;
|
| 618 |
+
}
|
collector/core/gripper/devices/gripper_serial.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 5 |
import threading
|
| 6 |
import time
|
| 7 |
from typing import Callable, Optional
|
|
@@ -21,7 +22,9 @@ class GripperSerial:
|
|
| 21 |
CONNECT_BAUD = 115200
|
| 22 |
CONNECT_TIMEOUT = 0.5
|
| 23 |
CONNECT_WRITE_TIMEOUT = 0.5
|
| 24 |
-
|
|
|
|
|
|
|
| 25 |
CONNECT_ATTEMPTS = 8
|
| 26 |
CONNECT_RETRY_DELAY = 0.3
|
| 27 |
COMMAND_TIMEOUT = 1.0
|
|
@@ -65,21 +68,28 @@ class GripperSerial:
|
|
| 65 |
return self._last_error
|
| 66 |
|
| 67 |
def connect(self, port: str, baud: int = CONNECT_BAUD) -> bool:
|
| 68 |
-
"""打开串口
|
| 69 |
with self._io_lock:
|
| 70 |
self._disconnect_locked()
|
| 71 |
self._last_error = None
|
| 72 |
try:
|
|
|
|
|
|
|
| 73 |
opened = self._serial_factory(
|
| 74 |
port,
|
| 75 |
baud,
|
| 76 |
timeout=self.CONNECT_TIMEOUT,
|
| 77 |
write_timeout=self.CONNECT_WRITE_TIMEOUT,
|
|
|
|
| 78 |
)
|
| 79 |
self._serial = opened
|
| 80 |
-
self._sleep(self.CONNECT_BOOT_DELAY)
|
| 81 |
opened.reset_input_buffer()
|
|
|
|
|
|
|
| 82 |
for attempt in range(self.CONNECT_ATTEMPTS):
|
|
|
|
|
|
|
|
|
|
| 83 |
opened.write(b"?\r\n")
|
| 84 |
line = opened.readline().decode(
|
| 85 |
errors="ignore").strip()
|
|
@@ -89,16 +99,18 @@ class GripperSerial:
|
|
| 89 |
)
|
| 90 |
if line.startswith("STATE"):
|
| 91 |
return True
|
| 92 |
-
self.
|
|
|
|
| 93 |
except Exception as exc:
|
| 94 |
self._last_error = (
|
| 95 |
f"serial open/handshake failed: {exc}"
|
| 96 |
)
|
| 97 |
self._disconnect_locked()
|
| 98 |
return False
|
|
|
|
| 99 |
self._last_error = (
|
| 100 |
"serial handshake timeout after "
|
| 101 |
-
f"{
|
| 102 |
)
|
| 103 |
self._disconnect_locked()
|
| 104 |
return False
|
|
@@ -135,6 +147,30 @@ class GripperSerial:
|
|
| 135 |
# 关闭失败时也不能把已经失效的句柄重新暴露给调用方。
|
| 136 |
pass
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def send(self, command: str) -> str:
|
| 139 |
"""发送一条文本命令并返回匹配的完整响应行。"""
|
| 140 |
with self._io_lock:
|
|
@@ -149,16 +185,32 @@ class GripperSerial:
|
|
| 149 |
except Exception:
|
| 150 |
pass
|
| 151 |
opened.write((str(command) + "\r\n").encode())
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
deadline = self._clock() + self.COMMAND_TIMEOUT
|
| 163 |
last_line = ""
|
| 164 |
while self._clock() < deadline:
|
|
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
+
import re
|
| 6 |
import threading
|
| 7 |
import time
|
| 8 |
from typing import Callable, Optional
|
|
|
|
| 22 |
CONNECT_BAUD = 115200
|
| 23 |
CONNECT_TIMEOUT = 0.5
|
| 24 |
CONNECT_WRITE_TIMEOUT = 0.5
|
| 25 |
+
# 握手不再固定白等 2 秒:在最长 CONNECT_BOOT_TIMEOUT 内反复发 ``?``,
|
| 26 |
+
# 收到 STATE 立刻返回。刚上电的 ESP32 仍在窗口内,已就绪的不必空等。
|
| 27 |
+
CONNECT_BOOT_TIMEOUT = 2.0
|
| 28 |
CONNECT_ATTEMPTS = 8
|
| 29 |
CONNECT_RETRY_DELAY = 0.3
|
| 30 |
COMMAND_TIMEOUT = 1.0
|
|
|
|
| 68 |
return self._last_error
|
| 69 |
|
| 70 |
def connect(self, port: str, baud: int = CONNECT_BAUD) -> bool:
|
| 71 |
+
"""独占打开串口,在有限窗口内用 ``?`` 握手确认主控在线。"""
|
| 72 |
with self._io_lock:
|
| 73 |
self._disconnect_locked()
|
| 74 |
self._last_error = None
|
| 75 |
try:
|
| 76 |
+
# 独占模式:串口被其他进程占用时立刻失败,而不是两个进程
|
| 77 |
+
# 各自读走半条响应,把「被占用」伪装成「握手超时」。
|
| 78 |
opened = self._serial_factory(
|
| 79 |
port,
|
| 80 |
baud,
|
| 81 |
timeout=self.CONNECT_TIMEOUT,
|
| 82 |
write_timeout=self.CONNECT_WRITE_TIMEOUT,
|
| 83 |
+
exclusive=True,
|
| 84 |
)
|
| 85 |
self._serial = opened
|
|
|
|
| 86 |
opened.reset_input_buffer()
|
| 87 |
+
deadline = self._clock() + self.CONNECT_BOOT_TIMEOUT
|
| 88 |
+
attempts_made = 0
|
| 89 |
for attempt in range(self.CONNECT_ATTEMPTS):
|
| 90 |
+
if attempt and self._clock() >= deadline:
|
| 91 |
+
break
|
| 92 |
+
attempts_made = attempt + 1
|
| 93 |
opened.write(b"?\r\n")
|
| 94 |
line = opened.readline().decode(
|
| 95 |
errors="ignore").strip()
|
|
|
|
| 99 |
)
|
| 100 |
if line.startswith("STATE"):
|
| 101 |
return True
|
| 102 |
+
if self._clock() < deadline:
|
| 103 |
+
self._sleep(self.CONNECT_RETRY_DELAY)
|
| 104 |
except Exception as exc:
|
| 105 |
self._last_error = (
|
| 106 |
f"serial open/handshake failed: {exc}"
|
| 107 |
)
|
| 108 |
self._disconnect_locked()
|
| 109 |
return False
|
| 110 |
+
# 报实际尝试次数,不报上限:被抢占/占用时上限是误导
|
| 111 |
self._last_error = (
|
| 112 |
"serial handshake timeout after "
|
| 113 |
+
f"{attempts_made} attempts"
|
| 114 |
)
|
| 115 |
self._disconnect_locked()
|
| 116 |
return False
|
|
|
|
| 147 |
# 关闭失败时也不能把已经失效的句柄重新暴露给调用方。
|
| 148 |
pass
|
| 149 |
|
| 150 |
+
def query_fays_serial(self) -> str:
|
| 151 |
+
"""读取 ESP32 NVS 中绑定的 Fays 产品序列号;未绑定/无效返回空串。"""
|
| 152 |
+
response = self.send("QF")
|
| 153 |
+
prefix = "FAYS_SERIAL:"
|
| 154 |
+
if not response.startswith(prefix):
|
| 155 |
+
return ""
|
| 156 |
+
return response[len(prefix):].strip()
|
| 157 |
+
|
| 158 |
+
def set_fays_serial(self, serial: str) -> bool:
|
| 159 |
+
"""写入并持久化 ESP32 绑定的 Fays 产品序列号。"""
|
| 160 |
+
value = str(serial or "").strip()
|
| 161 |
+
if (
|
| 162 |
+
not value
|
| 163 |
+
or len(value) > 64
|
| 164 |
+
or re.fullmatch(r"[A-Za-z0-9._-]+", value) is None
|
| 165 |
+
):
|
| 166 |
+
raise ValueError("Fays 序列号只能是 1-64 位字母、数字、点、下划线或短横线")
|
| 167 |
+
response = self.send(f"WF:{value}")
|
| 168 |
+
if response.startswith("OK FAYS_SERIAL_SET:"):
|
| 169 |
+
return True
|
| 170 |
+
if response.startswith("ERR "):
|
| 171 |
+
raise RuntimeError(f"ESP32 写入 Fays 序列号失败: {response}")
|
| 172 |
+
raise RuntimeError(f"ESP32 写入 Fays 序列号无有效响应: {response or '<empty>'}")
|
| 173 |
+
|
| 174 |
def send(self, command: str) -> str:
|
| 175 |
"""发送一条文本命令并返回匹配的完整响应行。"""
|
| 176 |
with self._io_lock:
|
|
|
|
| 185 |
except Exception:
|
| 186 |
pass
|
| 187 |
opened.write((str(command) + "\r\n").encode())
|
| 188 |
+
command_text = str(command)
|
| 189 |
+
if command_text.startswith("WF:"):
|
| 190 |
+
# 写绑定:三种终态都算「本条命令的响应」,其余行继续等,
|
| 191 |
+
# 免得把固件周期性的 STATE 当成写入结果。
|
| 192 |
+
expected = (
|
| 193 |
+
"OK FAYS_SERIAL_SET:",
|
| 194 |
+
"ERR FAYS_SERIAL_INVALID",
|
| 195 |
+
"ERR FAYS_SERIAL_SAVE",
|
| 196 |
+
)
|
| 197 |
+
else:
|
| 198 |
+
table = {
|
| 199 |
+
"QF": ("FAYS_SERIAL:", "ERR FAYS_SERIAL_NOT_SET"),
|
| 200 |
+
"?": ("STATE",),
|
| 201 |
+
"G": ("AS5048A", "AS5048A ERR"),
|
| 202 |
+
"C": ("OK CONNECT", "ERR"),
|
| 203 |
+
"I": ("OK INIT", "ERR"),
|
| 204 |
+
"F": ("OK READY", "ERR"),
|
| 205 |
+
"E": ("OK ERROR", "ERR"),
|
| 206 |
+
"R": ("OK ASR", "AS5048A ERR"),
|
| 207 |
+
"S": ("OK START", "ERR"),
|
| 208 |
+
}
|
| 209 |
+
# 先按完整命令查(QF 是两字符命令),再退回首字符。
|
| 210 |
+
# 只按 [:1] 查会让 "QF" 这条永远命中不到,等于宣布
|
| 211 |
+
# 「第一行就是响应」——固件周期性的 STATE 会顶替真响应。
|
| 212 |
+
expected = table.get(command_text, table.get(
|
| 213 |
+
command_text[:1], ()))
|
| 214 |
deadline = self._clock() + self.COMMAND_TIMEOUT
|
| 215 |
last_line = ""
|
| 216 |
while self._clock() < deadline:
|
collector/core/gripper/devices/uvc_camera_service.py
CHANGED
|
@@ -25,6 +25,7 @@ import sys
|
|
| 25 |
import cv2
|
| 26 |
|
| 27 |
from core.gripper.fays_runtime import PROJECT_ROOT
|
|
|
|
| 28 |
from core.gripper.runtime.device_access import device_access_guard
|
| 29 |
|
| 30 |
# camera_service/python is a checkout-local transport module, not an ambient
|
|
@@ -68,12 +69,23 @@ _LIBUVC_DIR = Path(
|
|
| 68 |
PROJECT_ROOT, "camera_service", "build", "third_party", "libuvc")
|
| 69 |
|
| 70 |
|
| 71 |
-
def _native_env():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
env = dict(os.environ)
|
| 73 |
-
|
| 74 |
-
if
|
| 75 |
-
|
| 76 |
-
str(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
return env
|
| 78 |
|
| 79 |
|
|
@@ -81,11 +93,87 @@ class UvcCameraServiceError(RuntimeError):
|
|
| 81 |
"""The live libusb/libuvc camera set cannot be used."""
|
| 82 |
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
return ""
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
|
| 91 |
def _path_contains(bus, outer_path, child_path):
|
|
@@ -323,7 +411,17 @@ class UvcCameraServiceManager:
|
|
| 323 |
self._lock = threading.RLock()
|
| 324 |
self._services = {}
|
| 325 |
|
| 326 |
-
def _run_discovery(self, *, usb_bus=None, port_prefix=None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
if not self._discovery_binary.is_file():
|
| 328 |
raise UvcCameraServiceError(
|
| 329 |
f"libusb/libuvc 发现程序不存在: {self._discovery_binary}"
|
|
@@ -332,16 +430,27 @@ class UvcCameraServiceManager:
|
|
| 332 |
raise UvcCameraServiceError(
|
| 333 |
f"libusb/libuvc 发现程序不可执行: {self._discovery_binary}"
|
| 334 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
command = [str(self._discovery_binary), "--json"]
|
| 336 |
if usb_bus is not None and port_prefix is not None:
|
| 337 |
command += ["--bus", str(usb_bus), "--port-prefix", str(port_prefix)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
completed = subprocess.run(
|
| 339 |
command,
|
| 340 |
capture_output=True,
|
| 341 |
text=True,
|
| 342 |
timeout=DISCOVERY_TIMEOUT_S,
|
| 343 |
check=False,
|
| 344 |
-
env=_native_env(),
|
| 345 |
)
|
| 346 |
if completed.returncode != 0:
|
| 347 |
details = "\n".join(
|
|
@@ -368,25 +477,27 @@ class UvcCameraServiceManager:
|
|
| 368 |
def _assignment_identity(assignment):
|
| 369 |
if not isinstance(assignment, dict):
|
| 370 |
raise UvcCameraServiceError(
|
| 371 |
-
"Fays 尚未完成
|
| 372 |
)
|
| 373 |
esp = assignment.get("esp32") or {}
|
| 374 |
fays = assignment.get("fays") or {}
|
| 375 |
esp_serial = str(esp.get("serial") or "").strip()
|
| 376 |
fays_serial = str(fays.get("product_serial") or "").strip()
|
| 377 |
-
|
| 378 |
-
fays.get("physical_usb_path") or ""
|
| 379 |
-
).strip()
|
| 380 |
-
if not esp_serial or not fays_serial or not fays_path:
|
| 381 |
raise UvcCameraServiceError(
|
| 382 |
-
"Fays 当前分配缺少 ESP32 serial
|
| 383 |
)
|
| 384 |
-
return esp_serial, fays_serial
|
| 385 |
|
| 386 |
def _associate_group(self, group, assignment, live_esp):
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
bus = int(group.get("bus", -1))
|
| 391 |
outer_path = str(group.get("outer_path") or "").strip()
|
| 392 |
matching_esp = [
|
|
@@ -401,15 +512,6 @@ class UvcCameraServiceManager:
|
|
| 401 |
f"serial={esp_serial} matches={len(matching_esp)} "
|
| 402 |
f"group={bus}-{outer_path}"
|
| 403 |
)
|
| 404 |
-
# Fays is USB3/FT602 and may be reported on a different libusb bus;
|
| 405 |
-
# the existing topology rule intentionally correlates its root port.
|
| 406 |
-
if _root_port(fays_path) != str(outer_path).split(".", 1)[0].split(
|
| 407 |
-
"-", 1
|
| 408 |
-
)[-1]:
|
| 409 |
-
raise UvcCameraServiceError(
|
| 410 |
-
"当前 UVC 组与已分配 Fays 不在同一根端口: "
|
| 411 |
-
f"fays={fays_path} group={bus}-{outer_path}"
|
| 412 |
-
)
|
| 413 |
normalized = {
|
| 414 |
"bus": bus,
|
| 415 |
"outer_path": outer_path,
|
|
@@ -453,7 +555,7 @@ class UvcCameraServiceManager:
|
|
| 453 |
|
| 454 |
def _discovery_scope(self, assignment, live_esp=None):
|
| 455 |
from core.gripper.fays_runtime import discover_esp32_devices
|
| 456 |
-
serial, _
|
| 457 |
devices = tuple(discover_esp32_devices()) if live_esp is None else live_esp
|
| 458 |
matches = [d for d in devices if str(d.get("serial", "")).strip() == serial]
|
| 459 |
if len(matches) != 1:
|
|
@@ -479,7 +581,7 @@ class UvcCameraServiceManager:
|
|
| 479 |
except UvcCameraServiceError:
|
| 480 |
continue
|
| 481 |
if len(matches) != 1:
|
| 482 |
-
esp_serial, fays_serial
|
| 483 |
raise UvcCameraServiceError(
|
| 484 |
"当前设备清单身份与动态 UVC 拓扑无法唯一配对: "
|
| 485 |
f"esp32={esp_serial} fays={fays_serial} "
|
|
@@ -493,6 +595,10 @@ class UvcCameraServiceManager:
|
|
| 493 |
(
|
| 494 |
int(record["bus"]),
|
| 495 |
record["outer_path"],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
tuple(sorted(
|
| 497 |
(side, str(candidate["port"]))
|
| 498 |
for side, candidate in record["sightac"].items()
|
|
@@ -502,6 +608,44 @@ class UvcCameraServiceManager:
|
|
| 502 |
for record in records
|
| 503 |
))
|
| 504 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
def _write_config(self, records):
|
| 506 |
runtime_dir = Path(tempfile.mkdtemp(prefix="ksq-camera-service-"))
|
| 507 |
lines = [
|
|
@@ -511,12 +655,14 @@ class UvcCameraServiceManager:
|
|
| 511 |
]
|
| 512 |
sockets = {}
|
| 513 |
for index, record in enumerate(records, 1):
|
|
|
|
| 514 |
for role in ("left", "right", "decxin"):
|
| 515 |
candidate = (
|
| 516 |
record["sightac"][role]
|
| 517 |
if role in {"left", "right"}
|
| 518 |
else record["decxin"]
|
| 519 |
)
|
|
|
|
| 520 |
name = f"unit_{index}_{role}"
|
| 521 |
socket_path = runtime_dir / f"{name}.sock"
|
| 522 |
sockets[(index, role)] = str(socket_path)
|
|
@@ -527,9 +673,22 @@ class UvcCameraServiceManager:
|
|
| 527 |
f"usb_bus={int(record['bus'])}",
|
| 528 |
f"usb_port_path={candidate['port']}",
|
| 529 |
f"streaming_interface={int(candidate['streaming_interface'])}",
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 533 |
"width=640" if role in {"left", "right"}
|
| 534 |
else "width=1280",
|
| 535 |
"height=480" if role in {"left", "right"}
|
|
@@ -575,7 +734,7 @@ class UvcCameraServiceManager:
|
|
| 575 |
stdout=subprocess.DEVNULL,
|
| 576 |
stderr=service_log,
|
| 577 |
start_new_session=True,
|
| 578 |
-
env=_native_env(),
|
| 579 |
)
|
| 580 |
finally:
|
| 581 |
service_log.close()
|
|
|
|
| 25 |
import cv2
|
| 26 |
|
| 27 |
from core.gripper.fays_runtime import PROJECT_ROOT
|
| 28 |
+
from core.gripper.paths import CAMERA_MODE_STATE_DIR
|
| 29 |
from core.gripper.runtime.device_access import device_access_guard
|
| 30 |
|
| 31 |
# camera_service/python is a checkout-local transport module, not an ambient
|
|
|
|
| 69 |
PROJECT_ROOT, "camera_service", "build", "third_party", "libuvc")
|
| 70 |
|
| 71 |
|
| 72 |
+
def _native_env(binary=None):
|
| 73 |
+
"""只给这个子进程用交付目录里的 libuvc,绝不动主进程的环境。
|
| 74 |
+
|
| 75 |
+
``binary`` 自带 ``third_party/libuvc`` 时优先用它(KSQ_UVC_*_BINARY
|
| 76 |
+
被指向别处的构建树时仍然自洽),再兜底交付目录内的副本。
|
| 77 |
+
"""
|
| 78 |
env = dict(os.environ)
|
| 79 |
+
directories = []
|
| 80 |
+
if binary is not None:
|
| 81 |
+
directories.append(
|
| 82 |
+
str(Path(binary).resolve().parent / "third_party" / "libuvc"))
|
| 83 |
+
directories.append(str(_LIBUVC_DIR))
|
| 84 |
+
directories.extend(
|
| 85 |
+
part for part in env.get("LD_LIBRARY_PATH", "").split(os.pathsep)
|
| 86 |
+
if part
|
| 87 |
+
)
|
| 88 |
+
env["LD_LIBRARY_PATH"] = os.pathsep.join(dict.fromkeys(directories))
|
| 89 |
return env
|
| 90 |
|
| 91 |
|
|
|
|
| 93 |
"""The live libusb/libuvc camera set cannot be used."""
|
| 94 |
|
| 95 |
|
| 96 |
+
# 每个机型允许的档位对(altsetting, payload),按从好到差排列,必须与服务端
|
| 97 |
+
# camera_service.c 的档位表逐项一致。服务认不出的档位对会直接 rc=2 退出,
|
| 98 |
+
# 把整个夹爪一起带走,所以这里必须先挡一道,把「配置写错」变成一条看得懂的
|
| 99 |
+
# 报错,而不是一个哑掉的服务进程。
|
| 100 |
+
_MODE_LADDERS = {
|
| 101 |
+
(0x0C45, 0x636F): ((3, 800),),
|
| 102 |
+
(0x1BCF, 0x2D4F): ((7, 1280), (6, 944)),
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
# 起始档位一律取档位表里最保守的那一档(=表的最后一档),不认控制器。
|
| 106 |
+
#
|
| 107 |
+
# 曾经这里有一张「已知扛不住的控制器 → 起始档位」的表(0000:74:00.4 → alt6)。
|
| 108 |
+
# 2026-09-15 实测把它推翻了:alt7 停摆不是某块控制器的毛病,0000:0a:00.0 上
|
| 109 |
+
# 整机三路一起出流时一样在 78 秒内停摆(两次独立运行、共 3 次停摆,全落在
|
| 110 |
+
# 68~116 秒)。当初判它「总线 1 没事」是拿一次根本没真的跑在 alt7 上的运行
|
| 111 |
+
# 当基准——那时 ini 里的 forced_* 还是死的,服务只是把 ini 的值原样打进了
|
| 112 |
+
# 日志。控制器不是变量,就没必要按机器写死。
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def _usb_controller_for_bus(bus):
|
| 116 |
+
"""这台相机所在 USB 控制器的 PCI 路径,例如 ``0000:0a:00.0``。
|
| 117 |
+
|
| 118 |
+
``/sys/bus/usb/devices/usbN`` 是指向根 Hub 的符号链接,解开的上一级就是
|
| 119 |
+
控制器;用 PCI 路径而不是总线号,因为总线号会随插口变。
|
| 120 |
+
|
| 121 |
+
它只进落盘键,不参与「选哪一档」的判断——**控制器是不是变量并没有定论**
|
| 122 |
+
(2026-09-15 实测 alt7 在 74:00.4 与 0a:00.0 上都停摆,但机制没查清),所
|
| 123 |
+
以不拿它做任何决策。键里带上它只是取保守:学到的东西严格绑在「学它的那条
|
| 124 |
+
物理路径」上,换机器/换口自然是新键。代价为零——新键没有记忆值,就退回档
|
| 125 |
+
位表里最保守的那一档,那本来就是我们要的默认。取不到返回空串,调用方退化
|
| 126 |
+
成「不区分控制器」,功能不受影响。
|
| 127 |
+
"""
|
| 128 |
+
link = Path(f"/sys/bus/usb/devices/usb{int(bus)}")
|
| 129 |
+
try:
|
| 130 |
+
return link.resolve(strict=True).parent.name
|
| 131 |
+
except (OSError, ValueError):
|
| 132 |
return ""
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _mode_state_key(vid, pid, controller, serial):
|
| 136 |
+
"""「这台相机 × 这条物理路径」的落盘键,规则必须与 C 侧 ``mode_state_key``
|
| 137 |
+
逐字一致:只保留 ``[A-Za-z0-9-]``,其余一律换成 ``_``。"""
|
| 138 |
+
raw = (
|
| 139 |
+
f"{int(vid):04x}_{int(pid):04x}_"
|
| 140 |
+
f"{controller or 'unknown'}_{serial or 'noserial'}"
|
| 141 |
+
)
|
| 142 |
+
return "".join(
|
| 143 |
+
char if (char.isascii() and (char.isalnum() or char == "-")) else "_"
|
| 144 |
+
for char in raw
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def _pair_is_known(vid, pid, pair):
|
| 149 |
+
return pair in _MODE_LADDERS.get((int(vid), int(pid)), ())
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def _read_learned_mode(state_dir, vid, pid, controller, serial):
|
| 153 |
+
"""读服务上一轮学到的档位。文件坏了 / 越界了一律当没学到。"""
|
| 154 |
+
if not state_dir:
|
| 155 |
+
return None
|
| 156 |
+
path = Path(state_dir) / (
|
| 157 |
+
_mode_state_key(vid, pid, controller, serial) + ".mode"
|
| 158 |
+
)
|
| 159 |
+
try:
|
| 160 |
+
text = path.read_text(encoding="utf-8")
|
| 161 |
+
except OSError:
|
| 162 |
+
return None
|
| 163 |
+
values = {}
|
| 164 |
+
for line in text.splitlines():
|
| 165 |
+
line = line.strip()
|
| 166 |
+
if not line or line.startswith("#") or "=" not in line:
|
| 167 |
+
continue
|
| 168 |
+
key, _, value = line.partition("=")
|
| 169 |
+
try:
|
| 170 |
+
values[key.strip()] = int(value.strip())
|
| 171 |
+
except ValueError:
|
| 172 |
+
return None
|
| 173 |
+
pair = (values.get("altsetting"), values.get("payload"))
|
| 174 |
+
if not _pair_is_known(vid, pid, pair):
|
| 175 |
+
return None
|
| 176 |
+
return pair
|
| 177 |
|
| 178 |
|
| 179 |
def _path_contains(bus, outer_path, child_path):
|
|
|
|
| 411 |
self._lock = threading.RLock()
|
| 412 |
self._services = {}
|
| 413 |
|
| 414 |
+
def _run_discovery(self, *, usb_bus=None, port_prefix=None,
|
| 415 |
+
skip_flash=False, identity_only=False,
|
| 416 |
+
topology_only=False):
|
| 417 |
+
"""Run the live libusb/libuvc discovery pass.
|
| 418 |
+
|
| 419 |
+
Three depths, mutually exclusive: ``topology_only`` (enumeration
|
| 420 |
+
only), ``identity_only`` (also opens UVC control, verifies mode and
|
| 421 |
+
reads Flash side/serial, skipping the five calibration tables) and
|
| 422 |
+
the default full read. ``skip_flash`` is the diagnostic alias for
|
| 423 |
+
the first two. Only the full mode can emit a usable config.
|
| 424 |
+
"""
|
| 425 |
if not self._discovery_binary.is_file():
|
| 426 |
raise UvcCameraServiceError(
|
| 427 |
f"libusb/libuvc 发现程序不存在: {self._discovery_binary}"
|
|
|
|
| 430 |
raise UvcCameraServiceError(
|
| 431 |
f"libusb/libuvc 发现程序不可执行: {self._discovery_binary}"
|
| 432 |
)
|
| 433 |
+
if sum(bool(flag) for flag in (
|
| 434 |
+
skip_flash, identity_only, topology_only)) > 1:
|
| 435 |
+
raise UvcCameraServiceError(
|
| 436 |
+
"discover-uvc-config 的扫描深度参数互斥,只能选一个"
|
| 437 |
+
)
|
| 438 |
command = [str(self._discovery_binary), "--json"]
|
| 439 |
if usb_bus is not None and port_prefix is not None:
|
| 440 |
command += ["--bus", str(usb_bus), "--port-prefix", str(port_prefix)]
|
| 441 |
+
if topology_only:
|
| 442 |
+
command.append("--topology-only")
|
| 443 |
+
elif identity_only:
|
| 444 |
+
command.append("--identity-only")
|
| 445 |
+
elif skip_flash:
|
| 446 |
+
command.append("--skip-flash")
|
| 447 |
completed = subprocess.run(
|
| 448 |
command,
|
| 449 |
capture_output=True,
|
| 450 |
text=True,
|
| 451 |
timeout=DISCOVERY_TIMEOUT_S,
|
| 452 |
check=False,
|
| 453 |
+
env=_native_env(self._discovery_binary),
|
| 454 |
)
|
| 455 |
if completed.returncode != 0:
|
| 456 |
details = "\n".join(
|
|
|
|
| 477 |
def _assignment_identity(assignment):
|
| 478 |
if not isinstance(assignment, dict):
|
| 479 |
raise UvcCameraServiceError(
|
| 480 |
+
"Fays 尚未完成身份分配"
|
| 481 |
)
|
| 482 |
esp = assignment.get("esp32") or {}
|
| 483 |
fays = assignment.get("fays") or {}
|
| 484 |
esp_serial = str(esp.get("serial") or "").strip()
|
| 485 |
fays_serial = str(fays.get("product_serial") or "").strip()
|
| 486 |
+
if not esp_serial or not fays_serial:
|
|
|
|
|
|
|
|
|
|
| 487 |
raise UvcCameraServiceError(
|
| 488 |
+
"Fays 当前分配缺少 ESP32 serial 或 Fays serial"
|
| 489 |
)
|
| 490 |
+
return esp_serial, fays_serial
|
| 491 |
|
| 492 |
def _associate_group(self, group, assignment, live_esp):
|
| 493 |
+
"""UVC 组只按 ESP32 serial + bus/outer_path 关联。
|
| 494 |
+
|
| 495 |
+
Fays 归属由 ESP32 NVS 里的绑定序列号决定(见 SingleFaysLease),
|
| 496 |
+
这里不再比较 Fays 的 USB 根端口:Fays 是 USB3/FT602,插在 USB3 口
|
| 497 |
+
时走 5000M 伴生总线,与触觉/RGB 的 480M 总线 bus 号不同;多控制器
|
| 498 |
+
机器上两套 rig 还常落在同号根端口,按端口关联只会互相误配。
|
| 499 |
+
"""
|
| 500 |
+
esp_serial, fays_serial = self._assignment_identity(assignment)
|
| 501 |
bus = int(group.get("bus", -1))
|
| 502 |
outer_path = str(group.get("outer_path") or "").strip()
|
| 503 |
matching_esp = [
|
|
|
|
| 512 |
f"serial={esp_serial} matches={len(matching_esp)} "
|
| 513 |
f"group={bus}-{outer_path}"
|
| 514 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
normalized = {
|
| 516 |
"bus": bus,
|
| 517 |
"outer_path": outer_path,
|
|
|
|
| 555 |
|
| 556 |
def _discovery_scope(self, assignment, live_esp=None):
|
| 557 |
from core.gripper.fays_runtime import discover_esp32_devices
|
| 558 |
+
serial, _ = self._assignment_identity(assignment)
|
| 559 |
devices = tuple(discover_esp32_devices()) if live_esp is None else live_esp
|
| 560 |
matches = [d for d in devices if str(d.get("serial", "")).strip() == serial]
|
| 561 |
if len(matches) != 1:
|
|
|
|
| 581 |
except UvcCameraServiceError:
|
| 582 |
continue
|
| 583 |
if len(matches) != 1:
|
| 584 |
+
esp_serial, fays_serial = self._assignment_identity(assignment)
|
| 585 |
raise UvcCameraServiceError(
|
| 586 |
"当前设备清单身份与动态 UVC 拓扑无法唯一配对: "
|
| 587 |
f"esp32={esp_serial} fays={fays_serial} "
|
|
|
|
| 595 |
(
|
| 596 |
int(record["bus"]),
|
| 597 |
record["outer_path"],
|
| 598 |
+
# ini 里那一行 `usb_controller=` 就是它,而它参与落盘键,所以
|
| 599 |
+
# 「这份配置长什么样」含控制器:换控制器必须重写 ini,不能复用
|
| 600 |
+
# 上一个进程。
|
| 601 |
+
_usb_controller_for_bus(record["bus"]),
|
| 602 |
tuple(sorted(
|
| 603 |
(side, str(candidate["port"]))
|
| 604 |
for side, candidate in record["sightac"].items()
|
|
|
|
| 608 |
for record in records
|
| 609 |
))
|
| 610 |
|
| 611 |
+
@staticmethod
|
| 612 |
+
def _starting_mode(candidate, controller):
|
| 613 |
+
"""这台相机从哪一档开始。
|
| 614 |
+
|
| 615 |
+
优先级:上一轮学到的 > 档位表里最保守的那一档 > 扫描程序报的首选档。
|
| 616 |
+
|
| 617 |
+
为什么默认不是首选档:实测 DECXIN 的 alt7(10.24 MB/s)在整机三路一起
|
| 618 |
+
出流时每 68~116 秒必停摆一次,alt6(7.552 MB/s)同样三路下 30.00 fps
|
| 619 |
+
长跑不停,而两档出帧率一模一样——首选档多出来的那点每帧余量(327680
|
| 620 |
+
对 241664 字节)不值一次停摆,用户要的「稳定帧数」也不允许开机就故意
|
| 621 |
+
赔一次。要那点余量就把 ini 里 forced_altsetting 改回 alt7:真撑不住
|
| 622 |
+
时服务停两次后会自己退回 alt6 并把结论落盘,下次开机直接用(见
|
| 623 |
+
camera_service.c 的 maybe_downgrade_altsetting)。学到的值就在这里读
|
| 624 |
+
回来。"""
|
| 625 |
+
vid = int(candidate["vid"])
|
| 626 |
+
pid = int(candidate["pid"])
|
| 627 |
+
preferred = (int(candidate["altsetting"]),
|
| 628 |
+
int(candidate["descriptor_payload"]))
|
| 629 |
+
if not _pair_is_known(vid, pid, preferred):
|
| 630 |
+
# 服务对认不出的档位对是 rc=2 直接退出,整个夹爪一起挂。先查
|
| 631 |
+
# 一遍扫描程序的输出:扫描程序与服务端档位表脱节(比如只装了新
|
| 632 |
+
# 扫描程序、没重编服务)必须报出来,不能被下面的记忆值盖过去。
|
| 633 |
+
raise UvcCameraServiceError(
|
| 634 |
+
f"扫描程序报出服务端不认识的档位 {vid:04x}:{pid:04x} "
|
| 635 |
+
f"alt={preferred[0]} payload={preferred[1]};"
|
| 636 |
+
"扫描程序与服务端档位表已经脱节,需要一起重编"
|
| 637 |
+
)
|
| 638 |
+
serial = str(candidate.get("usb_serial") or "").strip()
|
| 639 |
+
learned = _read_learned_mode(
|
| 640 |
+
CAMERA_MODE_STATE_DIR, vid, pid, controller, serial
|
| 641 |
+
)
|
| 642 |
+
if learned is not None:
|
| 643 |
+
return learned, "learned"
|
| 644 |
+
ladder = _MODE_LADDERS[(vid, pid)]
|
| 645 |
+
if ladder[-1] != preferred:
|
| 646 |
+
return ladder[-1], "conservative"
|
| 647 |
+
return preferred, "preferred"
|
| 648 |
+
|
| 649 |
def _write_config(self, records):
|
| 650 |
runtime_dir = Path(tempfile.mkdtemp(prefix="ksq-camera-service-"))
|
| 651 |
lines = [
|
|
|
|
| 655 |
]
|
| 656 |
sockets = {}
|
| 657 |
for index, record in enumerate(records, 1):
|
| 658 |
+
controller = _usb_controller_for_bus(record["bus"])
|
| 659 |
for role in ("left", "right", "decxin"):
|
| 660 |
candidate = (
|
| 661 |
record["sightac"][role]
|
| 662 |
if role in {"left", "right"}
|
| 663 |
else record["decxin"]
|
| 664 |
)
|
| 665 |
+
mode, mode_source = self._starting_mode(candidate, controller)
|
| 666 |
name = f"unit_{index}_{role}"
|
| 667 |
socket_path = runtime_dir / f"{name}.sock"
|
| 668 |
sockets[(index, role)] = str(socket_path)
|
|
|
|
| 673 |
f"usb_bus={int(record['bus'])}",
|
| 674 |
f"usb_port_path={candidate['port']}",
|
| 675 |
f"streaming_interface={int(candidate['streaming_interface'])}",
|
| 676 |
+
# altsetting 和 payload 是一个整体(alt7 的端点就是
|
| 677 |
+
# 1280 B/包),必须成对来自同一处:要么都是扫描程序实测的
|
| 678 |
+
# 端点描述符,要么都是服务上一轮学到的结论。拆开配会得到
|
| 679 |
+
# 一个自相矛盾的档位——2026-09-15 主程序连不上夹爪就是这
|
| 680 |
+
# 么来的(alt 取自扫描程序、payload 却是硬编码)。
|
| 681 |
+
#
|
| 682 |
+
# 这两项现在是**生效的**:服务打开设备后会把它们交给
|
| 683 |
+
# libuvc 的 uvc_set_altsetting_override()。在此之前它们
|
| 684 |
+
# 只是校验和日志,真正决定档位的是 libuvc 的编译期表,
|
| 685 |
+
# 所以「改 ini」曾经完全不改变行为。
|
| 686 |
+
f"forced_altsetting={mode[0]}",
|
| 687 |
+
f"forced_payload={mode[1]}",
|
| 688 |
+
f"# 起始档位来源: {mode_source}",
|
| 689 |
+
f"usb_serial={str(candidate.get('usb_serial') or '').strip()}",
|
| 690 |
+
f"usb_controller={controller}",
|
| 691 |
+
f"state_dir={CAMERA_MODE_STATE_DIR}",
|
| 692 |
"width=640" if role in {"left", "right"}
|
| 693 |
else "width=1280",
|
| 694 |
"height=480" if role in {"left", "right"}
|
|
|
|
| 734 |
stdout=subprocess.DEVNULL,
|
| 735 |
stderr=service_log,
|
| 736 |
start_new_session=True,
|
| 737 |
+
env=_native_env(self._service_binary),
|
| 738 |
)
|
| 739 |
finally:
|
| 740 |
service_log.close()
|
collector/core/gripper/fays_serial_probe.py
CHANGED
|
@@ -87,9 +87,14 @@ def probe_product_serial(
|
|
| 87 |
probe_binary: Optional[str] = None,
|
| 88 |
timeout: float = 60.0,
|
| 89 |
environment: Optional[Mapping[str, str]] = None,
|
|
|
|
| 90 |
) -> str:
|
| 91 |
"""Run the official calibration probe and return ``device.serial``.
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
timeout 同时是 SDK 全局初始化锁的等待上限与子进程运行上限:双夹爪
|
| 94 |
并发打开时,第二台的探针要等第一台 SLAM 初始化(最长 45s)释放锁,
|
| 95 |
20s 会在等待期间误报超时,故放宽到 60s。
|
|
@@ -113,15 +118,31 @@ def probe_product_serial(
|
|
| 113 |
# and the device_setup manifest below.
|
| 114 |
with device_access_guard(FAYS_SDK_INITIALIZATION_LOCK, timeout=timeout), \
|
| 115 |
fays_device_guard(ports["stereo_dev_port"]):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
completed = subprocess.run(
|
| 117 |
-
|
| 118 |
timeout=timeout,
|
| 119 |
env=dict(os.environ if environment is None else environment),
|
| 120 |
check=False,
|
| 121 |
)
|
| 122 |
except subprocess.TimeoutExpired as exc:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
raise RuntimeError(
|
| 124 |
-
f"Fays 标定探测超时: {ports}
|
|
|
|
| 125 |
) from exc
|
| 126 |
output = (
|
| 127 |
(completed.stdout or "") + "\n" + (completed.stderr or "")
|
|
|
|
| 87 |
probe_binary: Optional[str] = None,
|
| 88 |
timeout: float = 60.0,
|
| 89 |
environment: Optional[Mapping[str, str]] = None,
|
| 90 |
+
serial_only_fast: bool = False,
|
| 91 |
) -> str:
|
| 92 |
"""Run the official calibration probe and return ``device.serial``.
|
| 93 |
|
| 94 |
+
``serial_only_fast`` 只允许用于身份扫描:探针读一次 SDK 设备信息、停掉
|
| 95 |
+
自己持有的 IMU 流后直接退出进程,跳过厂商 SDK 较慢的 handle 析构。标定
|
| 96 |
+
与运行路径必须走完整生命周期,不得使用该参数。
|
| 97 |
+
|
| 98 |
timeout 同时是 SDK 全局初始化锁的等待上限与子进程运行上限:双夹爪
|
| 99 |
并发打开时,第二台的探针要等第一台 SLAM 初始化(最长 45s)释放锁,
|
| 100 |
20s 会在等待期间误报超时,故放宽到 60s。
|
|
|
|
| 118 |
# and the device_setup manifest below.
|
| 119 |
with device_access_guard(FAYS_SDK_INITIALIZATION_LOCK, timeout=timeout), \
|
| 120 |
fays_device_guard(ports["stereo_dev_port"]):
|
| 121 |
+
command = [executable]
|
| 122 |
+
if serial_only_fast:
|
| 123 |
+
command.append("--serial-only-fast")
|
| 124 |
+
command.append(config_path)
|
| 125 |
completed = subprocess.run(
|
| 126 |
+
command, capture_output=True, text=True,
|
| 127 |
timeout=timeout,
|
| 128 |
env=dict(os.environ if environment is None else environment),
|
| 129 |
check=False,
|
| 130 |
)
|
| 131 |
except subprocess.TimeoutExpired as exc:
|
| 132 |
+
# TimeoutExpired 在 text=True 下也可能带着 bytes。超时发生在
|
| 133 |
+
# SDK 释放阶段时,前半段(身份/序列号)往往已经成功,把子进程
|
| 134 |
+
# 最后 20 行留下来,别让一句「超时」把底层证据丢掉。
|
| 135 |
+
def _text(value):
|
| 136 |
+
return (
|
| 137 |
+
value.decode("utf-8", errors="replace")
|
| 138 |
+
if isinstance(value, bytes) else (value or "")
|
| 139 |
+
)
|
| 140 |
+
diagnostic = "\n".join(
|
| 141 |
+
(_text(exc.stdout) + "\n" + _text(exc.stderr)).splitlines()[-20:]
|
| 142 |
+
)
|
| 143 |
raise RuntimeError(
|
| 144 |
+
f"Fays 标定探测超时(含 SDK 资源释放): {ports} "
|
| 145 |
+
f"({exc.timeout}s)\n{diagnostic}"
|
| 146 |
) from exc
|
| 147 |
output = (
|
| 148 |
(completed.stdout or "") + "\n" + (completed.stderr or "")
|
collector/core/gripper/fays_single.py
CHANGED
|
@@ -1,16 +1,21 @@
|
|
| 1 |
"""单夹爪场景的 Fays S80M 租约(无 device_setup 清单版本)。
|
| 2 |
|
| 3 |
主程序一次只服务一套夹爪 rig,设备身份全部来自运行时探测:
|
| 4 |
-
1. ESP32
|
| 5 |
-
2.
|
| 6 |
-
3.
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
dist/fays_opencv48/s80m_{serial}_stereo_inertial.yaml;缺任一个就**现场
|
| 10 |
用厂商导出程序从这只夹爪读出厂标定补齐**(见 calibration.py),不必再
|
| 11 |
人工跑上位机 device_setup + 导入;
|
| 12 |
-
|
| 13 |
-
|
| 14 |
|
| 15 |
返回的 selected 字典携带 SlamProcessController 与相机服务组合所需字段
|
| 16 |
(ports/runtime_config/ipc_dir/trajectory/settings/executable/...)。
|
|
@@ -29,6 +34,7 @@ import time
|
|
| 29 |
|
| 30 |
from config import settings
|
| 31 |
from core.gripper import calibration, paths
|
|
|
|
| 32 |
from core.gripper.fays_runtime import (
|
| 33 |
FAYS_MIN_USB_SPEED_MBPS,
|
| 34 |
build_fays_probe_env,
|
|
@@ -37,8 +43,10 @@ from core.gripper.fays_runtime import (
|
|
| 37 |
discover_fays_device_groups,
|
| 38 |
materialize_fays_device_config,
|
| 39 |
validate_fays_sdk_access,
|
|
|
|
| 40 |
)
|
| 41 |
from core.gripper.fays_serial_probe import probe_product_serial
|
|
|
|
| 42 |
|
| 43 |
_INSTANCE_IPC_FILES = (
|
| 44 |
"orb_pose.json.tmp", "orb_pose.json", "orb_meta.json",
|
|
@@ -138,11 +146,15 @@ class SingleFaysLease:
|
|
| 138 |
)
|
| 139 |
|
| 140 |
def _match_esp(self, esp_serial):
|
| 141 |
-
"""按 USB serial 唯一匹配 ESP32 控制板,返回其
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
devices = discover_esp32_devices()
|
| 143 |
matches = [
|
| 144 |
info for info in devices
|
| 145 |
-
if str(info.get("serial") or "").strip() ==
|
| 146 |
]
|
| 147 |
if len(matches) != 1:
|
| 148 |
available = ", ".join(
|
|
@@ -150,68 +162,154 @@ class SingleFaysLease:
|
|
| 150 |
)
|
| 151 |
raise GripperFaysError(
|
| 152 |
"ESP32 控制板未唯一匹配: 期望 serial={} 当前=[{}]".format(
|
| 153 |
-
|
| 154 |
)
|
| 155 |
return matches[0]
|
| 156 |
|
| 157 |
-
def
|
| 158 |
-
"""
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
物理口的双总线伴生),因此原程序的拓扑规则只按根端口关联
|
| 163 |
-
(见 uvc_camera_service._associate_group 的同名注释)。
|
| 164 |
-
同一台机器可能还插着主程序自己的独立 S80M(不同根端口),
|
| 165 |
-
必须按夹爪 rig 的拓扑范围区分,不能全机枚举;多台命中即报错
|
| 166 |
-
不猜归属。rig 内仍只允许 1 台完整 S80M。
|
| 167 |
-
多控制器机器(双夹爪)还必须带控制器身份比较:两套 rig 可能
|
| 168 |
-
都插在各自控制器的同号根端口上(实测都是根端口 2,控制器
|
| 169 |
-
0000:0a:00.0 与 0000:74:00.4),只比端口号会互相误配。
|
| 170 |
"""
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
if not
|
| 174 |
raise GripperFaysError(
|
| 175 |
-
"ESP32
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
group for group in groups
|
| 180 |
-
if self._bus_root(group["physical_usb_path"])
|
| 181 |
-
== (esp_controller, esp_root)
|
| 182 |
-
]
|
| 183 |
-
if len(same_rig) != 1:
|
| 184 |
-
found = ", ".join(
|
| 185 |
-
str(g["physical_usb_path"]) for g in same_rig)
|
| 186 |
raise GripperFaysError(
|
| 187 |
-
"
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
|
| 192 |
@staticmethod
|
| 193 |
-
def
|
| 194 |
-
"""
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
机器上不同控制器的同号根端口必须区分;USB2/USB3 伴生双总线
|
| 199 |
-
(同一物理口 ESP=7-2、Fays=8-2)仍只按控制器+根端口关联,
|
| 200 |
-
不比较总线号,伴生容忍不变。
|
| 201 |
"""
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
return "", ""
|
| 206 |
try:
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
except
|
| 210 |
-
return
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
|
| 213 |
-
|
| 214 |
-
|
|
|
|
| 215 |
try:
|
| 216 |
speed = validate_fays_sdk_access(group)
|
| 217 |
except Exception as exc:
|
|
@@ -234,6 +332,13 @@ class SingleFaysLease:
|
|
| 234 |
raise GripperFaysError(
|
| 235 |
f"Fays SDK 产品序列号探测失败: {exc}"
|
| 236 |
) from exc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
return serial, speed
|
| 238 |
|
| 239 |
def _ensure_calibration(self, serial, group):
|
|
@@ -273,8 +378,9 @@ class SingleFaysLease:
|
|
| 273 |
"""
|
| 274 |
with self._lock:
|
| 275 |
esp = self._match_esp(esp_serial)
|
| 276 |
-
group = self.
|
| 277 |
-
serial, _speed = self._probe_serial(
|
|
|
|
| 278 |
self._logger(
|
| 279 |
f"[Gripper-Fays] 正在重新读取 {serial} 的厂商出厂标定…"
|
| 280 |
)
|
|
@@ -293,8 +399,8 @@ class SingleFaysLease:
|
|
| 293 |
return dict(self._selected)
|
| 294 |
self.release()
|
| 295 |
esp = self._match_esp(esp_serial)
|
| 296 |
-
group = self.
|
| 297 |
-
serial, speed = self._probe_serial(group)
|
| 298 |
sdk_yaml, orb_yaml = self._ensure_calibration(serial, group)
|
| 299 |
|
| 300 |
os.makedirs(paths.FAYS_LOCK_DIR, mode=0o700, exist_ok=True)
|
|
@@ -385,23 +491,6 @@ class SingleFaysLease:
|
|
| 385 |
stream.close()
|
| 386 |
raise
|
| 387 |
|
| 388 |
-
def resolve_assignment(self, esp_serial):
|
| 389 |
-
"""相机服务所需的身份 assignment(不探测、不锁租约、不校验速度)。
|
| 390 |
-
|
| 391 |
-
UvcCameraServiceManager 只把 fays identity 用作同根端口关联与
|
| 392 |
-
错误文案,从不打开 Fays 相机;此处用实时拓扑推导的物理路径即可,
|
| 393 |
-
S80M 的官方 SDK 串行探测留给 acquire()(P3 全链,含 USB3 校验)。
|
| 394 |
-
"""
|
| 395 |
-
esp = self._match_esp(esp_serial)
|
| 396 |
-
group = self._discover_unique_fays_group(esp)
|
| 397 |
-
return {
|
| 398 |
-
"esp32": {"serial": str(esp["serial"])},
|
| 399 |
-
"fays": {
|
| 400 |
-
"product_serial": "runtime-unprobed",
|
| 401 |
-
"physical_usb_path": group["physical_usb_path"],
|
| 402 |
-
},
|
| 403 |
-
}
|
| 404 |
-
|
| 405 |
def snapshot(self):
|
| 406 |
with self._lock:
|
| 407 |
return (
|
|
|
|
| 1 |
"""单夹爪场景的 Fays S80M 租约(无 device_setup 清单版本)。
|
| 2 |
|
| 3 |
主程序一次只服务一套夹爪 rig,设备身份全部来自运行时探测:
|
| 4 |
+
1. ESP32 按 USB serial 唯一匹配(不看 /dev/ttyACMN 编号);
|
| 5 |
+
2. 从该 ESP32 的 NVS 读它绑定的 Fays 产品序列号(串口命令 QF);
|
| 6 |
+
3. 枚举在线完整 S80M,先逐台做 USB SuperSpeed 预检,再用官方 SDK 的
|
| 7 |
+
serial-only-fast 读出真实产品序列号(FT602 描述符和枚举顺序都不算
|
| 8 |
+
身份);
|
| 9 |
+
4. 绑定的序列号必须唯一命中一台在线 Fays —— **不按根端口猜归属**;
|
| 10 |
+
被其他会话占用的 Fays 不参与匹配,但要写进错误文案;
|
| 11 |
+
5. 对选中的那台再走一次完整 SDK 生命周期,复核序列号与 ESP32 绑定值
|
| 12 |
+
一致(Connect 路径的完整校验);
|
| 13 |
+
6. 按序列号定位 fays_config/fays_vikit_{serial}.yaml 与
|
| 14 |
dist/fays_opencv48/s80m_{serial}_stereo_inertial.yaml;缺任一个就**现场
|
| 15 |
用厂商导出程序从这只夹爪读出厂标定补齐**(见 calibration.py),不必再
|
| 16 |
人工跑上位机 device_setup + 导入;
|
| 17 |
+
7. flock 租约 /tmp/ksq-gripper-fays-locks/{serial}.lock 防多开;
|
| 18 |
+
8. materialize 当前端口的运行时 SDK yaml,并在 /dev/shm 建 IPC 目录。
|
| 19 |
|
| 20 |
返回的 selected 字典携带 SlamProcessController 与相机服务组合所需字段
|
| 21 |
(ports/runtime_config/ipc_dir/trajectory/settings/executable/...)。
|
|
|
|
| 34 |
|
| 35 |
from config import settings
|
| 36 |
from core.gripper import calibration, paths
|
| 37 |
+
from core.gripper.devices.gripper_serial import GripperSerial
|
| 38 |
from core.gripper.fays_runtime import (
|
| 39 |
FAYS_MIN_USB_SPEED_MBPS,
|
| 40 |
build_fays_probe_env,
|
|
|
|
| 43 |
discover_fays_device_groups,
|
| 44 |
materialize_fays_device_config,
|
| 45 |
validate_fays_sdk_access,
|
| 46 |
+
validate_fays_superspeed,
|
| 47 |
)
|
| 48 |
from core.gripper.fays_serial_probe import probe_product_serial
|
| 49 |
+
from core.gripper.runtime.device_access import fays_device_guard
|
| 50 |
|
| 51 |
_INSTANCE_IPC_FILES = (
|
| 52 |
"orb_pose.json.tmp", "orb_pose.json", "orb_meta.json",
|
|
|
|
| 146 |
)
|
| 147 |
|
| 148 |
def _match_esp(self, esp_serial):
|
| 149 |
+
"""按 USB serial 唯一匹配 ESP32 控制板,返回其设备信息。"""
|
| 150 |
+
wanted = str(esp_serial or "").strip()
|
| 151 |
+
if not wanted:
|
| 152 |
+
raise GripperFaysError(
|
| 153 |
+
"未提供 ESP32 USB serial,拒绝按端口号或枚举顺序猜测控制板")
|
| 154 |
devices = discover_esp32_devices()
|
| 155 |
matches = [
|
| 156 |
info for info in devices
|
| 157 |
+
if str(info.get("serial") or "").strip() == wanted
|
| 158 |
]
|
| 159 |
if len(matches) != 1:
|
| 160 |
available = ", ".join(
|
|
|
|
| 162 |
)
|
| 163 |
raise GripperFaysError(
|
| 164 |
"ESP32 控制板未唯一匹配: 期望 serial={} 当前=[{}]".format(
|
| 165 |
+
wanted, available)
|
| 166 |
)
|
| 167 |
return matches[0]
|
| 168 |
|
| 169 |
+
def _query_esp_bound_fays_serial(self, esp):
|
| 170 |
+
"""读 ESP32 NVS 里绑定的 Fays 产品序列号(串口命令 ``QF``)。
|
| 171 |
+
|
| 172 |
+
串口是独占资源:查询完立刻归还,随后运行时的桥接还要独占打开。
|
| 173 |
+
未绑定、空值、ERR 一律失败关闭 —— 不允许回退到拓扑猜测。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
"""
|
| 175 |
+
device = str(esp.get("device") or "").strip()
|
| 176 |
+
esp_serial = str(esp.get("serial") or "<unknown>").strip()
|
| 177 |
+
if not device:
|
| 178 |
raise GripperFaysError(
|
| 179 |
+
"ESP32 {} 缺少串口节点,无法查询绑定的 Fays 序列号".format(
|
| 180 |
+
esp_serial))
|
| 181 |
+
bridge = GripperSerial(logger=self._logger)
|
| 182 |
+
if not bridge.connect(device):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
raise GripperFaysError(
|
| 184 |
+
"ESP32 {} 串口连接失败,无法查询绑定的 Fays 序列号: {}".format(
|
| 185 |
+
esp_serial, bridge.last_error or "unknown error"))
|
| 186 |
+
try:
|
| 187 |
+
response = ""
|
| 188 |
+
for attempt in range(3):
|
| 189 |
+
response = bridge.send("QF")
|
| 190 |
+
if response.startswith(("FAYS_SERIAL:", "ERR FAYS_SERIAL")):
|
| 191 |
+
break
|
| 192 |
+
if response.startswith("STATE") and attempt < 2:
|
| 193 |
+
# 固件周期性状态行插在命令响应前面:重试,别把串口
|
| 194 |
+
# 收发竞态误判成「没有绑定」。
|
| 195 |
+
time.sleep(0.05)
|
| 196 |
+
continue
|
| 197 |
+
break
|
| 198 |
+
finally:
|
| 199 |
+
bridge.disconnect()
|
| 200 |
+
prefix = "FAYS_SERIAL:"
|
| 201 |
+
if response.startswith(prefix):
|
| 202 |
+
serial = response[len(prefix):].strip()
|
| 203 |
+
if serial:
|
| 204 |
+
return serial
|
| 205 |
+
if response.startswith("ERR FAYS_SERIAL_NOT_SET"):
|
| 206 |
+
raise GripperFaysError(
|
| 207 |
+
"ESP32 {} 尚未写入 Fays 序列号,拒绝按端口猜测配对;"
|
| 208 |
+
"请先使用 WF:<serial> 写入后再扫描".format(esp_serial))
|
| 209 |
+
raise GripperFaysError(
|
| 210 |
+
"ESP32 {} 查询 Fays 序列号失败: {}".format(
|
| 211 |
+
esp_serial, response or "<empty>"))
|
| 212 |
|
| 213 |
@staticmethod
|
| 214 |
+
def _device_in_use(group):
|
| 215 |
+
"""这台 Fays 是否已被运行中的会话独占(另一套 rig 的 SLAM)。
|
| 216 |
+
|
| 217 |
+
双夹爪时另一套 rig 的 SLAM 会把本设备锁一直持有到会话结束,
|
| 218 |
+
它按定义不可能是本次要打开的夹爪,但探测不了就得如实记下来。
|
|
|
|
|
|
|
|
|
|
| 219 |
"""
|
| 220 |
+
port = str((group.get("ports") or {}).get("stereo_dev_port") or "")
|
| 221 |
+
if re.fullmatch(r"/dev/video[0-9]+", port) is None:
|
| 222 |
+
return False # 节点无效留给后面的 SDK 预检去报错
|
|
|
|
| 223 |
try:
|
| 224 |
+
with fays_device_guard(port, timeout=0.0):
|
| 225 |
+
return False
|
| 226 |
+
except RuntimeError:
|
| 227 |
+
return True
|
| 228 |
+
|
| 229 |
+
def _probe_fays_groups_by_serial(self, groups):
|
| 230 |
+
"""SuperSpeed 预检 + ``serial-only-fast`` 读出每台 Fays 的产品序列号。
|
| 231 |
+
|
| 232 |
+
预检在启动 SDK **之前**逐台做(只读 sysfs):降级到 USB2 的设备
|
| 233 |
+
根本不该进 SDK。返回 ``(by_serial, skipped)``:skipped 是被其他
|
| 234 |
+
会话占用的设备,它们不参与匹配,但要写进错误文案。
|
| 235 |
+
"""
|
| 236 |
+
for group in groups:
|
| 237 |
+
try:
|
| 238 |
+
validate_fays_superspeed(group)
|
| 239 |
+
except Exception as exc:
|
| 240 |
+
raise GripperFaysError(
|
| 241 |
+
"Fays USB 链路预检失败,拒绝启动 SDK: "
|
| 242 |
+
"physical={} error={}".format(
|
| 243 |
+
group.get("physical_usb_path"), exc)) from exc
|
| 244 |
+
by_serial = {}
|
| 245 |
+
skipped = []
|
| 246 |
+
for group in groups:
|
| 247 |
+
if self._device_in_use(group):
|
| 248 |
+
skipped.append(group)
|
| 249 |
+
continue
|
| 250 |
+
ports = dict(group.get("ports") or {})
|
| 251 |
+
if not ports:
|
| 252 |
+
raise GripperFaysError(
|
| 253 |
+
"Fays 设备组缺少 SDK 端口: {!r}".format(group))
|
| 254 |
+
try:
|
| 255 |
+
serial = str(probe_product_serial(
|
| 256 |
+
ports,
|
| 257 |
+
environment=build_fays_probe_env(),
|
| 258 |
+
serial_only_fast=True,
|
| 259 |
+
) or "").strip()
|
| 260 |
+
except Exception as exc:
|
| 261 |
+
raise GripperFaysError(
|
| 262 |
+
"Fays SDK 产品序列号探测失败,拒绝回退到拓扑猜测: "
|
| 263 |
+
"physical={} error={}".format(
|
| 264 |
+
group.get("physical_usb_path"), exc)) from exc
|
| 265 |
+
if not serial:
|
| 266 |
+
raise GripperFaysError(
|
| 267 |
+
"Fays SDK 返回空产品序列号,拒绝继续: physical={}".format(
|
| 268 |
+
group.get("physical_usb_path")))
|
| 269 |
+
if serial in by_serial:
|
| 270 |
+
raise GripperFaysError(
|
| 271 |
+
"Fays SDK 返回重复产品序列号,拒绝猜测归属: "
|
| 272 |
+
"serial={}".format(serial))
|
| 273 |
+
by_serial[serial] = group
|
| 274 |
+
return by_serial, skipped
|
| 275 |
+
|
| 276 |
+
def _resolve_fays_group(self, esp):
|
| 277 |
+
"""唯一 ESP32 → ``QF`` 绑定序列号 → 按 SDK serial 唯一匹配 Fays。
|
| 278 |
+
|
| 279 |
+
返回 ``(group, bound_serial)``。**不再按 USB 根端口关联**:Fays 是
|
| 280 |
+
USB3/FT602,插在 USB3 口时走 5000M 伴生总线,bus 号与 ESP/触觉
|
| 281 |
+
相机的 480M 总线不同;多控制器机器上两套 rig 又经常落在同号根
|
| 282 |
+
端口,按端口关联只会互相误配。
|
| 283 |
+
"""
|
| 284 |
+
groups = tuple(discover_fays_device_groups())
|
| 285 |
+
if not groups:
|
| 286 |
+
raise GripperFaysError(
|
| 287 |
+
"未发现完整的 Fays S80M(stereo + IMU 两个节点必须同时在线)")
|
| 288 |
+
bound = self._query_esp_bound_fays_serial(esp)
|
| 289 |
+
by_serial, skipped = self._probe_fays_groups_by_serial(groups)
|
| 290 |
+
group = by_serial.get(bound)
|
| 291 |
+
if group is None:
|
| 292 |
+
online = ", ".join(sorted(by_serial)) or "<无>"
|
| 293 |
+
detail = ""
|
| 294 |
+
if skipped:
|
| 295 |
+
detail = ";另有正在被占用的 Fays 未能探测: [{}]".format(
|
| 296 |
+
", ".join(
|
| 297 |
+
"{}({})".format(
|
| 298 |
+
item.get("physical_usb_path"),
|
| 299 |
+
(item.get("ports") or {}).get("stereo_dev_port"))
|
| 300 |
+
for item in skipped))
|
| 301 |
+
raise GripperFaysError(
|
| 302 |
+
"ESP32 {} 绑定的 Fays 序列号 {} 不在当前在线的 Fays 中,"
|
| 303 |
+
"拒绝按端口或枚举顺序猜测配对:在线 serial=[{}]{}".format(
|
| 304 |
+
esp.get("serial"), bound, online, detail))
|
| 305 |
+
return group, bound
|
| 306 |
+
|
| 307 |
+
def _probe_serial(self, group, expected_serial=None):
|
| 308 |
+
"""官方 SDK 完整生命周期读序列号;USB 速度不足时给出换口提示。
|
| 309 |
|
| 310 |
+
``expected_serial`` 是 ESP32 NVS 里的绑定值:Connect 路径必须复核
|
| 311 |
+
两者一致,不允许一台「碰巧在线」的 Fays 顶替绑定设备。
|
| 312 |
+
"""
|
| 313 |
try:
|
| 314 |
speed = validate_fays_sdk_access(group)
|
| 315 |
except Exception as exc:
|
|
|
|
| 332 |
raise GripperFaysError(
|
| 333 |
f"Fays SDK 产品序列号探测失败: {exc}"
|
| 334 |
) from exc
|
| 335 |
+
if expected_serial is not None and \
|
| 336 |
+
str(serial).strip() != str(expected_serial).strip():
|
| 337 |
+
raise GripperFaysError(
|
| 338 |
+
"Fays SDK 序列号与 ESP32 绑定值不一致,拒绝连接: "
|
| 339 |
+
"physical={} bound={} actual={}".format(
|
| 340 |
+
group.get("physical_usb_path"),
|
| 341 |
+
expected_serial, serial))
|
| 342 |
return serial, speed
|
| 343 |
|
| 344 |
def _ensure_calibration(self, serial, group):
|
|
|
|
| 378 |
"""
|
| 379 |
with self._lock:
|
| 380 |
esp = self._match_esp(esp_serial)
|
| 381 |
+
group, bound = self._resolve_fays_group(esp)
|
| 382 |
+
serial, _speed = self._probe_serial(
|
| 383 |
+
group, expected_serial=bound)
|
| 384 |
self._logger(
|
| 385 |
f"[Gripper-Fays] 正在重新读取 {serial} 的厂商出厂标定…"
|
| 386 |
)
|
|
|
|
| 399 |
return dict(self._selected)
|
| 400 |
self.release()
|
| 401 |
esp = self._match_esp(esp_serial)
|
| 402 |
+
group, bound = self._resolve_fays_group(esp)
|
| 403 |
+
serial, speed = self._probe_serial(group, expected_serial=bound)
|
| 404 |
sdk_yaml, orb_yaml = self._ensure_calibration(serial, group)
|
| 405 |
|
| 406 |
os.makedirs(paths.FAYS_LOCK_DIR, mode=0o700, exist_ok=True)
|
|
|
|
| 491 |
stream.close()
|
| 492 |
raise
|
| 493 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
def snapshot(self):
|
| 495 |
with self._lock:
|
| 496 |
return (
|
collector/core/gripper/paths.py
CHANGED
|
@@ -48,6 +48,12 @@ CAMERA_SERVICE_BINARY = os.path.join(
|
|
| 48 |
DISCOVER_UVC_CONFIG_BINARY = os.path.join(
|
| 49 |
CAMERA_SERVICE_DIR, "build", "discover-uvc-config",
|
| 50 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
SIGHTAC_SDK_ROOT = os.path.abspath(os.environ.get(
|
| 52 |
"KSQ_SIGHTAC_ROOT",
|
| 53 |
os.path.join(PACKAGE_ROOT, "sightac_sdk"),
|
|
|
|
| 48 |
DISCOVER_UVC_CONFIG_BINARY = os.path.join(
|
| 49 |
CAMERA_SERVICE_DIR, "build", "discover-uvc-config",
|
| 50 |
)
|
| 51 |
+
# 相机服务学到的等时档位就存在这里:按「机型 × 控制器 PCI 路径 × 相机序列号」
|
| 52 |
+
# 一个键一个文件。放在 GRIPPER_DIR 下而不是服务的 runtime_dir 里,因为后者
|
| 53 |
+
# 每次租约释放都会被整个删掉(见 uvc_camera_service._release_lease),存那儿
|
| 54 |
+
# 等于没存。C 侧按 state_dir 这个 ini 键收到它,见 camera_service.c 的
|
| 55 |
+
# persist_mode_choice。
|
| 56 |
+
CAMERA_MODE_STATE_DIR = os.path.join(GRIPPER_DIR, "camera_modes")
|
| 57 |
SIGHTAC_SDK_ROOT = os.path.abspath(os.environ.get(
|
| 58 |
"KSQ_SIGHTAC_ROOT",
|
| 59 |
os.path.join(PACKAGE_ROOT, "sightac_sdk"),
|