Skip to content

Commit 87c8fdb

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 8cff3e4 commit 87c8fdb

File tree

6 files changed

+140
-35
lines changed

6 files changed

+140
-35
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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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=30720
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=8192
14+
CONFIG_USBH_USB_DEVICE_HEAP=8192
15+
CONFIG_NOCACHE_MEMORY=y
16+
CONFIG_UHC_BUF_POOL_SIZE=2048
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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
CONFIG_VIDEO=y
1+
CONFIG_POLL=y
2+
CONFIG_EVENTS=y
23
CONFIG_SHELL=y
34
CONFIG_DEVICE_SHELL=y
5+
CONFIG_VIDEO=y
6+
CONFIG_USB_HOST_STACK=y
47
CONFIG_PRINTK=y
58
CONFIG_LOG=y
69
CONFIG_DISPLAY=y
710
CONFIG_REQUIRES_FLOAT_PRINTF=y
811
CONFIG_LOG_MODE_DEFERRED=y
12+
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: 99 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
* SPDX-License-Identifier: Apache-2.0
66
*/
77

8+
#include <zephyr/init.h>
89
#include <zephyr/kernel.h>
10+
#include <stdio.h>
11+
#include <errno.h>
912
#include <zephyr/device.h>
1013

1114
#include <zephyr/drivers/display.h>
1215
#include <zephyr/drivers/video.h>
1316
#include <zephyr/drivers/video-controls.h>
14-
17+
#include <zephyr/usb/usbh.h>
1518
#include <zephyr/logging/log.h>
1619

1720
#ifdef CONFIG_TEST
@@ -22,9 +25,8 @@ LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
2225
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);
2326
#endif
2427

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

2931
#if DT_HAS_CHOSEN(zephyr_display)
3032
static inline int display_setup(const struct device *const display_dev, const uint32_t pixfmt)
@@ -75,8 +77,8 @@ static inline int display_setup(const struct device *const display_dev, const ui
7577
}
7678

7779
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)
80+
const struct video_buffer *const vbuf,
81+
const struct video_format fmt)
8082
{
8183
struct display_buffer_descriptor buf_desc = {
8284
.buf_size = vbuf->bytesused,
@@ -89,15 +91,22 @@ static inline void video_display_frame(const struct device *const display_dev,
8991
}
9092
#endif
9193

94+
/* TODO: handle disconnection,etc.. */
95+
static void video_uvc_device_signal_handler(void)
96+
{
97+
}
98+
9299
int main(void)
93100
{
94101
struct video_buffer *buffers[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX];
95102
struct video_buffer *vbuf = &(struct video_buffer){};
96-
const struct device *video_dev;
97103
struct video_format fmt;
98104
struct video_caps caps;
99105
struct video_frmival frmival;
100106
struct video_frmival_enum fie;
107+
struct k_poll_signal sig;
108+
struct k_poll_event evt[1];
109+
k_timeout_t timeout = K_FOREVER;
101110
enum video_buf_type type = VIDEO_BUF_TYPE_OUTPUT;
102111
#if (CONFIG_VIDEO_SOURCE_CROP_WIDTH && CONFIG_VIDEO_SOURCE_CROP_HEIGHT) || \
103112
CONFIG_VIDEO_FRAME_HEIGHT || CONFIG_VIDEO_FRAME_WIDTH
@@ -116,17 +125,58 @@ int main(void)
116125
return 0;
117126
}
118127

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);
128+
if (!device_is_ready(uvc_host)) {
129+
LOG_ERR("%s: USB host is not ready", uvc_host->name);
122130
return 0;
123131
}
132+
LOG_INF("USB host: %s", uvc_host->name);
124133

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

127177
/* Get capabilities */
128178
caps.type = type;
129-
if (video_get_caps(video_dev, &caps)) {
179+
if (video_get_caps(uvc_host, &caps)) {
130180
LOG_ERR("Unable to retrieve video capabilities");
131181
return 0;
132182
}
@@ -144,7 +194,7 @@ int main(void)
144194

145195
/* Get default/native format */
146196
fmt.type = type;
147-
if (video_get_format(video_dev, &fmt)) {
197+
if (video_get_format(uvc_host, &fmt)) {
148198
LOG_ERR("Unable to retrieve video format");
149199
return 0;
150200
}
@@ -156,7 +206,7 @@ int main(void)
156206
sel.rect.top = CONFIG_VIDEO_SOURCE_CROP_TOP;
157207
sel.rect.width = CONFIG_VIDEO_SOURCE_CROP_WIDTH;
158208
sel.rect.height = CONFIG_VIDEO_SOURCE_CROP_HEIGHT;
159-
if (video_set_selection(video_dev, &sel)) {
209+
if (video_set_selection(uvc_host, &sel)) {
160210
LOG_ERR("Unable to set selection crop");
161211
return 0;
162212
}
@@ -178,7 +228,7 @@ int main(void)
178228
* and if compose is necessary
179229
*/
180230
sel.target = VIDEO_SEL_TGT_CROP;
181-
err = video_get_selection(video_dev, &sel);
231+
err = video_get_selection(uvc_host, &sel);
182232
if (err < 0 && err != -ENOSYS) {
183233
LOG_ERR("Unable to get selection crop");
184234
return 0;
@@ -190,7 +240,7 @@ int main(void)
190240
sel.rect.top = 0;
191241
sel.rect.width = fmt.width;
192242
sel.rect.height = fmt.height;
193-
err = video_set_selection(video_dev, &sel);
243+
err = video_set_selection(uvc_host, &sel);
194244
if (err < 0 && err != -ENOSYS) {
195245
LOG_ERR("Unable to set selection compose");
196246
return 0;
@@ -201,24 +251,31 @@ int main(void)
201251
if (strcmp(CONFIG_VIDEO_PIXEL_FORMAT, "")) {
202252
fmt.pixelformat = VIDEO_FOURCC_FROM_STR(CONFIG_VIDEO_PIXEL_FORMAT);
203253
}
254+
#if CONFIG_VIDEO_FRAME_WIDTH > 0
255+
fmt.width = CONFIG_VIDEO_FRAME_WIDTH;
256+
#endif
257+
258+
#if CONFIG_VIDEO_FRAME_HEIGHT > 0
259+
fmt.height = CONFIG_VIDEO_FRAME_HEIGHT;
260+
#endif
204261

205262
LOG_INF("- Video format: %s %ux%u",
206263
VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height);
207264

208-
if (video_set_format(video_dev, &fmt)) {
265+
if (video_set_format(uvc_host, &fmt)) {
209266
LOG_ERR("Unable to set format");
210267
return 0;
211268
}
212269

213-
if (!video_get_frmival(video_dev, &frmival)) {
270+
if (!video_get_frmival(uvc_host, &frmival)) {
214271
LOG_INF("- Default frame rate : %f fps",
215272
1.0 * frmival.denominator / frmival.numerator);
216273
}
217274

218275
LOG_INF("- Supported frame intervals for the default format:");
219276
memset(&fie, 0, sizeof(fie));
220277
fie.format = &fmt;
221-
while (video_enum_frmival(video_dev, &fie) == 0) {
278+
while (video_enum_frmival(uvc_host, &fie) == 0) {
222279
if (fie.type == VIDEO_FRMIVAL_TYPE_DISCRETE) {
223280
LOG_INF(" %u/%u", fie.discrete.numerator, fie.discrete.denominator);
224281
} else {
@@ -233,7 +290,7 @@ int main(void)
233290
/* Get supported controls */
234291
LOG_INF("- Supported controls:");
235292
const struct device *last_dev = NULL;
236-
struct video_ctrl_query cq = {.dev = video_dev, .id = VIDEO_CTRL_FLAG_NEXT_CTRL};
293+
struct video_ctrl_query cq = {.dev = uvc_host, .id = VIDEO_CTRL_FLAG_NEXT_CTRL};
237294

238295
while (!video_query_ctrl(&cq)) {
239296
if (cq.dev != last_dev) {
@@ -249,17 +306,17 @@ int main(void)
249306
int tp_set_ret = -ENOTSUP;
250307

251308
if (IS_ENABLED(CONFIG_VIDEO_CTRL_HFLIP)) {
252-
video_set_ctrl(video_dev, &ctrl);
309+
video_set_ctrl(uvc_host, &ctrl);
253310
}
254311

255312
if (IS_ENABLED(CONFIG_VIDEO_CTRL_VFLIP)) {
256313
ctrl.id = VIDEO_CID_VFLIP;
257-
video_set_ctrl(video_dev, &ctrl);
314+
video_set_ctrl(uvc_host, &ctrl);
258315
}
259316

260317
if (IS_ENABLED(CONFIG_TEST)) {
261318
ctrl.id = VIDEO_CID_TEST_PATTERN;
262-
tp_set_ret = video_set_ctrl(video_dev, &ctrl);
319+
video_set_ctrl(uvc_host, &ctrl);
263320
}
264321

265322
#if DT_HAS_CHOSEN(zephyr_display)
@@ -273,8 +330,10 @@ int main(void)
273330
err = display_setup(display_dev, fmt.pixelformat);
274331
if (err) {
275332
LOG_ERR("Unable to set up display");
333+
/* TODO: Use the format that this display supported, then need to do format covertion when streaming */
276334
return err;
277335
}
336+
278337
#endif
279338

280339
/* Size to allocate for each buffer */
@@ -284,6 +343,12 @@ int main(void)
284343
bsize = fmt.pitch * caps.min_line_count;
285344
}
286345

346+
/* Start video capture */
347+
if (video_stream_start(uvc_host, type)) {
348+
LOG_ERR("Unable to start capture (interface)");
349+
return 0;
350+
}
351+
287352
/* Alloc video buffers and enqueue for capture */
288353
for (i = 0; i < ARRAY_SIZE(buffers); i++) {
289354
/*
@@ -297,21 +362,20 @@ int main(void)
297362
return 0;
298363
}
299364
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;
365+
video_enqueue(uvc_host, buffers[i]);
307366
}
308367

309368
LOG_INF("Capture started");
310369

311370
/* Grab video frames */
312371
vbuf->type = type;
313372
while (1) {
314-
err = video_dequeue(video_dev, &vbuf, K_FOREVER);
373+
err = k_poll(evt, ARRAY_SIZE(evt), timeout);
374+
if (err != 0 && err != -EAGAIN) {
375+
LOG_ERR("Poll exited with status %d", err);
376+
return err;
377+
}
378+
err = video_dequeue(uvc_host, &vbuf, K_FOREVER);
315379
if (err) {
316380
LOG_ERR("Unable to dequeue video buf");
317381
return 0;
@@ -329,13 +393,15 @@ int main(void)
329393
#endif
330394

331395
#if DT_HAS_CHOSEN(zephyr_display)
396+
vbuf->type = VIDEO_BUF_TYPE_INPUT;
332397
video_display_frame(display_dev, vbuf, fmt);
333398
#endif
334-
335-
err = video_enqueue(video_dev, vbuf);
399+
err = video_enqueue(uvc_host, vbuf);
336400
if (err) {
337401
LOG_ERR("Unable to requeue video buf");
338402
return 0;
339403
}
404+
405+
video_uvc_device_signal_handler();
340406
}
341407
}

0 commit comments

Comments
 (0)