C++程序  |  250行  |  6.69 KB

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera2_JpegCompressor"

#include <utils/Log.h>

#include "gralloc_cb.h"
#include "JpegCompressor.h"
#include "../EmulatedFakeCamera2.h"
#include "../EmulatedFakeCamera3.h"
#include "../Exif.h"
#include "../Thumbnail.h"
#include "hardware/camera3.h"

namespace android {

JpegCompressor::JpegCompressor():
        Thread(false),
        mIsBusy(false),
        mSynchronous(false),
        mBuffers(NULL),
        mListener(NULL) {
}

JpegCompressor::~JpegCompressor() {
    Mutex::Autolock lock(mMutex);
}

status_t JpegCompressor::reserve() {
    Mutex::Autolock busyLock(mBusyMutex);
    if (mIsBusy) {
        ALOGE("%s: Already processing a buffer!", __FUNCTION__);
        return INVALID_OPERATION;
    }
    mIsBusy = true;
    return OK;
}

status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener, CameraMetadata* settings) {
    if (listener == NULL) {
        ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
        return BAD_VALUE;
    }
    Mutex::Autolock lock(mMutex);
    {
        Mutex::Autolock busyLock(mBusyMutex);

        if (!mIsBusy) {
            ALOGE("Called start without reserve() first!");
            return INVALID_OPERATION;
        }
        mSynchronous = false;
        mBuffers = buffers;
        mListener = listener;
        if (settings) {
              mSettings = *settings;
        }
    }

    status_t res;
    res = run("EmulatedFakeCamera2::JpegCompressor");
    if (res != OK) {
        ALOGE("%s: Unable to start up compression thread: %s (%d)",
                __FUNCTION__, strerror(-res), res);
        delete mBuffers;
    }
    return res;
}

status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
    status_t res;

    Mutex::Autolock lock(mMutex);
    {
        Mutex::Autolock busyLock(mBusyMutex);

        if (mIsBusy) {
            ALOGE("%s: Already processing a buffer!", __FUNCTION__);
            return INVALID_OPERATION;
        }

        mIsBusy = true;
        mSynchronous = true;
        mBuffers = buffers;
    }

    res = compress();

    cleanUp();

    return res;
}

status_t JpegCompressor::cancel() {
    requestExitAndWait();
    return OK;
}

status_t JpegCompressor::readyToRun() {
    return OK;
}

bool JpegCompressor::threadLoop() {
    status_t res;
    ALOGV("%s: Starting compression thread", __FUNCTION__);

    res = compress();

    mListener->onJpegDone(mJpegBuffer, res == OK);

    cleanUp();

    return false;
}

status_t JpegCompressor::compress() {
    // Find source and target buffers. Assumes only one buffer matches
    // each condition!
    bool foundJpeg = false, mFoundAux = false;
    int thumbWidth = 0, thumbHeight = 0;
    unsigned char thumbJpegQuality = 90;
    unsigned char jpegQuality = 90;
    camera_metadata_entry_t entry;

    for (size_t i = 0; i < mBuffers->size(); i++) {
        const StreamBuffer &b = (*mBuffers)[i];
        if (b.format == HAL_PIXEL_FORMAT_BLOB) {
            mJpegBuffer = b;
            mFoundJpeg = true;
        } else if (b.streamId <= 0) {
            mAuxBuffer = b;
            mFoundAux = true;
        }
        if (mFoundJpeg && mFoundAux) break;
    }
    if (!mFoundJpeg || !mFoundAux) {
        ALOGE("%s: Unable to find buffers for JPEG source/destination",
                __FUNCTION__);
        return BAD_VALUE;
    }

    // Create EXIF data and compress thumbnail
    ExifData* exifData = createExifData(mSettings, mAuxBuffer.width, mAuxBuffer.height);
    entry = mSettings.find(ANDROID_JPEG_THUMBNAIL_SIZE);
    if (entry.count > 0) {
        thumbWidth = entry.data.i32[0];
        thumbHeight = entry.data.i32[1];
    }
    entry = mSettings.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
    if (entry.count > 0) {
        thumbJpegQuality = entry.data.u8[0];
    }
    if (thumbWidth > 0 && thumbHeight > 0) {
        createThumbnail(static_cast<const unsigned char*>(mAuxBuffer.img),
                        mAuxBuffer.width, mAuxBuffer.height,
                        thumbWidth, thumbHeight,
                        thumbJpegQuality, exifData);
    }

    // Compress the image
    entry = mSettings.find(ANDROID_JPEG_QUALITY);
    if (entry.count > 0) {
        jpegQuality = entry.data.u8[0];
    }
    NV21JpegCompressor nV21JpegCompressor;
    nV21JpegCompressor.compressRawImage((void*)mAuxBuffer.img,
                                         mAuxBuffer.width,
                                         mAuxBuffer.height,
                                         jpegQuality, exifData);
    nV21JpegCompressor.getCompressedImage((void*)mJpegBuffer.img);

    // Refer to /hardware/libhardware/include/hardware/camera3.h
    // Transport header for compressed JPEG buffers in output streams.
    camera3_jpeg_blob_t jpeg_blob;
    cb_handle_t *cb = (cb_handle_t *)(*mJpegBuffer.buffer);
    jpeg_blob.jpeg_blob_id = CAMERA3_JPEG_BLOB_ID;
    jpeg_blob.jpeg_size = nV21JpegCompressor.getCompressedSize();
    memcpy(mJpegBuffer.img + cb->width - sizeof(camera3_jpeg_blob_t),
           &jpeg_blob, sizeof(camera3_jpeg_blob_t));

    freeExifData(exifData);
    return OK;
}

bool JpegCompressor::isBusy() {
    Mutex::Autolock busyLock(mBusyMutex);
    return mIsBusy;
}

bool JpegCompressor::isStreamInUse(uint32_t id) {
    Mutex::Autolock lock(mBusyMutex);

    if (mBuffers && mIsBusy) {
        for (size_t i = 0; i < mBuffers->size(); i++) {
            if ( (*mBuffers)[i].streamId == (int)id ) return true;
        }
    }
    return false;
}

bool JpegCompressor::waitForDone(nsecs_t timeout) {
    Mutex::Autolock lock(mBusyMutex);
    while (mIsBusy) {
        status_t res = mDone.waitRelative(mBusyMutex, timeout);
        if (res != OK) return false;
    }
    return true;
}

void JpegCompressor::cleanUp() {
    status_t res;
    Mutex::Autolock lock(mBusyMutex);

    if (mFoundAux) {
        if (mAuxBuffer.streamId == 0) {
            delete[] mAuxBuffer.img;
        } else if (!mSynchronous) {
            mListener->onJpegInputDone(mAuxBuffer);
        }
    }
    if (!mSynchronous) {
        delete mBuffers;
    }

    mBuffers = NULL;

    mIsBusy = false;
    mDone.signal();
}

JpegCompressor::JpegListener::~JpegListener() {
}

} // namespace android