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

Reply via email to