reference http://www.cnblogs.com/FlexChen/archive/2011/09/10/2169380.html
device/wolf/smdk6410/BoardConfig.mk
USE_CAMERA_STUB := false
hardware建立Camera HAL目录
hardware/wolf/libcamera
CannedJpeg.h
Ov965xCamera.cpp
Ov965xCamera.h
S3C6410CameraHardware.cpp
S3C6410CameraHareware.h
USBCamera.cpp
USBCamera.h
编写Android.mk文件
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
USBCamera.cpp \
Ov965xCamera.cpp \
S3C6410CameraHardware.cpp
LOCAL_SHARED_LIBRARIES:= \
libui \
libutils \
libbinder \
liblog \
libcamera_client
LOCAL_MODULE:= libcamera
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += \
frameworks/base/libs
include $(BUILD_SHARED_LIBRARY)
Build for the simulator or the device?
1. Device
2. Simulator
1
1
Which product would you like? [smdk6410]
回车
eng
开始编译摄像头模块,动态连接库文件*.so存放在
Install: out/target/product/smdk6410/system/lib/libcamera.so
用adb push把libcamera.so推到目标机/system/lib/中
cd D:\software\android-sdk-windows\tools
d:
adb push libcamera.so /system/lib/
export ANDROID_LOG_TAGS="CameraHardware:D CameraHardware:E *:S"
logcat -d
拍照
E/AudioService( 1902): Media server died.
解决:在init.c中添加
chmod 0777 /dev/video0
Camera系统分成以下几个部分
1)摄像头驱动程序:通常基于linux的video for linux视频驱动框架
2)Camera硬件抽象层
接口的代码路径:frameworks/base/include/camera/
主要文件为CameraHardwareInterfacd.h,需要各个系统根据自己的情况实现
3)Camera服务部分
代码路径:frameworks/base/include/camera
Camera服务是Android系统中一个单独部分,通过调用Camera硬件抽象层来实现
4)Camera的本地框架代码
代码路径:frameworks/base/include/camera
5)Camera的JNI代码
代码路径:frameworks/base/core/jni/android_hardware_Camera.cpp
提供给Java类的本地支持,也包含了反向调用java传递信息和数据功能
6)Camera系统的Java类
代码路径:frameworks/base/core/java/android/hardware/Camera.java
Camera对Java层次的类为android.hardware.Camera,自Java应用程序提供接口
可用于在Java应用程序层构建照相机和扫描类的程序
-----------------------------------------------------------------------
移植的内容
在Android系统中,照相机系统的标准化部分是硬件抽象层的接口,因此
针对特定平台Camera系统的移植包括Camera驱动程序和Camera硬件抽象层
在Linux系统中,Camera的驱动程序都是用Linux标准的Video for Linux 2(V4L2)
驱动程序,从内核空间到用户空间,主要的数据流和控制类的均由V4L2驱动程序
的框架来定义,在Android系统的实现中,一般也都使用标准的V4L2驱动程序作为
照相机部分的驱动程序
Camera硬件抽象层主要实现取景器,视频录制,拍摄相片三个方面功能。V4L2驱动
程序一般只提供的Video数据的获得,而如何实现视频预览,如何向上层发送数据等
功用,这些都是Camera的硬件抽象层需要负责的方面了。
frameworks/base/include/camera/
主要文件为CameraHardwareInterface.h,需要各个系统根据自己的情况实现
frameworks/base/services/camera/libcameraservice
Camera服务是Andriod系统中一个单独的部分,通过调用camera硬件抽象层
来实现。
frameworks/base/libs/camera/
是位于V4L2驱动程序和CameraService之间的部分,这是
一个C++的接口类,需要具体的实现者继承这个类,并实现其中的各个纯虚函数。
纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时
再去具体地给出定义。
硬件抽象层的主要的头文件为CameraHardwareInterface.h,定义了C++的接口类
需要根据系统的情况继承实现。
camera.h 这是Camera系统本地对上层的接口
CameraParameters.h定义Camera系统的参数,在本地代码的各个层次中使用。
Camera硬件抽象层的实现通常需要生成动态库libcamera.so
typedef void (*notify_callback)(int32_t msgType, //通知回调
int32_t ext1,
int32_t ext2,
void* user);
typedef void (*data_callback)(int32_t msgType, //数据回调
const sp<IMemory>& dataPtr,
void* user);
typedef void (*data_callback_timestamp)(nsecs_t timestamp,//带有时间的数据回调
int32_t msgType,
const sp<IMemory>& dataPtr,
void* user);
消息类型mstType 数据IMemory
回调函数由setCallbacks(),enableMsgType()函数统一处理。
setCallbacks()可以设置三个类型的回调函数指针
/** Set the notification and data callbacks */
virtual void setCallbacks(notify_callback notify_cb,
data_callback data_cb,
data_callback_timestamp data_cb_timestamp,
void* user) = 0;
A 取景器preview(使用YUV原始数据格式,发送到视频输出设备)
S3C6410CameraHardware.cpp中
1)在初始化过程中,建立preview的内存队列 void CameraHardware::initHeapLocked() { // Create raw heap. int picture_width, picture_height; mParameters.getPictureSize(&picture_width, &picture_height); mRawHeap = new MemoryHeapBase(picture_width * picture_height * 2); int preview_width, preview_height; mParameters.getPreviewSize(&preview_width, &preview_height); int how_big = preview_width * preview_height*2; } void CameraHardware::initDefaultParameters() { CameraParameters p; p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240"); p.setPreviewSize(320, 240);//大小为320*240 p.setPreviewFrameRate(15);//帧率为15bps p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_RGB565);//preview 的格式为RGB565 p.set(CameraParameters::KEY_ROTATION, 0);//90 p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240"); p.setPictureSize(320, 240); p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);// SET OUTPUT PIC TO BMP FORMAT if (setParameters(p) != NO_ERROR) { LOGE("Failed to set default parameters?!"); } } 2) 在startPreview()的实现中,建立preview线程 status_t CameraHardware::startPreview() { Mutex::Autolock lock(mLock); if (mPreviewThread != 0) { return INVALID_OPERATION; } mPreviewThread = new PreviewThread(this); return NO_ERROR; } 3)在preview线程的循环中,等待视频数据的到达 int CameraHardware::previewThread() { if(mCamType == CAMTYPE_CMOS) Ov965xCamera->getNextFrameAsRgb565((uint16_t *)frame); 4)视频帧到达后使用preview回调的机制CAMERA_MSG_PREVIEW_FRAME,将视频 帧向上层传送 // Notify the client of a new frame. if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie); B拍摄照片(可以使用原始数据或者压缩图像数据) 1) takePicture()函数表示开始拍摄,可以建立单独的线程来处理 status_t CameraHardware::takePicture() { stopPreview(); if (createThread(beginPictureThread, this) == false) return UNKNOWN_ERROR; return NO_ERROR; } int CameraHardware::beginPictureThread(void *cookie) { CameraHardware *c = (CameraHardware *)cookie; return c->pictureThread(); } int CameraHardware::pictureThread() { if (mMsgEnabled & CAMERA_MSG_SHUTTER) mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie); if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) { //FIXME: use a canned YUV image! // In the meantime just make another fake camera picture. int w, h; mParameters.getPictureSize(&w, &h); sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 2); Ov965xCamera cam(w, h); //cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base()); if(mCamType == CAMTYPE_CMOS) { cam.getNextFrameAsRgb565((uint16_t *)mRawHeap->base()); } else if (mCamType == CAMTYPE_USB) { //USBCamera cam1(w, h); LOGE("%s, Taking picure using USB CAM", LOG_TAG); //cam.getNextFrameAsRgb565((uint16_t *)mRawHeap->base()); //cam1.getNextFrameAsYuv420((uint16_t *)mRawHeap->base()); } mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie); } // ToDo: Release MemoryHeapBase // ToDo: Convert BMP to JPEG // Todo: Higher Resultion Support if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { LOGE("%s, COMPRESSED IMAGE", LOG_TAG); int w, h; unsigned int DATA_OFFSET = 54; uint16_t WIDTH = w; uint16_t HEIGHT = h; mParameters.getPictureSize(&w, &h); Ov965xCamera* Ov965xCamera = mOv965xCamera; sp<MemoryHeapBase> heap = new MemoryHeapBase(DATA_OFFSET+w * h* 2); sp<MemoryBase> mem = new MemoryBase(heap, 0, DATA_OFFSET+w * h* 2); //16 bits for one pixel uint8_t header[54] = { 0x42, // identity : B 0x4d, // identity : M 0, 0, 0, 0, // file size 0, 0, // reserved1 0, 0, // reserved2 54, 0, 0, 0, // RGB data offset 40, 0, 0, 0, // struct BITMAPINFOHEADER size 0, 0, 0, 0, // bmp height 0, 0, 0, 0, // bmp width 1, 0, // planes 16, 0, // bit per pixel 0, 0, 0, 0, // compression 0, 0, 0, 0, // data size 0, 0, 0, 0, // h resolution 0, 0, 0, 0, // v resolution 0, 0, 0, 0, // used colors 0, 0, 0, 0 // important colors }; // file size offset 54 uint16_t file_size = WIDTH * HEIGHT * 2 + DATA_OFFSET; header[2] = (uint8_t)(file_size & 0x000000ff); header[3] = (file_size >> 8) & 0x000000ff; header[4] = (file_size >> 16) & 0x000000ff; header[5] = (file_size >> 24) & 0x000000ff; // height header[18] = HEIGHT & 0x000000ff; header[19] = (HEIGHT >> 8) & 0x000000ff; header[20] = (HEIGHT >> 16) & 0x000000ff; header[21] = (HEIGHT >> 24) & 0x000000ff; // width header[22] = WIDTH & 0x000000ff; header[23] = (WIDTH >> 8) & 0x000000ff; header[24] = (WIDTH >> 16) & 0x000000ff; header[25] = (WIDTH >> 24) & 0x000000ff; LOGE("%s, Header Ready", LOG_TAG); unsigned int i; for(i=0;i<DATA_OFFSET;i++){ *((uint8_t*)heap->base()+i)=header[i]; } Ov965xCamera->getNextFrameAsRgb565((uint16_t*)heap->base()+DATA_OFFSET/2); uint16_t *heap_base = (uint16_t*)heap->base(); uint16_t pixel_data; uint8_t tail_data; for(i=DATA_OFFSET/2;i<DATA_OFFSET/2+WIDTH*HEIGHT;i++){ pixel_data = *(heap_base+i); tail_data = (uint8_t)(pixel_data & 0x001f); pixel_data = (pixel_data & 0xffc0)>>1 | tail_data; *(heap_base+i)=pixel_data; } mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie); heap=NULL; LOGE("%s, IMAGE SAVED!", LOG_TAG); } return NO_ERROR; }
2)使用回调机制传送数据:如果得原始格式(通常是YUV的格式,如yuv422sp,这里是RGB565)
的数据,使用CAMERA_MSG_RAW_IMAGE将数据传送;如果得到压缩图像(通常JPEG格式,这里是BMP)
使用CAMERA_MSG_COMPRESSED_IMAGE将数据传送
C 视频录制(将数据传送给视频编码器程序)
1)在startRecording()的实现中,开始录制的准备,录制视频可以使用自己的线程,也可以使用
preview线程
2)当一个视频帧到来的时候,通过录制回调机制(使用CAMERA_MSG_VIDEO_FRAME)将视频帧向上
发送
3)releaseRecordingFrame()被调用后,表示上层通知Camera硬件抽象层,这一帧的内存已经用完
可以进行下一次的处理。
1. 修改BoardConfig.mk USE_CAMERA_STUB := false 2. hardware下建立Camera HAL目录 /hardware/ego/libcamera 复制几个文件 cp ../../../frameworks/base/services/camera/libcameraservice/CameraHardwareStub.cpp S3C6410CameraHardware.cpp cp ../../../frameworks/base/services/camera/rks/base/services/camera/libcameraservice/CameraHardwareStub.h S3C6410CameraHardware.h cp ../../../frameworks/base/services/camera/libcameraservice/FakeCamera.cpp Ov965xCamera.cpp cp ../../../frameworks/base/services/camera/libcameraservice/FakeCamera.h Ov965xCamera.h 3. 编译Android.mk文件 LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ S3C6410CameraHardware.cpp \ Ov965xCamera.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ libutils \ libbinder \ liblog \ libcamera_client LOCAL_MODULE:= libcamera LOCAL_MODULE_TAGS:= eng LOCAL_C_INCLUDES += \ frameworks/base/libs include $(BUILD_SHARED_LIBRARY) 根据这个脚本可编译出libcamera.so 4. 修改Ov965xCamera.cpp,该文件可以按照V4L2流程来写 1)构造函数中进行初始化 Ov965xCamera::Ov965xCamera(int width, int height) : mTmpRgb16Buffer(0) { struct v4l2_format fmt; int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_fd = open("/dev/video0", O_RDWR | O_SYNC); LOGE("open /dev/video0 fd is %d", v4l2_fd); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt); ioctl(v4l2_fd, VIDIOC_STREAMON, &type); setSize(width, height); } -------->调用setSize() 5. 修改S3C6410CameraHardware.cpp //参数设置 void CameraHardware::initDefaultParameters() { CameraParameters p; //设置preview大小 p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240"); p.setPreviewSize(320, 240); //设置帧率 p.setPreviewFrameRate(15); p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_RGB565); p.set(CameraParameters::KEY_ROTATION, 0); p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240"); p.setPictureSize(320, 240); p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG); if (setParameters(p) != NO_ERROR) { LOGE("Failed to set default parameters!"); } } 调用----->setParameters(p) status_t CameraHardware::setParameters(const CameraParameters& params) { Mutex::Autolock lock(mLock); if (strcmp(params.getPreviewFormat(), CameraParameters::PIXEL_FORMAT_RGB565) != 0) { LOGE("Only rgb565 preview is supported"); return -1; } if (strcmp(params.getPictureFormat(), CameraParameters::PIXEL_FORMAT_JPEG) != 0) { LOGE("Only jpeg still pictures are supported"); return -1; } int w, h; params.getPictureSize(&w, &h); mParameters = params; initHeapLocked(); return NO_ERROR; } 调用----->initHeapLocked(); void CameraHardware::initHeapLocked() { int picture_width, picture_height; mParameters.getPictureSize(&picture_width, &picture_height); //建立内存堆 mRawHeap = new MemoryHeapBase(picture_width * picture_height * 2); //从参数中获取信息 int preview_width, preview_height; mParameters.getPreviewSize(&preview_width, &preview_height); LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height); int how_big = preview_width * preview_height * 2; mPreviewFrameSize = how_big; //制作新mmap'ed的堆,可以跨进程共享。 mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount); //建立内存队列kBufferCount 4个 for (int i = 0; i < kBufferCount; i++) { mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize); } if (mOv965xCamera != 0) { LOGD("delete mOv965xCamera"); delete mOv965xCamera; mOv965xCamera = 0; } else if(mCamType == CAMTYPE_CMOS) { LOGD("new mOv965xCamera"); mOv965xCamera = new Ov965xCamera(preview_width, preview_height); } } 在这个过程中,建立了两块内存(MemoryHeapBase): mRawHeap 表示一个拍照照片的内存 mPreviewHeap表示取景preview的内存,由于取景器preview的内容是一个队列,因此在 mPreviewHeap中建立kBufferCount(为4个)MemoryBase new Ov965xCamera作为摄像头输入数据的来源 CameraService.cpp调用----------->startPreview() status_t CameraHardware::startPreview() { Mutex::Autolock lock(mLock); if (mPreviewThread != 0) { return INVALID_OPERATION; } //建立视频preview的线程 mPreviewThread = new PreviewThread(this); return NO_ERROR; } 调用----------->PreviewThread() 在PreviewThread线程中通过调用preview的回调机制,实现preview 数据的数据传递给上层就是CameraService int CameraHardware::previewThread() { mLock.lock(); int previewFrameRate = mParameters.getPreviewFrameRate(); //获取内存偏移 ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize; sp<MemoryHeapBase> heap = mPreviewHeap; //获得Ov965xCamera类 Ov965xCamera* Ov965xCamera = mOv965xCamera; 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; //获取一个帧的数据,放入frame Ov965xCamera->getNextFrameAsRgb565((uint16_t *)frame); //通知客户端的一个新的一帧,调用Callback向上层发送数据 if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie); //推进缓冲区指针 mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount; usleep(delay); } return NO_ERROR; } 硬件抽象层实现的取景器数据来自 Ov965xCamera,可以得到数据,并使用 回调函数CAMERA_MSG_PREVIEW_FRAME宏为参数将数据送向上层,这里使用 mDataCb是上层CameraService,通过setCallbacks()函数设置。 其中mPreviewHeap存储着n个帧的缓冲,这块区域被分割为n个mBuffers。 buffer为当前帧的引用,通过mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie) 就可以将buffer输出到屏幕。那每一个帧是怎么存到mPreviewHeap上的呢? 关键的一句就是Ov965xCamera->getNextFrameAsRgb565((uint16_t *)frame) , 通过看它的实现可以知道(在Ov96xCamera.app中),一个帧的数据以16位的格式写入frame中, 这里的frame即是对mPreviewHeap上某个mBuffer的引用 调用--------->getNextFrameAsRgb565((uint16_t *)frame) void Ov965xCamera::getNextFrameAsRgb565(uint16_t *buffer) { int ret; unsigned long len; if (v4l2_fd != -1) { len = mWidth * mHeight*2; ret = read(v4l2_fd, buffer, len); } } -------------------------------------------------------- setCallbacks()函数的实现 void CameraHardware::setCallbacks(notify_callback notify_cb, data_callback data_cb, data_callback_timestamp data_cb_timestamp, void* user) { Mutex::Autolock lock(mLock); mNotifyCb = notify_cb;//通知回调 mDataCb = data_cb;//数据回调 mDataCbTimestamp = data_cb_timestamp; mCallbackCookie = user; } --------------->takePicture()函数在拍摄照片时被调用,它也保存了回调函数的指针,并建立了拍摄照片 的线程 status_t CameraHardware::takePicture() { stopPreview(); if (createThread(beginPictureThread, this) == false) return UNKNOWN_ERROR; return NO_ERROR; } 调用----------------->beginPictureThread() int CameraHardware::beginPictureThread(void *cookie) { CameraHardware *c = (CameraHardware *)cookie; return c->pictureThread(); } 调用----------------->pictureThread() int CameraHardware::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 * h * 2); Ov965xCamera cam(w, h); if (mCamType == CAMTYPE_CMOS) { //获得指针 cam.getNextFrameAsRgb565((uint16_t *)mRawHeap->base()); LOGD("----------Taking picture using OV9650 CAM------"); } //传送数据 mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie); } //传送JPEG数据的处理 if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { LOGE("%s, --------COMPRESSD IMAGE------", LOG_TAG); //step1 首先申请BMP暂存区: int w, h; unsigned int DATA_OFFSET = 54; uint16_t WIDTH = w; uint16_t HEIGHT = h; mParameters.getPictureSize(&w, &h); Ov965xCamera* Ov965xCamera = mOv965xCamera; sp<MemoryHeapBase> heap = new MemoryHeapBase(DATA_OFFSET + w * h * 2); //一个像素6位 sp<MemoryBase> mem = new MemoryBase(heap, 0, DATA_OFFSET + w * h * 2); //写BMP文件头 uint8_t header[54] = { 0x42, 0x4d,//BM 0, 0, 0, 0, //file size 0, 0, //reserved1 0, 0, //reserved2 54, 0, 0, 0, //RGB data offset 40, 0, 0, 0, //struct BITMAPINFOHEADER size 0, 0, 0, 0,//bmp height 0, 0, 0, 0, //bmp width 1, 0, //planes 16, 0, //一个像素16位 0, 0, 0, 0,//压缩 0, 0, 0, 0,//data size 0, 0, 0, 0,//h resolution 0, 0, 0, 0,//v resolution 0, 0, 0, 0,//used colors 0, 0, 0, 0//important colors }; //file size offset 54 uint16_t file_size = WIDTH * HEIGHT * 2 + DATA_OFFSET; header[2] = (uint8_t)(file_size & 0x000000ff); header[3] = (file_size >> 8) & 0x000000ff; header[4] = (file_size >> 16) & 0x000000ff; header[5] = (file_size >> 24) & 0x000000ff; //height header[18] = HEIGHT & 0x000000ff; header[19] = (HEIGHT >> 8) & 0x000000ff; header[20] = (HEIGHT >> 16) & 0x000000ff; header[21] = (HEIGHT >> 24) & 0x000000ff; //width header[22] = WIDTH & 0x000000ff; header[23] = (WIDTH >> 8 ) & 0x000000ff; header[24] = (WIDTH >> 16) & 0x000000ff; header[25] = (WIDTH >> 24) & 0x000000ff; //step2 获取当前帧,进行RGB565到RGB555的转换,将转换后的 //数据放入MemoryHeap中 unsigned int i; for (i=0; i< DATA_OFFSET; i++){ *((uint8_t *)heap->base() + i) = header[i]; } Ov965xCamera->getNextFrameAsRgb565((uint16_t *)heap->base() + DATA_OFFSET/2); uint16_t *heap_base = (uint16_t *)heap->base(); uint16_t pixel_data; uint8_t tail_data; for(i=DATA_OFFSET/2; i<DATA_OFFSET/2+WIDTH*HEIGHT; i++) { pixel_data = *(heap_base + i); tail_data = (uint8_t)(pixel_data & 0x001f); pixel_data = (pixel_data & 0xffc0)>>1 | tail_data; *(heap_base + i) = pixel_data; } //step3 调用callback,将数据存储到设备,并释放MemoryHeap mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie); heap = NULL; LOGE("%s, -----IMAGE SAVED-----", LOG_TAG); } return NO_ERROR; } ----------------------------------------------------------- ---------------->autoFocus() status_t CameraHardware::autoFocus() { Mutex::Autolock lock(mLock); if (createThread(beginAutoFocusThread, this) == false) return UNKNOWN_ERROR; return NO_ERROR; } 调用-------------->beginAutoFocusThread() int CameraHardware::beginAutoFocusThread(void *cookie) { CameraHardware *c = (CameraHardware *)cookie; return c->autoFocusThread(); } 调用-------------->autoFocusThread() int CameraHardware::autoFocusThread() { if (mMsgEnabled & CAMERA_MSG_FOCUS) mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie); return NO_ERROR; } ---------------------------------------------------------------- 上面是实现的RGB565写成BMP格式的