Remove GraphicBufferSource wrapper from OMX HAL: 1

Copy GraphicBufferSource impl to OMX HAL

Bug: 35449087
Test: Compiles
Change-Id: I793c918396da7c97ca9383cf6959a5334bfa5a75
diff --git a/media/libstagefright/omx/hal/1.0/impl/GraphicBufferSource.cpp b/media/libstagefright/omx/hal/1.0/impl/GraphicBufferSource.cpp
new file mode 100644
index 0000000..2f457ac
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/impl/GraphicBufferSource.cpp
@@ -0,0 +1,1203 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "GraphicBufferSource"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#define STRINGIFY_ENUMS // for asString in HardwareAPI.h/VideoAPI.h
+
+#include "GraphicBufferSource.h"
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include <media/hardware/MetadataBufferType.h>
+#include <ui/GraphicBuffer.h>
+#include <gui/BufferItem.h>
+#include <HardwareAPI.h>
+#include "omx/OMXUtils.h"
+#include <OMX_Component.h>
+#include <OMX_IndexExt.h>
+#include "OMXBuffer.h"
+
+#include <inttypes.h>
+#include "FrameDropper.h"
+
+namespace android {
+
+static const OMX_U32 kPortIndexInput = 0;
+
+class GraphicBufferSource::OmxBufferSource : public BnOMXBufferSource {
+public:
+    GraphicBufferSource* mSource;
+
+    OmxBufferSource(GraphicBufferSource* source): mSource(source) {
+    }
+
+    Status onOmxExecuting() override {
+        return mSource->onOmxExecuting();
+    }
+
+    Status onOmxIdle() override {
+        return mSource->onOmxIdle();
+    }
+
+    Status onOmxLoaded() override {
+        return mSource->onOmxLoaded();
+    }
+
+    Status onInputBufferAdded(int bufferId) override {
+        return mSource->onInputBufferAdded(bufferId);
+    }
+
+    Status onInputBufferEmptied(
+            int bufferId, const OMXFenceParcelable& fenceParcel) override {
+        return mSource->onInputBufferEmptied(bufferId, fenceParcel);
+    }
+};
+
+GraphicBufferSource::GraphicBufferSource() :
+    mInitCheck(UNKNOWN_ERROR),
+    mExecuting(false),
+    mSuspended(false),
+    mStopTimeUs(-1),
+    mLastDataSpace(HAL_DATASPACE_UNKNOWN),
+    mNumFramesAvailable(0),
+    mNumBufferAcquired(0),
+    mEndOfStream(false),
+    mEndOfStreamSent(false),
+    mLastActionTimeUs(-1ll),
+    mPrevOriginalTimeUs(-1ll),
+    mSkipFramesBeforeNs(-1ll),
+    mRepeatAfterUs(-1ll),
+    mRepeatLastFrameGeneration(0),
+    mRepeatLastFrameTimestamp(-1ll),
+    mRepeatLastFrameCount(0),
+    mLatestBufferId(-1),
+    mLatestBufferFrameNum(0),
+    mLatestBufferFence(Fence::NO_FENCE),
+    mRepeatBufferDeferred(false),
+    mTimePerCaptureUs(-1ll),
+    mTimePerFrameUs(-1ll),
+    mPrevCaptureUs(-1ll),
+    mPrevFrameUs(-1ll),
+    mInputBufferTimeOffsetUs(0ll),
+    mOmxBufferSource(new OmxBufferSource(this)) {
+    ALOGV("GraphicBufferSource");
+
+    String8 name("GraphicBufferSource");
+
+    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+    mConsumer->setConsumerName(name);
+
+    // 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 =
+            static_cast<BufferQueue::ConsumerListener*>(this);
+    sp<IConsumerListener> proxy =
+            new BufferQueue::ProxyConsumerListener(listener);
+
+    mInitCheck = mConsumer->consumerConnect(proxy, false);
+    if (mInitCheck != NO_ERROR) {
+        ALOGE("Error connecting to BufferQueue: %s (%d)",
+                strerror(-mInitCheck), mInitCheck);
+        return;
+    }
+
+    memset(&mColorAspects, 0, sizeof(mColorAspects));
+
+    CHECK(mInitCheck == NO_ERROR);
+}
+
+GraphicBufferSource::~GraphicBufferSource() {
+    ALOGV("~GraphicBufferSource");
+    if (mLatestBufferId >= 0) {
+        releaseBuffer(mLatestBufferId, mLatestBufferFrameNum, mLatestBufferFence);
+    }
+    if (mNumBufferAcquired != 0) {
+        ALOGW("potential buffer leak (acquired %d)", mNumBufferAcquired);
+    }
+    if (mConsumer != NULL) {
+        status_t err = mConsumer->consumerDisconnect();
+        if (err != NO_ERROR) {
+            ALOGW("consumerDisconnect failed: %d", err);
+        }
+    }
+}
+
+Status GraphicBufferSource::onOmxExecuting() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("--> executing; avail=%zu, codec vec size=%zd",
+            mNumFramesAvailable, mCodecBuffers.size());
+    CHECK(!mExecuting);
+    mExecuting = true;
+    mLastDataSpace = HAL_DATASPACE_UNKNOWN;
+    ALOGV("clearing last dataSpace");
+
+    // Start by loading up as many buffers as possible.  We want to do this,
+    // rather than just submit the first buffer, to avoid a degenerate case:
+    // if all BQ buffers arrive before we start executing, and we only submit
+    // one here, the other BQ buffers will just sit until we get notified
+    // that the codec buffer has been released.  We'd then acquire and
+    // submit a single additional buffer, repeatedly, never using more than
+    // one codec buffer simultaneously.  (We could instead try to submit
+    // all BQ buffers whenever any codec buffer is freed, but if we get the
+    // initial conditions right that will never be useful.)
+    while (mNumFramesAvailable) {
+        if (!fillCodecBuffer_l()) {
+            ALOGV("stop load with frames available (codecAvail=%d)",
+                    isCodecBufferAvailable_l());
+            break;
+        }
+    }
+
+    ALOGV("done loading initial frames, avail=%zu", mNumFramesAvailable);
+
+    // If EOS has already been signaled, and there are no more frames to
+    // submit, try to send EOS now as well.
+    if (mStopTimeUs == -1 && mEndOfStream && mNumFramesAvailable == 0) {
+        submitEndOfInputStream_l();
+    }
+
+    if (mRepeatAfterUs > 0ll && mLooper == NULL) {
+        mReflector = new AHandlerReflector<GraphicBufferSource>(this);
+
+        mLooper = new ALooper;
+        mLooper->registerHandler(mReflector);
+        mLooper->start();
+
+        if (mLatestBufferId >= 0) {
+            sp<AMessage> msg =
+                new AMessage(kWhatRepeatLastFrame, mReflector);
+
+            msg->setInt32("generation", ++mRepeatLastFrameGeneration);
+            msg->post(mRepeatAfterUs);
+        }
+    }
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::onOmxIdle() {
+    ALOGV("omxIdle");
+
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting) {
+        // We are only interested in the transition from executing->idle,
+        // not loaded->idle.
+        mExecuting = false;
+    }
+    return Status::ok();
+}
+
+Status GraphicBufferSource::onOmxLoaded(){
+    Mutex::Autolock autoLock(mMutex);
+    if (!mExecuting) {
+        // This can happen if something failed very early.
+        ALOGW("Dropped back down to Loaded without Executing");
+    }
+
+    if (mLooper != NULL) {
+        mLooper->unregisterHandler(mReflector->id());
+        mReflector.clear();
+
+        mLooper->stop();
+        mLooper.clear();
+    }
+
+    ALOGV("--> loaded; avail=%zu eos=%d eosSent=%d acquired=%d",
+            mNumFramesAvailable, mEndOfStream, mEndOfStreamSent, mNumBufferAcquired);
+
+    // Codec is no longer executing.  Releasing all buffers to bq.
+    for (int i = (int)mCodecBuffers.size() - 1; i >= 0; --i) {
+        if (mCodecBuffers[i].mGraphicBuffer != NULL) {
+            int id = mCodecBuffers[i].mSlot;
+            if (id != mLatestBufferId) {
+                ALOGV("releasing buffer for codec: slot=%d, useCount=%d, latest=%d",
+                        id, mBufferUseCount[id], mLatestBufferId);
+                sp<Fence> fence = new Fence(-1);
+                releaseBuffer(id, mCodecBuffers[i].mFrameNumber, fence);
+                mBufferUseCount[id] = 0;
+            }
+        }
+    }
+    // Also release the latest buffer
+    if (mLatestBufferId >= 0) {
+        releaseBuffer(mLatestBufferId, mLatestBufferFrameNum, mLatestBufferFence);
+        mBufferUseCount[mLatestBufferId] = 0;
+        mLatestBufferId = -1;
+    }
+
+    mCodecBuffers.clear();
+    mOMXNode.clear();
+    mExecuting = false;
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::onInputBufferAdded(int32_t bufferID) {
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting) {
+        // This should never happen -- buffers can only be allocated when
+        // transitioning from "loaded" to "idle".
+        ALOGE("addCodecBuffer: buffer added while executing");
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    ALOGV("addCodecBuffer: bufferID=%u", bufferID);
+
+    CodecBuffer codecBuffer;
+    codecBuffer.mBufferID = bufferID;
+    mCodecBuffers.add(codecBuffer);
+    return Status::ok();
+}
+
+Status GraphicBufferSource::onInputBufferEmptied(
+        int32_t bufferID, const OMXFenceParcelable &fenceParcel) {
+    int fenceFd = fenceParcel.get();
+
+    Mutex::Autolock autoLock(mMutex);
+    if (!mExecuting) {
+        if (fenceFd >= 0) {
+            ::close(fenceFd);
+        }
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    int cbi = findMatchingCodecBuffer_l(bufferID);
+    if (cbi < 0) {
+        // This should never happen.
+        ALOGE("codecBufferEmptied: buffer not recognized (bufferID=%u)", bufferID);
+        if (fenceFd >= 0) {
+            ::close(fenceFd);
+        }
+        return Status::fromServiceSpecificError(BAD_VALUE);
+    }
+
+    ALOGV("codecBufferEmptied: bufferID=%u, cbi=%d", bufferID, cbi);
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+    // header->nFilledLen may not be the original value, so we can't compare
+    // that to zero to see of this was the EOS buffer.  Instead we just
+    // see if the GraphicBuffer reference was null, which should only ever
+    // happen for EOS.
+    if (codecBuffer.mGraphicBuffer == NULL) {
+        if (!(mEndOfStream && mEndOfStreamSent)) {
+            // This can happen when broken code sends us the same buffer
+            // twice in a row.
+            ALOGE("ERROR: codecBufferEmptied on non-EOS null buffer "
+                    "(buffer emptied twice?)");
+        }
+        // No GraphicBuffer to deal with, no additional input or output is
+        // expected, so just return.
+        if (fenceFd >= 0) {
+            ::close(fenceFd);
+        }
+        return Status::fromServiceSpecificError(BAD_VALUE);
+    }
+
+    // Find matching entry in our cached copy of the BufferQueue slots.
+    // If we find a match, release that slot.  If we don't, the BufferQueue
+    // has dropped that GraphicBuffer, and there's nothing for us to release.
+    int id = codecBuffer.mSlot;
+    sp<Fence> fence = new Fence(fenceFd);
+    if (mBufferSlot[id] != NULL &&
+        mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
+        mBufferUseCount[id]--;
+
+        if (mBufferUseCount[id] < 0) {
+            ALOGW("mBufferUseCount for bq slot %d < 0 (=%d)", id, mBufferUseCount[id]);
+            mBufferUseCount[id] = 0;
+        }
+        if (id != mLatestBufferId && mBufferUseCount[id] == 0) {
+            releaseBuffer(id, codecBuffer.mFrameNumber, fence);
+        }
+        ALOGV("codecBufferEmptied: slot=%d, cbi=%d, useCount=%d, acquired=%d, handle=%p",
+                id, cbi, mBufferUseCount[id], mNumBufferAcquired, mBufferSlot[id]->handle);
+    } else {
+        ALOGV("codecBufferEmptied: no match for emptied buffer, "
+                "slot=%d, cbi=%d, useCount=%d, acquired=%d",
+                id, cbi, mBufferUseCount[id], mNumBufferAcquired);
+        // we will not reuse codec buffer, so there is no need to wait for fence
+    }
+
+    // Mark the codec buffer as available by clearing the GraphicBuffer ref.
+    codecBuffer.mGraphicBuffer = NULL;
+
+    if (mNumFramesAvailable) {
+        // Fill this codec buffer.
+        CHECK(!mEndOfStreamSent);
+        ALOGV("buffer freed, %zu frames avail (eos=%d)",
+                mNumFramesAvailable, mEndOfStream);
+        fillCodecBuffer_l();
+    } else if (mEndOfStream && mStopTimeUs == -1) {
+        // No frames available, but EOS is pending and no stop time, so use this buffer to
+        // send that.
+        ALOGV("buffer freed, EOS pending");
+        submitEndOfInputStream_l();
+    } else if (mRepeatBufferDeferred) {
+        bool success = repeatLatestBuffer_l();
+        if (success) {
+            ALOGV("deferred repeatLatestBuffer_l SUCCESS");
+        } else {
+            ALOGV("deferred repeatLatestBuffer_l FAILURE");
+        }
+        mRepeatBufferDeferred = false;
+    }
+
+    return Status::ok();
+}
+
+void GraphicBufferSource::onDataSpaceChanged_l(
+        android_dataspace dataSpace, android_pixel_format pixelFormat) {
+    ALOGD("got buffer with new dataSpace #%x", dataSpace);
+    mLastDataSpace = dataSpace;
+
+    if (ColorUtils::convertDataSpaceToV0(dataSpace)) {
+        omx_message msg;
+        msg.type = omx_message::EVENT;
+        msg.fenceFd = -1;
+        msg.u.event_data.event = OMX_EventDataSpaceChanged;
+        msg.u.event_data.data1 = mLastDataSpace;
+        msg.u.event_data.data2 = ColorUtils::packToU32(mColorAspects);
+        msg.u.event_data.data3 = pixelFormat;
+
+        mOMXNode->dispatchMessage(msg);
+    }
+}
+
+bool GraphicBufferSource::fillCodecBuffer_l() {
+    CHECK(mExecuting && mNumFramesAvailable > 0);
+
+    if (mSuspended && mActionQueue.empty()) {
+        return false;
+    }
+
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        // No buffers available, bail.
+        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %zu",
+                mNumFramesAvailable);
+        return false;
+    }
+
+    ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu",
+            mNumFramesAvailable);
+    BufferItem item;
+    status_t err = acquireBuffer(&item);
+    if (err != OK) {
+        ALOGE("fillCodecBuffer_l: acquireBuffer returned err=%d", err);
+        return false;
+    }
+
+    int64_t itemTimeUs = item.mTimestamp / 1000;
+
+    mNumFramesAvailable--;
+
+    // Process ActionItem in the Queue if there is any. If a buffer's timestamp
+    // is smaller than the first action's timestamp, no action need to be performed.
+    // If buffer's timestamp is larger or equal than the last action's timestamp,
+    // only the last action needs to be performed as all the acitions before the
+    // the action are overridden by the last action. For the other cases, traverse
+    // the Queue to find the newest action that with timestamp smaller or equal to
+    // the buffer's timestamp. For example, an action queue like
+    // [pause, 1s], [resume 2us], [pause 3us], [resume 4us], [pause 5us].... Upon
+    // receiving a buffer with timestamp 3.5us, only the action [pause, 3us] needs
+    // to be handled and [pause, 1us], [resume 2us] will be discarded.
+    bool dropped = false;
+    bool done = false;
+    if (!mActionQueue.empty()) {
+        // First scan to check if bufferTimestamp is smaller than first action's timestamp.
+        ActionItem nextAction = *(mActionQueue.begin());
+        if (itemTimeUs < nextAction.mActionTimeUs) {
+            ALOGV("No action. buffer timestamp %lld us < action timestamp: %lld us",
+                (long long)itemTimeUs, (long long)nextAction.mActionTimeUs);
+            // All the actions are ahead. No action need to perform now.
+            // Release the buffer if is in suspended state, or process the buffer
+            // if not in suspended state.
+            dropped = mSuspended;
+            done = true;
+        }
+
+        if (!done) {
+            List<ActionItem>::iterator it = mActionQueue.begin();
+            while(it != mActionQueue.end()) {
+                nextAction = *it;
+                mActionQueue.erase(it);
+                if (nextAction.mActionTimeUs > itemTimeUs) {
+                    break;
+                }
+                ++it;
+            }
+
+            CHECK(itemTimeUs >= nextAction.mActionTimeUs);
+            switch (nextAction.mAction) {
+                case ActionItem::PAUSE:
+                {
+                    mSuspended = true;
+                    dropped = true;
+                    ALOGV("RUNNING/PAUSE -> PAUSE at buffer %lld us  PAUSE Time: %lld us",
+                            (long long)itemTimeUs, (long long)nextAction.mActionTimeUs);
+                    break;
+                }
+                case ActionItem::RESUME:
+                {
+                    mSuspended = false;
+                    ALOGV("PAUSE/RUNNING -> RUNNING at buffer %lld us  RESUME Time: %lld us",
+                            (long long)itemTimeUs, (long long)nextAction.mActionTimeUs);
+                    break;
+                }
+                case ActionItem::STOP:
+                {
+                    ALOGV("RUNNING/PAUSE -> STOP at buffer %lld us  STOP Time: %lld us",
+                            (long long)itemTimeUs, (long long)nextAction.mActionTimeUs);
+                    dropped = true;
+                    // Clear the whole ActionQueue as recording is done
+                    mActionQueue.clear();
+                    submitEndOfInputStream_l();
+                    break;
+                }
+                default:
+                    ALOGE("Unknown action type");
+                    return false;
+            }
+        }
+    }
+
+    if (dropped) {
+        releaseBuffer(item.mSlot, item.mFrameNumber, item.mFence);
+        return true;
+    }
+
+    if (item.mDataSpace != mLastDataSpace) {
+        onDataSpaceChanged_l(
+                item.mDataSpace, (android_pixel_format)mBufferSlot[item.mSlot]->getPixelFormat());
+    }
+
+    err = UNKNOWN_ERROR;
+
+    // only submit sample if start time is unspecified, or sample
+    // is queued after the specified start time
+    if (mSkipFramesBeforeNs < 0ll || item.mTimestamp >= mSkipFramesBeforeNs) {
+        // if start time is set, offset time stamp by start time
+        if (mSkipFramesBeforeNs > 0) {
+            item.mTimestamp -= mSkipFramesBeforeNs;
+        }
+
+        int64_t timeUs = item.mTimestamp / 1000;
+        if (mFrameDropper != NULL && mFrameDropper->shouldDrop(timeUs)) {
+            ALOGV("skipping frame (%lld) to meet max framerate", static_cast<long long>(timeUs));
+            // set err to OK so that the skipped frame can still be saved as the lastest frame
+            err = OK;
+            dropped = true;
+        } else {
+            err = submitBuffer_l(item, cbi);
+        }
+    }
+
+    if (err != OK) {
+        ALOGV("submitBuffer_l failed, releasing bq slot %d", item.mSlot);
+        releaseBuffer(item.mSlot, item.mFrameNumber, item.mFence);
+    } else {
+        // Don't set the last buffer id if we're not repeating,
+        // we'll be holding on to the last buffer for nothing.
+        if (mRepeatAfterUs > 0ll) {
+            setLatestBuffer_l(item);
+        }
+        if (!dropped) {
+            ++mBufferUseCount[item.mSlot];
+        }
+        ALOGV("buffer submitted: slot=%d, cbi=%d, useCount=%d, acquired=%d",
+                item.mSlot, cbi, mBufferUseCount[item.mSlot], mNumBufferAcquired);
+    }
+
+    return true;
+}
+
+bool GraphicBufferSource::repeatLatestBuffer_l() {
+    CHECK(mExecuting && mNumFramesAvailable == 0);
+
+    if (mLatestBufferId < 0 || mSuspended) {
+        return false;
+    }
+    if (mBufferSlot[mLatestBufferId] == NULL) {
+        // This can happen if the remote side disconnects, causing
+        // onBuffersReleased() to NULL out our copy of the slots.  The
+        // buffer is gone, so we have nothing to show.
+        //
+        // To be on the safe side we try to release the buffer.
+        ALOGD("repeatLatestBuffer_l: slot was NULL");
+        mConsumer->releaseBuffer(
+                mLatestBufferId,
+                mLatestBufferFrameNum,
+                EGL_NO_DISPLAY,
+                EGL_NO_SYNC_KHR,
+                mLatestBufferFence);
+        mLatestBufferId = -1;
+        mLatestBufferFrameNum = 0;
+        mLatestBufferFence = Fence::NO_FENCE;
+        return false;
+    }
+
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        // No buffers available, bail.
+        ALOGV("repeatLatestBuffer_l: no codec buffers.");
+        return false;
+    }
+
+    BufferItem item;
+    item.mSlot = mLatestBufferId;
+    item.mFrameNumber = mLatestBufferFrameNum;
+    item.mTimestamp = mRepeatLastFrameTimestamp;
+    item.mFence = mLatestBufferFence;
+
+    status_t err = submitBuffer_l(item, cbi);
+
+    if (err != OK) {
+        return false;
+    }
+
+    ++mBufferUseCount[item.mSlot];
+
+    /* repeat last frame up to kRepeatLastFrameCount times.
+     * in case of static scene, a single repeat might not get rid of encoder
+     * ghosting completely, refresh a couple more times to get better quality
+     */
+    if (--mRepeatLastFrameCount > 0) {
+        mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000;
+
+        if (mReflector != NULL) {
+            sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector);
+            msg->setInt32("generation", ++mRepeatLastFrameGeneration);
+            msg->post(mRepeatAfterUs);
+        }
+    }
+
+    return true;
+}
+
+void GraphicBufferSource::setLatestBuffer_l(const BufferItem &item) {
+    if (mLatestBufferId >= 0 && mBufferUseCount[mLatestBufferId] == 0) {
+        releaseBuffer(mLatestBufferId, mLatestBufferFrameNum, mLatestBufferFence);
+        // mLatestBufferFence will be set to new fence just below
+    }
+
+    mLatestBufferId = item.mSlot;
+    mLatestBufferFrameNum = item.mFrameNumber;
+    mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000;
+
+    ALOGV("setLatestBuffer_l: slot=%d, useCount=%d",
+            item.mSlot, mBufferUseCount[item.mSlot]);
+
+    mRepeatBufferDeferred = false;
+    mRepeatLastFrameCount = kRepeatLastFrameCount;
+    mLatestBufferFence = item.mFence;
+
+    if (mReflector != NULL) {
+        sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector);
+        msg->setInt32("generation", ++mRepeatLastFrameGeneration);
+        msg->post(mRepeatAfterUs);
+    }
+}
+
+bool GraphicBufferSource::getTimestamp(
+        const BufferItem &item, int64_t *codecTimeUs) {
+    int64_t timeUs = item.mTimestamp / 1000;
+    timeUs += mInputBufferTimeOffsetUs;
+
+    if (mTimePerCaptureUs > 0ll
+            && (mTimePerCaptureUs > 2 * mTimePerFrameUs
+            || mTimePerFrameUs > 2 * mTimePerCaptureUs)) {
+        // Time lapse or slow motion mode
+        if (mPrevCaptureUs < 0ll) {
+            // first capture
+            mPrevCaptureUs = timeUs;
+            // adjust the first sample timestamp.
+            mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
+        } else {
+            // snap to nearest capture point
+            int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
+                    / mTimePerCaptureUs;
+            if (nFrames <= 0) {
+                // skip this frame as it's too close to previous capture
+                ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
+                return false;
+            }
+            mPrevCaptureUs = mPrevCaptureUs + nFrames * mTimePerCaptureUs;
+            mPrevFrameUs += mTimePerFrameUs * nFrames;
+        }
+
+        ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
+                static_cast<long long>(timeUs),
+                static_cast<long long>(mPrevCaptureUs),
+                static_cast<long long>(mPrevFrameUs));
+
+        *codecTimeUs = mPrevFrameUs;
+        return true;
+    } else {
+        int64_t originalTimeUs = timeUs;
+        if (originalTimeUs <= mPrevOriginalTimeUs) {
+                // Drop the frame if it's going backward in time. Bad timestamp
+                // could disrupt encoder's rate control completely.
+            ALOGW("Dropping frame that's going backward in time");
+            return false;
+        }
+
+        mPrevOriginalTimeUs = originalTimeUs;
+    }
+
+    *codecTimeUs = timeUs;
+    return true;
+}
+
+status_t GraphicBufferSource::submitBuffer_l(const BufferItem &item, int cbi) {
+    ALOGV("submitBuffer_l: slot=%d, cbi=%d", item.mSlot, cbi);
+
+    int64_t codecTimeUs;
+    if (!getTimestamp(item, &codecTimeUs)) {
+        return UNKNOWN_ERROR;
+    }
+
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+    codecBuffer.mGraphicBuffer = mBufferSlot[item.mSlot];
+    codecBuffer.mSlot = item.mSlot;
+    codecBuffer.mFrameNumber = item.mFrameNumber;
+
+    IOMX::buffer_id bufferID = codecBuffer.mBufferID;
+    const sp<GraphicBuffer> &buffer = codecBuffer.mGraphicBuffer;
+    int fenceID = item.mFence->isValid() ? item.mFence->dup() : -1;
+
+    status_t err = mOMXNode->emptyBuffer(
+            bufferID, buffer, OMX_BUFFERFLAG_ENDOFFRAME, codecTimeUs, fenceID);
+
+    if (err != OK) {
+        ALOGW("WARNING: emptyGraphicBuffer failed: 0x%x", err);
+        codecBuffer.mGraphicBuffer = NULL;
+        return err;
+    }
+
+    ALOGV("emptyGraphicBuffer succeeded, bufferID=%u buf=%p bufhandle=%p",
+            bufferID, buffer->getNativeBuffer(), buffer->handle);
+    return OK;
+}
+
+void GraphicBufferSource::submitEndOfInputStream_l() {
+    CHECK(mEndOfStream);
+    if (mEndOfStreamSent) {
+        ALOGV("EOS already sent");
+        return;
+    }
+
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        ALOGV("submitEndOfInputStream_l: no codec buffers available");
+        return;
+    }
+
+    // We reject any additional incoming graphic buffers, so there's no need
+    // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
+    // in-use.
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+    IOMX::buffer_id bufferID = codecBuffer.mBufferID;
+
+    status_t err = mOMXNode->emptyBuffer(
+            bufferID, (sp<GraphicBuffer>)NULL,
+            OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
+            0 /* timestamp */, -1 /* fenceFd */);
+    if (err != OK) {
+        ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
+    } else {
+        ALOGV("submitEndOfInputStream_l: buffer submitted, bufferID=%u cbi=%d",
+                bufferID, cbi);
+        mEndOfStreamSent = true;
+    }
+}
+
+int GraphicBufferSource::findAvailableCodecBuffer_l() {
+    CHECK(mCodecBuffers.size() > 0);
+
+    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+        if (mCodecBuffers[i].mGraphicBuffer == NULL) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int GraphicBufferSource::findMatchingCodecBuffer_l(IOMX::buffer_id bufferID) {
+    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+        if (mCodecBuffers[i].mBufferID == bufferID) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+status_t GraphicBufferSource::acquireBuffer(BufferItem *bi) {
+    status_t err = mConsumer->acquireBuffer(bi, 0);
+    if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+        // shouldn't happen
+        ALOGW("acquireBuffer: frame was not available");
+        return err;
+    } else if (err != OK) {
+        ALOGW("acquireBuffer: failed with err=%d", err);
+        return err;
+    }
+    // If this is the first time we're seeing this buffer, add it to our
+    // slot table.
+    if (bi->mGraphicBuffer != NULL) {
+        ALOGV("acquireBuffer: setting mBufferSlot %d", bi->mSlot);
+        mBufferSlot[bi->mSlot] = bi->mGraphicBuffer;
+        mBufferUseCount[bi->mSlot] = 0;
+    }
+    mNumBufferAcquired++;
+    return OK;
+}
+
+/*
+ * Releases an acquired buffer back to the consumer.
+ *
+ * id: buffer slot to release
+ * frameNum: frame number of the frame being released
+ * fence: fence of the frame being released
+ */
+void GraphicBufferSource::releaseBuffer(
+        int id, uint64_t frameNum, const sp<Fence> &fence) {
+    ALOGV("releaseBuffer: slot=%d", id);
+    mConsumer->releaseBuffer(
+            id, frameNum, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence);
+    mNumBufferAcquired--;
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) {
+    Mutex::Autolock autoLock(mMutex);
+
+    ALOGV("onFrameAvailable exec=%d avail=%zu",
+            mExecuting, mNumFramesAvailable);
+
+    if (mOMXNode == NULL || mEndOfStreamSent || (mSuspended && mActionQueue.empty())) {
+        if (mEndOfStreamSent) {
+            // This should only be possible if a new buffer was queued after
+            // EOS was signaled, i.e. the app is misbehaving.
+
+            ALOGW("onFrameAvailable: EOS is sent, ignoring frame");
+        } else {
+            ALOGV("onFrameAvailable: suspended, ignoring frame");
+        }
+
+        BufferItem item;
+        status_t err = acquireBuffer(&item);
+        if (err == OK) {
+            releaseBuffer(item.mSlot, item.mFrameNumber, item.mFence);
+        } else {
+            ALOGE("onFrameAvailable: acquireBuffer returned err=%d", err);
+        }
+        return;
+    }
+
+    mNumFramesAvailable++;
+
+    mRepeatBufferDeferred = false;
+    ++mRepeatLastFrameGeneration;
+
+    if (mExecuting) {
+        fillCodecBuffer_l();
+    }
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onBuffersReleased() {
+    Mutex::Autolock lock(mMutex);
+
+    uint64_t slotMask;
+    if (mConsumer->getReleasedBuffers(&slotMask) != NO_ERROR) {
+        ALOGW("onBuffersReleased: unable to get released buffer set");
+        slotMask = 0xffffffffffffffffULL;
+    }
+
+    ALOGV("onBuffersReleased: 0x%016" PRIx64, slotMask);
+
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        if ((slotMask & 0x01) != 0) {
+            // Last buffer (if set) is always acquired even if its use count
+            // is 0, because we could have skipped that frame but kept it for
+            // repeating. Otherwise a buffer is only acquired if use count>0.
+            if (mBufferSlot[i] != NULL &&
+                    (mBufferUseCount[i] > 0 || mLatestBufferId == i)) {
+                ALOGV("releasing acquired buffer: slot=%d, useCount=%d, latest=%d",
+                        i, mBufferUseCount[i], mLatestBufferId);
+                mNumBufferAcquired--;
+            }
+            if (mLatestBufferId == i) {
+                mLatestBufferId = -1;
+            }
+            mBufferSlot[i] = NULL;
+            mBufferUseCount[i] = 0;
+        }
+        slotMask >>= 1;
+    }
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onSidebandStreamChanged() {
+    ALOG_ASSERT(false, "GraphicBufferSource can't consume sideband streams");
+}
+
+Status GraphicBufferSource::configure(
+        const sp<IOMXNode>& omxNode, int32_t dataSpace) {
+    if (omxNode == NULL) {
+        return Status::fromServiceSpecificError(BAD_VALUE);
+    }
+
+    // Do setInputSurface() first, the node will try to enable metadata
+    // mode on input, and does necessary error checking. If this fails,
+    // we can't use this input surface on the node.
+    status_t err = omxNode->setInputSurface(mOmxBufferSource);
+    if (err != NO_ERROR) {
+        ALOGE("Unable to set input surface: %d", err);
+        return Status::fromServiceSpecificError(err);
+    }
+
+    // use consumer usage bits queried from encoder, but always add
+    // HW_VIDEO_ENCODER for backward compatibility.
+    uint32_t consumerUsage;
+    if (omxNode->getParameter(
+            (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
+            &consumerUsage, sizeof(consumerUsage)) != OK) {
+        consumerUsage = 0;
+    }
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexInput;
+
+    err = omxNode->getParameter(
+            OMX_IndexParamPortDefinition, &def, sizeof(def));
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get port definition: %d", err);
+        return Status::fromServiceSpecificError(UNKNOWN_ERROR);
+    }
+
+    // Call setMaxAcquiredBufferCount without lock.
+    // setMaxAcquiredBufferCount could call back to onBuffersReleased
+    // if the buffer count change results in releasing of existing buffers,
+    // which would lead to deadlock.
+    err = mConsumer->setMaxAcquiredBufferCount(def.nBufferCountActual);
+    if (err != NO_ERROR) {
+        ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+                def.nBufferCountActual, err);
+        return Status::fromServiceSpecificError(err);
+    }
+
+    {
+        Mutex::Autolock autoLock(mMutex);
+        mOMXNode = omxNode;
+
+        err = mConsumer->setDefaultBufferSize(
+                def.format.video.nFrameWidth,
+                def.format.video.nFrameHeight);
+        if (err != NO_ERROR) {
+            ALOGE("Unable to set BQ default buffer size to %ux%u: %d",
+                    def.format.video.nFrameWidth,
+                    def.format.video.nFrameHeight,
+                    err);
+            return Status::fromServiceSpecificError(err);
+        }
+
+        consumerUsage |= GRALLOC_USAGE_HW_VIDEO_ENCODER;
+        mConsumer->setConsumerUsageBits(consumerUsage);
+
+        // Sets the default buffer data space
+        ALOGD("setting dataspace: %#x, acquired=%d", dataSpace, mNumBufferAcquired);
+        mConsumer->setDefaultBufferDataSpace((android_dataspace)dataSpace);
+        mLastDataSpace = (android_dataspace)dataSpace;
+
+        mExecuting = false;
+        mSuspended = false;
+        mEndOfStream = false;
+        mEndOfStreamSent = false;
+        mPrevOriginalTimeUs = -1ll;
+        mSkipFramesBeforeNs = -1ll;
+        mRepeatAfterUs = -1ll;
+        mRepeatLastFrameGeneration = 0;
+        mRepeatLastFrameTimestamp = -1ll;
+        mRepeatLastFrameCount = 0;
+        mLatestBufferId = -1;
+        mLatestBufferFrameNum = 0;
+        mLatestBufferFence = Fence::NO_FENCE;
+        mRepeatBufferDeferred = false;
+        mTimePerCaptureUs = -1ll;
+        mTimePerFrameUs = -1ll;
+        mPrevCaptureUs = -1ll;
+        mPrevFrameUs = -1ll;
+        mInputBufferTimeOffsetUs = 0;
+        mStopTimeUs = -1;
+        mActionQueue.clear();
+    }
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setSuspend(bool suspend, int64_t suspendStartTimeUs) {
+    ALOGV("setSuspend=%d at time %lld us", suspend, (long long)suspendStartTimeUs);
+
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mStopTimeUs != -1) {
+        ALOGE("setSuspend failed as STOP action is pending");
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    // Push the action to the queue.
+    if (suspendStartTimeUs != -1) {
+        // suspendStartTimeUs must be smaller or equal to current systemTime.
+        int64_t currentSystemTimeUs = systemTime() / 1000;
+        if (suspendStartTimeUs > currentSystemTimeUs) {
+            ALOGE("setSuspend failed. %lld is larger than current system time %lld us",
+                    (long long)suspendStartTimeUs, (long long)currentSystemTimeUs);
+            return Status::fromServiceSpecificError(INVALID_OPERATION);
+        }
+        if (mLastActionTimeUs != -1 && suspendStartTimeUs < mLastActionTimeUs) {
+            ALOGE("setSuspend failed. %lld is smaller than last action time %lld us",
+                    (long long)suspendStartTimeUs, (long long)mLastActionTimeUs);
+            return Status::fromServiceSpecificError(INVALID_OPERATION);
+        }
+        mLastActionTimeUs = suspendStartTimeUs;
+        ActionItem action;
+        action.mAction = suspend ? ActionItem::PAUSE : ActionItem::RESUME;
+        action.mActionTimeUs = suspendStartTimeUs;
+        ALOGV("Push %s action into actionQueue", suspend ? "PAUSE" : "RESUME");
+        mActionQueue.push_back(action);
+    } else {
+        if (suspend) {
+            mSuspended = true;
+
+            while (mNumFramesAvailable > 0) {
+                BufferItem item;
+                status_t err = acquireBuffer(&item);
+
+                if (err != OK) {
+                    ALOGE("setSuspend: acquireBuffer returned err=%d", err);
+                    break;
+                }
+
+                --mNumFramesAvailable;
+
+                releaseBuffer(item.mSlot, item.mFrameNumber, item.mFence);
+            }
+            return Status::ok();
+        } else {
+
+            mSuspended = false;
+
+            if (mExecuting && mNumFramesAvailable == 0 && mRepeatBufferDeferred) {
+                if (repeatLatestBuffer_l()) {
+                    ALOGV("suspend/deferred repeatLatestBuffer_l SUCCESS");
+
+                    mRepeatBufferDeferred = false;
+                } else {
+                    ALOGV("suspend/deferred repeatLatestBuffer_l FAILURE");
+                }
+            }
+        }
+    }
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) {
+    ALOGV("setRepeatPreviousFrameDelayUs: delayUs=%lld", (long long)repeatAfterUs);
+
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting || repeatAfterUs <= 0ll) {
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    mRepeatAfterUs = repeatAfterUs;
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setTimeOffsetUs(int64_t timeOffsetUs) {
+    Mutex::Autolock autoLock(mMutex);
+
+    // timeOffsetUs must be negative for adjustment.
+    if (timeOffsetUs >= 0ll) {
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    mInputBufferTimeOffsetUs = timeOffsetUs;
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setMaxFps(float maxFps) {
+    ALOGV("setMaxFps: maxFps=%lld", (long long)maxFps);
+
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting) {
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    mFrameDropper = new FrameDropper();
+    status_t err = mFrameDropper->setMaxFrameRate(maxFps);
+    if (err != OK) {
+        mFrameDropper.clear();
+        return Status::fromServiceSpecificError(err);
+    }
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setStartTimeUs(int64_t skipFramesBeforeUs) {
+    ALOGV("setStartTimeUs: skipFramesBeforeUs=%lld", (long long)skipFramesBeforeUs);
+
+    Mutex::Autolock autoLock(mMutex);
+
+    mSkipFramesBeforeNs =
+            (skipFramesBeforeUs > 0) ? (skipFramesBeforeUs * 1000) : -1ll;
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setStopTimeUs(int64_t stopTimeUs) {
+    ALOGV("setStopTimeUs: %lld us", (long long)stopTimeUs);
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mStopTimeUs != -1) {
+        // Ignore if stop time has already been set
+        return Status::ok();
+    }
+
+    // stopTimeUs must be smaller or equal to current systemTime.
+    int64_t currentSystemTimeUs = systemTime() / 1000;
+    if (stopTimeUs > currentSystemTimeUs) {
+        ALOGE("setStopTimeUs failed. %lld is larger than current system time %lld us",
+            (long long)stopTimeUs, (long long)currentSystemTimeUs);
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+    if (mLastActionTimeUs != -1 && stopTimeUs < mLastActionTimeUs) {
+        ALOGE("setSuspend failed. %lld is smaller than last action time %lld us",
+            (long long)stopTimeUs, (long long)mLastActionTimeUs);
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+    mLastActionTimeUs = stopTimeUs;
+    ActionItem action;
+    action.mAction = ActionItem::STOP;
+    action.mActionTimeUs = stopTimeUs;
+    mActionQueue.push_back(action);
+    mStopTimeUs = stopTimeUs;
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) {
+    ALOGV("setTimeLapseConfig: timePerFrameUs=%lld, timePerCaptureUs=%lld",
+            (long long)timePerFrameUs, (long long)timePerCaptureUs);
+
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting || timePerFrameUs <= 0ll || timePerCaptureUs <= 0ll) {
+        return Status::fromServiceSpecificError(INVALID_OPERATION);
+    }
+
+    mTimePerFrameUs = timePerFrameUs;
+    mTimePerCaptureUs = timePerCaptureUs;
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::setColorAspects(int32_t aspectsPacked) {
+    Mutex::Autolock autoLock(mMutex);
+    mColorAspects = ColorUtils::unpackToColorAspects(aspectsPacked);
+    ALOGD("requesting color aspects (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s))",
+            mColorAspects.mRange, asString(mColorAspects.mRange),
+            mColorAspects.mPrimaries, asString(mColorAspects.mPrimaries),
+            mColorAspects.mMatrixCoeffs, asString(mColorAspects.mMatrixCoeffs),
+            mColorAspects.mTransfer, asString(mColorAspects.mTransfer));
+
+    return Status::ok();
+}
+
+Status GraphicBufferSource::signalEndOfInputStream() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("signalEndOfInputStream: exec=%d avail=%zu eos=%d",
+            mExecuting, mNumFramesAvailable, mEndOfStream);
+
+    if (mEndOfStream) {
+        ALOGE("EOS was already signaled");
+        return Status::fromStatusT(INVALID_OPERATION);
+    }
+
+    // Set the end-of-stream flag.  If no frames are pending from the
+    // BufferQueue, and a codec buffer is available, and we're executing,
+    // and there is no stop timestamp, we initiate the EOS from here.
+    // Otherwise, we'll let codecBufferEmptied() (or omxExecuting) do it.
+    //
+    // Note: if there are no pending frames and all codec buffers are
+    // available, we *must* submit the EOS from here or we'll just
+    // stall since no future events are expected.
+    mEndOfStream = true;
+
+    if (mStopTimeUs == -1 && mExecuting && mNumFramesAvailable == 0) {
+        submitEndOfInputStream_l();
+    }
+
+    return Status::ok();
+}
+
+void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRepeatLastFrame:
+        {
+            Mutex::Autolock autoLock(mMutex);
+
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mRepeatLastFrameGeneration) {
+                // stale
+                break;
+            }
+
+            if (!mExecuting || mNumFramesAvailable > 0) {
+                break;
+            }
+
+            bool success = repeatLatestBuffer_l();
+
+            if (success) {
+                ALOGV("repeatLatestBuffer_l SUCCESS");
+            } else {
+                ALOGV("repeatLatestBuffer_l FAILURE");
+                mRepeatBufferDeferred = true;
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/impl/GraphicBufferSource.h b/media/libstagefright/omx/hal/1.0/impl/GraphicBufferSource.h
new file mode 100644
index 0000000..475548e
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/impl/GraphicBufferSource.h
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2013 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 GRAPHIC_BUFFER_SOURCE_H_
+
+#define GRAPHIC_BUFFER_SOURCE_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/BufferQueue.h>
+#include <utils/RefBase.h>
+
+#include <VideoAPI.h>
+#include <media/IOMX.h>
+#include <media/OMXFenceParcelable.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ALooper.h>
+
+#include <android/BnGraphicBufferSource.h>
+#include <android/BnOMXBufferSource.h>
+
+namespace android {
+
+using ::android::binder::Status;
+
+struct FrameDropper;
+
+/*
+ * This class is used to feed OMX codecs from a Surface via BufferQueue.
+ *
+ * Instances of the class don't run on a dedicated thread.  Instead,
+ * various events trigger data movement:
+ *
+ *  - Availability of a new frame of data from the BufferQueue (notified
+ *    via the onFrameAvailable callback).
+ *  - The return of a codec buffer (via OnEmptyBufferDone).
+ *  - Application signaling end-of-stream.
+ *  - Transition to or from "executing" state.
+ *
+ * Frames of data (and, perhaps, the end-of-stream indication) can arrive
+ * before the codec is in the "executing" state, so we need to queue
+ * things up until we're ready to go.
+ */
+class GraphicBufferSource : public BnGraphicBufferSource,
+                            public BufferQueue::ConsumerListener {
+public:
+    GraphicBufferSource();
+
+    virtual ~GraphicBufferSource();
+
+    // We can't throw an exception if the constructor fails, so we just set
+    // this and require that the caller test the value.
+    status_t initCheck() const {
+        return mInitCheck;
+    }
+
+    // Returns the handle to the producer side of the BufferQueue.  Buffers
+    // queued on this will be received by GraphicBufferSource.
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+        return mProducer;
+    }
+
+    // This is called when OMX transitions to OMX_StateExecuting, which means
+    // we can start handing it buffers.  If we already have buffers of data
+    // sitting in the BufferQueue, this will send them to the codec.
+    Status onOmxExecuting();
+
+    // This is called when OMX transitions to OMX_StateIdle, indicating that
+    // the codec is meant to return all buffers back to the client for them
+    // to be freed. Do NOT submit any more buffers to the component.
+    Status onOmxIdle();
+
+    // This is called when OMX transitions to OMX_StateLoaded, indicating that
+    // we are shutting down.
+    Status onOmxLoaded();
+
+    // A "codec buffer", i.e. a buffer that can be used to pass data into
+    // the encoder, has been allocated.  (This call does not call back into
+    // OMXNodeInstance.)
+    Status onInputBufferAdded(int32_t bufferID);
+
+    // Called from OnEmptyBufferDone.  If we have a BQ buffer available,
+    // fill it with a new frame of data; otherwise, just mark it as available.
+    Status onInputBufferEmptied(
+            int32_t bufferID, const OMXFenceParcelable& fenceParcel);
+
+    // Configure the buffer source to be used with an OMX node with the default
+    // data space.
+    Status configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+
+    // This is called after the last input frame has been submitted or buffer
+    // timestamp is greater or equal than stopTimeUs. We need to submit an empty
+    // buffer with the EOS flag set.  If we don't have a codec buffer ready,
+    // we just set the mEndOfStream flag.
+    Status signalEndOfInputStream() override;
+
+    // If suspend is true, all incoming buffers (including those currently
+    // in the BufferQueue) with timestamp larger than timeUs will be discarded
+    // until the suspension is lifted. If suspend is false, all incoming buffers
+    // including those currently in the BufferQueue) with timestamp larger than
+    // timeUs will be processed. timeUs uses SYSTEM_TIME_MONOTONIC time base.
+    Status setSuspend(bool suspend, int64_t timeUs) override;
+
+    // Specifies the interval after which we requeue the buffer previously
+    // queued to the encoder. This is useful in the case of surface flinger
+    // providing the input surface if the resulting encoded stream is to
+    // be displayed "live". If we were not to push through the extra frame
+    // the decoder on the remote end would be unable to decode the latest frame.
+    // This API must be called before transitioning the encoder to "executing"
+    // state and once this behaviour is specified it cannot be reset.
+    Status setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
+
+    // Sets the input buffer timestamp offset.
+    // When set, the sample's timestamp will be adjusted with the timeOffsetUs.
+    Status setTimeOffsetUs(int64_t timeOffsetUs) override;
+
+    // When set, the max frame rate fed to the encoder will be capped at maxFps.
+    Status setMaxFps(float maxFps) override;
+
+    // Sets the time lapse (or slow motion) parameters.
+    // When set, the sample's timestamp will be modified to playback framerate,
+    // and capture timestamp will be modified to capture rate.
+    Status setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+
+    // Sets the start time us (in system time), samples before which should
+    // be dropped and not submitted to encoder
+    Status setStartTimeUs(int64_t startTimeUs) override;
+
+    // Sets the stop time us (in system time), samples after which should be dropped
+    // and not submitted to encoder. timeUs uses SYSTEM_TIME_MONOTONIC time base.
+    Status setStopTimeUs(int64_t stopTimeUs) override;
+
+    // Sets the desired color aspects, e.g. to be used when producer does not specify a dataspace.
+    Status setColorAspects(int32_t aspectsPacked) override;
+
+protected:
+    // BufferQueue::ConsumerListener interface, called when a new frame of
+    // data is available.  If we're executing and a codec buffer is
+    // available, we acquire the buffer, copy the GraphicBuffer reference
+    // into the codec buffer, and call Empty[This]Buffer.  If we're not yet
+    // executing or there's no codec buffer available, we just increment
+    // mNumFramesAvailable and return.
+    void onFrameAvailable(const BufferItem& item) override;
+
+    // BufferQueue::ConsumerListener interface, called when the client has
+    // released one or more GraphicBuffers.  We clear out the appropriate
+    // set of mBufferSlot entries.
+    void onBuffersReleased() override;
+
+    // BufferQueue::ConsumerListener interface, called when the client has
+    // changed the sideband stream. GraphicBufferSource doesn't handle sideband
+    // streams so this is a no-op (and should never be called).
+    void onSidebandStreamChanged() override;
+
+private:
+
+    // Keep track of codec input buffers.  They may either be available
+    // (mGraphicBuffer == NULL) or in use by the codec.
+    struct CodecBuffer {
+        IOMX::buffer_id mBufferID;
+
+        // buffer producer's frame-number for buffer
+        uint64_t mFrameNumber;
+
+        // buffer producer's buffer slot for buffer
+        int mSlot;
+
+        sp<GraphicBuffer> mGraphicBuffer;
+    };
+
+    // Returns the index of an available codec buffer.  If none are
+    // available, returns -1.  Mutex must be held by caller.
+    int findAvailableCodecBuffer_l();
+
+    // Returns true if a codec buffer is available.
+    bool isCodecBufferAvailable_l() {
+        return findAvailableCodecBuffer_l() >= 0;
+    }
+
+    // Finds the mCodecBuffers entry that matches.  Returns -1 if not found.
+    int findMatchingCodecBuffer_l(IOMX::buffer_id bufferID);
+
+    // Fills a codec buffer with a frame from the BufferQueue.  This must
+    // only be called when we know that a frame of data is ready (i.e. we're
+    // in the onFrameAvailable callback, or if we're in codecBufferEmptied
+    // and mNumFramesAvailable is nonzero).  Returns without doing anything if
+    // we don't have a codec buffer available.
+    //
+    // Returns true if we successfully filled a codec buffer with a BQ buffer.
+    bool fillCodecBuffer_l();
+
+    // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer
+    // reference into the codec buffer, and submits the data to the codec.
+    status_t submitBuffer_l(const BufferItem &item, int cbi);
+
+    // Submits an empty buffer, with the EOS flag set.   Returns without
+    // doing anything if we don't have a codec buffer available.
+    void submitEndOfInputStream_l();
+
+    // Acquire buffer from the consumer
+    status_t acquireBuffer(BufferItem *bi);
+
+    // Release buffer to the consumer
+    void releaseBuffer(int id, uint64_t frameNum, const sp<Fence> &fence);
+
+    void setLatestBuffer_l(const BufferItem &item);
+    bool repeatLatestBuffer_l();
+    bool getTimestamp(const BufferItem &item, int64_t *codecTimeUs);
+
+    // called when the data space of the input buffer changes
+    void onDataSpaceChanged_l(android_dataspace dataSpace, android_pixel_format pixelFormat);
+
+    // Lock, covers all member variables.
+    mutable Mutex mMutex;
+
+    // Used to report constructor failure.
+    status_t mInitCheck;
+
+    // Pointer back to the IOMXNode that created us.  We send buffers here.
+    sp<IOMXNode> mOMXNode;
+
+    // Set by omxExecuting() / omxIdling().
+    bool mExecuting;
+
+    bool mSuspended;
+
+    // The time to stop sending buffers.
+    int64_t mStopTimeUs;
+
+    // Last dataspace seen
+    android_dataspace mLastDataSpace;
+
+    // Our BufferQueue interfaces. mProducer is passed to the producer through
+    // getIGraphicBufferProducer, and mConsumer is used internally to retrieve
+    // the buffers queued by the producer.
+    sp<IGraphicBufferProducer> mProducer;
+    sp<IGraphicBufferConsumer> mConsumer;
+
+    // Number of frames pending in BufferQueue that haven't yet been
+    // forwarded to the codec.
+    size_t mNumFramesAvailable;
+
+    // Number of frames acquired from consumer (debug only)
+    int32_t mNumBufferAcquired;
+
+    // Set to true if we want to send end-of-stream after we run out of
+    // frames in BufferQueue.
+    bool mEndOfStream;
+    bool mEndOfStreamSent;
+
+    // Cache of GraphicBuffers from the buffer queue.  When the codec
+    // is done processing a GraphicBuffer, we can use this to map back
+    // to a slot number.
+    sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS];
+    int32_t mBufferUseCount[BufferQueue::NUM_BUFFER_SLOTS];
+
+    // Tracks codec buffers.
+    Vector<CodecBuffer> mCodecBuffers;
+
+    struct ActionItem {
+        typedef enum {
+            PAUSE,
+            RESUME,
+            STOP
+        } ActionType;
+        ActionType mAction;
+        int64_t mActionTimeUs;
+    };
+
+    // Maintain last action timestamp to ensure all the action timestamps are
+    // monotonically increasing.
+    int64_t mLastActionTimeUs;
+
+    // An action queue that queue up all the actions sent to GraphicBufferSource.
+    // STOP action should only show up at the end of the list as all the actions
+    // after a STOP action will be discarded. mActionQueue is protected by mMutex.
+    List<ActionItem> mActionQueue;
+
+    ////
+    friend struct AHandlerReflector<GraphicBufferSource>;
+
+    enum {
+        kWhatRepeatLastFrame,
+    };
+    enum {
+        kRepeatLastFrameCount = 10,
+    };
+
+    int64_t mPrevOriginalTimeUs;
+    int64_t mSkipFramesBeforeNs;
+
+    sp<FrameDropper> mFrameDropper;
+
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<GraphicBufferSource> > mReflector;
+
+    int64_t mRepeatAfterUs;
+    int32_t mRepeatLastFrameGeneration;
+    int64_t mRepeatLastFrameTimestamp;
+    int32_t mRepeatLastFrameCount;
+
+    int mLatestBufferId;
+    uint64_t mLatestBufferFrameNum;
+    sp<Fence> mLatestBufferFence;
+
+    // The previous buffer should've been repeated but
+    // no codec buffer was available at the time.
+    bool mRepeatBufferDeferred;
+
+    // Time lapse / slow motion configuration
+    int64_t mTimePerCaptureUs;
+    int64_t mTimePerFrameUs;
+    int64_t mPrevCaptureUs;
+    int64_t mPrevFrameUs;
+
+    int64_t mInputBufferTimeOffsetUs;
+
+    ColorAspects mColorAspects;
+
+    class OmxBufferSource;
+    sp<OmxBufferSource> mOmxBufferSource;
+
+    void onMessageReceived(const sp<AMessage> &msg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
+};
+
+}  // namespace android
+
+#endif  // GRAPHIC_BUFFER_SOURCE_H_