最近在研究如何移植Android的camera系統(tǒng),對(duì)camera的應(yīng)用場景做了一些分析。Camera一般用于圖像瀏覽、拍照和視頻錄制。圖像瀏覽和拍照的數(shù)據(jù)流是比較清晰的,這里就不做贅述了。視頻錄制應(yīng)用于視頻電話中。撥打視頻電話時(shí),既可以看見對(duì)方的圖像,又可以看見自己的圖像;當(dāng)然,對(duì)方也是如此。從camera獲取的圖像數(shù)據(jù),既需要在本地瀏覽,還需要video encoder編碼后傳輸?shù)綄?duì)方手機(jī)。這樣的場景中,圖像數(shù)據(jù)要同時(shí)做preview和record兩種操作。
一、回調(diào)函數(shù)傳遞
首先需要將客戶端的回調(diào)函數(shù)傳遞到底層,當(dāng)?shù)讓荧@取完圖像數(shù)據(jù)后,回調(diào)該函數(shù),通知上層,做相應(yīng)的處理。
類AndroidCameraInput作為客戶端,它有兩個(gè)成員,分別為:
sp<android::Camera> mCamera;
sp<AndroidCameraInputListener> mListener;
類AndroidCameraInput就可以通過mCamera訪問camera系統(tǒng)的對(duì)外接口。
類AndroidCameraInputListener繼承于類CameraListener,它有三個(gè)成員函數(shù),分別為:
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2){}
virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
這里需要說明的是postDataTimestamp(),它是客戶端實(shí)現(xiàn)的回調(diào)函數(shù),其定義為:
void AndroidCameraInputListener:: postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr){}
當(dāng)camera HAL層捕獲到一幀數(shù)據(jù)后,就會(huì)調(diào)用該回調(diào)函數(shù),告訴客戶端??蛻舳嗽谶@個(gè)回調(diào)函數(shù)里先判斷參數(shù)msgType是不是CAMERA_MSG_VIDEO_FRAME,如果是,即表示要對(duì)該幀數(shù)據(jù)進(jìn)行編碼處理,編碼結(jié)束后會(huì)調(diào)用mCamera的函數(shù)releaseRecordingFrame(),其對(duì)應(yīng)的HAL層的定義為:
void QualcommCameraHardware::releaseRecordingFrame(const sp<IMemory>& mem_attibute_((unused))){}
在這個(gè)函數(shù)里,會(huì)調(diào)用函數(shù)LINK_camera_release_frame()告訴camera硬件,存放當(dāng)前幀的buffer可以被釋放,用于下一幀使用。
回調(diào)函數(shù)postDataTimestamp()如何注冊(cè)到HAL層,這里需要詳細(xì)說明。
在客戶端中,是通過mCamera的setListener()函數(shù)將mListener注冊(cè)到mCamera中的,既將幾個(gè)回調(diào)函數(shù)注冊(cè)給mCamera:
mCamera->setListener(mListener);
mCamera繼承于類BnCameraClient,而BnCameraClient繼承于類ICameraClient。類ICameraClient有純虛函數(shù):
virtual void dataCallbackTimstamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data) = 0;
類Camera中定義了虛函數(shù)dataCallbackTimstamp()并做了實(shí)現(xiàn):
virtual void dataCallbackTimstamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data);
dataCallbackTimstamp()實(shí)現(xiàn)中,調(diào)用了類Camera的成員mListener的成員函數(shù)postDataTimestamp()。
類CameraService有成員類Client,類Client有成員:
sp<ICameraClient> mCameraClient;
sp<CameraHardwareInterface> mHardware;
在類CameraService的成員類Client構(gòu)造函數(shù)中:
CameraService::Client::Client(constsp<CameraService>& cameraService, constsp<ICameraClient>& cameraClient, pid_t clientPid){}
其中調(diào)用了mHardware的函數(shù)setCallbacks():
mHardware->setCallbacks(notifyCallback,
dataCallback,
dataCallbackTimestamp,
mCameraService.get());
如下函數(shù):
bool QualcommCameraHardware::setCallbacks(preview_callback pcb, void *puser,
recording_callback rcb, void *ruser){}
將recording_callback賦給了mHardware的成員mRecordingCallback,至此,已將類AndroidCameraInput中的回調(diào)函數(shù)postDataTimestamp()傳遞給了camera的HAL層的成員mRecordingCallback。
二、數(shù)據(jù)buffer分配
在提供的Android代碼中,有一個(gè)camera HAL層的實(shí)例,即QualcommCameraHardware。在類QualcommCameraHardware中,默認(rèn)分配了6個(gè)buffer,其中4個(gè)preview buffer,1個(gè)raw buffer,1個(gè)JPEG buffer。在視頻電話中,只用了preview buffer。
類QualcommCameraHardware在函數(shù)initPreview()中對(duì)preview buffer做了初始化。
bool QualcommCameraHardware::initPreview(){}
其中有code為:
mPreviewHeap = new PreviewPmemPool(kRawFrameHeaderSize +
mPreviewWidth * mPreviewHeight * 2, //worst
kPreviewBufferCount,
mPreviewFrameSize,
kRawFrameHeaderSize,
“preview”);
QualcommCameraHardware::PreviewPmemPool::PreviewPmemPool(
int buffer_size, int num_buffers,
int frame_size,
int frame_offset,
const char *name) :
QualcommCameraHardware::PmemPool("/dev/pmem_adsp",
buffer_size,
num_buffers,
frame_size,
frame_offset,
name)
{
LOGV("constructing PreviewPmemPool");
if (initialized()) {
LINK_camera_assoc_pmem(QDSP_MODULE_VFETASK,
mFd,
mHeap->base(),
mAlignedSize,
0); // external
}
}
QualcommCameraHardwares使用PMEM驅(qū)動(dòng)對(duì)preview buffer進(jìn)行了分配。Android PMEM是其專用的驅(qū)動(dòng),稱為物理內(nèi)存驅(qū)動(dòng)。在HAL層分配完preview buffer后,通過函數(shù)LINK_camera_assoc_pmem將分配信息傳遞給底層庫。底層庫中有對(duì)這4個(gè)preview buffer的管理機(jī)制。
視頻電話中,當(dāng)camera捕獲一幀數(shù)據(jù)后,存儲(chǔ)該數(shù)據(jù)的buffer會(huì)被同時(shí)用于preview和record。只用當(dāng)客戶端調(diào)用了函數(shù)releaseRecordingFrame()之后才能將對(duì)應(yīng)的buffer釋放掉,用于其它幀使用。
三、視頻錄制調(diào)用
客戶端類AndroidCameraInput啟動(dòng)record,通過下面的調(diào)用:
mCamera->startRecording();
其會(huì)調(diào)到函數(shù):
status_t CameraService::Client::startRecording(){}
此處為mHardware設(shè)置了message CAMERA_MSG_VIDEO_FRAME,并調(diào)用函數(shù) startCameraMode()。其定義為:
status_t CameraService::Client::startCameraMode(camera_mode mode){}
參數(shù)camera_mode為CAMERA_RECORDING_MODE,于是調(diào)了startRecordingMode。其定義為:
status_t CameraService::Client::startRecordingMode(){}
其中會(huì)先啟動(dòng)preview,如果它沒有啟動(dòng)的話,調(diào)用了startPreviewMode,即:
status_t CameraService::Client::startPreviewMode(){}
這里會(huì)處理preview的顯示介質(zhì),如果使用Overlay顯示,會(huì)設(shè)置相應(yīng)的Overlay,同時(shí)調(diào)用mHardware->startPreview()以啟動(dòng)preview;否則先調(diào)用mHardware->startPreview()啟動(dòng)preview,然后設(shè)置buffer:調(diào)用函數(shù)registerPreviewBuffers(),其定義為:
status_t CameraService::Client::registerPreviewBuffers(){}
這里會(huì)調(diào)用mHardware->getPreviewHeap(),從HAL層獲得preview的buffer,將其設(shè)置給Surface去顯示preview的結(jié)果。
類QualcommCameraHardware對(duì)startPreview的定義如下:
status_t QualcommCameraHardware::startPreview(preview_callback pcb, void *puser){}
它調(diào)用了startPreviewInternal(),其定義為:
status_t QualcommCameraHardware:startPreviewInternal(preview_callback pcb, void (puser, recording_callback rcb, void *ruser){}
函數(shù)里調(diào)用了setCallbacks(pcb, puser, rcb, ruser),更新了preview和record的回調(diào)函數(shù)。另外調(diào)用函數(shù)LINK_camera_start_preview(camera_cb, this),向driver層傳遞函數(shù)camera_cb。其定義為:
void QualcommCameraHardware::camera_cb(camera_cb_type cb, const void *client_data, camera_func_type func, int32_t parm4){}
函數(shù)里,當(dāng)mCameraState為QCS_PREVIEW_IN_PROGRESS時(shí),preview成功,同時(shí)調(diào)用函數(shù)receivePreviewFrame,其定義為:
void QualcommCameraHardware::receivePreviewFrame(camera_frame_type *frame){}
它調(diào)用了回調(diào)函數(shù)mPreviewCallback和mRecordingCallback,這就回調(diào)了函數(shù)postDataTimstamp(),告訴客戶端一幀數(shù)據(jù)已經(jīng)獲取成功,其可以開始編碼了。
前面已經(jīng)講過,當(dāng)客戶端對(duì)該幀數(shù)據(jù)的處理結(jié)束后,會(huì)告訴底層庫釋放該幀所占用的buffer空間,以備其他幀使用。
如此,preview和record同時(shí)進(jìn)行,即可實(shí)現(xiàn)視頻電話功能。
四、Preview數(shù)據(jù)的顯示
Preview數(shù)據(jù)可以通過Overlay和Surface兩種介質(zhì)去顯示,下面分別作以介紹。
1、 使用Overlay顯示
如果要使用Overlay,底層硬件必須支持Overlay。在CameraService::Client的構(gòu)造函數(shù)中,有相應(yīng)的判斷。
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient, pid_t clientPid){}
mUseOverlay = mHardware->useOverlay();如果返回值為true,則表示硬件支持Overlay;否則只能使用Surface顯示。
status_t CameraService::Client::startPreviewMode(){}中,判斷如果Overlay可用,就會(huì)調(diào)用函數(shù)setOverlay(),其定義為:
status_t CameraService::Client::setOverlay(){}
其中會(huì)通過mSurface->createOverlay()創(chuàng)建Overlay,然后通過函數(shù)mHardware->setOverlay(new Overlay(mOverlayRef));將其設(shè)置給mHardware。
Android系統(tǒng)中提供了Overlay的接口,其具體實(shí)現(xiàn)需要自己做。在TI的實(shí)例中,在創(chuàng)建Overlay時(shí),它就為Overlay申請(qǐng)了8個(gè)buffer。然后在HAL層,通過Overlay的接口:void* getBufferAddress(overlay_buffer_t buffer),獲得Overlay的buffer地址。將該buffer傳遞給硬件,camera硬件將捕獲的數(shù)據(jù)填充到該buffer中。然后通過Overlay的接口:status_t queueBuffer(overlay_buffer_t buffer),將該buffer加入到Overlay的隊(duì)列中。最后調(diào)用Overlay的接口:status_t dequeueBuffer(overlay_buffer_t* buffer),將那個(gè)buffer從隊(duì)列中取出,此時(shí)即Overlay已經(jīng)對(duì)其顯示過了。dequeueBuffer()一般應(yīng)該是一個(gè)阻塞函數(shù),當(dāng)顯示完成后這個(gè)函數(shù)才返回。
當(dāng)然,Overlay的buffer也可以在HAL層申請(qǐng),然后通過queueBuffer(),將buffer加入到Overlay的隊(duì)列中。
2、 使用Surface顯示
在CameraService的函數(shù):status_t CameraService::Client::startPreviewMode()中,如果使用Surface,會(huì)調(diào)用函數(shù)registerPreviewBuffers()向Surface注冊(cè)buffers。registerPreviewBuffers()的定義為:
status_t CameraService::Client::registerPreviewBuffers(){}
其中有:
ISurface::BufferHeap buffers(w, h, w, h,
PIXEL_FORMAT_YCbCr_420_SP,
transform,
0,
mHardware->getPreviewHeap());
status_t ret = mSurface->registerBuffers(buffers);
其將mHardware的preview heap傳遞給了Surface。
當(dāng)Camera的底層庫中獲取到了preview數(shù)據(jù),它會(huì)回調(diào)函數(shù):
void QualcommCameraHardware::camera_cb(camera_cb_type cb,
const void *client_data,
camera_func_type func,
int32_t parm4){}
其中,在preview模式下,會(huì)調(diào)用函數(shù):
void QualcommCameraHardware::receivePreviewFrame(camera_frame_type *frame){}
它有代碼如下:
mPreviewCallback(mPreviewHeap->mBuffers[offset],
mPreviewCallbackCookie);
這是回調(diào)了preview的回調(diào)函數(shù)。其定義為:
void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user){}
其中,preview模式下,會(huì)調(diào)用函數(shù):
void CameraService::Client::handlePreviewData(const sp<IMemory>& mem){}
其通過mSurface->postBuffer(offset),將存儲(chǔ)當(dāng)前捕獲數(shù)據(jù)的buffer送給Surface去顯示。
函數(shù)CameraService::Client::stopPreview()中會(huì)調(diào)用函數(shù):
mSurface->unregisterBuffers(),以釋放原來注冊(cè)的buffers。
當(dāng)回調(diào)了preview的回調(diào)函數(shù)mPreviewCallback之后,如果mRecordingCallback為空,則直接調(diào)用LINK_camera_release_frame(),釋放當(dāng)前圖像數(shù)據(jù)的buffer;否則調(diào)用回調(diào)函數(shù)mRecordingCallback,進(jìn)入錄制的流程。
以上就是我對(duì)Android系統(tǒng)中,視頻電話的Camera部分的數(shù)據(jù)流的一個(gè)簡單分析。如有問題,還望各位高手指點(diǎn),謝謝。
聯(lián)系客服