标准调用流程
1、open
RecordInit()–>recordInit()–>initializeDev()–>connectCamera()–>connectDevice()–>openCameraDev()–>open()
应用程序首先调用open打开“/dev/video0”设备节点,获得方便后续操作的文件描述符:
注意这里的打开模式使用“O_RDWR | O_NONBLOCK”,即可读可写和非阻塞。
2、获取Camera驱动支持的功能(能力)
RecordInit()–>recordInit()–>initializeDev()–>connectCamera()–>connectDevice()–>openCameraDev()–>ioctl()
调用Ioctl方法,并使用VIDIOC_QUERYCAP命令标识,调用结果是填充一个v4l2_capability结构体变量。
在/drivers/media/v4l2-core/v4l2-ioctl.c中有:
IOCTL_INFO_FNC宏的意思是:
IOCTL_INFO_FNC(命令, 实现函数, 打印信息(debug使用), flag)
所以设置VIDIOC_QUERYCAP命令标识会调用v4l_querycap(),从而调用到vin驱动中的vidioc_querycap():
V4L2_CAP_VIDEO_CAPTURE_MPLANE:是支持多平面格式的视频捕获设备;
V4L2_CAP_STREAMING:支持流I/O控制。V4L2 Framework定义了三种不同的方式(read/write系统调用、异步I/O、流I/O控制),用于从设备中读取数据。 Streaming I/O设计的目的就是为了减少在数据处理的各个环节中,拷贝的次数,从而实现各阶段硬件的无缝配合,其用结合了内存映射和DMA传输方式。
V4L2_CAP_READWRITE:支持read/write系统调用的方式来读写数据。效率不高,一般使用Streaming I/O方式。
3、选择当前视频输入通道
RecordInit()–>recordInit()–>initializeDev()–>connectCamera()–>connectDevice()–>openCameraDev()–>ioctl()
调用Ioctl方法,并使用VIDIOC_S_INPUT命令标识:
最终调用到vin_video.c的vidioc_s_input(),实际上会对 csi device 进行初始化工作。这个函数通过应用程序传递下来的index序号,选择一条视频通道pipe,然后调用这个pipe涉及到的ISP、Scaler、Sensor等的“subdev–>ops–>core–>init”函数,例如nvp6134的sensor_init()。
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
__vin_sensor_setup_link(module, valid_idx, 1);
ret = vin_pipeline_call(vinc, open, &cap->pipe, &cap->vdev.entity, true);
//下行:调用sunxi_isp.c sunxi_isp_subdev_init(1)
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_ISP], core, init, 1);
//下行:调用sunxi_scaler.c sunxi_csi_subdev_init(1)
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SCALER], core, init, 1);
//下行:调用nvp6134的sensor_init()
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, init, 1);
}
__vin_sensor_setup_link():创建一个media link:[nvp6134] => [sunxi_csi.2]
vin_pipeline_call 那句代码:实际调用vinc->pipeline_ops->open(&cap->pipe, &cap->vdev.entity, true),即调用了vin.c __vin_pipeline_open(&cap->pipe, &cap->vdev.entity, true),具体参考《vin core驱动.docx》
最后一行代码将调用nvp6134的sensor_init(),它并没有真正的操作到nvp6134的寄存器,而是初始化驱动中的一些参数:
这里设置输出分辨率为1280*720,、帧率为30fps。
4、枚举帧大小
RecordInit()–>recordInit()–>initializeDev()–>Initialize()–>initDefaultParameters()–>getFullsize()–>ioctl()
调用Ioctl方法,并使用VIDIOC_ENUM_FRAMESIZES命令标识:
但是驱动没有实现该IOCTL,返回-ENOIOCTLCMD
最好在调试过程中打印这里的mFullSizeWidth、mFullSizeHeight是多少(initDefaultParameters()里面很多参数不直观,需要在线调试)
5、尝试设置一种数据格式
RecordInit()–>recordInit()–>initializeDev()–>Initialize()–>initDefaultParameters()–>tryFmtSize ()–>ioctl()
RecordInit()–> start()–>setParameters()–>tryFmtSize ()–>ioctl()
调用Ioctl方法,并使用VIDIOC_TRY_FMT命令标识:
这个ioctl用于协商驱动程序和应用程序之间交换的图像数据格式,所谓“try”,即大部分情况下等同于VIDIOC_S_FMT,但是它不会更改驱动程序状态。它可以在任何时候调用,永远不会返回EBUSY。驱动提供此功能是为了协商参数,了解硬件限制,而不禁用I / O或可能耗费时间的硬件准备。
本测试程序中,调用VIDIOC_TRY_FMT,根据捕捉视频的类型、格式和大小,判断模式、格式等是否被驱动支持,此过程不会改变任何硬件设置。对 于 VIN 设 备 , type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE 。 使 用 struct v4l2_pix_format_mplane 进行参数传递。应用程序输入该结构体里面的 width、 height、 pixelformat、 field 等参数,驱动返回最接近的 width、 height;若 pixelformat、 field 不支持,则默认选择驱动支持的第一种格式。
但vin驱动实现了该方法,最终调用了vidioc_try_fmt_vid_cap_mplane()。
这个函数首先调用vin_find_format()从vin_formats[]数组中试图找到与应用程序提供的参数匹配的图像数据格式,例如应用程序默认要求的是V4L2_PIX_FMT_NV21格式,vin_formats[]中对应的格式是:
注意:这里的V4L2_PIX_FMT_NV21是表示像素格式
找到后调用vin_pipeline_try_format()遍历pipe用到的子设备、调用它们驱动的pad–>set_fmt(如果有的话),例如csi驱动的sunxi_csi_subdev_set_fmt()、ISP驱动的sunxi_isp_subdev_set_fmt()、nvp6134驱动的sensor_set_fmt()等。总的代码很多,但还是没有发现与nvp6134寄存器相关的操作。这里会分析sensor_set_fmt()。
注:每一个子设备都预备有一个输出格式的列表,设置好这些格式,使得sink pad(输入)与上一个source pad(输出)保持一致。nvp6134中sensor_formats[]、csi中sunxi_csi_formats[]、isp中sunxi_isp_formats[],不过scaler没有。
打印信息如下:
[ 67.125670] [VIN_LOG_FMT]found nvp6134 in this pipeline
[ 67.132201] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 640*480 0x2008 0xe4ae9d40
[ 67.150357] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[ 67.167420] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[ 67.185074] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[ 67.202736] [VIN_LOG_FMT]found sunxi_isp.0 in this pipeline
[ 67.219805] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[ 67.237475] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[ 67.255113] [VIN_LOG_FMT]found sunxi_scaler.0 in this pipeline
[ 67.272487] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[ 67.290432] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 67.308660] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
(V) V4L2Camera::TryFmtSize: w: 640, h: 480
[ 67.326772] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 67.337912] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 67.345176] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 640*480 2012 1
[ 67.352129] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[ 67.359346] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
[ 67.366664] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[ 67.373901] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
[ 67.381238] [VIN_LOG_FMT]found nvp6134 in this pipeline
[ 67.387024] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 640*480 0x2012 0x1
[ 67.393913] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[ 67.400103] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[ 67.406829] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[ 67.413591] [VIN_LOG_FMT]found sunxi_isp.0 in this pipeline
[ 67.419781] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[ 67.426514] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[ 67.433268] [VIN_LOG_FMT]found sunxi_scaler.0 in this pipeline
[ 67.439748] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[ 67.446781] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 67.454119] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 67.461357] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 67.468688] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 67.475924] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 640*480 2012 1
[ 67.482885] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[ 67.490112] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
[ 67.497445] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[ 67.504663] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
01-01 00:01:53.250 (V) V4L2Camera::TryFmtSize: w: 1280, h: 720
[ 68.013790] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[ 68.027617] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.034983] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.042223] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.049560] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.056777] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[ 68.063829] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.071161] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.078406] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.085724] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.092968] [VIN_LOG_FMT]found nvp6134 in this pipeline
[ 68.098767] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 1280*720 0x2012 0x1
[ 68.105695] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[ 68.111882] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[ 68.118643] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[ 68.125391] [VIN_LOG_FMT]found sunxi_isp.0 in this pipeline
[ 68.131581] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[ 68.138338] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[ 68.145081] [VIN_LOG_FMT]found sunxi_scaler.0 in this pipeline
[ 68.151556] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[
68.158604] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w
= 1280, h = 720
[ 68.165937] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.173161] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.180478] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.187714] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[ 68.194747] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.202082] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.209317] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[ 68.216647] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[ 68.224398] [VIN_LOG_FMT]found nvp6134 in this pipeline280, h: 720
[ 68.235764] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 1280*720 0x2008 0xc008f2ec
[ 68.254279] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[ 68.271354] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
01-01 00:01:53.622 (V) V4L2Camera::TryFmtSize: w: 1280, h: 720
返回:
然后调用vin_pipeline_try_format(),该函数会多次调用vin_find_format()
[ 14.121812] anthony vin_pipeline_try_format() >>>enter vin_find_format()
[ 14.140405] anthony vin_find_format() line=475 i=29 id=0
[ 14.157050] found nvp6134 in this pipeline sfmt.format.code=0x2008
这是根据各种条件刷选出来的默认格式:MEDIA_BUS_FMT_YVYU8_2X8=0x2008(这是总线数据格式),它符合V4L2_PIX_FMT_NV21格式(这是像素格式)等要求。然后调用nvp6134的set_fmt来尝试这种格式是否符合。其中调用sensor_set_fmt()–>sensor_try_format(),它首先从Sensor驱动中获得Sensor支持的格式:
上面V4L2_MBUS_FMT_YVYU8_1X16=0x2012,它在代码里面也表示为MEDIA_BUS_FMT_YVYU8_1X16。然后调用sensor_fill_mbus_fmt()重新设置格式为V4L2_MBUS_FMT_YVYU8_1X16
[ 14.174847] anthony sensor_try_format() fmt->format.code=0x2008, code=0x2012
然后再次vin_find_format(),因mbus_code=MEDIA_BUS_FMT_YVYU8_1X16,所以获得:
于是通过VIDIOC_TRY_FMT命令标识、返回给AP的fmt最终是V4L2_MBUS_FMT_YVYU8_1X16。
修改vin_pipeline_try_format()打印代码:
found nvp6134 in this pipeline sfmt.format.code=0x2012
found sunxi_csi.2 in this pipeline sfmt.format.code=0x2012
found sunxi_isp.0 in this pipeline sfmt.format.code=0x2012
found sunxi_scaler.0 in this pipeline sfmt.format.code=0x2012
说明每个pipeline传递的是0x2012(MEDIA_BUS_FMT_YVYU8_1X16)格式的数据。
sensor_set_fmt()分析:
sensor_set_fmt() sensor fmt=0x2012
即nvp6134在代码里面最终设置为MEDIA_BUS_FMT_YVYU8_1X16格式作为原始数据格式,在https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/subdev-formats.html?highlight=v4l2_mbus_pixelcode#v4l2-mbus-pixelcode中解释到,这种格式表明一个像素点是通过一个16bit来表示,其中bit[715]是y0y7,bit[07]是v0v7或u0~u7:
它的格式实际是一种YUV420。
但是在nvp6324中:
sensor_set_fmt() sensor fmt=0x2008
即MEDIA_BUS_FMT_YUYV8_2X8,格式如下:
6、获取/设置图像数据格式
RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2SetVideoParams()–>ioctl()
调用Ioctl方法,并使用VIDIOC_G_FMT、VIDIOC_S_FMT命令标识:
对于VIDIOC_S_FMT命令标识:
用于设置捕捉视频的类型、格式和大小,设置之前会调用 VIDIOC_TRY_FMT。
对 于 VIN 设 备 , type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE 。 使 用 struct v4l2_pix_format_mplane 进行参数传递。应用程序输入 width、height、pixelformat、field 等,驱动返回最接近的 width、height;若 pixelformat、field 不支持,则默认选择驱动支持的第一种格式。
应用程序应该以驱动返回的 width、 height、 pixelformat、 field 等作为后续使用传递的参数。
对于 OSD 设备, type 为 V4L2_BUF_TYPE_VIDEO_OVERLAY。使用 struct v4l2_window 进行参数传递。应用程序输入水印的个数、窗口位置和大小、 bitmap 地址、 bitmap 格式以及 global_alpha 等。驱动保存这些参数,并在 VIDIOC_OVERLAY 命令传递使能命令时生效。
驱动方面最终调用vin_video.c vidioc_s_fmt_vid_cap_mplane(),先try fmt测试(过程和第6小节类似),然后把要设置的格式/分辨率存起来。过程中会调用nvp6134.c sensor_ioctl(GET_CURRENT_WIN_CFG),设置info->current_wins。
info->current_wins结构体的值如下:
[nvp6134]width=1280 heitht=720 [nvp6134]hoffset=0 voffset=0 [nvp6134]hts=0 vts=0 [nvp6134]pclk=0 mipi_bps=0 [nvp6134]fps_fixed=0 if_mode=0 [nvp6134]wdr_mode=0 bin_factor=0 [nvp6134]intg_min=0 intg_max=0 [nvp6134]gain_min=0 gain_max=0 [nvp6134]width_input=1280 gain_max=720 [nvp6134]vipp_hoff=0 vipp_voff=0
对于VIDIOC_G_FMT命令标识:
获取捕捉视频的 width、 height、 pixelformat、 field、 bytesperline、 sizeimage 等参数。
应用程序一般在调用VIDIOC_S_FMT命令标识之后,调用VIDIOC_G_FMT命令标识再获取一次视频参数,并以驱动返回的 width、 height、 pixelformat、 field 等为后续使用。
驱动方面最终调用vin_video.c vidioc_g_fmt_vid_cap_mplane()。AP调用VIDIOC_S_FMT命令时,参数ormat.fmt.pix_mp.pixelformat=V4L2_PIX_FMT_NV21,保存到驱动里面:
7、设置流参数
RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2setCaptureParams()–>ioctl()
调用Ioctl方法,并使用VIDIOC_S_PARM命令标识:
第三个参数是指向struct v4l2_streamparm的指针,该结构包含一个保存输入设备或保存输出设备的联和结构体。在本测试程序中CSI作为输入设备,所以只初始化输入设备(v4l2_streamparm.param.capture)、parms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,而通 过 设 定 parms.capture.capturemode ( V4L2_MODE_VIDEO 或V4L2_MODE_IMAGE),实现视频或图片的采集,通过设定 parms->capture.timeperframe,可以设置帧率。
这个VIDIOC_S_PARM命令标识的调用会先调用驱动vin_video.c vidioc_s_parm(),然后逐步调用sensor、CSI、ISP驱动的video.s_parm所指向的函数,例如nvp6134驱动里的sensor_s_parm()
相对于VIDIOC_S_FMT,这里设置的parm——参数主要是针对功能(有capture和output两种)来说的,例如这里使用capture功能,它由以下参数:
应用程序设置这些参数,通过VIDIOC_S_PARM将参数保存到驱动中。8、分配设备缓冲区
RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2ReqBufs()–>ioctl()
调用Ioctl方法,并使用VIDIOC_REQBUFS命令标识:
VIDIOC_REQBUFS命令标识的ioctl调用用于启动内存映射、用户指针或基于I / O流的DMABUF。内存映射缓冲区位于设备内存中,必须先使用调用此ioctl让驱动程序分配内存,然后才能映射到应用程序的地址空间。用户缓冲区则由应用程序本身分配,调用此ioctl可将驱动程序切换到用户指针I / O模式并设置一些内部结构。类似地,DMABUF缓冲区由应用程序通过设备驱动程序分配,此ioctl调用时驱动程序仅配置为DMABUF I / O模式,而不执行任何直接分配。
应用程序为了让驱动分配设备缓冲区,需先初始化struct v4l2_requestbuffers结构的所有字段(要memset为0)。它们将type字段设置为相应的流或缓冲区类型(例如本测试程序“rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE”,即“单平面视频捕获流的缓冲区”,因为测试程序用到设备的capture(捕获、截图)功能),将count字段设置为所需的缓冲区数,必须将memory设置为所请求的I / O方法(本测试程序设置为V4L2_MEMORY_MMAP,即内存映射)。
当使用指向struct v4l2_requestbuffers的指针作为参数来这个ioctl时,驱动程序将尝试分配所请求数量的缓冲区,并实际分配的缓冲区数量存储在count字段。当驱动程序耗尽空闲内存时,它可能小于请求的数量,甚至为零。当驱动程序需要更多缓冲区才能正常运行时,也可以使用更大的数字。例如,视频输出需要至少两个缓冲区,一个显示,一个由应用程序填充。(所以本测试程序在函数最后返回实际分配的buf数量)
应用程序可以再次调用ioctl VIDIOC_REQBUFS来更改缓冲区的数量,但是如果缓冲区还在被映射时会失败。在异常中止或完成DMA操作、或调用VIDIOC_STREAMOFF后,count将为零并释放所有缓冲区,。
本测试程序中,在v4l2_requestbuffers 结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立 FIFO,来提高视频采集的效率。 这些 buffer 通过内核申请,申请后需要通过 mmap 方法,映射到 User 空间。
Count:定义需要申请的 video buffer 数量
Type:对于 VIN 设备,为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
Memory : 目 前 支 持 V4L2_MEMORY_MMAP 、 V4L2_MEMORY_USERPTR 、V4L2_MEMORY_DMABUF 方式,当前使用V4L2_MEMORY_MMAP。
vin驱动中,这个Ioctl最终调用videobuf2-core.c vb2_ioctl_reqbufs()。驱动会根据应用程序中 VIDIOC_S_FMT 参数设置的格式计算供需要buffer的大小,并返回buf的实际数量—— count ,这应该和cap->frame.fmt.memplanes有关,即vin_formats[]中对应图像格式的memplanes。[ 27.653947] vb2_ioctl_reqbufs()>>> [ 27.663842] [VIN_LOG_VIDEO]queue_setup, buf count = 6, nplanes = 1, size = 1384448 [ 27.684104] vb2_ioctl_reqbufs()<<<
9、查询缓冲区的状态
RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2QueryBuf()–>ioctl()
调用Ioctl方法,并使用VIDIOC_QUERYBUF命令标识:
此ioctl是流式I / O方法的一部分。在使用ioctl VIDIOC_REQBUFS ioctl分配缓冲区后,它可以用于查询缓冲区的状态。
应用程序将struct v4l2_buffer的type字段设置为与之前struct v4l2_format和struct v4l2_requestbuffers使用的缓冲区类型,以及index字段。index的有效范围从0到使用ioctl VIDIOC_REQBUFS分配的缓冲区数减1(即v4l2_requestbuffers.count-1,本测试程序使用for循环,index被设为0到mBufferCnt-1)。使用多平面API时,m.planes字段必须包含指向struct v4l2_plane数组的用户空间指针,并且length字段必须设置为其中的元素数量。在使用指向此结构的指针作为参数、调用ioctl VIDIOC_QUERYBUF之后,驱动程序返回错误代码或填充结构的其余部分。
flags字段将设置V4L2_BUF_FLAG_MAPPED,V4L2_BUF_FLAG_PREPARED,V4L2_BUF_FLAG_QUEUED和V4L2_BUF_FLAG_DONE标志。memory字段将设置为当前I / O方法。对于单平面API,m.offset指缓冲区起始地址到设备内存起始地址的偏移量,length字段是缓冲区大小。对于多平面API,则使用m.planes数组元素中的字段m.mem_offset和length来表示一块缓冲区。
在本测试程序中,运用多个缓冲区来存储数据,且上一步调用VIDIOC_REQBUFS时已经返回了驱动分配了count个缓冲区,所以这里通过for循环从驱动中查询到各个缓冲区的地址和大小。在VIDIOC_REQBUFS调用中使用V4L2_MEMORY_MMAP的I/O方式,所以在获得缓冲区地址和大小后,程序再通过mmap()方法将缓冲区映射到应用程序中。驱动方面最终调用videobuf2-core.c vb2_querybuf()。
10、应用程序与驱动程序交换缓冲区
RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2QueryBuf()–>ioctl()
另一处:V4L2CameraDevice::releasePreviewFrame()–>ioctl()
接上一步调用VIDIOC_QUERYBUF并使用mmap()映射缓冲区后,程序再次调用Ioctl方法,并使用VIDIOC_QBUF命令标识:
应用程序调用VIDIOC_QBUF ioctl将队列中的空(捕获)或填充(输出)缓冲区传入给驱动程序,意为“入队”操作。
为了要将缓冲区入队,应用程序初始化struct v4l2_buffer,类似调用VIDIOC_QUERYBUF时的初始化,或者直接调用VIDIOC_QUERYBUF返回的struct v4l2_buffer。当缓冲区用于输出(类型为V4L2_BUF_TYPE_VIDEO_OUTPUT,V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE或V4L2_BUF_TYPE_VBI_OUTPUT)时,应用程序还必须初始化bytesused,field和timestamp字段。
应用程序要将内存映射缓冲区排入队列,将memory字段设置为V4L2_MEMORY_MMAP。当使用指向此结构的指针调用VIDIOC_QBUF时,驱动程序将设置V4L2_BUF_FLAG_MAPPED和V4L2_BUF_FLAG_QUEUED标志并清除flags字段中的V4L2_BUF_FLAG_DONE标志,否则它将返回EINVAL错误代码。在本测试程序中,直接将VIDIOC_QUERYBUF返回的struct v4l2_buffer作为VIDIOC_QBUF参数传递给驱动。驱动方面最终调用videobuf2-core.c vb2_qbuf()。
11、启动流式I / O传输
RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2StartStreaming()–>ioctl()
调用Ioctl方法,并使用VIDIOC_STREAMON命令标识:
在流(内存映射,用户指针或DMABUF)I / O控制期间,调用VIDIOC_STREAMON或VIDIOC_STREAMOFF ioctl启动或停止捕获(或输出)过程。参数是一个指向整数的指针,即所需的缓冲区或流类型。这与struct v4l2_requestbuffers类型相同。
在VIDIOC_STREAMON之前,捕获硬件是禁用的,并且没有填充输入缓冲区(如果传入队列中有任何空缓冲区)。而输出硬件是禁用,不会产生视频信号。当最新的一个输出缓冲区传入队列中时,表示ioctl调用成功。
在为捕获和输出流类型调用VIDIOC_STREAMON之前,memory-to-memory设备不会启动。
如果VIDIOC_STREAMON失败,则任何在排队的缓冲区将保持排队。
如果缓冲区已使用ioctl VIDIOC_QBUF进行排队,则调用VIDIOC_DQBUF和VIDIOC_STREAMOFF而不调用VIDIOC_STREAMON,那么这些排队缓冲区也将从传入队列中删除,并且所有队列缓冲区都返回到调用ioctl VIDIOC_REQBUFS之后的状态,并且可以重新启动。
如果在流式传输正在进行时调用VIDIOC_STREAMON,或者在流式传输已停止时调用VIDIOC_STREAMOFF,则返回0。
本测试程序中, buffer type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE。运行此 IOCTL,将 buffer队列中所有 buffer 入队,并开启 CSIC DMA 硬件中断,每次中断便表示完成一帧 buffer 数据的填入。驱动最终调用vin_video.c videoc_streamon(),这是一个重要的函数,其内部会调用sensor_s_stream()–>sensor_reg_init(),从而初始化sensor的寄存器。
打印:
[VIN]vidioc_streamon()>>> [VIN]vidioc_streamon()<<< [VIN]__vin_s_stream_handle [VIN_LOG_MD]__vin_pipeline_s_stream() >>> on_idx=1 [VIN_LOG_MD]__vin_pipeline_s_stream() total_rx_ch=4 [VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=0 [VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=1 [VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=2 [VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=3 [VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 vipp_sel=0 isp_tx_ch=0 [VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:nvp6134 [VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:dmafd=48 [VIN_LOG_MD]__vin_subdev_set_stream()>>> on=1 stream_count=0 [nvp6134]sensor_s_stream on = 1, 1280*720 2012 [nvp6134]sensor_reg_init + [nvp6134]sensor_reg_init - [VIN_LOG_MD]__vin_subdev_set_stream() <<< [VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:sunxi_isp.0 [VIN_LOG_MD]__vin_subdev_set_stream()>>> on=1 stream_count=0 [VIN_LOG_FMT]sunxi_isp_subdev_s_stream isp->use_isp=0 [VIN_LOG_MD]__vin_subdev_set_stream() <<< [VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:sunxi_scaler.0 [VIN_LOG_MD]__vin_subdev_set_stream()>>> on=1 stream_count=0 [VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() cscaler->id=0 [VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() crop.hor=0 crop.ver=0 [VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() crop.width=1280 crop.height=720 [VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() out_fmt=0 is_osd_en=0 [VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() scaler_cfg.sc_x_ratio=256 sc_y_ratio=256 sc_w_shift=1 [VIN_LOG_FMT]vipp0 stream on, 1280*720 hoff: 0 voff: 0 xr: 256 yr: 256 [VIN_LOG_MD]__vin_subdev_set_stream() <<< [VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:vin_cap.0 [VIN_LOG_MD]__vin_subdev_set_stream() >>>on=1 stream_count=0 [VIN_LOG_FMT]csic_dma0 stream on, 1280*720 hoff: 0 voff: 0 [VIN_LOG_MD]__vin_subdev_set_stream() <<< [VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:sunxi_csi.2 [VIN_LOG_MD]__vin_subdev_set_stream() >>>on=1 stream_count=0 [VIN_LOG_FMT]sunxi_csi_subdev_s_stream()>>> csi->id=2 [VIN_LOG_FMT]__csi_set_fmt_hw() csi->csi_fmt->seq=1 data_width=16 [VIN_LOG_FMT]__csi_set_fmt_hw() mf->field=1 [VIN_LOG_FMT]__csi_set_fmt_hw() csi->bus_info.bus_if=1 [VIN_LOG_FMT]__csi_set_fmt_hw() csi->capture_mode=2 [VIN_LOG_FMT]__csi_set_fmt_hw() out_size.hor_len=1280 out_size.ver_len=720 [VIN_LOG_FMT]__csi_set_fmt_hw() out_size.hor_start=0 out_size.ver_start=0 [VIN_LOG_FMT]__csi_set_fmt_hw() csi->bus_info.ch_total_num=4 [VIN_LOG_FMT]parser2 stream on, 1280*720 hoff: 0 voff: 0 code: 2012 field: 1 [VIN_LOG_MD]__vin_subdev_set_stream() <<< [VIN_LOG_MD]__vin_pipeline_s_stream() ptn_cfg.ptn_en=0 ptn_type=0 ptn_on_cnt=0 [VIN_LOG_MD]__vin_pipeline_s_stream() <<< [VIN_LOG_VIDEO]__vin_s_stream_handle done, id = 0! [VIN_LOG_VIDEO]video0 first frame done!