summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/SurfaceMediaSource.cpp
diff options
context:
space:
mode:
authorDaniel Lam <dalam@google.com>2012-03-30 16:04:43 -0700
committerDaniel Lam <dalam@google.com>2012-04-09 22:58:28 -0700
commitbdddc659a941afdb7f4958f582c6901c07246097 (patch)
tree37423f7b734d26e086328f1f9b337e1499c026b8 /media/libstagefright/SurfaceMediaSource.cpp
parent3f502bfc6d04d08369cf147284c5c4c2ac7c9a0e (diff)
downloadframeworks_av-bdddc659a941afdb7f4958f582c6901c07246097.zip
frameworks_av-bdddc659a941afdb7f4958f582c6901c07246097.tar.gz
frameworks_av-bdddc659a941afdb7f4958f582c6901c07246097.tar.bz2
Refactored SurfaceMediaSource
SurfaceMediaSource takes advantage of BufferQueue to avoid duplicated code. Change-Id: I5e60b8eca21e6c3cf728d363cd8f3786125182d1
Diffstat (limited to 'media/libstagefright/SurfaceMediaSource.cpp')
-rw-r--r--media/libstagefright/SurfaceMediaSource.cpp772
1 files changed, 138 insertions, 634 deletions
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 1345cd9..efc71a8 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -35,545 +35,50 @@
namespace android {
-SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) :
- mDefaultWidth(bufW),
- mDefaultHeight(bufH),
- mPixelFormat(0),
- mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
- mClientBufferCount(0),
- mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
- mCurrentSlot(INVALID_BUFFER_SLOT),
- mCurrentTimestamp(0),
- mSynchronousMode(true),
- mConnectedApi(NO_CONNECTED_API),
- mFrameRate(30),
- mStopped(false),
- mNumFramesReceived(0),
- mNumFramesEncoded(0),
- mFirstFrameTimestamp(0) {
+SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeight) :
+ mWidth(bufferWidth),
+ mHeight(bufferHeight),
+ mCurrentSlot(BufferQueue::INVALID_BUFFER_SLOT),
+ mCurrentTimestamp(0),
+ mFrameRate(30),
+ mStopped(false),
+ mNumFramesReceived(0),
+ mNumFramesEncoded(0),
+ mFirstFrameTimestamp(0)
+{
ALOGV("SurfaceMediaSource::SurfaceMediaSource");
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
- if (mGraphicBufferAlloc == 0) {
- ALOGE("createGraphicBufferAlloc() failed in SurfaceMediaSource()");
- }
-}
-SurfaceMediaSource::~SurfaceMediaSource() {
- ALOGV("SurfaceMediaSource::~SurfaceMediaSource");
- if (!mStopped) {
- reset();
+ if (bufferWidth == 0 || bufferHeight == 0) {
+ ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
}
-}
-
-size_t SurfaceMediaSource::getQueuedCount() const {
- Mutex::Autolock lock(mMutex);
- return mQueue.size();
-}
-
-status_t SurfaceMediaSource::setBufferCountServerLocked(int bufferCount) {
- if (bufferCount > NUM_BUFFER_SLOTS)
- return BAD_VALUE;
- // special-case, nothing to do
- if (bufferCount == mBufferCount)
- return OK;
-
- if (!mClientBufferCount &&
- bufferCount >= mBufferCount) {
- // easy, we just have more buffers
- mBufferCount = bufferCount;
- mServerBufferCount = bufferCount;
- mDequeueCondition.signal();
- } else {
- // we're here because we're either
- // - reducing the number of available buffers
- // - or there is a client-buffer-count in effect
-
- // less than 2 buffers is never allowed
- if (bufferCount < 2)
- return BAD_VALUE;
-
- // when there is non client-buffer-count in effect, the client is not
- // allowed to dequeue more than one buffer at a time,
- // so the next time they dequeue a buffer, we know that they don't
- // own one. the actual resizing will happen during the next
- // dequeueBuffer.
-
- mServerBufferCount = bufferCount;
- }
- return OK;
-}
-
-// Called from the consumer side
-status_t SurfaceMediaSource::setBufferCountServer(int bufferCount) {
- Mutex::Autolock lock(mMutex);
- return setBufferCountServerLocked(bufferCount);
-}
-
-status_t SurfaceMediaSource::setBufferCount(int bufferCount) {
- ALOGV("SurfaceMediaSource::setBufferCount");
- if (bufferCount > NUM_BUFFER_SLOTS) {
- ALOGE("setBufferCount: bufferCount is larger than the number of buffer slots");
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mMutex);
- // Error out if the user has dequeued buffers
- for (int i = 0 ; i < mBufferCount ; i++) {
- if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
- ALOGE("setBufferCount: client owns some buffers");
- return INVALID_OPERATION;
- }
- }
+ mBufferQueue = new BufferQueue(true, MIN_UNDEQUEUED_BUFFERS);
+ mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
+ mBufferQueue->setSynchronousMode(true);
- if (bufferCount == 0) {
- const int minBufferSlots = mSynchronousMode ?
- MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
- mClientBufferCount = 0;
- bufferCount = (mServerBufferCount >= minBufferSlots) ?
- mServerBufferCount : minBufferSlots;
- return setBufferCountServerLocked(bufferCount);
- }
-
- // We don't allow the client to set a buffer-count less than
- // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
- if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
- return BAD_VALUE;
- }
-
- // here we're guaranteed that the client doesn't have dequeued buffers
- // and will release all of its buffer references.
- mBufferCount = bufferCount;
- mClientBufferCount = bufferCount;
- mCurrentSlot = INVALID_BUFFER_SLOT;
- mQueue.clear();
- mDequeueCondition.signal();
- freeAllBuffersLocked();
- return OK;
-}
-
-status_t SurfaceMediaSource::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- ALOGV("SurfaceMediaSource::requestBuffer");
- Mutex::Autolock lock(mMutex);
- if (slot < 0 || mBufferCount <= slot) {
- ALOGE("requestBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, slot);
- return BAD_VALUE;
- }
- mSlots[slot].mRequestBufferCalled = true;
- *buf = mSlots[slot].mGraphicBuffer;
- return NO_ERROR;
-}
-
-status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
- uint32_t format, uint32_t usage) {
- ALOGV("dequeueBuffer");
- Mutex::Autolock lock(mMutex);
-
- // Check for the buffer size- the client should just use the
- // default width and height, and not try to set those.
- // This is needed since
- // the getFormat() returns mDefaultWidth/ Height for the OMX. It is
- // queried by OMX in the beginning and not every time a frame comes.
- // Not sure if there is a way to update the
- // frame size while recording. So as of now, the client side
- // sets the default values via the constructor, and the encoder is
- // setup to encode frames of that size
- // The design might need to change in the future.
- // TODO: Currently just uses mDefaultWidth/Height. In the future
- // we might declare mHeight and mWidth and check against those here.
- if ((w != 0) || (h != 0)) {
- if ((w != mDefaultWidth) || (h != mDefaultHeight)) {
- ALOGE("dequeuebuffer: invalid buffer size! Req: %dx%d, Found: %dx%d",
- mDefaultWidth, mDefaultHeight, w, h);
- return BAD_VALUE;
- }
- }
-
- status_t returnFlags(OK);
- int found, foundSync;
- int dequeuedCount = 0;
- bool tryAgain = true;
- while (tryAgain) {
- // We need to wait for the FIFO to drain if the number of buffer
- // needs to change.
- //
- // The condition "number of buffer needs to change" is true if
- // - the client doesn't care about how many buffers there are
- // - AND the actual number of buffer is different from what was
- // set in the last setBufferCountServer()
- // - OR -
- // setBufferCountServer() was set to a value incompatible with
- // the synchronization mode (for instance because the sync mode
- // changed since)
- //
- // As long as this condition is true AND the FIFO is not empty, we
- // wait on mDequeueCondition.
-
- int minBufferCountNeeded = mSynchronousMode ?
- MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
-
- if (!mClientBufferCount &&
- ((mServerBufferCount != mBufferCount) ||
- (mServerBufferCount < minBufferCountNeeded))) {
- // wait for the FIFO to drain
- while (!mQueue.isEmpty()) {
- ALOGV("Waiting for the FIFO to drain");
- mDequeueCondition.wait(mMutex);
- }
- if (mStopped) {
- return NO_INIT;
- }
- // need to check again since the mode could have changed
- // while we were waiting
- minBufferCountNeeded = mSynchronousMode ?
- MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
- }
-
- if (!mClientBufferCount &&
- ((mServerBufferCount != mBufferCount) ||
- (mServerBufferCount < minBufferCountNeeded))) {
- // here we're guaranteed that mQueue is empty
- freeAllBuffersLocked();
- mBufferCount = mServerBufferCount;
- if (mBufferCount < minBufferCountNeeded)
- mBufferCount = minBufferCountNeeded;
- mCurrentSlot = INVALID_BUFFER_SLOT;
- returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
- }
-
- // look for a free buffer to give to the client
- found = INVALID_BUFFER_SLOT;
- foundSync = INVALID_BUFFER_SLOT;
- dequeuedCount = 0;
- for (int i = 0; i < mBufferCount; i++) {
- const int state = mSlots[i].mBufferState;
- if (state == BufferSlot::DEQUEUED) {
- dequeuedCount++;
- continue; // won't be continuing if could
- // dequeue a non 'FREE' current slot like
- // that in SurfaceTexture
- }
- // In case of Encoding, we do not deque the mCurrentSlot buffer
- // since we follow synchronous mode (unlike possibly in
- // SurfaceTexture that could be using the asynch mode
- // or has some mechanism in GL to be able to wait till the
- // currentslot is done using the data)
- // Here, we have to wait for the MPEG4Writer(or equiv)
- // to tell us when it's done using the current buffer
- if (state == BufferSlot::FREE) {
- foundSync = i;
- // Unlike that in SurfaceTexture,
- // We don't need to worry if it is the
- // currentslot or not as it is in state FREE
- found = i;
- break;
- }
- }
-
- // clients are not allowed to dequeue more than one buffer
- // if they didn't set a buffer count.
- if (!mClientBufferCount && dequeuedCount) {
- return -EINVAL;
- }
-
- // See whether a buffer has been queued since the last setBufferCount so
- // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below.
- bool bufferHasBeenQueued = mCurrentSlot != INVALID_BUFFER_SLOT;
- if (bufferHasBeenQueued) {
- // make sure the client is not trying to dequeue more buffers
- // than allowed.
- const int avail = mBufferCount - (dequeuedCount+1);
- if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
- ALOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
- MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
- dequeuedCount);
- return -EBUSY;
- }
- }
-
- // we're in synchronous mode and didn't find a buffer, we need to wait
- // for for some buffers to be consumed
- tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
- if (tryAgain) {
- ALOGV("Waiting..In synchronous mode and no buffer to dequeue");
- mDequeueCondition.wait(mMutex);
- }
- if (mStopped) {
- return NO_INIT;
- }
- }
-
- if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
- // foundSync guaranteed to be != INVALID_BUFFER_SLOT
- found = foundSync;
- }
-
- if (found == INVALID_BUFFER_SLOT) {
- return -EBUSY;
- }
-
- const int bufIndex = found;
- *outBuf = found;
-
- const bool useDefaultSize = !w && !h;
- if (useDefaultSize) {
- // use the default size
- w = mDefaultWidth;
- h = mDefaultHeight;
- }
-
- const bool updateFormat = (format != 0);
- if (!updateFormat) {
- // keep the current (or default) format
- format = mPixelFormat;
- }
-
- // buffer is now in DEQUEUED (but can also be current at the same time,
- // if we're in synchronous mode)
- mSlots[bufIndex].mBufferState = BufferSlot::DEQUEUED;
-
- const sp<GraphicBuffer>& buffer(mSlots[bufIndex].mGraphicBuffer);
- if ((buffer == NULL) ||
- (uint32_t(buffer->width) != w) ||
- (uint32_t(buffer->height) != h) ||
- (uint32_t(buffer->format) != format) ||
- ((uint32_t(buffer->usage) & usage) != usage)) {
- // XXX: This will be changed to USAGE_HW_VIDEO_ENCODER once driver
- // issues with that flag get fixed.
- usage |= GraphicBuffer::USAGE_HW_TEXTURE;
- status_t error;
- sp<GraphicBuffer> graphicBuffer(
- mGraphicBufferAlloc->createGraphicBuffer(
- w, h, format, usage, &error));
- if (graphicBuffer == 0) {
- ALOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed");
- return error;
- }
- if (updateFormat) {
- mPixelFormat = format;
- }
- mSlots[bufIndex].mGraphicBuffer = graphicBuffer;
- mSlots[bufIndex].mRequestBufferCalled = false;
- returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
- }
- return returnFlags;
-}
-
-// TODO: clean this up
-status_t SurfaceMediaSource::setSynchronousMode(bool enabled) {
- Mutex::Autolock lock(mMutex);
- if (mStopped) {
- ALOGE("setSynchronousMode: SurfaceMediaSource has been stopped!");
- return NO_INIT;
- }
-
- if (!enabled) {
- // Async mode is not allowed
- ALOGE("SurfaceMediaSource can be used only synchronous mode!");
- return INVALID_OPERATION;
- }
-
- if (mSynchronousMode != enabled) {
- // - if we're going to asynchronous mode, the queue is guaranteed to be
- // empty here
- // - if the client set the number of buffers, we're guaranteed that
- // we have at least 3 (because we don't allow less)
- mSynchronousMode = enabled;
- mDequeueCondition.signal();
- }
- return OK;
-}
-
-status_t SurfaceMediaSource::connect(int api,
- uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
- ALOGV("SurfaceMediaSource::connect");
- Mutex::Autolock lock(mMutex);
-
- if (mStopped) {
- ALOGE("Connect: SurfaceMediaSource has been stopped!");
- return NO_INIT;
- }
-
- status_t err = NO_ERROR;
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- case NATIVE_WINDOW_API_CPU:
- case NATIVE_WINDOW_API_MEDIA:
- case NATIVE_WINDOW_API_CAMERA:
- if (mConnectedApi != NO_CONNECTED_API) {
- err = -EINVAL;
- } else {
- mConnectedApi = api;
- *outWidth = mDefaultWidth;
- *outHeight = mDefaultHeight;
- *outTransform = 0;
- }
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-// This is called by the client side when it is done
-// TODO: Currently, this also sets mStopped to true which
-// is needed for unblocking the encoder which might be
-// waiting to read more frames. So if on the client side,
-// the same thread supplies the frames and also calls stop
-// on the encoder, the client has to call disconnect before
-// it calls stop.
-// In the case of the camera,
-// that need not be required since the thread supplying the
-// frames is separate than the one calling stop.
-status_t SurfaceMediaSource::disconnect(int api) {
- ALOGV("SurfaceMediaSource::disconnect");
- Mutex::Autolock lock(mMutex);
-
- if (mStopped) {
- ALOGE("disconnect: SurfaceMediaSoource is already stopped!");
- return NO_INIT;
- }
-
- status_t err = NO_ERROR;
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- case NATIVE_WINDOW_API_CPU:
- case NATIVE_WINDOW_API_MEDIA:
- case NATIVE_WINDOW_API_CAMERA:
- if (mConnectedApi == api) {
- mConnectedApi = NO_CONNECTED_API;
- mStopped = true;
- mDequeueCondition.signal();
- mFrameAvailableCondition.signal();
- } else {
- err = -EINVAL;
- }
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-status_t SurfaceMediaSource::queueBuffer(int bufIndex, int64_t timestamp,
- const Rect& crop, int scalingMode, uint32_t transform,
- uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
- ALOGV("queueBuffer");
-
- Mutex::Autolock lock(mMutex);
- *outWidth = mDefaultWidth;
- *outHeight = mDefaultHeight;
- *outTransform = 0;
-
- if (bufIndex < 0 || bufIndex >= mBufferCount) {
- ALOGE("queueBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, bufIndex);
- return -EINVAL;
- } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) {
- ALOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
- bufIndex, mSlots[bufIndex].mBufferState);
- return -EINVAL;
- } else if (!mSlots[bufIndex].mRequestBufferCalled) {
- ALOGE("queueBuffer: slot %d was enqueued without requesting a "
- "buffer", bufIndex);
- return -EINVAL;
- }
-
- if (mNumFramesReceived == 0) {
- mFirstFrameTimestamp = timestamp;
- // Initial delay
- if (mStartTimeNs > 0) {
- if (timestamp < mStartTimeNs) {
- // This frame predates start of record, discard
- mSlots[bufIndex].mBufferState = BufferSlot::FREE;
- mDequeueCondition.signal();
- return OK;
- }
- mStartTimeNs = timestamp - mStartTimeNs;
- }
- }
- timestamp = mStartTimeNs + (timestamp - mFirstFrameTimestamp);
-
- mNumFramesReceived++;
- if (mSynchronousMode) {
- // in synchronous mode we queue all buffers in a FIFO
- mQueue.push_back(bufIndex);
- ALOGV("Client queued buf# %d @slot: %d, Q size = %d, handle = %p, timestamp = %lld",
- mNumFramesReceived, bufIndex, mQueue.size(),
- mSlots[bufIndex].mGraphicBuffer->handle, timestamp);
- } else {
- // in asynchronous mode we only keep the most recent buffer
- if (mQueue.empty()) {
- mQueue.push_back(bufIndex);
- } else {
- Fifo::iterator front(mQueue.begin());
- // buffer currently queued is freed
- mSlots[*front].mBufferState = BufferSlot::FREE;
- // and we record the new buffer index in the queued list
- *front = bufIndex;
- }
- }
-
- mSlots[bufIndex].mBufferState = BufferSlot::QUEUED;
- mSlots[bufIndex].mTimestamp = timestamp;
- // TODO: (Confirm) Don't want to signal dequeue here.
- // May be just in asynchronous mode?
- // mDequeueCondition.signal();
-
- // Once the queuing is done, we need to let the listener
- // and signal the buffer consumer (encoder) know that a
- // buffer is available
- onFrameReceivedLocked();
-
-
- return OK;
-}
-
-
-// onFrameReceivedLocked informs the buffer consumers (StageFrightRecorder)
-// or listeners that a frame has been received
-// It is supposed to be called only from queuebuffer.
-// The buffer is NOT made available for dequeueing immediately. We need to
-// wait to hear from StageFrightRecorder to set the buffer FREE
-// Make sure this is called when the mutex is locked
-status_t SurfaceMediaSource::onFrameReceivedLocked() {
- ALOGV("On Frame Received locked");
- // Signal the encoder that a new frame has arrived
- mFrameAvailableCondition.signal();
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- // call back the listener
- // TODO: The listener may not be needed in SurfaceMediaSource at all.
- // This can be made a SurfaceTexture specific thing
- sp<FrameAvailableListener> listener;
- if (mSynchronousMode || mQueue.empty()) {
- listener = mFrameAvailableListener;
- }
+ // Note that we can't create an sp<...>(this) in a ctor that will not keep a
+ // reference once the ctor ends, as that would cause the refcount of 'this'
+ // dropping to 0 at the end of the ctor. Since all we need is a wp<...>
+ // that's what we create.
+ wp<BufferQueue::ConsumerListener> listener;
+ sp<BufferQueue::ConsumerListener> proxy;
+ listener = static_cast<BufferQueue::ConsumerListener*>(this);
+ proxy = new BufferQueue::ProxyConsumerListener(listener);
- if (listener != 0) {
- listener->onFrameAvailable();
+ status_t err = mBufferQueue->consumerConnect(proxy);
+ if (err != NO_ERROR) {
+ ALOGE("SurfaceMediaSource: error connecting to BufferQueue: %s (%d)",
+ strerror(-err), err);
}
- return OK;
}
-
-void SurfaceMediaSource::cancelBuffer(int bufIndex) {
- ALOGV("SurfaceMediaSource::cancelBuffer");
- Mutex::Autolock lock(mMutex);
- if (bufIndex < 0 || bufIndex >= mBufferCount) {
- ALOGE("cancelBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, bufIndex);
- return;
- } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) {
- ALOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
- bufIndex, mSlots[bufIndex].mBufferState);
- return;
+SurfaceMediaSource::~SurfaceMediaSource() {
+ ALOGV("SurfaceMediaSource::~SurfaceMediaSource");
+ if (!mStopped) {
+ reset();
}
- mSlots[bufIndex].mBufferState = BufferSlot::FREE;
- mDequeueCondition.signal();
}
nsecs_t SurfaceMediaSource::getTimestamp() {
@@ -582,7 +87,6 @@ nsecs_t SurfaceMediaSource::getTimestamp() {
return mCurrentTimestamp;
}
-
void SurfaceMediaSource::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
ALOGV("SurfaceMediaSource::setFrameAvailableListener");
@@ -590,49 +94,11 @@ void SurfaceMediaSource::setFrameAvailableListener(
mFrameAvailableListener = listener;
}
-void SurfaceMediaSource::freeAllBuffersLocked() {
- ALOGV("freeAllBuffersLocked");
- for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- mSlots[i].mGraphicBuffer = 0;
- mSlots[i].mBufferState = BufferSlot::FREE;
- }
-}
-
sp<GraphicBuffer> SurfaceMediaSource::getCurrentBuffer() const {
Mutex::Autolock lock(mMutex);
return mCurrentBuf;
}
-int SurfaceMediaSource::query(int what, int* outValue)
-{
- ALOGV("query");
- Mutex::Autolock lock(mMutex);
- int value;
- switch (what) {
- case NATIVE_WINDOW_WIDTH:
- value = mDefaultWidth;
- if (!mDefaultWidth && !mDefaultHeight && mCurrentBuf != 0)
- value = mCurrentBuf->width;
- break;
- case NATIVE_WINDOW_HEIGHT:
- value = mDefaultHeight;
- if (!mDefaultWidth && !mDefaultHeight && mCurrentBuf != 0)
- value = mCurrentBuf->height;
- break;
- case NATIVE_WINDOW_FORMAT:
- value = mPixelFormat;
- break;
- case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
- value = mSynchronousMode ?
- (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
- break;
- default:
- return BAD_VALUE;
- }
- outValue[0] = value;
- return NO_ERROR;
-}
-
void SurfaceMediaSource::dump(String8& result) const
{
char buffer[1024];
@@ -642,46 +108,10 @@ void SurfaceMediaSource::dump(String8& result) const
void SurfaceMediaSource::dump(String8& result, const char* prefix,
char* buffer, size_t SIZE) const
{
- Mutex::Autolock _l(mMutex);
- snprintf(buffer, SIZE,
- "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
- "mPixelFormat=%d, \n",
- prefix, mBufferCount, mSynchronousMode, mDefaultWidth, mDefaultHeight,
- mPixelFormat);
- result.append(buffer);
-
- String8 fifo;
- int fifoSize = 0;
- Fifo::const_iterator i(mQueue.begin());
- while (i != mQueue.end()) {
- snprintf(buffer, SIZE, "%02d ", *i++);
- fifoSize++;
- fifo.append(buffer);
- }
+ Mutex::Autolock lock(mMutex);
result.append(buffer);
-
- struct {
- const char * operator()(int state) const {
- switch (state) {
- case BufferSlot::DEQUEUED: return "DEQUEUED";
- case BufferSlot::QUEUED: return "QUEUED";
- case BufferSlot::FREE: return "FREE";
- default: return "Unknown";
- }
- }
- } stateName;
-
- for (int i = 0; i < mBufferCount; i++) {
- const BufferSlot& slot(mSlots[i]);
- snprintf(buffer, SIZE,
- "%s%s[%02d] state=%-8s, "
- "timestamp=%lld\n",
- prefix, (i==mCurrentSlot)?">":" ", i, stateName(slot.mBufferState),
- slot.mTimestamp
- );
- result.append(buffer);
- }
+ mBufferQueue->dump(result);
}
status_t SurfaceMediaSource::setFrameRate(int32_t fps)
@@ -726,10 +156,9 @@ status_t SurfaceMediaSource::reset()
Mutex::Autolock lock(mMutex);
// TODO: Add waiting on mFrameCompletedCondition here?
mStopped = true;
+
mFrameAvailableCondition.signal();
- mDequeueCondition.signal();
- mQueue.clear();
- freeAllBuffersLocked();
+ mBufferQueue->consumerDisconnect();
return OK;
}
@@ -737,17 +166,18 @@ status_t SurfaceMediaSource::reset()
sp<MetaData> SurfaceMediaSource::getFormat()
{
ALOGV("getFormat");
- Mutex::Autolock autoLock(mMutex);
+
+ Mutex::Autolock lock(mMutex);
sp<MetaData> meta = new MetaData;
- meta->setInt32(kKeyWidth, mDefaultWidth);
- meta->setInt32(kKeyHeight, mDefaultHeight);
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
// The encoder format is set as an opaque colorformat
// The encoder will later find out the actual colorformat
// from the GL Frames itself.
meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatAndroidOpaque);
- meta->setInt32(kKeyStride, mDefaultWidth);
- meta->setInt32(kKeySliceHeight, mDefaultHeight);
+ meta->setInt32(kKeyStride, mWidth);
+ meta->setInt32(kKeySliceHeight, mHeight);
meta->setInt32(kKeyFrameRate, mFrameRate);
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
return meta;
@@ -756,16 +186,54 @@ sp<MetaData> SurfaceMediaSource::getFormat()
status_t SurfaceMediaSource::read( MediaBuffer **buffer,
const ReadOptions *options)
{
- Mutex::Autolock autoLock(mMutex) ;
+ ALOGV("read");
+ Mutex::Autolock lock(mMutex);
- ALOGV("Read. Size of queued buffer: %d", mQueue.size());
*buffer = NULL;
+ // Update the current buffer info
+ // TODO: mCurrentSlot can be made a bufferstate since there
+ // can be more than one "current" slots.
+
+ BufferQueue::BufferItem item;
// If the recording has started and the queue is empty, then just
// wait here till the frames come in from the client side
- while (!mStopped && mQueue.empty()) {
- ALOGV("NO FRAMES! Recorder waiting for FrameAvailableCondition");
- mFrameAvailableCondition.wait(mMutex);
+ while (!mStopped) {
+
+ status_t err = mBufferQueue->acquireBuffer(&item);
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // wait for a buffer to be queued
+ mFrameAvailableCondition.wait(mMutex);
+ } else if (err == OK) {
+
+ // First time seeing the buffer? Added it to the SMS slot
+ if (item.mGraphicBuffer != NULL) {
+ mBufferSlot[item.mBuf] = item.mGraphicBuffer;
+ }
+
+ // check for the timing of this buffer
+ if (mNumFramesReceived == 0) {
+ mFirstFrameTimestamp = item.mTimestamp;
+ // Initial delay
+ if (mStartTimeNs > 0) {
+ if (item.mTimestamp < mStartTimeNs) {
+ // This frame predates start of record, discard
+ mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ continue;
+ }
+ mStartTimeNs = item.mTimestamp - mStartTimeNs;
+ }
+ }
+ item.mTimestamp = mStartTimeNs + (item.mTimestamp - mFirstFrameTimestamp);
+
+ mNumFramesReceived++;
+
+ break;
+ } else {
+ ALOGE("read: acquire failed with error code %d", err);
+ return ERROR_END_OF_STREAM;
+ }
+
}
// If the loop was exited as a result of stopping the recording,
@@ -775,15 +243,15 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer,
return ERROR_END_OF_STREAM;
}
- // Update the current buffer info
- // TODO: mCurrentSlot can be made a bufferstate since there
- // can be more than one "current" slots.
- Fifo::iterator front(mQueue.begin());
- mCurrentSlot = *front;
- mQueue.erase(front);
- mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer;
+ mCurrentSlot = item.mBuf;
+
+ // First time seeing the buffer? Added it to the SMS slot
+ if (item.mGraphicBuffer != NULL) {
+ mBufferSlot[mCurrentSlot] = item.mGraphicBuffer;
+ }
+ mCurrentBuf = mBufferSlot[mCurrentSlot];
int64_t prevTimeStamp = mCurrentTimestamp;
- mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp;
+ mCurrentTimestamp = item.mTimestamp;
mNumFramesEncoded++;
// Pass the data to the MediaBuffer. Pass in only the metadata
@@ -796,6 +264,7 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer,
mNumFramesEncoded, mCurrentTimestamp / 1000,
mCurrentTimestamp / 1000 - prevTimeStamp / 1000);
+
return OK;
}
@@ -833,25 +302,27 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) {
ALOGV("signalBufferReturned");
bool foundBuffer = false;
- Mutex::Autolock autoLock(mMutex);
+
+ Mutex::Autolock lock(mMutex);
if (mStopped) {
ALOGV("signalBufferReturned: mStopped = true! Nothing to do!");
return;
}
- for (int id = 0; id < NUM_BUFFER_SLOTS; id++) {
- if (mSlots[id].mGraphicBuffer == NULL) {
+ for (int id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) {
+ if (mBufferSlot[id] == NULL) {
continue;
}
if (checkBufferMatchesSlot(id, buffer)) {
ALOGV("Slot %d returned, matches handle = %p", id,
- mSlots[id].mGraphicBuffer->handle);
- mSlots[id].mBufferState = BufferSlot::FREE;
+ mBufferSlot[id]->handle);
+
+ mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+
buffer->setObserver(0);
buffer->release();
- mDequeueCondition.signal();
- mFrameCompleteCondition.signal();
+
foundBuffer = true;
break;
}
@@ -868,7 +339,40 @@ bool SurfaceMediaSource::checkBufferMatchesSlot(int slot, MediaBuffer *buffer) {
// copy the byte stream into our handle
buffer_handle_t bufferHandle ;
memcpy( &bufferHandle, (char *)(buffer->data()) + 4, sizeof(buffer_handle_t));
- return mSlots[slot].mGraphicBuffer->handle == bufferHandle;
+ return mBufferSlot[slot]->handle == bufferHandle;
+}
+
+// Part of the BufferQueue::ConsumerListener
+void SurfaceMediaSource::onFrameAvailable() {
+ ALOGV("onFrameAvailable");
+
+ sp<FrameAvailableListener> listener;
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableCondition.broadcast();
+ listener = mFrameAvailableListener;
+ }
+
+ if (listener != NULL) {
+ ALOGV("actually calling onFrameAvailable");
+ listener->onFrameAvailable();
+ }
+}
+
+// SurfaceMediaSource hijacks this event to assume
+// the prodcuer is disconnecting from the BufferQueue
+// and that it should stop the recording
+void SurfaceMediaSource::onBuffersReleased() {
+ ALOGV("onBuffersReleased");
+
+ Mutex::Autolock lock(mMutex);
+
+ mFrameAvailableCondition.signal();
+ mStopped = true;
+
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ mBufferSlot[i] = 0;
+ }
}
} // end of namespace android