Skip to content

Commit 1df3f73

Browse files
author
Alain Volmat
committed
Merge branch 'pr_uvc_no_policy' into HEAD
2 parents 5c21020 + f5328dd commit 1df3f73

File tree

5 files changed

+234
-138
lines changed

5 files changed

+234
-138
lines changed

doc/releases/migration-guide-4.3.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ Stepper
4747

4848
* :dtcompatible:`zephyr,gpio-stepper` has been replaced by :dtcompatible:`zephyr,h-bridge-stepper`.
4949

50+
USB
51+
===
52+
53+
* The USB Video Class was configuring the framerate and format of the source video device.
54+
This is now to be done by the application after the host selected the format (:github:`93192`).
55+
5056
.. zephyr-keep-sorted-stop
5157
5258
Bluetooth

doc/releases/release-notes-4.3.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ New APIs and options
123123

124124
* :kconfig:option:`CONFIG_SETTINGS_TFM_ITS`
125125

126+
* USB
127+
128+
* Video
129+
130+
* :c:func:`uvc_add_format`
131+
126132
.. zephyr-keep-sorted-stop
127133
128134
New Boards

include/zephyr/usb/class/usbd_uvc.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,33 @@
2626
*/
2727

2828
/**
29-
* @brief Set the video device that a UVC instance will use.
29+
* @brief Set the video device that a UVC instance will use for control requests.
3030
*
31-
* It will query its supported controls, formats and frame rates, and use this information to
32-
* generate USB descriptors sent to the host.
33-
*
34-
* At runtime, it will forward all USB controls from the host to this device.
31+
* It will query its supported video controls and frame intervals and use this information to
32+
* generate the USB descriptors presented to the host. At runtime, it will forward all USB controls
33+
* from the host to this device.
3534
*
3635
* @note This function must be called before @ref usbd_enable.
3736
*
3837
* @param uvc_dev The UVC device
39-
* @param video_dev The video device that this UVC instance controls
38+
* @param video_dev The video device that this UVC instance send controls requests to
4039
*/
4140
void uvc_set_video_dev(const struct device *uvc_dev, const struct device *video_dev);
4241

42+
/**
43+
* @brief Set the video format capabilities that a UVC instance will present to the host.
44+
*
45+
* This information will be used to generate USB descriptors.
46+
* The particular format selected by the host can be queried with @ref video_get_format.
47+
*
48+
* @note This function must be called before @ref usbd_enable and before @ref uvc_set_video_dev.
49+
*
50+
* @param uvc_dev The UVC device to configure
51+
* @param fmt The video format to add to this UVC instance
52+
* @return 0 on success, negative value on error
53+
*/
54+
int uvc_add_format(const struct device *const uvc_dev, const struct video_format *const fmt);
55+
4356
/**
4457
* @}
4558
*/

samples/subsys/usb/uvc/src/main.c

Lines changed: 111 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,95 @@
1717

1818
LOG_MODULE_REGISTER(uvc_sample, LOG_LEVEL_INF);
1919

20-
const struct device *const uvc_dev = DEVICE_DT_GET(DT_NODELABEL(uvc));
21-
const struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
20+
const static struct device *const uvc_dev = DEVICE_DT_GET(DT_NODELABEL(uvc));
21+
const static struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
22+
23+
/* Format capabilities of video_dev, usd everywhere through the sampel */
24+
static struct video_caps video_caps = {.type = VIDEO_BUF_TYPE_OUTPUT};
25+
26+
static size_t app_get_min_buf_size(const struct video_format *const fmt)
27+
{
28+
if (video_caps.min_line_count == LINE_COUNT_HEIGHT) {
29+
return fmt->pitch * fmt->height;
30+
} else {
31+
return fmt->pitch * video_caps.min_line_count;
32+
}
33+
}
34+
35+
static bool app_is_standard_format(uint32_t pixfmt)
36+
{
37+
return pixfmt == VIDEO_PIX_FMT_GREY || pixfmt == VIDEO_PIX_FMT_JPEG ||
38+
pixfmt == VIDEO_PIX_FMT_YUYV;
39+
}
40+
41+
/* Check whether the video device supports one of the wisespread image sensor formats */
42+
static bool app_has_standard_formats(void)
43+
{
44+
for (int i = 0;; i++) {
45+
uint32_t pixfmt = video_caps.format_caps[i].pixelformat;
46+
47+
if (pixfmt == 0) {
48+
return false;
49+
}
50+
if (app_is_standard_format(pixfmt)) {
51+
return true;
52+
}
53+
}
54+
}
55+
56+
static void app_add_format(uint32_t pixfmt, uint16_t width, uint16_t height, bool has_std_fmts)
57+
{
58+
struct video_format fmt = {
59+
.pixelformat = pixfmt,
60+
.width = width,
61+
.height = height,
62+
.type = VIDEO_BUF_TYPE_OUTPUT,
63+
};
64+
int ret;
65+
66+
/* If the system has any standard pixel format, only propose them to the host */
67+
if (has_std_fmts && !app_is_standard_format(pixfmt)) {
68+
return;
69+
}
70+
71+
/* Set the format to get the pitch */
72+
ret = video_set_format(video_dev, &fmt);
73+
if (ret != 0) {
74+
LOG_ERR("Could not set the format of %s", video_dev->name);
75+
return;
76+
}
77+
78+
if (app_get_min_buf_size(&fmt) > CONFIG_VIDEO_BUFFER_POOL_SZ_MAX) {
79+
LOG_WRN("Skipping format %ux%u", fmt.width, fmt.height);
80+
return;
81+
}
82+
83+
uvc_add_format(uvc_dev, &fmt);
84+
}
85+
86+
/* Submit to UVC only the formats expected to be working (enough memory for the size, etc.) */
87+
static void app_add_filtered_formats(void)
88+
{
89+
const bool has_std_fmts = app_has_standard_formats();
90+
91+
for (int i = 0; video_caps.format_caps[i].pixelformat != 0; i++) {
92+
const struct video_format_cap *vcap = &video_caps.format_caps[i];
93+
94+
app_add_format(vcap->pixelformat, vcap->width_min, vcap->height_min, has_std_fmts);
95+
96+
if (vcap->width_min != vcap->width_max || vcap->height_min != vcap->height_max) {
97+
app_add_format(vcap->pixelformat, vcap->width_max, vcap->height_max,
98+
has_std_fmts);
99+
}
100+
}
101+
}
22102

23103
int main(void)
24104
{
25105
struct usbd_context *sample_usbd;
26106
struct video_buffer *vbuf;
27107
struct video_format fmt = {0};
28-
struct video_caps caps;
108+
struct video_frmival frmival = {0};
29109
struct k_poll_signal sig;
30110
struct k_poll_event evt[1];
31111
k_timeout_t timeout = K_FOREVER;
@@ -37,16 +117,18 @@ int main(void)
37117
return -ENODEV;
38118
}
39119

40-
caps.type = VIDEO_BUF_TYPE_OUTPUT;
41-
42-
if (video_get_caps(video_dev, &caps)) {
120+
ret = video_get_caps(video_dev, &video_caps);
121+
if (ret != 0) {
43122
LOG_ERR("Unable to retrieve video capabilities");
44123
return 0;
45124
}
46125

47-
/* Must be done before initializing USB */
126+
/* Must be called before usb_enable() */
48127
uvc_set_video_dev(uvc_dev, video_dev);
49128

129+
/* Must be called before uvc_set_video_dev() */
130+
app_add_filtered_formats();
131+
50132
sample_usbd = sample_usbd_init_device(NULL);
51133
if (sample_usbd == NULL) {
52134
return -ENODEV;
@@ -59,7 +141,6 @@ int main(void)
59141

60142
LOG_INF("Waiting the host to select the video format");
61143

62-
/* Get the video format once it is selected by the host */
63144
while (true) {
64145
fmt.type = VIDEO_BUF_TYPE_INPUT;
65146

@@ -75,17 +156,32 @@ int main(void)
75156
k_sleep(K_MSEC(10));
76157
}
77158

78-
LOG_INF("The host selected format '%s' %ux%u, preparing %u buffers of %u bytes",
159+
ret = video_get_frmival(uvc_dev, &frmival);
160+
if (ret != 0) {
161+
LOG_ERR("Failed to get the video frame interval");
162+
return ret;
163+
}
164+
165+
LOG_INF("The host selected format '%s' %ux%u at frame interval %u/%u",
79166
VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height,
80-
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX, fmt.pitch * fmt.height);
167+
frmival.numerator, frmival.denominator);
81168

82-
/* Size to allocate for each buffer */
83-
if (caps.min_line_count == LINE_COUNT_HEIGHT) {
84-
bsize = fmt.pitch * fmt.height;
85-
} else {
86-
bsize = fmt.pitch * caps.min_line_count;
169+
fmt.type = VIDEO_BUF_TYPE_OUTPUT;
170+
171+
ret = video_set_format(video_dev, &fmt);
172+
if (ret != 0) {
173+
LOG_WRN("Could not set the format of %s", video_dev->name);
174+
}
175+
176+
ret = video_set_frmival(video_dev, &frmival);
177+
if (ret != 0) {
178+
LOG_WRN("Could not set the framerate of %s", video_dev->name);
87179
}
88180

181+
bsize = app_get_min_buf_size(&fmt);
182+
183+
LOG_INF("Preparing %u buffers of %u bytes", CONFIG_VIDEO_BUFFER_POOL_NUM_MAX, bsize);
184+
89185
for (int i = 0; i < CONFIG_VIDEO_BUFFER_POOL_NUM_MAX; i++) {
90186
vbuf = video_buffer_alloc(bsize, K_NO_WAIT);
91187
if (vbuf == NULL) {

0 commit comments

Comments
 (0)