summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/camera/libcameraservice/Android.mk3
-rw-r--r--services/camera/libcameraservice/Camera2Client.cpp87
-rw-r--r--services/camera/libcameraservice/Camera2Client.h8
-rw-r--r--services/camera/libcameraservice/MediaConsumer.cpp196
-rw-r--r--services/camera/libcameraservice/MediaConsumer.h126
5 files changed, 390 insertions, 30 deletions
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 8cccf49..c14ae22 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -10,7 +10,8 @@ LOCAL_SRC_FILES:= \
CameraService.cpp \
CameraClient.cpp \
Camera2Client.cpp \
- Camera2Device.cpp
+ Camera2Device.cpp \
+ MediaConsumer.cpp
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index f21a518..2e1940a 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -24,6 +24,7 @@
#include <cutils/properties.h>
#include <gui/SurfaceTextureClient.h>
#include <gui/Surface.h>
+#include <media/hardware/MetadataBufferType.h>
#include <math.h>
@@ -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() {
@@ -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)",
@@ -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 "
@@ -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++;
}
@@ -1519,30 +1560,21 @@ void Camera2Client::onRecordingFrameAvailable() {
discardData = true;
}
- CpuConsumer::LockedBuffer imgBuffer;
- res = mRecordingConsumer->lockNextBuffer(&imgBuffer);
+ buffer_handle_t imgBuffer;
+ res = mRecordingConsumer->getNextBuffer(&imgBuffer, &timestamp);
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);
@@ -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--;
@@ -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
@@ -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);
@@ -2329,6 +2361,9 @@ status_t Camera2Client::buildDefaultParameters() {
CameraParameters::FALSE);
}
+ // Always use metadata mode for recording
+ mParameters.storeMetadataInBuffers = true;
+
mParamsFlattened = params.flatten();
return OK;
@@ -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(
@@ -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);
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 8d410f1..d7836ca 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -23,6 +23,7 @@
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <gui/CpuConsumer.h>
+#include "MediaConsumer.h"
namespace android {
@@ -170,6 +171,8 @@ private:
bool recordingHint;
bool videoStabilization;
+
+ bool storeMetadataInBuffers;
} mParameters;
/** Camera device-related private members */
@@ -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(); }
@@ -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();
diff --git a/services/camera/libcameraservice/MediaConsumer.cpp b/services/camera/libcameraservice/MediaConsumer.cpp
new file mode 100644
index 0000000..0d857cf
--- /dev/null
+++ b/services/camera/libcameraservice/MediaConsumer.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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
diff --git a/services/camera/libcameraservice/MediaConsumer.h b/services/camera/libcameraservice/MediaConsumer.h
new file mode 100644
index 0000000..3377d94
--- /dev/null
+++ b/services/camera/libcameraservice/MediaConsumer.h
@@ -0,0 +1,126 @@
+/*
+ * 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