这两天一直在调拍照,由于之前拍照照片的格式只支持VGA以下的,我拿我的pad一看,居然可以支持5M和1.3M,也就是前后摄像头的最大分辨率。这让我想到肯定是软件没做好,有待改善,然后这两天有空,就调了调。终于调出来了,一直卡在死锁状态。
由于软件功底不够,对于锁,没什么概念,还好最后还是搞出来了,过程真是痛苦啊。。
流程如下:
首先cameraservice 会调用 takepicture()。
status_t CameraHardwareStub::takePicture()
{
disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
if (createThread(beginPictureThread, this) == false)
return UNKNOWN_ERROR;
return NO_ERROR;
}
/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie)
{
CameraHardwareStub *c = (CameraHardwareStub *)cookie;
return c->pictureThread();
}
int CameraHardwareStub::pictureThread()
{
int picture_width, picture_height;
mParameters.getPictureSize(&picture_width, &picture_height);
LOGD("picture size=%dx%d", picture_width, picture_height);
if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
LOGE("Take Picture COMPRESSED IMAGE");
/*
* The takePicture() function called by the CameraService
* must return synchronously without attempting to wait
* for any locks, to prevent circular deadlocking in
* attempting to call CameraService callbacks to deliver
* frames. This includes waiting for the preview thread
* to finish cleanly. So stopping the preview thread
* is done in this separate pictureThread
*/
sp<PreviewThread> previewThread;
{
Mutex::Autolock lock(mLock);
previewThread = mPreviewThread;
}
if (previewThread != 0) {
previewThread->requestExitAndWait();
Mutex::Autolock lock(mLock);
mPreviewThread.clear();
}
mFakeCamera->Uninit();
mFakeCamera->StopStreaming(); //卸载掉preview 内存。
{
mPictureLock.lock();
mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mFakeCamera->GrabJpegFrame(picture_width, picture_height), mCallbackCookie);
mPictureLock.unlock();
}
}
return NO_ERROR;
}
GrabJpegFrame 会具体执行拍照功能。
sp<IMemory> V4L2Camera::GrabJpegFrame (int pic_width, int pic_height)
{
FILE *output;
FILE *input;
int fileSize;
int ret, i;
int w, h;
struct v4l2_buffer v4l2_buf_picture;
struct v4l2_format format;
struct v4l2_streamparm parm;
struct v4l2_requestbuffers reqbuf;
sp<MemoryHeapBase> rawPictureHeap;
sp<MemoryHeapBase> mjpegPictureHeap;
sp<MemoryBase> jpegMemBase;
/* VIDIOC_S_FMT */
memset(&format, 0, sizeof(format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = pic_width;
format.fmt.pix.height = pic_height;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (ioctl(fd, VIDIOC_S_FMT, &format) < 0)
{
LOGE("Error: VIDIOC_S_FMT");
goto done;
}
/* VIDIOC_S_PARM */
memset(&parm, 0, sizeof(parm));
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 2;
parm.parm.capture.timeperframe.denominator = 15;
if (ioctl(fd, VIDIOC_S_PARM, &parm) < 0)
{
LOGE("Error: VIDIOC_S_PARM");
goto done;
}
/* VIDIOC_REQBUFS */
memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 1;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0)
{
LOGE ("Error: VIDIOC_REQBUFS");
goto done;
}
memset(&v4l2_buf_picture, 0, sizeof(v4l2_buf_picture));
v4l2_buf_picture.index = 0;
v4l2_buf_picture.type = reqbuf.type;
if (ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buf_picture) < 0)
{
LOGE("Error: VIDIOC_QUERYBUF");
goto done;
}
/* use MemoryHeapBase to do mmap */
rawPictureHeap = new MemoryHeapBase(fd,
v4l2_buf_picture.length, 0, v4l2_buf_picture.m.offset);
if (rawPictureHeap == NULL)
{
LOGE("Error: Cannot create rawPictureHeap");
goto done;
}
/* VIDIOC_QBUF */
if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf_picture) < 0)
{
LOGE("Error: VIDIOC_QBUF");
}
StartStreaming();
for(i = 0; i < PICTURE_FRAME_GRAB_COUNT; i++){
/* get one frame from camera */
if (ioctl(fd, VIDIOC_DQBUF, &v4l2_buf_picture) < 0){
LOGE("Error: VIDIOC_DQBUF");
goto done;
}
if((ioctl(fd, VIDIOC_QBUF, &v4l2_buf_picture)) < 0){
LOGE("Error: VIDIOC_QBUF = %d:%d", ret, errno);
}
usleep(50*1000);
}
output = fopen("/data/tmp.jpg", "wb");
if (output == NULL) {
LOGE("GrabJpegFrame: Ouput file == NULL");
return NULL;
}
fileSize = saveYUYVtoJPEG((unsigned char *)rawPictureHeap->getBase(), pic_width, pic_height, output, 85);
fclose(output);
input = fopen("/data/tmp.jpg", "rb");
if (input == NULL)
LOGE("GrabJpegFrame: Input file == NULL");
else {
mjpegPictureHeap = new MemoryHeapBase(fileSize);
jpegMemBase = new MemoryBase(mjpegPictureHeap, 0, fileSize);
fread((uint8_t *)mjpegPictureHeap->base(), 1, fileSize, input);
fclose(input);
rawPictureHeap.clear();
StopStreaming();
return jpegMemBase;
}
done:
rawPictureHeap.clear();
StopStreaming();
return NULL;
}
int V4L2Camera::saveYUYVtoJPEG (unsigned char *inputBuffer, int width, int height, FILE *file, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1];
unsigned char *line_buffer, *yuyv;
int z;
int fileSize;
line_buffer = (unsigned char *) calloc (width * 3, 1);
yuyv = inputBuffer;
cinfo.err = jpeg_std_error (&jerr);
jpeg_create_compress (&cinfo);
jpeg_stdio_dest (&cinfo, file);
LOGI("JPEG PICTURE WIDTH AND HEIGHT: %dx%d", width, height);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults (&cinfo);
jpeg_set_quality (&cinfo, quality, TRUE);
jpeg_start_compress (&cinfo, TRUE);
z = 0;
while (cinfo.next_scanline < cinfo.image_height) {
int x;
unsigned char *ptr = line_buffer;
for (x = 0; x < width; x++) {
int r, g, b;
int y, u, v;
if (!z)
y = yuyv[0] << 8;
else
y = yuyv[2] << 8;
u = yuyv[1] - 128;
v = yuyv[3] - 128;
r = (y + (359 * v)) >> 8;
g = (y - (88 * u) - (183 * v)) >> 8;
b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
if (z++) {
z = 0;
yuyv += 4;
}
}
row_pointer[0] = line_buffer;
jpeg_write_scanlines (&cinfo, row_pointer, 1);
}
jpeg_finish_compress (&cinfo);
fileSize = ftell(file);
jpeg_destroy_compress (&cinfo);
free (line_buffer);
return fileSize;
}
下面这个函数,可以查询摄像头模块支持哪些照片的尺寸:
String8 V4L2Camera::GetSupportPictureSize(int fd, int pixelformat, int &w, int &h)
{
LOGD("%s", __func__);
String8 size_list;
struct v4l2_frmsizeenum framesize_enum;
memset(&framesize_enum, 0, sizeof(framesize_enum));
framesize_enum.index = 0;
framesize_enum.pixel_format = pixelformat;
int ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &framesize_enum);
if (ret < 0) {
LOGE("Error: VIDIOC_ENUM_FRAMESIZES - %s", strerror(errno));
return size_list;
}
switch(framesize_enum.type)
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
do {
if (framesize_enum.index) {
w = framesize_enum.discrete.width;
h = framesize_enum.discrete.height;
}
size_list.appendFormat(
framesize_enum.index ? ",%dx%d" : "%dx%d",
framesize_enum.discrete.width,
framesize_enum.discrete.height);
framesize_enum.index++;
ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &framesize_enum);
} while (ret >= 0);
LOGD("Supported sizes string: %s", size_list.string());
break;
case V4L2_FRMSIZE_TYPE_STEPWISE:
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
LOGD("STEPWISE/CONTINUOUS ignored: %dx%d-%dx%d/%dx%d",
framesize_enum.stepwise.min_width,
framesize_enum.stepwise.min_height,
framesize_enum.stepwise.max_width,
framesize_enum.stepwise.max_height,
framesize_enum.stepwise.step_width,
framesize_enum.stepwise.step_height);
break;
default:
LOGE("Unknown format type: %d!!!", framesize_enum.type);
}
return size_list;
}