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來顯示捕獲到的image,所以和capture相比它更具有效率,而不是將捕獲到的image拷貝以後通過其他的方式(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換個角度來說是將捕獲的image重新組合成能在屏幕上顯示的視頻信號,在這裏的設置輸出應該是如果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我們在屏幕上看到的image的格式,如果在video capture中,這個就是我們拍照時image的格式。

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 中捕獲的某個image 保存下來。在FSL code 中是在overlay 的基礎上,重新打開設備,在新的文件描述符上設置take picture 相關的參數,從設備中讀到數據以後,再將參數還原到overlay 的狀態。下面我們看看take picture 的sequence :

1 、shutter callback

因爲拍照是通過快門事件激發的,所以首先會調用mShutterCallback ;

2 、open device

打開一個新的文件描述符來進行take picture 的操作

3 、set format

設置capture 的image 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.sizeimage = 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 image data

read(camera_device, buf1, fmt.fmt.pix.sizeimage) ;

這裏存在一點疑問,這個camera_device 中的數據是從何而來的,根據v4l2 協議 就是當前overlay 到的數據麼?

7 、將數據直接以rawcallback 返回或者壓縮成jpeg 返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章