take picture 过程分析

这两天一直在调拍照,由于之前拍照照片的格式只支持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;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值