在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的共享库中,代码并没有公开。