嵌入式linux相机(2)

发布于:2025-09-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

  本人从0开始学习linux,使用的是韦东山的教程,在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。本人将前几章的内容大致学完之后,考虑到后续驱动方面得更多的开始实操,后续的内容将以韦东山教程Linux项目的内容为主,学习其中的代码并手敲。做到锻炼动手能力的同时钻研其中的理论知识点。
摘要:相机部分代码的理解
摘要关键词:相机驱动、照片亮度调节、开发板ip配置

问题汇总
1.为什么int变量给枚举不会报错?  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2.void *bufs[32] 是什么?
3.为什么需要配置和检查捕获能力?
4.申请缓冲区是向谁申请?
5.缓冲区位于哪里?
6.明明要传入函数指针,为什么可以传入结构体?
7.是将数据都放到了bufs[i]里面了对吧,是这段代码实现的数据存放吗? 
8.尝试了adb拉取所有图片发现拉取不了,只能从头开始设置IP地址。 

第一个项目相关使用的命令行,得记录使用的数据分辨率大小

在这里插入图片描述

arm-buildroot-linux-gnueabihf-gcc -o video_get_data video_get_data.c
adb push video_get_data root
./video_get_data /dev/video1
adb pull root/video_raw_data_0010.jpg  home

代码程序

代码实现输出支持的图像大小以及格式,并截取图片。


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

/* ./video_test </dev/video0>  */

int main(int argc, char **argv)
{
    int fd;
    struct v4l2_fmtdesc fmtdesc;              // 参数结构体初始化
    int fmt_index = 0;
    int frame_index = 0;
    struct v4l2_frmsizeenum fsenum;
    void *bufs[32];                          
    int buf_cnt;
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    struct pollfd fds[1];
    char filename[32];
    int file_cnt = 0;
    if (argc != 2)
    {
        printf("Usage: %s </dev/videoX>,print detail for video device\n",argv[0]);
        return -1;

    }

    /* open */
    fd = open(argv[1],O_RDWR);
    if (fd < 0)
    {
        printf("can not open %s \n",argv[1]);
        return -1;
    }
    
    // 查询能力
    struct v4l2_capability capability;
    memset(&capability, 0, sizeof(struct v4l2_capability));

    if (0 == ioctl(fd, VIDIOC_QUERYCAP, &capability))
    {
        if((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) 
        {
            printf("Error opening device %s: video capture not supported.\n",
                    argv[1]);
            return -1;
        }

        if(!(capability.capabilities & V4L2_CAP_STREAMING)) 
        {
            printf("%s does not support streaming i/o\n", argv[1]);
            return -1;
        }
            
    }
    else 
    {
        printf("can not get capability\n");
        return -1;
    }


    /* */
    while(1)
    {
        /* 枚举格式 */
        /* 枚举这种格式所支持的帧大小*/
        fmtdesc.index = fmt_index;  // 比如从0开始
        fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 指定type为"捕获"
        if(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != 0) break;
        frame_index = 0;
        while(1)
        {
            //枚举所支持的帧大小
            memset(&fsenum,0,sizeof(struct v4l2_frmsizeenum));
            fsenum.pixel_format = fmtdesc.pixelformat;
            fsenum.index = frame_index;
            if(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&fsenum) == 0)
            {
                printf("format %s %d ,framesize %d x %d \n",fmtdesc.description,fmtdesc.pixelformat,fsenum.discrete.width,fsenum.discrete.height);
            }
            else break;
            frame_index++;
        }
        fmt_index++;
    }
    // 设置摄像头分辨率
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 800;
    fmt.fmt.pix.height = 480;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;
    if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt))
    {
        printf("set format ok :%d x %d\n",fmt.fmt.pix.width,fmt.fmt.pix.height);
    }
    else 
    {
        printf("can not set format\n");
        return -1;
    }


    /*
     * 申请 buffers
     */
    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(fd, VIDIOC_REQBUFS, &rb))
    {

        /*
        * 申请成功后,map the buffers
        */
        buf_cnt = rb.count;
        for(int 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(fd, VIDIOC_QUERYBUF, &buf))
            {
                // mmap
                bufs[i] = mmap(0 /* start anywhere */ ,
                            buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
                            buf.m.offset);
                if(bufs[i] == MAP_FAILED) 
                {
                    printf("Unable to map buffer");
                    return -1;
                }
            }
            else
            {
                printf("can not query buffer");
                return -1 ;
            }

        }
        printf("map %d bufders ok \n ",buf_cnt);
    }
    else 
    {
        printf("can not request buffers\n");
        return -1;
    }

    // 把所有buffer放入"空闲链表"
    /*
     * Queue the buffers.
     */
    for(int 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(fd, VIDIOC_QBUF, &buf))
        {
            printf("Unable to queue buffer");
            return -1;
        }

    }
    printf("queue buffers ok \n");
    // ioctl VIDIOC_STREAMON:启动摄像头
    if( 0 == ioctl(fd, VIDIOC_STREAMON, &type))
    {
        printf("start capture ok");
    }
    else 
    {
        printf("Unable to start capture");
        return -1;
    }

    //就是这段代码的问题查原因  
    while(1)     
    {
        /* poll */
        memset(fds,0,sizeof(fds));
        fds[0].fd = fd;
        fds[0].events = POLLIN;

        /* 把buffer取出队列 */
        if (1 == poll(fds,1,-1))
        {
            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(fd, VIDIOC_DQBUF, &buf));
            else{
                printf("Unable to dequeue buffer");
                return -1;
            }
         
            /* 把buffer的数据存储为文件 */
            sprintf(filename,"video_raw_data_%04d.jpg",file_cnt++);
            int fd_file = open(filename,O_RDWR | O_CREAT, 0666);
            if (fd_file < 0)
            {
                printf("can not create file: %s \n",filename);
            }
            printf("capture to %s\n",filename);
            write(fd_file,bufs[buf.index], buf.bytesused);
            close(fd_file);

            /* 把buffer放入队列 */
            if (0 == ioctl(fd, VIDIOC_DQBUF, &buf));
            else
            {
                printf("Unable to dequeue buffer");
                return -1;
            }

        }
    }

    if (0 == ioctl(fd, VIDIOC_STREAMOFF, &type))
    {
        printf("stop capture");
    }
    else 
    {
        printf("Unable to stop capture");
        return -1;
    }
    
    close(fd);
    
    return 0;

}


代码不详细解释,只有本人不熟的问题。

1.为什么int变量给枚举不会报错? int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
在C++中,枚举类型本质上就是整数类型。每个枚举值在编译时会被替换为对应的整数值。因此,将枚举值赋给int变量是完全合法的,编译器不会报错。

2.void bufs[32] 是什么?
这是一个包含32个void指针的数组:void
是一种通用指针类型,可以指向任何类型的数据
[32] 表示这是一个包含32个元素的数组,每个元素都是一个指针,可以存储任何数据类型的地址这种结构在系统编程中很常见,特别是在需要管理多种类型数据或需要通用数据存储的场景中。

3.为什么需要配置和检查捕获能力?
1. 设备多样性
不是所有视频设备都支持视频捕获功能。有些设备可能是:
视频输出设备(如HDMI输出)
音频设备
无线电接收设备
其他特殊用途设备

2. 功能支持差异
不同设备支持不同的I/O方法:
V4L2_CAP_STREAMING: 支持流式I/O(内存映射或用户指针)
V4L2_CAP_READWRITE: 支持传统的read()/write() I/O
V4L2_CAP_VIDEO_CAPTURE: 支持视频捕获
V4L2_CAP_VIDEO_OUTPUT: 支持视频输出

4.申请缓冲区是向谁申请?
不是直接向摄像头硬件申请,而是向内核中的V4L2驱动程序申请
驱动程序负责管理视频捕获所需的内存区域
这些内存区域用于存储摄像头捕获的帧数据

5.缓冲区位于哪里?
缓冲区位于内核空间,通过mmap系统调用映射到用户空间,摄像头硬件通过DMA(直接内存访问) 直接将数据写入这些缓冲区

void *memset(void *s, int c, size_t count)
{
	char *xs = s;
	while (count--)
		*xs++ = c;
	return s;
}   memset

6.明明要传入函数指针,为什么可以传入结构体? `

struct v4l2_requestbuffers rb;
memset(&rb, 0, sizeof(struct v4l2_requestbuffers));

void* 的通用性​​:
void* 是“无类型指针”,可接受任何类型的数据指针(如 int*、char*、结构体指针等)。编译器会自动将具体指针类型转换为 void*。
​​结构体的内存本质​​:
结构体在内存中是连续的字节块。memset 按字节操作内存,与数据类型无关。传入 &rb 时,函数会从该地址开始,将 sizeof(struct v4l2_requestbuffers) 个字节全部设为 0

7.是将数据都放到了bufs[i]里面了对吧,是这段代码实现的数据存放吗? 是的,你的理解非常准确!这段代码确实是实现数据存放的关键步骤之一。它通过 mmap 系统调用,​​将驱动在内核空间申请的内存映射到你的用户空间应用程序中​​,这样你就可以直接读取或处理采集到的视频帧数据了。
为了让这个过程更清晰,我们来分解一下:
内存映射 (mmap) 的作用 直接访问​​:mmap 让你无需在用户态和内核态之间拷贝数据(避免了 read/write 之类的系统调用开销),就能直接访问驱动中申请的帧缓冲区。这对于需要高效处理大量视频数据的应用至关重要。

8.尝试了adb拉取所有图片发现拉取不了,只能从头开始设置IP地址。

在这里插入图片描述
直接输入ifconfig eth0 192.168.5.9设置IP地址。
在这里插入图片描述
方案2:更改永久保存如图所示输入 vi/etc/network/interfaces 命令行,更改设置,设置内容如图所示。
在这里插入图片描述
重启之后可以看到。
在这里插入图片描述
电脑之间实现相互应答。

在这里插入图片描述

在这里插入图片描述

arm-buildroot-linux-gnueabihf-gcc -o video_brightness video_brightness.c
adb push video_brightness root
./video_brightness /dev/video1
shift+鼠标左键指定选择区域
d+回车要慢一点按

实现摄像机亮度调节如图所示。
在这里插入图片描述
pthread_create(&thread, NULL, thread_brightness_control, (void )fd);
void 的通用性
:void* 是C语言中的通用指针类型,可以指向任何数据类型,类型转换:C语言允许在整数类型和指针类型之间进行显式,转换大小兼容:在大多数系统上,指针的大小足以存储一个int值


网站公告

今日签到

点亮在社区的每一天
去签到