Android 视频电话中的camera部分数据流分析

来源:互联网 发布:linux trap catch 编辑:程序博客网 时间:2024/05/16 10:55

http://www.eoeandroid.com/thread-99098-1-1.html


  最近在研究如何移植Android的camera系统,对camera的应用场景做了一些分析。Camera一般用于图像浏览、拍照和视频录制。图像浏览和拍照的数据流是比较清晰的,这里就不做赘述了。视频录制应用于视频电话中。拨打视频电话时,既可以看见对方的图像,又可以看见自己的图像;当然,对方也是如此。从camera获取的图像数据,既需要在本地浏览,还需要video encoder编码后传输到对方手机。这样的场景中,图像数据要同时做preview和record两种操作。

       一、回调函数传递
       首先需要将客户端的回调函数传递到底层,当底层获取完图像数据后,回调该函数,通知上层,做相应的处理。

       类AndroidCameraInput作为客户端,它有两个成员,分别为:

java代码:

  1. sp<android::Camera> mCamera;
  2. sp<AndroidCameraInputListener> mListener;
复制代码

        类AndroidCameraInput就可以通过mCamera访问camera系统的对外接口。
        类AndroidCameraInputListener继承于类CameraListener,它有三个成员函数,分别为:

java代码:
  1. virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2){}
  2. virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
  3. virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory
复制代码

        这里需要说明的是postDataTimestamp(),它是客户端实现的回调函数,其定义为:

java代码:
  1. void AndroidCameraInputListener:: postDataTimestamp(nsecs_t timestamp, int32_t msgType
复制代码

       当camera HAL层捕获到一帧数据后,就会调用该回调函数,告诉客户端。客户端在这个回调函数里先判断参数msgType是不是CAMERA_MSG_VIDEO_FRAME,如果是,即表示要对该帧数据进行编码处理,编码结束后会调用mCamera的函数releaseRecordingFrame(),其对应的HAL层的定义为:

java代码:
  1. void QualcommCameraHardware::releaseRecordingFrame(const sp<IMemory>& mem_attibute
复制代码

       在这个函数里,会调用函数LINK_camera_release_frame()告诉camera硬件,存放当前帧的buffer可以被释放,用于下一帧使用。

       回调函数postDataTimestamp()如何注册到HAL层,这里需要详细说明。
       在客户端中,是通过mCamera的setListener()函数将mListener注册到mCamera中的,既将几个回调函数注册给mCamera:

java代码:
  1. mCamera->setListener(mListener);
复制代码

        mCamera继承于类BnCameraClient,而BnCameraClient继承于类ICameraClient。类ICameraClient有纯虚函数:

java代码:
  1. virtual void dataCallbackTimstamp(nsecs_t timestamp, int32_t msgType, const sp
复制代码

       类Camera中定义了虚函数dataCallbackTimstamp()并做了实现:

java代码:
  1. virtual void dataCallbackTimstamp(nsecs_t timestamp, int32_t msgType, const sp
复制代码

        dataCallbackTimstamp()实现中,调用了类Camera的成员mListener的成员函数postDataTimestamp()。
        类CameraService有成员类Client,类Client有成员:

java代码:
  1. sp<ICameraClient> mCameraClient;
  2. sp<CameraHardwareInterface> mHardware;
复制代码

       在类CameraService的成员类Client构造函数中:

java代码:
  1. CameraService::Client::Client(const sp<CameraService>& cameraService, const sp<ICameraClient
复制代码

        其中调用了mHardware的函数setCallbacks():

java代码:
  1. mHardware->setCallbacks(notifyCallback,
  2. dataCallback,
  3. dataCallbackTimestamp,
  4. mCameraService.get());
复制代码

如下函数:

java代码:
  1. bool QualcommCameraHardware::setCallbacks(preview_callback pcb, void *puser,
  2. recording_callback rcb, void
复制代码

       将recording_callback赋给了mHardware的成员mRecordingCallback,至此,已将类AndroidCameraInput中的回调函数postDataTimestamp()传递给了camera的HAL层的成员mRecordingCallback。

       二、数据buffer分配
       在提供的Android代码中,有一个camera HAL层的实例,即QualcommCameraHardware。在类QualcommCameraHardware中,默认分配了6个buffer,其中4个preview buffer,1个raw buffer,1个JPEG buffer。在视频电话中,只用了preview buffer。

        类QualcommCameraHardware在函数initPreview()中对preview buffer做了初始化。

java代码:
  1. bool QualcommCameraHardware::initPreview(){}
复制代码

其中有code为:

java代码:
  1. mPreviewHeap = new PreviewPmemPool(kRawFrameHeaderSize +
  2. mPreviewWidth * mPreviewHeight * 2, //worst
  3. kPreviewBufferCount,
  4. mPreviewFrameSize,
  5. kRawFrameHeaderSize,“preview”);

  6. QualcommCameraHardware::PreviewPmemPool::PreviewPmemPool(
  7. int buffer_size, int num_buffers,
  8. int frame_size,
  9. int frame_offset,const char *name) :

  10. QualcommCameraHardware::PmemPool("/dev/pmem_adsp",
  11. buffer_size,
  12. num_buffers,
  13. frame_size,
  14. frame_offset,
  15. name)

  16. {
  17. LOGV("constructing PreviewPmemPool");


  18. if (initialized()) {
  19. LINK_camera_assoc_pmem(QDSP_MODULE_VFETASK,
  20. mFd,
  21. mHeap->base(),
  22. mAlignedSize,
  23. 0); // external
  24. }
  25. }
复制代码

       QualcommCameraHardwares使用PMEM驱动对preview buffer进行了分配。Android PMEM是其专用的驱动,称为物理内存驱动。在HAL层分配完preview buffer后,通过函数LINK_camera_assoc_pmem将分配信息传递给底层库。底层库中有对这4个preview buffer的管理机制。

       视频电话中,当camera捕获一帧数据后,存储该数据的buffer会被同时用于preview和record。只用当客户端调用了函数releaseRecordingFrame()之后才能将对应的buffer释放掉,用于其它帧使用。


  三、视频录制调用
       客户端类AndroidCameraInput启动record,通过下面的调用:

java代码:

  1. mCamera->startRecording();
复制代码

其会调到函数:

java代码:
  1. status_t CameraService::Client::startRecording(){}
复制代码

java代码:
  1. //此处为mHardware设置了message CAMERA_MSG_VIDEO_FRAME,并调用函数 startCameraMode()。其定义为:
  2. status_t CameraService::Client::startCameraMode(camera_mode mode){}


  3. //参数camera_mode为CAMERA_RECORDING_MODE,于是调了startRecordingMode。其定义为:
  4. status_t CameraService::Client::startRecordingMode(){}


  5. //其中会先启动preview,如果它没有启动的话,调用了startPreviewMode,即:
  6. status_t CameraService::Client::startPreviewMode(){}


  7. //这里会处理preview的显示介质,如果使用Overlay显示,会设置相应的Overlay,同时调用mHardware->startPreview()以启动preview;否则先调用mHardware->startPreview()启动preview,然后设置buffer:调用函数registerPreviewBuffers(),其定义为:


  8. status_t CameraService::Client::registerPreviewBuffers(){}


  9. //这里会调用mHardware->getPreviewHeap(),从HAL层获得preview的buffer,将其设置给Surface去显示preview的结果。

  10. //类QualcommCameraHardware对startPreview的定义如下:


  11. status_t QualcommCameraHardware::startPreview(preview_callback pcb, void *puser){}


  12. 它调用了startPreviewInternal(),其定义为:
  13. //status_t QualcommCameraHardware:startPreviewInternal(preview_callback pcb, void (puser,

  14. recording_callback rcb, void *ruser){}


  15. //函数里调用了setCallbacks(pcb, puser, rcb, ruser),更新了preview和record的回调函数。另外调用函数LINK_camera_start_preview(camera_cb, this),向driver层传递函数camera_cb。其定义为:


  16. void QualcommCameraHardware::camera_cb(camera_cb_type cb, const void *client_data, camera_func_type func, int32_t parm4){}


  17. //函数里,当mCameraState为QCS_PREVIEW_IN_PROGRESS时,preview成功,同时调用函数

  18. receivePreviewFrame,其定义为:
  19. void QualcommCameraHardware::receivePreviewFrame(camera_frame_type *frame){}
复制代码

       它调用了回调函数mPreviewCallback和mRecordingCallback,这就回调了函数postDataTimstamp(),告诉客户端一帧数据已经获取成功,其可以开始编码了。

       前面已经讲过,当客户端对该帧数据的处理结束后,会告诉底层库释放该帧所占用的buffer空间,以备其他帧使用。

       如此,preview和record同时进行,即可实现视频电话功能。

       四、Preview数据的显示

       Preview数据可以通过Overlay和Surface两种介质去显示,下面分别作以介绍。
       1、 使用Overlay显示
       如果要使用Overlay,底层硬件必须支持Overlay。在CameraService::Client的构造函数中,有相应的判断。

java代码:
  1. CameraService::Client::Client(const sp<CameraService>& cameraService,
  2. const sp<ICameraClient>& cameraClient, pid_t clientPid){}
复制代码

       mUseOverlay = mHardware->useOverlay();如果返回值为true,则表示硬件支持Overlay;否则只能使用Surface显示。
       status_t CameraService::Client::startPreviewMode(){}中,判断如果Overlay可用,就会调用函数setOverlay(),其定义为:

java代码:
  1. status_t CameraService::Client::setOverlay(){}
复制代码

       其中会通过mSurface->createOverlay()创建Overlay,然后通过函数mHardware->setOverlay(new Overlay(mOverlayRef));将其设置给mHardware。

        Android系统中提供了Overlay的接口,其具体实现需要自己做。在TI的实例中,在创建Overlay时,它就为Overlay申请了8个buffer。然后在HAL层,通过Overlay的接口:void* getBufferAddress(overlay_buffer_t buffer),获得Overlay的buffer地址。将该buffer传递给硬件,camera硬件将捕获的数据填充到该buffer中。然后通过Overlay的接口:status_t queueBuffer(overlay_buffer_t buffer),将该buffer加入到Overlay的队列中。最后调用Overlay的接口:status_t dequeueBuffer(overlay_buffer_t* buffer),将那个buffer从队列中取出,此时即Overlay已经对其显示过了。dequeueBuffer()一般应该是一个阻塞函数,当显示完成后这个函数才返回。

       当然,Overlay的buffer也可以在HAL层申请,然后通过queueBuffer(),将buffer加入到Overlay的队列中。

       2、 使用Surface显示
       在CameraService的函数:status_t CameraService::Client::startPreviewMode()中,如果使用Surface,会调用函数registerPreviewBuffers()向Surface注册buffers。registerPreviewBuffers()的定义为:

java代码:
  1. status_t CameraService::Client::registerPreviewBuffers(){}
复制代码

其中有:

java代码:
  1. ISurface::BufferHeap buffers(w, h, w, h,PIXEL_FORMAT_YCbCr_420_SP,
  2. transform,
  3. 0,
  4. mHardware->getPreviewHeap());
  5. status_t ret = mSurface->registerBuffers(buffers);
复制代码

        其将mHardware的preview heap传递给了Surface。
        当Camera的底层库中获取到了preview数据,它会回调函数:

java代码:
  1. void QualcommCameraHardware::camera_cb(camera_cb_type cb,
  2. const void *client_data,
  3. camera_func_type func,
  4. int32_t parm4){}
复制代码

        其中,在preview模式下,会调用函数:

java代码:
  1. void QualcommCameraHardware::receivePreviewFrame(camera_frame_type *frame){}
复制代码

它有代码如下:

java代码:
  1. mPreviewCallback(mPreviewHeap->mBuffers[offset],
  2. mPreviewCallbackCookie);
复制代码

       这是回调了preview的回调函数。其定义为:

java代码:
  1. void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr
复制代码

       其中,preview模式下,会调用函数:

java代码:
  1. void CameraService::Client::handlePreviewData(const sp<IMemory>& mem){}
复制代码

       其通过mSurface->postBuffer(offset),将存储当前捕获数据的buffer送给Surface去显示。
       函数CameraService::Client::stopPreview()中会调用函数:
       mSurface->unregisterBuffers(),以释放原来注册的buffers。
       当回调了preview的回调函数mPreviewCallback之后,如果mRecordingCallback为空,则直接调用LINK_camera_release_frame(),释放当前图像数据的buffer;否则调用回调函数mRecordingCallback,进入录制的流程。