在初次接触 android 的时候就注意到通过模拟器,相机预览的时候只能显示黑白电影。这些天总算有点空闲时间了,决定研究下这个。 关于 android camera 的构造不再说了,模拟器上使用用的是一个虚拟的 Camera-----FakeCamera 这个大家应该都知道。
首先初略的了解一下 camera preview 图像显示的原理。 Camera application 初始化的时候会创建一个 surfaceview ,从 camera device 接收到的数据就可以通过它显示在屏幕上。内部处理的流程是很复杂的,下面我只给出一个从初始化到绘图函数调用的流程。
Surface 初始化:
Android_view_surface:: Surface_init--> SurfaceComposerClient::CreateSurface-->
SurfaceFlinger:: createSurface--> Android_view_surface:: setSurface
Camera 数据缓冲区 Heap 初始化:
CameraHardwareStub::initHeapLocked--> new MemoryBase--> new FakeCamera
Heap 缓冲区注册到 surface:
Android_hardware_camera:: android_hardware_Camera_setPreviewDisplay-->
Camera:: setPreviewDisplay--> CameraService::Client::setPreviewDisplay-->
CameraService::Client::registerPreviewBuffers--> LayerBuffer::registerBuffers
FakeCamera 原始数据的传递与绘图:
CameraHardwareStub::previewThread--> CameraService::Client::previewCallback-->
CameraService::Client::postPreviewFrame--> ISurface:: postBuffer-->
LayerBuffer::postBuffer--> LayerBuffer::BufferSource::postBuffer-->
LayerBase::invalidate--> SurfaceFlinger::signalEvent--> SurfaceFlinger::threadLoop-->
SurfaceFlinger::handleRepaint--> LayerBase::draw-->
LayerBuffer::BufferSource::onDraw--> LayerBase::drawWithOpenGL
FakeCamera 得到数据为 Yuv422, 原以为是库里某个地方绘图的时候出问题,但 LayerBuffer::OnDraw 往下走很复杂的:
t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) {
// just show the Y plane of YUV buffers。 不明白为什么google这么做。。。。。
但要解决预览为黑白电影的问题应该就比较简单了。只要我们将 YUV 数据转换为 RGB 丢给 cameraService 就可以了。用以下的代码替代 ccrgb16toyuv_wo_colorkey 。
int32_t ccrgb16toyuv_wo_colorkey (uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[])
{
int32_t width_dst = param[0];
int32_t height_dst = param[1];
memcpy(yuv422, rgb16, width_dst*height_dst*2);
}
CameraService 中改动:
status_t CameraService::Client::registerPreviewBuffers ()
{
。。。。。。。。。。。。。。。。。。。。。。。
ISurface::BufferHeap buffers(w, h, w, h,
PIXEL_FORMAT_RGB_565 ,
//PIXEL_FORMAT_YCbCr_420_SP,
transform,
0,
mHardware->getPreviewHeap());
status_t ret = mSurface->registerBuffers(buffers);
if (ret != NO_ERROR) {
LOGE("registerBuffers failed with status %d", ret);
}
return ret;
}
用 PIXEL_FORMAT_RGB_565 替换 PIXEL_FORMAT_YCbCr_420_SP 。这样就可以了。不妨动手试一试吧,看看是不是预览视频变为彩色的了。
对于实际的 camera device 出来的数据如果是 yuv 的话,也可以通过算法将 yuv 转换为 RGB ,这样做并不会导致转换效率降低,因为即使将 yuv 得数据丢到 surfaceflinger 中,最终还是会转换为 BMp 。