看看Android系統,高通camera hal(camera3)當中camera preview數據是怎麼從kernel一層一層傳遞,最終發給SurfaceView的。
高通的Camera HAL的module在QualcommCamera.cpp[android/hardware/qcom/camera/qcamera2/hal/wrapper]
這個Camera HAL被加載是在CameraServer裏,加載流程如下:
Camera2Client創建的XXProcessor。這幾個Processor的說明如下:
1. StreamingProcessor並啓動一個他所屬的thread,該模塊主要負責處理previews與record兩種視頻流的處理,用於從hal層獲取原始的視頻數據
2. FrameProcessor並啓動一個thread,該模塊專門用於處理回調回來的每一幀的3A等信息,即每一幀視頻除去原始視頻數據外,還應該有其他附加的數據信息,如3A值。
3. CaptureSequencer並啓動一個thread,該模塊需要和其他模塊配合使用,主要用於向APP層告知capture到的picture。
4. pegProcessor並啓動一個thread,該模塊和streamprocessor類似,他啓動一個拍照流,一般用於從HAL層獲取jpeg編碼後的圖像照片數據
5. 此外ZslProcessor模塊稱之爲0秒快拍,其本質是直接從原始的Preview流中獲取預存着的最近的幾幀,直接編碼後返回給APP,而不需要再經過take picture去請求獲取jpeg數據。0秒快拍技術得意於當下處理器CSI2 MIPI性能的提升以及Sensor支持全像素高幀率的實時輸出。一般手機拍照在按下快門後都會有一定的延時,是因爲需要切換底層Camera以及ISP等的工作模式,並重新設置參數以及重新對焦等等,都需要花一定時間後才抓取一幀用於編碼爲jpeg圖像。
以上5個模塊整合在一起基本上實現了Camera應用開發所需的基本業務功能。
CameraServer啓動,HAL加載完之後,就可以從上層APP調用相關的接口打開camera並進行preview了。
1.第一步當然是打開camera了,也就是camera_device_open()函數。這個函數被調用之後的調用棧如下(中間的JNI等等忽略了~):
camera_device_open()[QualcommCamera.cpp]
->camera_device_open()[QCamera2Factory.cpp]
->cameraDeviceOpen()[QCamera2Factory.cpp]
->openCamera()[QCamera3HWI.cpp]
->camera_open()[mm_camera_interface.c]
->mm_camera_open()[mm_camera.c]
mm_camera_open()[mm_camera.c]這裏主要做的工作就是打開/dev/videoXX這個節點了。
還有就是創建一個socket,這個還不知道幹嘛~~
int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl){
...
sscanf(dev_name, "/dev/video%d", &cam_idx);
my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK);
...
mm_camera_socket_create(cam_idx, MM_CAMERA_SOCK_TYPE_UDP);
...
}
2.第二步,要進行preview當然要設置一個surfaceview給camera hal。這樣纔可以在得到frame數據之後在surfaceview中刷。這一步是APP調用setPreviewDisplay(SurfaceHolder holder)來完成的。
調用棧如下:
android_hardware_Camera_setPreviewSurface()
->setPreviewTarget()[camera.cpp]
->經過Binder
->setPreviewTarget()[CameraClient2.cpp]
->setPreviewWindow()[StreamingProcess.cpp]
最後保存在mPreviewWindow
3.第三步就是調用StartPreview來開始camera preview了。
status_t CameraClient::startPreview()
-> status_t CameraClient::startCameraMode(camera_mode mode)
->CameraClient::startPreviewMode()
->QCamera2HardwareInterface::setPreviewWindow(mPreviewWindow)
->QCamera2HardwareInterface::startPreview()[QCamera2HWI.cpp]
startPreview()函數設置Preview channel的回調函數
int QCamera2HardwareInterface::startPreview(){
...
rc = startChannel(QCAMERA_CH_TYPE_PREVIEW);
...
}
QCamera2HardwareInterface::startChannel()
-> QCameraChannel::config()
-> QCameraStream::configStream()
這裏設置stream_config.stream_cb = dataNotifyCB。
-> QCameraChannel::start()
-> mm_camera_intf_start_channel()
-> mm_camera_start_channel()
-> mm_channel_start() [mm_camera_channel.c]
-> mm_stream_fsm_reg()
->mm_camera_cmd_thread_launch()
->啓動線程,回調函數是mm_camera_cmd_thread()
//這個函數處理從kernel得到buffer等之後的操作,
//比如mm_stream_handle_rcvd_buf函數發的
//MM_CAMERA_CMD_TYPE_DATA_CB命令
->mm_stream_dispatch_app_data()//mm_camera_cmd_threa()又會調用這個函數
//最後buf_cb[0].cb是QCameraStream::dataNotifyCB()函數,這個又調用
//QCamera3Stream::processDataNotify把frame加到mDataQ,之後發
//CAMERA_CMD_TYPE_DO_NEXT_JOB。
//QCamera3Stream::dataProcRoutine()處理CAMERA_CMD_TYPE_DO_NEXT_JOB。
//調用pme->mDataCB()
//這個mDataCB()是在addPreviewChannel()->addStreamToChannel()的時候
//加的回調函數preview_stream_cb_routine !!!
//這個函數應該是處理並顯示用的
->mm_stream_streamon()
那camera的數據在哪裏監聽? 又傳給mm_camera_cmd_thread()線程的呢?
mm_stream_qbuf()
-> mm_camera_poll_thread_add_poll_fd(&my_obj->ch_obj->poll_thread[0],
my_obj->my_hdl, my_obj->fd, mm_stream_data_notify, (void*)my_obj,
mm_camera_async_call);
-> notify_cb就是mm_stream_data_notify()
mm_channel_init()
-> mm_camera_poll_thread_launch()
-> pthread_create(&poll_cb->pid, NULL, mm_camera_poll_thread, (void *)poll_cb);
-> mm_camera_poll_thread()
-> mm_camera_poll_fn()
-> 在調用poll,然後有返回就去調用notify_cb()了,也就是mm_stream_data_notify
mm_stream_data_notify()又會去調用mm_stream_handle_rcvd_buf()把數據發過去!!
參考:
http://blog.chinaunix.net/uid-2630593-id-3307179.html
qualcomm camera hal
http://my.oschina.net/jerikc/blog/215653
http://www.linuxidc.com/Linux/2011-09/42501p4.htm