您的位置:华清远见教育科技集团 >> Android资料 >> Android Camera录像过程分析  
 
Android Camera录像过程分析
分享到:

Android视频编码的实现是在Android 1.5中才引入的,其设计主要围绕着Camera子系统进行。在进行Camera录像时,流程与拍照基本相同。需要了解的是,对于应用开发者而言,录像的上层接口为MediaRecorder。下图所示为原生层MediaRecorder的状态迁移过程。Android的视频编码框架代码位于mydroid\frameworks\base\media目录下。


视频编码状态图

下面简要介绍在进行录像时,Java层调用Camera原生服务的过程。下图显示的是基于CameraHardwareStub的Camera录像过程。


Camera录像过程

流程说明:

当用户需要进行录像时,首先要进行Camera设备的连接,然后进行预览,为了进行录像,需要通过扩展的Java JNI调用Camera客户端的Camera::startRecording()方法。客户端会请求Camera服务器端提供录像服务,在这一过程中,Camera服务器端会激活CAMERA_ MSG_VIDEO_FRAME消息类型,并将Camera模式设置为CAMERA_RECORDING_MODE。

为了进行录像,首先预先设置录像回调函数(在mRecordingCallback回调函数中,上层实现将收到传过去的帧),然后启动预览,下面是QualcommCameraHardware::startRecording()方法的实现过程:

代码:QualcommCameraHardware:: startRecording ()方法的实现过程

获取媒体播放服务的实现如下:

代码:MediaPlayer::getMediaPlayerService()的实现

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;    //设置录像回调
      mRecordingCallbackCookie=ruser;    //设置录像Cookie回调
      return NO_ERROR;
      }
    }
    if (!initPreview()) {    //初始化预览
      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驱动发送CAMERA_START_PREVIEW,开始预览
    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;
    }

在进行预览或者录像时,需要将图像渲染到屏幕。在这一过程中,首先需要创建PMEM预览缓冲,然后将该缓冲进行注册,接着创建frame_thread线程,下面是Qualcomm CameraHardware::initPreview()方法的实现过程:

代码:QualcommCameraHardware::initPreview()方法的实现过程

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;
    //创建PMEM预览缓冲
    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];
    //创建frame_thread线程
    pthread_create(&frame_thread, NULL,LINK_cam_frame,&frames[PREVIEW_FRAMES_NUM-1]);
     }
    return true;
    }

需要说明的是,PreviewPmemPool、RawPmemPool、AshmemPool都是基于MemPool内存池的,MemPool内存池则是基于Android PMEM虚拟设备的,Android PMEM用于向用户空间提供连续的物理内存区域。Android PMEM虚拟设备的作用主要有两个方面,第一,CPU核与GPU(Graphic Processing Unit)或者VPU(Vector Permutate Unit)共享的缓冲;第二,用做Android原生服务的内存堆。

当录像结束时,要调用QualcommCameraHardware::stopRecording()方法停止录像,下面是该方法的实现过程:

代码:QualcommCameraHardware::stopRecording()方法的实现过程

void QualcommCameraHardware::stopRecording()
    {
    LOGV("stopRecording: E");
    Mutex::Autolock l(&mLock);
    int cnt=0;
    {
        Mutex::Autolock cbLock(&mCallbackLock);
        mRecordingCallback=NULL; //清空回调
        mRecordingCallbackCookie=NULL;
        mReleaseRecordingFrame=TRUE;
        mRecordWait.signal();
        mCameraRunning=0;
        if(mPreviewCallback !=NULL)
        return;
        mPreviewstatus=NULL;
    }
    native_stop_preview(camerafd);    //停止预览
    LOGV("stopRecording: Freeing preview heap.");
        if (!frame_count) {
        LINK_camframe_terminate();
        if (pthread_join(frame_thread, NULL) !=0) {//结束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");
    }

在结束录像过程中,会首先将录像用的回调函数如mRecordingCallback、mRecordingCallbackCookie清空,通过native_stop_preview()向Camera驱动发送CAMERA_STOP_PREVIEW消息结束预览,然后调用pthread_join()结束frame_thread线程,后调用native_unregister_preview_bufs()注销预览用的PMEM缓冲。

 更多相关文章

·Android 原生层媒体播放过程
·Android 视频解码过程详解
·Android 视频编码源码分享
·Android 双缓冲局部渲染
·Android Overlay硬件加速