summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZixun LI <[email protected]>2025-11-14 20:04:07 +0100
committerGitHub <[email protected]>2025-11-14 20:04:07 +0100
commit20c36442246279d884a6b61066fda6778c1c06ee (patch)
tree7f534d628d0fecb5110a6f4441265651c079fa45
parentfc5415bff6f177ad5f122f38dd937f577042bb32 (diff)
parentac903db1cee942232bcf7548a04ae6f9b952f08b (diff)
Merge pull request #3322 from embedded-ideas/master
Bufferless (on the fly) video data generation for the video device class
-rw-r--r--examples/device/video_capture/src/main.c67
-rw-r--r--examples/device/video_capture/src/tusb_config.h1
-rw-r--r--src/class/video/video_device.c22
-rw-r--r--src/class/video/video_device.h20
4 files changed, 93 insertions, 17 deletions
diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c
index adcfb9f95..ffa2a7afa 100644
--- a/examples/device/video_capture/src/main.c
+++ b/examples/device/video_capture/src/main.c
@@ -56,6 +56,20 @@ void video_task(void* param);
void freertos_init(void);
#endif
+#if !defined(CFG_EXAMPLE_VIDEO_READONLY) || defined(CFG_EXAMPLE_VIDEO_BUFFERLESS)
+/* EBU color bars: https://stackoverflow.com/questions/6939422 */
+static uint8_t const bar_color[8][4] = {
+ /* Y, U, Y, V */
+ { 235, 128, 235, 128}, /* 100% White */
+ { 219, 16, 219, 138}, /* Yellow */
+ { 188, 154, 188, 16}, /* Cyan */
+ { 173, 42, 173, 26}, /* Green */
+ { 78, 214, 78, 230}, /* Magenta */
+ { 63, 102, 63, 240}, /* Red */
+ { 32, 240, 32, 118}, /* Blue */
+ { 16, 128, 16, 128}, /* Black */
+};
+#endif
//--------------------------------------------------------------------+
// Main
@@ -111,12 +125,43 @@ void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
+#ifdef CFG_EXAMPLE_VIDEO_BUFFERLESS
+
+#ifndef CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
+ #error Demo only supports YUV2 please define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
+#endif
+
+void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request)
+{
+ static uint32_t frame_counter = 0;
+ (void)ctl_idx;
+ (void)stm_idx;
+
+ /* Offset will be zero at the start of a new frame */
+ if (!request->offset) frame_counter++;
+
+ for (size_t buf_pos = 0; buf_pos < request->length; buf_pos += 2) {
+
+ /* Position within the current line (pixel relative) */
+ int line_pos = ((request->offset + buf_pos)>>1) % FRAME_WIDTH;
+
+ /* Choose color based on the position and change the table offset every 4 frames */
+ const uint8_t* color = bar_color[(line_pos/(FRAME_WIDTH / 8) + (frame_counter>>2)) % 8];
+
+ /* Copy pixel data for odd or even pixels */
+ memcpy(&((uint8_t*)request->buf)[buf_pos], &color[(line_pos & 1) ? 2 : 0], 2);
+ }
+
+}
+#endif
+
//--------------------------------------------------------------------+
// USB Video
//--------------------------------------------------------------------+
static unsigned frame_num = 0;
static unsigned tx_busy = 0;
static unsigned interval_ms = 1000 / FRAME_RATE;
+#ifndef CFG_EXAMPLE_VIDEO_BUFFERLESS
#ifdef CFG_EXAMPLE_VIDEO_READONLY
// For mcus that does not have enough SRAM for frame buffer, we use fixed frame data.
@@ -145,18 +190,6 @@ static struct {
static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8];
static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
- /* EBU color bars: https://stackoverflow.com/questions/6939422 */
- static uint8_t const bar_color[8][4] = {
- /* Y, U, Y, V */
- { 235, 128, 235, 128}, /* 100% White */
- { 219, 16, 219, 138}, /* Yellow */
- { 188, 154, 188, 16}, /* Cyan */
- { 173, 42, 173, 26}, /* Green */
- { 78, 214, 78, 230}, /* Magenta */
- { 63, 102, 63, 240}, /* Red */
- { 32, 240, 32, 118}, /* Blue */
- { 16, 128, 16, 128}, /* Black */
- };
uint8_t* p;
/* Generate the 1st line */
@@ -183,6 +216,8 @@ static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
#endif
+#endif /* NDEF CFG_EXAMPLE_VIDEO_BUFFERLESS */
+
static void video_send_frame(void) {
static unsigned start_ms = 0;
static unsigned already_sent = 0;
@@ -197,7 +232,9 @@ static void video_send_frame(void) {
already_sent = 1;
tx_busy = 1;
start_ms = board_millis();
-#ifdef CFG_EXAMPLE_VIDEO_READONLY
+#if defined(CFG_EXAMPLE_VIDEO_BUFFERLESS)
+ tud_video_n_frame_xfer(0, 0, NULL, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
+#elif defined (CFG_EXAMPLE_VIDEO_READONLY)
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
@@ -220,7 +257,9 @@ static void video_send_frame(void) {
start_ms += interval_ms;
tx_busy = 1;
-#ifdef CFG_EXAMPLE_VIDEO_READONLY
+#if defined(CFG_EXAMPLE_VIDEO_BUFFERLESS)
+ tud_video_n_frame_xfer(0, 0, NULL, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
+#elif defined(CFG_EXAMPLE_VIDEO_READONLY)
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h
index 390152e16..1aa1a5ce7 100644
--- a/examples/device/video_capture/src/tusb_config.h
+++ b/examples/device/video_capture/src/tusb_config.h
@@ -110,6 +110,7 @@
//#define CFG_EXAMPLE_VIDEO_READONLY
//#define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
+//#define CFG_EXAMPLE_VIDEO_BUFFERLESS
#ifdef __cplusplus
}
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 14eba86fb..844a5e672 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -214,6 +214,12 @@ TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
return VIDEO_ERROR_NONE;
}
+TU_ATTR_WEAK void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request) {
+ (void) ctl_idx;
+ (void) stm_idx;
+ (void) request;
+}
+
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -874,7 +880,16 @@ static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm, uint
}
TU_ASSERT(pkt_len >= hdr_len);
uint_fast16_t data_len = pkt_len - hdr_len;
- memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ if (stm->buffer) {
+ memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ } else {
+ tud_video_payload_request_t rqst = {
+ .buf = &ep_buf[hdr_len],
+ .length = data_len,
+ .offset = stm->offset
+ };
+ tud_video_prepare_payload_cb(stm->index_vc, stm->index_vs, &rqst);
+ }
stm->offset += data_len;
remaining -= data_len;
if (!remaining) {
@@ -1255,13 +1270,14 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
- if (NULL == buffer || 0 == bufsize) {
+ if (0 == bufsize) {
return false;
}
+
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[ctl_idx];
- if (NULL == stm || 0 == stm->desc.ep[0] || stm->buffer) {
+ if (NULL == stm || 0 == stm->desc.ep[0] || stm->bufsize) {
return false;
}
if (stm->state == VS_STATE_PROBING) {
diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h
index 2b41c3bfe..f14555e4f 100644
--- a/src/class/video/video_device.h
+++ b/src/class/video/video_device.h
@@ -35,6 +35,16 @@
extern "C" {
#endif
+
+//--------------------------------------------------------------------+
+// Payload request
+//--------------------------------------------------------------------+
+typedef struct TU_ATTR_PACKED {
+ void* buf; /* Payload buffer to be filled */
+ size_t length; /* Length of the requested data in bytes */
+ size_t offset; /* Offset within the frame (in bytes) */
+} tud_video_payload_request_t;
+
//--------------------------------------------------------------------+
// Application API (Multiple Ports)
// CFG_TUD_VIDEO > 1
@@ -83,6 +93,16 @@ int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod);
int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
video_probe_and_commit_control_t const *parameters);
+/** Invoked if buffer is set to NULL (allows bufferless on the fly data generation)
+ *
+ * @param[in] ctl_idx Destination control interface index
+ * @param[in] stm_idx Destination streaming interface index
+ * @param[out] payload_buf Payload storage buffer (target buffer for requested data)
+ * @param[in] payload_size Size of payload_buf (requested data size)
+ * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize)
+ * @return video_error_code_t */
+void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request);
+
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+