ARM Linux LCD上实时预览摄像头画面

news2025/3/4 1:57:01

文章目录

  • 1、前言
  • 2、环境介绍
  • 3、步骤
  • 4、应用程序编写
    • 4.1、lcd初始化
    • 4.2、摄像头初始化
    • 4.3、jpeg解码
    • 4.4、开启摄像头
    • 4.5、完整的程序如下
  • 5、测试
    • 5.1、编译应用程序
    • 5.2、运行应用程序
  • 6、总结

1、前言

本次应用程序主要针对支持MJPEG格式输出的UVC摄像头。

2、环境介绍

rk3566 + 7寸 mipi lcd + uvc摄像头

3、步骤

应用程序编写主要分为以下几个步骤:

1、lcd初始化。

2、摄像头初始化。

3、摄像头采集数据。

4、jpeg解码。

5、lcd显示。

4、应用程序编写

4.1、lcd初始化

typedef struct lcd_mes {
    int fd;
    unsigned char *fb_base;
    int lcd_width;
    int lcd_height;
    unsigned int bpp;
    unsigned int line_width;
} lcd_mes;

int lcd_init(const char *fb_dev, lcd_mes *lcd)
{
    int screen_size;
    struct fb_var_screeninfo var;   

    if (fb_dev == NULL)
        goto _err;

    /* 1. open /dev/fb* */    
    lcd->fd = open(fb_dev, O_RDWR);
    if(lcd->fd < 0)
    {
        printf("can not open %s\n", fb_dev);
        goto _err;
    }

    /* 2. get lcd message */
    if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &var))
    {
        printf("can not get var\n");
        goto _err;
    }

    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    lcd->line_width  = var.xres * var.bits_per_pixel / 8;
    lcd->lcd_width = var.xres;
    lcd->lcd_height = var.yres;
    lcd->bpp = var.bits_per_pixel;
    lcd->fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd->fd, 0);
    if (lcd->fb_base == (unsigned char *)-1)
    {
        printf("can not mmap\n");
        goto _err;
    }

    memset(lcd->fb_base, 0x00, screen_size);
    return 0;

_err:
    return -1;
}

4.2、摄像头初始化

使用v4l2接口初始化uvc摄像头:

typedef struct camera_mes {
    int fd;
    void *bufs[32];
    int bufs_index;
    int buf_length;
    char fmt[20];
    int frame_x_size;
    int frame_y_size;
} camera_mes;

int camera_init(const char *video, camera_mes *camera)
{   
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_frmsizeenum fsenum;
    int fmt_index = 0;
    int frame_index = 0;
    int buf_cnt;
    int i;

    if (video == NULL)
        goto _err;

    /* 1. open /dev/video* */
    camera->fd = open(video, O_RDWR);
    if (camera->fd < 0)
    {
        printf("can not open %s\n", video);
        goto _err;
    }

    /* 2. query capability */
    struct v4l2_capability cap;
    memset(&cap, 0, sizeof(struct v4l2_capability));

    if (0 == ioctl(camera->fd, VIDIOC_QUERYCAP, &cap))
    {        
        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) 
        {
            fprintf(stderr, "Error opening device %s: video capture not supported.\n", video);
            goto _ioc_querycap_err;
        }

        if(!(cap.capabilities & V4L2_CAP_STREAMING)) 
        {
            fprintf(stderr, "%s does not support streaming i/o\n", video);
            goto _ioc_querycap_err;
        }
    }
    else
    {
        printf("can not get capability\n");
        goto _ioc_querycap_err;
    }

    /* 3. enum formt */
    while (1)
    {
        fmtdesc.index = fmt_index;  
        fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
        if (0 != ioctl(camera->fd, VIDIOC_ENUM_FMT, &fmtdesc))
            break;

        frame_index = 0;
        // printf("format %s,%d:\n", fmtdesc.description, fmtdesc.pixelformat);
        while (1)
        {
            memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));
            fsenum.pixel_format = fmtdesc.pixelformat;
            fsenum.index = frame_index;

            /* get framesize */
            if (ioctl(camera->fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0)
            {
                // printf("\t%d: %d x %d\n", frame_index, fsenum.discrete.width, fsenum.discrete.height);
            }
            else
            {
                break;
            }

            frame_index++;
        }

        fmt_index++;
    }

    /* 4. set formt */
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = camera->frame_x_size;
    fmt.fmt.pix.height = camera->frame_y_size;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;
    if (0 == ioctl(camera->fd, VIDIOC_S_FMT, &fmt))
    {
        // printf("the final frame-size has been set : %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
        camera->frame_x_size = fmt.fmt.pix.width;
        camera->frame_y_size = fmt.fmt.pix.height;
        strncpy(camera->fmt, "Motion-JPEG", strlen("Motion-JPEG"));
    }
    else
    {
        printf("can not set format\n");
        goto _ioc_sfmt_err;
    }

    /* 5. require buffer */
    struct v4l2_requestbuffers rb;
    memset(&rb, 0, sizeof(struct v4l2_requestbuffers));
    rb.count = 32;
    rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    rb.memory = V4L2_MEMORY_MMAP;

    if (0 == ioctl(camera->fd, VIDIOC_REQBUFS, &rb))
    {
        buf_cnt = rb.count;
        for(i = 0; i < rb.count; i++) 
        {
            struct v4l2_buffer buf;
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.index = i;
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            if (0 == ioctl(camera->fd, VIDIOC_QUERYBUF, &buf))
            {
                /* mmap */
                camera->bufs[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera->fd, buf.m.offset);
                if(camera->bufs[i] == MAP_FAILED) 
                {
                    printf("Unable to map buffer");
                    goto _err;
                }
            }
            else
            {
                printf("can not query buffer\n");
                goto _err;
            }            
        }
    }
    else
    {
        printf("can not request buffers\n");
        goto _ioc_reqbufs_err;
    }

    /* 6. queue buffer */
    for(i = 0; i < buf_cnt; ++i) 
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        buf.index = i;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        if (0 != ioctl(camera->fd, VIDIOC_QBUF, &buf))
        {
            perror("Unable to queue buffer");
            goto _ioc_qbuf_err;
        }
    }

    camera->bufs_index = 0;     // init camera struct
    camera->buf_length = 0;
    return 0;

_ioc_qbuf_err:
_ioc_reqbufs_err:
_ioc_sfmt_err:
_ioc_querycap_err:
_err:
    return -1;
}

4.3、jpeg解码

int jpeg_show_on_lcd(lcd_mes *lcd, camera_mes *camera)
{
    int min_width, min_height;
    int valid_bytes;
    int offset_x, offset_y;

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
    
	cinfo.err = jpeg_std_error(&jerr);      // 错误处理对象与解码对象绑定
	jpeg_create_decompress(&cinfo);         // 初始化解码器
	jpeg_mem_src(&cinfo, camera->bufs[camera->bufs_index], camera->buf_length);   // 指定JPEG数据的来源

	jpeg_read_header(&cinfo, TRUE);         // 读取图像信息
	cinfo.out_color_space = JCS_RGB;        // 设置解码后的颜色空间为RGB

	jpeg_start_decompress(&cinfo);          // 开始解码
	
	unsigned char *jpeg_line_buf = (char *)malloc(cinfo.output_components * cinfo.output_width);    // 用于存储从JPEG解码器读取的一行数据
	unsigned int *fb_line_buf = (int *)malloc(lcd->line_width);                                     // 用于存储转换后的RGB数据,准备写入framebuffer

    min_width = (cinfo.output_width < lcd->lcd_width) ? cinfo.output_width : lcd->lcd_width;
    min_height = (cinfo.output_height < lcd->lcd_height) ? cinfo.output_height : lcd->lcd_height;

	valid_bytes = min_width * lcd->bpp / 8;             // 一行的有效字节数
	unsigned char *ptr = lcd->fb_base;

    offset_x = ((lcd->lcd_width - min_width) / 2) * lcd->bpp / 8;   // x方向居中
    offset_y = (lcd->lcd_height - min_height) / 2;      // y方向居中
    for (int i = 0; i < offset_y; i++)                  
        ptr += lcd->lcd_width * lcd->bpp / 8;
    
    unsigned int red, green, blue;
    unsigned int color;
	while (cinfo.output_scanline < min_height)
	{
		jpeg_read_scanlines(&cinfo, &jpeg_line_buf, 1); // 每次读取一行

		for(int i = 0; i < min_width; i++)              
		{
			red = jpeg_line_buf[i * 3];                 
			green = jpeg_line_buf[i * 3 + 1];           
			blue = jpeg_line_buf[i * 3 + 2];            
			color = red << 16 | green << 8 | blue;      // RGB888转RGB8888

			fb_line_buf[i] = color;
		}

		memcpy(ptr + offset_x, fb_line_buf, valid_bytes);   // 将一行数据写入framebuffer
		ptr += lcd->lcd_width * lcd->bpp / 8;               // 移动到下一行
	}
	
	jpeg_finish_decompress(&cinfo);                     // 完成解码
	jpeg_destroy_decompress(&cinfo);                    // 销毁解码对象
	free(jpeg_line_buf);                                // 释放内存
	free(fb_line_buf);                                  // 释放内存

    return 0;
}

4.4、开启摄像头

int main(int argc, char **argv)
{

    ...

    /* start camera */
    if (0 != ioctl(camera.fd, VIDIOC_STREAMON, &type))
    {
        printf("Unable to start capture\n");
        goto _err;
    }

    printf("\nstart camera ...\n");
    while (1)
    {
        /* poll */
        memset(fds, 0, sizeof(fds));
        fds[0].fd = camera.fd;
        fds[0].events = POLLIN;
        if (1 == poll(fds, 1, -1))
        {
            /* dequeue buffer */
            struct v4l2_buffer buf;
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            
            if (0 != ioctl(camera.fd, VIDIOC_DQBUF, &buf))
            {
                printf("Unable to dequeue buffer\n");
                goto _ioc_dqbuf_err;
            }
            
            /* jpeg show on lcd */
            camera.bufs_index = buf.index;
            camera.buf_length = buf.length;
            jpeg_show_on_lcd(&lcd, &camera);

            /* queue buffer */
            if (0 != ioctl(camera.fd, VIDIOC_QBUF, &buf))
            {
                printf("Unable to queue buffer");
                goto _ioc_qbuf_err;
            }
        }
    }

    ...
    
}

4.5、完整的程序如下


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>          
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <linux/fb.h>

typedef struct lcd_mes {
    int fd;
    unsigned char *fb_base;
    int lcd_width;
    int lcd_height;
    unsigned int bpp;
    unsigned int line_width;
} lcd_mes;

typedef struct camera_mes {
    int fd;
    void *bufs[32];
    int bufs_index;
    int buf_length;
    char fmt[20];
    int frame_x_size;
    int frame_y_size;
} camera_mes;

int jpeg_show_on_lcd(lcd_mes *lcd, camera_mes *camera)
{
    int min_width, min_height;
    int valid_bytes;
    int offset_x, offset_y;

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
    
	cinfo.err = jpeg_std_error(&jerr);      // 错误处理对象与解码对象绑定
	jpeg_create_decompress(&cinfo);         // 初始化解码器
	jpeg_mem_src(&cinfo, camera->bufs[camera->bufs_index], camera->buf_length);   // 指定JPEG数据的来源

	jpeg_read_header(&cinfo, TRUE);         // 读取图像信息
	cinfo.out_color_space = JCS_RGB;        // 设置解码后的颜色空间为RGB

	jpeg_start_decompress(&cinfo);          // 开始解码
	
	unsigned char *jpeg_line_buf = (char *)malloc(cinfo.output_components * cinfo.output_width);    // 用于存储从JPEG解码器读取的一行数据
	unsigned int *fb_line_buf = (int *)malloc(lcd->line_width);                                     // 用于存储转换后的RGB数据,准备写入framebuffer

    min_width = (cinfo.output_width < lcd->lcd_width) ? cinfo.output_width : lcd->lcd_width;
    min_height = (cinfo.output_height < lcd->lcd_height) ? cinfo.output_height : lcd->lcd_height;

	valid_bytes = min_width * lcd->bpp / 8;             // 一行的有效字节数
	unsigned char *ptr = lcd->fb_base;

    offset_x = ((lcd->lcd_width - min_width) / 2) * lcd->bpp / 8;   // x方向居中
    offset_y = (lcd->lcd_height - min_height) / 2;      // y方向居中
    for (int i = 0; i < offset_y; i++)                  
        ptr += lcd->lcd_width * lcd->bpp / 8;
    
    unsigned int red, green, blue;
    unsigned int color;
	while (cinfo.output_scanline < min_height)
	{
		jpeg_read_scanlines(&cinfo, &jpeg_line_buf, 1); // 每次读取一行

		for(int i = 0; i < min_width; i++)              
		{
			red = jpeg_line_buf[i * 3];                 
			green = jpeg_line_buf[i * 3 + 1];           
			blue = jpeg_line_buf[i * 3 + 2];            
			color = red << 16 | green << 8 | blue;      // RGB888转RGB8888

			fb_line_buf[i] = color;
		}

		memcpy(ptr + offset_x, fb_line_buf, valid_bytes);   // 将一行数据写入framebuffer
		ptr += lcd->lcd_width * lcd->bpp / 8;               // 移动到下一行
	}
	
	jpeg_finish_decompress(&cinfo);                     // 完成解码
	jpeg_destroy_decompress(&cinfo);                    // 销毁解码对象
	free(jpeg_line_buf);                                // 释放内存
	free(fb_line_buf);                                  // 释放内存

    return 0;
}

int lcd_init(const char *fb_dev, lcd_mes *lcd)
{
    
    int screen_size;
    struct fb_var_screeninfo var;   

    if (fb_dev == NULL)
        goto _err;

    /* 1. open /dev/fb* */    
	lcd->fd = open(fb_dev, O_RDWR);
	if(lcd->fd < 0)
	{
        printf("can not open %s\n", fb_dev);
        goto _err;
    }

	/* 2. get lcd message */
	if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &var))
	{
		printf("can not get var\n");
		goto _err;
	}

    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    lcd->line_width  = var.xres * var.bits_per_pixel / 8;
	lcd->lcd_width = var.xres;
	lcd->lcd_height = var.yres;
	lcd->bpp = var.bits_per_pixel;
	lcd->fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd->fd, 0);
	if (lcd->fb_base == (unsigned char *)-1)
    {
        printf("can not mmap\n");
        goto _err;
    }

    memset(lcd->fb_base, 0x00, screen_size);
    return 0;

_err:
    return -1;
}

int camera_init(const char *video, camera_mes *camera)
{   
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_frmsizeenum fsenum;
    int fmt_index = 0;
    int frame_index = 0;
    int buf_cnt;
    int i;

    if (video == NULL)
        goto _err;

    /* 1. open /dev/video* */
    camera->fd = open(video, O_RDWR);
    if (camera->fd < 0)
    {
        printf("can not open %s\n", video);
        goto _err;
    }

    /* 2. query capability */
    struct v4l2_capability cap;
    memset(&cap, 0, sizeof(struct v4l2_capability));

    if (0 == ioctl(camera->fd, VIDIOC_QUERYCAP, &cap))
    {        
        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) 
        {
            fprintf(stderr, "Error opening device %s: video capture not supported.\n", video);
            goto _ioc_querycap_err;
        }

        if(!(cap.capabilities & V4L2_CAP_STREAMING)) 
        {
            fprintf(stderr, "%s does not support streaming i/o\n", video);
            goto _ioc_querycap_err;
        }
    }
    else
    {
        printf("can not get capability\n");
        goto _ioc_querycap_err;
    }

    /* 3. enum formt */
    while (1)
    {
        fmtdesc.index = fmt_index;  
        fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
        if (0 != ioctl(camera->fd, VIDIOC_ENUM_FMT, &fmtdesc))
            break;

        frame_index = 0;
        // printf("format %s,%d:\n", fmtdesc.description, fmtdesc.pixelformat);
        while (1)
        {
            memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));
            fsenum.pixel_format = fmtdesc.pixelformat;
            fsenum.index = frame_index;

            /* get framesize */
            if (ioctl(camera->fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0)
            {
                // printf("\t%d: %d x %d\n", frame_index, fsenum.discrete.width, fsenum.discrete.height);
            }
            else
            {
                break;
            }

            frame_index++;
        }

        fmt_index++;
    }

    /* 4. set formt */
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = camera->frame_x_size;
    fmt.fmt.pix.height = camera->frame_y_size;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;
    if (0 == ioctl(camera->fd, VIDIOC_S_FMT, &fmt))
    {
        // printf("the final frame-size has been set : %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
        camera->frame_x_size = fmt.fmt.pix.width;
        camera->frame_y_size = fmt.fmt.pix.height;
        strncpy(camera->fmt, "Motion-JPEG", strlen("Motion-JPEG"));
    }
    else
    {
        printf("can not set format\n");
        goto _ioc_sfmt_err;
    }

    /* 5. require buffer */
    struct v4l2_requestbuffers rb;
    memset(&rb, 0, sizeof(struct v4l2_requestbuffers));
    rb.count = 32;
    rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    rb.memory = V4L2_MEMORY_MMAP;

    if (0 == ioctl(camera->fd, VIDIOC_REQBUFS, &rb))
    {
        buf_cnt = rb.count;
        for(i = 0; i < rb.count; i++) 
        {
            struct v4l2_buffer buf;
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.index = i;
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            if (0 == ioctl(camera->fd, VIDIOC_QUERYBUF, &buf))
            {
                /* mmap */
                camera->bufs[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera->fd, buf.m.offset);
                if(camera->bufs[i] == MAP_FAILED) 
                {
                    printf("Unable to map buffer");
                    goto _err;
                }
            }
            else
            {
                printf("can not query buffer\n");
                goto _err;
            }            
        }
    }
    else
    {
        printf("can not request buffers\n");
        goto _ioc_reqbufs_err;
    }

    /* 6. queue buffer */
    for(i = 0; i < buf_cnt; ++i) 
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        buf.index = i;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        if (0 != ioctl(camera->fd, VIDIOC_QBUF, &buf))
        {
            perror("Unable to queue buffer");
            goto _ioc_qbuf_err;
        }
    }

    camera->bufs_index = 0;     // init camera struct
    camera->buf_length = 0;
    return 0;

_ioc_qbuf_err:
_ioc_reqbufs_err:
_ioc_sfmt_err:
_ioc_querycap_err:
_err:
    return -1;
}

int main(int argc, char **argv)
{
    int ret;
    lcd_mes lcd;
    camera_mes camera;
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    struct pollfd fds[1];

    if (argc != 3)
    {
        printf("Usage: %s </dev/videoX> </dev/fbX>\n", argv[0]);
        return -1;
    }

    /* lcd init */
    ret = lcd_init(argv[2], &lcd);
    if (ret == -1)
    {
        printf("lcd init err !\n");
        goto _err;
    }

    printf("\n-------------- lcd message --------------\n");
    printf("screen pixel: %d x %d\n", lcd.lcd_width, lcd.lcd_height);
    printf("line width: %d (byte)\n", lcd.line_width);
	printf("bpp: %d\n", lcd.bpp);
    printf("-----------------------------------------\n");

    /* camera init */
    camera.frame_x_size = lcd.lcd_width;
    camera.frame_y_size = lcd.lcd_height;
    ret = camera_init(argv[1], &camera);
    if (ret == -1)
    {
        printf("camera init err !\n");
        goto _err;
    }

    printf("\n------------ camera message -------------\n");
    printf("frame size: %d x %d\n", camera.frame_x_size, camera.frame_y_size);
    printf("format: %s\n", camera.fmt);
    printf("-----------------------------------------\n");

    /* start camera */
    if (0 != ioctl(camera.fd, VIDIOC_STREAMON, &type))
    {
        printf("Unable to start capture\n");
        goto _err;
    }

    printf("\nstart camera ...\n");
    while (1)
    {
        /* poll */
        memset(fds, 0, sizeof(fds));
        fds[0].fd = camera.fd;
        fds[0].events = POLLIN;
        if (1 == poll(fds, 1, -1))
        {
            /* dequeue buffer */
            struct v4l2_buffer buf;
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            
            if (0 != ioctl(camera.fd, VIDIOC_DQBUF, &buf))
            {
                printf("Unable to dequeue buffer\n");
                goto _ioc_dqbuf_err;
            }
            
            /* jpeg show on lcd */
            camera.bufs_index = buf.index;
            camera.buf_length = buf.length;
            jpeg_show_on_lcd(&lcd, &camera);

            /* queue buffer */
            if (0 != ioctl(camera.fd, VIDIOC_QBUF, &buf))
            {
                printf("Unable to queue buffer");
                goto _ioc_qbuf_err;
            }
        }
    }

    /* close camera */
    if (0 != ioctl(camera.fd, VIDIOC_STREAMOFF, &type))
    {
        printf("Unable to stop capture\n");
        goto _ioc_streamoff_err;
    }
    close(camera.fd);

    return 0;

_ioc_streamoff_err:
_ioc_qbuf_err:
_ioc_dqbuf_err:
_err:
    return -1;
}

5、测试

5.1、编译应用程序

如果你使用的buildroot系统,需要交叉编译。

这里测试所使用的板子跑的是ubuntu,执行如下命令直接编译:

sudo gcc -o uvctolcd uvctolcd.c -ljpeg

5.2、运行应用程序

sudo ./uvctolcd /dev/video10 /dev/fb0


6、总结

参考文章:Linux摄像头(v4l2应用)——在LCD上实时显示摄像头采集JPEG数据_v4l2调用并显示图像-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2309263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MacOS本地部署Deepseek,不联网也可以使用AI,保护隐私

苹果笔记本本地部署deepseek主要用到Ollama与open-webui 1. 安装Ollama “Ollama” 是一个轻量级的 AI 模型运行时环境&#xff08;runtime&#xff09;&#xff0c;旨在简化在本地部署和使用大语言模型&#xff08;LLM&#xff09;的过程。它由 Vicarious 公司开发&#xff…

Sqlserver安全篇之_启用TLS即配置SQL Server 数据库引擎以加密连接

官方文档 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/configure-sql-server-encryption?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/manage-certificates?viewsql-server-ver15&pre…

创建一个MCP服务器,并在Cline中使用,增强自定义功能。

MCP介绍 MCP 是一个开放协议&#xff0c;它标准化了应用程序如何向LLMs提供上下文。可以将 MCP 视为 AI 应用程序的 USB-C 端口。正如 USB-C 提供了一种标准化的方法来将您的设备连接到各种外围设备和配件一样&#xff0c;MCP 提供了一种标准化的方法来将 AI 模型连接到不同的…

游戏引擎学习第131天

仓库:https://gitee.com/mrxiao_com/2d_game_3 运行游戏并识别我们的小问题 今天的工作重点是对游戏引擎进行架构优化&#xff0c;特别是针对渲染和多线程的部分。目前&#xff0c;我们的目标是让地面块在独立线程上进行渲染&#xff0c;以提高性能。在此过程中&#xff0c;我…

人大金仓国产数据库与PostgreSQL

一、简介 在前面项目中&#xff0c;我们使用若依前后端分离整合人大金仓&#xff0c;在后续开发过程中&#xff0c;我们经常因为各种”不适配“问题&#xff0c;但可以感觉得到大部分问题&#xff0c;将人大金仓视为postgreSQL就能去解决大部分问题。据了解&#xff0c;Kingba…

MacBook Pro使用FFmpeg捕获摄像头与麦克风推流音视频

FFmpeg查看macos系统音视频设备列表 ffmpeg -f avfoundation -list_devices true -i "" 使用摄像头及麦克风同时推送音频及视频流: ffmpeg -f avfoundation -pixel_format yuyv422 -framerate 30 -i "0:1" -c:v libx264 -preset ultrafast -b:v 1000k -…

APISIX Dashboard上的配置操作

文章目录 登录配置路由配置消费者创建后端服务项目配置上游再创建一个路由测试 登录 http://192.168.10.101:9000/user/login?redirect%2Fdashboard 根据docker 容器里的指定端口&#xff1a; 配置路由 通过apisix 的API管理接口来创建&#xff08;此路由&#xff0c;直接…

计算机毕业设计SpringBoot+Vue.js人力资源管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

腾讯云扩容记录

腾讯云扩容&#xff1a; sudo yum install -y cloud-utils-growpart 安装扩容工具 sudo file -s /dev/vda1 有数据 sudo LC_ALLen_US.UTF-8 growpart /dev/vda 1 sudo resize2fs /dev/vda1 df -Th 完毕 以下是对执行的命令的详细解释以及背后的原理&#xff1a; 1. 安装 cloud…

Cherry Studio + 火山引擎 构建个人AI智能知识库

&#x1f349;在信息化时代&#xff0c;个人知识库的构建对于提高工作效率、知识管理和信息提取尤为重要。尤其是当这些知识库能结合人工智能来智能化地整理、分类和管理数据时&#xff0c;效果更为显著。我最近尝试通过 Cherry Studio 和 火山引擎 来搭建个人智能知识库&#…

完美解锁便捷版!

视频文件的种类繁多&#xff0c;包括常见的格式如MP4、AVI、MOV、MKV等。随着视频技术的发展&#xff0c;新的编码格式如HEVC&#xff08;H.265&#xff09;和AV1也逐渐被广泛应用&#xff1b;视频文件通常会经过压缩&#xff0c;并且每种格式都有其独特的编码方式和特性&#…

JAVA SE 包装类和泛型

文章目录 &#x1f4d5;1. 包装类✏️1.1 基本数据类型和对应的包装类✏️1.2 装箱和拆箱✏️1.3 自动装箱和自动拆箱 &#x1f4d5;2. 泛型✏️2.1 泛型的语法✏️2.2 泛型类的使用✏️2.3 裸类型(Raw Type)✏️2.4 擦除机制✏️2.5 泛型的上界✏️2.6 泛型方法✏️2.7 通配符…

人工智能AI在汽车设计领域的应用探索

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…

Vue3国际化开发实战:i18n-Ally + vue-i18n@next高效配置教程,项目中文显示

本文详解 Vue3 国际化开发全流程&#xff1a;从安装 vue-i18nnext 到配置多语言文件&#xff08;JSON/YAML&#xff09;&#xff0c;结合 i18n-Ally 插件实现高效翻译管理。重点涵盖&#xff1a; 工程配置&#xff1a;创建 i18n 实例、模块化语言文件结构&#xff08;支持命名…

深入理解Spring @Async:异步编程的利器与实战指南

一、为什么需要异步编程&#xff1f; 在现代高并发系统中&#xff0c;同步阻塞式编程会带来两大核心问题&#xff1a; // 同步处理示例 public void processOrder(Order order) {// 1. 保存订单&#xff08;耗时50ms&#xff09;orderRepository.save(order); // 2. 发送短信…

java后端开发day25--阶段项目(二)

&#xff08;以下内容全部来自上述课程&#xff09; 1.美化界面 private void initImage() {//路径分两种&#xff1a;//1.绝对路径&#xff1a;从盘符开始写的路径 D:\\aaa\\bbb\\ccc.jpg//2.相对路径&#xff1a;从当前项目开始写的路径 aaa\\bbb\\ccc.jpg//添加图片的时…

【 开发知识点 一 】 随机数生成器 /dev/urandom 和 /dev/random

文章目录 一、随机数生成器 是什么 ?二、为什么 需要 随机数生成器 ?三、随机数生成器 基本原理四、随机数生成器 三个输出接口五、随机生成器 应用1、简单应用2、项目应用一、随机数生成器 是什么 ? /dev/random 和 /dev/urandom 是 Linux 上的字符设备文件,它们是随机数…

(动态规划 最长递增的子序列)leetcode 300

这道题我第一眼反应就是暴力&#xff0c;但是暴力的话就是n*n-1*n-2*...n-(n-1) 也就是O(n^n)dfs做绝对超时 贪心也不行&#xff0c;这里是子序列&#xff0c;要考虑在ni的范围内考虑多种路线取最优&#xff0c;所以用动态规划 如何用动态规划呢&#xff1f; 答&#xff1a;…

小皮网站搭建

前提&#xff1a;小皮的安装下载 1、在www目录下创建一个新的文件夹&#xff0c;用来存放网站源码&#xff1b; 2、安装数据库管理工具phpMyadmin 3、新建数据表 添加字段 4、创建网站 5、前端的登录代码 注册 后端php 网页展示 登录成功跳转welcome.php

【CSS—前端快速入门】CSS 常用样式

CSS 常用 CSS 样式 1. 前端样式查询网站&#xff1a; MDN Web Docs (mozilla.org) w3school 2. border 2.1 借助 MDN 了解 border 我们借助 MDN 网站来学习 border 样式的使用&#xff1a; 2.2 border 常见属性 保存代码&#xff0c;打开页面&#xff1a; 对于标签不同样式的…