Android視頻編碼的實現(xiàn)是在Android 1.5中才引入的,其設(shè)計主要圍繞著Camera子系統(tǒng)進行。在進行Camera錄像時,流程與拍照基本相同。需要了解的是,對于應(yīng)用開發(fā)者而言,錄像的上層接口為MediaRecorder。下圖所示為原生層MediaRecorder的狀態(tài)遷移過程。Android的視頻編碼框架代碼位于mydroid\frameworks\base\media目錄下。

視頻編碼狀態(tài)圖
下面簡要介紹在進行錄像時,Java層調(diào)用Camera原生服務(wù)的過程。下圖顯示的是基于CameraHardwareStub的Camera錄像過程。

Camera錄像過程
流程說明:
當用戶需要進行錄像時,首先要進行Camera設(shè)備的連接,然后進行預(yù)覽,為了進行錄像,需要通過擴展的Java JNI調(diào)用Camera客戶端的Camera::startRecording()方法?蛻舳藭埱驝amera服務(wù)器端提供錄像服務(wù),在這一過程中,Camera服務(wù)器端會激活CAMERA_ MSG_VIDEO_FRAME消息類型,并將Camera模式設(shè)置為CAMERA_RECORDING_MODE。
為了進行錄像,首先預(yù)先設(shè)置錄像回調(diào)函數(shù)(在mRecordingCallback回調(diào)函數(shù)中,上層實現(xiàn)將收到傳過去的幀),然后啟動預(yù)覽,下面是QualcommCameraHardware::startRecording()方法的實現(xiàn)過程:
代碼:QualcommCameraHardware:: startRecording ()方法的實現(xiàn)過程
獲取媒體播放服務(wù)的實現(xiàn)如下:
代碼:MediaPlayer::getMediaPlayerService()的實現(xiàn)
status_t QualcommCameraHardware::startRecording(recording_callback rcb, void *ruser)
{
LOGV("start Recording E");
Mutex::Autolock l(&mLock);
{
Mutex::Autolock cbLock(&mCallbackLock);
if (mPreviewstatus) {
mRecordingCallback=rcb; //設(shè)置錄像回調(diào)
mRecordingCallbackCookie=ruser; //設(shè)置錄像Cookie回調(diào)
return NO_ERROR;
}
}
if (!initPreview()) { //初始化預(yù)覽
LOGE("startPreview X initPreview failed. Not starting preview.");
return UNKNOWN_ERROR;
}
{
Mutex::Autolock cbLock(&mCallbackLock);
mRecordingCallback=rcb;
mRecordingCallbackCookie=ruser;
mPreviewstatus=TRUE;
mCameraRunning=mParameters.getCameraEnabledVal();
}
//向Camera驅(qū)動發(fā)送CAMERA_START_PREVIEW,開始預(yù)覽
if (!native_start_preview(camerafd)) {
LOGE("main: start_preview failed!\n");
return UNKNOWN_ERROR;
}
LOGV("waiting for QCS_PREVIEW_IN_PROGRESS");
LOGV("Start Recording X");
return NO_ERROR;
}
在進行預(yù)覽或者錄像時,需要將圖像渲染到屏幕。在這一過程中,首先需要創(chuàng)建PMEM預(yù)覽緩沖,然后將該緩沖進行注冊,接著創(chuàng)建frame_thread線程,下面是Qualcomm CameraHardware::initPreview()方法的實現(xiàn)過程:
代碼:QualcommCameraHardware::initPreview()方法的實現(xiàn)過程
bool QualcommCameraHardware::initPreview()
{
LOGV("initPreview: preview size=%dx%d", mPreviewWidth, mPreviewHeight);
int cnt=0;
dimension->picture_width= PICTURE_WIDTH;
dimension->picture_height=PICTURE_HEIGHT;
if((native_set_dimension(camerafd, dimension) ==TRUE)) {
// 初始化SF幀
mPreviewFrameSize=mPreviewWidth * mPreviewHeight * 3/2;
//創(chuàng)建PMEM預(yù)覽緩沖
mPreviewHeap=
new PreviewPmemPool(kRawFrameHeaderSize +
mPreviewWidth * mPreviewHeight * 3/2,
kPreviewBufferCount,
mPreviewFrameSize,
kRawFrameHeaderSize,
"preview");
if (!mPreviewHeap->initialized()) {
mPreviewHeap=NULL;
return false;
}
LOGI("hal display_width=%d height=%d\n",
(int)dimension->display_width, (int)dimension->display_height);
frame_size=(clp2(dimension->display_width * dimension->display_height *3/2));
unsigned char activeBuffer;
for (cnt=0; cnt < PREVIEW_FRAMES_NUM; cnt++) {
frames[cnt].fd=mPreviewHeap->mHeapnew[cnt]->heapID();
frames[cnt].buffer=(unsigned long)mPreviewHeap->mHeapnew[cnt]->base();
LOGE("hal_mmap #%d start=%x end=%x", (int)cnt, (int)frames[cnt].buffer,
(int)(frames[cnt].buffer+frame_size-1));
frames[cnt].y_off=0;
frames[cnt].cbcr_off=dimension->display_width * dimension->display_height;
if (frames[cnt].buffer==0) {
LOGV("main: malloc failed!\n");
return 0;
}
if (cnt==PREVIEW_FRAMES_NUM-1) {
activeBuffer=FALSE;
} else {
activeBuffer=TRUE;
}
frames[cnt].path=MSM_FRAME_ENC;
LOGV("do_mmap pbuf=0x%x, pmem_fd=%d, active=%d\n",(unsigned int)frames[cnt].buffer, frames[cnt].fd, activeBuffer);
native_register_preview_bufs(camerafd, //注冊PMEM緩沖dimension,&frames[cnt],activeBuffer);
}
}
if (frame_count==1) {
frame_count--;
lastframe=frames[PREVIEW_FRAMES_NUM-1];
//創(chuàng)建frame_thread線程
pthread_create(&frame_thread, NULL,LINK_cam_frame,&frames[PREVIEW_FRAMES_NUM-1]);
}
return true;
}
需要說明的是,PreviewPmemPool、RawPmemPool、AshmemPool都是基于MemPool內(nèi)存池的,MemPool內(nèi)存池則是基于Android PMEM虛擬設(shè)備的,Android PMEM用于向用戶空間提供連續(xù)的物理內(nèi)存區(qū)域。Android PMEM虛擬設(shè)備的作用主要有兩個方面,第一,CPU核與GPU(Graphic Processing Unit)或者VPU(Vector Permutate Unit)共享的緩沖;第二,用做Android原生服務(wù)的內(nèi)存堆。
當錄像結(jié)束時,要調(diào)用QualcommCameraHardware::stopRecording()方法停止錄像,下面是該方法的實現(xiàn)過程:
代碼:QualcommCameraHardware::stopRecording()方法的實現(xiàn)過程
void QualcommCameraHardware::stopRecording()
{
LOGV("stopRecording: E");
Mutex::Autolock l(&mLock);
int cnt=0;
{
Mutex::Autolock cbLock(&mCallbackLock);
mRecordingCallback=NULL; //清空回調(diào)
mRecordingCallbackCookie=NULL;
mReleaseRecordingFrame=TRUE;
mRecordWait.signal();
mCameraRunning=0;
if(mPreviewCallback !=NULL)
return;
mPreviewstatus=NULL;
}
native_stop_preview(camerafd); //停止預(yù)覽
LOGV("stopRecording: Freeing preview heap.");
if (!frame_count) {
LINK_camframe_terminate();
if (pthread_join(frame_thread, NULL) !=0) {//結(jié)束frame_thread線程
LOGE("frame_thread exit failure!\n");
} else
LOGE("pthread_cancel succeeded on frame_thread\n");
for (cnt=0; cnt< PREVIEW_FRAMES_NUM-1; ++cnt) {
native_unregister_preview_bufs(camerafd,dimension,frames[cnt].fd,(unsigned char *)frames[cnt].buffer); //注銷PMEM緩沖
}
native_unregister_preview_bufs(camerafd, dimension,lastframe.fd,(unsigned char *)lastframe.buffer);
frame_count=1;
}
mPreviewHeap=NULL;
mRecordingCallback=NULL;
LOGV("stopRecording: X");
}
在結(jié)束錄像過程中,會首先將錄像用的回調(diào)函數(shù)如mRecordingCallback、mRecordingCallbackCookie清空,通過native_stop_preview()向Camera驅(qū)動發(fā)送CAMERA_STOP_PREVIEW消息結(jié)束預(yù)覽,然后調(diào)用pthread_join()結(jié)束frame_thread線程,后調(diào)用native_unregister_preview_bufs()注銷預(yù)覽用的PMEM緩沖。