摄像头V4L2编程应用开发

article/2025/9/29 17:11:34

1.V4L2简介
Video for Linux two(Video4Linux2)简称V4L2,是V4L的改进版。V4L2是linux系统操作系统下一套用于采集图片、视频、和音视频数据的通用API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。
在linux中,一切皆文件,所有外设都被看做是一种特殊的文件,成为“设备文件”。视频设备可以看做是设备文件,可以像访问普通文件一样对其进行读写。
V4L2驱动的摄像头的设备文件一般是/dev/videoX(X为任意数字,要与自己的设备相对应)
V4L2支持三种方式来采集图像:内存映射方式(mmap)、直接读取方式(read)和用户指针。内存映射的方式采集速度最快,一般用于连续视频数据的采集,实际工作中用的最多;直接读取的方式相对速度慢一些,所以常用与静态图片数据的采集;用户指针使用较少。

int munmap(void addr, size_t length);//addr是地址,length是映射区大小
函数成功执行,munmap()返回0;否则返回(void
)-1,

2.V4L2视频采集原理
在通过V4L2采集图像之前,最重要的一步是分配帧缓冲区,并将分配的帧缓冲区从内核空间映射到用户空间,然后将申请到的帧缓冲区在视频采集输入队列排队,剩下的就是等待视频数据的到来。下面了解一下视频数据的流动过程。

当启动视频采集后,驱动程序开始采集一帧图像数据,会把采集的图像数据放入视频采集输入队列的第一个帧缓冲区,一帧图像数据就算采集完成了。第一个帧缓冲区存满一帧图像数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出,应用程序取出图像数据可以对图像数据进行处理或存储操作,然后将该帧缓冲区放入视频采集队列的尾部。驱动程序接下来采集下一帧数据,放入第二个缓冲区,同样的帧缓冲区存满一帧数据后,驱动程序将该缓冲区移至视频采集输出队列,应用程序将该帧缓冲区的图像数据取出后又将该帧缓冲区放入视频输入队列尾部,这样循环往复就实现了循环采集。流程如下图所示:
在这里插入图片描述
3.V4L2程序实现流程
使用V4L2进行视频采集,一般分为5个步骤:
(1)打开设备,进行初始化参数设置,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;
(2)申请图像帧缓冲,并进行内存映射,将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取、处理图像数据;
(3)将帧缓冲进行入队操作,启动视频采集
(4)驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完毕后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;
(5)释放资源,停止采集工作。

在进行V4L2开发中,常用的命令标识符如下:
(1)VIDIOC_REQBUFS:分配内存
(2)VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
(3)VIDIOC_QUERYCAP:查询驱动功能
(4)VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
(5)VIDIOC_S_FMT:设置当前驱动的视频捕获格式
(6)VIDIOC_G_FMT:读取当前驱动的视频捕获格式
(7)VIDIOC_TRY_FMT:验证当前驱动的显示格式
(8)VIDIOC_CROPCAP:查询驱动的修剪功能
(9)VIDIOC_S_CROP:设置视频信号的边框
(10)VIDIOC_G_CROP:读取视频信号的边框
(11)VIDIOC_QBUF:把数据从缓冲区中读取出来
(12)VIDIOC_DQBUF:把数据放回缓冲队列
(13)VIDIOC_STREAMON:开始视频显示函数
(14)VIDIOC_STREAMOFF:结束视频显示函数
(15)VIDIOC_QUERYSTD:检查当前设备支持的标准,例如PAL和NTSC;

具体流程如下图所示:
在这里插入图片描述4.V4L2程序实例
V4L2的代码主要位于video2lcd/video/v4l2.c文件中,接下来就针对上文V4L2程序实现流程和流程中使用的重要数据结构,结合v4l2.c文件中的代码进行说明。

(1)打开设备
应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQUUFF)里的东西返回给应用程序。如果使用非阻塞的方式打开摄像头设备,open加O_NONBLOCK即可。

ifd = open(strDevName, O_RDWR);
if(ifd < 0)
{printf("can not open\n");return -1;
}

(2)查询设备属性
查询设备属性需要使用struct v4l2_capability结构体,该结构体描述了视频采集设备的driver信息。

01 struct v4l2_capability
02 {
03     __u8 driver[16];       // 驱动名字
04     __u8 card[32];         // 设备名字
05     __u8 bus_info[32];     // 设备在系统中的位置
06     __u32 version;         // 驱动版本号
07     __u32 capabilities;    // 设备支持的操作
08     __u32 reserved[4];     // 保留字段
09 };

通过VIDIOC_QUERYCAP命令来查询driver是否合乎规范。因为V4L2要求所有driver和device都支持这个ioctl。所以,通过VIDIOC_QUERYCAP命令是否成功来判断当前device和driver是否符合V4L2规范。当然,这个命令执行成功的同时还能够得到设备足够的信息,如struct v4l2_capability结构体所示内容。86~98行代码检查当前设备是否为capture设备,并检查使用内存映射还是直接读的方式获取图像数据。

iError = ioctl(ifd, VIDIOC_QUERYCAP, &tv4l2Cap);
memset(&tv4l2Cap, 0, sizeof(struct v4l2_capability));
iError = ioctl(ifd, VIDIOC_QUERYCAP, &tv4l2Cap);
if(iError)
{goto err_exit;
}
if(!(tv4l2Cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{goto err_exit;
}
if(!(tv4l2Cap.capabilities & V4L2_CAP_STREAMING))
{goto err_exit;
}
if(!(tv4l2Cap.capabilities & V4L2_CAP_VIDEO_READWRITE))
{goto err_exit;
}

(3)显示所有支持的格式
显示所有支持的格式需要用到struct v4l2_fmtdesc结构体,该结构体描述当前camera支持的格式信息。

01 struct v4l2_fmtdesc
02 {
03     __u32 index;               // 要查询的格式序号,应用程序设置
04     enum v4l2_buf_type type;   // 帧类型,应用程序设置
05     __u32 flags;               // 是否为压缩格式
06     __u8 description[32];      // 格式名称
07     __u32 pixelformat;         //所支持的格式
08     __u32 reserved[4];         // 保留
09 };

使用VIDIOC_ENUM_FMT命令查询当前camera支持的所有格式。struct v4l2_fmtdesc结构体中index要设置,从0开始;enum v4l2_buf_type type也要设置,如果使用的是camera设备,则enum v4l2_buf_type type要设置为V4L2_BUF_TYPE_VIDEO_CAPTURE,因为camera是CAPTURE设备。结构体中的其他内容driver会填充。其中__u32 pixelformat参数在设置图像帧格式时需要使用。

100     memset(&tFmtDesc, 0, sizeof(tFmtDesc));
101     tFmtDesc.index = 0;
102     tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
103     while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
104         if (isSupportThisFormat(tFmtDesc.pixelformat))
105         {
106             ptVideoDevice->iPixelFormat = tFmtDesc.pixelformat;
107             break;
108         }
109             tFmtDesc.index++;
110     }

(4)设置图像帧格式
设置图像帧格式需要用到struct v4l2_format结构体,该结构体描述每帧图像的具体格式,包扣帧类型以及图像的长、宽等信息。

01 struct v4l2_format
02 {
03     enum v4l2_buf_type type;          // 帧类型,应用程序设置
04     union fmt
05     {
06         struct v4l2_pix_format pix;   // 视频设备使用
07         structv 4l2_window win;
08         struct v4l2_vbi_format vbi;
09         struct v4l2_sliced_vbi_format sliced;
10         __u8 raw_data[200];
11     };
12 };
struct v4l2_format结构体需要设置enum v4l2_buf_type type和union fmt中的struct v4l2_pix_format pix。enum v4l2_buf_type type因为使用的是camera设备,camera是CAPTURE设备,所以设置成V4L2_BUF_TYPE_VIDEO_CAPTURE。struct v4l2_pix_format pix设置一帧图像的长、宽和格式等,由于要适配LCD输出,所以长、宽设置为LCD支持的长、宽。
/*set format in */
GetDispResolution(&iLcdWidth, &iLcdHeight, &iLcdBpp);
memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
tV4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2_format.fmt.pix.pixelformat = tFmtDesc.pixelformat;
tV4l2_format.fmt.pix.width = iLcdWidth;
tV4l2_format.fmt.pix.height = iLcdHeight;
tV4l2_format.fmt.pix.field = V4L2_FIELD_ANY;/*如果驱动程序无法发现某些参数(分辨率),他会调整这些参数,并且返回给应用程序*/
iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2_format);
if(iError)
{goto err_exit;
}

(5)申请缓冲区
相关结构体如下,该结构体描述申请的缓冲区的基本信息。

01 struct v4l2_requestbuffers
02 {
03     __u32 count;                    // 缓冲区内缓冲帧的数目
04     enum v4l2_buf_type type;        // 缓冲帧数据格式
05     enum v4l2_memorymemory;         // 区别是内存映射还是用户指针方式
06     __u32 reserved[2];
07 };

申请一个拥有四个缓冲帧的缓冲区,__u32 count为缓冲帧的数目;enum v4l2_buf_type type和前文一样,同样设置成V4L2_BUF_TYPE_VIDEO_CAPTURE;enum v4l2_memorymemory用来区分是内存映射还是用户指针,我们使用内存映射的方式,所以设置成V4L2_MEMORY_MMAP。

140     /* request buffers */
141     memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
142     tV4l2ReqBuffs.count = NB_BUFFER;
143     tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
144     tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;
145
146     iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
147     if (iError)
148     {
149             DBG_PRINTF("Unable to allocate buffers.\n");
150         goto err_exit;
151     }

(6)将申请的缓冲帧从内核空间映射到用户空间

01 struct v4l2_buffer
02 {
03     __u32 index;                    //buffer 序号
04     enum v4l2_buf_type type;        //buffer 类型
05     __u32 byteused;                 //buffer 中已使用的字节数
06     __u32 flags;                    // 区分是MMAP 还是USERPTR
07     enum v4l2_field field;
08     struct timeval timestamp;       // 获取第一个字节时的系统时间
09     struct v4l2_timecode timecode;
10     __u32 sequence;                 // 队列中的序号
11     enum v4l2_memory memory;        //IO 方式,被应用程序设置
12     union m
13     {
14         __u32 offset;               // 缓冲帧地址,只对MMAP 有效
15         unsigned long userptr;
16     };
17     __u32 length;                   // 缓冲帧长度
18     __u32 input;
19     __u32 reserved;
20 };

​ 将内核空间的帧缓冲映射到用户空间,需要两个数据接收帧缓冲的长度和地址,我们需要自己定义一个结构体,该结构体位于video2lcd/include/video_manager.h文件中,其中iVideoBufMaxLen接收帧缓冲的长度,pucVideBuf接收帧缓冲地址。

16 struct VideoDevice {
17     int iFd;
18     int iPixelFormat;
19     int iWidth;
20     int iHeight;
21
22     int iVideoBufCnt;
23     int iVideoBufMaxLen;
24     int iVideoBufCurIndex;
25     unsigned char *pucVideBuf[NB_BUFFER];
26
27     /* 函数 */
28     PT_VideoOpr ptOPr;
29 };

​ 以下代码使用VIDIOC_QUERYBUF命令和mmap函数将内核空间的缓冲区映射到用户空间。VIDIOC_QUERYBUF命令的使用需要参数struct v4l2_buffer结构体,结构体中的type、memory和index参数需要设置,type和memory和前文中的设置一样,分别设置成V4L2_BUF_TYPE_VIDEO_CAPTURE和 V4L2_MEMORY_MMAP,index参数表示申请的缓冲帧的标号,从0开始,包含申请的所有缓冲帧。

156         /* map the buffers */
157         for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++)
158         {
159             memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
160             tV4l2Buf.index = i;
161             tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
162             tV4l2Buf.memory = V4L2_MEMORY_MMAP;
163             iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
164             if (iError)
165             {
166                 DBG_PRINTF("Unable to query buffer.\n");
167                 goto err_exit;
168             }
169
170             ptVideoDevice->iVideoBufMaxLen = tV4l2Buf.length;
171             ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
172                               tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
173                               tV4l2Buf.m.offset);
174             if (ptVideoDevice->pucVideBuf[i] == MAP_FAILED)
175             {
176                 DBG_PRINTF("Unable to map buffer\n");
177                 goto err_exit;
178             }
179         }

(7)将申请的缓冲帧放入队列,并启动数据流
使用VIDIOC_QBUF命令,将申请的缓冲帧依次放入缓冲帧输入队列,等待被图像采集设备依次填满;

181         /* Queue the buffers. */
182         for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++)
183         {
184             memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
185             tV4l2Buf.index = i;
186             tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
187             tV4l2Buf.memory = V4L2_MEMORY_MMAP;
188             iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
189             if (iError)
190             {
191                 DBG_PRINTF("Unable to queue buffer.\n");
192                 goto err_exit;
193             }
194         }

(8)启动捕捉图像数据
启动捕捉图像数据使用VIDIOC_STREAMON命令,当该命令执行成功后,便可以等待图像数据的到来。

356 /**********************************************************************
357 * 函数名称:V4l2StartDevice
358 * 功能描述:开始捕捉图像数据
359 * 输入参数:ptVideoDevice
360 * 输出参数:无
361 * 返 回 值:无
362 * 修改日期             版本号        修改人           修改内容
363 * -----------------------------------------------
364 * 2020/02/16         V1.0     zhenhua             创建
365 ***********************************************************************/
366 static int V4l2StartDevice(PT_VideoDevice ptVideoDevice)
367 {
368     int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
369     int iError;
370
371     iError = ioctl(ptVideoDevice->iFd, VIDIOC_STREAMON, &iType);
372     if (iError)
373     {
374             DBG_PRINTF("Unable to start capture.\n");
375             return -1;
376     }
377     return 0;
378 }

(9)出列采集的帧缓冲,并处理图像数据,然后再将数据帧入列
​ 我们可以使用VIDIOC_DQBUF命令,等待缓冲帧的到来,当有缓冲帧被放入视频输出缓冲队列,我们便可以采到一帧图像。接收到图像我们可以对图像进行操作,例如保存、压缩或者LCD输出等。

243 /**********************************************************************
244 * 函数名称:V4l2GetFrameForStreaming
245 * 功能描述:从图像数据流中获取一帧图像数据
246 * 输入参数:ptVideoDevice
247             ptVideoBuf
248 * 输出参数:无
249 * 返 回 值:无
250 * 修改日期             版本号        修改人           修改内容
251 * -----------------------------------------------
252 * 2020/02/16         V1.0     zhenhua             创建
253 ***********************************************************************/
254 static int V4l2GetFrameForStreaming(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
255 {
256     struct pollfd tFds[1];
257     int iRet;
258     struct v4l2_buffer tV4l2Buf;
259
260     /* poll */
261     tFds[0].fd     = ptVideoDevice->iFd;
262     tFds[0].events = POLLIN;
263
264     iRet = poll(tFds, 1, -1);
265     if (iRet <= 0)
266     {
267         DBG_PRINTF("poll error!\n");
268         return -1;
269     }
270
271     /* VIDIOC_DQBUF */
272     memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
273     tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
274     tV4l2Buf.memory = V4L2_MEMORY_MMAP;
275     iRet = ioctl(ptVideoDevice->iFd, VIDIOC_DQBUF, &tV4l2Buf);
276     if (iRet < 0)
277     {
278             DBG_PRINTF("Unable to dequeue buffer.\n");
279             return -1;
280     }
281     ptVideoDevice->iVideoBufCurIndex = tV4l2Buf.index;
282
283     ptVideoBuf->iPixelFormat        = ptVideoDevice->iPixelFormat;
284     ptVideoBuf->tPixelDatas.iWidth  = ptVideoDevice->iWidth;
285     ptVideoBuf->tPixelDatas.iHeight = ptVideoDevice->iHeight;
286     ptVideoBuf->tPixelDatas.iBpp    = (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_YUYV) ? 16 : \
287                                         (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_MJPEG) ? 0 :  \
288                                         (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_RGB565) ? 16 :  \
289                                         0;
290     ptVideoBuf->tPixelDatas.iLineBytes    = ptVideoDevice->iWidth * ptVideoBuf->tPixelDatas.iBpp / 8;
291     ptVideoBuf->tPixelDatas.iTotalBytes   = tV4l2Buf.bytesused;
292     ptVideoBuf->tPixelDatas.aucPixelDatas = ptVideoDevice->pucVideBuf[tV4l2Buf.index];
293     return 0;
294 }

​ 当我们从缓冲帧输出队列取出一个缓冲帧,取出图像数据后我们需要将缓冲帧重新放回到视频输入缓冲队列,该操作还是使用VIDIOC_QBUF命令,放回缓冲帧输入队列后继续等待被填满。

296 /**********************************************************************
297 * 函数名称:V4l2PutFrameForStreaming
298 * 功能描述:将取出的帧缓冲重新放回图像输入队列
299 * 输入参数:ptVideoDevice
300             ptVideoBuf
301 * 输出参数:无
302 * 返 回 值:无
303 * 修改日期             版本号        修改人           修改内容
304 * -----------------------------------------------
305 * 2020/02/16         V1.0     zhenhua             创建
306 ***********************************************************************/
307 static int V4l2PutFrameForStreaming(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
308 {
309     /* VIDIOC_QBUF */
310     struct v4l2_buffer tV4l2Buf;
311     int iError;
312
313     memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
314     tV4l2Buf.index  = ptVideoDevice->iVideoBufCurIndex;
315     tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
316     tV4l2Buf.memory = V4L2_MEMORY_MMAP;
317     iError = ioctl(ptVideoDevice->iFd, VIDIOC_QBUF, &tV4l2Buf);
318     if (iError)
319     {
320         DBG_PRINTF("Unable to queue buffer.\n");
321         return -1;
322     }
323     return 0;
324 }

(10)停止捕捉图像数据
停止采集图像数据,首先使用VIDIOC_STREAMOFF命令,关闭捕获图像数据。同时要注意取消内存映射和关闭句柄,防止不必要的内存泄漏。

380 /**********************************************************************
381 * 函数名称:V4l2StopDevice
382 * 功能描述:停止捕捉图像数据
383 * 输入参数:ptVideoDevice
384 * 输出参数:无
385 * 返 回 值:无
386 * 修改日期             版本号        修改人           修改内容
387 * -----------------------------------------------
388 * 2020/02/16         V1.0     zhenhua             创建
389 ***********************************************************************/
390 static int V4l2StopDevice(PT_VideoDevice ptVideoDevice)
391 {
392     int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
393     int iError;
394
395     iError = ioctl(ptVideoDevice->iFd, VIDIOC_STREAMOFF, &iType);
396     if (iError)
397     {
398             DBG_PRINTF("Unable to stop capture.\n");
399             return -1;
400     }
401     return 0;
402 }
403
404 static int V4l2GetFormat(PT_VideoDevice ptVideoDevice)
405 {
406     return ptVideoDevice->iPixelFormat;
407 }217 /**********************************************************************
218 * 函数名称:V4l2ExitDevice
219 * 功能描述:退出采集设备,取消帧缓冲映射和关闭句柄
220 * 输入参数:ptVideoDevice
221 * 输出参数:无
222 * 返 回 值:无
223 * 修改日期             版本号        修改人           修改内容
224 * -----------------------------------------------
225 * 2020/02/16         V1.0     zhenhua             创建
226 ***********************************************************************/
227 static int V4l2ExitDevice(PT_VideoDevice ptVideoDevice)
228 {
229     int i;
230     for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++)
231     {
232         if (ptVideoDevice->pucVideBuf[i])
233         {
234             munmap(ptVideoDevice->pucVideBuf[i], ptVideoDevice->iVideoBufMaxLen);
235             ptVideoDevice->pucVideBuf[i] = NULL;
236         }
237     }
238
239     close(ptVideoDevice->iFd);
240     return 0;
241 }

http://chatgpt.dhexx.cn/article/Y4Fi73ec.shtml

相关文章

V4L2框架

前言 在分析v4l2之前最好具有的知识&#xff1a; 1.字符设备,因为v4l2是被枚举为字符设备。 2.内存分配和映射,比如相关数据结构的分配和buffer。 3.DMA&#xff0c;因为v4l2的数据传输用到了DMA。 4.I2C&#xff0c;因为很多传感器都是用的i2c接口。 5.文件系统的基本操作&am…

V4L2系列 之 V4L2驱动框架

目录 前言一、V4L2驱动框架概览1、应用层 -》中间层-》驱动层2、主要代码文件(Linux 4.19版本内核) 二、怎么写V4L2驱动1、如何写一个设备的驱动&#xff1f;2、Video设备主要结构体3、怎么写V4L2驱动 三、V4L2的调试工具1、v4l2-ctl2、dev_debug3、v4l2-compliance 前言 本篇文…

V4L2框架概述

原文链接 本文开启 linux 内核 V4L2 框架部分的学习之旅&#xff0c;本文仅先对 V4L2 的框架做一个综述性的概括介绍&#xff0c;然后接下来的文章中会对 V4L2 框架的各个子模块进行一个全面的介绍&#xff0c;包括每一部分的实现原理&#xff0c;如何使用&#xff0c;用在什么…

V4l2框架分析

Table of Contents 1.V4L2框架概述 1.1 v4l2设备应用层流程 1.2 内核V4L2模块 1.2.1 video_device 1.2.2 v4l2_subdev 1.2.3 videobuf2 2. video_device结构体 2.1 图像处理模块 2.2 video_device处理流程 2.2.1 video_device 结构体成员介绍: 3. video_buf2 3.1 …

V4L2

V4L2(video 4 linux 2) V4L2有一段历史了。大约在1998的秋天&#xff0c;它的光芒第一次出现在Bill Dirks 的眼中。经过长足的发展&#xff0c;它于2002年11 月&#xff0c;发布2.5.46 时&#xff0c;融入了内核主干之中。然而直到今天&#xff0c;仍有一部分内核驱不支持新的A…

V4L2简介

http://work-blog.readthedocs.org/en/latest/v4l2%20intro.html 第一章 V4L2简介 1.1、什么是v4l2 V4L2&#xff08;Video4Linux的缩写&#xff09;是Linux下关于视频采集相关设备的驱动框架&#xff0c;为驱动和应用程序提供了一套统一的接口规范。 V4L2支持的设备十分广泛&…

我们一起学linux之V4L2摄像头应用流程

一、概述 Video for Linuxtwo(Video4Linux2)简称V4L2&#xff0c;是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口&#xff0c;配合适当的视频采集设备和相应的驱动程序&#xff0c;可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频…

深入学习Linux摄像头(一)v4l2应用编程

深入学习Linux摄像头&#xff08;一&#xff09;v4l2应用编程 一、什么是v4l2 二、v4l2 API介绍 2.1 Querying Capabilities 2.2 Application Priority 2.3 Device Inputs and Outputs 2.4 Video Standards 2.5 Camera Control Reference 2.6 Image Format 2.7 Cropping, compo…

V4L2驱动框架详解

1. V4L2框架概述 1.1 v4l2设备应用层流程 1.2 内核V4L2模块 2 2. video_device 2.1图像处理模块 2.2 video注册流程 3. videobuf2 3.1 与video device的关系: 3.2 buffer类型 3.3 vb2_ops回调函数 3.4 mmap 流程 4. Subdev 4.1 概念 4.2 subdev注册流程 5. media fra…

html背景自动适应,css背景图片如何自适应?

css可以使用background-size属性设置背景图片自适应&#xff0c;为背景图片设置background-size:cover;样式即可使背景图片自适应。 css可以使用background-size属性设置背景图片自适应。background-size属性规定背景图像的尺寸。 background-size属性&#xff1a; 语法&#x…

css实现图片自适应容器的几种方式

css实现图片自适应容器 经常有这样一个场景,需要让图片自适应容器的大小。 1、img标签的方式 我们马上就能想到,把width、height 设置为100%啊。来看一哈效果。 <div class=div1><img src="./peiqi.png" alt=""> </div>.div1 {widt…

css 背景图片自适应属性整理

本篇博客主要记录一些使用 css 对背景图片自适应的操作整合 背景图片取消重复 background-image: url(image.jpg); background-repeat:no-repeat;修改前 修改后 背景图片固定&#xff08;不会随着内容滚动而改变位置&#xff09; background-image: url(image.jpg); back…

CSS实现图片自适应布局

CSS实现图片自适应布局 最轻松的写法 <div class"container">// 这里图片尺寸为440X440像素&#xff0c;<img src"./images/medium.jpg" alt"test"> </div><style>.container {width: 600px;height: 600px;border: 1p…

CSS图片自适应框架

CSS图片自适应框架 使用img来设置 <!DOCTYPE html> <html><style>body{margin: 0;padding: 10px;}#a_1{display: block;width: 100px;height: 50px;overflow: hidden;padding: 10px;border: dashed;margin: 10px;}#a_1img{width: 100%;height: 100%;}</s…

css之实现图片自适应

文章目录 一、position实现图片自适应二、padding补偿法三、图片以正方形显示 原图如下&#xff1a; 在开发中&#xff0c;想在固定的高度和宽度中显示不一样的图片时&#xff0c;就会出现压缩&#xff0c;导致图片最后显示不好看&#xff0c;以下提供几种方法进行调整&#xf…

css实现图片自适应缩放的两种方法

想要实现图片根据给定宽高来自适应缩放的时候&#xff0c;会想到什么属性&#xff1f;是object-fit: cover;吗&#xff1f; 来试一试&#xff01;为了展示效果&#xff0c;我特地选了对称的图片。下面两个img元素都有一个div包裹&#xff0c;div设置固定宽高&#xff0c;第一个…

3种CSS实现背景图片全屏铺满自适应的方式

来源 | https://www.fly63.com/ 一张清晰漂亮的背景图片能给网页加分不少&#xff0c;设计师也经常会给页面的背景使用大图&#xff0c;我们既不想图片因为不同分辨率图片变形&#xff0c;也不希望当在大屏的情况下&#xff0c;背景有一块露白&#xff0c;简而言之&#xff0c;…

css怎么设置背景图片自适应大小

在css中&#xff0c;可以利用“background-size”属性设置背景图片自适应大小&#xff0c;该属性用于设置背景图片的大小&#xff0c;只需要给背景图片元素添加“background-size:cover;”样式&#xff0c;即可使背景图片的大小自适应。 本教程操作环境&#xff1a;windows10系…

CSS——图片自适应宽高

宽度拉伸&#xff1a;把width设置成100%&#xff0c;height设置auto 高度拉伸&#xff1a;把height设置成100%&#xff0c;width设置auto ​ <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv&…

使用CSS进行图片自适应的两个方法

1.object-fit 属性 设置好图片的宽高&#xff0c;然后设置object-fit属性为contain就是常见的图片自适应效果。 img {width: 400px;height: 400px;object-fit: contain;} object-fit: fill|contain|cover|scale-down|none|initial|inherit; 2.background 我们把图片作为背景…