Camera | 4.瑞芯微平台MIPI摄像头应用程序编写
来源:一口Linux发布时间:2023-02-143614浏览
询问 AI前面3篇我们讲解了camera的基础概念,MIPI协议,CSI2,常用命令等,本文带领大家入门,如何用c语言编写应用程序来操作摄像头。
Linux下摄像头驱动都是基于v4l2架构,要基于该架构编写摄像头的应用程序,必须先要搞清楚什么是v4l2。
vl42是video for Linux 2的缩写,是一套Linux内核视频设备的驱动框架,该驱动框架为应用层提供一套统一的操作接口(一系列的ioctl)
https://linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/
官网有一个简单的用于抓图的程序capture.c。
本文后面基于该实例编写一个最简单的抓图程序。
V4L2 :video for linux 2,是 linux ⾥⼀套标准的视频驱动,让应⽤层可以像访问普通⽂件⼀样对**/dev/videoX** 节点进⾏ open 、 read 、 ioctl 等操作。
V4L2在设计时,是要支持很多广泛的设备的,它们之中只有一部分在本质上是真正的视频设备,可以支持多种设备,它可以有以下几种接口:
1. video capture interface(捕获):视频采集接口,这种接口应用于摄像头,v4l2在最初设计的时候就是应用于这种功能
2. video output interface(输出):视频输出接口,将静止图像或图像序列编码为模拟视频信号,通过此接口,应用程序可以控制编码过程并将图像从用户空间移动到驱动程序
3. video overlay interface(预览):视频直接传输接口,可以将采集到的视频数据直接传输到显示设备,不需要cpu参与,这种方式的显示图像的效率比其他方式高得多
本文主要讲解如何使用capture功能。
mmap( )系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然
采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝
*(1)设置picture的属性*(2) 初始化video_mbuf,以得到所映射的buffer的信息
ioctl(vd->fd,VIDIOCGMBUF,(vd->mbuf))
*(3)可以修改video_mmap和帧状态的当前设置
Eg.vd->mmap.format=VIDEO_PALETTE_RGB24
vd->framestat[0]=vd->framestat[1]=0;vd->frame=0;
*(4)将mmap与video_mbuf绑定
void*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset)
len//映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起
Prot//指定共享内存的访问权限PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行)
flags//MAP_SHAREDMAP_PRIVATE中必选一个//MAP_FIXED不推荐使用addr//共内存享的起始地址,一般设0,表示由系统分配
Mmap()返回值是系统实际分配的起始地址
if((vd->map=(unsignedchar*)mmap(0,vd->mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,vd->fd,0))<0)
{
perror("v4l_mmapmmap:");
return-1;
}
*(5)Mmap方式下真正做视频截取的 VIDIOCMCAPTURE
ioctl(vd->fd,VIDIOCMCAPTURE,(vd->mmap));
若调用成功,开始一帧的截取,是非阻塞的,
是否截取完毕留给VIDIOCSYNC来判断
*(6)调用VIDIOCSYNC等待一帧截取结束
if(ioctl(vd->fd,VIDIOCSYNC,frame)<0)
{
perror("v4l_sync:VIDIOCSYNC");
return-1;
}
若成功,表明一帧截取已完成。可以开始做下一次 VIDIOCMCAPTURE
frame是当前截取的帧的序号。
关于双缓冲:
video_bmufbmuf.frames=2;
一帧被处理时可以采集另一帧
intframe;//当前采集的是哪一帧
intframestat[2];//帧的状态没开始采集|等待采集结束
帧的地址由vd->map+vd->mbuf.offsets[vd->frame]得到
采集工作结束后调用munmap取消绑定
munmap(vd->map,vd->mbuf.size)
关于缓冲大小,图象等的属性须由使用者事先设置
intread(要访问的文件描述符;指向要读写的信息的指针;应该读写的字符数);
返回值为实际读写的字符数
实例:
intlen;
unsignedchar*vd->map=(unsignedchar*)malloc(vd->capability.maxwidth*vd->capability.maxheight);
len=read(vd->fd,vd->map,vd->capability.maxwidth*vd->capability.maxheight*3);
对设备的大多数操作都是应用层通过调用ioctl实现的,不同的命令需要操作不同的文件设备节点,具体的需要根据拓扑结构来决定操作那个字符设备。
以下是瑞芯微rk3568平台的摄像头拓扑图,移植了ov13850摄像头。
在这里插入图片描述- 其中摄像头对应的此设备为:/dev/v4l-subdev3
- 应用层要配置通用配置、或者获取图像,需要操作设备/dev/video0
- 有一些摄像头专用的命令,我们可以操作/dev/v4l-subdev3
ov13850摄像头驱动中注册了一些命令对应的回调函数:

这些回调函数都注册到了V4L2架构中,我们可以通过字符设备/dev/videox,、/dev/v4l-subdevx直接或者间接访问到这些回调函数。
V4L2定义了一些通用的命令,操作字符设备/dev/videox即可调用,命令具体定义如下:
kernel\drivers\media\v4l2-core\v4l2-ioctl.c
可以通过数组名+命令对应的数值方式访问对应的回调函数。该数组定义如下:
structv4l2_ioctl_info{
unsignedintioctl;
u32flags;
constchar*constname;
int(*func)(conststructv4l2_ioctl_ops*ops,structfile*file,
void*fh,void*p);
void(*debug)(constvoid*arg,boolwrite_only);
};
字符设备**/dev/v4l-subdevx**支持的命令如下:
@kernel\drivers\media\v4l2-core\v4l2-subdev.c
staticlongsubdev_do_ioctl(structfile*file,unsignedintcmd,void*arg)
{
……
switch(cmd){
caseVIDIOC_QUERYCTRL:
……
caseVIDIOC_QUERY_EXT_CTRL:
……
caseVIDIOC_QUERYMENU:
……
caseVIDIOC_G_CTRL:
……
caseVIDIOC_S_CTRL:
……
caseVIDIOC_G_EXT_CTRLS:
……
caseVIDIOC_S_EXT_CTRLS:
……
caseVIDIOC_TRY_EXT_CTRLS:
……
caseVIDIOC_DQEVENT:
……
caseVIDIOC_SUBSCRIBE_EVENT:
returnv4l2_subdev_call(sd,core,subscribe_event,vfh,arg);
caseVIDIOC_UNSUBSCRIBE_EVENT:
returnv4l2_subdev_call(sd,core,unsubscribe_event,vfh,arg);
#ifdefCONFIG_VIDEO_ADV_DEBUG
caseVIDIOC_DBG_G_REGISTER:
……
caseVIDIOC_DBG_S_REGISTER:
……
caseVIDIOC_DBG_G_CHIP_INFO:
……
#endif
caseVIDIOC_LOG_STATUS:{
……
#ifdefined(CONFIG_VIDEO_V4L2_SUBDEV_API)
caseVIDIOC_SUBDEV_G_FMT:{
……
caseVIDIOC_SUBDEV_S_FMT:{
……
caseVIDIOC_SUBDEV_G_CROP:{
……
}
caseVIDIOC_SUBDEV_S_CROP:{
……
}
caseVIDIOC_SUBDEV_ENUM_MBUS_CODE:{
……
}
caseVIDIOC_SUBDEV_ENUM_FRAME_SIZE:{
……
}
caseVIDIOC_SUBDEV_G_FRAME_INTERVAL:{
……
}
caseVIDIOC_SUBDEV_S_FRAME_INTERVAL:{
……
}
caseVIDIOC_SUBDEV_ENUM_FRAME_INTERVAL:{
……
}
caseVIDIOC_SUBDEV_G_SELECTION:{
……
}
caseVIDIOC_SUBDEV_S_SELECTION:{
……
}
caseVIDIOC_G_EDID:{
……
}
caseVIDIOC_S_EDID:{
……
}
caseVIDIOC_SUBDEV_DV_TIMINGS_CAP:{
……
}
caseVIDIOC_SUBDEV_ENUM_DV_TIMINGS:{
……
}
caseVIDIOC_SUBDEV_QUERY_DV_TIMINGS:
returnv4l2_subdev_call(sd,video,query_dv_timings,arg);
caseVIDIOC_SUBDEV_G_DV_TIMINGS:
returnv4l2_subdev_call(sd,video,g_dv_timings,arg);
caseVIDIOC_SUBDEV_S_DV_TIMINGS:
returnv4l2_subdev_call(sd,video,s_dv_timings,arg);
caseVIDIOC_G_INPUT:
returnv4l2_subdev_call(sd,video,g_input_status,arg);
caseVIDIOC_SUBDEV_G_STD:
returnv4l2_subdev_call(sd,video,g_std,arg);
caseVIDIOC_SUBDEV_S_STD:{
v4l2_std_id*std=arg;
returnv4l2_subdev_call(sd,video,s_std,*std);
}
caseVIDIOC_SUBDEV_ENUMSTD:{
……
}
caseVIDIOC_SUBDEV_QUERYSTD:
……
}
return0;
}
这其中有一些命令是和字符设备/dev/videox的命令重复的,
比如:VIDIOC_S_CTRL,
VIDIOC_SUBDEV_开头的则是subdev私有的。
关于这些命令和回调函数,后续会再深入讲解,对于应用程序开发,
我们首先搞清楚设备的拓扑结构,然后需要知道我们要执行的命令功能以及对应的是哪一个设备节点即可。
参见结构体见
/usr/include/linux/videodev2.h
查询设备的功能
由于V4L2涵盖了各种各样的设备,因此并非API的所有方面都适用于所有类型的设备,在使用v4l2设备时,必须调用此API,获得设备支持的功能(capture、output、overlay…)
| ID | 描述 |
|---|---|
| VIDIOC_QUERYCAP | 查询设备功能 |
structv4l2_capability
{
u8driver[16];//驱动名字
u8card[32];//设备名字
u8bus_info[32];//设备在系统中的位置
u32version;//驱动版本号
u32capabilities;//设备支持的操作
u32reserved[4];//保留字段
};
capabilities 常用值:
V4L2_CAP_VIDEO_CAPTURE//是否支持图像获取
应用优先级
当多个应用程序共享设备时,可能需要为它们分配不同的优先级。视频录制应用程序可以例如阻止其他应用程序改变视频控制或切换当前的电视频道。
另一个目标是允许在后台工作的低优先级应用程序,这些应用程序可以被用户控制的应用程序抢占,并在以后自动重新获得对设备的控制
| ID | 描述 |
|---|---|
| VIDIOC_G_PRIORITY | 获取优先级 |
| VIDIOC_S_PRIORITY | 设置优先级 |
输入和输出设备
| ID | 描述 |
|---|---|
| VIDIOC_ENUMINPUT | 枚举视频输入设备 |
| VIDIOC_G_INPUT | 获取当前的视频输入设备 |
| VIDIOC_S_INPUT | 设置视频输入设备 |
| VIDIOC_ENUMOUTPUT | 枚举视频输出设备 |
| VIDIOC_G_OUTPUT | 获取当前视频输出设备 |
| VIDIOC_S_OUTPUT | 设置视频输出设备 |
| VIDIOC_ENUMAUDIO | 枚举音频输入设备 |
| VIDIOC_G_AUDIO | 获取当前音频输入设备 |
| VIDIOC_S_AUDIO | 设置音频输入设备 |
| VIDIOC_ENUMAUDOUT | 枚举音频输出设备 |
| VIDIOC_G_OUTPUT | 获取音频输出设备 |
| VIDIOC_S_AUDOUT | 设置音频输出设备 |
VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 用来查询和选则当前的 input,一个 video 设备 节点可能对应多个视频源,比如 saf7113 可以最多支持四路 cvbs 输入,如果上层想在四 个cvbs视频输入间切换,那么就要调用 ioctl(fd, VIDIOC_S_INPUT, input) 来切换。
VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回当前的 video input和output的index.
structv4l2_input{
__u32index;/*Whichinput*/
__u8name[32];/*Label*/
__u32type;/*Typeofinput*/
__u32audioset;/*Associatedaudios(bitfield)*/
__u32tuner;/*Associatedtuner*/
v4l2_std_idstd;
__u32status;
__u32reserved[4];
};
我们可以通过VIDIOC_ENUMINPUT and VIDIOC_ENUMOUTPUT 分别列举一个input或者 output的信息,我们使用一个v4l2_input结构体来存放查询结果,这个结构体中有一个 index域用来指定你索要查询的是第几个input/ouput,如果你所查询的这个input是当前正 在使用的,那么在v4l2_input还会包含一些当前的状态信息,如果所 查询的input/output 不存在,那么回返回EINVAL错误,所以,我们通过循环查找,直到返回错误来遍历所有的 input/output. VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回当前的video input和output 的index.
视频标准
| ID | 描述 |
|---|---|
| VIDIOC_ENUMSTD | 枚举设备支持的所有标准 |
| VIDIOC_G_STD | 获取当前正在使用的标准 |
| VIDIOC_S_STD | 设置视频标准 |
| VIDIOC_QUERYSTD | 有的设备支持自动侦测输入源的视频标准,此ioctl获取检测到的标准 |
typedefu64v4l2_std_id;
structv4l2_standard{
u32index;
v4l2_std_idid;
u8name[24];
structv4l2_fractframeperiod;/*Frames,notfields*/
u32framelines;
u32reserved[4];
};
当然世界上现在有多个视频标准,如NTSC和PAL,他们又细分为好多种,那么我们的设 备输入/输出究竟支持什么样的标准呢?我们的当前在使用的输入和输出正在使用的是哪 个标准呢?我们怎么设置我们的某个输入输出使用的标准呢?这都是有方法的。
查询我们的输入支持什么标准,首先就得找到当前的这个输入的index,然后查出它的 属性,在其属性里面可以得到该输入所支持的标准,将它所支持的各个标准与所有的标准 的信息进行比较,就可以获知所支持的各个标准的属性。一个输入所支持的标准应该是一 个集合,而这个集合是用bit与的方式用一个64位数字表示。因此我们所查到的是一个数字。
控制属性
| ID | 描述 |
|---|---|
| VIDIOC_QUERYCTRL | 查询指定的control详细信息 |
| VIDIOC_QUERYMENU | 查询menu |
| VIDIOC_G_CTRL | 获取设备指定control的当前信息 |
| VIDIOC_S_CTRL | 设置设备指定的control |
图像格式
图像由多种格式YUV和RGB还有压缩格式等等,其中每种格式又分有多种格式,比如RGB:RGB565、RGB888…
所以在使用设备时,需要对格式进行设置
| ID | 描述 |
|---|---|
| VIDIOC_ENUM_FMT | 枚举设备支持的图像格式 |
| VIDIOC_G_FMT | 获取当前设备的图像格式 |
| VIDIOC_S_FMT | 设置图像格式 |
| VIDIOC_TRY_FMT | 测试设备是否支持此格式 |
查询并显示所有支持的格式:VIDIOC_ENUM_FMT
structv4l2_fmtdesc
{
u32index;//要查询的格式序号,应用程序设置
enumv4l2_buf_typetype;//帧类型,应用程序设置
u32flags;//是否为压缩格式
u8description[32];//格式名称
u32pixelformat;//格式
u32reserved[4];//保留
};
查看或设置当前格式: VIDIOC_G_FMT, VIDIOC_S_FMT
structv4l2_format
{
enumv4l2_buf_typetype;//帧类型,应用程序设置
unionfmt
{
structv4l2_pix_formatpix;//视频设备使用
structv4l2_windowwin;
structv4l2_vbi_formatvbi;
structv4l2_sliced_vbi_formatsliced;
u8raw_data[200];
};
};
structv4l2_pix_format
{
u32width;//帧宽,单位像素
u32height;//帧高,单位像素
u32pixelformat;//帧格式
enumv4l2_fieldfield;
u32bytesperline;
u32sizeimage;
enumv4l2_colorspacecolorspace;
u32priv;
};
图像裁剪、插入与缩放
| ID | 描述 |
|---|---|
| VIDIOC_CROPCAP | 获取图像裁剪缩放能力 |
| VIDIOC_G_CROP | 获取当前的裁剪矩阵 |
| VIDIOC_S_CROP | 设置裁剪矩阵 |
Cropping 和 scaling 主要指的是图像的取景范围及图片的比例缩放的支持。Crop 就 是把得到的数据作一定的裁剪和伸缩,裁剪可以只取样我们可以得到的图像大小的一部分, 剪裁的主要参数是位置、长度、宽度。而 scale 的设置是通过 VIDIOC_G_FMT 和 VIDIOC_S_FMT 来获得和设置当前的 image 的长度,宽度来实现的。看下图:
我们可以假设 bounds 是 sensor 最大能捕捉到的图像范围,而 defrect 是设备默认 的最大取样范围,这个可以通过 VIDIOC_CROPCAP 的 ioctl 来获得设备的 crap 相关的属 性 v4l2_cropcap,其中的 bounds 就是这个 bounds,其实就是上限。每个设备都有个默 认的取样范围,就是 defrect,就是 default rect 的意思,它比 bounds 要小一些。这 个范围也是通过 VIDIOC_CROPCAP 的 ioctl 来获得的 v4l2_cropcap 结构中的 defrect 来表示的,我们可以通过 VIDIOC_G_CROP 和 VIDIOC_S_CROP 来获取和设置设备当前的 crop 设置。
设置设备捕捉能力的参数
structv4l2_cropcap
{
enumv4l2_buf_typetype;//数据流的类型,应用程序设置
structv4l2_rectbounds;//这是camera的镜头能捕捉到的窗口大小的局限
structv4l2_rectdefrect;//定义默认窗口大小,包括起点位置及长,宽的大小,大小以像素为单位
structv4l2_fractpixelaspect;//定义了图片的宽高比
};
设置窗口取景参数 VIDIOC_G_CROP 和 VIDIOC_S_CROP
structv4l2_crop
{
enumv4l2_buf_typetype;//应用程序设置
structv4l2_rectc;
}
数据的输入和输出
内核中使用缓存队列对图像数据进行管理,用户空间获取图像数据有两种方式,一种是通过read、write方式读取内核空间的缓存,一种是将内核空间的缓存映射到用户空间,即streaming。在操作v4l2设备时,通过VIDIOC_QUERYCAP获取设备支持哪种方式。streaming就是在内核空间中维护一个缓存队列,然后将内存映射到用户空间,应用读取图像数据就是一个不断地出队列和入队列的过程,如下图所示

| ID | 描述 |
|---|---|
| VIDIOC_REQBUFS | 申请缓存 |
| VIDIOC_QUERYBUF | 获取缓存信息 |
| VIDIOC_QBUF | 将缓存放入队列中 |
| VIDIOC_DQBUF | 将缓存从队列中取出 |
1. 向设备申请缓冲区 VIDIOC_REQBUFS
structv4l2_requestbuffers
{
u32count;//缓冲区内缓冲帧的数目
enumv4l2_buf_typetype;//缓冲帧数据格式
enumv4l2_memorymemory;//区别是内存映射还是用户指针方式
u32reserved[2];
};
enumv4l2_memoy
{
V4L2_MEMORY_MMAP,V4L2_MEMORY_USERPTR
};
获取缓冲帧的地址,长度:VIDIOC_QUERYBUF
structv4l2_buffer
{
u32index;//buffer序号
enumv4l2_buf_typetype;//buffer类型
u32byteused;//buffer中已使用的字节数
u32flags;//区分是MMAP还是USERPTR
enumv4l2_fieldfield;
structtimevaltimestamp;//获取第一个字节时的系统时间
structv4l2_timecodetimecode;
u32sequence;//队列中的序号
enumv4l2_memorymemory;//IO方式,被应用程序设置
unionm
{
u32offset;//缓冲帧地址,只对MMAP有效
unsignedlonguserptr;
};
u32length;//缓冲帧长度
u32input;
u32reserved;
};
2. 内存映射MMAP 及定义一个结构体来映射每个缓冲帧。相关结构体:
structbuffer
{
void*start;
unsignedintlength;
}*buffers;
相关函数:
#include<sys/mman.h>
void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset)
参数:
addr映射起始地址,一般为NULL,让内核自动选择
length被映射内存块的长度
prot标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE
flags确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE
fd,offset,确定被映射的内存地址返回成功映射后的地址,不成功返回MAP_FAILED((void*)-1)
3.将所有的缓存放入队列
structv4l2_bufferv4l2_buffer;
for(i=0;i<nr_bufs;i++)
{
memset(v4l2_buffer,0,sizeof(structv4l2_buffer));
v4l2_buffer.index=i;//想要放入队列的缓存
v4l2_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buffer.memory=V4L2_MEMORY_MMAP;
ret=ioctl(fd,VIDIOC_QBUF,v4l2_buffer);
if(ret<0)
{
printf("Unabletoqueuebuffer.\n");
return-1;
}
}
VIDIOC_STREAMON, VIDIOC_STREAMOFF
type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON,type);
v4l2设备的命令比较多,其实常用的并不是很多,下面通过一个实例来详细讲解,如何操作v4l2设备。
本例,将常用的摄像头配置参数等装成结构体:
structv4l2_dev
{
intfd;//videoO对应的设备描述符
intsub_fd;
constchar*path;//字符设备/dev/videoO
constchar*name;//摄像头名称
constchar*subdev_path;//字符设备/dev/v4l-subdev3
constchar*out_type;//输出图像格式
enumv4l2_buf_typebuf_type;//缓存类型
intformat;//像素格式
intwidth;//图像宽度
intheight;//图像高度
unsignedintreq_count;//缓存数量
enumv4l2_memorymemory_type;//读取图像的方法,DMA还是MMAP
structbuffer*buffers;//缓冲区
unsignedlonginttimestamp;//时长度
intdata_len;//图像数据长度
unsignedchar*out_data;//图像数据
};
本例填写的摄像头ov13850的配置信息如下:
structv4l2_devov13850={
.fd=-1,
.sub_fd=-1,
.path="/dev/video0",
.name="ov13850",
.subdev_path="/dev/v4l-subdev3",
.out_type="nv12",
.buf_type=V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
.format=V4L2_PIX_FMT_NV12,
.width=800,
.height=600,
.req_count=4,
.memory_type=V4L2_MEMORY_MMAP,
.buffers=NULL,
.timestamp=0,
.data_len=0,
.out_data=NULL,
};
v4l2设备一般操作流程如下图所示:

各功能对应的ioctrl命令如下:

测试程序一口君已经上传到gitee:
https://gitee.com/yikoulinux/v4l2-app
gitclonegit@gitee.com:yikoulinux/v4l2-app.git

欢迎各位老铁star。
以下是在瑞芯微rk3568实际测试的log。
rk3568_r:/#/data/capture
/data/capture
Open/dev/video0succeed-3
Open/dev/v4l-subdev3succeed
-------VIDIOC_QUERYCAP----
driver:rkisp_v5
card:rkisp_mainpath
bus_info:platform:rkisp-vir0
version:1.8.0
capabilities:84201000
VideoCaptureMplane
Streaming
VIDIOC_S_FMTsucceed!
width800,height600,size720000,bytesperline0,formatNV12
VIDIOC_REQBUFSsucceed!
Memorymapsucceed!
VIDIOC_QBUFsucceed!
VIDIOC_STREAMONsucceed!
image:sequence=0,timestamp=1115378780
image:sequence=1,timestamp=1115511890
image:sequence=2,timestamp=1115645004
image:sequence=3,timestamp=1115778130
image:sequence=4,timestamp=1115911257
image:sequence=5,timestamp=1116044365
image:sequence=6,timestamp=1116177498
image:sequence=7,timestamp=1116310610
image:sequence=8,timestamp=1116443739
image:sequence=9,timestamp=1116576844
Saveoneframeto/data/ov13850.nv12succeed!
VIDIOC_STREAMOFFsucceed!
文中各种mipi技术文档,后台回复关键字:mipi
掌握了这些命令,我们就可以调试摄像头了。
后面还会继续更新几篇Camera文章,
建议大家订阅本专题!
也可以后台留言,加一口君好友yikoupeng,
拉你进高质量技术交流群。
一口Linux
关注,回复【1024】海量Linux资料赠送
精彩文章合集
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。

