Skip to content

Commit 8f0c551

Browse files
committed
fix(uvc): add MJPEG checks for uvc
1 parent 4c7f3ef commit 8f0c551

File tree

10 files changed

+47
-11
lines changed

10 files changed

+47
-11
lines changed

host/class/uvc/usb_host_uvc/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 2.3.2
2+
3+
- Added SOI check to prevent output of corrupted MJPEG frames
4+
- Moved `usb_types_uvc.h` to `private_include` directory
5+
16
## 2.3.1
27

38
- Added support for ESP32-H4

host/class/uvc/usb_host_uvc/idf_component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## IDF Component Manager Manifest File
2-
version: "2.3.1"
2+
version: "2.3.2"
33
description: USB Host UVC driver
44
url: https://github.com/espressif/esp-usb/tree/master/host/class/uvc/usb_host_uvc
55
dependencies:

host/class/uvc/usb_host_uvc/include/esp_private/uvc_control.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#pragma once
88

99
#include "usb/uvc_host.h"
10+
#include "usb_types_uvc.h"
1011

1112
#ifdef __cplusplus
1213
extern "C" {

host/class/uvc/usb_host_uvc/include/usb/uvc_host.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include <stdbool.h>
1010
#include "usb/usb_host.h"
11-
#include "usb/usb_types_uvc.h"
1211
#include "esp_err.h"
1312

1413
// Use this macros for opening a UVC stream with any VID or PID

host/class/uvc/usb_host_uvc/private_include/uvc_descriptors_priv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// So we include only files with USB specification definitions
1111
// This interface is also used in host_tests
1212
#include "usb/usb_types_ch9.h"
13-
#include "usb/usb_types_uvc.h"
13+
#include "usb_types_uvc.h"
1414

1515
#define UVC_DESC_FPS_TO_DWFRAMEINTERVAL(fps) (((fps) != 0) ? 10000000.0f / (fps) : 0)
1616
#define UVC_DESC_DWFRAMEINTERVAL_TO_FPS(dwFrameInterval) (((dwFrameInterval) != 0) ? 10000000.0f / ((float)(dwFrameInterval)) : 0)

host/class/uvc/usb_host_uvc/private_include/uvc_types_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "usb/usb_host.h"
1414
#include "usb/uvc_host.h"
15+
#include "usb_types_uvc.h"
1516

1617
#include "freertos/FreeRTOS.h"
1718
#include "freertos/queue.h"

host/class/uvc/usb_host_uvc/uvc_bulk.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ void bulk_transfer_callback(usb_transfer_t *transfer)
125125
uvc_stream->single_thread.current_frame_id = payload_header->bmHeaderInfo.frame_id;
126126
uvc_stream->single_thread.skip_current_frame = payload_header->bmHeaderInfo.error; // Check for error flag
127127

128+
// Check mjpeg frame start
129+
if (uvc_stream->dynamic.vs_format.format == UVC_VS_FORMAT_MJPEG &&
130+
payload_data[payload_header->bHeaderLength] != 0xff && payload_data[1 + payload_header->bHeaderLength] != 0xd8) {
131+
// We received frame with invalid frame, skip this frame
132+
uvc_stream->single_thread.skip_current_frame = true;
133+
ESP_LOGW(TAG, "invalid MJPEG SOI");
134+
}
135+
128136
// Get free frame buffer for this new frame
129137
UVC_ENTER_CRITICAL();
130138
const bool need_new_frame = (uvc_stream->dynamic.streaming && !uvc_stream->dynamic.current_frame);

host/class/uvc/usb_host_uvc/uvc_control.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#include "uvc_control.h"
1414
#include "usb/usb_types_ch9.h"
15-
#include "usb/usb_types_uvc.h"
15+
#include "usb_types_uvc.h"
1616
#include "uvc_types_priv.h"
1717
#include "uvc_descriptors_priv.h"
1818
#include "uvc_check_priv.h"

host/class/uvc/usb_host_uvc/uvc_isoc.c

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,34 @@ void isoc_transfer_callback(usb_transfer_t *transfer)
8787

8888
// Check for start of new frame
8989
const uvc_payload_header_t *payload_header = (const uvc_payload_header_t *)payload;
90+
// Derive payload data pointer/length once and reuse below
91+
const uint8_t *payload_data = payload + payload_header->bHeaderLength;
92+
const size_t payload_data_len = isoc_desc->actual_num_bytes - payload_header->bHeaderLength;
93+
94+
if (payload_data_len == 0) {
95+
goto next_isoc_packet;
96+
}
97+
98+
// Check for error flag
99+
if (payload_header->bmHeaderInfo.error) {
100+
ESP_LOGW(TAG, "frame error");
101+
uvc_stream->single_thread.skip_current_frame = true;
102+
}
103+
90104
const bool start_of_frame = (uvc_stream->single_thread.current_frame_id != payload_header->bmHeaderInfo.frame_id);
91105
if (start_of_frame) {
92106
// We detected start of new frame. Update Frame ID and start fetching this frame
93107
uvc_stream->single_thread.current_frame_id = payload_header->bmHeaderInfo.frame_id;
94108
uvc_stream->single_thread.skip_current_frame = payload_header->bmHeaderInfo.error;
95109

110+
// Check mjpeg frame start
111+
if (uvc_stream->dynamic.vs_format.format == UVC_VS_FORMAT_MJPEG &&
112+
payload_data[0] != 0xff && payload_data[1] != 0xd8) {
113+
// We received frame with invalid frame, skip this frame
114+
uvc_stream->single_thread.skip_current_frame = true;
115+
ESP_LOGW(TAG, "invalid MJPEG SOI");
116+
}
117+
96118
// Get free frame buffer for this new frame
97119
UVC_ENTER_CRITICAL();
98120
const bool need_new_frame = (uvc_stream->dynamic.streaming && !uvc_stream->dynamic.current_frame);
@@ -115,20 +137,15 @@ void isoc_transfer_callback(usb_transfer_t *transfer)
115137
}
116138
} else {
117139
// We received SoF but current_frame is not NULL: We missed EoF - reset the frame buffer
140+
ESP_EARLY_LOGW(TAG, "missed EoF");
141+
uvc_stream->single_thread.skip_current_frame = true;
118142
uvc_frame_reset(uvc_stream->dynamic.current_frame);
119143
UVC_EXIT_CRITICAL();
120144
}
121145
}
122146

123-
// Check for error flag
124-
if (payload_header->bmHeaderInfo.error) {
125-
uvc_stream->single_thread.skip_current_frame = true;
126-
}
127-
128147
// Add received data to frame buffer
129148
if (!uvc_stream->single_thread.skip_current_frame) {
130-
const uint8_t *payload_data = payload + payload_header->bHeaderLength;
131-
const size_t payload_data_len = isoc_desc->actual_num_bytes - payload_header->bHeaderLength;
132149
uvc_host_frame_t *current_frame = UVC_ATOMIC_LOAD(uvc_stream->dynamic.current_frame);
133150

134151
esp_err_t ret = uvc_frame_add_data(current_frame, payload_data, payload_data_len);
@@ -157,6 +174,11 @@ void isoc_transfer_callback(usb_transfer_t *transfer)
157174
uvc_host_frame_t *this_frame = uvc_stream->dynamic.current_frame;
158175
uvc_stream->dynamic.current_frame = NULL; // Stop writing more data to this frame
159176

177+
if (this_frame && this_frame->data_len <= 2) {
178+
// We received too small frame, skip this frame
179+
uvc_stream->single_thread.skip_current_frame = true;
180+
}
181+
160182
// Determine if we should invoke the frame callback:
161183
// Only invoke the callback if streaming is active, a frame callback exists,
162184
// and we have a valid frame to pass to the user.

0 commit comments

Comments
 (0)