This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 7857c514a3d6c61c6e30aebcaca1983aa125dd0d Author: niyinghao <[email protected]> AuthorDate: Fri May 8 10:29:18 2026 +0800 video/v4l2_cap: add FIFO mode drop-frame on buffer underrun In FIFO buffer mode, when every buffer was occupied (no vacant container for the next frame), the driver stopped capture and went back to the STREAMON state. Restarting capture means re-initializing the sensor pipeline, which is expensive and adds visible latency. Camera sensors usually handle a buffer underrun by dropping frames: the sensor keeps running and the next frame is delivered as soon as a buffer is available again. Do the same here for imgdata drivers that set the new IMGDATA_CAP_DROP_FRAME flag: pass them a NULL buffer with IMGDATA_SET_BUF(NULL, 0), so the hardware keeps capturing and discards frames until QBUF supplies a buffer again. Such a driver reports a dropped frame through the capture callback with size 0. Drivers without the flag keep the old behavior. The sim camera sets the flag and follows the new contract: it accepts a NULL buffer, keeps its timer running without one, reports dequeued frames with size 0, and returns -EAGAIN from host_video_dqbuf() when no frame is ready, so the callback still runs and the upper half can supply a new buffer. While here, fix the nxstyle errors that checkpatch reports in the two sim files: the missing blank lines after declarations in imgdata_fmt_to_v4l2() and sim_camera_is_available(), and the indentation of the unmap loop in host_video_stop_capture(). Signed-off-by: niyinghao <[email protected]> --- arch/sim/src/sim/posix/sim_host_v4l2.c | 14 +++++++----- arch/sim/src/sim/sim_camera.c | 41 +++++++++++++++++++++++----------- drivers/video/v4l2_cap.c | 23 +++++++++++++++---- include/nuttx/video/imgdata.h | 27 ++++++++++++++++++++++ 4 files changed, 83 insertions(+), 22 deletions(-) diff --git a/arch/sim/src/sim/posix/sim_host_v4l2.c b/arch/sim/src/sim/posix/sim_host_v4l2.c index 880b7fb5e47..d12c121fd1a 100644 --- a/arch/sim/src/sim/posix/sim_host_v4l2.c +++ b/arch/sim/src/sim/posix/sim_host_v4l2.c @@ -352,7 +352,7 @@ int host_video_dqbuf(struct host_video_dev_s *vdev, uint8_t *addr, /* No buffer in the outgoing queue */ - return 0; + return -EAGAIN; default: perror("VIDIOC_DQBUF"); @@ -365,7 +365,11 @@ int host_video_dqbuf(struct host_video_dev_s *vdev, uint8_t *addr, size = buf.bytesused; } - memcpy(addr, vdev->addrs[buf.index], size); + if (addr != NULL) + { + memcpy(addr, vdev->addrs[buf.index], size); + } + if (-1 == ioctl(vdev->fd, VIDIOC_QBUF, &buf)) { perror("VIDIOC_QBUF"); @@ -480,9 +484,9 @@ int host_video_stop_capture(struct host_video_dev_s *vdev) break; } - munmap(vdev->addrs[i], vdev->buflen[i]); - vdev->addrs[i] = NULL; - vdev->buflen[i] = 0; + munmap(vdev->addrs[i], vdev->buflen[i]); + vdev->addrs[i] = NULL; + vdev->buflen[i] = 0; } return 0; diff --git a/arch/sim/src/sim/sim_camera.c b/arch/sim/src/sim/sim_camera.c index ef12b3f4843..bfa9e464f8f 100644 --- a/arch/sim/src/sim/sim_camera.c +++ b/arch/sim/src/sim/sim_camera.c @@ -159,6 +159,7 @@ static const struct v4l2_frmsizeenum g_frmsizes[] = static uint32_t imgdata_fmt_to_v4l2(uint32_t pixelformat) { uint32_t fourcc; + switch (pixelformat) { case IMGDATA_PIX_FMT_NV12: @@ -204,6 +205,7 @@ static uint32_t imgdata_fmt_to_v4l2(uint32_t pixelformat) static bool sim_camera_is_available(struct imgsensor_s *sensor) { sim_camera_priv_t *priv = container_of(sensor, sim_camera_priv_t, sensor); + return host_video_is_available(priv->devpath); } @@ -298,7 +300,14 @@ static int sim_camera_data_uninit(struct imgdata_s *data) static int sim_camera_data_validate_buf(uint8_t *addr, uint32_t size) { - if (!addr || ((uintptr_t)(addr) & 0x1f)) + /* NULL addr is valid: signals drop-frame mode (size must be 0) */ + + if (addr == NULL) + { + return size == 0 ? 0 : -EINVAL; + } + + if ((uintptr_t)(addr) & 0x1f) { return -EINVAL; } @@ -376,6 +385,8 @@ static int sim_camera_data_start_capture(struct imgdata_s *data, ret = host_video_start_capture(priv->vdev); if (ret < 0) { + priv->capture_cb = NULL; + priv->capture_arg = NULL; return ret; } @@ -389,10 +400,15 @@ static int sim_camera_data_stop_capture(struct imgdata_s *data) { sim_camera_priv_t *priv = container_of(data, sim_camera_priv_t, data); - priv->next_buf = NULL; priv->capture_started = false; wd_cancel(&priv->wdog); - return host_video_stop_capture(priv->vdev); + + priv->capture_cb = NULL; + priv->capture_arg = NULL; + priv->next_buf = NULL; + priv->buf_size = 0; + host_video_stop_capture(priv->vdev); + return 0; } static void sim_camera_interrupt(wdparm_t arg) @@ -402,23 +418,21 @@ static void sim_camera_interrupt(wdparm_t arg) struct timeval tv; int ret; - if (priv == NULL) + if (priv == NULL || priv->capture_cb == NULL) { return; } - if (priv->next_buf != NULL) + ret = host_video_dqbuf(priv->vdev, priv->next_buf, priv->buf_size); + if (ret >= 0) { - ret = host_video_dqbuf(priv->vdev, priv->next_buf, priv->buf_size); - if (ret > 0) - { - clock_gettime(CLOCK_MONOTONIC, &ts); - TIMESPEC_TO_TIMEVAL(&tv, &ts); - priv->capture_cb(0, ret, &tv, priv->capture_arg); - } + clock_gettime(CLOCK_MONOTONIC, &ts); + TIMESPEC_TO_TIMEVAL(&tv, &ts); + priv->capture_cb(0, priv->next_buf ? ret : 0, + &tv, priv->capture_arg); } - if (priv->capture_started && priv->next_buf != NULL) + if (priv->capture_started) { wd_start(&priv->wdog, SIM_CAMERA_PERIOD, sim_camera_interrupt, (wdparm_t)priv); @@ -454,6 +468,7 @@ int sim_camera_initialize(void) int ret; priv->data.ops = &g_sim_camera_data_ops; + priv->data.caps = IMGDATA_CAP_DROP_FRAME; priv->sensor.ops = &g_sim_camera_ops; priv->sensor.frmsizes_num = 1; priv->sensor.frmsizes = g_frmsizes; diff --git a/drivers/video/v4l2_cap.c b/drivers/video/v4l2_cap.c index 0aa5b49e6b2..181a6a6f1ea 100644 --- a/drivers/video/v4l2_cap.c +++ b/drivers/video/v4l2_cap.c @@ -2208,6 +2208,16 @@ static int complete_capture(uint8_t err_code, return -EINVAL; } + /* Drop-frame callback (size=0): skip frame processing, + * go directly to set next buffer. + */ + + if (datasize == 0 && + (cmng->imgdata->caps & IMGDATA_CAP_DROP_FRAME) != 0) + { + goto set_next_buf; + } + poll_notify(&type_inf->fds, 1, POLLIN); if (err_code == 0) @@ -2243,6 +2253,7 @@ static int complete_capture(uint8_t err_code, nxsem_post(&type_inf->wait_capture.dqbuf_wait_flg); } +set_next_buf: if (type_inf->remaining_capnum == 0) { stop_capture(cmng, buf_type); @@ -2261,7 +2272,8 @@ static int complete_capture(uint8_t err_code, else { container = video_framebuff_get_vacant_container(&type_inf->bufinf); - if (container == NULL) + if (container == NULL && + (cmng->imgdata->caps & IMGDATA_CAP_DROP_FRAME) == 0) { stop_capture(cmng, buf_type); type_inf->state = CAPTURE_STATE_STREAMON; @@ -2281,9 +2293,12 @@ static int complete_capture(uint8_t err_code, IMGDATA_SET_BUF(cmng->imgdata, type_inf->nr_fmt, df, - (FAR uint8_t *)container->buf.m.userptr, - container->buf.length); - container->buf.sequence = type_inf->seqnum++; + container ? (FAR uint8_t *)container->buf.m.userptr : NULL, + container ? container->buf.length : 0); + if (container != NULL) + { + container->buf.sequence = type_inf->seqnum++; + } } } diff --git a/include/nuttx/video/imgdata.h b/include/nuttx/video/imgdata.h index 3bfcc7ba157..0b8d80dd6ec 100644 --- a/include/nuttx/video/imgdata.h +++ b/include/nuttx/video/imgdata.h @@ -51,6 +51,10 @@ #define IMGDATA_PIX_FMT_RGB565X (9) #define IMGDATA_PIX_FMT_ENTROPY (10) +/* Flags for imgdata_s.caps */ + +#define IMGDATA_CAP_DROP_FRAME (1 << 0) + /* Method access helper macros */ #define IMGDATA_INIT(d) \ @@ -87,6 +91,19 @@ typedef struct imgdata_interval_s uint32_t denominator; } imgdata_interval_t; +/* Callback invoked by imgdata driver when a frame capture completes. + * + * Parameters: + * result - 0 on success, non-zero error code on failure + * size - Number of bytes captured. A driver that sets + * IMGDATA_CAP_DROP_FRAME reports a dropped frame with 0: it + * had no buffer (set_buf was called with addr=NULL) and + * consumed the frame without storing data. The upper layer + * re-supplies a buffer via set_buf when one becomes available. + * ts - Timestamp of the captured frame + * arg - Opaque argument passed to start_capture + */ + typedef int (*imgdata_capture_t)(uint8_t result, uint32_t size, FAR const struct timeval *ts, FAR void *arg); @@ -99,6 +116,15 @@ struct imgdata_ops_s CODE int (*init)(FAR struct imgdata_s *data); CODE int (*uninit)(FAR struct imgdata_s *data); + /* Set the buffer for the next frame capture. + * + * Only a driver that sets IMGDATA_CAP_DROP_FRAME gets addr NULL (size + * 0). It then enters drop-frame mode: it continues consuming frames + * from the hardware but invokes the capture callback with size=0 to + * signal that no data was stored. The upper layer will call set_buf + * again with a valid buffer once one becomes available. + */ + CODE int (*set_buf)(FAR struct imgdata_s *data, uint8_t nr_datafmts, FAR imgdata_format_t *datafmts, @@ -133,6 +159,7 @@ struct imgdata_ops_s struct imgdata_s { FAR const struct imgdata_ops_s *ops; + uint32_t caps; }; #ifdef __cplusplus
