This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git

commit d6b6f39d9b93daf632f3cea1ca00ea521b9c9624
Author: zengshuang <[email protected]>
AuthorDate: Sat May 9 14:10:28 2026 +0800

    system/nxcamera: add file output support for captured frames
    
    Add support for writing captured frames to file when the output
    command specifies a file path instead of a framebuffer device.
    
    - Video file mode (e.g. output /host/video.mjpeg): append each
      frame to the file continuously until stop is called
    - Image file mode (e.g. output /host/frame.jpg): save one frame
      then automatically stop streaming
    - Optional frame count: output /host/video.mjpeg 10 saves exactly
      10 frames then stops (0 or omitted = unlimited)
    - Skip framebuffer open when file output is configured, fixing
      -ENOTSUP error on systems without /dev/fb0
    
    Signed-off-by: zengshuang <[email protected]>
---
 include/system/nxcamera.h       |  1 +
 system/nxcamera/nxcamera.c      | 83 +++++++++++++++++++++++++++++++++++++----
 system/nxcamera/nxcamera_main.c | 30 +++++++++++++--
 3 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/include/system/nxcamera.h b/include/system/nxcamera.h
index 4d42dfbfe..45f2c46c3 100644
--- a/include/system/nxcamera.h
+++ b/include/system/nxcamera.h
@@ -62,6 +62,7 @@ struct nxcamera_s
   struct fb_videoinfo_s display_vinfo;               /* Display video 
controller info */
   char                  videopath[CONFIG_NAME_MAX];  /* Output video file path 
*/
   char                  imagepath[CONFIG_NAME_MAX];  /* Output image file path 
*/
+  uint32_t              max_frames;                  /* Max frames to save 
(0=unlimited) */
   int                   crefs;                       /* Number of references */
   pthread_mutex_t       mutex;                       /* Thread sync mutex */
   char                  mqname[14];                  /* Name of display 
message queue */
diff --git a/system/nxcamera/nxcamera.c b/system/nxcamera/nxcamera.c
index bfc108a4c..c47f27d05 100644
--- a/system/nxcamera/nxcamera.c
+++ b/system/nxcamera/nxcamera.c
@@ -272,7 +272,13 @@ static int nxcamera_opendevice(FAR struct nxcamera_s *pcam)
         }
       else
         {
-          /* TODO: Add file output */
+          /* File output mode: no display device needed */
+
+          if (pcam->videopath[0] != '\0' ||
+              pcam->imagepath[0] != '\0')
+            {
+              return OK;
+            }
 
           return -ENOTSUP;
         }
@@ -325,6 +331,7 @@ static void *nxcamera_loopthread(pthread_addr_t pvarg)
   ssize_t                 size;
   struct video_msg_s      msg;
   bool                    streaming = true;
+  int                     output_fd = -1;
   int                     i;
   int                     ret;
   struct v4l2_buffer      buf;
@@ -373,6 +380,22 @@ static void *nxcamera_loopthread(pthread_addr_t pvarg)
    *     VIDEO_MSG_DEQUEUE message.
    */
 
+  /* Open output file before the loop (video mode uses append) */
+
+  if (pcam->videopath[0] != '\0')
+    {
+      output_fd = open(pcam->videopath,
+                       O_WRONLY | O_CREAT | O_APPEND, 0644);
+    }
+  else if (pcam->imagepath[0] != '\0')
+    {
+      output_fd = open(pcam->imagepath,
+                       O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    }
+
+  vinfo("output_fd=%d videopath=%s imagepath=%s\n",
+        output_fd, pcam->videopath, pcam->imagepath);
+
   while (streaming)
     {
       size = mq_receive(pcam->mq, (FAR char *)&msg, sizeof(msg), &prio);
@@ -411,16 +434,55 @@ static void *nxcamera_loopthread(pthread_addr_t pvarg)
           goto err_out;
         }
 
-      ret = show_image(pcam, &buf);
-      if (ret < 0)
+      if (buf.index >= pcam->nbuffers)
         {
-          verr("Fail to show image %d\n", -ret);
+          verr("Invalid buf.index %d\n", buf.index);
           goto err_out;
         }
 
-      if (pcam->display_pinfo.yres_virtual > pcam->display_vinfo.yres)
+      /* Write frame to file if output file is configured */
+
+      if (output_fd >= 0)
         {
-          pan_display(pcam->display_fd, &pcam->display_pinfo);
+          uint32_t frame_size;
+
+          frame_size = buf.bytesused > 0 ?
+                       buf.bytesused : pcam->buf_sizes[buf.index];
+
+          ret = write(output_fd, pcam->bufs[buf.index], frame_size);
+          if (ret != frame_size)
+            {
+              verr("write failed: ret=%d frame_size=%" PRIu32
+                   " errno=%d\n", ret, frame_size, errno);
+            }
+
+          /* Stop after max_frames if specified (0=unlimited) */
+
+          if (pcam->max_frames > 0)
+            {
+              pcam->max_frames--;
+              if (pcam->max_frames == 0)
+                {
+                  streaming = false;
+                  continue;
+                }
+            }
+        }
+      else
+        {
+          ret = show_image(pcam, &buf);
+          if (ret < 0)
+            {
+              verr("Fail to show image %d\n", -ret);
+              goto err_out;
+            }
+
+          if (pcam->display_pinfo.yres_virtual >
+              pcam->display_vinfo.yres)
+            {
+              pan_display(pcam->display_fd,
+                          &pcam->display_pinfo);
+            }
         }
 
       ret = ioctl(pcam->capture_fd, VIDIOC_QBUF, (uintptr_t)&buf);
@@ -436,6 +498,13 @@ static void *nxcamera_loopthread(pthread_addr_t pvarg)
 err_out:
   vinfo("Clean-up and exit\n");
 
+  /* Close output file if open */
+
+  if (output_fd >= 0)
+    {
+      close(output_fd);
+    }
+
   /* Cleanup */
 
   pthread_mutex_lock(&pcam->mutex);  /* Lock the mutex */
@@ -586,7 +655,7 @@ int nxcamera_setfile(FAR struct nxcamera_s *pcam, FAR const 
char *pfile,
 
   /* Try to open the file */
 
-  temp_fd = open(pfile, O_CREAT | O_RDWR, 0666);
+  temp_fd = open(pfile, O_CREAT | O_RDWR, 0644);
   if (temp_fd == -1)
     {
       /* Error opening the file */
diff --git a/system/nxcamera/nxcamera_main.c b/system/nxcamera/nxcamera_main.c
index 8e56694fe..07656115f 100644
--- a/system/nxcamera/nxcamera_main.c
+++ b/system/nxcamera/nxcamera_main.c
@@ -124,9 +124,9 @@ static const struct nxcamera_cmd_s g_nxcamera_cmds[] =
   },
   {
     "output",
-    "fbdev|filename",
+    "fbdev|filename [nframes]",
     nxcamera_cmd_output,
-    NXCAMERA_HELP_TEXT("Specify a display device or filename")
+    NXCAMERA_HELP_TEXT("Specify output device or file (nframes=0 unlimited)")
   },
   {
     "set",
@@ -310,7 +310,13 @@ static int nxcamera_cmd_input(FAR struct nxcamera_s *pcam, 
FAR char *parg)
 /****************************************************************************
  * Name: nxcamera_cmd_output
  *
- *   nxcamera_cmd_device() sets the output device/file for display/save.
+ *   nxcamera_cmd_output() sets the output device/file for display/save.
+ *
+ *   Usage: output <path> [nframes]
+ *
+ *   - /dev/fbN          : display to framebuffer (default)
+ *   - file.jpg/.jpeg    : image mode, saves 1 frame (nframes ignored)
+ *   - file.mjpeg/.yuv/… : video mode, nframes (0/omitted=unlimited)
  *
  ****************************************************************************/
 
@@ -319,8 +325,19 @@ static int nxcamera_cmd_output(FAR struct nxcamera_s 
*pcam, FAR char *parg)
   int      ret;
   char     path[PATH_MAX];
   FAR char *ext;
+  FAR char *space;
   bool     isimage = false;
 
+  /* Parse optional frame count after the path */
+
+  pcam->max_frames = 0;
+  space = strchr(parg, ' ');
+  if (space)
+    {
+      *space = '\0';
+      pcam->max_frames = (uint32_t)atoi(space + 1);
+    }
+
   /* First try to open the device directly */
 
   ret = nxcamera_setfb(pcam, parg);
@@ -351,6 +368,13 @@ static int nxcamera_cmd_output(FAR struct nxcamera_s 
*pcam, FAR char *parg)
         }
 
       ret = nxcamera_setfile(pcam, parg, isimage);
+
+      /* Image files (.jpg/.jpeg) always save exactly 1 frame */
+
+      if (isimage)
+        {
+          pcam->max_frames = 1;
+        }
     }
 
   if (ret < 0)

Reply via email to