mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
examples/uvc_cam: add UVC camera streaming app
Usage: uvc_cam [nframes] [video_dev] [uvc_dev] V4L2 capture -> write UVC gadget device. Uses boardctl to initialize/deinitialize UVC gadget. Queries sensor pixel format, resolution and frame rate via V4L2. Uses poll() to wait for USB host streaming state. Supports optional device path arguments (default /dev/video0, /dev/uvc0). Supports configurable frame count (0=infinite). Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
This commit is contained in:
parent
dc4ecc973e
commit
073df9d1de
5 changed files with 608 additions and 0 deletions
35
examples/uvc_cam/CMakeLists.txt
Normal file
35
examples/uvc_cam/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ##############################################################################
|
||||
# apps/examples/uvc_cam/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_EXAMPLES_UVC_CAM)
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
${CONFIG_EXAMPLES_UVC_CAM_PROGNAME}
|
||||
PRIORITY
|
||||
${CONFIG_EXAMPLES_UVC_CAM_PRIORITY}
|
||||
STACKSIZE
|
||||
${CONFIG_EXAMPLES_UVC_CAM_STACKSIZE}
|
||||
MODULE
|
||||
${CONFIG_EXAMPLES_UVC_CAM}
|
||||
SRCS
|
||||
uvc_cam_main.c)
|
||||
endif()
|
||||
32
examples/uvc_cam/Kconfig
Normal file
32
examples/uvc_cam/Kconfig
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config EXAMPLES_UVC_CAM
|
||||
tristate "UVC Camera streaming example"
|
||||
default n
|
||||
depends on USBUVC && VIDEO
|
||||
---help---
|
||||
Captures frames from V4L2 camera and streams to USB host
|
||||
via UVC gadget driver (/dev/uvc0).
|
||||
|
||||
if EXAMPLES_UVC_CAM
|
||||
|
||||
config EXAMPLES_UVC_CAM_PROGNAME
|
||||
string "Program name"
|
||||
default "uvc_cam"
|
||||
|
||||
config EXAMPLES_UVC_CAM_PRIORITY
|
||||
int "Task priority"
|
||||
default 100
|
||||
|
||||
config EXAMPLES_UVC_CAM_STACKSIZE
|
||||
int "Stack size"
|
||||
default 4096
|
||||
|
||||
config EXAMPLES_UVC_CAM_NFRAMES
|
||||
int "Number of frames (0=infinite)"
|
||||
default 0
|
||||
|
||||
endif
|
||||
25
examples/uvc_cam/Make.defs
Normal file
25
examples/uvc_cam/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/examples/uvc_cam/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_EXAMPLES_UVC_CAM),)
|
||||
CONFIGURED_APPS += $(APPDIR)/examples/uvc_cam
|
||||
endif
|
||||
34
examples/uvc_cam/Makefile
Normal file
34
examples/uvc_cam/Makefile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
############################################################################
|
||||
# apps/examples/uvc_cam/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# UVC Camera streaming application
|
||||
|
||||
PROGNAME = $(CONFIG_EXAMPLES_UVC_CAM_PROGNAME)
|
||||
PRIORITY = $(CONFIG_EXAMPLES_UVC_CAM_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_EXAMPLES_UVC_CAM_STACKSIZE)
|
||||
MODULE = $(CONFIG_EXAMPLES_UVC_CAM)
|
||||
|
||||
MAINSRC = uvc_cam_main.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
482
examples/uvc_cam/uvc_cam_main.c
Normal file
482
examples/uvc_cam/uvc_cam_main.c
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
/****************************************************************************
|
||||
* apps/examples/uvc_cam/uvc_cam_main.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* USB Video Class camera streaming application.
|
||||
* Captures frames from V4L2 camera and streams to /dev/uvc0.
|
||||
*
|
||||
* Queries the sensor's native pixel format, resolution and frame rate
|
||||
* via V4L2, then streams frames to the UVC gadget.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/boardctl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <nuttx/video/video.h>
|
||||
#include <nuttx/video/v4l2_cap.h>
|
||||
#include <nuttx/usb/uvc.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define UVC_DEV_DEFAULT "/dev/uvc0"
|
||||
#define VIDEO_DEV_DEFAULT "/dev/video0"
|
||||
|
||||
#ifndef CONFIG_EXAMPLES_UVC_CAM_NFRAMES
|
||||
# define CONFIG_EXAMPLES_UVC_CAM_NFRAMES 0 /* 0 = infinite */
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pixfmt_to_bpp
|
||||
*
|
||||
* Description:
|
||||
* Return bytes-per-pixel for common V4L2 pixel formats.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int pixfmt_to_bpp(uint32_t pixfmt)
|
||||
{
|
||||
switch (pixfmt)
|
||||
{
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
return 2;
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: camera_init
|
||||
*
|
||||
* Description:
|
||||
* Open and configure the V4L2 camera device.
|
||||
* Queries the sensor's native pixel format, resolution and frame rate
|
||||
* and returns them via output parameters.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int camera_init(int fd, FAR uint32_t *pixfmt, FAR uint16_t *width,
|
||||
FAR uint16_t *height, FAR uint16_t *fps)
|
||||
{
|
||||
struct v4l2_fmtdesc fmtdesc;
|
||||
struct v4l2_frmivalenum frmival;
|
||||
struct v4l2_frmsizeenum frmsize;
|
||||
struct v4l2_format fmt;
|
||||
struct v4l2_requestbuffers req;
|
||||
int ret;
|
||||
|
||||
/* Query native pixel format from sensor */
|
||||
|
||||
memset(&fmtdesc, 0, sizeof(fmtdesc));
|
||||
fmtdesc.index = 0;
|
||||
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_ENUM_FMT failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
printf("Sensor format: %.4s\n", (FAR const char *)&fmtdesc.pixelformat);
|
||||
|
||||
/* Query native resolution from sensor */
|
||||
|
||||
memset(&frmsize, 0, sizeof(frmsize));
|
||||
frmsize.index = 0;
|
||||
frmsize.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
frmsize.pixel_format = fmtdesc.pixelformat;
|
||||
|
||||
ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_ENUM_FRAMESIZES failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
printf("Sensor native: %dx%d\n",
|
||||
frmsize.discrete.width, frmsize.discrete.height);
|
||||
|
||||
/* Set format — capture at native resolution */
|
||||
|
||||
memset(&fmt, 0, sizeof(fmt));
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.width = frmsize.discrete.width;
|
||||
fmt.fmt.pix.height = frmsize.discrete.height;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix.pixelformat = fmtdesc.pixelformat;
|
||||
|
||||
ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_S_FMT failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Request buffers — match camcap: USERPTR + FIFO */
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.count = 1;
|
||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
req.memory = V4L2_MEMORY_USERPTR;
|
||||
req.mode = V4L2_BUF_MODE_FIFO;
|
||||
|
||||
ret = ioctl(fd, VIDIOC_REQBUFS, &req);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_REQBUFS failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* NOTE: Do NOT call VIDIOC_STREAMON here.
|
||||
* With USERPTR + FIFO mode, STREAMON would start DMA capture
|
||||
* immediately using the container's uninitialized userptr (0),
|
||||
* causing DMA to write 153KB of video data to address 0 and
|
||||
* corrupt memory. STREAMON must be called AFTER the first QBUF
|
||||
* so that userptr points to a valid buffer.
|
||||
*/
|
||||
|
||||
/* Query frame interval from sensor */
|
||||
|
||||
memset(&frmival, 0, sizeof(frmival));
|
||||
frmival.index = 0;
|
||||
frmival.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
frmival.pixel_format = fmtdesc.pixelformat;
|
||||
frmival.width = frmsize.discrete.width;
|
||||
frmival.height = frmsize.discrete.height;
|
||||
|
||||
ret = ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_ENUM_FRAMEINTERVALS failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*pixfmt = fmtdesc.pixelformat;
|
||||
*width = frmsize.discrete.width;
|
||||
*height = frmsize.discrete.height;
|
||||
*fps = frmival.discrete.numerator > 0
|
||||
? frmival.discrete.denominator / frmival.discrete.numerator
|
||||
: 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: camera_capture_frame
|
||||
*
|
||||
* Description:
|
||||
* Capture one frame from V4L2 into the provided buffer.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int camera_capture_frame(int camfd, FAR uint8_t *buf, size_t buflen,
|
||||
bool do_streamon)
|
||||
{
|
||||
struct v4l2_buffer vbuf;
|
||||
int ret;
|
||||
|
||||
memset(&vbuf, 0, sizeof(vbuf));
|
||||
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
vbuf.memory = V4L2_MEMORY_USERPTR;
|
||||
vbuf.index = 0;
|
||||
vbuf.m.userptr = (uintptr_t)buf;
|
||||
vbuf.length = buflen;
|
||||
|
||||
ret = ioctl(camfd, VIDIOC_QBUF, &vbuf);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_QBUF failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Start streaming after the first QBUF so DMA has a valid buffer */
|
||||
|
||||
if (do_streamon)
|
||||
{
|
||||
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
ret = ioctl(camfd, VIDIOC_STREAMON, &type);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_STREAMON failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ioctl(camfd, VIDIOC_DQBUF, &vbuf);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("VIDIOC_DQBUF failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return vbuf.bytesused > 0 ? (int)vbuf.bytesused : (int)buflen;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, FAR char *argv[])
|
||||
{
|
||||
struct boardioc_usbdev_ctrl_s ctrl;
|
||||
struct uvc_params_s uvc_params;
|
||||
FAR void *uvc_handle = NULL;
|
||||
FAR const char *video_dev = VIDEO_DEV_DEFAULT;
|
||||
FAR const char *uvc_dev = UVC_DEV_DEFAULT;
|
||||
FAR uint8_t *framebuf;
|
||||
ssize_t written;
|
||||
enum v4l2_buf_type cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
uint32_t pixfmt = 0;
|
||||
uint16_t width = 0;
|
||||
uint16_t height = 0;
|
||||
uint16_t fps = 0;
|
||||
uint32_t frame_size;
|
||||
int bpp;
|
||||
int camfd;
|
||||
int uvcfd;
|
||||
int nframes;
|
||||
int count = 0;
|
||||
int ret;
|
||||
|
||||
nframes = CONFIG_EXAMPLES_UVC_CAM_NFRAMES;
|
||||
if (argc > 1)
|
||||
{
|
||||
nframes = atoi(argv[1]);
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
video_dev = argv[2];
|
||||
}
|
||||
|
||||
if (argc > 3)
|
||||
{
|
||||
uvc_dev = argv[3];
|
||||
}
|
||||
|
||||
/* Create capture device */
|
||||
|
||||
ret = capture_initialize(video_dev);
|
||||
if (ret < 0 && ret != -EEXIST)
|
||||
{
|
||||
printf("Failed to initialize capture: %d\n", ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Open camera and query resolution */
|
||||
|
||||
camfd = open(video_dev, O_RDWR);
|
||||
if (camfd < 0)
|
||||
{
|
||||
printf("Failed to open %s: %d\n", video_dev, errno);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = camera_init(camfd, &pixfmt, &width, &height, &fps);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Camera init failed: %d\n", ret);
|
||||
close(camfd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bpp = pixfmt_to_bpp(pixfmt);
|
||||
if (bpp == 0)
|
||||
{
|
||||
printf("Unsupported pixel format: 0x%08lx\n",
|
||||
(unsigned long)pixfmt);
|
||||
close(camfd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
frame_size = (uint32_t)width * height * bpp;
|
||||
printf("UVC Camera: %dx%d %.4s @%dfps, frames=%d (0=infinite)\n",
|
||||
width, height, (FAR const char *)&pixfmt, fps, nframes);
|
||||
|
||||
/* Allocate frame buffer — 32-byte aligned for DMA (same as camcap) */
|
||||
|
||||
framebuf = memalign(32, frame_size);
|
||||
if (!framebuf)
|
||||
{
|
||||
printf("Failed to allocate frame buffer (%lu bytes)\n",
|
||||
(unsigned long)frame_size);
|
||||
close(camfd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Initialize and connect UVC gadget via boardctl.
|
||||
* Pass video params queried from camera so USB descriptors
|
||||
* match the actual sensor resolution.
|
||||
*/
|
||||
|
||||
uvc_params.width = width;
|
||||
uvc_params.height = height;
|
||||
uvc_params.fps = fps;
|
||||
|
||||
uvc_handle = (FAR void *)&uvc_params;
|
||||
#ifdef CONFIG_USBUVC_COMPOSITE
|
||||
ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE;
|
||||
#else
|
||||
ctrl.usbdev = BOARDIOC_USBDEV_UVC;
|
||||
#endif
|
||||
ctrl.action = BOARDIOC_USBDEV_CONNECT;
|
||||
ctrl.instance = 0;
|
||||
ctrl.config = 0;
|
||||
ctrl.handle = &uvc_handle;
|
||||
|
||||
ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Failed to initialize UVC device: %d\n", ret);
|
||||
close(camfd);
|
||||
free(framebuf);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Open UVC device */
|
||||
|
||||
uvcfd = open(uvc_dev, O_WRONLY);
|
||||
if (uvcfd < 0)
|
||||
{
|
||||
printf("Failed to open %s: %d\n", uvc_dev, errno);
|
||||
ctrl.action = BOARDIOC_USBDEV_DISCONNECT;
|
||||
boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
|
||||
close(camfd);
|
||||
free(framebuf);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Wait for USB host to enumerate and start streaming */
|
||||
|
||||
struct pollfd pfd;
|
||||
pfd.fd = uvcfd;
|
||||
pfd.events = POLLOUT;
|
||||
|
||||
printf("Waiting for USB host to connect...\n");
|
||||
while (poll(&pfd, 1, 500) <= 0 || !(pfd.revents & POLLOUT))
|
||||
{
|
||||
}
|
||||
|
||||
printf("Streaming started...\n");
|
||||
|
||||
/* Main capture-stream loop */
|
||||
|
||||
while (nframes == 0 || count < nframes)
|
||||
{
|
||||
ret = camera_capture_frame(camfd, framebuf, frame_size,
|
||||
count == 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Capture failed: %d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write frame to UVC directly from framebuf.
|
||||
* DMA won't touch framebuf until next QBUF.
|
||||
*/
|
||||
|
||||
written = 0;
|
||||
while (written < (ssize_t)frame_size)
|
||||
{
|
||||
ret = write(uvcfd, framebuf + written,
|
||||
frame_size - written);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
{
|
||||
/* Host stopped streaming - wait for reconnect.
|
||||
*
|
||||
* NOTE: The printf inside the loop is required
|
||||
* on boards where the USB-UART bridge (CH341)
|
||||
* and the ESP32-S3 OTG share an on-board USB
|
||||
* hub (e.g. lckfb-szpi). Continuous UART TX
|
||||
* keeps the hub active and prevents the host
|
||||
* from power-cycling the hub port, which would
|
||||
* cause a full board POWERON reset.
|
||||
*/
|
||||
|
||||
printf("Host disconnected, waiting...\n");
|
||||
pfd.revents = 0;
|
||||
while (poll(&pfd, 1, 500) <= 0
|
||||
|| !(pfd.revents & POLLOUT))
|
||||
{
|
||||
printf(".\n");
|
||||
}
|
||||
|
||||
printf("Streaming restarted.\n");
|
||||
written = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("UVC write failed: %d\n", errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
written += ret;
|
||||
}
|
||||
|
||||
count++;
|
||||
if ((count % 10) == 0)
|
||||
{
|
||||
printf("Streamed %d frames\n", count);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Done. Streamed %d frames total.\n", count);
|
||||
|
||||
out:
|
||||
ioctl(camfd, VIDIOC_STREAMOFF, &cap_type);
|
||||
|
||||
close(uvcfd);
|
||||
close(camfd);
|
||||
|
||||
/* Disconnect UVC gadget */
|
||||
|
||||
ctrl.action = BOARDIOC_USBDEV_DISCONNECT;
|
||||
boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
|
||||
|
||||
free(framebuf);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue