-
Notifications
You must be signed in to change notification settings - Fork 7.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
usb: device_next: new USB Video Class (UVC) implementation #76798
base: main
Are you sure you want to change the base?
Conversation
I am grateful for the work on the USB and Video stacks of Zephyr, as well as the entire Zephyr tree, on the shoulder of which this is built. |
Force push:
|
Force-push:
|
.dwMinBitRate = sys_cpu_to_le32(15360000), \ | ||
.dwMaxBitRate = sys_cpu_to_le32(15360000), \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are these values coming from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are chosen arbitrarily, and I need to think about what should I do here: pick reasonable defaults? Deduce from other values? Let the user figure out by exposing a devicetree option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In future work, it will be possible to ask the sensor for VIDEO_CID_PIXEL_RATE
in combination with video_bits_per_pixel()
to generate this field.
.bLength = sizeof(struct usb_ep_descriptor), \ | ||
.bDescriptorType = USB_DESC_ENDPOINT, \ | ||
.bEndpointAddress = 0x81, \ | ||
.bmAttributes = USB_EP_TYPE_BULK, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe UVC can be BULK or ISO.... but I only see BULK supported here. Do you plan to have a way to select which type of EP?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot to mention that only BULK is supported through this early version. I will need to spend more time investigating the device_next
APIs for how ISO is implemented.
I cannot guarantee ETAs as I would do this feature on free time, but will add it to the roadmap: this will be needed at some point.
Very grateful for your reviews @XenuIsWatching I will force-push with the changes as soon as I get a chance to do so. |
Force-push:
|
182ac7c
to
cec26cc
Compare
02c1087
to
4bdc1ea
Compare
force push: rebased on |
force push: changed the way descriptors are declared and introduce video controls at the descriptor level (no support for controls commands yet) |
force push: implemented the UVC controls for the supported Video class controls. I did not test this yet with the samples, but on the internal fork, we could get an IMX219 sensor with exposure and gain control from the host, format selection at startup (but not runtime, see [1]). I believe that the last step for me is to test the sample on several boards that support |
Known limitations:[1]: there is currently no [2]: [3]: [4]: [5]: There are missing implementations for selector unit, extensions unit, encoding unit. TODO. [6]: UVC introduces dependencies between some controls, i.e. auto-expopsure needs to be off for exposure to be accepted. TODO. [7]: [8]: [9]: [10]: Supports custom header size, but not passing custom header data yet. [11]: Still image capture (capturing one frame at full resolution) not supported. [12]: USB3CV compliance tests not all passing since last rebase. TODO. [13]: Announcing different resolutions/FPS for different connection speed not supported. [14]: Asynchronous controls (the host setting a control, and a notification interrupt alert of completion) not supported. Supported features:[A]: Class API and enumeration [B]: [C]: [D]: [E]: Handling of control commands end-to-end from host to Zephyr video device [F]: Zephyr native Video API that allows to enqueue/dequeue frames like any other Zephyr video device. [G]: Supports fragmented frames as discussed in #66994 and #72827 [H]: [I]: Supports querying the min/max/current values of every controls end-to-end from host to Zephyr video driver. |
a7cd6cb
to
ea60ec0
Compare
c049412
to
8f2155e
Compare
Force-push:
On Windows, multiple streams only give 1 camera device (unlike Linux) and for instance the default camera app does not support switching between the streams: Using "composite devices" with 2 UVC instances on devicetree allows to show multiple camera devices, much better supported in applications: This also permits to simplify the implementation everywhere. |
451bde6
to
b50ad20
Compare
force-push:
[1]: For getting an actual stream out on Wndows, there need any of these, to support the YUYV pixel format:
|
NET_BUF_POOL_VAR_DEFINE(uvc_buf_pool, DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 16, | ||
512 * DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 16, | ||
sizeof(struct uvc_buf_info), NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UDC_BUF_POOL_VAR_DEFINE(uvc_buf_pool, DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 16,
512 * DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 16,
sizeof(struct uvc_buf_info), NULL);
I was confused, but while working on #86920 I noticed that actually ud_size parameter can be passed to the macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a drop-in replacement... 😯 Zero excuse for using NET_BUF_POOL_VAR_DEFINE()
instead of UDC_BUF_POOL_VAR_DEFINE()
!
.size = 4, | ||
.bit = 3, | ||
.selector = UVC_CT_EXPOSURE_TIME_ABS_CONTROL, | ||
.op = UVC_OP_VC_INT, | ||
.param = VIDEO_CID_EXPOSURE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
speaking of windows in your last comment... doesn't Windows also like to "not follow" the specification where it doesn't use 100us units for the exposure time absolute like Linux and Mac OS do, and sends it over in log2 value.
I know that in the past when I had to implement a 6 class USB composite device superspeed plus camera interface many years ago, and IIRC, Windows was kind of a nightmare to deal with 😠 (and I also ran in to the same issue you're having with V4L2 being 'better' and had to put a YUVU format desc in there for 'windows' peeps), and did a not fun workaround to get users happy and put an extension unit in there as a quick bandaid to make the 100us still work. While I don't think having an extenstion unit here just for special Windows users is the right solution.... I'm not quite sure what good solution here would be... but maybe it's something to think about...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exposure being much more sensitive in the low values, I suppose they wanted to improve user experience of all the naive implementations exposing a linear exposure slider... Preventing to write correct software at the same time.
Currently, UVC does not really have a mapping between the controls and their integration. This is an arbitrary value passed to the sensor register directly, happily overflowing the max.
When #82158 is introduced, this would be the opportunitty to focus on how the UVC controls and Zephyr Video controls are mapped to each other, allowing some customization here.
Much grateful for this insight! This is the opportunity to put a long-term fix/workaround for these issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some proposal that could also help with such quirk and others:
e7b5d11
to
aa8b3f1
Compare
samples/subsys/usb/uvc/Kconfig
Outdated
@@ -0,0 +1,9 @@ | |||
# Copyright (c) 2023 Nordic Semiconductor ASA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change it to # Copyright The Zephyr Project Contributors
samples/subsys/usb/uvc/README.rst
Outdated
Requirements | ||
************ | ||
|
||
The requirements for this sample is an USB driver using the latest | ||
``device_next`` API. | ||
|
||
If a ``zephyr,camera`` node is chosen for the board, it will be used as source. | ||
If not, this sample can be built using this command to use an emulated video source: | ||
|
||
.. code-block:: console | ||
|
||
west build -b <board> -- -DEXTRA_DTC_OVERLAY_FILE=video-emul.overlay | ||
|
||
The USB descriptors are generated from the video API, and the user does not need | ||
to configure them. | ||
|
||
Building and Running | ||
******************** | ||
|
||
Build the sample application and flash the resulting binaries. | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/subsys/usb/uvc | ||
:board: nrf52840dongle | ||
:goals: build flash | ||
:compact: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/subsys/usb/uvc | ||
:board: rpi_pico | ||
:goals: build flash | ||
:compact: | ||
|
||
Upon reboot, the device is expected to be detected as a webcam device: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be outdated and a bit unclear. What about
diff --git a/samples/subsys/usb/uvc/README.rst b/samples/subsys/usb/uvc/README.rst
index 6539692f137..e500375abbd 100644
--- a/samples/subsys/usb/uvc/README.rst
+++ b/samples/subsys/usb/uvc/README.rst
@@ -19,37 +19,34 @@ video streams.
Requirements
************
-The requirements for this sample is an USB driver using the latest
-``device_next`` API.
-
-If a ``zephyr,camera`` node is chosen for the board, it will be used as source.
-If not, this sample can be built using this command to use an emulated video source:
-
-.. code-block:: console
-
- west build -b <board> -- -DEXTRA_DTC_OVERLAY_FILE=video-emul.overlay
-
-The USB descriptors are generated from the video API, and the user does not need
-to configure them.
+This sample uses the new USB device stack and requires the USB device
+controller ported to the :ref:`udc_api`.
Building and Running
********************
-Build the sample application and flash the resulting binaries.
+If a board is equipped with a supported video sensor, and ``zephyr,camera``
+node is chosen for the board, it will be used as the video source. Currently
+only Arduino Nicla Vision is supported. The sample can be built as follows:
.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/uvc
- :board: nrf52840dongle
+ :board: arduino_nicla_vision/stm32h747xx/m7
:goals: build flash
:compact:
+If the video sensor is not present, the emulated video source can be used
+instead, and the sample can be built for any board with a supported USB device
+controller and sufficient RAM as follows:
+
.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/uvc
:board: rpi_pico
:goals: build flash
+ :gen-args: -DEXTRA_DTC_OVERLAY_FILE=video-emul.overlay
:compact:
-Upon reboot, the device is expected to be detected as a webcam device:
+The device is expected to be detected as a webcam device:
.. tabs::
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed video-emul.overlay
altogether to make it like the other video samples, which use VIDEO_SW_GENERATOR
as a fallback.
Thank you for the clarifications!
Currently only Arduino Nicla Vision is supported
I just checked, and it enumerates, but fail to start. I'll solder UART pins and debug... and try on a couple more boards. This was tried most extensively on 3rd-party boards not yet on Zephyr.
How is it usually done?
Should there be a list of boards that were tested end-to-end on the README? If so no problem, I'll try to add PRs every time some extra device is known to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought you used Vision board for testing/development and want to include it in the sample. To make it easy and get this PR in ASAP, I suggest to make the sample very simple, it is UVC class implementation sample, there is no requirement to support real hardware, using emulated video data source is the preferred way since it just works. Ideally, the emulated video source should have build/runtime behavior close to the real hardware.
samples/subsys/usb/uvc/src/main.c
Outdated
LOG_INF("Dequeued %p from %s, enqueueing to %s", | ||
vbuf, video_dev->name, uvc_dev->name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change it to
LOG_INF("Dequeued %p from %s, enqueueing to %s", | |
vbuf, video_dev->name, uvc_dev->name); | |
LOG_DBG("Dequeued %p from %s, enqueueing to %s", | |
(void *)vbuf, video_dev->name, uvc_dev->name); |
samples/subsys/usb/uvc/src/main.c
Outdated
LOG_INF("Dequeued %p from %s, enqueueing to %s", | ||
vbuf, uvc_dev->name, video_dev->name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change it to
LOG_INF("Dequeued %p from %s, enqueueing to %s", | |
vbuf, uvc_dev->name, video_dev->name); | |
LOG_DBG("Dequeued %p from %s, enqueueing to %s", | |
(void *)vbuf, uvc_dev->name, video_dev->name); |
chosen { | ||
zephyr,camera = &rx0; | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then VIDEO_SW_GENERATOR generator cannot be enabled because
zephyr/drivers/video/Kconfig.sw_generator
Lines 6 to 10 in 5ae9f44
DT_CHOSEN_ZEPHYR_CAMERA := zephyr,camera | |
config VIDEO_SW_GENERATOR | |
bool "Video Software Generator" | |
depends on !$(dt_chosen_enabled,$(DT_CHOSEN_ZEPHYR_CAMERA)) |
The concept behind VIDEO_SW_GENERATOR/imager/video-frame-rx-core is a bit confusing, I would expect there just VIDEO_SW_GENERATOR configured using devicetree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VIDEO_SW_GENERATOR
is the main test pattern generator, but has no devicetree bindings, a Kconfig-enabled struct device
.
The emulated imager/video-frame-rx is a stub implementation for CI purpose, and was abused for UVC before uvc_set_video_dev(uvc0_dev, video0_dev)
support, allowing to use VIDEO_SW_GENERATOR
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open to suggestions on how to handle "hardware-less" devices for USB UVC or the Video area!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@josuah Can you explain more why uvc requires video_sw_generator as a device-tree based driver to be used by uvc ?
At first look at uvc_set_video_dev(uvc0_dev, video0_dev)
, maybe with the new video control framwork implementation, it can help as the video device is defined at compile time, so it can be retrieved easily at any moment ?
DEVICE_DEFINE(video_sw_generator, "VIDEO_SW_GENERATOR", &video_sw_generator_init, NULL,
&video_sw_generator_data_0, NULL, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY,
&video_sw_generator_driver_api);
VIDEO_DEVICE_DEFINE(video_sw_generator, DEVICE_GET(video_sw_generator), NULL);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, just ignore my unrelated comment ... I understand the issue now: we need to expose the video_sw_generator some how as the device-tree based hardware so that it can be "chosen" as "zephyr,camera"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why uvc requires video_sw_generator as a device-tree based driver
It was in the past, but not anymore following @jfischer-no guidance.
maybe with the new video control framwork implementation, it can help as the video device is defined at compile time
This can avoid usability bugs such as "forgot to call uvc_set_video_dev()
before enabling USB".
I did not know about DEVICE_GET(video_sw_generator)
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why uvc requires video_sw_generator as a device-tree based driver
It was in the past, but not anymore following @jfischer-no guidance.
I am not sure we are talking about the same thing.
If video_sw_generator is a virtual video source device, using DT to configure and instantiate that device is the only correct way to make it behave like a real hardware video source. How the UVC implementation instantiates the class instances and binds them to the video source is another matter entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With video_sw_generator as the emulated video source, but configured the same way as a real video device, the sample code would be as follows:
/ {
chosen {
zephyr,camera = &sw_gen_foo1;
};
sw_gen_foo1: sw_gen_foo1 {
compatible = "zephyr,video-sw-generator";
port {
...
};
};
};
&uvc {
port {
....
};
};
video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
if (!device_is_ready(video_dev)) {
LOG_ERR("video source %s is not ready or failed to initialize",
video_dev == NULL ? "(nil)" : video_dev->name);
return -ENODEV;
}
/* Must be done before initializing USB */
uvc_set_video_dev(uvc_dev, video_dev);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open to suggestions on how to handle "hardware-less" devices for USB UVC or the Video area!
As in #82158, the video subsystem now keeps a list of registered video devices which can be exposed to user (by index), similar to the way Linux expose /dev/video0, /dev/video1, ... sysfs.
So, I think we don't need to predefine a single "zephyr,camera" chosen node anymore. Applications can work with the video device (HW or SW) of their choice and can change as they want:
But then VIDEO_SW_GENERATOR generator cannot be enabled because
And then,
zephyr/drivers/video/Kconfig.sw_generator
Lines 6 to 10 in 5ae9f44
DT_CHOSEN_ZEPHYR_CAMERA := zephyr,camera | |
config VIDEO_SW_GENERATOR | |
bool "Video Software Generator" | |
depends on !$(dt_chosen_enabled,$(DT_CHOSEN_ZEPHYR_CAMERA)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The virtual video source selection got split out into this PR to facilitate review on this current PR in hope to make this current PR more manageable.
Tested on FRDM-MCXN947 (64x64 test pattern):
Tested on Arducam Nicla Vision (320x240 image from the GC2145):
The colors are wrong with this board, probably because of an H-Flip: the LED in the middle is red. |
fddc96f
to
e844110
Compare
Force-push:
|
Introduce a new USB Video Class (UVC) implementation from scratch. It exposes a native Zephyr Video driver interface, allowing to call the video_enqueue()/video_dequeue() interface. It will query the attached video device to learn about the pipeline capabilities, and use this to configure the USB descriptors. At runtime, this UVC implementation will send this device all the control requests, which it can then dispatch to the rest of the pipeline. The application can poll the format currently selected by the host, but will not be alerted when the host configures a new format, as there is no video.h API for it yet. Signed-off-by: Josuah Demangeon <me@josuah.net>
The Arduino Nicla Vision board supports the new device_next USB stack and can be used for testing USB Device features such as UVC. Signed-off-by: Josuah Demangeon <me@josuah.net>
fb8e6e8
to
1c6117c
Compare
Force-push:
For now:
In hope this makes review easier! |
Following the addition of USB Video Class, this adds a sample that makes use of the &zephyr,camera chosen node of any board to stream the video source to the host. A fallback video-emul.overlay is provided for test and debugging purpose for devices without a camera. Signed-off-by: Josuah Demangeon <me@josuah.net>
How to test:
From any branch you wish to test, get the UVC and sample commits:
The method to test with any
device_next
board:The method to test with any device with a
dcmi
node:It will use a very small test pattern as video source, so no camera is required.
To connect a camera instead of a test pattern, connect a different video source through the devicetree.
The USB descriptors will be updated by querying the driver.
Dependencies:
drivers: video: introduce "GET" sub-operations #78603replaced by something else to comedrivers: video: allow allocation with a header preceding the buffer #79168not required anymoreThis is a preview of an USB Video Class implementation for Zephyr that was only tested on custom devices insofar.
Proper Zephyr samples will be provided on upcoming commits. The API is simply to submit frames to the UVC device like to any Zephyr video device.
There is an unsolved challenge around the Video API: there is no
set_format
because the Zephyr application cannot decide what the host uses, onlyget_format
for what the host does support. But there is a missing video API to allow the driver to warn the application about a forced format change requested by the host. I thought of maybe reusingset_signal()
to also warn about format changes and not just buffer events.I will now work on building examples for existing Zephyr devices, as this was built for a custom USB3 board.
Here is the devicetree configuration used insofar:
The sizes and FPS are to be selected by the developer. The USB descriptors get configured as described above, and the host will request a particular format. Once that is done, the USB Video Class driver can let the application know which of these was selected through the video.hget_format()
API.This is still a draft PR, but I am grateful for comments and suggestions. I am willing to do the work of refactoring this as much as needed.
[EDIT: see this comment for latest description]