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