From bdddc659a941afdb7f4958f582c6901c07246097 Mon Sep 17 00:00:00 2001 From: Daniel Lam Date: Fri, 30 Mar 2012 16:04:43 -0700 Subject: Refactored SurfaceMediaSource SurfaceMediaSource takes advantage of BufferQueue to avoid duplicated code. Change-Id: I5e60b8eca21e6c3cf728d363cd8f3786125182d1 --- media/libstagefright/SurfaceMediaSource.cpp | 772 +++++----------------------- 1 file changed, 138 insertions(+), 634 deletions(-) (limited to 'media/libstagefright/SurfaceMediaSource.cpp') 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 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* 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& 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( - 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 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 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 listener; + sp proxy; + listener = static_cast(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& 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 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 SurfaceMediaSource::getFormat() { ALOGV("getFormat"); - Mutex::Autolock autoLock(mMutex); + + Mutex::Autolock lock(mMutex); sp 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 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 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 -- cgit v1.1