转自:http://zhougaofeng.ixiezi.com/2009/12/02/android-camera-preview-and-take-picture-with-v4l2/
这部分spec的内容没有全看懂,但是根据FSL的代码能知道这其中的sequence,下面就结合着FSL的代码来描述下video overlay过程的sequence。
首先介绍一下video overlay,vieo overlay不同于video capture,是指不需要对video信号的帧进行copy,直接将视频信号转化成显卡的VGA信号或者将捕获到的视频帧直接存放在显卡的内存中,具体 过程就是将视频帧直接写入framebuffer中,不需要经过android 平台的处理。实际上看过FSL的camera preview过程之后就知道,它就是直接将视频数据 写入framebuffer,而没有经过android的surfaceflinger的处理。Video overlay需要硬件的支持,必须是支持video overlay的camera才能使用这套overlay interface。
因为video overlay直接使用 linux 的framebuffer来显示捕获到的 p_w_picpath , 所以和capture相比它更具有效率,而不是将捕获到的p_w_picpath拷贝以后通过其他的方式(android surfaceflinger)来显示。Viedo overlay只用来preview,又被称为framebuffer overlay或previwing。
从spec上来看,实际上video capture interface也能实现preview,只是没有overlay有效率,因为video capture是将数据经过copy以后由android surfaceflinger来控制进行显示(实际上最后surfaceflinger还是通过framebuffer来显示的)。
Video overlay和Video capture使用同样的device,overlay的功能只有在调用VIDIOC_S_FMT后才会有效。下面就看看overlay流程的sequcence。
1、open device
这部分同video capture,首先是要打开设备。如果是同时进行overlay和capture,应该尽量不使用同一个文件 描述符,比如说如果此时在overlay,要拍照的话应该再打开设备,使用一个分开的文件描述符来进行capture。如果driver支持同时进行 overlay和capture的话,必须支持使用分开的文件描述符来分别进行overlay和capture 。
camera_device = open(VIDEO_DEVICE, O_RDWR)) ;
2、set output
对于这个设置 输出不是太理解,但overlay换个角度来说是将捕获的p_w_picpath重新组合成能在屏幕上显示的视频信号,在这里的设置输出应该是如果device有多个输出的话,选择一个输出来将数据输入到屏幕,也就是framebuffer。
ioctl(camera_device, VIDIOC_S_OUTPUT, &g_display_lcd) ;
3、set control[可选]
设置用户控制参数,FSL在这里使用了他们驱动自定义的控制参数,不是很理解这个参数设置是想实现什么操作,我觉得这个应该是可选的。
ioctl(camera_device, VIDIOC_S_CTRL, &ctl) ;
4、set crop
这个同video capture是一样的,只是type由V4L2_BUF_TYPE_VIDEO_CAPTURE换成了V4L2_BUF_TYPE_VIDEO_OVERLAY,然后是取景参数的设置:left,top,width,height。
ioctl(camera_device, VIDIOC_S_CROP, &crop) ;
5、set format
这个format应该是最后preview我们在屏幕上看到的p_w_picpath的格式,如果在video capture中,这个就是我们拍照时p_w_picpath的格式。
ioctl(camera_device, VIDIOC_S_FMT, fmt) ;
6、 get video std
这个我觉得肯定是可选的,取得当前视频标准
ioctl(camera_device, VIDIOC_G_STD, &id) ;
7、set stream param
设置流参数,这个和video capture是相同的,这里的param.type是V4L2_BUF_TYPE_VIDEO_CAPTURE,其中timeperframe的分母是需要设定的帧率,而分子是1。
ioctl(camera_device, VIDIOC_S_PARM, &parm) ;
[PS]这里补充一点stream param方面的spec:
一般来说当前的帧率是由当前的视频标准来决定的,如果默认采用视频标准的帧率就不需要设置流参数,但是如果想获得或者设定自己的帧率就需要使用VIDIOC_G_PARM, VIDIOC_S_PARM:
int ioctl(int fd, int request, v4l2_streamparm *argp);
struct v4l2_stramparam包含以下主要成员:
enum v4l2_buf_type type
union param
struct v4l2_captureparm capture
struct v4l2_outputparam output
//要注意的是在这里,不管是overaly还是capture,这里的buffer type都是选择的V4L2_BUF_TYPE_VIDEO_CAPTURE。
Struct v4l2_captureparam包含以下主要成员:
__u32 capturemode
//是否支持高质量图像捕捉
struct v4l2_fract timeperframe
//设置帧率,通过分母分子实现
这里要注意的是,通过 VIDIOC_S_PARM设置帧率不一定成功,driver会根据硬件限制来设置这些参数,所以一般设置以后可以通过VIDIOC_G_PARM来看设定是否成功。
前面从1-8都是设置overlay的参数,然后需要设置framebuufer的参数,framebuffer参数部分的设置通过VIDIOC_G_FBUF,VIDIOC_S_FBUF来实现,这里的参数也是比较复杂,具体可以去参照这个spec:http://v4l2spec.bytesex.org/spec/r10595.htm
我看FSL的代码,它也是采用了默认的framebuffer的参数,唯一的就是改变了一下flag:
ioctl(camera_device, VIDIOC_G_FBUF, &fb_v4l2) ;
fb_v4l2.flags = V4L2_FBUF_FLAG_OVERLAY;
ioctl(camera_device, VIDIOC_S_FBUF, &fb_v4l2) ;
在设置framebubffer参数之前,FSL打开了framebuffer设备,并对屏幕进行了一系列的配置,因为overlay状态下是直接写屏,所以它这里加入了对framebuffer设备的配置工作。
完成这一系列的配置以后通过调用overlay即开始了overlay过程,在这里overlay的数为0时为关闭overlay,为1时为打开overlay。
ioctl(camera_device, VIDIOC_OVERLAY, &overlay) ;
实际上看FSL的preview代码,虽然也加入了线程的概念,但线程内没有作什么实际性的内容,在preview线程起来之前,overlay实际上已经开始了。个人觉得这个preview线程是一个多余的操作。而且它也没有用到android内部的 memory , 因此在overlay preview的启动过程中对initHeapLocked()的调用也完全是多余的,没有进行实质性的操作。由此可见FSL code完全就是通过linux和v4l2来实现了camera preview的功能,甚至连previewcallback都省去了。
FSL camera take picture with v4l2
首先说明一下可能是因为FSL 的camera 不支持autofocus ,所以它没有实现对autofocus 的支持,autofucus 线程直接调用了callback ,没有进行操作。
FSL 对拍照提供了一套它自己的 jpeg 编码,获得的数据可以进行jpeg 编码,也可以直接将捕获的raw data 以call back 返回。Take picture 是建立在preview 的基础 上的,实际上就是将preview 中捕获的某个p_w_picpath 保存下来。在FSL code 中是在overlay 的基础上,重新打开设备,在新的文件描述符上设置take picture 相关的参数,从设备中读到数据以后,再将参数还原到overlay 的状态。下面我们看看take picture 的sequence :
1 、shutter callback
因为拍照是通过快门事件激发的,所以首先会调用mShutterCallback ;
2 、open device
打开一个新的文件描述符来进行take picture 的操作
3 、set format
设置capture 的p_w_picpath format ,可以看看它对参数的设置:
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = g_still_pixelformat;
fmt.fmt.pix.width = g_width;
fmt.fmt.pix.height = g_height;
fmt.fmt.pix.sizep_w_picpath = fmt.fmt.pix.width * fmt.fmt.pix.height * g_still_bpp / 8;
fmt.fmt.pix.bytesperline = g_width * g_still_bpp / 8;
其中bpp 是每个像素所占有的比特位
ioctl(fd_v4l, VIDIOC_S_FMT, &fmt) ;
4 、set crop
同样可以看看设置的crop parameter :
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c.left = 0;
crop.c.top = 0;
crop.c.width = g_width;
crop.c.height = g_height;
ioctl(fd_v4l, VIDIOC_S_CROP, &crop) ;
5 、set stream paramter
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = g_still_camera_framerate;
parm.parm.capture.capturemode = g_capture_mode;
ioctl(fd_v4l, VIDIOC_S_PARM, &parm) ;
6 、read p_w_picpath data
read(camera_device, buf1, fmt.fmt.pix.sizep_w_picpath) ;
这里存在一点疑问,这个camera_device 中的数据是从何而来的,根据v4l2 协议 就是当前overlay 到的数据么?
7 、将数据直接以rawcallback 返回或者压缩成jpeg 返回。