注:文章都是通过阅读各位前辈总结的资料 Android 10.0 && Linux(Kernel 4.19)Rockchip平台源码、加上自己的思考分析总结出来的,其中难免有理解不对的地方,欢迎大家批评指正。文章为个人学习、研究、欣赏之用,图文内容整理自互联网,如有侵权,请联系删除(◕‿◕),转载请注明出处(©Rockchip ©Android @Linux 版权所有),谢谢。
(==文章基于 Kernel-4.19==)&&(==文章基于 Android 10.0==)
【zhoujinjian.com博客原图链接】
【开发板 Khadas Edge V】
【开发板 Khadas Edge V Android 10.0 && Linux(Kernel 4.19)源码链接】
正是由于前人的分析和总结,帮助我节约了大量的时间和精力,特别感谢!!!
V4L2框架-videobuf2
==源码(部分)==:
F:\Khadas_Edge_Android_Q\hardware\rockchip\camera_engine_rkisp\tests\rkisp_demo\rkisp_demo.cpp
(一)、V4L2 App流程简介
1、打开video设备
在需要进行视频数据流的操作之前,首先要通过标准的字符设备操作接口open方法来打开一个video设备,并且将返回的字符句柄存在本地,之后的一系列操作都是基于该句柄,而在打开的过程中,会去给每一个子设备的上电,并完成各自的一系列初始化操作。
2、查看并设置设备
在打开设备获取其文件句柄之后,就需要查询设备的属性,该动作主要通过ioctl传入VIDIOC_QUERYCAP参数来完成,其中该系列属性通过v4l2_capability结构体来表达,除此之外,还可以通过传入VIDIOC_ENUM_FMT来枚举支持的数据格式,通过传入VIDIOC_G_FMT/VIDIOC_S_FMT来分别获取和获取当前的数据格式,通过传入VIDIOC_G_PARM/VIDIOC_S_PARM来分别获取和设置参数。
3、申请帧缓冲区
完成设备的配置之后,便可以开始向设备申请多个用于盛装图像数据的帧缓冲区,该动作通过调用ioctl并且传入VIDIOC_REQBUFS命令来完成,最后将缓冲区通过mmap方式映射到用户空间。
4、将帧缓冲区入队
申请好帧缓冲区之后,通过调用ioctl方法传入VIDIOC_QBUF命令来将帧缓冲区加入到v4l2 框架中的缓冲区队列中,静等硬件模块将图像数据填充到缓冲区中。
5、开启数据流
将所有的缓冲区都加入队列中之后便可以调用ioctl并且传入VIDIOC_STREAMON命令,来通知整个框架开始进行数据传输,其中大致包括了通知各个子设备开始进行工作,最终将数据填充到V4L2框架中的缓冲区队列中。
6、将帧缓冲区出队
一旦数据流开始进行流转了,我们就可以通过调用ioctl下发VIDIOC_DQBUF命令来获取帧缓冲区,并且将缓冲区的图像数据取出,进行预览、拍照或者录像的处理,处理完成之后,需要将此次缓冲区再次放入V4L2框架中的队列中等待下次的图像数据的填充。
整个采集图像数据的流程现在看来还是比较简单的,接口的控制逻辑很清晰,主要原因是为了提供给用户的接口简单而且抽象,这样方便用户进行集成开发,其中的大部分复杂的业务处理都被V4L2很好的封装了,接下来我们来详细了解下V4L2框架内部是如何表达以及如何运转的。
(二)、rkisp_demo分析
1、流程图
流程图根据rkisp_demo源码分析得出:
2、源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
| F:\Khadas_Edge_Android_Q\hardware\rockchip\camera_engine_rkisp\tests\rkisp_demo\rkisp_demo.cpp
static void init_mmap(void) { struct v4l2_requestbuffers req; req.count = BUFFER_COUNT; req.type = buf_type; req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { ...... } ...... buffers = (struct buffer*)calloc(req.count, sizeof(*buffers)); ...... for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct v4l2_buffer buf; struct v4l2_plane planes[FMT_NUM_PLANES]; CLEAR(buf); CLEAR(planes);
buf.type = buf_type; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers;
if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == buf_type) { buf.m.planes = planes; buf.length = FMT_NUM_PLANES; }
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) errno_exit("VIDIOC_QUERYBUF");
if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == buf_type) { buffers[n_buffers].length = buf.m.planes[0].length; buffers[n_buffers].start = mmap(NULL , buf.m.planes[0].length, PROT_READ | PROT_WRITE , MAP_SHARED , fd, buf.m.planes[0].m.mem_offset); } else { buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap(NULL , buf.length, PROT_READ | PROT_WRITE , MAP_SHARED , fd, buf.m.offset); }
if (MAP_FAILED == buffers[n_buffers].start) errno_exit("mmap"); } }
static void open_device(void) { fd = open(dev_name, O_RDWR , 0); ...... }
static void init_device(void) { struct v4l2_capability cap; struct v4l2_format fmt;
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { ........ } ...... if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
CLEAR(fmt); fmt.type = buf_type; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; fmt.fmt.pix.pixelformat = format; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) errno_exit("VIDIOC_S_FMT");
if (io == IO_METHOD_MMAP) init_mmap(); else if (io == IO_METHOD_USERPTR) init_userp(fmt.fmt.pix.sizeimage, width, height); else if (io == IO_METHOD_DMABUF) init_dmabuf(fmt.fmt.pix.sizeimage, width, height); ...... }
static void start_capturing(void) { unsigned int i; struct RKisp_media_ctl rkisp; enum v4l2_buf_type type;
....... for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf;
CLEAR(buf); buf.type = buf_type; if (io == IO_METHOD_MMAP) buf.memory = V4L2_MEMORY_MMAP; else if (io == IO_METHOD_DMABUF) buf.memory = V4L2_MEMORY_DMABUF; else buf.memory = V4L2_MEMORY_USERPTR; buf.index = i;
if (io == IO_METHOD_USERPTR) { buf.m.userptr = (unsigned long)buffers[i].start; buf.length = buffers[i].length; } else if (io == IO_METHOD_DMABUF) { buf.m.fd = buffers[i].v4l2_buf.m.fd; buf.length = buffers[i].length; }
if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == buf_type) { struct v4l2_plane planes[FMT_NUM_PLANES];
if (io == IO_METHOD_USERPTR) { planes[0].m.userptr = (unsigned long)buffers[i].start; planes[0].length = buffers[i].length; } else if (io == IO_METHOD_DMABUF) { planes[0].m.fd = buffers[i].v4l2_buf.m.fd; planes[0].length = buffers[i].length; } buf.m.planes = planes; buf.length = FMT_NUM_PLANES; } if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); } type = buf_type; if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) errno_exit("VIDIOC_STREAMON"); }
static void mainloop(void) { unsigned int count = frame_count; float exptime, expgain; int64_t frame_id, frame_sof; ...... while (count-- > 0) { DBG("No.%d\n",frame_count - count); ...... read_frame(fp); } DBG("\nREAD AND SAVE DONE!\n"); } static void stop_capturing(void) { enum v4l2_buf_type type;
if (_RKIspFunc.stop_func != NULL) { DBG("stop rkisp engine\n"); _RKIspFunc.stop_func(_rkisp_engine); } type = buf_type; if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) errno_exit("VIDIOC_STREAMOFF"); } static void uninit_device(void) { unsigned int i; struct drm_mode_destroy_dumb destory_arg;
for (i = 0; i < n_buffers; ++i) { if (-1 == munmap(buffers[i].start, buffers[i].length)) errno_exit("munmap");
if (io == IO_METHOD_DMABUF) { close(buffers[i].v4l2_buf.m.fd); CLEAR(destory_arg); destory_arg.handle = drm_handle; drmIoctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); } }
free(buffers);
if (_RKIspFunc.deinit_func != NULL) { _RKIspFunc.deinit_func(_rkisp_engine); deinit_3A_control_params(); }
dlclose(_RKIspFunc.rkisp_handle); if (drm_fd != -1) deinit_drm(drm_fd); }
static void close_device(void) { if (-1 == close(fd)) errno_exit("close"); fd = -1; }
int main(int argc, char **argv) { parse_args(argc, argv); ...... open_device(); init_device(); start_capturing(); mainloop(); fclose(fp); stop_capturing(); uninit_device(); close_device(); return 0; }
|
(三)、rkisp_demo运行效果
1 2 3 4 5 6 7 8 9
| cd F:\Khadas_Edge_Android_Q\hardware\rockchip\camera_engine_rkisp\ mm 编译得到bin文件:rkisp_demo adb push out\target\product\rk3399_Android10\commit_id.xml /etc/ adb push out\target\product\rk3399_Android10\vendor\bin\rkisp_demo /vendor/bin/ 运行: adb shell rkisp_demo -d /dev/video0 -w 1280 -h 960 -f NV21 --count 16 -o /data/misc/cameraserver/rk3399_1280_960_16.nv21 然后pull文件: adb pull /data/misc/cameraserver/rk3399_1280_960_16.nv21
|
使用RawView打开(没经过算法优化感觉画质不好):