嵌入式linux相机(2)

  本人从0开始学习linux,使用的是韦东山的教程,在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。本人将前几章的内容大致学完之后,考虑到后续驱动方面得更多的开始实操,后续的内容将以韦东山教程Linux项目的内容为主,学习其中的代码并手敲。做到锻炼动手能力的同时钻研其中的理论知识点。

摘要:相机部分代码的理解
摘要关键词:相机驱动、照片亮度调节、开发板ip配置

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

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

c 复制代码
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

代码程序

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

c 复制代码
#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 bufs32 是什么?
这是一个包含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(直接内存访问) 直接将数据写入这些缓冲区

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

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

c 复制代码
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 命令行,更改设置,设置内容如图所示。

重启之后可以看到。

电脑之间实现相互应答。

c 复制代码
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值

相关推荐
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao3 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush44 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5204 天前
Linux 11 动态监控指令top
linux
胖咕噜的稞达鸭4 天前
如何写好一个skill
人工智能·数码相机