Skip to content

Commit

Permalink
feat(uvc): Testable descriptor parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
tore-espressif committed Oct 17, 2024
1 parent 5d1e75e commit bea7249
Show file tree
Hide file tree
Showing 20 changed files with 910 additions and 165 deletions.
2 changes: 1 addition & 1 deletion host/class/uvc/usb_host_uvc_2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
idf_component_register(SRCS "uvc_host.c" "uvc_descriptor_parsing.c" "uvc_frame.c" "uvc_control.c"
idf_component_register(SRCS "uvc_host.c" "uvc_descriptor_parsing.c" "uvc_descriptor_printing.c" "uvc_frame.c" "uvc_control.c"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include include/esp_private
REQUIRES usb
Expand Down
10 changes: 8 additions & 2 deletions host/class/uvc/usb_host_uvc_2/TODO
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
- [ ] Allow external events handling
- [ ] Allow VID and PID to be 0
- [x] Allow VID and PID to be 0
- [x] 1 CTRL transfer for the driver? of 1 transfer for a device?
- [ ] Mock USB host for descriptor parsing
- [x] Mock USB host for descriptor parsing
- [ ] Allow floating FPS
- [ ] Allow setting of default
- [ ] Add descriptor printing from UVC version 1
- [ ] Test reconnections
- [ ] Test frequent stream pause/resume
- [ ] Support bulk transfers
26 changes: 0 additions & 26 deletions host/class/uvc/usb_host_uvc_2/host_test/main/descriptors/old.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,30 +110,4 @@ const uint8_t Logitech_C980[] = {
0x03, 0x00, 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x6c, 0x00, 0x07, 0x05, 0x87,
0x03, 0x02, 0x00, 0x06, 0x06, 0x30, 0x00, 0x00, 0x02, 0x00
};

const uint8_t unknown_camera[] = {
0x09, 0x02, 0x69, 0x01, 0x02, 0x01, 0x00, 0x80, 0xfa, 0x08, 0x0b, 0x00, 0x02, 0x0e, 0x03, 0x00,
0x05, 0x09, 0x04, 0x00, 0x00, 0x01, 0x0e, 0x01, 0x00, 0x05, 0x0d, 0x24, 0x01, 0x00, 0x01, 0x4d,
0x00, 0xc0, 0xe1, 0xe4, 0x00, 0x01, 0x01, 0x09, 0x24, 0x03, 0x05, 0x01, 0x01, 0x00, 0x03, 0x00,
0x1a, 0x24, 0x06, 0x03, 0x70, 0x33, 0xf0, 0x28, 0x11, 0x63, 0x2e, 0x4a, 0xba, 0x2c, 0x68, 0x90,
0xeb, 0x33, 0x40, 0x16, 0x08, 0x01, 0x02, 0x01, 0x1f, 0x00, 0x12, 0x24, 0x02, 0x01, 0x01, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x0b, 0x24, 0x05, 0x02,
0x01, 0x00, 0x00, 0x02, 0x7f, 0x17, 0x00, 0x07, 0x05, 0x83, 0x03, 0x10, 0x00, 0x06, 0x05, 0x25,
0x03, 0x10, 0x00, 0x09, 0x04, 0x01, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x05, 0x0f, 0x24, 0x01, 0x02,
0x8d, 0x00, 0x81, 0x00, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x0b, 0x24, 0x06, 0x01, 0x01,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x24, 0x07, 0x01, 0x00, 0x40, 0x01, 0xf0, 0x00, 0x08,
0x3c, 0x2f, 0x00, 0x30, 0x68, 0x1b, 0x01, 0x4d, 0x2e, 0x01, 0x00, 0x15, 0x16, 0x05, 0x00, 0x05,
0x15, 0x16, 0x05, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00,
0x80, 0x84, 0x1e, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01, 0x04, 0x1b, 0x24, 0x04, 0x02, 0x01, 0x59,
0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, 0x10,
0x01, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x24, 0x05, 0x01, 0x00, 0x40, 0x01, 0xf0, 0x00, 0x00, 0xc0,
0x5d, 0x00, 0x00, 0xc0, 0x5d, 0x00, 0x00, 0x58, 0x02, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x01, 0x80,
0x84, 0x1e, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01, 0x04, 0x09, 0x04, 0x01, 0x01, 0x01, 0x0e, 0x02,
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x80, 0x00, 0x01, 0x09, 0x04, 0x01, 0x02, 0x01, 0x0e, 0x02,
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01, 0x03, 0x01, 0x0e, 0x02,
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x02, 0x01, 0x09, 0x04, 0x01, 0x04, 0x01, 0x0e, 0x02,
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x58, 0x02, 0x01, 0x09, 0x04, 0x01, 0x05, 0x01, 0x0e, 0x02,
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x20, 0x03, 0x01, 0x09, 0x04, 0x01, 0x06, 0x01, 0x0e, 0x02,
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0xbc, 0x03, 0x01
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <catch2/catch_test_macros.hpp>

#include "usb/uvc_host.h"
#include "uvc_descriptors_priv.h"

#include "test_parsing_helpers.hpp"
#include "descriptors/customer.hpp"
using namespace customer_camera;

SCENARIO("Camera descriptor parsing: Customer", "[customer][single]")
{
const usb_config_desc_t *cfg = (const usb_config_desc_t *)cfg_desc;

GIVEN("Customer MJPEG") {
uvc_host_stream_format_t formats[] = {
{1280, 720, 15, UVC_VS_FORMAT_MJPEG},
{1280, 720, 10, UVC_VS_FORMAT_MJPEG},
{800, 480, 20, UVC_VS_FORMAT_MJPEG},
{800, 480, 15, UVC_VS_FORMAT_MJPEG},
{640, 480, 25, UVC_VS_FORMAT_MJPEG},
{640, 480, 15, UVC_VS_FORMAT_MJPEG},
{480, 320, 25, UVC_VS_FORMAT_MJPEG},
{480, 320, 15, UVC_VS_FORMAT_MJPEG},
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("Customer Unsupported") {
uvc_host_stream_format_t formats[] = {
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS
{645, 480, 25, UVC_VS_FORMAT_MJPEG}, // Invalid definition
{640, 480, 20, UVC_VS_FORMAT_H264}, // Invalid format
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_NOT_SUPPORTED(cfg, this_format);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <catch2/catch_test_macros.hpp>

#include "usb/uvc_host.h"
#include "uvc_descriptors_priv.h"

#include "test_parsing_helpers.hpp"
#include "descriptors/customer_dual.hpp"
using namespace customer_camera_dual;

SCENARIO("Camera descriptor parsing: Customer dual", "[customer][dual]")
{
const usb_config_desc_t *cfg = (const usb_config_desc_t *)cfg_desc;

GIVEN("Customer dual MJPEG") {
uvc_host_stream_format_t formats[] = {
{720, 1280, 30, UVC_VS_FORMAT_MJPEG},
{720, 1280, 15, UVC_VS_FORMAT_MJPEG},
{720, 1280, 10, UVC_VS_FORMAT_MJPEG},
{720, 1280, 5, UVC_VS_FORMAT_MJPEG},
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("Customer dual h265") {
uvc_host_stream_format_t formats[] = {
FORMAT_H265_30_20_15(640, 480),
{640, 480, 25, UVC_VS_FORMAT_H265},

FORMAT_H265_30_20_15(1280, 720),
{1280, 720, 25, UVC_VS_FORMAT_H265},

FORMAT_H265_30_20_15(640, 480),
{1920, 1080, 25, UVC_VS_FORMAT_H265},

FORMAT_H265_30_20_15(640, 480),
{2560, 1440, 25, UVC_VS_FORMAT_H265},

FORMAT_H265_30_20_15(640, 480),
{2304, 1296, 25, UVC_VS_FORMAT_H265},
};

for (uvc_host_stream_format_t this_format : formats) {
// Here we are testing uvc_index = 1 and expected_interface = 3
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
uint8_t bInterfaceNumber = 0;
uint16_t bcdUVC = 0;
REQUIRE(ESP_OK == uvc_desc_get_streaming_interface_num(cfg, 1, &this_format, &bcdUVC, &bInterfaceNumber));
REQUIRE(bInterfaceNumber == 3);
const usb_intf_desc_t *intf_desc = nullptr;
const usb_ep_desc_t *ep_desc = nullptr;
REQUIRE(ESP_OK == uvc_desc_get_streaming_intf_and_ep(cfg, bInterfaceNumber, &intf_desc, &ep_desc));
REQUIRE(intf_desc != nullptr);
REQUIRE(ep_desc != nullptr);
const uvc_format_desc_t *format_desc = nullptr;
const uvc_frame_desc_t *frame_desc = nullptr;
REQUIRE(ESP_OK == uvc_desc_get_frame_format_by_format(cfg, bInterfaceNumber, &this_format, &format_desc, &frame_desc));
REQUIRE(format_desc != nullptr);
REQUIRE(frame_desc != nullptr);
}
}
}

GIVEN("Customer dual Unsupported") {
uvc_host_stream_format_t formats[] = {
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS
{645, 480, 25, UVC_VS_FORMAT_MJPEG}, // Invalid definition
{640, 480, 20, UVC_VS_FORMAT_H264}, // Invalid format
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_NOT_SUPPORTED(cfg, this_format);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <catch2/catch_test_macros.hpp>

#include "usb/uvc_host.h"
#include "uvc_descriptors_priv.h"

#include "test_parsing_helpers.hpp"
#include "descriptors/elp_h264.hpp"
using namespace elp_h264;

SCENARIO("Camera descriptor parsing: ELP h264", "[elp][h264]")
{
const usb_config_desc_t *cfg = (const usb_config_desc_t *)cfg_desc;

GIVEN("ELP h264 MJPEG") {
uvc_host_stream_format_t formats[] = {
FORMAT_MJPEG_30_25_15(1920, 1080),
FORMAT_MJPEG_30_25_15(1280, 720),
FORMAT_MJPEG_30_25_15(800, 600),
FORMAT_MJPEG_30_25_15(640, 480),
FORMAT_MJPEG_30_25_15(640, 360),
FORMAT_MJPEG_30_25_15(352, 288),
FORMAT_MJPEG_30_25_15(320, 240),
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("ELP h264 Uncompressed") {
uvc_host_stream_format_t formats[] = {
FORMAT_UNCOMPRESSED_30_25_15(640, 480),
{800, 600, 15, UVC_VS_FORMAT_UNCOMPRESSED},
FORMAT_UNCOMPRESSED_30_25_15(352, 288),
FORMAT_UNCOMPRESSED_30_25_15(320, 240),
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("ELP h264 h264") {
uvc_host_stream_format_t formats[] = {
FORMAT_H264_30_25_15(1920, 1080),
FORMAT_H264_30_25_15(1280, 720),
FORMAT_H264_30_25_15(800, 600),
FORMAT_H264_30_25_15(640, 480),
FORMAT_H264_30_25_15(640, 360),
FORMAT_H264_30_25_15(352, 288),
FORMAT_H264_30_25_15(320, 240),
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 2);
}
}
}

GIVEN("ELP h264 Unsupported") {
uvc_host_stream_format_t formats[] = {
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS
{645, 480, 25, UVC_VS_FORMAT_MJPEG}, // Invalid definition
{640, 480, 20, UVC_VS_FORMAT_H265}, // Invalid format
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_NOT_SUPPORTED(cfg, this_format);
}
}
}
}
115 changes: 115 additions & 0 deletions host/class/uvc/usb_host_uvc_2/host_test/main/test_parsing_elp_h265.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <catch2/catch_test_macros.hpp>

#include "usb/uvc_host.h"
#include "uvc_descriptors_priv.h"

#include "test_parsing_helpers.hpp"
#include "descriptors/elp_h265.hpp"
using namespace elp_h265;

SCENARIO("Camera descriptor parsing: ELP h265", "[elp][h265]")
{
const usb_config_desc_t *cfg = (const usb_config_desc_t *)cfg_desc;

GIVEN("ELP h265 MJPEG") {
uvc_host_stream_format_t formats[] = {
FORMAT_MJPEG_30_20_15(640, 360),
FORMAT_MJPEG_30_20_15(640, 480),
FORMAT_MJPEG_30_20_15(960, 540),
FORMAT_MJPEG_30_20_15(1024, 576),
FORMAT_MJPEG_30_20_15(1280, 720),
FORMAT_MJPEG_30_20_15(1920, 1080),
FORMAT_MJPEG_30_20_15(2560, 1440),
FORMAT_MJPEG_30_20_15(3840, 2160),
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("ELP h265 h264") {
uvc_host_stream_format_t formats[] = {
FORMAT_H264_30_20_15(640, 360),
FORMAT_H264_30_20_15(640, 480),
FORMAT_H264_30_20_15(960, 540),
FORMAT_H264_30_20_15(1024, 576),
FORMAT_H264_30_20_15(1280, 720),
FORMAT_H264_30_20_15(1920, 1080),
FORMAT_H264_30_20_15(2560, 1440),
FORMAT_H264_30_20_15(3840, 2160),
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("ELP h265 h265") {
uvc_host_stream_format_t formats[] = {
FORMAT_H265_30_20_15(640, 360),
FORMAT_H265_30_20_15(640, 480),
FORMAT_H265_30_20_15(960, 540),
FORMAT_H265_30_20_15(1024, 576),
FORMAT_H265_30_20_15(1280, 720),
FORMAT_H265_30_20_15(1920, 1080),
FORMAT_H265_30_20_15(2560, 1440),
FORMAT_H265_30_20_15(3840, 2160),
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("ELP h265 Uncompressed") {
uvc_host_stream_format_t formats[] = {
FORMAT_UNCOMPRESSED_30_20_15(640, 360),
FORMAT_UNCOMPRESSED_30_20_15(640, 480),
{960, 540, 15, UVC_VS_FORMAT_UNCOMPRESSED},
{960, 540, 10, UVC_VS_FORMAT_UNCOMPRESSED},
{960, 540, 5, UVC_VS_FORMAT_UNCOMPRESSED},
{1024, 576, 15, UVC_VS_FORMAT_UNCOMPRESSED},
{1024, 576, 10, UVC_VS_FORMAT_UNCOMPRESSED},
{1024, 576, 5, UVC_VS_FORMAT_UNCOMPRESSED},
{1280, 720, 10, UVC_VS_FORMAT_UNCOMPRESSED},
{1280, 720, 5, UVC_VS_FORMAT_UNCOMPRESSED},
{1280, 720, 2, UVC_VS_FORMAT_UNCOMPRESSED},
{1920, 1080, 5, UVC_VS_FORMAT_UNCOMPRESSED},
{1920, 1080, 2, UVC_VS_FORMAT_UNCOMPRESSED},
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_SUPPORTED(cfg, this_format, 1);
}
}
}

GIVEN("ELP h265 Unsupported") {
uvc_host_stream_format_t formats[] = {
{640, 480, 28, UVC_VS_FORMAT_MJPEG}, // Invalid FPS
{645, 480, 25, UVC_VS_FORMAT_MJPEG}, // Invalid definition
{640, 480, 20, UVC_VS_FORMAT_UNDEFINED}, // Invalid format
};

for (uvc_host_stream_format_t this_format : formats) {
SECTION(std::to_string(this_format.h_res) + "x" + std::to_string(this_format.v_res) + "@" + std::to_string(this_format.fps)) {
REQUIRE_FORMAT_NOT_SUPPORTED(cfg, this_format);
}
}
}
}
Loading

0 comments on commit bea7249

Please sign in to comment.