-
Notifications
You must be signed in to change notification settings - Fork 37
fix(uvc): improve uac compatibility and add checks for uvc #308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(uvc): improve uac compatibility and add checks for uvc #308
Conversation
ae92d53 to
efa0fe5
Compare
016831a to
4ef3e94
Compare
fae33ed to
389afe6
Compare
389afe6 to
fd1c730
Compare
tore-espressif
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TDA-2030 UAC changes look OK. Could you please provide more information about the bInterval!=1 issue? Maybe there is a better way to fix it.
I have more comments for the UVC part, so if you want to speed up the release, please split this PR into 2. One for UAC and one for UVC, thanks!
| @@ -0,0 +1,12 @@ | |||
| | Supported Targets | ESP32-S2 | ESP32-S3 | ESP32-P4 | | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should work also on H4
| | Supported Targets | ESP32-S2 | ESP32-S3 | ESP32-P4 | | ||
| | ----------------- | -------- | -------- | -------- | | ||
|
|
||
| # UVC driver example: Video stream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outdated description.
It would be nice to provide a simple 'how-to' use this example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
| if (is_full_speed && ep_desc->bInterval != 1) { | ||
| // Full-Speed isochronous endpoints must have bInterval = 1 | ||
| uint8_t *_bInterval = (uint8_t *) & (ep_desc->bInterval); | ||
| // Check if we can modify the bInterval value | ||
| if (esp_ptr_in_dram(_bInterval) || esp_ptr_in_diram_dram(_bInterval) | ||
| #if CONFIG_SPIRAM | ||
| || esp_ptr_in_psram(_bInterval) | ||
| #endif | ||
| ) { | ||
| ESP_LOGW(TAG, "UAC Full-Speed device, Endpoint %d, bInterval %d, set to 1", USB_EP_DESC_GET_EP_NUM(ep_desc), ep_desc->bInterval); | ||
| *_bInterval = 1; | ||
| } else { | ||
| ESP_LOGW(TAG, "UAC Full-Speed device, Endpoint %d, bInterval %d, can't set to 1", USB_EP_DESC_GET_EP_NUM(ep_desc), ep_desc->bInterval); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very risky, non-standard way that deserves explanation comment.
BTW, have you found the underlaying problem? If the device needs bInterval=2 why doesn't it work correctly with 2ms polling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this operation is risky. I suspect the reason is that the camera descriptor was not modified for full-speed, while in high-speed, bInterval=4 is exactly a 1ms interval.
| case UAC_HOST_DEVICE_EVENT_RX_DONE: | ||
| if (s_mic_dev_handle && s_mic_record_buf && s_mic_recording) { | ||
| uint32_t rx_size = 0; | ||
| esp_err_t ret = ESP_OK; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a build warning (treated as an error in CI)
esp-usb/host/class/uac/usb_host_uac/examples/audio_player/main/main.c:414:35: warning: unused variable 'ret' [-Wunused-variable]
414 | esp_err_t ret = ESP_OK;
| ^~~
| bool "Playback audio from microphone" | ||
| default n | ||
| help | ||
| Enabled this to playback audio from microphone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo:
| Enabled this to playback audio from microphone | |
| Enable this to playback audio from microphone |
| CONFIG_ESP32P4_REV_MIN_0=y | ||
| CONFIG_RTC_CLK_SRC_EXT_CRYS=y | ||
| CONFIG_RTC_CLK_CAL_CYCLES=1024 | ||
| CONFIG_SPIRAM=y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we leave this config in the sdkconfig.defaults ? It's shared for all the targets.
| // Check for start of new frame | ||
| const uvc_payload_header_t *payload_header = (const uvc_payload_header_t *)payload; | ||
| if (!uvc_frame_payload_header_validate(payload_header, isoc_desc->actual_num_bytes)) { | ||
| ESP_LOGD(TAG, "invalid UVC payload header, %02x, %02x, len:%d", payload[0], payload[1], transfer->actual_num_bytes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be isoc_desc ? Since you are processing individual isoc packets instead of the whole transfer.
| ESP_LOGD(TAG, "invalid UVC payload header, %02x, %02x, len:%d", payload[0], payload[1], transfer->actual_num_bytes); | |
| ESP_LOGD(TAG, "invalid UVC payload header, %02x, %02x, len:%d", payload[0], payload[1], isoc_desc->actual_num_bytes); |
| // Query device speed once to decide whether to force bInterval for Full-Speed | ||
| usb_device_info_t dev_info_local; | ||
| ESP_RETURN_ON_ERROR(usb_host_device_info(uac_device->dev_hdl, &dev_info_local), TAG, "Failed to get device info"); | ||
| ESP_RETURN_ON_FALSE(dev_info_local.speed != USB_SPEED_LOW, ESP_ERR_NOT_SUPPORTED, TAG, "Low-Speed device not supported"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this is valid point. You allocate memory for uac_iface in the beginning of the function. If the device is LS, the function just returns with an error code, leaving the memory allocated.
You could use one of the ESP_GOTO_ macros, to jump to fail: lablel and free the allocated memory.
| get_wav_data(iface_alt_params.sample_freq[0], &player_config.pcm_ptr, &player_config.pcm_size, | ||
| &stm_config.sample_freq, &stm_config.channels, &stm_config.bit_resolution); | ||
|
|
||
| ESP_LOGI(TAG, "Start UAC speaker with %"PRIu32" Hz, %u bits, %s (PCM bytes=%u)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tired running the example with some UAC device.
My uac device has a sample rate of 48000, whereas this example only supports 8000 and 16000.
The get_wav_data returned error in this case, yet still the code continued and tired to start the device (even though the device is not supported) by this example.
I (753) usb_audio_player: UAC Device connected: SPK
find UAC 1.0 Speaker device
interface number: 1
total alt interfaces number: 1
--------alt interface[1]---------
channels = 2
bit_resolution = 16
sample_freq:
48000
44100
E (763) usb_audio_player: Unsupported sample rate: 48000
I (768) usb_audio_player: Start UAC speaker with 0 Hz, 0 bits, Stereo (PCM bytes=0)
E (775) uac-host: uac_host_device_start(2289): Invalid bit resolution
ESP_ERROR_CHECK failed: esp_err_t 0x102 (ESP_ERR_INVALID_ARG) at 0x4000a402
--- 0x4000a402: uac_lib_task at /home/peter/esp/esp-usb/host/class/uac/usb_host_uac/examples/audio_player/main/main.c:346
And the esp kept resetting. I think the examples should be build in a way, that they crash only when some serious issue happens, not when you insert "unsupported device".
I think adding more wav files with different sample rate is not necessary here. The example could have shown something like: W usb_audio_player: Unsupported device, try different device or something similar.
| #include "usb/uac_host.h" | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to emphasize, that some configs are used in the code.
| #include "sdkconfig.h" |
| #include "sdkconfig.h" | ||
| #include "esp_log.h" | ||
| #include "esp_check.h" | ||
| #include "esp_memory_utils.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To pass the linux host test, this head should be closed in
#if !CONFIG_IDF_TARGET_LINUX
"esp_memory_utils.h"
#endif
As well as all the functions from the esp_memory_utils.h used in this file. Because the esp_memory_utils.h includes soc.h which is pretty shallow for linux target.
Or update the esp-idf/components/soc/linux/include/soc/soc.h with missing defines. But tis might not be an ideal solution.
A customer's camera has a UAC descriptor with bInterval=4, which would mean sending data every 8 frames. However, it should actually send data every frame for proper audio playback. I limited the camera to full-speed and connected it to my computer, then used a tool to capture USB frames. I found that the computer also ignored bInterval=4 and sent data every 1ms.
I will split this PR into 2 |
Description
UAC
UVC
usb_types_uvc.htoprivate_includedirectoryRelated
Testing
Checklist
Before submitting a Pull Request, please ensure the following:
Note
Fix UAC FS isochronous interval handling and harden UVC streaming with header/SOI validation; add an audio player example; bump UAC to 1.3.3 and UVC to 2.3.2.
UAC (usb_host_uac)
bInterval = 1on Full-Speed isoch endpoints during descriptor parse to prevent playback stutter; reject Low-Speed.USB_EP_DESC_GET_EP_NUM; direction asserts use USB masks.examples/audio_player(embedded WAV playback, optional MIC loopback) with build files, Kconfig, and configs.1.3.3; updateCHANGELOG.md.UVC (usb_host_uvc)
uvc_frame_payload_header_validate()and MJPEG SOI checks; improve BULK EoF handling; validate headers in ISOC; skip tiny/invalid frames; warn on missed EoF.usb_types_uvc.htoprivate_include; update includes across sources and tests.2.3.2; updateCHANGELOG.md.Written by Cursor Bugbot for commit fd1c730. This will update automatically on new commits. Configure here.