diff --git a/tools/emulator/system/camera/Android.mk b/tools/emulator/system/camera/Android.mk index dc2e6247f..ee08f9403 100755 --- a/tools/emulator/system/camera/Android.mk +++ b/tools/emulator/system/camera/Android.mk @@ -60,7 +60,8 @@ LOCAL_SRC_FILES := \ EmulatedFakeCamera2.cpp \ EmulatedQemuCamera2.cpp \ fake-pipeline2/Scene.cpp \ - fake-pipeline2/Sensor.cpp + fake-pipeline2/Sensor.cpp \ + fake-pipeline2/JpegCompressor.cpp ifeq ($(TARGET_PRODUCT),vbox_x86) diff --git a/tools/emulator/system/camera/EmulatedFakeCamera2.cpp b/tools/emulator/system/camera/EmulatedFakeCamera2.cpp index ef273ba0a..31ac8ea95 100644 --- a/tools/emulator/system/camera/EmulatedFakeCamera2.cpp +++ b/tools/emulator/system/camera/EmulatedFakeCamera2.cpp @@ -30,8 +30,10 @@ namespace android { -const uint32_t EmulatedFakeCamera2::kAvailableFormats[3] = { +const uint32_t EmulatedFakeCamera2::kAvailableFormats[5] = { HAL_PIXEL_FORMAT_RAW_SENSOR, + HAL_PIXEL_FORMAT_BLOB, + HAL_PIXEL_FORMAT_RGBA_8888, HAL_PIXEL_FORMAT_YV12, HAL_PIXEL_FORMAT_YCrCb_420_SP }; @@ -101,6 +103,11 @@ status_t EmulatedFakeCamera2::Initialize() { } if (res != OK) return res; + mNextStreamId = 0; + mRawStreamCount = 0; + mProcessedStreamCount = 0; + mJpegStreamCount = 0; + return NO_ERROR; } @@ -114,10 +121,10 @@ status_t EmulatedFakeCamera2::connectCamera(hw_device_t** device) { mConfigureThread = new ConfigureThread(this); mReadoutThread = new ReadoutThread(this); - mSensor = new Sensor(); + mSensor = new Sensor(this); + mJpegCompressor = new JpegCompressor(this); mNextStreamId = 0; - mRawStreamOps = NULL; res = mSensor->startUp(); if (res != NO_ERROR) return res; @@ -145,10 +152,12 @@ status_t EmulatedFakeCamera2::closeCamera() { mConfigureThread->requestExit(); mReadoutThread->requestExit(); + mJpegCompressor->cancel(); mConfigureThread->join(); mReadoutThread->join(); + ALOGV("%s exit", __FUNCTION__); return NO_ERROR; } @@ -174,12 +183,23 @@ int EmulatedFakeCamera2::requestQueueNotify() { ALOG_ASSERT(mFrameQueueDst != NULL, "%s: Request queue src not set, but received queue notification!", __FUNCTION__); - ALOG_ASSERT(mRawStreamOps != NULL, - "%s: No raw stream allocated, but received queue notification!", + ALOG_ASSERT(mStreams.size() != 0, + "%s: No streams allocated, but received queue notification!", __FUNCTION__); return mConfigureThread->newRequestAvailable(); } +int EmulatedFakeCamera2::getInProgressCount() { + Mutex::Autolock l(mMutex); + + int requestCount = 0; + requestCount += mConfigureThread->getInProgressCount(); + requestCount += mReadoutThread->getInProgressCount(); + requestCount += mJpegCompressor->isBusy() ? 1 : 0; + + return requestCount; +} + int EmulatedFakeCamera2::constructDefaultRequest( int request_template, camera_metadata_t **request) { @@ -219,12 +239,6 @@ int EmulatedFakeCamera2::allocateStream( uint32_t *max_buffers) { Mutex::Autolock l(mMutex); - if (mNextStreamId > 0) { - // TODO: Support more than one stream - ALOGW("%s: Only one stream supported", __FUNCTION__); - return BAD_VALUE; - } - if (format != CAMERA2_HAL_PIXEL_FORMAT_OPAQUE) { unsigned int numFormats = sizeof(kAvailableFormats) / sizeof(uint32_t); unsigned int formatIdx = 0; @@ -243,14 +257,25 @@ int EmulatedFakeCamera2::allocateStream( const uint32_t *availableSizes; size_t availableSizeCount; - if (format == HAL_PIXEL_FORMAT_RAW_SENSOR) { - availableSizes = kAvailableRawSizes; - availableSizeCount = sizeof(kAvailableRawSizes)/sizeof(uint32_t); - } else { - availableSizes = kAvailableProcessedSizes; - availableSizeCount = sizeof(kAvailableProcessedSizes)/sizeof(uint32_t); + switch (format) { + case HAL_PIXEL_FORMAT_RAW_SENSOR: + availableSizes = kAvailableRawSizes; + availableSizeCount = sizeof(kAvailableRawSizes)/sizeof(uint32_t); + break; + case HAL_PIXEL_FORMAT_BLOB: + availableSizes = kAvailableJpegSizes; + availableSizeCount = sizeof(kAvailableJpegSizes)/sizeof(uint32_t); + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + availableSizes = kAvailableProcessedSizes; + availableSizeCount = sizeof(kAvailableProcessedSizes)/sizeof(uint32_t); + break; + default: + ALOGE("%s: Unknown format 0x%x", __FUNCTION__, format); + return BAD_VALUE; } - // TODO: JPEG sizes unsigned int resIdx = 0; for (; resIdx < availableSizeCount; resIdx++) { @@ -263,16 +288,41 @@ int EmulatedFakeCamera2::allocateStream( return BAD_VALUE; } - // TODO: Generalize below to work for variable types of streams, etc. - // Currently only correct for raw sensor format, sensor resolution. + switch (format) { + case HAL_PIXEL_FORMAT_RAW_SENSOR: + if (mRawStreamCount >= kMaxRawStreamCount) { + ALOGE("%s: Cannot allocate another raw stream (%d already allocated)", + __FUNCTION__, mRawStreamCount); + return INVALID_OPERATION; + } + mRawStreamCount++; + break; + case HAL_PIXEL_FORMAT_BLOB: + if (mJpegStreamCount >= kMaxJpegStreamCount) { + ALOGE("%s: Cannot allocate another JPEG stream (%d already allocated)", + __FUNCTION__, mJpegStreamCount); + return INVALID_OPERATION; + } + mJpegStreamCount++; + break; + default: + if (mProcessedStreamCount >= kMaxProcessedStreamCount) { + ALOGE("%s: Cannot allocate another processed stream (%d already allocated)", + __FUNCTION__, mProcessedStreamCount); + return INVALID_OPERATION; + } + mProcessedStreamCount++; + } - ALOG_ASSERT(width == Sensor::kResolution[0], - "%s: TODO: Only supporting raw sensor size right now", __FUNCTION__); - ALOG_ASSERT(height == Sensor::kResolution[1], - "%s: TODO: Only supporting raw sensor size right now", __FUNCTION__); + Stream newStream; + newStream.ops = stream_ops; + newStream.width = width; + newStream.height = height; + newStream.format = format; + // TODO: Query stride from gralloc + newStream.stride = width; - mStreamFormat = format; - mRawStreamOps = stream_ops; + mStreams.add(mNextStreamId, newStream); *stream_id = mNextStreamId; if (format_actual) *format_actual = format; @@ -298,11 +348,32 @@ int EmulatedFakeCamera2::registerStreamBuffers( int EmulatedFakeCamera2::releaseStream(uint32_t stream_id) { Mutex::Autolock l(mMutex); - ALOG_ASSERT(stream_id == 0, - "%s: TODO: Only one stream supported", __FUNCTION__); - // TODO: Need to clean up better than this - in-flight buffers likely - mRawStreamOps = NULL; + ssize_t streamIndex = mStreams.indexOfKey(stream_id); + if (streamIndex < 0) { + ALOGE("%s: Unknown stream id %d!", __FUNCTION__, stream_id); + return BAD_VALUE; + } + + if (isStreamInUse(stream_id)) { + ALOGE("%s: Cannot release stream %d; in use!", __FUNCTION__, + stream_id); + return BAD_VALUE; + } + + switch(mStreams.valueAt(streamIndex).format) { + case HAL_PIXEL_FORMAT_RAW_SENSOR: + mRawStreamCount--; + break; + case HAL_PIXEL_FORMAT_BLOB: + mJpegStreamCount--; + break; + default: + mProcessedStreamCount--; + break; + } + + mStreams.removeItemsAt(streamIndex); return NO_ERROR; } @@ -435,6 +506,21 @@ status_t EmulatedFakeCamera2::ConfigureThread::newRequestAvailable() { return OK; } +bool EmulatedFakeCamera2::ConfigureThread::isStreamInUse(uint32_t id) { + Mutex::Autolock lock(mInternalsMutex); + + if (mNextBuffers == NULL) return false; + for (size_t i=0; i < mNextBuffers->size(); i++) { + if ((*mNextBuffers)[i].streamId == (int)id) return true; + } + return false; +} + +int EmulatedFakeCamera2::ConfigureThread::getInProgressCount() { + Mutex::Autolock lock(mInternalsMutex); + return mNextBuffers == NULL ? 0 : 1; +} + bool EmulatedFakeCamera2::ConfigureThread::threadLoop() { static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms status_t res; @@ -457,6 +543,8 @@ bool EmulatedFakeCamera2::ConfigureThread::threadLoop() { // Active } if (mRequest == NULL) { + Mutex::Autolock il(mInternalsMutex); + ALOGV("Getting next request"); res = mParent->mRequestQueueSrc->dequeue_request( mParent->mRequestQueueSrc, @@ -486,11 +574,24 @@ bool EmulatedFakeCamera2::ConfigureThread::threadLoop() { mParent->signalError(); return false; } - // TODO: Only raw stream supported - if (streams.count != 1 || streams.data.u8[0] != 0) { - ALOGE("%s: TODO: Only raw stream supported", __FUNCTION__); - mParent->signalError(); - return false; + + mNextBuffers = new Buffers; + mNextNeedsJpeg = false; + ALOGV("Setting up buffers for capture"); + for (size_t i = 0; i < streams.count; i++) { + const Stream &s = mParent->getStreamInfo(streams.data.u8[i]); + StreamBuffer b; + b.streamId = streams.data.u8[i]; + b.width = s.width; + b.height = s.height; + b.format = s.format; + b.stride = s.stride; + mNextBuffers->push_back(b); + ALOGV(" Buffer %d: Stream %d, %d x %d, format 0x%x, stride %d", + i, b.streamId, b.width, b.height, b.format, b.stride); + if (b.format == HAL_PIXEL_FORMAT_BLOB) { + mNextNeedsJpeg = true; + } } camera_metadata_entry_t e; @@ -548,56 +649,70 @@ bool EmulatedFakeCamera2::ConfigureThread::threadLoop() { mParent->mSensor->getScene().setHour(*e.data.i32); } - // TODO: Fetch stride from gralloc - mNextBufferStride = Sensor::kResolution[0]; + // Start waiting on sensor or JPEG block + if (mNextNeedsJpeg) { + ALOGV("Waiting for JPEG compressor"); + } else { + ALOGV("Waiting for sensor"); + } + } + + if (mNextNeedsJpeg) { + bool jpegDone; + jpegDone = mParent->mJpegCompressor->waitForDone(kWaitPerLoop); + if (!jpegDone) return true; - // Start waiting on sensor ALOGV("Waiting for sensor"); + mNextNeedsJpeg = false; } bool vsync = mParent->mSensor->waitForVSync(kWaitPerLoop); - if (vsync) { - ALOGV("Configuring sensor for frame %d", mNextFrameNumber); - mParent->mSensor->setExposureTime(mNextExposureTime); - mParent->mSensor->setFrameDuration(mNextFrameDuration); - mParent->mSensor->setSensitivity(mNextSensitivity); + if (!vsync) return true; - /** Get buffer to fill for this frame */ - // TODO: Only does raw stream + Mutex::Autolock il(mInternalsMutex); + ALOGV("Configuring sensor for frame %d", mNextFrameNumber); + mParent->mSensor->setExposureTime(mNextExposureTime); + mParent->mSensor->setFrameDuration(mNextFrameDuration); + mParent->mSensor->setSensitivity(mNextSensitivity); - /* Get next buffer from raw stream */ - mNextBuffer = NULL; - res = mParent->mRawStreamOps->dequeue_buffer(mParent->mRawStreamOps, - &mNextBuffer); - if (res != NO_ERROR || mNextBuffer == NULL) { - ALOGE("%s: Unable to dequeue buffer from stream %d: %d", - __FUNCTION__, 0, res); + /** Get buffers to fill for this frame */ + for (size_t i = 0; i < mNextBuffers->size(); i++) { + StreamBuffer &b = mNextBuffers->editItemAt(i); + + Stream s = mParent->getStreamInfo(b.streamId); + + res = s.ops->dequeue_buffer(s.ops, &(b.buffer) ); + if (res != NO_ERROR || b.buffer == NULL) { + ALOGE("%s: Unable to dequeue buffer from stream %d: %s (%d)", + __FUNCTION__, b.streamId, strerror(-res), res); mParent->signalError(); return false; } /* Lock the buffer from the perspective of the graphics mapper */ uint8_t *img; - const Rect rect(Sensor::kResolution[0], Sensor::kResolution[1]); + const Rect rect(s.width, s.height); - res = GraphicBufferMapper::get().lock(*mNextBuffer, + res = GraphicBufferMapper::get().lock(*(b.buffer), GRALLOC_USAGE_SW_WRITE_OFTEN, - rect, (void**)&img); + rect, (void**)&(b.img) ); if (res != NO_ERROR) { - ALOGE("%s: grbuffer_mapper.lock failure: %d", __FUNCTION__, res); - mParent->mRawStreamOps->cancel_buffer(mParent->mRawStreamOps, - mNextBuffer); + ALOGE("%s: grbuffer_mapper.lock failure: %s (%d)", + __FUNCTION__, strerror(-res), res); + s.ops->cancel_buffer(s.ops, + b.buffer); mParent->signalError(); return false; } - mParent->mSensor->setDestinationBuffer(img, mParent->mStreamFormat, - mNextBufferStride); - mParent->mReadoutThread->setNextCapture(mRequest, mNextBuffer); - - mRequest = NULL; } + mParent->mReadoutThread->setNextCapture(mRequest, mNextBuffers); + mParent->mSensor->setDestinationBuffers(mNextBuffers); + + mRequest = NULL; + mNextBuffers = NULL; + return true; } @@ -606,8 +721,7 @@ EmulatedFakeCamera2::ReadoutThread::ReadoutThread(EmulatedFakeCamera2 *parent): mParent(parent), mRunning(false), mActive(false), - mRequest(NULL), - mBuffer(NULL) + mRequest(NULL) { mInFlightQueue = new InFlightQueue[kInFlightQueueSize]; mInFlightHead = 0; @@ -635,8 +749,9 @@ status_t EmulatedFakeCamera2::ReadoutThread::waitUntilRunning() { return OK; } -void EmulatedFakeCamera2::ReadoutThread::setNextCapture(camera_metadata_t *request, - buffer_handle_t *buffer) { +void EmulatedFakeCamera2::ReadoutThread::setNextCapture( + camera_metadata_t *request, + Buffers *buffers) { Mutex::Autolock lock(mInputMutex); if ( (mInFlightTail + 1) % kInFlightQueueSize == mInFlightHead) { ALOGE("In flight queue full, dropping captures"); @@ -644,7 +759,7 @@ void EmulatedFakeCamera2::ReadoutThread::setNextCapture(camera_metadata_t *reque return; } mInFlightQueue[mInFlightTail].request = request; - mInFlightQueue[mInFlightTail].buffer = buffer; + mInFlightQueue[mInFlightTail].buffers = buffers; mInFlightTail = (mInFlightTail + 1) % kInFlightQueueSize; if (!mActive) { @@ -653,6 +768,41 @@ void EmulatedFakeCamera2::ReadoutThread::setNextCapture(camera_metadata_t *reque } } +bool EmulatedFakeCamera2::ReadoutThread::isStreamInUse(uint32_t id) { + Mutex::Autolock lock(mInputMutex); + + size_t i = mInFlightHead; + while (i != mInFlightTail) { + for (size_t j = 0; j < mInFlightQueue[i].buffers->size(); j++) { + if ( (*(mInFlightQueue[i].buffers))[j].streamId == (int)id ) + return true; + } + i = (i + 1) % kInFlightQueueSize; + } + + Mutex::Autolock iLock(mInternalsMutex); + + if (mBuffers != NULL) { + for (i = 0; i < mBuffers->size(); i++) { + if ( (*mBuffers)[i].streamId == (int)id) return true; + } + } + + return false; +} + +int EmulatedFakeCamera2::ReadoutThread::getInProgressCount() { + Mutex::Autolock lock(mInputMutex); + Mutex::Autolock iLock(mInternalsMutex); + + int requestCount = + ((mInFlightTail + kInFlightQueueSize) - mInFlightHead) + % kInFlightQueueSize; + requestCount += (mBuffers == NULL) ? 0 : 1; + + return requestCount; +} + bool EmulatedFakeCamera2::ReadoutThread::threadLoop() { static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms status_t res; @@ -679,11 +829,14 @@ bool EmulatedFakeCamera2::ReadoutThread::threadLoop() { mActive = false; return true; } else { + Mutex::Autolock iLock(mInternalsMutex); mRequest = mInFlightQueue[mInFlightHead].request; - mBuffer = mInFlightQueue[mInFlightHead].buffer; + mBuffers = mInFlightQueue[mInFlightHead].buffers; mInFlightQueue[mInFlightHead].request = NULL; - mInFlightQueue[mInFlightHead].buffer = NULL; + mInFlightQueue[mInFlightHead].buffers = NULL; mInFlightHead = (mInFlightHead + 1) % kInFlightQueueSize; + ALOGV("Ready to read out request %p, %d buffers", + mRequest, mBuffers->size()); } } } @@ -700,6 +853,7 @@ bool EmulatedFakeCamera2::ReadoutThread::threadLoop() { // Got sensor data, construct frame and send it out ALOGV("Readout: Constructing metadata and frames"); + Mutex::Autolock iLock(mInternalsMutex); camera_metadata_entry_t metadataMode; res = find_camera_metadata_entry(mRequest, @@ -771,16 +925,41 @@ bool EmulatedFakeCamera2::ReadoutThread::threadLoop() { } mRequest = NULL; - ALOGV("Sending image buffer to output stream."); - GraphicBufferMapper::get().unlock(*mBuffer); - res = mParent->mRawStreamOps->enqueue_buffer(mParent->mRawStreamOps, - captureTime, mBuffer); - if (res != OK) { - ALOGE("Error enqueuing image buffer %p: %s (%d)", mBuffer, - strerror(-res), res); - // TODO: Should this cause a stop? + int compressedBufferIndex = -1; + ALOGV("Processing %d buffers", mBuffers->size()); + for (size_t i = 0; i < mBuffers->size(); i++) { + const StreamBuffer &b = (*mBuffers)[i]; + ALOGV(" Buffer %d: Stream %d, %d x %d, format 0x%x, stride %d", + i, b.streamId, b.width, b.height, b.format, b.stride); + if (b.streamId >= 0) { + if (b.format == HAL_PIXEL_FORMAT_BLOB) { + // Assumes only one BLOB buffer type per capture + compressedBufferIndex = i; + } else { + ALOGV("Sending image buffer %d to output stream %d", + i, b.streamId); + GraphicBufferMapper::get().unlock(*(b.buffer)); + res = mParent->getStreamInfo(b.streamId).ops->enqueue_buffer( + mParent->getStreamInfo(b.streamId).ops, + captureTime, b.buffer); + if (res != OK) { + ALOGE("Error enqueuing image buffer %p: %s (%d)", b.buffer, + strerror(-res), res); + mParent->signalError(); + } + } + } + } + if (compressedBufferIndex == -1) { + delete mBuffers; + mBuffers = NULL; + } else { + ALOGV("Starting JPEG compression for buffer %d, stream %d", + compressedBufferIndex, + (*mBuffers)[compressedBufferIndex].streamId); + mParent->mJpegCompressor->start(mBuffers, captureTime); + mBuffers = NULL; } - mBuffer = NULL; return true; } @@ -789,7 +968,7 @@ bool EmulatedFakeCamera2::ReadoutThread::threadLoop() { status_t EmulatedFakeCamera2::constructStaticInfo( camera_metadata_t **info, - bool sizeRequest) { + bool sizeRequest) const { size_t entryCount = 0; size_t dataCount = 0; @@ -958,6 +1137,9 @@ status_t EmulatedFakeCamera2::constructStaticInfo( ADD_OR_SIZE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegThumbnailSizes, sizeof(jpegThumbnailSizes)/sizeof(int32_t)); + static const int32_t jpegMaxSize = JpegCompressor::kMaxJpegSize; + ADD_OR_SIZE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1); + // android.stats static const uint8_t availableFaceDetectModes[] = { @@ -1080,7 +1262,7 @@ status_t EmulatedFakeCamera2::constructStaticInfo( status_t EmulatedFakeCamera2::constructDefaultRequest( int request_template, camera_metadata_t **request, - bool sizeRequest) { + bool sizeRequest) const { size_t entryCount = 0; size_t dataCount = 0; @@ -1433,5 +1615,25 @@ status_t EmulatedFakeCamera2::addOrSize(camera_metadata_t *request, } } +bool EmulatedFakeCamera2::isStreamInUse(uint32_t id) { + // Assumes mMutex is locked; otherwise new requests could enter + // configureThread while readoutThread is being checked + + // Order of isStreamInUse calls matters + if (mConfigureThread->isStreamInUse(id) || + mReadoutThread->isStreamInUse(id) || + mJpegCompressor->isStreamInUse(id) ) { + ALOGE("%s: Stream %d is in use in active requests!", + __FUNCTION__, id); + return true; + } + return false; + } + +const Stream& EmulatedFakeCamera2::getStreamInfo(uint32_t streamId) { + Mutex::Autolock lock(mMutex); + + return mStreams.valueFor(streamId); +} }; /* namespace android */ diff --git a/tools/emulator/system/camera/EmulatedFakeCamera2.h b/tools/emulator/system/camera/EmulatedFakeCamera2.h index eba1a8ead..34d1b1221 100644 --- a/tools/emulator/system/camera/EmulatedFakeCamera2.h +++ b/tools/emulator/system/camera/EmulatedFakeCamera2.h @@ -24,8 +24,11 @@ */ #include "EmulatedCamera2.h" +#include "fake-pipeline2/Base.h" #include "fake-pipeline2/Sensor.h" +#include "fake-pipeline2/JpegCompressor.h" #include +#include #include namespace android { @@ -69,7 +72,7 @@ protected: virtual int requestQueueNotify(); /** Count of requests in flight */ - //virtual int getInProgressCount(); + virtual int getInProgressCount(); /** Cancel all captures in flight */ //virtual int flushCapturesInProgress(); @@ -121,7 +124,13 @@ protected: virtual int dump(int fd); - /** Methods for worker threads to call */ +public: + /**************************************************************************** + * Utility methods called by configure/readout threads and pipeline + ***************************************************************************/ + + // Get information about a given stream. Will lock mMutex + const Stream &getStreamInfo(uint32_t streamId); // Notifies rest of camera subsystem of serious error void signalError(); @@ -133,15 +142,15 @@ private: /** Construct static camera metadata, two-pass */ status_t constructStaticInfo( camera_metadata_t **info, - bool sizeRequest); + bool sizeRequest) const; /** Two-pass implementation of constructDefaultRequest */ status_t constructDefaultRequest( int request_template, camera_metadata_t **request, - bool sizeRequest); + bool sizeRequest) const; /** Helper function for constructDefaultRequest */ - status_t addOrSize( camera_metadata_t *request, + static status_t addOrSize( camera_metadata_t *request, bool sizeRequest, size_t *entryCount, size_t *dataCount, @@ -149,6 +158,10 @@ private: const void *entry_data, size_t entry_count); + /** Determine if the stream id is listed in any currently-in-flight + * requests. Assumes mMutex is locked */ + bool isStreamInUse(uint32_t streamId); + /**************************************************************************** * Pipeline controller threads ***************************************************************************/ @@ -161,6 +174,9 @@ private: status_t waitUntilRunning(); status_t newRequestAvailable(); status_t readyToRun(); + + bool isStreamInUse(uint32_t id); + int getInProgressCount(); private: EmulatedFakeCamera2 *mParent; @@ -173,12 +189,14 @@ private: // working on them camera_metadata_t *mRequest; + + Mutex mInternalsMutex; // Lock before accessing below members. + bool mNextNeedsJpeg; int32_t mNextFrameNumber; int64_t mNextExposureTime; int64_t mNextFrameDuration; int32_t mNextSensitivity; - buffer_handle_t *mNextBuffer; - int mNextBufferStride; + Buffers *mNextBuffers; }; class ReadoutThread: public Thread { @@ -191,8 +209,10 @@ private: // Input status_t waitUntilRunning(); void setNextCapture(camera_metadata_t *request, - buffer_handle_t *buffer); + Buffers *buffers); + bool isStreamInUse(uint32_t id); + int getInProgressCount(); private: EmulatedFakeCamera2 *mParent; @@ -207,15 +227,16 @@ private: static const int kInFlightQueueSize = 4; struct InFlightQueue { camera_metadata_t *request; - buffer_handle_t *buffer; + Buffers *buffers; } *mInFlightQueue; - int mInFlightHead; - int mInFlightTail; + size_t mInFlightHead; + size_t mInFlightTail; // Internals + Mutex mInternalsMutex; camera_metadata_t *mRequest; - buffer_handle_t *mBuffer; + Buffers *mBuffers; }; @@ -223,6 +244,9 @@ private: * Static configuration information ***************************************************************************/ private: + static const uint32_t kMaxRawStreamCount = 1; + static const uint32_t kMaxProcessedStreamCount = 3; + static const uint32_t kMaxJpegStreamCount = 1; static const uint32_t kAvailableFormats[]; static const uint32_t kAvailableRawSizes[]; static const uint64_t kAvailableRawMinDurations[]; @@ -245,11 +269,15 @@ private: /** Stream manipulation */ uint32_t mNextStreamId; - const camera2_stream_ops_t *mRawStreamOps; - uint32_t mStreamFormat; + uint32_t mRawStreamCount; + uint32_t mProcessedStreamCount; + uint32_t mJpegStreamCount; + + KeyedVector mStreams; /** Simulated hardware interfaces */ sp mSensor; + sp mJpegCompressor; /** Pipeline control threads */ sp mConfigureThread; diff --git a/tools/emulator/system/camera/fake-pipeline2/Base.h b/tools/emulator/system/camera/fake-pipeline2/Base.h new file mode 100644 index 000000000..f6ee5db39 --- /dev/null +++ b/tools/emulator/system/camera/fake-pipeline2/Base.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +/** + * This file includes various basic structures that are needed by multiple parts + * of the fake camera 2 implementation. + */ + +#ifndef HW_EMULATOR_CAMERA2_BASE_H +#define HW_EMULATOR_CAMERA2_BASE_H + +#include +#include +#include + +namespace android { + + +/* Internal structure for passing buffers across threads */ +struct StreamBuffer { + int streamId; + uint32_t width, height; + uint32_t format; + uint32_t stride; + buffer_handle_t *buffer; + uint8_t *img; +}; +typedef Vector Buffers; + +struct Stream { + uint32_t id; + const camera2_stream_ops_t *ops; + uint32_t width, height; + uint32_t format; + uint32_t stride; +}; + +} // namespace android; + +#endif diff --git a/tools/emulator/system/camera/fake-pipeline2/JpegCompressor.cpp b/tools/emulator/system/camera/fake-pipeline2/JpegCompressor.cpp new file mode 100644 index 000000000..76fbb94a0 --- /dev/null +++ b/tools/emulator/system/camera/fake-pipeline2/JpegCompressor.cpp @@ -0,0 +1,256 @@ +/* + * 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 +#include + +#include "JpegCompressor.h" +#include "../EmulatedFakeCamera2.h" + +namespace android { + +JpegCompressor::JpegCompressor(EmulatedFakeCamera2 *parent): + Thread(false), + mIsBusy(false), + mParent(parent), + mBuffers(NULL), + mCaptureTime(0) { +} + +JpegCompressor::~JpegCompressor() { + Mutex::Autolock lock(mMutex); +} + +status_t JpegCompressor::start(Buffers *buffers, + nsecs_t captureTime) { + Mutex::Autolock lock(mMutex); + { + Mutex::Autolock busyLock(mBusyMutex); + + if (mIsBusy) { + ALOGE("%s: Already processing a buffer!", __FUNCTION__); + return INVALID_OPERATION; + } + + mIsBusy = true; + + mBuffers = buffers; + mCaptureTime = captureTime; + } + + 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::cancel() { + requestExitAndWait(); + return OK; +} + +status_t JpegCompressor::readyToRun() { + return OK; +} + +bool JpegCompressor::threadLoop() { + Mutex::Autolock lock(mMutex); + ALOGV("%s: Starting compression thread", __FUNCTION__); + + // Find source and target buffers + + bool foundJpeg = false, mFoundAux = false; + 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 == -1) { + mAuxBuffer = b; + mFoundAux = true; + } + if (mFoundJpeg && mFoundAux) break; + } + if (!mFoundJpeg || !mFoundAux) { + ALOGE("%s: Unable to find buffers for JPEG source/destination", + __FUNCTION__); + cleanUp(); + return false; + } + + // Set up error management + + mJpegErrorInfo = NULL; + JpegError error; + error.parent = this; + + mCInfo.err = jpeg_std_error(&error); + mCInfo.err->error_exit = jpegErrorHandler; + + jpeg_create_compress(&mCInfo); + if (checkError("Error initializing compression")) return false; + + // Route compressed data straight to output stream buffer + + JpegDestination jpegDestMgr; + jpegDestMgr.parent = this; + jpegDestMgr.init_destination = jpegInitDestination; + jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer; + jpegDestMgr.term_destination = jpegTermDestination; + + mCInfo.dest = &jpegDestMgr; + + // Set up compression parameters + + mCInfo.image_width = mAuxBuffer.width; + mCInfo.image_height = mAuxBuffer.height; + mCInfo.input_components = 3; + mCInfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&mCInfo); + if (checkError("Error configuring defaults")) return false; + + // Do compression + + jpeg_start_compress(&mCInfo, TRUE); + if (checkError("Error starting compression")) return false; + + size_t rowStride = mAuxBuffer.stride * 3; + const size_t kChunkSize = 32; + while (mCInfo.next_scanline < mCInfo.image_height) { + JSAMPROW chunk[kChunkSize]; + for (size_t i = 0 ; i < kChunkSize; i++) { + chunk[i] = (JSAMPROW) + (mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride); + } + jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); + if (checkError("Error while compressing")) return false; + if (exitPending()) { + ALOGV("%s: Cancel called, exiting early", __FUNCTION__); + cleanUp(); + return false; + } + } + + jpeg_finish_compress(&mCInfo); + if (checkError("Error while finishing compression")) return false; + + // Write to JPEG output stream + + ALOGV("%s: Compression complete, pushing to stream %d", __FUNCTION__, + mJpegBuffer.streamId); + + GraphicBufferMapper::get().unlock(*(mJpegBuffer.buffer)); + status_t res; + const Stream &s = mParent->getStreamInfo(mJpegBuffer.streamId); + res = s.ops->enqueue_buffer(s.ops, mCaptureTime, mJpegBuffer.buffer); + if (res != OK) { + ALOGE("%s: Error queueing compressed image buffer %p: %s (%d)", + __FUNCTION__, mJpegBuffer.buffer, strerror(-res), res); + mParent->signalError(); + } + + // All done + + cleanUp(); + + return false; +} + +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); + status_t res = OK; + if (mIsBusy) { + res = mDone.waitRelative(mBusyMutex, timeout); + } + return (res == OK); +} + +bool JpegCompressor::checkError(const char *msg) { + if (mJpegErrorInfo) { + char errBuffer[JMSG_LENGTH_MAX]; + mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer); + ALOGE("%s: %s: %s", + __FUNCTION__, msg, errBuffer); + cleanUp(); + mJpegErrorInfo = NULL; + return true; + } + return false; +} + +void JpegCompressor::cleanUp() { + jpeg_destroy_compress(&mCInfo); + Mutex::Autolock lock(mBusyMutex); + + if (mFoundAux) { + delete[] mAuxBuffer.img; + } + delete mBuffers; + mBuffers = NULL; + + mIsBusy = false; + mDone.signal(); +} + +void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) { + JpegError *error = static_cast(cinfo->err); + error->parent->mJpegErrorInfo = cinfo; +} + +void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) { + JpegDestination *dest= static_cast(cinfo->dest); + ALOGV("%s: Setting destination to %p, size %d", + __FUNCTION__, dest->parent->mJpegBuffer.img, kMaxJpegSize); + dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer.img); + dest->free_in_buffer = kMaxJpegSize; +} + +boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr cinfo) { + ALOGE("%s: JPEG destination buffer overflow!", + __FUNCTION__); + return true; +} + +void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) { + ALOGV("%s: Done writing JPEG data. %d bytes left in buffer", + __FUNCTION__, cinfo->dest->free_in_buffer); +} + +} // namespace android diff --git a/tools/emulator/system/camera/fake-pipeline2/JpegCompressor.h b/tools/emulator/system/camera/fake-pipeline2/JpegCompressor.h new file mode 100644 index 000000000..ea2a84f01 --- /dev/null +++ b/tools/emulator/system/camera/fake-pipeline2/JpegCompressor.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + + +/** + * This class simulates a hardware JPEG compressor. It receives image buffers + * in RGBA_8888 format, processes them in a worker thread, and then pushes them + * out to their destination stream. + */ + +#ifndef HW_EMULATOR_CAMERA2_JPEG_H +#define HW_EMULATOR_CAMERA2_JPEG_H + +#include "utils/Thread.h" +#include "utils/Mutex.h" +#include "utils/Timers.h" + +#include "Base.h" + +#include + +extern "C" { +#include +} + +namespace android { + +class EmulatedFakeCamera2; + +class JpegCompressor: private Thread, public virtual RefBase { + public: + + JpegCompressor(EmulatedFakeCamera2 *parent); + ~JpegCompressor(); + + // Start compressing COMPRESSED format buffers; JpegCompressor takes + // ownership of the Buffers vector. + status_t start(Buffers *buffers, + nsecs_t captureTime); + + status_t cancel(); + + bool isBusy(); + bool isStreamInUse(uint32_t id); + + bool waitForDone(nsecs_t timeout); + + // TODO: Measure this + static const size_t kMaxJpegSize = 300000; + + private: + Mutex mBusyMutex; + bool mIsBusy; + Condition mDone; + + Mutex mMutex; + + EmulatedFakeCamera2 *mParent; + + Buffers *mBuffers; + nsecs_t mCaptureTime; + + StreamBuffer mJpegBuffer, mAuxBuffer; + bool mFoundJpeg, mFoundAux; + + jpeg_compress_struct mCInfo; + + struct JpegError : public jpeg_error_mgr { + JpegCompressor *parent; + }; + j_common_ptr mJpegErrorInfo; + + struct JpegDestination : public jpeg_destination_mgr { + JpegCompressor *parent; + }; + + static void jpegErrorHandler(j_common_ptr cinfo); + + static void jpegInitDestination(j_compress_ptr cinfo); + static boolean jpegEmptyOutputBuffer(j_compress_ptr cinfo); + static void jpegTermDestination(j_compress_ptr cinfo); + + bool checkError(const char *msg); + void cleanUp(); + + /** + * Inherited Thread virtual overrides + */ + private: + virtual status_t readyToRun(); + virtual bool threadLoop(); +}; + +} // namespace android + +#endif diff --git a/tools/emulator/system/camera/fake-pipeline2/Sensor.cpp b/tools/emulator/system/camera/fake-pipeline2/Sensor.cpp index bd4a6562a..29898b840 100644 --- a/tools/emulator/system/camera/fake-pipeline2/Sensor.cpp +++ b/tools/emulator/system/camera/fake-pipeline2/Sensor.cpp @@ -26,6 +26,7 @@ #include +#include "../EmulatedFakeCamera2.h" #include "Sensor.h" #include #include @@ -98,14 +99,15 @@ float sqrtf_approx(float r) { -Sensor::Sensor(): +Sensor::Sensor(EmulatedFakeCamera2 *parent): Thread(false), + mParent(parent), mGotVSync(false), mExposureTime(kFrameDurationRange[0]-kMinVerticalBlank), mFrameDuration(kFrameDurationRange[0]), mGainFactor(kDefaultSensitivity), - mNextBuffer(NULL), - mCapturedBuffer(NULL), + mNextBuffers(NULL), + mCapturedBuffers(NULL), mScene(kResolution[0], kResolution[1], kElectronsPerLuxSecond) { @@ -119,12 +121,7 @@ status_t Sensor::startUp() { ALOGV("%s: E", __FUNCTION__); int res; - mCapturedBuffer = NULL; - res = readyToRun(); - if (res != OK) { - ALOGE("Unable to prepare sensor capture thread to run: %d", res); - return res; - } + mCapturedBuffers = NULL; res = run("EmulatedFakeCamera2::Sensor", ANDROID_PRIORITY_URGENT_DISPLAY); @@ -167,12 +164,9 @@ void Sensor::setSensitivity(uint32_t gain) { mGainFactor = gain; } -void Sensor::setDestinationBuffer(uint8_t *buffer, - uint32_t format, uint32_t stride) { +void Sensor::setDestinationBuffers(Buffers *buffers) { Mutex::Autolock lock(mControlMutex); - mNextBuffer = buffer; - mNextBufferFmt = format; - mNextStride = stride; + mNextBuffers = buffers; } bool Sensor::waitForVSync(nsecs_t reltime) { @@ -192,18 +186,18 @@ bool Sensor::waitForNewFrame(nsecs_t reltime, nsecs_t *captureTime) { Mutex::Autolock lock(mReadoutMutex); uint8_t *ret; - if (mCapturedBuffer == NULL) { + if (mCapturedBuffers == NULL) { int res; res = mReadoutComplete.waitRelative(mReadoutMutex, reltime); if (res == TIMED_OUT) { return false; - } else if (res != OK || mCapturedBuffer == NULL) { + } else if (res != OK || mCapturedBuffers == NULL) { ALOGE("Error waiting for sensor readout signal: %d", res); return false; } } *captureTime = mCaptureTime; - mCapturedBuffer = NULL; + mCapturedBuffers = NULL; return true; } @@ -211,7 +205,7 @@ status_t Sensor::readyToRun() { ALOGV("Starting up sensor thread"); mStartupTime = systemTime(); mNextCaptureTime = 0; - mNextCapturedBuffer = NULL; + mNextCapturedBuffers = NULL; return OK; } @@ -229,19 +223,15 @@ bool Sensor::threadLoop() { uint64_t exposureDuration; uint64_t frameDuration; uint32_t gain; - uint8_t *nextBuffer; - uint32_t nextBufferFmt; - uint32_t stride; + Buffers *nextBuffers; { Mutex::Autolock lock(mControlMutex); exposureDuration = mExposureTime; frameDuration = mFrameDuration; gain = mGainFactor; - nextBuffer = mNextBuffer; - nextBufferFmt = mNextBufferFmt; - stride = mNextStride; - // Don't reuse a buffer - mNextBuffer = NULL; + nextBuffers = mNextBuffers; + // Don't reuse a buffer set + mNextBuffers = NULL; // Signal VSync for start of readout ALOGVV("Sensor VSync"); @@ -253,7 +243,7 @@ bool Sensor::threadLoop() { * Stage 3: Read out latest captured image */ - uint8_t *capturedBuffer = NULL; + Buffers *capturedBuffers = NULL; nsecs_t captureTime = 0; nsecs_t startRealTime = systemTime(); @@ -262,52 +252,78 @@ bool Sensor::threadLoop() { nsecs_t frameReadoutEndRealTime = startRealTime + kRowReadoutTime * kResolution[1]; - if (mNextCapturedBuffer != NULL) { + if (mNextCapturedBuffers != NULL) { ALOGVV("Sensor starting readout"); // Pretend we're doing readout now; will signal once enough time has elapsed - capturedBuffer = mNextCapturedBuffer; + capturedBuffers = mNextCapturedBuffers; captureTime = mNextCaptureTime; } simulatedTime += kRowReadoutTime + kMinVerticalBlank; + // TODO: Move this signal to another thread to simulate readout + // time properly + if (capturedBuffers != NULL) { + ALOGVV("Sensor readout complete"); + Mutex::Autolock lock(mReadoutMutex); + mCapturedBuffers = capturedBuffers; + mCaptureTime = captureTime; + mReadoutComplete.signal(); + capturedBuffers = NULL; + } + /** * Stage 2: Capture new image */ mNextCaptureTime = simulatedTime; - mNextCapturedBuffer = nextBuffer; + mNextCapturedBuffers = nextBuffers; - if (mNextCapturedBuffer != NULL) { - ALOGVV("Sensor capturing image (%d x %d) stride %d", - kResolution[0], kResolution[1], stride); - ALOGVV("Exposure: %f ms, gain: %d", (float)exposureDuration/1e6, gain); + if (mNextCapturedBuffers != NULL) { + ALOGVV("Starting next capture: Exposure: %f ms, gain: %d", + (float)exposureDuration/1e6, gain); mScene.setExposureDuration((float)exposureDuration/1e9); mScene.calculateScene(mNextCaptureTime); - - switch(nextBufferFmt) { - case HAL_PIXEL_FORMAT_RAW_SENSOR: - captureRaw(gain, stride, &capturedBuffer, - captureTime, frameEndRealTime); - break; - case HAL_PIXEL_FORMAT_RGBA_8888: - captureRGBA(gain, stride, &capturedBuffer, - captureTime, frameEndRealTime); - break; - default: - ALOGE("%s: Unknown format %x, no output", __FUNCTION__, - nextBufferFmt); - break; + for (size_t i = 0; i < mNextCapturedBuffers->size(); i++) { + const StreamBuffer &b = (*mNextCapturedBuffers)[i]; + ALOGVV("Sensor capturing buffer %d: stream %d," + " %d x %d, format %x, stride %d, buf %p, img %p", + i, b.streamId, b.width, b.height, b.format, b.stride, + b.buffer, b.img); + switch(b.format) { + case HAL_PIXEL_FORMAT_RAW_SENSOR: + captureRaw(b.img, gain, b.stride); + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + captureRGBA(b.img, gain, b.stride); + break; + case HAL_PIXEL_FORMAT_BLOB: + // Add auxillary buffer of the right size + // Assumes only one BLOB (JPEG) buffer in + // mNextCapturedBuffers + StreamBuffer bAux; + bAux.streamId = -1; + bAux.width = b.width; + bAux.height = b.height; + bAux.format = HAL_PIXEL_FORMAT_RGB_888; + bAux.stride = b.width; + bAux.buffer = NULL; + // TODO: Reuse these + bAux.img = new uint8_t[b.width * b.height * 3]; + captureRGB(bAux.img, gain, b.stride); + mNextCapturedBuffers->push_back(bAux); + break; + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + // TODO: + ALOGE("%s: Format %x is TODO", __FUNCTION__, b.format); + break; + default: + ALOGE("%s: Unknown format %x, no output", __FUNCTION__, + b.format); + break; + } } } - // No capture done, or finished image generation before readout was completed - if (capturedBuffer != NULL) { - ALOGVV("Sensor readout complete"); - Mutex::Autolock lock(mReadoutMutex); - mCapturedBuffer = capturedBuffer; - mCaptureTime = captureTime; - mReadoutComplete.signal(); - capturedBuffer = NULL; - } ALOGVV("Sensor vertical blanking interval"); nsecs_t workDoneRealTime = systemTime(); @@ -329,18 +345,17 @@ bool Sensor::threadLoop() { return true; }; -void Sensor::captureRaw(uint32_t gain, uint32_t stride, - uint8_t **capturedBuffer, nsecs_t captureTime, nsecs_t frameReadoutTime) { +void Sensor::captureRaw(uint8_t *img, uint32_t gain, uint32_t stride) { float totalGain = gain/100.0 * kBaseGainFactor; float noiseVarGain = totalGain * totalGain; float readNoiseVar = kReadNoiseVarBeforeGain * noiseVarGain + kReadNoiseVarAfterGain; int bayerSelect[4] = {Scene::R, Scene::Gr, Scene::Gb, Scene::B}; // RGGB - + mScene.setReadoutPixel(0,0); for (unsigned int y = 0; y < kResolution[1]; y++ ) { int *bayerRow = bayerSelect + (y & 0x1) * 2; - uint16_t *px = (uint16_t*)mNextCapturedBuffer + y * stride; + uint16_t *px = (uint16_t*)img + y * stride; for (unsigned int x = 0; x < kResolution[0]; x++) { uint32_t electronCount; electronCount = mScene.getPixelElectrons()[bayerRow[x & 0x1]]; @@ -367,59 +382,61 @@ void Sensor::captureRaw(uint32_t gain, uint32_t stride, } // TODO: Handle this better //simulatedTime += kRowReadoutTime; - - // If enough time has elapsed to complete readout, signal done frame - // Only check every so often, though - if ((*capturedBuffer != NULL) && - ((y & 63) == 0) && - (systemTime() >= frameReadoutTime) ) { - ALOGV("Sensor readout complete"); - Mutex::Autolock lock(mReadoutMutex); - mCapturedBuffer = *capturedBuffer; - mCaptureTime = captureTime; - mReadoutComplete.signal(); - *capturedBuffer = NULL; - } } ALOGVV("Raw sensor image captured"); } -void Sensor::captureRGBA(uint32_t gain, uint32_t stride, - uint8_t **capturedBuffer, nsecs_t captureTime, nsecs_t frameReadoutTime) { - int totalGain = gain/100.0 * kBaseGainFactor; +void Sensor::captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride) { + float totalGain = gain/100.0 * kBaseGainFactor; + // In fixed-point math, calculate total scaling from electrons to 8bpp + int scale64x = 64 * totalGain * 255 / kMaxRawValue; + mScene.setReadoutPixel(0,0); for (unsigned int y = 0; y < kResolution[1]; y++ ) { - uint8_t *px = (uint8_t*)mNextCapturedBuffer + y * stride * 4; + uint8_t *px = img + y * stride * 4; for (unsigned int x = 0; x < kResolution[0]; x++) { uint32_t rCount, gCount, bCount; // TODO: Perfect demosaicing is a cheat const uint32_t *pixel = mScene.getPixelElectrons(); - rCount = pixel[Scene::R] * totalGain / (kMaxRawValue / 255); - gCount = pixel[Scene::Gr] * totalGain / (kMaxRawValue / 255); - bCount = pixel[Scene::B] * totalGain / (kMaxRawValue / 255); + rCount = pixel[Scene::R] * scale64x; + gCount = pixel[Scene::Gr] * scale64x; + bCount = pixel[Scene::B] * scale64x; - *px++ = rCount < 255 ? rCount : 255; - *px++ = gCount < 255 ? gCount : 255; - *px++ = bCount < 255 ? bCount : 255; + *px++ = rCount < 255*64 ? rCount / 64 : 255; + *px++ = gCount < 255*64 ? gCount / 64 : 255; + *px++ = bCount < 255*64 ? bCount / 64 : 255; *px++ = 255; } // TODO: Handle this better //simulatedTime += kRowReadoutTime; - - // If enough time has elapsed to complete readout, signal done frame - // Only check every so often, though - if ((*capturedBuffer != NULL) && - ((y & 63) == 0) && - (systemTime() >= frameReadoutTime) ) { - ALOGV("Sensor readout complete"); - Mutex::Autolock lock(mReadoutMutex); - mCapturedBuffer = *capturedBuffer; - mCaptureTime = captureTime; - mReadoutComplete.signal(); - *capturedBuffer = NULL; - } } ALOGVV("RGBA sensor image captured"); } +void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) { + float totalGain = gain/100.0 * kBaseGainFactor; + // In fixed-point math, calculate total scaling from electrons to 8bpp + int scale64x = 64 * totalGain * 255 / kMaxRawValue; + mScene.setReadoutPixel(0,0); + + for (unsigned int y = 0; y < kResolution[1]; y++ ) { + uint8_t *px = img + y * stride * 3; + for (unsigned int x = 0; x < kResolution[0]; x++) { + uint32_t rCount, gCount, bCount; + // TODO: Perfect demosaicing is a cheat + const uint32_t *pixel = mScene.getPixelElectrons(); + rCount = pixel[Scene::R] * scale64x; + gCount = pixel[Scene::Gr] * scale64x; + bCount = pixel[Scene::B] * scale64x; + + *px++ = rCount < 255*64 ? rCount / 64 : 255; + *px++ = gCount < 255*64 ? gCount / 64 : 255; + *px++ = bCount < 255*64 ? bCount / 64 : 255; + } + // TODO: Handle this better + //simulatedTime += kRowReadoutTime; + } + ALOGVV("RGB sensor image captured"); +} + } // namespace android diff --git a/tools/emulator/system/camera/fake-pipeline2/Sensor.h b/tools/emulator/system/camera/fake-pipeline2/Sensor.h index 50ec2b744..a666cc387 100644 --- a/tools/emulator/system/camera/fake-pipeline2/Sensor.h +++ b/tools/emulator/system/camera/fake-pipeline2/Sensor.h @@ -78,13 +78,16 @@ #include "utils/Timers.h" #include "Scene.h" +#include "Base.h" namespace android { +class EmulatedFakeCamera2; + class Sensor: private Thread, public virtual RefBase { public: - Sensor(); + Sensor(EmulatedFakeCamera2 *parent); ~Sensor(); /* @@ -107,7 +110,7 @@ class Sensor: private Thread, public virtual RefBase { void setFrameDuration(uint64_t ns); void setSensitivity(uint32_t gain); // Buffer must be at least stride*height*2 bytes in size - void setDestinationBuffer(uint8_t *buffer, uint32_t format, uint32_t stride); + void setDestinationBuffers(Buffers *buffers); /* * Controls that cause reconfiguration delay @@ -169,6 +172,7 @@ class Sensor: private Thread, public virtual RefBase { static const uint32_t kDefaultSensitivity; private: + EmulatedFakeCamera2 *mParent; Mutex mControlMutex; // Lock before accessing control parameters // Start of control parameters @@ -177,16 +181,14 @@ class Sensor: private Thread, public virtual RefBase { uint64_t mExposureTime; uint64_t mFrameDuration; uint32_t mGainFactor; - uint8_t *mNextBuffer; - uint32_t mNextBufferFmt; - uint32_t mNextStride; + Buffers *mNextBuffers; // End of control parameters Mutex mReadoutMutex; // Lock before accessing readout variables // Start of readout variables Condition mReadoutComplete; - uint8_t *mCapturedBuffer; + Buffers *mCapturedBuffers; nsecs_t mCaptureTime; // End of readout variables @@ -203,17 +205,13 @@ class Sensor: private Thread, public virtual RefBase { virtual bool threadLoop(); nsecs_t mNextCaptureTime; - uint8_t *mNextCapturedBuffer; + Buffers *mNextCapturedBuffers; Scene mScene; - void captureRaw(uint32_t gain, uint32_t stride, - uint8_t **capturedBuffer, nsecs_t captureTime, - nsecs_t frameReadoutTime); - - void captureRGBA(uint32_t gain, uint32_t stride, - uint8_t **capturedBuffer, nsecs_t captureTime, - nsecs_t frameReadoutTime); + void captureRaw(uint8_t *img, uint32_t gain, uint32_t stride); + void captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride); + void captureRGB(uint8_t *img, uint32_t gain, uint32_t stride); };