您的位置:华清远见教育科技集团 >> Android资料 >> Android Camera的HAL接口  
 
Android Camera的HAL接口
分享到:

在Linux系统中,硬件平台驱动,以及其他需要商业保护的部分通常都会通过HAL来封装,在Aurora中也如此。

目前产业界的Camera传感器主要有两种类型:电荷耦合设备(CCD,Charge Couple Device)和互补金属氧化物半导体(CMOS,Complementary Metal Oxide Semiconductor)。其中CCD主要应用在高档的DC、DV和高档移动终端上,图像质量较好,但驱动模组比较复杂;而CMOS则门槛较低,工艺更加成熟,成本较低,外围电路也比较简单,很多厂商已将驱动和信号处理的图像信号处理器(ISP,Image Signal Processor)集成到驱动模组中。因此CMOS传感器在移动终端中应用广泛。如图5-4所示为Camera进行图像编码的底层过程。


图5-4 图像编码过程

与Camera传感器通信,需涉及I2C驱动;进行文件保存,需涉及GPIO驱动和PMIC驱动;利用aDSP解码,就需要用到VFE aDSP驱动。

在这些平台驱动之上,在Android的较新版本中,通常会封装一层HAL,可以为开发者提供更加抽象的接口,同时也可以保护硬件厂商的利益。

在Android中,为了实现Camera的HAL封装,必须继承CameraHardwareInterface.h定义的CameraHardwareInterface接口。

在Android原始代码中,提供了CameraHardwareStub.cpp和QualcommCamera Hardware.cpp两种实现,CameraHardwareStub提供了基于模拟器的FakeCamera实现。QualcommCameraHardware实现了对真实物理设备的HAL封装。下面首先介绍CameraHardwareStub的实现。

在CameraHardwareStub中,为了保证系统的流畅运行,在发起预览时,会将Camera预览放置在一个单独的预览线程CameraHardwareStub::previewThread()中,并在线程的事件循环中周期性调用previewThread,从底层提取数据。CameraHardwareStub::previewThread()的实现如下:

代码5-4 预览线程的事件循环处理

int CameraHardwareStub::previewThread()
    {
      mLock.lock();
      int previewFrameRate=mParameters.getPreviewFrameRate(); //帧速率
      ssize_t offset=mCurrentPreviewFrame * mPreviewFrameSize; //偏移量
      sp< MemoryHeapBase> heap=mPreviewHeap;
      FakeCamera* fakeCamera=mFakeCamera;
      sp< MemoryBase> buffer=mBuffers[mCurrentPreviewFrame]; //数据缓冲
      mLock.unlock();
      if (buffer != 0) {
      //计算帧延迟
      int delay=(int)(1000000.0f / float(previewFrameRate));
      void *base=heap->base();
      //用假数据填充当前帧
      uint8_t *frame=((uint8_t *)base) + offset;
      fakeCamera->getNextFrameAsYuv422(frame); //获取数据缓冲
      //通知客户端有新帧到来
      if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)
      mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);
      mCurrentPreviewFrame=(mCurrentPreviewFrame + 1) % kBufferCount;
      usleep(delay);
      }
      return NO_ERROR;
    }

在CameraHardwareStub中,目前共有4个缓冲用于Camera数据的交替存放,这些数据被放置在一个名为mPreviewHeap的预览内存堆中。具体实现如下:

代码5-5 Camera堆的初始化

void CameraHardwareStub::initHeapLocked()
    {
      int picture_width, picture_height;
      mParameters.getPictureSize(&picture_width, &picture_height);//拍摄大小
      //MMAP映射,存在拍摄时的原始数据
      mRawHeap=new MemoryHeapBase(picture_width * 2 * picture_height);
      int preview_width, preview_height;
      mParameters.getPreviewSize(&preview_width, &preview_height);//预览大小
      LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
      //强制设置为yuv422
      int how_big=preview_width * preview_height * 2;
      if (how_big==mPreviewFrameSize)
      return;
      mPreviewFrameSize=how_big;
      //构建一个新MMAP堆用于进程间共享
      mPreviewHeap=new MemoryHeapBase(mPreviewFrameSize * kBufferCount);
      //为每帧都构建一个IMemory
      for (int i=0; i < kBufferCount; i++) {
      mBuffers[i]=new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize);
      }
      delete mFakeCamera;
      mFakeCamera=new FakeCamera(preview_width, preview_height);
    }

模拟器默认配置下,预览分辨率为176×144像素,帧速率为15fps,预览数据格式为YUV422 SP(仅支持),照片编码格式为JPEG(仅支持)。

出于某种需求,需要将元数据格式YUV422SP转换为RGB565,yuv420sp2rgb.c文件给出的方法如下:

代码5-6 YUV422sp转换为RGB565的实现

static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
    final int frameSize=width * height;
    for (int j=0, yp=0; j< height; j++) {
    int uvp=frameSize + (j>>1) * width, u=0, v=0;
    for (int i=0; i< width; i++, yp++) {
      int y=(0xff & ((int) yuv420sp[yp])) - 16;
      if (y< 0) y=0;
      if ((i & 1)==0) {
      v=(0xff & yuv420sp[uvp++]) - 128;
      u=(0xff & yuv420sp[uvp++]) - 128;
      }
      int y1192=1192 * y;
      int r=(y1192+1634 * v);
      int g=(y1192-833 * v-400*u);
      int b=(y1192+2066 * u);       if (r< 0) r=0; else if (r>262143) r=262143;
      if (g< 0) g=0; else if (g>262143) g=262143;
      if (b< 0) b=0; else if (b>262143) b=262143;
      rgb[yp]=0xff000000 | ((r<<6) & 0xff0000) | ((g>>2) & 0xff00) | ((b>>10) & 0xff);
        }
      }
    }

如果希望在预览时就做这样的转换,编码的转换好放置在一个单独的线程中处理。RGB565向YUV422SP的转换参考FakeCamera.cpp文件。

在进行自动对焦和拍照时,CameraHardwareStub同样会发起一个新的线程进行处理,相关的线程为autoFocusThread、pictureThread。在QualcommCameraHardware中,则没有发起新线程进行处理。

在进行拍照时,为了向上层传递假图片,CameraHardwareStub的处理方法如下:

代码5-7 CameraHardwareStub拍摄照片的过程

int CameraHardwareStub::pictureThread()
    {
        if (mMsgEnabled & CAMERA_MSG_SHUTTER)
        mNotifyCb(CAMERA_MSG_SHUTTER,0,0,mCallbackCookie);
        if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {
        int w,h;
        mParameters.getPictureSize(&w, &h);//获得图片大小
        sp< MemoryBase> mem=new MemoryBase(mRawHeap,0,w * 2 * h);
        FakeCamera cam(w,h);
        cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base());//获取数据缓冲
        mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);
      }
      if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
        sp< MemoryHeapBase> heap=new MemoryHeapBase(kCannedJpegSize);
        sp< MemoryBase> mem=new MemoryBase(heap,0,kCannedJpegSize);
        memcpy(heap->base(),kCannedJpeg, kCannedJpegSize);//填充假图片数据
        mDataCb(CAMERA_MSG_COMPRESSED_IMAGE,mem,mCallbackCookie);
      }
      return NO_ERROR;
    }

当然在真实终端环境下,基于CameraHardwareInterface的实现必须通过和Camera驱动进行通信来获得真实数据。其实现和CameraHardwareStub存在着差异。下面为QualcommCameraHardware公开的JPEG编码过程:

代码5-8 QualcommCameraHardware编码的过程

unsigned char QualcommCameraHardware::native_jpeg_encode (
    void *pDim,
    int pmemThumbnailfd,
    int pmemSnapshotfd,
    unsigned char *thumbnail_buf,
    unsigned char *main_img_buf, //存放拍摄的元数据缓冲
    void *pCrop)
    {
      char jpegFileName[256]={0};
      static int snapshotCntr=0;
      cam_ctrl_dimension_t *dimension=(cam_ctrl_dimension_t *)pDim;
      common_crop_t *cropInfo=(common_crop_t *)pCrop;
      sprintf(jpegFileName, "snapshot_%d.jpg", ++snapshotCntr);//获得文件名
      #ifndef SURF8K
      LOGV("native_jpeg_encode , current jpeg main img quality =%d", mParameters.getJpeg MainimageQuality());
      //设置图像质量
      if (! LINK_jpeg_encoder_setMainImageQuality(mParameters.getJpegMainimageQuality()))
    {
      LOGE("native_jpeg_encode set jpeg main image quality :%d@%s: jpeg_encoder_encode failed.\n",__LINE__, __FILE__);
      return FALSE;
      }
      #endif
      //调用libmmcamera.so中的jpeg_encoder_encode进行解码
      if ( !LINK_jpeg_encoder_encode(jpegFileName, dimension,thumbnail_buf, pmemThumbnailfd,main_img_buf, pmemSnapshotfd, cropInfo))
      {
      LOGV("native_jpeg_encode:%d@%s: jpeg_encoder_encode failed.\n", __LINE__, __FILE__);
      return FALSE;
      }
      return TRUE;
    }

在Qualcomm的实现中,底层的细节被封装在名为libmmcamera.so和libmmcamera_ target.so的共享库中,代码并没有公开。

 更多相关文章

·Android中Camera数据的处理
·Android Camera拍照源码分析
·Android Camera原生服务架构
·Android Camera录像过程分析
·Android 原生层媒体播放过程