Skip to content

Commit ef69e6f

Browse files
committed
samples: drivers: video: update video capture sample.
Update this sample is dedicated to support usb host video. This sample can enumerate UVC device, configure UVC device and start video stream transfers then display it on LCD. Signed-off-by: Aiden Hu <[email protected]>
1 parent 6816dab commit ef69e6f

File tree

6 files changed

+131
-36
lines changed

6 files changed

+131
-36
lines changed

samples/drivers/display/sample.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ tests:
111111
- platform:frdm_mcxn236/mcxn236:SHIELD=lcd_par_s035_8080
112112
- platform:frdm_mcxa156/mcxa156:SHIELD=lcd_par_s035_8080
113113
- platform:frdm_rw612:SHIELD=lcd_par_s035_spi
114+
- platform:rd_rw612_bga:SHIELD=lcd_par_s035_spi
114115
- platform:ek_ra8d1:SHIELD=rtkmipilcdb00000be
115116
- platform:ek_ra8d1:SHIELD=rtk7eka6m3b00001bu
116117
- platform:nucleo_g071rb/stm32g071xx:SHIELD=x_nucleo_gfx01m2
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CONFIG_UHC_DRIVER=y
2+
CONFIG_USB_HOST_STACK=y
3+
CONFIG_USBH_VIDEO_CLASS=y
4+
5+
# Video buffer configuration
6+
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=307200
7+
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=4
8+
CONFIG_VIDEO_BUFFER_POOL_ALIGN=32
9+
10+
11+
# Memory configuration for USB transfers
12+
CONFIG_HEAP_MEM_POOL_SIZE=32768
13+
CONFIG_MAIN_STACK_SIZE=4096
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright 2025 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/ {
8+
uvc_host: uvc_host {
9+
compatible = "zephyr,uvc-host";
10+
};
11+
};
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
CONFIG_VIDEO=y
2-
CONFIG_SHELL=y
3-
CONFIG_DEVICE_SHELL=y
2+
CONFIG_USB_HOST_STACK=y
43
CONFIG_PRINTK=y
54
CONFIG_LOG=y
65
CONFIG_DISPLAY=y
76
CONFIG_REQUIRES_FLOAT_PRINTF=y
87
CONFIG_LOG_MODE_DEFERRED=y
8+
CONFIG_USBH_VIDEO_LOG_LEVEL_WRN=y

samples/drivers/video/capture/sample.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ tests:
44
sample.video.capture:
55
tags:
66
- video
7+
- usb
78
- shield
89
- samples
910
extra_args:
@@ -13,9 +14,11 @@ tests:
1314
- platform:frdm_mcxn947/mcxn947/cpu0:SHIELD="dvp_20pin_ov7670;lcd_par_s035_8080"
1415
- platform:frdm_mcxn236/mcxn236:SHIELD="dvp_20pin_ov7670;lcd_par_s035_8080"
1516
- platform:stm32h7b3i_dk:SHIELD="st_b_cams_omv_mb1683"
17+
- platform:rd_rw612_bga/rw612:SHIELD="usb_camera;lcd_par_s035_8080"
1618
extra_configs:
1719
- CONFIG_TEST=y
1820
- CONFIG_FPU=y
21+
- CONFIG_USB_HOST=y
1922
harness: console
2023
harness_config:
2124
fixture: fixture_camera
@@ -31,15 +34,19 @@ tests:
3134
- mimxrt1064_evk/mimxrt1064
3235
- mimxrt1170_evk/mimxrt1176/cm7
3336
- mimxrt1170_evk@B/mimxrt1176/cm7
37+
- rd_rw612_bga/rw612
3438
- frdm_mcxn947/mcxn947/cpu0
3539
- frdm_mcxn236/mcxn236
3640
- mm_swiftio
3741
- esp32s3_eye/esp32s3/procpu
3842
- stm32h7b3i_dk
39-
depends_on: video
43+
depends_on:
44+
- video
45+
- usbh
4046
integration_platforms:
4147
- mimxrt1064_evk/mimxrt1064
4248
- mimxrt1170_evk/mimxrt1176/cm7
49+
- rd_rw612_bga/rw612
4350
sample.video.capture.shell:
4451
tags:
4552
- video

samples/drivers/video/capture/src/main.c

Lines changed: 96 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include <zephyr/drivers/display.h>
1212
#include <zephyr/drivers/video.h>
1313
#include <zephyr/drivers/video-controls.h>
14-
14+
#include <zephyr/usb/usbh.h>
1515
#include <zephyr/logging/log.h>
1616

1717
#ifdef CONFIG_TEST
@@ -22,9 +22,8 @@ LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
2222
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);
2323
#endif
2424

25-
#if !DT_HAS_CHOSEN(zephyr_camera)
26-
#error No camera chosen in devicetree. Missing "--shield" or "--snippet video-sw-generator" flag?
27-
#endif
25+
USBH_CONTROLLER_DEFINE(uhs_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_uhc0)));
26+
const struct device *const uvc_host = DEVICE_DT_GET(DT_NODELABEL(uvc_host));
2827

2928
#if DT_HAS_CHOSEN(zephyr_display)
3029
static inline int display_setup(const struct device *const display_dev, const uint32_t pixfmt)
@@ -75,8 +74,8 @@ static inline int display_setup(const struct device *const display_dev, const ui
7574
}
7675

7776
static inline void video_display_frame(const struct device *const display_dev,
78-
const struct video_buffer *const vbuf,
79-
const struct video_format fmt)
77+
const struct video_buffer *const vbuf,
78+
const struct video_format fmt)
8079
{
8180
struct display_buffer_descriptor buf_desc = {
8281
.buf_size = vbuf->bytesused,
@@ -89,15 +88,22 @@ static inline void video_display_frame(const struct device *const display_dev,
8988
}
9089
#endif
9190

91+
/* TODO: handle disconnection,etc.. */
92+
static void video_uvc_device_signal_handler(void)
93+
{
94+
}
95+
9296
int main(void)
9397
{
9498
struct video_buffer *buffers[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX];
9599
struct video_buffer *vbuf = &(struct video_buffer){};
96-
const struct device *video_dev;
97100
struct video_format fmt;
98101
struct video_caps caps;
99102
struct video_frmival frmival;
100103
struct video_frmival_enum fie;
104+
struct k_poll_signal sig;
105+
struct k_poll_event evt[1];
106+
k_timeout_t timeout = K_FOREVER;
101107
enum video_buf_type type = VIDEO_BUF_TYPE_OUTPUT;
102108
#if (CONFIG_VIDEO_SOURCE_CROP_WIDTH && CONFIG_VIDEO_SOURCE_CROP_HEIGHT) || \
103109
CONFIG_VIDEO_FRAME_HEIGHT || CONFIG_VIDEO_FRAME_WIDTH
@@ -116,17 +122,58 @@ int main(void)
116122
return 0;
117123
}
118124

119-
video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
120-
if (!device_is_ready(video_dev)) {
121-
LOG_ERR("%s: video device is not ready", video_dev->name);
125+
if (!device_is_ready(uvc_host)) {
126+
LOG_ERR("%s: USB host is not ready", uvc_host->name);
122127
return 0;
123128
}
129+
LOG_INF("USB host: %s", uvc_host->name);
124130

125-
LOG_INF("Video device: %s", video_dev->name);
131+
err = usbh_init(&uhs_ctx);
132+
if (err) {
133+
LOG_ERR("Failed to initialize host support");
134+
return err;
135+
}
136+
137+
err = usbh_enable(&uhs_ctx);
138+
if (err) {
139+
LOG_ERR("Failed to enable USB host support");
140+
return err;
141+
}
142+
143+
k_poll_signal_init(&sig);
144+
k_poll_event_init(&evt[0], K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &sig);
145+
146+
err = video_set_signal(uvc_host, &sig);
147+
if (err != 0) {
148+
LOG_WRN("Failed to setup the signal on %s output endpoint", uvc_host->name);
149+
timeout = K_MSEC(10);
150+
}
151+
152+
/* Wait for UVC device connection event */
153+
while (true) {
154+
err = k_poll(evt, ARRAY_SIZE(evt), timeout);
155+
if (err != 0) {
156+
LOG_WRN("Poll failed with error %d, retrying...", err);
157+
continue;
158+
}
159+
160+
int signaled, result;
161+
k_poll_signal_check(&sig, &signaled, &result);
162+
163+
if (signaled && result == USBH_DEVICE_CONNECTED) {
164+
LOG_INF("UVC device connected successfully!");
165+
k_poll_signal_reset(&sig);
166+
break;
167+
}
168+
169+
/* Received other signal, reset and continue waiting */
170+
k_poll_signal_reset(&sig);
171+
LOG_DBG("Received signal %d, waiting for device connection...", result);
172+
}
126173

127174
/* Get capabilities */
128175
caps.type = type;
129-
if (video_get_caps(video_dev, &caps)) {
176+
if (video_get_caps(uvc_host, &caps)) {
130177
LOG_ERR("Unable to retrieve video capabilities");
131178
return 0;
132179
}
@@ -144,7 +191,7 @@ int main(void)
144191

145192
/* Get default/native format */
146193
fmt.type = type;
147-
if (video_get_format(video_dev, &fmt)) {
194+
if (video_get_format(uvc_host, &fmt)) {
148195
LOG_ERR("Unable to retrieve video format");
149196
return 0;
150197
}
@@ -156,7 +203,7 @@ int main(void)
156203
sel.rect.top = CONFIG_VIDEO_SOURCE_CROP_TOP;
157204
sel.rect.width = CONFIG_VIDEO_SOURCE_CROP_WIDTH;
158205
sel.rect.height = CONFIG_VIDEO_SOURCE_CROP_HEIGHT;
159-
if (video_set_selection(video_dev, &sel)) {
206+
if (video_set_selection(uvc_host, &sel)) {
160207
LOG_ERR("Unable to set selection crop");
161208
return 0;
162209
}
@@ -178,7 +225,7 @@ int main(void)
178225
* and if compose is necessary
179226
*/
180227
sel.target = VIDEO_SEL_TGT_CROP;
181-
err = video_get_selection(video_dev, &sel);
228+
err = video_get_selection(uvc_host, &sel);
182229
if (err < 0 && err != -ENOSYS) {
183230
LOG_ERR("Unable to get selection crop");
184231
return 0;
@@ -190,7 +237,7 @@ int main(void)
190237
sel.rect.top = 0;
191238
sel.rect.width = fmt.width;
192239
sel.rect.height = fmt.height;
193-
err = video_set_selection(video_dev, &sel);
240+
err = video_set_selection(uvc_host, &sel);
194241
if (err < 0 && err != -ENOSYS) {
195242
LOG_ERR("Unable to set selection compose");
196243
return 0;
@@ -201,24 +248,31 @@ int main(void)
201248
if (strcmp(CONFIG_VIDEO_PIXEL_FORMAT, "")) {
202249
fmt.pixelformat = VIDEO_FOURCC_FROM_STR(CONFIG_VIDEO_PIXEL_FORMAT);
203250
}
251+
#if CONFIG_VIDEO_FRAME_WIDTH > 0
252+
fmt.width = CONFIG_VIDEO_FRAME_WIDTH;
253+
#endif
254+
255+
#if CONFIG_VIDEO_FRAME_HEIGHT > 0
256+
fmt.height = CONFIG_VIDEO_FRAME_HEIGHT;
257+
#endif
204258

205259
LOG_INF("- Video format: %s %ux%u",
206260
VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height);
207261

208-
if (video_set_format(video_dev, &fmt)) {
262+
if (video_set_format(uvc_host, &fmt)) {
209263
LOG_ERR("Unable to set format");
210264
return 0;
211265
}
212266

213-
if (!video_get_frmival(video_dev, &frmival)) {
267+
if (!video_get_frmival(uvc_host, &frmival)) {
214268
LOG_INF("- Default frame rate : %f fps",
215269
1.0 * frmival.denominator / frmival.numerator);
216270
}
217271

218272
LOG_INF("- Supported frame intervals for the default format:");
219273
memset(&fie, 0, sizeof(fie));
220274
fie.format = &fmt;
221-
while (video_enum_frmival(video_dev, &fie) == 0) {
275+
while (video_enum_frmival(uvc_host, &fie) == 0) {
222276
if (fie.type == VIDEO_FRMIVAL_TYPE_DISCRETE) {
223277
LOG_INF(" %u/%u", fie.discrete.numerator, fie.discrete.denominator);
224278
} else {
@@ -233,7 +287,7 @@ int main(void)
233287
/* Get supported controls */
234288
LOG_INF("- Supported controls:");
235289
const struct device *last_dev = NULL;
236-
struct video_ctrl_query cq = {.dev = video_dev, .id = VIDEO_CTRL_FLAG_NEXT_CTRL};
290+
struct video_ctrl_query cq = {.dev = uvc_host, .id = VIDEO_CTRL_FLAG_NEXT_CTRL};
237291

238292
while (!video_query_ctrl(&cq)) {
239293
if (cq.dev != last_dev) {
@@ -249,17 +303,17 @@ int main(void)
249303
int tp_set_ret = -ENOTSUP;
250304

251305
if (IS_ENABLED(CONFIG_VIDEO_CTRL_HFLIP)) {
252-
video_set_ctrl(video_dev, &ctrl);
306+
video_set_ctrl(uvc_host, &ctrl);
253307
}
254308

255309
if (IS_ENABLED(CONFIG_VIDEO_CTRL_VFLIP)) {
256310
ctrl.id = VIDEO_CID_VFLIP;
257-
video_set_ctrl(video_dev, &ctrl);
311+
video_set_ctrl(uvc_host, &ctrl);
258312
}
259313

260314
if (IS_ENABLED(CONFIG_TEST)) {
261315
ctrl.id = VIDEO_CID_TEST_PATTERN;
262-
tp_set_ret = video_set_ctrl(video_dev, &ctrl);
316+
video_set_ctrl(uvc_host, &ctrl);
263317
}
264318

265319
#if DT_HAS_CHOSEN(zephyr_display)
@@ -273,8 +327,10 @@ int main(void)
273327
err = display_setup(display_dev, fmt.pixelformat);
274328
if (err) {
275329
LOG_ERR("Unable to set up display");
330+
/* TODO: Use the format that this display supported, then need to do format covertion when streaming */
276331
return err;
277332
}
333+
278334
#endif
279335

280336
/* Size to allocate for each buffer */
@@ -284,6 +340,12 @@ int main(void)
284340
bsize = fmt.pitch * caps.min_line_count;
285341
}
286342

343+
/* Start video capture */
344+
if (video_stream_start(uvc_host, type)) {
345+
LOG_ERR("Unable to start capture (interface)");
346+
return 0;
347+
}
348+
287349
/* Alloc video buffers and enqueue for capture */
288350
for (i = 0; i < ARRAY_SIZE(buffers); i++) {
289351
/*
@@ -297,21 +359,20 @@ int main(void)
297359
return 0;
298360
}
299361
buffers[i]->type = type;
300-
video_enqueue(video_dev, buffers[i]);
301-
}
302-
303-
/* Start video capture */
304-
if (video_stream_start(video_dev, type)) {
305-
LOG_ERR("Unable to start capture (interface)");
306-
return 0;
362+
video_enqueue(uvc_host, buffers[i]);
307363
}
308364

309365
LOG_INF("Capture started");
310366

311367
/* Grab video frames */
312368
vbuf->type = type;
313369
while (1) {
314-
err = video_dequeue(video_dev, &vbuf, K_FOREVER);
370+
err = k_poll(evt, ARRAY_SIZE(evt), timeout);
371+
if (err != 0 && err != -EAGAIN) {
372+
LOG_ERR("Poll exited with status %d", err);
373+
return err;
374+
}
375+
err = video_dequeue(uvc_host, &vbuf, K_FOREVER);
315376
if (err) {
316377
LOG_ERR("Unable to dequeue video buf");
317378
return 0;
@@ -329,13 +390,15 @@ int main(void)
329390
#endif
330391

331392
#if DT_HAS_CHOSEN(zephyr_display)
393+
vbuf->type = VIDEO_BUF_TYPE_INPUT;
332394
video_display_frame(display_dev, vbuf, fmt);
333395
#endif
334-
335-
err = video_enqueue(video_dev, vbuf);
396+
err = video_enqueue(uvc_host, vbuf);
336397
if (err) {
337398
LOG_ERR("Unable to requeue video buf");
338399
return 0;
339400
}
401+
402+
video_uvc_device_signal_handler();
340403
}
341404
}

0 commit comments

Comments
 (0)