参考http://hi.baidu.com/aokikyon/blog/item/ee9339f3e1b23fc60b46e047.html的设计

转载:http://blog.csdn.net/flyingpipi/article/details/5773666

使用zc301 USB摄像头,这个摄像头返回JPEG图形留,camera的preview需要进行jpeg解码(没做),但是可以直接take jpeg照片。

1、修改你的BoardConfig.mk
USE_CAMERA_STUB := false

将stub设置为false,在编译时不会编译android2.1/frameworks/base/camera/libcameraservice中的
CameraHardwareStub.cpp
CameraHardwareStub.h
FakeCamera.cpp
FakeCamera.h
几个文件

2、hardware下建立Camera HAL目录,android2.1/hardware/your board/libcamera
复制以上几个文件
CameraHardwareStub.cpp
CameraHardwareStub.h
FakeCamera.cpp
FakeCamera.h

可以将其重命名
S3C6410CameraHardware.cpp
UsbCamera.cpp

文件中的Fake和Stub同样可以替换

3、编写Android.mk文件
================================================================
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= S3C6410CameraHardware.cpp /
UsbCamera.cpp
LOCAL_C_INCLUDES := /
external/jpeg
LOCAL_SHARED_LIBRARIES:= libutils libbinder libui liblog
LOCAL_STATIC_LIBRARIES:= /
libjpeg
LOCAL_MODULE:= libcamera
include $(BUILD_SHARED_LIBRARY)
=================================================================
其中jpeg库是为了将来解码jpeg使用的,根据这个脚本可编译出libcamera.so

4、修改FakeCamera.cpp->UsbCamera.cpp
目前只实现了基本功能
该文件可以按照V4L2流程来写
网上也有现成的patch,使用mmap方式,可惜是1.6的,没编译过去,待研究

1)构造函数中进行初始化
UsbCamera::UsbCamera(int width, int height)
: mTmpRgb16Buffer(0)
{
fd = open (DEFAULT_DEVICE, O_RDWR /* required */ | O_NONBLOCK, 0);
LOGE("open /dev/video0 fd is %d",fd);
ioctl (fd, VIDIOC_QUERYCAP, &cap);
CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
ioctl (fd, VIDIOC_S_FMT, &fmt);
length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
}

2)获取一帧数据
void UsbCamera::getNextFrameAsYuv422(uint8_t *buffer)
{
LOGE("read to get a pic from camera!");
for (;;)
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO (&fds);
FD_SET (fd, &fds);
/* Timeout. */
tv.tv_sec = 3;
tv.tv_usec = 0;
r = select (fd + 1, &fds, NULL, NULL, &tv);
if (-1 == r) {
LOGE ("select"); }
if (0 == r) {
LOGE ("select timeout/n"); }
if (read (fd, buffer, length));
break;
}
}

3)析构函数
UsbCamera::~UsbCamera()
{
delete[] mTmpRgb16Buffer;
close (fd);
}


5、修改CameraHardwareStub.cpp->S3C6410CameraHardware.cpp

1)改成640x480吧
void CameraHardware::initDefaultParameters()
{
CameraParameters p;
p.setPreviewSize(640,480);
p.setPreviewFrameRate(1);
p.setPreviewFormat("yuv422sp");//("yuv422sp");
p.setPictureSize(640, 480);
p.setPictureFormat("jpeg");//("jpeg");
if (setParameters(p) != NO_ERROR) {
LOGE("Failed to set default parameters?!");
}
}

2)拍照部分要改,因为可以直接获取压缩的jpeg图片
int CameraHardware::pictureThread()
{
UsbCamera* usbCamera = mUsbCamera;
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 * 2 * h);
LOGE("CAMERA_MSG_RAW_IMAGE");
//UsbCamera 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(20000);
//sp<MemoryBase> mem = new MemoryBase(heap, 0, 20000);
//memcpy(heap->base(), kCannedJpeg, 20000);
LOGE("CAMERA_MSG_COMPRESSED_IMAGE");
int w, h;
mParameters.getPictureSize(&w, &h);
sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
//UsbCamera cam(w, h);
usbCamera->getNextFrameAsYuv422((uint8_t *)mRawHeap->base());
mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie);

}
return NO_ERROR;
}

3)status_t CameraHardware::setParameters(const CameraParameters& params)
有个地方只让take 320x240的pic,要注释掉
/* if (w != 320 && h != 240) {
LOGE("Still picture size must be size of canned JPEG (%dx%d)",
320, 240);
return -1;
}*/

至此Camera HAL已经可以拍照了。

存在问题:

1)需要做jpeg->YUV以实现preview功能,libjpeg没用过,暂时不做。
2)UsbCamera.cpp要改成V4L2标准流程,现在这种read模式太简单,效率低。

**********************************************************************************

以下是自己遇到的问题

1、有一些头文件需要包含

2、还是不能抓图

2010.7.30

1、不明原因的可以使用cat /dev/video0 > /sdcard/a.jpg获得图片

期间进行的操作或许对此有影响:

a、进入U盘目录

b、更换Usb端口

c、换一个文件少的U盘,可以让数据保存全

2、有video0节点,但是不能open video0,解决办法,

chmod 666 /dev/video0

就可以了。

点击拍照键之后在右上角能出现图片,但是预览框还是没有数据的。

第一步:按照上一个日志的步骤,用自己的libcamera替换stub的camera。

第二步:配置视频方式,由于ZC0301输出的视频数据只能是jpeg的,而Android系统带的camera的应用程序预览只能是RGB或者YCbCr的,目前只调试成功RGB565,(YCbCr方式预览窗口不能显示正确的颜色)所以如下配置:

UsbCamera.cpp

fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;

MyCameraHardware.cpp
p.setPreviewFormat("rgb565");//("yuv422sp");
p.setPictureFormat("jpeg");//("jpeg");

CameraService.cpp

ISurface::BufferHeap buffers(w, h, w, h,PIXEL_FORMAT_RGB_565, transform,0,
mHardware->getPreviewHeap());

第二步:按照V4l2编程要求的步骤,使用mmap方式的调用,并需要在析构函数中调用munmap函数。

第三步:每次上层调用获取数据时,再获取数据,底层不使用任务持续获取。

第四步:数据是JPEG拍照可以直接保存,但是预览就需要解码成rgb,于是增加libjpeg共享库。由于该共享库只解码文件,这里需要的是解码内存中的jpeg,所以增加内存jpeg的解码。详情见下一个日志

第五步:至此,就完成了这个工作。

需要注意的地方:

1、显示窗口的大小设置:经过测试发现设置成320*240是最佳的,如果设置成640*480,可以显示,但是明显比较慢,由于拍照的时候和预览的时候,都是将数据拷贝到别的模块,所以运行很吃力。如果设置成480*360,预览窗口显示不正常,会并列3个小窗口。

2、cpp文件调用c文件的函数时,一定要用extern ,否则编译连接会报错。

extern "C"{
#include "jpeglib.h"
}

3、进行jpeg解码时候,jpeg_read_header(&cinfo, 1);获取的cinfo信息里面的cinfo.num_components不正确,实际是2,获取的却是3,所以计算偏移的时候不能使用offset += cinfo.num_components * cinfo.output_width;而必须使用offset += 2 * cinfo.output_width;

需要改进的地方:

1、预览使用overlay方式,一定会快很多。

2、camera java应用程序的权限要改,正常启动程序,居然不能open video0

3、拍照时memcpy数据,效率太低,最好能够直接存成文件,不知道其他的camera应用程序怎么处理的。

原帖:http://my.unix-center.net/~Simon_fu/?p=565

熟悉libjpeg的朋友都知道libjpeg是一个开源的库。Linux和Android都是用libjpeg来支持jpeg文件的,可见其功能多么强大。但是默认情况下libjpeg只能处理jpeg文件的解码,或者把图像编码到jpeg文件。在嵌入式设备中没有文件系统也是很正常的事情,难道我们就不能利用libjpeg的强大功能了吗?当然不是!本文将会介绍怎样扩展libjpeg让其能够解码内存中的jpeg数据。

在介绍主题之前,请允许我讨论一下公共代码库的数据输入的一些问题。因为一个公共代码库是开放给大家用的,这个世界的输入方式也是多种多样的,比如可以通过文件输入,shell用户手工输入,内存缓存输入,网络socket输入等等。所以实现库的时候,千万不要假定用户只有一种输入方式。

通用的做法是实现一个输入的中间层。如果库是以支持面向对象语言实现的话,可以实现一套流机制,实现各式各样的流(文件流,缓存流,socket流等)。公共代码库的输入为流对象。这样库就可以实现各式各样的输入了。一个例子请参考Android图形引擎Skia的实现。

假如库是用非面向对象的语言实现的话,那么怎样来实现多种输入方式呢?可以通过定义输入对象的数据结构,该数据结构中让用户注册读写数据的函数和数据。因为只有调用者最清楚他的数据来源,数据读取方式。在公共代码库中,只需要调用用户注册的回调函数对数据进行读写就可以了。这样的话,也可以实现公共代码库对多种输入方式的支持。

回到本文的主题,libjpeg对多种输入的支持就不好,它假设了用户只会用文件作为输入,没有考虑其他的输入方式。经过研究他的源代码发现其内部也是非常容易扩展,进而实现对多种输入的支持的,但是libjpeg没有更这样做,不明白为什么。请看jpeglib.h中如下定义:

/* Data source object for decompression */ struct jpeg_source_mgr { const JOCTET * next_input_byte; /* => next byte to read from buffer */ size_t bytes_in_buffer; /* # of bytes remaining in buffer */ JMETHOD(void, init_source, (j_decompress_ptr cinfo)); JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); JMETHOD(void, term_source, (j_decompress_ptr cinfo)); };

可以看出source manager对象可以注册多个回调函数来对数据进行读写。在看jdatasrc.c中的代码:

typedef struct { struct jpeg_source_mgr pub; /* public fields */ FILE * infile; /* source stream */ JOCTET * buffer; /* start of buffer */ boolean start_of_file; /* have we gotten any data yet? */ } my_source_mgr;

该文件为jpeglib的source manger初始化和管理的地方。上面的数据结构是内部使用的源数据。可以看出其源数据只支持文件输入(infile变量),并提供缓存功能(buffer变量)。

其对source manager初始化的接口定义子jpeglib.h中,定义如下:

EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile));

通过这个接口我们可以看出它的source manager只能接收文件作为输入。该函数的实现在jdatasrc.c文件中。

为了支持内存jpeg数据输入,我的设计是在jdatasrc.c中实现一个新的接口来初始化jpeglib的source manger对象。并完成注册其读写的回调函数给source manager。

说干就干,首先我们需要让source manager对象支持内存数据。修改my_source_mgr数据结构如下:

typedef struct{ UINT8* img_buffer; UINT32 buffer_size; UINT32 pos; }BUFF_JPG; /* Expanded data source object for stdio input */ typedef struct { struct jpeg_source_mgr pub; /* public fields */ union{ BUFF_JPG jpg; /* jpeg image buffer */ VFS_FILE * infile; /* source stream */ }; JOCTET * buffer; /* start of buffer */ boolean start_of_file; /* have we gotten any data yet? */ } my_source_mgr;

可以看出我们通过union来支持内存数据(jpg变量)或者文件输入。因为需要负责读写必须要标识出当前内存读写的位置,所以必须要在BUFF_JPG数据结构中定义pos变量。

下一步我们需要实现读写内存jpeg数据的回调函数了。经过分析对文件数据读写的回调函数,发现我们只需要实现jpeg_source_mgr数据结构中的fill_input_buffer回调函数就可以了,其他的回调函数可以延用对文件读取的回调函数。在jdatasrc.c文件中,定义回调函数如下:

/* * This function will read the jpeg memery block to fill the library buffer. */ METHODDEF(boolean) jpg_fill_input_buffer (j_decompress_ptr cinfo) { my_src_ptr src = (my_src_ptr) cinfo->src; size_t nbytes; if(src->jpg.img_buffer == NULL || src->jpg.pos >= src->jpg.buffer_size){ nbytes = -1; } else { nbytes = (src->jpg.pos + INPUT_BUF_SIZE > src->jpg.buffer_size ? / src->jpg.buffer_size - src->jpg.pos : INPUT_BUF_SIZE); MEMCPY(src->buffer, src->jpg.img_buffer + src->jpg.pos, nbytes); src->jpg.pos += nbytes; } if (nbytes <= 0) { if (src->start_of_file) /* Treat empty input file as fatal error */ ERREXIT(cinfo, JERR_INPUT_EMPTY); WARNMS(cinfo, JWRN_JPEG_EOF); /* Insert a fake EOI marker */ src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; nbytes = 2; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = nbytes; src->start_of_file = FALSE; return TRUE; }

可以看出我们读取数据都是从内存缓存中读取,如果到达缓存末尾就返回-1。

经过调试分析还发现jdatasrc.c文件中skip_input_data函数有一个不严谨的地方。原来代码中如下:

METHODDEF(void) skip_input_data (j_decompress_ptr cinfo, long num_bytes) { my_src_ptr src = (my_src_ptr) cinfo->src; /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth * any trouble anyway --- large skips are infrequent. */ if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) fill_input_buffer(cinfo); /* note we assume that fill_input_buffer will never return FALSE, * so suspension need not be handled. */ } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } }

请注意显示地调用了fill_input_buffer,而不是调用注册给source manager的回调函数。这样做是不严谨的,虽然只支持文件输入的情况下,这样写没有任何问题,但是如果我们增加其他的输入方式的话(比如内存数据输入),这样写将不会调用到我们注册给Source manager的fill_input_buffer回调函数。所以如上的代码修改为:

METHODDEF(void) skip_input_data (j_decompress_ptr cinfo, long num_bytes) { my_src_ptr src = (my_src_ptr) cinfo->src; /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth * any trouble anyway --- large skips are infrequent. */ if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; //(void) fill_input_buffer(cinfo); (void) src->pub.fill_input_buffer(cinfo); /* note we assume that fill_input_buffer will never return FALSE, * so suspension need not be handled. */ } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } }

调用我们注册的回调函数来读取数据。

最好我们需要实现一个供用户用内存jpeg数据初始化source manager的接口。我的定义如下:

/* * This function improve the library can use the jpeg memory block as source. */ GLOBAL(void) jpeg_stdio_buffer_src (j_decompress_ptr cinfo, UINT8 * buffer, UINT32 size) { my_src_ptr src; if (cinfo->src == NULL) { /* first time for this JPEG object? */ cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(my_source_mgr)); src = (my_src_ptr) cinfo->src; src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * SIZEOF(JOCTET)); } src = (my_src_ptr) cinfo->src; src->pub.init_source = init_source; src->pub.fill_input_buffer = jpg_fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ src->pub.term_source = term_source; //src->infile = infile; src->jpg.img_buffer = buffer; src->jpg.buffer_size = size; src->jpg.pos = 0; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src->pub.next_input_byte = NULL; /* until buffer loaded */ }

通过该函数会发现:我们用户输入的buffer初始化了my_source_mgr,并用我们实现的回调函数jpg_fill_input_buffer初始化了jpeg_source_mgr数据结构中的fill_input_buffer。这样每次libjpeg读取数据就将会调用jpg_fill_input_buffer来读取内存jpeg数据了。

最后把jpeg_stdio_buffer_src接口暴露给最终用户。在jpeglib.h中增加如下定义:

EXTERN(void) jpeg_stdio_buffer_src JPP((j_decompress_ptr cinfo, UINT8 * buffer, UINT32 size));

至此libjpeg已经可以支持内存jpeg数据的解码了。只需要在调用jpeg_stdio_src接口的地方改调用jpeg_stdio_buffer_src就可以了。

以上代码仅供参考,以上代码初步测试没有问题,但是未经过严格测试。如果你经过测试发现代码中的错误或者有改进的方法,请你和我联系。我的联系方式请看《关于云中漫步》。

更多相关文章

  1. Java、Android中Math详解
  2. Android(安卓)读取元素的数据
  3. android mvvm livedata_Android(安卓)MVP && MVVM深度解析
  4. Android训练课程)快三如何快速回本稳赢81512073
  5. 协程 Flow 最佳实践 | 基于 Android(安卓)开发者峰会应用
  6. 物联网温湿度显示控制项目(网页、Android双端显示&搭载linux平台
  7. android studio 获取SHA1(指纹)
  8. android连接小票打印机,打印小票数据的两种模式
  9. Android(安卓)Intent机制实例详解(1)

随机推荐

  1. Kotlin 成为 Android(安卓)开发首选语言
  2. Android消息传递机制Handler完全解析之4
  3. Android中的四种布局
  4. Android刮刮乐效果-proterDuffXfermode的
  5. Android下 读写文件
  6. android recover 系统代码分析 -- 选择进
  7. 2020最新Android大厂面试真题大全(附答案)
  8. 详解Android(安卓)消息处理机制
  9. Android(安卓)UI组件框架AndroidMaterial
  10. android layout_weight的使用