Skip to content

Commit 7aa147c

Browse files
Merge pull request #176 from espressif/feat/uvc_esp_video
UVC updates for esp_video
2 parents 5d61fca + 2fb643a commit 7aa147c

28 files changed

+826
-98
lines changed

host/class/uvc/usb_host_uvc/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## [Unreleased]
2+
3+
- Added `uvc_host_stream_format_get()` function that returns current stream's format
4+
- Added `uvc_host_buf_info_get()` function for esp_video binding
5+
- Added option to request default FPS by setting FPS = 0
6+
- Fixed UVC driver re-installation
7+
18
## 2.2.0
29

310
- Added `uvc_host_stream_format_select()` function that allows change of format of an opened stream

host/class/uvc/usb_host_uvc/host_test/main/opening/test_opening.cpp

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ SCENARIO("UVC driver install/uninstall")
6767
}
6868

6969
REQUIRE(ESP_OK == test_uvc_host_uninstall());
70+
71+
AND_WHEN("UVC driver is installed again") {
72+
THEN("Installation succeeds") {
73+
REQUIRE(ESP_OK == test_uvc_host_install(&uvc_driver_config));
74+
REQUIRE(ESP_OK == test_uvc_host_uninstall());
75+
}
76+
}
7077
}
7178
}
7279

@@ -97,9 +104,9 @@ SCENARIO("Test mocked device opening and closing")
97104
.frame_cb = nullptr,
98105
.user_ctx = nullptr,
99106
.usb = {
100-
.dev_addr = 0,
101-
.vid = 0,
102-
.pid = 0,
107+
.dev_addr = UVC_HOST_ANY_DEV_ADDR,
108+
.vid = UVC_HOST_ANY_VID,
109+
.pid = UVC_HOST_ANY_PID,
103110
.uvc_stream_index = 0,
104111
},
105112
.vs_format = {
@@ -157,14 +164,103 @@ SCENARIO("Test mocked device opening and closing")
157164
REQUIRE(ESP_OK == test_uvc_host_stream_close(stream[1]));
158165
}
159166

160-
THEN("Non-existent device cannot be opened") {
167+
THEN("Non-existent VID/PID cannot be opened") {
161168
uvc_host_stream_hdl_t stream = nullptr;
162169
stream_config.usb.vid = 1;
163170
stream_config.usb.pid = 1;
164-
REQUIRE(ESP_ERR_NOT_FOUND == test_uvc_host_stream_open(&stream_config, 0, &stream, true));
171+
REQUIRE(ESP_ERR_NOT_FOUND == uvc_host_stream_open(&stream_config, 0, &stream));
165172
REQUIRE(stream == nullptr);
166173
}
167174

175+
THEN("Non-existent address cannot be opened") {
176+
uvc_host_stream_hdl_t stream = nullptr;
177+
stream_config.usb.dev_addr = 30;
178+
REQUIRE(ESP_ERR_NOT_FOUND == uvc_host_stream_open(&stream_config, 0, &stream));
179+
REQUIRE(stream == nullptr);
180+
}
181+
182+
THEN("One stream cannot be opened twice") {
183+
uvc_host_stream_hdl_t stream = nullptr;
184+
uvc_host_stream_hdl_t stream2 = nullptr;
185+
stream_config.usb.vid = 0x046D;
186+
stream_config.usb.pid = 0x0825;
187+
REQUIRE(ESP_OK == test_uvc_host_stream_open(&stream_config, 0, &stream, true));
188+
189+
// Second call to claim the same interface will fail in usb_host_lib
190+
usb_host_interface_claim_ExpectAnyArgsAndReturn(ESP_FAIL);
191+
REQUIRE_FALSE(ESP_OK == uvc_host_stream_open(&stream_config, 0, &stream2));
192+
193+
REQUIRE(ESP_OK == test_uvc_host_stream_close(stream));
194+
}
195+
196+
REQUIRE(ESP_OK == test_uvc_host_uninstall());
197+
}
198+
}
199+
200+
SCENARIO("Test mocked device format negotiation")
201+
{
202+
// We will put device adding to the SECTION, to run it just once, not repeatedly for all the following SECTIONs
203+
// (if multiple sections are present)
204+
SECTION("Add mocked devices") {
205+
_add_mocked_devices();
206+
207+
// Optionally, print all the devices
208+
//usb_host_mock_print_mocked_devices(0xFF);
209+
}
210+
211+
SECTION("Install the UVC driver") {
212+
const uvc_host_driver_config_t uvc_driver_config = {
213+
.driver_task_stack_size = 4 * 1024,
214+
.driver_task_priority = 10,
215+
.xCoreID = tskNO_AFFINITY,
216+
.create_background_task = true,
217+
.event_cb = nullptr,
218+
.user_ctx = nullptr,
219+
};
220+
REQUIRE(ESP_OK == test_uvc_host_install(&uvc_driver_config));
221+
222+
uvc_host_stream_config_t stream_config = {
223+
.event_cb = nullptr,
224+
.frame_cb = nullptr,
225+
.user_ctx = nullptr,
226+
.usb = {
227+
.dev_addr = UVC_HOST_ANY_DEV_ADDR,
228+
.vid = UVC_HOST_ANY_VID,
229+
.pid = UVC_HOST_ANY_PID,
230+
.uvc_stream_index = 0,
231+
},
232+
.vs_format = {
233+
.h_res = 1280,
234+
.v_res = 720,
235+
.fps = 15,
236+
.format = UVC_VS_FORMAT_MJPEG,
237+
},
238+
.advanced = {
239+
.number_of_frame_buffers = 3,
240+
.frame_size = 0,
241+
.frame_heap_caps = 0,
242+
.number_of_urbs = 4,
243+
.urb_size = 10 * 1024,
244+
},
245+
};
246+
247+
THEN("Stream can be opened with default FPS value") {
248+
uvc_host_stream_hdl_t stream = nullptr;
249+
stream_config.vs_format.fps = 0;
250+
REQUIRE(ESP_OK == test_uvc_host_stream_open(&stream_config, 0, &stream, true));
251+
REQUIRE(ESP_OK == test_uvc_host_stream_close(stream));
252+
}
253+
254+
// @todo this test is not working, because the mock does not support default format negotiation
255+
// To make it work, we must implement our own usb_host_transfer_submit_control_success_mock_callback()
256+
// that would return the default format
257+
// THEN("Stream can be opened with default format") {
258+
// uvc_host_stream_hdl_t stream = nullptr;
259+
// stream_config.vs_format.format = UVC_VS_FORMAT_DEFAULT;
260+
// REQUIRE(ESP_OK == test_uvc_host_stream_open(&stream_config, 0, &stream, true));
261+
// REQUIRE(ESP_OK == test_uvc_host_stream_close(stream));
262+
// }
263+
168264
REQUIRE(ESP_OK == test_uvc_host_uninstall());
169265
}
170266
}

host/class/uvc/usb_host_uvc/host_test/main/parsing/test_parsing_anker_powerconf_c200.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -26,6 +26,7 @@ SCENARIO("Camera descriptor parsing: Anker Powerconf C200", "[anker][powerconf][
2626
{640, 480, 30, UVC_VS_FORMAT_MJPEG},
2727
{640, 360, 30, UVC_VS_FORMAT_MJPEG},
2828
{320, 240, 30, UVC_VS_FORMAT_MJPEG},
29+
{320, 240, 0, UVC_VS_FORMAT_MJPEG},
2930
};
3031

3132
for (uvc_host_stream_format_t this_format : formats) {
@@ -40,6 +41,7 @@ SCENARIO("Camera descriptor parsing: Anker Powerconf C200", "[anker][powerconf][
4041
{640, 480, 30, UVC_VS_FORMAT_YUY2},
4142
{640, 360, 30, UVC_VS_FORMAT_YUY2},
4243
{320, 240, 30, UVC_VS_FORMAT_YUY2},
44+
{320, 240, 0, UVC_VS_FORMAT_YUY2},
4345
};
4446

4547
for (uvc_host_stream_format_t this_format : formats) {
@@ -57,6 +59,7 @@ SCENARIO("Camera descriptor parsing: Anker Powerconf C200", "[anker][powerconf][
5759
{640, 480, 30, UVC_VS_FORMAT_H264},
5860
{640, 360, 30, UVC_VS_FORMAT_H264},
5961
{320, 240, 30, UVC_VS_FORMAT_H264},
62+
{320, 240, 0, UVC_VS_FORMAT_H264},
6063
};
6164

6265
for (uvc_host_stream_format_t this_format : formats) {
@@ -66,6 +69,10 @@ SCENARIO("Camera descriptor parsing: Anker Powerconf C200", "[anker][powerconf][
6669
}
6770
}
6871

72+
GIVEN("Default format") {
73+
REQUIRE_FORMAT_DEFAULT(cfg, 1);
74+
}
75+
6976
GIVEN("Anker Powerconf C200 Unsupported") {
7077
uvc_host_stream_format_t formats[] = {
7178
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS

host/class/uvc/usb_host_uvc/host_test/main/parsing/test_parsing_canyon_cne_cwc2.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ SCENARIO("Camera descriptor parsing: Canyon CNE CWC2", "[canyon][cne_cwc2]")
2929
{1280, 720, 15, UVC_VS_FORMAT_MJPEG},
3030
{1280, 720, 10, UVC_VS_FORMAT_MJPEG},
3131
{1280, 720, 5, UVC_VS_FORMAT_MJPEG},
32+
{1280, 720, 0, UVC_VS_FORMAT_MJPEG},
3233
{1280, 960, 15, UVC_VS_FORMAT_MJPEG},
3334
{1280, 960, 10, UVC_VS_FORMAT_MJPEG},
3435
{1280, 960, 5, UVC_VS_FORMAT_MJPEG},
36+
{1280, 960, 0, UVC_VS_FORMAT_MJPEG},
3537
{1600, 1200, 15, UVC_VS_FORMAT_MJPEG},
3638
{1600, 1200, 10, UVC_VS_FORMAT_MJPEG},
3739
{1600, 1200, 5, UVC_VS_FORMAT_MJPEG},
40+
{1600, 1200, 0, UVC_VS_FORMAT_MJPEG},
3841
};
3942

4043
for (uvc_host_stream_format_t this_format : formats) {
@@ -54,9 +57,12 @@ SCENARIO("Camera descriptor parsing: Canyon CNE CWC2", "[canyon][cne_cwc2]")
5457
FORMAT_UNCOMPRESSED_20_5(800, 600),
5558
{1280, 720, 10, UVC_VS_FORMAT_YUY2},
5659
{1280, 720, 5, UVC_VS_FORMAT_YUY2},
60+
{1280, 720, 0, UVC_VS_FORMAT_YUY2},
5761
{1280, 960, 9, UVC_VS_FORMAT_YUY2},
5862
{1280, 960, 5, UVC_VS_FORMAT_YUY2},
63+
{1280, 960, 0, UVC_VS_FORMAT_YUY2},
5964
{1600, 1200, 5, UVC_VS_FORMAT_YUY2},
65+
{1600, 1200, 0, UVC_VS_FORMAT_YUY2},
6066
};
6167

6268
for (uvc_host_stream_format_t this_format : formats) {
@@ -66,6 +72,10 @@ SCENARIO("Camera descriptor parsing: Canyon CNE CWC2", "[canyon][cne_cwc2]")
6672
}
6773
}
6874

75+
GIVEN("Default format") {
76+
REQUIRE_FORMAT_DEFAULT(cfg, 1);
77+
}
78+
6979
GIVEN("Canyon CNE CWC2 Unsupported") {
7080
uvc_host_stream_format_t formats[] = {
7181
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS

host/class/uvc/usb_host_uvc/host_test/main/parsing/test_parsing_customer.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -22,12 +22,16 @@ SCENARIO("Camera descriptor parsing: Customer", "[customer][single]")
2222
uvc_host_stream_format_t formats[] = {
2323
{1280, 720, 15, UVC_VS_FORMAT_MJPEG},
2424
{1280, 720, 10, UVC_VS_FORMAT_MJPEG},
25+
{1280, 720, 0, UVC_VS_FORMAT_MJPEG},
2526
{800, 480, 20, UVC_VS_FORMAT_MJPEG},
2627
{800, 480, 15, UVC_VS_FORMAT_MJPEG},
28+
{800, 480, 0, UVC_VS_FORMAT_MJPEG},
2729
{640, 480, 25, UVC_VS_FORMAT_MJPEG},
2830
{640, 480, 15, UVC_VS_FORMAT_MJPEG},
31+
{640, 480, 0, UVC_VS_FORMAT_MJPEG},
2932
{480, 320, 25, UVC_VS_FORMAT_MJPEG},
3033
{480, 320, 15, UVC_VS_FORMAT_MJPEG},
34+
{480, 320, 0, UVC_VS_FORMAT_MJPEG},
3135
};
3236

3337
for (uvc_host_stream_format_t this_format : formats) {
@@ -37,6 +41,10 @@ SCENARIO("Camera descriptor parsing: Customer", "[customer][single]")
3741
}
3842
}
3943

44+
GIVEN("Default format") {
45+
REQUIRE_FORMAT_DEFAULT(cfg, 1);
46+
}
47+
4048
GIVEN("Customer Unsupported") {
4149
uvc_host_stream_format_t formats[] = {
4250
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS

host/class/uvc/usb_host_uvc/host_test/main/parsing/test_parsing_customer_dual.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -24,6 +24,7 @@ SCENARIO("Camera descriptor parsing: Customer dual", "[customer][dual]")
2424
{720, 1280, 15, UVC_VS_FORMAT_MJPEG},
2525
{720, 1280, 10, UVC_VS_FORMAT_MJPEG},
2626
{720, 1280, 5, UVC_VS_FORMAT_MJPEG},
27+
{720, 1280, 0, UVC_VS_FORMAT_MJPEG},
2728
};
2829

2930
for (uvc_host_stream_format_t this_format : formats) {
@@ -72,6 +73,10 @@ SCENARIO("Camera descriptor parsing: Customer dual", "[customer][dual]")
7273
}
7374
}
7475

76+
GIVEN("Default format") {
77+
REQUIRE_FORMAT_DEFAULT(cfg, 1);
78+
}
79+
7580
GIVEN("Customer dual Unsupported") {
7681
uvc_host_stream_format_t formats[] = {
7782
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS

host/class/uvc/usb_host_uvc/host_test/main/parsing/test_parsing_elp_h264.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -40,6 +40,7 @@ SCENARIO("Camera descriptor parsing: ELP h264", "[elp][h264]")
4040
uvc_host_stream_format_t formats[] = {
4141
FORMAT_UNCOMPRESSED_30_25_15(640, 480),
4242
{800, 600, 15, UVC_VS_FORMAT_YUY2},
43+
{800, 600, 0, UVC_VS_FORMAT_YUY2},
4344
FORMAT_UNCOMPRESSED_30_25_15(640, 360),
4445
FORMAT_UNCOMPRESSED_30_25_15(352, 288),
4546
FORMAT_UNCOMPRESSED_30_25_15(320, 240),
@@ -70,6 +71,10 @@ SCENARIO("Camera descriptor parsing: ELP h264", "[elp][h264]")
7071
}
7172
}
7273

74+
GIVEN("Default format") {
75+
REQUIRE_FORMAT_DEFAULT(cfg, 1);
76+
}
77+
7378
GIVEN("ELP h264 Unsupported") {
7479
uvc_host_stream_format_t formats[] = {
7580
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS

host/class/uvc/usb_host_uvc/host_test/main/parsing/test_parsing_elp_h265.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -79,17 +79,25 @@ SCENARIO("Camera descriptor parsing: ELP h265", "[elp][h265]")
7979
uvc_host_stream_format_t formats[] = {
8080
FORMAT_UNCOMPRESSED_30_20_15(640, 360),
8181
FORMAT_UNCOMPRESSED_30_20_15(640, 480),
82+
8283
{960, 540, 15, UVC_VS_FORMAT_YUY2},
8384
{960, 540, 10, UVC_VS_FORMAT_YUY2},
8485
{960, 540, 5, UVC_VS_FORMAT_YUY2},
86+
{960, 540, 0, UVC_VS_FORMAT_YUY2},
87+
8588
{1024, 576, 15, UVC_VS_FORMAT_YUY2},
8689
{1024, 576, 10, UVC_VS_FORMAT_YUY2},
8790
{1024, 576, 5, UVC_VS_FORMAT_YUY2},
91+
{1024, 576, 0, UVC_VS_FORMAT_YUY2},
92+
8893
{1280, 720, 10, UVC_VS_FORMAT_YUY2},
8994
{1280, 720, 5, UVC_VS_FORMAT_YUY2},
9095
{1280, 720, 2, UVC_VS_FORMAT_YUY2},
96+
{1280, 720, 0, UVC_VS_FORMAT_YUY2},
97+
9198
{1920, 1080, 5, UVC_VS_FORMAT_YUY2},
9299
{1920, 1080, 2, UVC_VS_FORMAT_YUY2},
100+
{1920, 1080, 0, UVC_VS_FORMAT_YUY2},
93101
};
94102

95103
for (uvc_host_stream_format_t this_format : formats) {
@@ -99,11 +107,14 @@ SCENARIO("Camera descriptor parsing: ELP h265", "[elp][h265]")
99107
}
100108
}
101109

110+
GIVEN("Default format") {
111+
REQUIRE_FORMAT_DEFAULT(cfg, 1);
112+
}
113+
102114
GIVEN("ELP h265 Unsupported") {
103115
uvc_host_stream_format_t formats[] = {
104116
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS
105117
{645, 480, 25, UVC_VS_FORMAT_MJPEG}, // Invalid definition
106-
{640, 480, 20, UVC_VS_FORMAT_UNDEFINED}, // Invalid format
107118
};
108119

109120
for (uvc_host_stream_format_t this_format : formats) {

0 commit comments

Comments
 (0)