Skip to content

Commit

Permalink
feat(esp32_usb_stream): update usb stream lib
Browse files Browse the repository at this point in the history
  • Loading branch information
lijunru-hub committed Jul 18, 2024
1 parent 1f4970d commit 6534c42
Show file tree
Hide file tree
Showing 24 changed files with 5,739 additions and 263 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# ChangeLog

## v0.1.0 - [2024-7-18]

### Enhancements:

* Supports compilation with ESP-IDF release/v5.1.
* Synchronize code to uvc_steam version 1.4.0.

## v0.0.1 - [2023-11-10]

### Enhancements:
Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
idf_component_register(SRC_DIRS "src/original" "src"
INCLUDE_DIRS "src" "src/original/include" "src/original"
REQUIRES usb esp_ringbuf)

include(package_manager)
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
15 changes: 15 additions & 0 deletions idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: "1.4.0"
targets:
- esp32s2
- esp32s3
description: USB Host streaming driver, support UVC(video) + UAC(audio)
url: https://github.com/espressif/esp-iot-solution/tree/master/components/usb/usb_stream
repository: https://github.com/espressif/esp-iot-solution.git
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/usb/usb_host/usb_stream.html
issues: https://github.com/espressif/esp-iot-solution/issues
dependencies:
idf: ">=4.4.1"
cmake_utilities: "0.5.*"
examples:
- path: ../../../examples/usb/host/usb_camera_mic_spk
- path: ../../../examples/usb/host/usb_camera_lcd_display
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name=ESP32_USB_STREAM
version=0.0.1
version=0.1.0
author=espressif
maintainer=alibukharai
maintainer=lijunru
sentence=ESP32_USB_STREAM is a specialized library created to facilitate the implementation of USB stream functionality on ESP SoCs.
paragraph=This means that it provides a convenient and efficient way to transmit audio and video data through USB connections, making it an invaluable tool for a wide range of applications such as audio and video streaming, data transfer, and more. Currently, it is only competible with ESP32-S2 and ESP32-S3.
category=Other
Expand Down
4 changes: 2 additions & 2 deletions src/USB_STREAM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ USB_STREAM::~USB_STREAM()
{}

// Method to register a user-defined callback function
void USB_STREAM::uvcCamRegisterCb(uvc_frame_callback_t *newFunction, void *cb_arg)
void USB_STREAM::uvcCamRegisterCb(uvc_frame_callback_t newFunction, void *cb_arg)
{
if (newFunction == NULL) {
ESP_LOGE(TAG, "registerCallBack function error\n");
Expand All @@ -59,7 +59,7 @@ static void _camera_frame_cb(uvc_frame_t *frame, void *ptr)
}

// Method to register a user-defined callback function
void USB_STREAM::uacMicRegisterCb(mic_callback_t *newFunction, void *cb_arg)
void USB_STREAM::uacMicRegisterCb(mic_callback_t newFunction, void *cb_arg)
{
if (newFunction == NULL) {
ESP_LOGE(TAG, "registerCallBack function error\n");
Expand Down
8 changes: 4 additions & 4 deletions src/USB_STREAM.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class USB_STREAM {
//Public member variables for storing user-defined callback function and arguments
void *_user_mic_frame_cb_arg = NULL;
void *_user_frame_cb_arg = NULL;
uvc_frame_callback_t *_user_frame_cb = NULL;
mic_callback_t *_user_mic_frame_cb = NULL;
uvc_frame_callback_t _user_frame_cb = NULL;
mic_callback_t _user_mic_frame_cb = NULL;
typedef void (*StateChangeCallback)(usb_stream_state_t event, void *arg);

/**
Expand Down Expand Up @@ -67,7 +67,7 @@ class USB_STREAM {
* @param newFunction Callback function
* @param cb_arg callback args
*/
void uvcCamRegisterCb(uvc_frame_callback_t *newFunction, void *cb_arg);
void uvcCamRegisterCb(uvc_frame_callback_t newFunction, void *cb_arg);

/**
* @brief Configuration for an object
Expand Down Expand Up @@ -185,7 +185,7 @@ class USB_STREAM {
* @param newFunction Callback function
* @param cb_arg callback args
*/
void uacMicRegisterCb(mic_callback_t *newFunction, void *cb_arg);
void uacMicRegisterCb(mic_callback_t newFunction, void *cb_arg);

/**
* @brief Read data from internal mic buffer, the actual size will be returned
Expand Down
219 changes: 207 additions & 12 deletions src/original/descriptor.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -10,11 +10,11 @@
#include <inttypes.h>

#include "esp_log.h"
#include "libuvc_def.h"
#include "usb/usb_host.h"
#include "usb/usb_types_ch9.h"
#include "usb_stream_descriptor.h"

// esp32/tools/esp32-arduino-libs/idf-release_v5.1-6b1f40b9bf/esp32s3/include/usb/include/usb/
void print_device_descriptor(const uint8_t *buff)
{
if (buff == NULL) {
Expand Down Expand Up @@ -91,12 +91,106 @@ void print_uvc_header_desc(const uint8_t *buff, uint8_t sub_class)
#endif
}

void parse_vs_format_mjpeg_desc(const uint8_t *buff, uint8_t *format_idx, uint8_t *frame_num)
struct format_table_entry {
enum uvc_frame_format format;
uint8_t abstract_fmt;
uint8_t guid[16];
int children_count;
enum uvc_frame_format *children;
};

struct format_table_entry *_get_format_entry(enum uvc_frame_format format)
{
#define ABS_FMT(_fmt, _num, ...) \
case _fmt: { \
static enum uvc_frame_format _fmt##_children[] = __VA_ARGS__; \
static struct format_table_entry _fmt##_entry = { \
_fmt, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, _num, _fmt##_children }; \
return &_fmt##_entry; }

#define FMT(_fmt, ...) \
case _fmt: { \
static struct format_table_entry _fmt##_entry = { \
_fmt, 0, __VA_ARGS__, 0, NULL }; \
return &_fmt##_entry; }

switch (format) {
/* Define new formats here */
ABS_FMT(UVC_FRAME_FORMAT_ANY, 2,
{UVC_FRAME_FORMAT_UNCOMPRESSED, UVC_FRAME_FORMAT_COMPRESSED})

ABS_FMT(UVC_FRAME_FORMAT_UNCOMPRESSED, 8, {
UVC_FRAME_FORMAT_YUYV, UVC_FRAME_FORMAT_UYVY, UVC_FRAME_FORMAT_GRAY8,
UVC_FRAME_FORMAT_GRAY16, UVC_FRAME_FORMAT_NV12, UVC_FRAME_FORMAT_P010,
UVC_FRAME_FORMAT_BGR, UVC_FRAME_FORMAT_RGB
})
FMT(UVC_FRAME_FORMAT_YUYV,
{'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_UYVY,
{'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_GRAY8,
{'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_GRAY16,
{'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_NV12,
{'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_P010,
{'P', '0', '1', '0', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_BGR,
{0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70})
FMT(UVC_FRAME_FORMAT_RGB,
{0x7e, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70})
FMT(UVC_FRAME_FORMAT_BY8,
{'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_BA81,
{'B', 'A', '8', '1', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_SGRBG8,
{'G', 'R', 'B', 'G', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_SGBRG8,
{'G', 'B', 'R', 'G', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_SRGGB8,
{'R', 'G', 'G', 'B', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
FMT(UVC_FRAME_FORMAT_SBGGR8,
{'B', 'G', 'G', 'R', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})
ABS_FMT(UVC_FRAME_FORMAT_COMPRESSED, 2,
{UVC_FRAME_FORMAT_MJPEG, UVC_FRAME_FORMAT_H264})
FMT(UVC_FRAME_FORMAT_MJPEG,
{'M', 'J', 'P', 'G'})
FMT(UVC_FRAME_FORMAT_H264,
{'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71})

default:
return NULL;
}

#undef ABS_FMT
#undef FMT
}

static enum uvc_frame_format uvc_frame_format_for_guid(const uint8_t guid[16])
{
struct format_table_entry *format;
enum uvc_frame_format fmt;

for (fmt = 0; fmt < UVC_FRAME_FORMAT_COUNT; ++fmt) {
format = _get_format_entry(fmt);
if (!format || format->abstract_fmt) {
continue;
}
if (!memcmp(format->guid, guid, 16)) {
return format->format;
}
}

return UVC_FRAME_FORMAT_UNKNOWN;
}

void parse_vs_format_mjpeg_desc(const uint8_t *buff, uint8_t *format_idx, uint8_t *frame_num, enum uvc_frame_format *fmt)
{
if (buff == NULL) {
return;
}
const vs_format_desc_t *desc = (const vs_format_desc_t *) buff;
const vs_format_mjpeg_desc_t *desc = (const vs_format_mjpeg_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** VS Format MJPEG Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
Expand All @@ -123,14 +217,17 @@ void parse_vs_format_mjpeg_desc(const uint8_t *buff, uint8_t *format_idx, uint8_
if (frame_num) {
*frame_num = desc->bNumFrameDescriptors;
}
if (fmt) {
*fmt = UVC_FRAME_FORMAT_MJPEG;
}
}

void parse_vs_frame_mjpeg_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t *width, uint16_t *heigh, uint8_t *interval_type, const uint32_t **pp_interval, uint32_t *dflt_interval)
void parse_vs_frame_mjpeg_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t *width, uint16_t *height, uint8_t *interval_type, const uint32_t **pp_interval, uint32_t *dflt_interval)
{
if (buff == NULL) {
return;
}
const vs_frame_desc_t *desc = (const vs_frame_desc_t *) buff;
const vs_frame_mjpeg_desc_t *desc = (const vs_frame_mjpeg_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** VS MJPEG Frame Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
Expand Down Expand Up @@ -160,6 +257,104 @@ void parse_vs_frame_mjpeg_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t
} else {
// Discrete Frame Intervals
size_t num_of_intervals = (desc->bLength - 26) / 4;
assert(num_of_intervals == desc->bFrameIntervalType); // num_of_intervals should same as bFrameIntervalType
uint32_t *interval = (uint32_t *)&desc->dwFrameInterval;
for (int i = 0; i < num_of_intervals; ++i) {
printf("\tFrameInterval[%d] %"PRIu32"\n", i, interval[i]);
}
}
#endif
if (width) {
*width = desc->wWidth;
}
if (height) {
*height = desc->wHeigh;
}
if (frame_idx) {
*frame_idx = desc->bFrameIndex;
}
if (interval_type) {
*interval_type = desc->bFrameIntervalType;
}
if (pp_interval) {
*pp_interval = &(desc->dwFrameInterval);
}
if (dflt_interval) {
*dflt_interval = desc->dwDefaultFrameInterval;
}
}

void parse_vs_format_frame_based_desc(const uint8_t *buff, uint8_t *format_idx, uint8_t *frame_num, enum uvc_frame_format *fmt)
{
if (buff == NULL) {
return;
}
const vs_format_frame_based_desc_t *desc = (const vs_format_frame_based_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** VS Format Frame-Based Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
#endif
printf("\tbFormatIndex 0x%x\n", desc->bFormatIndex);
printf("\tbNumFrameDescriptors %u\n", desc->bNumFrameDescriptors);
printf("\tguidFormat %.*s\n", 16, desc->guidFormat);
printf("\tbDefaultFrameIndex %u\n", desc->bDefaultFrameIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbAspectRatioX %u\n", desc->bAspectRatioX);
printf("\tbAspectRatioY %u\n", desc->bAspectRatioY);
printf("\tbmInterlaceFlags 0x%x\n", desc->bmInterlaceFlags);
printf("\tbCopyProtect %u\n", desc->bCopyProtect);
#endif
#endif
if (format_idx) {
*format_idx = desc->bFormatIndex;
}
if (frame_num) {
*frame_num = desc->bNumFrameDescriptors;
}
if (fmt) {
*fmt = uvc_frame_format_for_guid(desc->guidFormat);
}
}

void parse_vs_frame_frame_based_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t *width, uint16_t *height, uint8_t *interval_type, const uint32_t **pp_interval, uint32_t *dflt_interval)
{
if (buff == NULL) {
return;
}
const vs_frame_frame_based_desc_t *desc = (const vs_frame_frame_based_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** VS Frame-Based Frame Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
#endif
printf("\tbFrameIndex 0x%x\n", desc->bFrameIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbmCapabilities 0x%x\n", desc->bmCapabilities);
#endif
printf("\twWidth %u\n", desc->wWidth);
printf("\twHeigh %u\n", desc->wHeigh);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tdwMinBitRate %"PRIu32"\n", desc->dwMinBitRate);
printf("\tdwMaxBitRate %"PRIu32"\n", desc->dwMaxBitRate);
printf("\tdwDefaultFrameInterval %"PRIu32"\n", desc->dwDefaultFrameInterval);
printf("\tbFrameIntervalType %u\n", desc->bFrameIntervalType);
printf("\tdwBytesPerLine %"PRIu32"\n", desc->dwBytesPerLine);
#endif

if (desc->bFrameIntervalType == 0) {
// Continuous Frame Intervals
printf("\tdwMinFrameInterval %"PRIu32"\n", desc->dwMinFrameInterval);
printf("\tdwMaxFrameInterval %"PRIu32"\n", desc->dwMaxFrameInterval);
printf("\tdwFrameIntervalStep %"PRIu32"\n", desc->dwFrameIntervalStep);
} else {
// Discrete Frame Intervals
size_t num_of_intervals = (desc->bLength - 26) / 4;
assert(num_of_intervals == desc->bFrameIntervalType); // num_of_intervals should same as bFrameIntervalType
uint32_t *interval = (uint32_t *)&desc->dwFrameInterval;
for (int i = 0; i < num_of_intervals; ++i) {
printf("\tFrameInterval[%d] %"PRIu32"\n", i, interval[i]);
Expand All @@ -169,8 +364,8 @@ void parse_vs_frame_mjpeg_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t
if (width) {
*width = desc->wWidth;
}
if (heigh) {
*heigh = desc->wHeigh;
if (height) {
*height = desc->wHeigh;
}
if (frame_idx) {
*frame_idx = desc->bFrameIndex;
Expand Down Expand Up @@ -260,8 +455,8 @@ void print_intf_desc(const uint8_t *buff)
printf("\tbAlternateSetting %d\n", intf_desc->bAlternateSetting);
printf("\tbNumEndpoints %d\n", intf_desc->bNumEndpoints);
printf("\tbInterfaceClass 0x%x (%s)\n", intf_desc->bInterfaceClass,
intf_desc->bInterfaceClass==USB_CLASS_VIDEO?"Video":
(intf_desc->bInterfaceClass==USB_CLASS_AUDIO?"Audio":"Unknown"));
intf_desc->bInterfaceClass == USB_CLASS_VIDEO ? "Video" :
(intf_desc->bInterfaceClass == USB_CLASS_AUDIO ? "Audio" : "Unknown"));
printf("\tbInterfaceSubClass 0x%x\n", intf_desc->bInterfaceSubClass);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbInterfaceProtocol 0x%x\n", intf_desc->bInterfaceProtocol);
Expand Down Expand Up @@ -429,7 +624,7 @@ void parse_ac_feature_desc(const uint8_t *buff, uint8_t *source_idx, uint8_t *fe
printf("\tbUnitID %d\n", desc->bUnitID);
printf("\tbSourceID %d\n", desc->bSourceID);
printf("\tbControlSize %d\n", desc->bControlSize);
for (size_t i = 0; i < (desc->bLength-7)/desc->bControlSize; i += desc->bControlSize) {
for (size_t i = 0; i < (desc->bLength - 7) / desc->bControlSize; i += desc->bControlSize) {
printf("\tbmaControls[ch%d] 0x%x\n", i, desc->bmaControls[i]);
}
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
Expand All @@ -443,7 +638,7 @@ void parse_ac_feature_desc(const uint8_t *buff, uint8_t *source_idx, uint8_t *fe
*source_idx = desc->bSourceID;
}
uint8_t ch_num = 0;
for (size_t i = 0; i < (desc->bLength-7)/desc->bControlSize; i += desc->bControlSize) {
for (size_t i = 0; i < (desc->bLength - 7) / desc->bControlSize; i += desc->bControlSize) {
if ((desc->bmaControls[i] & AUDIO_FEATURE_CONTROL_VOLUME) && volume_ch) {
*volume_ch = *volume_ch | (1 << ch_num);
}
Expand Down
Loading

0 comments on commit 6534c42

Please sign in to comment.