Skip to content

Commit 751e976

Browse files
author
Alain Volmat
committed
samples: usb: uvc: add video encoder support
Allow creating a pipeline as follow camera receiver -> encoder -> uvc If the chosen zephyr,videoenc is available, the sample will pipe the camera receiver to the encoder and then the UVC device instead of directly the camera receiver to the UVC. Current implementation has several points hardcoded for the time being: 1. intermediate pixel format between the camera receiver and encoder is set to NV12. This shouldn't be hardcoded and should instead be discovered as a commonly capable format from the encoder / video dev 2. it is considered that encoder device do NOT perform any resolution change and that encoder output resolution is directly based on the camera receiver resolution. Thanks to this, UVC exposed formats are thus the encoder output pixel format & camera receiver resolutions. Signed-off-by: Alain Volmat <[email protected]>
1 parent 9c0851f commit 751e976

File tree

1 file changed

+200
-10
lines changed
  • samples/subsys/usb/uvc/src

1 file changed

+200
-10
lines changed

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

Lines changed: 200 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,37 @@ LOG_MODULE_REGISTER(uvc_sample, LOG_LEVEL_INF);
1919

2020
const static struct device *const uvc_dev = DEVICE_DT_GET(DT_NODELABEL(uvc));
2121
const static struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
22+
static const struct device *const videoenc_dev = DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_videoenc));
2223

2324
/* Format capabilities of video_dev, used everywhere through the sample */
2425
static struct video_caps video_caps = {.type = VIDEO_BUF_TYPE_OUTPUT};
26+
static struct video_caps videoenc_out_caps = {.type = VIDEO_BUF_TYPE_OUTPUT};
27+
28+
#if DT_HAS_CHOSEN(zephyr_videoenc) && CONFIG_VIDEO_BUFFER_POOL_NUM_MAX < 2
29+
#error CONFIG_VIDEO_BUFFER_POOL_NUM_MAX must be >=2 in order to use a zephyr,videoenc
30+
#endif
31+
32+
static bool app_has_videoenc(void)
33+
{
34+
return (videoenc_dev != NULL);
35+
}
2536

2637
static const struct device *app_uvc_source_dev(void)
2738
{
28-
return video_dev;
39+
if (app_has_videoenc()) {
40+
return videoenc_dev;
41+
} else {
42+
return video_dev;
43+
}
44+
}
45+
46+
static struct video_caps *app_uvc_source_caps(void)
47+
{
48+
if (app_has_videoenc()) {
49+
return &videoenc_out_caps;
50+
} else {
51+
return &video_caps;
52+
}
2953
}
3054

3155
/* Pixel formats present in one of the UVC 1.5 standard */
@@ -38,7 +62,7 @@ static bool app_is_supported_format(uint32_t pixfmt)
3862

3963
static bool app_has_supported_format(void)
4064
{
41-
const struct video_format_cap *fmts = video_caps.format_caps;
65+
const struct video_format_cap *fmts = app_uvc_source_caps()->format_caps;
4266

4367
for (int i = 0; fmts[i].pixelformat != 0; i++) {
4468
if (app_is_supported_format(fmts[i].pixelformat)) {
@@ -103,12 +127,14 @@ static struct video_resolution video_common_fmts[] = {
103127
static void app_add_filtered_formats(void)
104128
{
105129
const bool has_sup_fmts = app_has_supported_format();
130+
struct video_caps *uvc_src_caps = app_uvc_source_caps();
106131

107-
for (int i = 0; video_caps.format_caps[i].pixelformat != 0; i++) {
132+
for (int i = 0; uvc_src_caps->format_caps[i].pixelformat != 0; i++) {
133+
uint32_t pixelformat = uvc_src_caps->format_caps[i].pixelformat;
108134
const struct video_format_cap *vcap = &video_caps.format_caps[i];
109135
int count = 1;
110136

111-
app_add_format(vcap->pixelformat, vcap->width_min, vcap->height_min, has_sup_fmts);
137+
app_add_format(pixelformat, vcap->width_min, vcap->height_min, has_sup_fmts);
112138

113139
if (vcap->width_min != vcap->width_max || vcap->height_min != vcap->height_max) {
114140
app_add_format(vcap->pixelformat, vcap->width_max, vcap->height_max,
@@ -138,19 +164,119 @@ static void app_add_filtered_formats(void)
138164
continue;
139165
}
140166

141-
app_add_format(vcap->pixelformat, video_common_fmts[j].width,
167+
app_add_format(pixelformat, video_common_fmts[j].width,
142168
video_common_fmts[j].height, has_sup_fmts);
143169
count++;
144170
}
145171
}
146172
}
147173

174+
static int app_init_videoenc(const struct device *const dev)
175+
{
176+
int ret;
177+
178+
if (!device_is_ready(dev)) {
179+
LOG_ERR("video encoder %s failed to initialize", dev->name);
180+
return -ENODEV;
181+
}
182+
183+
ret = video_get_caps(dev, &videoenc_out_caps);
184+
if (ret != 0) {
185+
LOG_ERR("Unable to retrieve video encoder output capabilities");
186+
return ret;
187+
}
188+
189+
/*
190+
* FIXME - we should look carefully at both video capture output and encoder input
191+
* caps to detect intermediate format.
192+
* This is where we should define the format which is going to be used
193+
* between the camera and the encoder input
194+
*/
195+
196+
return 0;
197+
}
198+
199+
static int app_configure_videoenc(const struct device *const dev,
200+
uint32_t width, uint32_t height,
201+
uint32_t sink_pixelformat, uint32_t source_pixelformat,
202+
uint32_t nb_buffer)
203+
{
204+
struct video_format fmt = {
205+
.width = width,
206+
.height = height,
207+
};
208+
struct video_buffer *buf;
209+
int ret;
210+
211+
/*
212+
* Need to configure both input & output of the encoder
213+
* and allocate / enqueue buffers to the output of the
214+
* encoder
215+
*/
216+
fmt.type = VIDEO_BUF_TYPE_INPUT;
217+
fmt.pixelformat = sink_pixelformat;
218+
ret = video_set_compose_format(dev, &fmt);
219+
if (ret != 0) {
220+
LOG_ERR("Could not set the %s encoder input format", dev->name);
221+
return ret;
222+
}
223+
224+
fmt.type = VIDEO_BUF_TYPE_OUTPUT;
225+
fmt.pixelformat = source_pixelformat;
226+
ret = video_set_compose_format(dev, &fmt);
227+
if (ret != 0) {
228+
LOG_ERR("Could not set the %s encoder output format", dev->name);
229+
return ret;
230+
}
231+
232+
LOG_INF("Preparing %u buffers of %u bytes for encoder output", nb_buffer, fmt.size);
233+
234+
for (int i = 0; i < nb_buffer; i++) {
235+
buf = video_buffer_aligned_alloc(fmt.size, CONFIG_VIDEO_BUFFER_POOL_ALIGN,
236+
K_NO_WAIT);
237+
if (buf == NULL) {
238+
LOG_ERR("Could not allocate the encoder output buffer");
239+
return -ENOMEM;
240+
}
241+
242+
buf->type = VIDEO_BUF_TYPE_OUTPUT;
243+
244+
ret = video_enqueue(dev, buf);
245+
if (ret != 0) {
246+
LOG_ERR("Could not enqueue video buffer");
247+
return ret;
248+
}
249+
}
250+
251+
return 0;
252+
}
253+
254+
static int app_start_videoenc(const struct device *const dev)
255+
{
256+
int ret;
257+
258+
ret = video_stream_start(dev, VIDEO_BUF_TYPE_OUTPUT);
259+
if (ret != 0) {
260+
LOG_ERR("Failed to start %s output", dev->name);
261+
return ret;
262+
}
263+
264+
ret = video_stream_start(dev, VIDEO_BUF_TYPE_INPUT);
265+
if (ret != 0) {
266+
LOG_ERR("Failed to start %s input", dev->name);
267+
return ret;
268+
}
269+
270+
return 0;
271+
}
272+
148273
int main(void)
149274
{
150275
const struct device *uvc_src_dev = app_uvc_source_dev();
151276
struct usbd_context *sample_usbd;
152277
struct video_buffer *vbuf;
153278
struct video_format fmt = {0};
279+
uint32_t uvc_buf_count = CONFIG_VIDEO_BUFFER_POOL_NUM_MAX;
154280
struct video_frmival frmival = {0};
155281
struct k_poll_signal sig;
156282
struct k_poll_event evt[1];
@@ -168,6 +294,16 @@ int main(void)
168294
return 0;
169295
}
170296

297+
if (app_has_videoenc()) {
298+
ret = app_init_videoenc(videoenc_dev);
299+
if (ret != 0) {
300+
return ret;
301+
}
302+
303+
/* When using encoder, we split the VIDEO_BUFFER_POOL_NUM_MAX in 2 */
304+
uvc_buf_count /= 2;
305+
}
306+
171307
/* Must be called before usb_enable() */
172308
uvc_set_video_dev(uvc_dev, uvc_src_dev);
173309

@@ -211,7 +347,27 @@ int main(void)
211347
VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height,
212348
frmival.numerator, frmival.denominator);
213349

350+
if (app_has_videoenc()) {
351+
/*
352+
* FIXME - this is currently hardcoded in NV12 while it should be
353+
* a format that has been validated for both video dev and encoder
354+
*/
355+
ret = app_configure_videoenc(videoenc_dev, fmt.width, fmt.height,
356+
VIDEO_PIX_FMT_NV12, fmt.pixelformat,
357+
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX - uvc_buf_count);
358+
if (ret != 0) {
359+
return ret;
360+
}
361+
}
362+
214363
fmt.type = VIDEO_BUF_TYPE_OUTPUT;
364+
if (app_has_videoenc()) {
365+
/*
366+
* FIXME - this is currently hardcoded in NV12 while it should be
367+
* a format that has been validated for both video dev and encoder
368+
*/
369+
fmt.pixelformat = VIDEO_PIX_FMT_NV12;
370+
}
215371

216372
ret = video_set_compose_format(video_dev, &fmt);
217373
if (ret != 0) {
@@ -220,14 +376,19 @@ int main(void)
220376
fmt.width, fmt.height, fmt.size);
221377
}
222378

379+
/*
380+
* FIXME - shortcut here since current available encoders do not
381+
* have frmival support for the time being so this is done directly
382+
* at camera level
383+
*/
223384
ret = video_set_frmival(video_dev, &frmival);
224385
if (ret != 0) {
225386
LOG_WRN("Could not set the framerate of %s", video_dev->name);
226387
}
227388

228-
LOG_INF("Preparing %u buffers of %u bytes", CONFIG_VIDEO_BUFFER_POOL_NUM_MAX, fmt.size);
389+
LOG_INF("Preparing %u buffers of %u bytes", uvc_buf_count, fmt.size);
229390

230-
for (int i = 0; i < CONFIG_VIDEO_BUFFER_POOL_NUM_MAX; i++) {
391+
for (int i = 0; i < uvc_buf_count; i++) {
231392
vbuf = video_buffer_aligned_alloc(fmt.size, CONFIG_VIDEO_BUFFER_POOL_ALIGN,
232393
K_NO_WAIT);
233394
if (vbuf == NULL) {
@@ -244,14 +405,14 @@ int main(void)
244405
}
245406
}
246407

247-
LOG_DBG("Preparing signaling for %s input/output", video_dev->name);
408+
LOG_DBG("Preparing signaling for %s input/output", uvc_src_dev->name);
248409

249410
k_poll_signal_init(&sig);
250411
k_poll_event_init(&evt[0], K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &sig);
251412

252-
ret = video_set_signal(video_dev, &sig);
413+
ret = video_set_signal(uvc_src_dev, &sig);
253414
if (ret != 0) {
254-
LOG_WRN("Failed to setup the signal on %s output endpoint", video_dev->name);
415+
LOG_WRN("Failed to setup the signal on %s output endpoint", uvc_src_dev->name);
255416
timeout = K_MSEC(1);
256417
}
257418

@@ -263,6 +424,13 @@ int main(void)
263424

264425
LOG_INF("Starting the video transfer");
265426

427+
if (app_has_videoenc()) {
428+
ret = app_start_videoenc(videoenc_dev);
429+
if (ret != 0) {
430+
return ret;
431+
}
432+
}
433+
266434
ret = video_stream_start(video_dev, VIDEO_BUF_TYPE_OUTPUT);
267435
if (ret != 0) {
268436
LOG_ERR("Failed to start %s", video_dev->name);
@@ -276,6 +444,17 @@ int main(void)
276444
return ret;
277445
}
278446

447+
if (app_has_videoenc()) {
448+
ret = video_transfer_buffer(video_dev, uvc_src_dev,
449+
VIDEO_BUF_TYPE_OUTPUT, VIDEO_BUF_TYPE_INPUT,
450+
K_NO_WAIT);
451+
if (ret != 0 && ret != -EAGAIN) {
452+
LOG_ERR("Failed to transfer from %s to %s",
453+
video_dev->name, uvc_src_dev->name);
454+
return ret;
455+
}
456+
}
457+
279458
ret = video_transfer_buffer(uvc_src_dev, uvc_dev,
280459
VIDEO_BUF_TYPE_OUTPUT, VIDEO_BUF_TYPE_INPUT, K_NO_WAIT);
281460
if (ret != 0 && ret != -EAGAIN) {
@@ -284,6 +463,17 @@ int main(void)
284463
return ret;
285464
}
286465

466+
if (app_has_videoenc()) {
467+
ret = video_transfer_buffer(uvc_src_dev, video_dev,
468+
VIDEO_BUF_TYPE_INPUT, VIDEO_BUF_TYPE_OUTPUT,
469+
K_NO_WAIT);
470+
if (ret != 0 && ret != -EAGAIN) {
471+
LOG_ERR("Failed to transfer from %s to %s",
472+
uvc_src_dev->name, video_dev->name);
473+
return ret;
474+
}
475+
}
476+
287477
ret = video_transfer_buffer(uvc_dev, uvc_src_dev,
288478
VIDEO_BUF_TYPE_INPUT, VIDEO_BUF_TYPE_OUTPUT, K_NO_WAIT);
289479
if (ret != 0 && ret != -EAGAIN) {

0 commit comments

Comments
 (0)