Loading services/camera/libcameraservice/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -10,7 +10,8 @@ LOCAL_SRC_FILES:= \ CameraService.cpp \ CameraClient.cpp \ Camera2Client.cpp \ Camera2Device.cpp Camera2Device.cpp \ MediaConsumer.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ Loading services/camera/libcameraservice/Camera2Client.cpp +61 −26 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <cutils/properties.h> #include <gui/SurfaceTextureClient.h> #include <gui/Surface.h> #include <media/hardware/MetadataBufferType.h> #include <math.h> Loading Loading @@ -611,7 +612,21 @@ bool Camera2Client::previewEnabled() { status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { ATRACE_CALL(); Mutex::Autolock icl(mICameraLock); return BAD_VALUE; switch (mState) { case RECORD: case VIDEO_SNAPSHOT: ALOGE("%s: Camera %d: Can't be called in state %s", __FUNCTION__, mCameraId, getStateName(mState)); return INVALID_OPERATION; default: // OK break; } Mutex::Autolock pl(mParamsLock); mParameters.storeMetadataInBuffers = enabled; return OK; } status_t Camera2Client::startRecording() { Loading Loading @@ -640,6 +655,13 @@ status_t Camera2Client::startRecording() { Mutex::Autolock pl(mParamsLock); if (!mParameters.storeMetadataInBuffers) { ALOGE("%s: Camera %d: Recording only supported in metadata mode, but " "non-metadata recording mode requested!", __FUNCTION__, mCameraId); return INVALID_OPERATION; } res = updateRecordingStream(); if (res != OK) { ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)", Loading Loading @@ -730,6 +752,7 @@ void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { // Make sure this is for the current heap ssize_t offset; size_t size; status_t res; sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) { ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release " Loading @@ -737,6 +760,24 @@ void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { heap->getHeapID(), mRecordingHeap->mHeap->getHeapID()); return; } uint8_t *data = (uint8_t*)heap->getBase() + offset; uint32_t type = *(uint32_t*)data; if (type != kMetadataBufferTypeGrallocSource) { ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)", __FUNCTION__, mCameraId, type, kMetadataBufferTypeGrallocSource); return; } buffer_handle_t imgBuffer = *(buffer_handle_t*)(data + 4); ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__, mCameraId, imgBuffer, *(uint32_t*)(data + 4)); res = mRecordingConsumer->freeBuffer(imgBuffer); if (res != OK) { ALOGE("%s: Camera %d: Unable to free recording frame (buffer_handle_t: %p):" "%s (%d)", __FUNCTION__, mCameraId, imgBuffer, strerror(-res), res); return; } mRecordingHeapFree++; } Loading Loading @@ -1519,30 +1560,21 @@ void Camera2Client::onRecordingFrameAvailable() { discardData = true; } CpuConsumer::LockedBuffer imgBuffer; res = mRecordingConsumer->lockNextBuffer(&imgBuffer); buffer_handle_t imgBuffer; res = mRecordingConsumer->getNextBuffer(&imgBuffer, ×tamp); if (res != OK) { ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); return; } if (imgBuffer.format != (int)kRecordingFormat) { ALOGE("%s: Camera %d: Unexpected recording format: %x", __FUNCTION__, mCameraId, imgBuffer.format); discardData = true; } if (discardData) { mRecordingConsumer->unlockBuffer(imgBuffer); mRecordingConsumer->freeBuffer(imgBuffer); return; } size_t bufferSize = imgBuffer.width * imgBuffer.height * 3 / 2; if (mRecordingHeap == 0 || bufferSize > mRecordingHeap->mHeap->getSize() / kRecordingHeapCount) { if (mRecordingHeap == 0) { const size_t bufferSize = 4 + sizeof(buffer_handle_t); ALOGV("%s: Camera %d: Creating recording heap with %d buffers of " "size %d bytes", __FUNCTION__, mCameraId, kRecordingHeapCount, bufferSize); Loading @@ -1560,22 +1592,20 @@ void Camera2Client::onRecordingFrameAvailable() { if (mRecordingHeap->mHeap->getSize() == 0) { ALOGE("%s: Camera %d: Unable to allocate memory for recording", __FUNCTION__, mCameraId); mRecordingConsumer->unlockBuffer(imgBuffer); mRecordingConsumer->freeBuffer(imgBuffer); return; } mRecordingHeapHead = 0; mRecordingHeapFree = kRecordingHeapCount; } // TODO: Optimize this to avoid memcopy if ( mRecordingHeapFree == 0) { ALOGE("%s: Camera %d: No free recording buffers, dropping frame", __FUNCTION__, mCameraId); mRecordingConsumer->unlockBuffer(imgBuffer); mRecordingConsumer->freeBuffer(imgBuffer); return; } heapIdx = mRecordingHeapHead; timestamp = imgBuffer.timestamp; mRecordingHeapHead = (mRecordingHeapHead + 1) % kRecordingHeapCount; mRecordingHeapFree--; Loading @@ -1588,10 +1618,12 @@ void Camera2Client::onRecordingFrameAvailable() { mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset, &size); memcpy((uint8_t*)heap->getBase() + offset, imgBuffer.data, size); mRecordingConsumer->unlockBuffer(imgBuffer); uint8_t *data = (uint8_t*)heap->getBase() + offset; uint32_t type = kMetadataBufferTypeGrallocSource; memcpy(data, &type, 4); memcpy(data + 4, &imgBuffer, sizeof(buffer_handle_t)); ALOGV("%s: Camera %d: Sending out buffer_handle_t %p", __FUNCTION__, mCameraId, imgBuffer, *(uint32_t*)(data + 4)); currentClient = mCameraClient; } // Call outside mICameraLock to allow re-entrancy from notification Loading Loading @@ -2306,7 +2338,7 @@ status_t Camera2Client::buildDefaultParameters() { 0); params.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT, formatEnumToString(kRecordingFormat)); CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE); params.set(CameraParameters::KEY_RECORDING_HINT, CameraParameters::FALSE); Loading @@ -2329,6 +2361,9 @@ status_t Camera2Client::buildDefaultParameters() { CameraParameters::FALSE); } // Always use metadata mode for recording mParameters.storeMetadataInBuffers = true; mParamsFlattened = params.flatten(); return OK; Loading Loading @@ -2580,7 +2615,7 @@ status_t Camera2Client::updateRecordingStream() { if (mRecordingConsumer == 0) { // Create CPU buffer queue endpoint mRecordingConsumer = new CpuConsumer(1); mRecordingConsumer = new MediaConsumer(4); mRecordingConsumer->setFrameAvailableListener(new RecordingWaiter(this)); mRecordingConsumer->setName(String8("Camera2Client::RecordingConsumer")); mRecordingWindow = new SurfaceTextureClient( Loading Loading @@ -2615,7 +2650,7 @@ status_t Camera2Client::updateRecordingStream() { if (mRecordingStreamId == NO_STREAM) { res = mDevice->createStream(mRecordingWindow, mParameters.videoWidth, mParameters.videoHeight, kRecordingFormat, 0, &mRecordingStreamId); CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for recording: " "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); Loading services/camera/libcameraservice/Camera2Client.h +5 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> #include <gui/CpuConsumer.h> #include "MediaConsumer.h" namespace android { Loading Loading @@ -170,6 +171,8 @@ private: bool recordingHint; bool videoStabilization; bool storeMetadataInBuffers; } mParameters; /** Camera device-related private members */ Loading Loading @@ -219,11 +222,11 @@ private: /* Recording related members */ int mRecordingStreamId; sp<CpuConsumer> mRecordingConsumer; sp<MediaConsumer> mRecordingConsumer; sp<ANativeWindow> mRecordingWindow; // Simple listener that forwards frame available notifications from // a CPU consumer to the recording notification class RecordingWaiter: public CpuConsumer::FrameAvailableListener { class RecordingWaiter: public MediaConsumer::FrameAvailableListener { public: RecordingWaiter(Camera2Client *parent) : mParent(parent) {} void onFrameAvailable() { mParent->onRecordingFrameAvailable(); } Loading @@ -237,7 +240,6 @@ private: // TODO: This needs to be queried from somewhere, or the BufferQueue needs // to be passed all the way to stagefright static const size_t kRecordingHeapCount = 4; static const uint32_t kRecordingFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; size_t mRecordingHeapHead, mRecordingHeapFree; // Handle new recording image buffers void onRecordingFrameAvailable(); Loading services/camera/libcameraservice/MediaConsumer.cpp 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * 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 "MediaConsumer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Log.h> #include "MediaConsumer.h" #define MC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) namespace android { // Get an ID that's unique within this process. static int32_t createProcessUniqueId() { static volatile int32_t globalCounter = 0; return android_atomic_inc(&globalCounter); } MediaConsumer::MediaConsumer(uint32_t maxLockedBuffers) : mMaxLockedBuffers(maxLockedBuffers), mCurrentLockedBuffers(0) { mName = String8::format("mc-unnamed-%d-%d", getpid(), createProcessUniqueId()); mBufferQueue = new BufferQueue(true); wp<BufferQueue::ConsumerListener> listener; sp<BufferQueue::ConsumerListener> proxy; listener = static_cast<BufferQueue::ConsumerListener*>(this); proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mBufferQueue->consumerConnect(proxy); if (err != NO_ERROR) { ALOGE("MediaConsumer: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { mBufferQueue->setSynchronousMode(true); mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER); mBufferQueue->setConsumerName(mName); } } MediaConsumer::~MediaConsumer() { Mutex::Autolock _l(mMutex); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } mBufferQueue->consumerDisconnect(); mBufferQueue.clear(); } void MediaConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; mBufferQueue->setConsumerName(name); } status_t MediaConsumer::getNextBuffer(buffer_handle_t *buffer, nsecs_t *timestamp) { status_t err; if (!buffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { return INVALID_OPERATION; } BufferQueue::BufferItem b; Mutex::Autolock _l(mMutex); err = mBufferQueue->acquireBuffer(&b); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; } else { MC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); return err; } } int buf = b.mBuf; if (b.mGraphicBuffer != NULL) { mBufferSlot[buf] = b.mGraphicBuffer; } if (b.mFence.get()) { err = b.mFence->wait(Fence::TIMEOUT_NEVER); if (err != OK) { MC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", strerror(-err), err); return err; } } *buffer = mBufferSlot[buf]->handle; *timestamp = b.mTimestamp; mCurrentLockedBuffers++; return OK; } status_t MediaConsumer::freeBuffer(buffer_handle_t buffer) { Mutex::Autolock _l(mMutex); int buf = 0; status_t err; for (; buf < BufferQueue::NUM_BUFFER_SLOTS; buf++) { if (buffer == mBufferSlot[buf]->handle) break; } if (buf == BufferQueue::NUM_BUFFER_SLOTS) { MC_LOGE("%s: Can't find buffer to free", __FUNCTION__); return BAD_VALUE; } err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); if (err == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(buf); } else if (err != OK) { MC_LOGE("%s: Unable to release graphic buffer %d to queue", __FUNCTION__, buf); return err; } mCurrentLockedBuffers--; return OK; } void MediaConsumer::setFrameAvailableListener( const sp<FrameAvailableListener>& listener) { MC_LOGV("setFrameAvailableListener"); Mutex::Autolock lock(mMutex); mFrameAvailableListener = listener; } void MediaConsumer::onFrameAvailable() { MC_LOGV("onFrameAvailable"); sp<FrameAvailableListener> listener; { // scope for the lock Mutex::Autolock _l(mMutex); listener = mFrameAvailableListener; } if (listener != NULL) { MC_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(); } } void MediaConsumer::onBuffersReleased() { MC_LOGV("onBuffersReleased"); Mutex::Autolock lock(mMutex); uint32_t mask = 0; mBufferQueue->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); } } } status_t MediaConsumer::freeBufferLocked(int buf) { status_t err = OK; mBufferSlot[buf] = NULL; return err; } } // namespace android services/camera/libcameraservice/MediaConsumer.h 0 → 100644 +126 −0 Original line number Diff line number Diff line /* * 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. */ #ifndef ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H #define ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H #include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> #include <utils/String8.h> #include <utils/Vector.h> #include <utils/threads.h> #define ANDROID_GRAPHICS_MEDIACONSUMER_JNI_ID "mMediaConsumer" namespace android { /** * MediaConsumer is a BufferQueue consumer endpoint that makes it * straightforward to bridge Camera 2 to the existing media recording framework. * This queue is synchronous by default. * * TODO: This is a temporary replacement for the full camera->media recording * path using SurfaceMediaEncoder or equivalent. */ class MediaConsumer: public virtual RefBase, protected BufferQueue::ConsumerListener { public: struct FrameAvailableListener : public virtual RefBase { // onFrameAvailable() is called each time an additional frame becomes // available for consumption. A new frame queued will always trigger the // callback, whether the queue is empty or not. // // This is called without any lock held and can be called concurrently // by multiple threads. virtual void onFrameAvailable() = 0; }; // Create a new media consumer. The maxBuffers parameter specifies // how many buffers can be locked for user access at the same time. MediaConsumer(uint32_t maxBuffers); virtual ~MediaConsumer(); // set the name of the MediaConsumer that will be used to identify it in // log messages. void setName(const String8& name); // Gets the next graphics buffer from the producer. Returns BAD_VALUE if no // new buffer is available, and INVALID_OPERATION if the maximum number of // buffers is already in use. // // Only a fixed number of buffers can be available at a time, determined by // the construction-time maxBuffers parameter. If INVALID_OPERATION is // returned by getNextBuffer, then old buffers must be returned to the // queue by calling freeBuffer before more buffers can be acquired. status_t getNextBuffer(buffer_handle_t *buffer, nsecs_t *timestamp); // Returns a buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be locked at a time, old buffers must // be released by calling unlockBuffer to ensure new buffers can be acquired by // lockNextBuffer. status_t freeBuffer(buffer_handle_t buffer); // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); sp<ISurfaceTexture> getProducerInterface() const { return mBufferQueue; } protected: // Implementation of the BufferQueue::ConsumerListener interface. These // calls are used to notify the MediaConsumer of asynchronous events in the // BufferQueue. virtual void onFrameAvailable(); virtual void onBuffersReleased(); private: // Free local buffer state status_t freeBufferLocked(int buf); // Maximum number of buffers that can be locked at a time uint32_t mMaxLockedBuffers; // mName is a string used to identify the SurfaceTexture in log messages. // It can be set by the setName method. String8 mName; // mFrameAvailableListener is the listener object that will be called when a // new frame becomes available. If it is not NULL it will be called from // queueBuffer. sp<FrameAvailableListener> mFrameAvailableListener; // Underlying buffer queue sp<BufferQueue> mBufferQueue; // Array for caching buffers from the buffer queue sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS]; // Count of currently outstanding buffers uint32_t mCurrentLockedBuffers; // mMutex is the mutex used to prevent concurrent access to the member // variables of MediaConsumer objects. It must be locked whenever the // member variables are accessed. mutable Mutex mMutex; }; } // namespace android #endif // ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H Loading
services/camera/libcameraservice/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -10,7 +10,8 @@ LOCAL_SRC_FILES:= \ CameraService.cpp \ CameraClient.cpp \ Camera2Client.cpp \ Camera2Device.cpp Camera2Device.cpp \ MediaConsumer.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ Loading
services/camera/libcameraservice/Camera2Client.cpp +61 −26 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <cutils/properties.h> #include <gui/SurfaceTextureClient.h> #include <gui/Surface.h> #include <media/hardware/MetadataBufferType.h> #include <math.h> Loading Loading @@ -611,7 +612,21 @@ bool Camera2Client::previewEnabled() { status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { ATRACE_CALL(); Mutex::Autolock icl(mICameraLock); return BAD_VALUE; switch (mState) { case RECORD: case VIDEO_SNAPSHOT: ALOGE("%s: Camera %d: Can't be called in state %s", __FUNCTION__, mCameraId, getStateName(mState)); return INVALID_OPERATION; default: // OK break; } Mutex::Autolock pl(mParamsLock); mParameters.storeMetadataInBuffers = enabled; return OK; } status_t Camera2Client::startRecording() { Loading Loading @@ -640,6 +655,13 @@ status_t Camera2Client::startRecording() { Mutex::Autolock pl(mParamsLock); if (!mParameters.storeMetadataInBuffers) { ALOGE("%s: Camera %d: Recording only supported in metadata mode, but " "non-metadata recording mode requested!", __FUNCTION__, mCameraId); return INVALID_OPERATION; } res = updateRecordingStream(); if (res != OK) { ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)", Loading Loading @@ -730,6 +752,7 @@ void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { // Make sure this is for the current heap ssize_t offset; size_t size; status_t res; sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) { ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release " Loading @@ -737,6 +760,24 @@ void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { heap->getHeapID(), mRecordingHeap->mHeap->getHeapID()); return; } uint8_t *data = (uint8_t*)heap->getBase() + offset; uint32_t type = *(uint32_t*)data; if (type != kMetadataBufferTypeGrallocSource) { ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)", __FUNCTION__, mCameraId, type, kMetadataBufferTypeGrallocSource); return; } buffer_handle_t imgBuffer = *(buffer_handle_t*)(data + 4); ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__, mCameraId, imgBuffer, *(uint32_t*)(data + 4)); res = mRecordingConsumer->freeBuffer(imgBuffer); if (res != OK) { ALOGE("%s: Camera %d: Unable to free recording frame (buffer_handle_t: %p):" "%s (%d)", __FUNCTION__, mCameraId, imgBuffer, strerror(-res), res); return; } mRecordingHeapFree++; } Loading Loading @@ -1519,30 +1560,21 @@ void Camera2Client::onRecordingFrameAvailable() { discardData = true; } CpuConsumer::LockedBuffer imgBuffer; res = mRecordingConsumer->lockNextBuffer(&imgBuffer); buffer_handle_t imgBuffer; res = mRecordingConsumer->getNextBuffer(&imgBuffer, ×tamp); if (res != OK) { ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); return; } if (imgBuffer.format != (int)kRecordingFormat) { ALOGE("%s: Camera %d: Unexpected recording format: %x", __FUNCTION__, mCameraId, imgBuffer.format); discardData = true; } if (discardData) { mRecordingConsumer->unlockBuffer(imgBuffer); mRecordingConsumer->freeBuffer(imgBuffer); return; } size_t bufferSize = imgBuffer.width * imgBuffer.height * 3 / 2; if (mRecordingHeap == 0 || bufferSize > mRecordingHeap->mHeap->getSize() / kRecordingHeapCount) { if (mRecordingHeap == 0) { const size_t bufferSize = 4 + sizeof(buffer_handle_t); ALOGV("%s: Camera %d: Creating recording heap with %d buffers of " "size %d bytes", __FUNCTION__, mCameraId, kRecordingHeapCount, bufferSize); Loading @@ -1560,22 +1592,20 @@ void Camera2Client::onRecordingFrameAvailable() { if (mRecordingHeap->mHeap->getSize() == 0) { ALOGE("%s: Camera %d: Unable to allocate memory for recording", __FUNCTION__, mCameraId); mRecordingConsumer->unlockBuffer(imgBuffer); mRecordingConsumer->freeBuffer(imgBuffer); return; } mRecordingHeapHead = 0; mRecordingHeapFree = kRecordingHeapCount; } // TODO: Optimize this to avoid memcopy if ( mRecordingHeapFree == 0) { ALOGE("%s: Camera %d: No free recording buffers, dropping frame", __FUNCTION__, mCameraId); mRecordingConsumer->unlockBuffer(imgBuffer); mRecordingConsumer->freeBuffer(imgBuffer); return; } heapIdx = mRecordingHeapHead; timestamp = imgBuffer.timestamp; mRecordingHeapHead = (mRecordingHeapHead + 1) % kRecordingHeapCount; mRecordingHeapFree--; Loading @@ -1588,10 +1618,12 @@ void Camera2Client::onRecordingFrameAvailable() { mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset, &size); memcpy((uint8_t*)heap->getBase() + offset, imgBuffer.data, size); mRecordingConsumer->unlockBuffer(imgBuffer); uint8_t *data = (uint8_t*)heap->getBase() + offset; uint32_t type = kMetadataBufferTypeGrallocSource; memcpy(data, &type, 4); memcpy(data + 4, &imgBuffer, sizeof(buffer_handle_t)); ALOGV("%s: Camera %d: Sending out buffer_handle_t %p", __FUNCTION__, mCameraId, imgBuffer, *(uint32_t*)(data + 4)); currentClient = mCameraClient; } // Call outside mICameraLock to allow re-entrancy from notification Loading Loading @@ -2306,7 +2338,7 @@ status_t Camera2Client::buildDefaultParameters() { 0); params.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT, formatEnumToString(kRecordingFormat)); CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE); params.set(CameraParameters::KEY_RECORDING_HINT, CameraParameters::FALSE); Loading @@ -2329,6 +2361,9 @@ status_t Camera2Client::buildDefaultParameters() { CameraParameters::FALSE); } // Always use metadata mode for recording mParameters.storeMetadataInBuffers = true; mParamsFlattened = params.flatten(); return OK; Loading Loading @@ -2580,7 +2615,7 @@ status_t Camera2Client::updateRecordingStream() { if (mRecordingConsumer == 0) { // Create CPU buffer queue endpoint mRecordingConsumer = new CpuConsumer(1); mRecordingConsumer = new MediaConsumer(4); mRecordingConsumer->setFrameAvailableListener(new RecordingWaiter(this)); mRecordingConsumer->setName(String8("Camera2Client::RecordingConsumer")); mRecordingWindow = new SurfaceTextureClient( Loading Loading @@ -2615,7 +2650,7 @@ status_t Camera2Client::updateRecordingStream() { if (mRecordingStreamId == NO_STREAM) { res = mDevice->createStream(mRecordingWindow, mParameters.videoWidth, mParameters.videoHeight, kRecordingFormat, 0, &mRecordingStreamId); CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for recording: " "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); Loading
services/camera/libcameraservice/Camera2Client.h +5 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> #include <gui/CpuConsumer.h> #include "MediaConsumer.h" namespace android { Loading Loading @@ -170,6 +171,8 @@ private: bool recordingHint; bool videoStabilization; bool storeMetadataInBuffers; } mParameters; /** Camera device-related private members */ Loading Loading @@ -219,11 +222,11 @@ private: /* Recording related members */ int mRecordingStreamId; sp<CpuConsumer> mRecordingConsumer; sp<MediaConsumer> mRecordingConsumer; sp<ANativeWindow> mRecordingWindow; // Simple listener that forwards frame available notifications from // a CPU consumer to the recording notification class RecordingWaiter: public CpuConsumer::FrameAvailableListener { class RecordingWaiter: public MediaConsumer::FrameAvailableListener { public: RecordingWaiter(Camera2Client *parent) : mParent(parent) {} void onFrameAvailable() { mParent->onRecordingFrameAvailable(); } Loading @@ -237,7 +240,6 @@ private: // TODO: This needs to be queried from somewhere, or the BufferQueue needs // to be passed all the way to stagefright static const size_t kRecordingHeapCount = 4; static const uint32_t kRecordingFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; size_t mRecordingHeapHead, mRecordingHeapFree; // Handle new recording image buffers void onRecordingFrameAvailable(); Loading
services/camera/libcameraservice/MediaConsumer.cpp 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * 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 "MediaConsumer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Log.h> #include "MediaConsumer.h" #define MC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) #define MC_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) namespace android { // Get an ID that's unique within this process. static int32_t createProcessUniqueId() { static volatile int32_t globalCounter = 0; return android_atomic_inc(&globalCounter); } MediaConsumer::MediaConsumer(uint32_t maxLockedBuffers) : mMaxLockedBuffers(maxLockedBuffers), mCurrentLockedBuffers(0) { mName = String8::format("mc-unnamed-%d-%d", getpid(), createProcessUniqueId()); mBufferQueue = new BufferQueue(true); wp<BufferQueue::ConsumerListener> listener; sp<BufferQueue::ConsumerListener> proxy; listener = static_cast<BufferQueue::ConsumerListener*>(this); proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mBufferQueue->consumerConnect(proxy); if (err != NO_ERROR) { ALOGE("MediaConsumer: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { mBufferQueue->setSynchronousMode(true); mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER); mBufferQueue->setConsumerName(mName); } } MediaConsumer::~MediaConsumer() { Mutex::Autolock _l(mMutex); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } mBufferQueue->consumerDisconnect(); mBufferQueue.clear(); } void MediaConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; mBufferQueue->setConsumerName(name); } status_t MediaConsumer::getNextBuffer(buffer_handle_t *buffer, nsecs_t *timestamp) { status_t err; if (!buffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { return INVALID_OPERATION; } BufferQueue::BufferItem b; Mutex::Autolock _l(mMutex); err = mBufferQueue->acquireBuffer(&b); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; } else { MC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); return err; } } int buf = b.mBuf; if (b.mGraphicBuffer != NULL) { mBufferSlot[buf] = b.mGraphicBuffer; } if (b.mFence.get()) { err = b.mFence->wait(Fence::TIMEOUT_NEVER); if (err != OK) { MC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", strerror(-err), err); return err; } } *buffer = mBufferSlot[buf]->handle; *timestamp = b.mTimestamp; mCurrentLockedBuffers++; return OK; } status_t MediaConsumer::freeBuffer(buffer_handle_t buffer) { Mutex::Autolock _l(mMutex); int buf = 0; status_t err; for (; buf < BufferQueue::NUM_BUFFER_SLOTS; buf++) { if (buffer == mBufferSlot[buf]->handle) break; } if (buf == BufferQueue::NUM_BUFFER_SLOTS) { MC_LOGE("%s: Can't find buffer to free", __FUNCTION__); return BAD_VALUE; } err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); if (err == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(buf); } else if (err != OK) { MC_LOGE("%s: Unable to release graphic buffer %d to queue", __FUNCTION__, buf); return err; } mCurrentLockedBuffers--; return OK; } void MediaConsumer::setFrameAvailableListener( const sp<FrameAvailableListener>& listener) { MC_LOGV("setFrameAvailableListener"); Mutex::Autolock lock(mMutex); mFrameAvailableListener = listener; } void MediaConsumer::onFrameAvailable() { MC_LOGV("onFrameAvailable"); sp<FrameAvailableListener> listener; { // scope for the lock Mutex::Autolock _l(mMutex); listener = mFrameAvailableListener; } if (listener != NULL) { MC_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(); } } void MediaConsumer::onBuffersReleased() { MC_LOGV("onBuffersReleased"); Mutex::Autolock lock(mMutex); uint32_t mask = 0; mBufferQueue->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); } } } status_t MediaConsumer::freeBufferLocked(int buf) { status_t err = OK; mBufferSlot[buf] = NULL; return err; } } // namespace android
services/camera/libcameraservice/MediaConsumer.h 0 → 100644 +126 −0 Original line number Diff line number Diff line /* * 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. */ #ifndef ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H #define ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H #include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> #include <utils/String8.h> #include <utils/Vector.h> #include <utils/threads.h> #define ANDROID_GRAPHICS_MEDIACONSUMER_JNI_ID "mMediaConsumer" namespace android { /** * MediaConsumer is a BufferQueue consumer endpoint that makes it * straightforward to bridge Camera 2 to the existing media recording framework. * This queue is synchronous by default. * * TODO: This is a temporary replacement for the full camera->media recording * path using SurfaceMediaEncoder or equivalent. */ class MediaConsumer: public virtual RefBase, protected BufferQueue::ConsumerListener { public: struct FrameAvailableListener : public virtual RefBase { // onFrameAvailable() is called each time an additional frame becomes // available for consumption. A new frame queued will always trigger the // callback, whether the queue is empty or not. // // This is called without any lock held and can be called concurrently // by multiple threads. virtual void onFrameAvailable() = 0; }; // Create a new media consumer. The maxBuffers parameter specifies // how many buffers can be locked for user access at the same time. MediaConsumer(uint32_t maxBuffers); virtual ~MediaConsumer(); // set the name of the MediaConsumer that will be used to identify it in // log messages. void setName(const String8& name); // Gets the next graphics buffer from the producer. Returns BAD_VALUE if no // new buffer is available, and INVALID_OPERATION if the maximum number of // buffers is already in use. // // Only a fixed number of buffers can be available at a time, determined by // the construction-time maxBuffers parameter. If INVALID_OPERATION is // returned by getNextBuffer, then old buffers must be returned to the // queue by calling freeBuffer before more buffers can be acquired. status_t getNextBuffer(buffer_handle_t *buffer, nsecs_t *timestamp); // Returns a buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be locked at a time, old buffers must // be released by calling unlockBuffer to ensure new buffers can be acquired by // lockNextBuffer. status_t freeBuffer(buffer_handle_t buffer); // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); sp<ISurfaceTexture> getProducerInterface() const { return mBufferQueue; } protected: // Implementation of the BufferQueue::ConsumerListener interface. These // calls are used to notify the MediaConsumer of asynchronous events in the // BufferQueue. virtual void onFrameAvailable(); virtual void onBuffersReleased(); private: // Free local buffer state status_t freeBufferLocked(int buf); // Maximum number of buffers that can be locked at a time uint32_t mMaxLockedBuffers; // mName is a string used to identify the SurfaceTexture in log messages. // It can be set by the setName method. String8 mName; // mFrameAvailableListener is the listener object that will be called when a // new frame becomes available. If it is not NULL it will be called from // queueBuffer. sp<FrameAvailableListener> mFrameAvailableListener; // Underlying buffer queue sp<BufferQueue> mBufferQueue; // Array for caching buffers from the buffer queue sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS]; // Count of currently outstanding buffers uint32_t mCurrentLockedBuffers; // mMutex is the mutex used to prevent concurrent access to the member // variables of MediaConsumer objects. It must be locked whenever the // member variables are accessed. mutable Mutex mMutex; }; } // namespace android #endif // ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H