Skip to content

Commit d12370e

Browse files
committed
subsys: usb: host: enhance host video stream transfer
Adjust video stream callback function to be more robust. Use multiple primes to improve the throughput. Signed-off-by: Aiden Hu <[email protected]>
1 parent 1ce0e22 commit d12370e

File tree

1 file changed

+187
-100
lines changed

1 file changed

+187
-100
lines changed

subsys/usb/host/class/usbh_uvc.c

Lines changed: 187 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ struct uvc_device {
128128
struct k_poll_signal *sig;
129129
/** Byte offset within the currently transmitted video buffer */
130130
size_t vbuf_offset;
131+
/** Current video buffer being filled */
132+
struct video_buffer *current_vbuf;
133+
/** Expected frame ID for synchronization */
134+
uint8_t expect_frame_id;
135+
/** Flag to discard first incomplete frame */
136+
uint8_t discard_first_frame;
137+
/** Count of discarded frames */
138+
uint32_t discard_frame_cnt;
139+
/** Number of transfers to prime initially */
140+
uint8_t multi_prime_cnt;
131141
/** Number of completed transfers for current frame */
132142
size_t transfer_count;
133143
/** USB camera control parameters */
@@ -2235,57 +2245,52 @@ static int uvc_host_remove_payload_header(struct net_buf *buf, struct video_buff
22352245
* @return 0 on success, negative error code on failure
22362246
*/
22372247
static int uvc_host_initiate_transfer(struct uvc_device *uvc_dev,
2238-
struct video_buffer *const vbuf)
2248+
struct video_buffer *const vbuf)
22392249
{
2240-
struct net_buf *buf;
2241-
struct uhc_transfer *xfer;
2242-
int ret;
2243-
2244-
/* Validate input parameters */
2245-
if (!vbuf || !uvc_dev || !uvc_dev->current_stream_iface_info.current_stream_ep) {
2246-
LOG_ERR("Invalid parameters for transfer initiation");
2247-
return -EINVAL;
2248-
}
2249-
2250-
LOG_DBG("Initiating transfer: ep=0x%02x, vbuf=%p",
2251-
uvc_dev->current_stream_iface_info.current_stream_ep->bEndpointAddress, vbuf);
2252-
2253-
/* Allocate USB transfer with callback */
2254-
xfer = usbh_xfer_alloc(uvc_dev->udev, uvc_dev->current_stream_iface_info.current_stream_ep->bEndpointAddress,
2255-
uvc_host_stream_iso_req_cb, uvc_dev);
2256-
if (!xfer) {
2257-
LOG_ERR("Failed to allocate transfer");
2258-
return -ENOMEM;
2259-
}
2260-
2261-
/* Allocate transfer buffer with maximum packet size */
2262-
buf = usbh_xfer_buf_alloc(&uvc_dev->udev, uvc_dev->current_stream_iface_info.cur_ep_mps_mult);
2263-
if (!buf) {
2264-
LOG_ERR("Failed to allocate buffer");
2265-
usbh_xfer_free(uvc_dev->udev, xfer);
2266-
return -ENOMEM;
2267-
}
2268-
2269-
buf->len = 0;
2270-
2271-
/* Reset buffer offset and associate video buffer with transfer */
2272-
uvc_dev->vbuf_offset = 0;
2273-
vbuf->bytesused = 0;
2274-
memset(vbuf->buffer, 0, vbuf->size);
2275-
2276-
/* Save video buffer pointer in transfer's user data */
2277-
*(void **)net_buf_user_data(buf) = vbuf;
2278-
xfer->buf = buf;
2279-
2280-
ret = usbh_xfer_enqueue(uvc_dev->udev, xfer);
2281-
if (ret != 0) {
2282-
LOG_ERR("Enqueue failed: ret=%d", ret);
2283-
net_buf_unref(buf);
2284-
usbh_xfer_free(uvc_dev->udev, xfer);
2285-
return ret;
2286-
}
2287-
2288-
return 0;
2250+
struct net_buf *buf;
2251+
struct uhc_transfer *xfer;
2252+
int ret;
2253+
2254+
/* Validate input parameters */
2255+
if (!vbuf || !uvc_dev || !uvc_dev->current_stream_iface_info.current_stream_ep) {
2256+
LOG_ERR("Invalid parameters for transfer initiation");
2257+
return -EINVAL;
2258+
}
2259+
2260+
LOG_DBG("Initiating transfer: ep=0x%02x, vbuf=%p",
2261+
uvc_dev->current_stream_iface_info.current_stream_ep->bEndpointAddress, vbuf);
2262+
2263+
/* Allocate USB transfer with callback */
2264+
xfer = usbh_xfer_alloc(uvc_dev->udev,
2265+
uvc_dev->current_stream_iface_info.current_stream_ep->bEndpointAddress,
2266+
uvc_host_stream_iso_req_cb, uvc_dev);
2267+
if (!xfer) {
2268+
LOG_ERR("Failed to allocate transfer");
2269+
return -ENOMEM;
2270+
}
2271+
2272+
/* Allocate transfer buffer with maximum packet size */
2273+
buf = usbh_xfer_buf_alloc(&uvc_dev->udev,
2274+
uvc_dev->current_stream_iface_info.cur_ep_mps_mult);
2275+
if (!buf) {
2276+
LOG_ERR("Failed to allocate buffer");
2277+
usbh_xfer_free(uvc_dev->udev, xfer);
2278+
return -ENOMEM;
2279+
}
2280+
2281+
buf->len = 0;
2282+
uvc_dev->vbuf_offset = 0;
2283+
xfer->buf = buf;
2284+
2285+
ret = usbh_xfer_enqueue(uvc_dev->udev, xfer);
2286+
if (ret != 0) {
2287+
LOG_ERR("Enqueue failed: ret=%d", ret);
2288+
net_buf_unref(buf);
2289+
usbh_xfer_free(uvc_dev->udev, xfer);
2290+
return ret;
2291+
}
2292+
2293+
return 0;
22892294
}
22902295

22912296
/**
@@ -2312,7 +2317,6 @@ static int uvc_host_continue_transfer(struct uvc_device *uvc_dev, struct uhc_tra
23122317
}
23132318

23142319
buf->len = 0;
2315-
*(void **)net_buf_user_data(buf) = vbuf;
23162320
xfer->buf = buf;
23172321

23182322
ret = usbh_xfer_enqueue(uvc_dev->udev, xfer);
@@ -2330,6 +2334,7 @@ static int uvc_host_continue_transfer(struct uvc_device *uvc_dev, struct uhc_tra
23302334
*
23312335
* Handles completion of isochronous video data transfers. Processes
23322336
* received video data, removes UVC headers, and manages frame completion.
2337+
* Only processes data with matching frame ID to ensure frame integrity.
23332338
*
23342339
* @param dev Pointer to USB device
23352340
* @param xfer Pointer to completed transfer
@@ -2339,10 +2344,15 @@ static int uvc_host_stream_iso_req_cb(struct usb_device *const dev, struct uhc_t
23392344
{
23402345
struct uvc_device *uvc_dev = (struct uvc_device *)xfer->priv;
23412346
struct net_buf *buf = xfer->buf;
2342-
struct video_buffer *vbuf = *(struct video_buffer **)net_buf_user_data(buf);
2347+
struct video_buffer *vbuf = uvc_dev->current_vbuf;
23432348
struct uvc_payload_header *payload_header;
2349+
uint32_t headLength;
2350+
uint32_t dataSize;
23442351
uint8_t endOfFrame;
2345-
uint32_t payload_len;
2352+
uint8_t frame_id;
2353+
uint32_t presentationTime;
2354+
static uint8_t s_savePicture = 0;
2355+
static uint32_t s_currentFrameTimeStamp = 0;
23462356

23472357
/* Validate callback parameters */
23482358
if (!buf || !uvc_dev) {
@@ -2353,62 +2363,126 @@ static int uvc_host_stream_iso_req_cb(struct usb_device *const dev, struct uhc_t
23532363
/* Handle transfer completion status */
23542364
if (xfer->err == -ECONNRESET) {
23552365
LOG_INF("ISO transfer canceled");
2366+
goto cleanup;
23562367
} else if (xfer->err) {
23572368
LOG_WRN("ISO request failed, err %d", xfer->err);
2358-
} else {
2359-
LOG_DBG("ISO request finished, len=%u", buf->len);
2369+
goto cleanup;
23602370
}
23612371

2362-
/* Process received video data if present */
2363-
if (buf->len > 0 && vbuf)
2364-
{
2365-
/* Extract frame end marker from payload header */
2366-
payload_header = (struct uvc_payload_header *)buf->data;
2367-
endOfFrame = payload_header->bmHeaderInfo & UVC_BMHEADERINFO_END_OF_FRAME;
2368-
/* Remove UVC header and extract payload data */
2369-
payload_len = uvc_host_remove_payload_header(buf, vbuf);
2370-
if (payload_len < 0) {
2371-
LOG_ERR("Header removal failed: %d", payload_len);
2372-
goto cleanup;
2373-
}
2374-
2375-
/* Update video buffer with processed data */
2376-
vbuf->bytesused += payload_len;
2377-
uvc_dev->vbuf_offset = vbuf->bytesused;
2378-
2379-
LOG_DBG("Processed %u payload bytes, total: %u, EOF: %u",
2380-
payload_len, vbuf->bytesused, endOfFrame);
2381-
2382-
/* Handle frame completion */
2383-
if (endOfFrame) {
2384-
LOG_INF("Frame completed: %u bytes", vbuf->bytesused);
2385-
/* Release network buffer reference */
2386-
net_buf_unref(buf);
2387-
/* Move completed buffer from input to output queue */
2388-
k_fifo_get(&uvc_dev->fifo_in, K_NO_WAIT);
2389-
k_fifo_put(&uvc_dev->fifo_out, vbuf);
2390-
2391-
/* Clean up transfer resources */
2392-
uvc_dev->vbuf_offset = 0;
2393-
usbh_xfer_free(dev, xfer);
2394-
uvc_dev->transfer_count = 0;
2395-
2396-
/* Signal frame completion to application */
2397-
LOG_DBG("Raising VIDEO_BUF_DONE signal");
2398-
k_poll_signal_raise(uvc_dev->sig, VIDEO_BUF_DONE);
2399-
if ((vbuf = k_fifo_peek_head(&uvc_dev->fifo_in)) != NULL) {
2400-
vbuf->bytesused = 0;
2401-
uvc_host_initiate_transfer(uvc_dev, vbuf);
2372+
payload_header = (struct uvc_payload_header *)buf->data;
2373+
endOfFrame = payload_header->bmHeaderInfo & UVC_BMHEADERINFO_END_OF_FRAME;
2374+
frame_id = payload_header->bmHeaderInfo & UVC_BMHEADERINFO_FRAMEID;
2375+
presentationTime = sys_le32_to_cpu(payload_header->dwPresentationTime);
2376+
headLength = payload_header->bHeaderLength;
2377+
2378+
if (buf->len > 0) {
2379+
/* the standard header is 12 bytes */
2380+
if (buf->len > 11) {
2381+
dataSize = buf->len - headLength;
2382+
/* there is payload for this transfer */
2383+
if (dataSize) {
2384+
if (vbuf->bytesused == 0U) {
2385+
if (s_currentFrameTimeStamp != presentationTime) {
2386+
s_currentFrameTimeStamp = presentationTime;
2387+
}
2388+
}
2389+
/* presentation time should be the same for the same frame, if not, discard this picture */
2390+
else if (presentationTime != s_currentFrameTimeStamp) {
2391+
s_savePicture = 0;
2392+
}
2393+
2394+
/* the current picture buffers are not available, now discard the receiving picture */
2395+
if ((!vbuf && s_savePicture)) {
2396+
s_savePicture = 0;
2397+
} else if (s_savePicture) {
2398+
if (dataSize > (vbuf->size - vbuf->bytesused)) { /* error here */
2399+
s_savePicture = 0;
2400+
vbuf->bytesused = 0U;
2401+
uvc_dev->vbuf_offset = 0;
2402+
} else {
2403+
/* the same frame id indicates they belong to the same frame */
2404+
if (frame_id == uvc_dev->expect_frame_id) {
2405+
/* copy data to picture buffer */
2406+
memcpy((void *)(((uint8_t *)vbuf->buffer) + vbuf->bytesused),
2407+
(void *)(((uint8_t *)buf->data) + headLength),
2408+
dataSize);
2409+
vbuf->bytesused += dataSize;
2410+
uvc_dev->vbuf_offset = vbuf->bytesused;
2411+
2412+
LOG_DBG("Processed %u payload bytes (FID:%u), total: %u, EOF: %u",
2413+
dataSize, frame_id, vbuf->bytesused, endOfFrame);
2414+
} else {
2415+
/* for the payload that has different frame id, discard it */
2416+
s_savePicture = 0;
2417+
LOG_DBG("Frame ID mismatch: expected %u, got %u - discarding",
2418+
uvc_dev->expect_frame_id, frame_id);
2419+
}
2420+
}
2421+
} else {
2422+
/* no action */
2423+
}
2424+
}
2425+
2426+
if (s_savePicture) {
2427+
if (endOfFrame) {
2428+
if (vbuf->bytesused != 0) {
2429+
LOG_INF("Frame completed: %u bytes (FID: %u)",
2430+
vbuf->bytesused, frame_id);
2431+
2432+
/* Move completed buffer from input to output queue */
2433+
k_fifo_get(&uvc_dev->fifo_in, K_NO_WAIT);
2434+
k_fifo_put(&uvc_dev->fifo_out, vbuf);
2435+
2436+
/* toggle the expected frame id */
2437+
uvc_dev->expect_frame_id = uvc_dev->expect_frame_id ^ 1;
2438+
s_savePicture = 1;
2439+
2440+
/* Clean up transfer resources */
2441+
uvc_dev->vbuf_offset = 0;
2442+
uvc_dev->transfer_count = 0;
2443+
2444+
/* Signal frame completion to application */
2445+
LOG_DBG("Raising VIDEO_BUF_DONE signal");
2446+
k_poll_signal_raise(uvc_dev->sig, VIDEO_BUF_DONE);
2447+
2448+
/* switch to another buffer to save picture frame */
2449+
if ((vbuf = k_fifo_peek_head(&uvc_dev->fifo_in)) != NULL) {
2450+
vbuf->bytesused = 0;
2451+
memset(vbuf->buffer, 0, vbuf->size);
2452+
uvc_dev->current_vbuf = vbuf;
2453+
}
2454+
}
2455+
}
2456+
} else {
2457+
/* the last frame of one picture */
2458+
if (endOfFrame) {
2459+
if (uvc_dev->discard_first_frame) {
2460+
uvc_dev->discard_first_frame = 0;
2461+
uvc_dev->expect_frame_id = frame_id ^ 1;
2462+
}
2463+
if (vbuf && vbuf->bytesused != 0) {
2464+
uvc_dev->discard_frame_cnt++;
2465+
}
2466+
if (vbuf) {
2467+
vbuf->bytesused = 0U;
2468+
uvc_dev->vbuf_offset = 0;
2469+
}
2470+
if (!uvc_dev->discard_first_frame) {
2471+
uvc_dev->expect_frame_id = frame_id ^ 1;
2472+
}
2473+
s_savePicture = 1;
2474+
}
24022475
}
2403-
return 0;
24042476
}
24052477
}
24062478

24072479
cleanup:
24082480
/* Release network buffer reference */
24092481
net_buf_unref(buf);
24102482
/* Continue processing pending buffers */
2411-
uvc_host_continue_transfer(uvc_dev, xfer, vbuf);
2483+
if (vbuf) {
2484+
uvc_host_continue_transfer(uvc_dev, xfer, vbuf);
2485+
}
24122486
return 0;
24132487
}
24142488

@@ -2742,7 +2816,6 @@ static int uvc_host_init(struct usbh_class_data *cdata)
27422816
uvc_dev->vs_input_header = NULL;
27432817
uvc_dev->vs_output_header = NULL;
27442818

2745-
27462819
/** Initialize format information */
27472820
memset(&uvc_dev->formats, 0, sizeof(struct uvc_format_info_all));
27482821
if (uvc_dev->video_format_caps) {
@@ -2753,6 +2826,12 @@ static int uvc_host_init(struct usbh_class_data *cdata)
27532826
/** Initialize current format information */
27542827
memset(&uvc_dev->current_format, 0, sizeof(struct uvc_format_info));
27552828

2829+
uvc_dev->expect_frame_id = 0xFF;
2830+
/* dicard the first picture because the first picture may be not complete */
2831+
uvc_dev->discard_first_frame = 1;
2832+
uvc_dev->discard_frame_cnt = 0;
2833+
uvc_dev->multi_prime_cnt = CONFIG_USBH_VIDEO_MULTIPLE_PRIME_COUNT;
2834+
27562835
LOG_INF("UVC device structure initialized successfully");
27572836
return 0;
27582837
}
@@ -2885,6 +2964,8 @@ static int uvc_host_removed(struct usb_device *udev, struct usbh_class_data *cda
28852964
return -ENODEV;
28862965
}
28872966

2967+
/*TODO: cancel all of transfers and free all of usb xfer */
2968+
28882969
/* Reset video buffer state */
28892970
uvc_dev->vbuf_offset = 0;
28902971
uvc_dev->transfer_count = 0;
@@ -3591,7 +3672,14 @@ static int video_usb_uvc_host_set_stream(const struct device *dev, bool enable,
35913672
if (enable)
35923673
{
35933674
if ((vbuf = k_fifo_peek_head(&uvc_dev->fifo_in)) != NULL) {
3594-
uvc_host_initiate_transfer(uvc_dev, vbuf);
3675+
vbuf->bytesused = 0;
3676+
memset(vbuf->buffer, 0, vbuf->size);
3677+
uvc_dev->current_vbuf = vbuf;
3678+
while (uvc_dev->multi_prime_cnt)
3679+
{
3680+
uvc_host_initiate_transfer(uvc_dev, vbuf);
3681+
uvc_dev->multi_prime_cnt--;
3682+
}
35953683
}
35963684
}
35973685

@@ -3715,4 +3803,3 @@ static DEVICE_API(video, uvc_host_video_api) = {
37153803
VIDEO_DEVICE_DEFINE(usb_camera_##n, (void *)DEVICE_DT_INST_GET(n), NULL);
37163804

37173805
DT_INST_FOREACH_STATUS_OKAY(USBH_VIDEO_DT_DEVICE_DEFINE)
3718-

0 commit comments

Comments
 (0)