Merge "Add MEDIA_STARTED/PAUSED/STOPPED events to media players" into klp-dev
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 28fc00f..3f8567c 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -36,6 +36,7 @@
 #include <media/ICrypto.h>
 
 #include <stdio.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <getopt.h>
 
@@ -143,6 +144,10 @@
     looper->start();
     ALOGV("Creating codec");
     sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);
+    if (codec == NULL) {
+        fprintf(stderr, "ERROR: unable to create video/avc codec instance\n");
+        return UNKNOWN_ERROR;
+    }
     err = codec->configure(format, NULL, NULL,
             MediaCodec::CONFIGURE_FLAG_ENCODE);
     if (err != NO_ERROR) {
@@ -460,17 +465,9 @@
         printf("Stopping encoder and muxer\n");
     }
 
-    // Shut everything down.
-    //
-    // The virtual display will continue to produce frames until "dpy"
-    // goes out of scope (and something causes the Binder traffic to transmit;
-    // can be forced with IPCThreadState::self()->flushCommands()).  This
-    // could cause SurfaceFlinger to get stuck trying to feed us, so we want
-    // to set a NULL Surface to make the virtual display "dormant".
+    // Shut everything down, starting with the producer side.
     bufferProducer = NULL;
-    SurfaceComposerClient::openGlobalTransaction();
-    SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);
-    SurfaceComposerClient::closeGlobalTransaction();
+    SurfaceComposerClient::destroyDisplay(dpy);
 
     encoder->stop();
     muxer->stop();
@@ -599,7 +596,19 @@
         return 2;
     }
 
-    status_t err = recordScreen(argv[optind]);
+    // MediaMuxer tries to create the file in the constructor, but we don't
+    // learn about the failure until muxer.start(), which returns a generic
+    // error code without logging anything.  We attempt to create the file
+    // now for better diagnostics.
+    const char* fileName = argv[optind];
+    int fd = open(fileName, O_CREAT | O_RDWR, 0644);
+    if (fd < 0) {
+        fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
+        return 1;
+    }
+    close(fd);
+
+    status_t err = recordScreen(fileName);
     ALOGD(err == NO_ERROR ? "success" : "failed");
     return (int) err;
 }
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 1060131..561ce02 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -19,6 +19,8 @@
 
 LOCAL_CFLAGS += -Wno-multichar
 
+LOCAL_MODULE_TAGS := optional
+
 LOCAL_MODULE:= stagefright
 
 include $(BUILD_EXECUTABLE)
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 797e0b6..030bf1b 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -51,6 +51,7 @@
 
 #include <fcntl.h>
 
+#include <gui/GLConsumer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index f457261..62f0c64 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -423,14 +423,7 @@
             nsecs_t processAudioBuffer(const sp<AudioRecordThread>& thread);
 
             // caller must hold lock on mLock for all _l methods
-            status_t openRecord_l(uint32_t sampleRate,
-                                audio_format_t format,
-                                size_t frameCount,
-                                audio_input_flags_t flags,
-                                audio_io_handle_t input,
-                                size_t epoch);
-
-            audio_io_handle_t getInput_l();
+            status_t openRecord_l(size_t epoch);
 
             // FIXME enum is faster than strcmp() for parameter 'from'
             status_t restoreRecord_l(const char *from);
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 6d116f0..db9093a 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -137,6 +137,7 @@
 
     enum InternalOptionType {
         INTERNAL_OPTION_SUSPEND,  // data is a bool
+        INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY,  // data is an int64_t
     };
     virtual status_t setInternalOption(
             node_id node,
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 6bf83dd..a8ffd4a 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -124,7 +124,8 @@
     };
 
     enum {
-        kFlagIsSecure   = 1,
+        kFlagIsSecure                                 = 1,
+        kFlagPushBlankBuffersToNativeWindowOnShutdown = 2,
     };
 
     struct BufferInfo {
@@ -199,6 +200,8 @@
     bool mStoreMetaDataInOutputBuffers;
     int32_t mMetaDataBuffersToSubmit;
 
+    int64_t mRepeatFrameDelayUs;
+
     status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffersOnPort(OMX_U32 portIndex);
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index dfb845b..590623b 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -50,7 +50,8 @@
     status_t getCodecCapabilities(
             size_t index, const char *type,
             Vector<ProfileLevel> *profileLevels,
-            Vector<uint32_t> *colorFormats) const;
+            Vector<uint32_t> *colorFormats,
+            uint32_t *flags) const;
 
 private:
     enum Section {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 583c3b3..daaf20f 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -361,9 +361,14 @@
 };
 
 struct CodecCapabilities {
+    enum {
+        kFlagSupportsAdaptivePlayback = 1 << 0,
+    };
+
     String8 mComponentName;
     Vector<CodecProfileLevel> mProfileLevels;
     Vector<OMX_U32> mColorFormats;
+    uint32_t mFlags;
 };
 
 // Return a vector of componentNames with supported profile/level pairs
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 616c3d6..e934a3e 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -175,6 +175,7 @@
     if (inputSource == AUDIO_SOURCE_DEFAULT) {
         inputSource = AUDIO_SOURCE_MIC;
     }
+    mInputSource = inputSource;
 
     if (sampleRate == 0) {
         ALOGE("Invalid sample rate %u", sampleRate);
@@ -210,28 +211,10 @@
     // Assumes audio_is_linear_pcm(format), else sizeof(uint8_t)
     mFrameSize = channelCount * audio_bytes_per_sample(format);
 
-    if (sessionId == 0 ) {
-        mSessionId = AudioSystem::newAudioSessionId();
-    } else {
-        mSessionId = sessionId;
-    }
-    ALOGV("set(): mSessionId %d", mSessionId);
-
-    mFlags = flags;
-
-    audio_io_handle_t input = AudioSystem::getInput(inputSource,
-                                                    sampleRate,
-                                                    format,
-                                                    channelMask,
-                                                    mSessionId);
-    if (input == 0) {
-        ALOGE("Could not get audio input for record source %d", inputSource);
-        return BAD_VALUE;
-    }
-
     // validate framecount
     size_t minFrameCount = 0;
-    status_t status = getMinFrameCount(&minFrameCount, sampleRate, format, channelMask);
+    status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
+            sampleRate, format, channelMask);
     if (status != NO_ERROR) {
         ALOGE("getMinFrameCount() failed; status %d", status);
         return status;
@@ -244,13 +227,23 @@
         ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
         return BAD_VALUE;
     }
+    mFrameCount = frameCount;
 
     mNotificationFramesReq = notificationFrames;
     mNotificationFramesAct = 0;
 
+    if (sessionId == 0 ) {
+        mSessionId = AudioSystem::newAudioSessionId();
+    } else {
+        mSessionId = sessionId;
+    }
+    ALOGV("set(): mSessionId %d", mSessionId);
+
+    mFlags = flags;
+
     // create the IAudioRecord
-    status = openRecord_l(sampleRate, format, frameCount, mFlags, input, 0 /*epoch*/);
-    if (status != NO_ERROR) {
+    status = openRecord_l(0 /*epoch*/);
+    if (status) {
         return status;
     }
 
@@ -274,8 +267,6 @@
     mMarkerReached = false;
     mNewPosition = 0;
     mUpdatePeriod = 0;
-    mInputSource = inputSource;
-    mInput = input;
     AudioSystem::acquireAudioSessionId(mSessionId);
     mSequence = 1;
     mObservedSequence = mSequence;
@@ -429,13 +420,7 @@
 // -------------------------------------------------------------------------
 
 // must be called with mLock held
-status_t AudioRecord::openRecord_l(
-        uint32_t sampleRate,
-        audio_format_t format,
-        size_t frameCount,
-        audio_input_flags_t flags,
-        audio_io_handle_t input,
-        size_t epoch)
+status_t AudioRecord::openRecord_l(size_t epoch)
 {
     status_t status;
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -449,12 +434,11 @@
 
     // Client can only express a preference for FAST.  Server will perform additional tests.
     // The only supported use case for FAST is callback transfer mode.
-    if (flags & AUDIO_INPUT_FLAG_FAST) {
+    if (mFlags & AUDIO_INPUT_FLAG_FAST) {
         if ((mTransfer != TRANSFER_CALLBACK) || (mAudioRecordThread == 0)) {
             ALOGW("AUDIO_INPUT_FLAG_FAST denied by client");
             // once denied, do not request again if IAudioRecord is re-created
-            flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST);
-            mFlags = flags;
+            mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
         } else {
             trackFlags |= IAudioFlinger::TRACK_FAST;
             tid = mAudioRecordThread->getTid();
@@ -463,18 +447,25 @@
 
     mNotificationFramesAct = mNotificationFramesReq;
 
-    if (!(flags & AUDIO_INPUT_FLAG_FAST)) {
+    if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) {
         // Make sure that application is notified with sufficient margin before overrun
-        if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
-            mNotificationFramesAct = frameCount/2;
+        if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) {
+            mNotificationFramesAct = mFrameCount/2;
         }
     }
 
+    audio_io_handle_t input = AudioSystem::getInput(mInputSource, mSampleRate, mFormat,
+            mChannelMask, mSessionId);
+    if (input == 0) {
+        ALOGE("Could not get audio input for record source %d", mInputSource);
+        return BAD_VALUE;
+    }
+
     int originalSessionId = mSessionId;
     sp<IAudioRecord> record = audioFlinger->openRecord(input,
-                                                       sampleRate, format,
+                                                       mSampleRate, mFormat,
                                                        mChannelMask,
-                                                       frameCount,
+                                                       mFrameCount,
                                                        &trackFlags,
                                                        tid,
                                                        &mSessionId,
@@ -484,6 +475,7 @@
 
     if (record == 0) {
         ALOGE("AudioFlinger could not create record track, status: %d", status);
+        AudioSystem::releaseInput(input);
         return status;
     }
     sp<IMemory> iMem = record->getCblk();
@@ -495,27 +487,27 @@
         mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
         mDeathNotifier.clear();
     }
+    mInput = input;
     mAudioRecord = record;
     mCblkMemory = iMem;
     audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
     mCblk = cblk;
     // FIXME missing fast track frameCount logic
     mAwaitBoost = false;
-    if (flags & AUDIO_INPUT_FLAG_FAST) {
+    if (mFlags & AUDIO_INPUT_FLAG_FAST) {
         if (trackFlags & IAudioFlinger::TRACK_FAST) {
-            ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount);
+            ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", mFrameCount);
             mAwaitBoost = true;
             // double-buffering is not required for fast tracks, due to tighter scheduling
-            if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) {
-                mNotificationFramesAct = frameCount;
+            if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount) {
+                mNotificationFramesAct = mFrameCount;
             }
         } else {
-            ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
+            ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", mFrameCount);
             // once denied, do not request again if IAudioRecord is re-created
-            flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST);
-            mFlags = flags;
-            if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
-                mNotificationFramesAct = frameCount/2;
+            mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
+            if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) {
+                mNotificationFramesAct = mFrameCount/2;
             }
         }
     }
@@ -524,7 +516,7 @@
     void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
 
     // update proxy
-    mProxy = new AudioRecordClientProxy(cblk, buffers, frameCount, mFrameSize);
+    mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize);
     mProxy->setEpoch(epoch);
     mProxy->setMinimum(mNotificationFramesAct);
 
@@ -651,17 +643,6 @@
     return mInput;
 }
 
-// must be called with mLock held
-audio_io_handle_t AudioRecord::getInput_l()
-{
-    mInput = AudioSystem::getInput(mInputSource,
-                                mSampleRate,
-                                mFormat,
-                                mChannelMask,
-                                mSessionId);
-    return mInput;
-}
-
 // -------------------------------------------------------------------------
 
 ssize_t AudioRecord::read(void* buffer, size_t userSize)
@@ -949,7 +930,7 @@
     // It will also delete the strong references on previous IAudioRecord and IMemory
     size_t position = mProxy->getPosition();
     mNewPosition = position + mUpdatePeriod;
-    result = openRecord_l(mSampleRate, mFormat, mFrameCount, mFlags, getInput_l(), position);
+    result = openRecord_l(position);
     if (result == NO_ERROR) {
         if (mActive) {
             // callback thread or sync event hasn't changed
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 36549d1..2e55c4f 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -369,7 +369,8 @@
       mChannelMask(0),
       mDequeueCounter(0),
       mStoreMetaDataInOutputBuffers(false),
-      mMetaDataBuffersToSubmit(0) {
+      mMetaDataBuffersToSubmit(0),
+      mRepeatFrameDelayUs(-1ll) {
     mUninitializedState = new UninitializedState(this);
     mLoadedState = new LoadedState(this);
     mLoadedToIdleState = new LoadedToIdleState(this);
@@ -1089,6 +1090,12 @@
         } else {
             mUseMetadataOnEncoderOutput = enable;
         }
+
+        if (!msg->findInt64(
+                    "repeat-previous-frame-after",
+                    &mRepeatFrameDelayUs)) {
+            mRepeatFrameDelayUs = -1ll;
+        }
     }
 
     // Always try to enable dynamic output buffers on native surface
@@ -1107,6 +1114,12 @@
             ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str());
             mStoreMetaDataInOutputBuffers = true;
         }
+
+        int32_t push;
+        if (msg->findInt32("push-blank-buffers-on-shutdown", &push)
+                && push != 0) {
+            mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
+        }
     }
 
     if (video) {
@@ -3577,6 +3590,7 @@
 
     if (componentName.endsWith(".secure")) {
         mCodec->mFlags |= kFlagIsSecure;
+        mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
     }
 
     mCodec->mQuirks = quirks;
@@ -3611,6 +3625,7 @@
 
     mCodec->mDequeueCounter = 0;
     mCodec->mMetaDataBuffersToSubmit = 0;
+    mCodec->mRepeatFrameDelayUs = -1ll;
 
     if (mCodec->mShutdownInProgress) {
         bool keepComponentAllocated = mCodec->mKeepComponentAllocated;
@@ -3742,6 +3757,23 @@
 
     err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput,
             &bufferProducer);
+
+    if (err == OK && mCodec->mRepeatFrameDelayUs > 0ll) {
+        err = mCodec->mOMX->setInternalOption(
+                mCodec->mNode,
+                kPortIndexInput,
+                IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY,
+                &mCodec->mRepeatFrameDelayUs,
+                sizeof(mCodec->mRepeatFrameDelayUs));
+
+        if (err != OK) {
+            ALOGE("[%s] Unable to configure option to repeat previous "
+                  "frames (err %d)",
+                  mCodec->mComponentName.c_str(),
+                  err);
+        }
+    }
+
     if (err == OK) {
         notify->setObject("input-surface",
                 new BufferProducerWrapper(bufferProducer));
@@ -4388,7 +4420,8 @@
         CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexInput), (status_t)OK);
         CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexOutput), (status_t)OK);
 
-        if (mCodec->mFlags & kFlagIsSecure && mCodec->mNativeWindow != NULL) {
+        if ((mCodec->mFlags & kFlagPushBlankBuffersToNativeWindowOnShutdown)
+                && mCodec->mNativeWindow != NULL) {
             // We push enough 1x1 blank buffers to ensure that one of
             // them has made it to the display.  This allows the OMX
             // component teardown to zero out any protected buffers
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index d24337f..6248e90 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -509,7 +509,8 @@
 status_t MediaCodecList::getCodecCapabilities(
         size_t index, const char *type,
         Vector<ProfileLevel> *profileLevels,
-        Vector<uint32_t> *colorFormats) const {
+        Vector<uint32_t> *colorFormats,
+        uint32_t *flags) const {
     profileLevels->clear();
     colorFormats->clear();
 
@@ -547,6 +548,8 @@
         colorFormats->push(caps.mColorFormats.itemAt(i));
     }
 
+    *flags = caps.mFlags;
+
     return OK;
 }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 3de3c28..7b37365 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -4567,7 +4567,7 @@
         CodecCapabilities *caps) {
     if (strncmp(componentName, "OMX.", 4)) {
         // Not an OpenMax component but a software codec.
-
+        caps->mFlags = 0;
         caps->mComponentName = componentName;
         return OK;
     }
@@ -4582,8 +4582,15 @@
 
     OMXCodec::setComponentRole(omx, node, isEncoder, mime);
 
+    caps->mFlags = 0;
     caps->mComponentName = componentName;
 
+    if (!isEncoder && !strncmp(mime, "video/", 6) &&
+            omx->storeMetaDataInBuffers(
+                    node, 1 /* port index */, OMX_TRUE) == OK) {
+        caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback;
+    }
+
     OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
     InitOMXParams(&param);
 
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 325ffcf..cf43e94 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -22,6 +22,7 @@
 
 #include <OMX_Core.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
 
 #include <media/hardware/MetadataBufferType.h>
 #include <ui/GraphicBuffer.h>
@@ -39,7 +40,13 @@
     mSuspended(false),
     mNumFramesAvailable(0),
     mEndOfStream(false),
-    mEndOfStreamSent(false) {
+    mEndOfStreamSent(false),
+    mRepeatAfterUs(-1ll),
+    mRepeatLastFrameGeneration(0),
+    mLatestSubmittedBufferId(-1),
+    mLatestSubmittedBufferFrameNum(0),
+    mLatestSubmittedBufferUseCount(0),
+    mRepeatBufferDeferred(false) {
 
     ALOGV("GraphicBufferSource w=%u h=%u c=%u",
             bufferWidth, bufferHeight, bufferCount);
@@ -123,6 +130,22 @@
     if (mEndOfStream && mNumFramesAvailable == 0) {
         submitEndOfInputStream_l();
     }
+
+    if (mRepeatAfterUs > 0ll && mLooper == NULL) {
+        mReflector = new AHandlerReflector<GraphicBufferSource>(this);
+
+        mLooper = new ALooper;
+        mLooper->registerHandler(mReflector);
+        mLooper->start();
+
+        if (mLatestSubmittedBufferId >= 0) {
+            sp<AMessage> msg =
+                new AMessage(kWhatRepeatLastFrame, mReflector->id());
+
+            msg->setInt32("generation", ++mRepeatLastFrameGeneration);
+            msg->post(mRepeatAfterUs);
+        }
+    }
 }
 
 void GraphicBufferSource::omxLoaded(){
@@ -132,6 +155,14 @@
         ALOGW("Dropped back down to Loaded without Executing");
     }
 
+    if (mLooper != NULL) {
+        mLooper->unregisterHandler(mReflector->id());
+        mReflector.clear();
+
+        mLooper->stop();
+        mLooper.clear();
+    }
+
     ALOGV("--> loaded; avail=%d eos=%d eosSent=%d",
             mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
 
@@ -211,8 +242,12 @@
         ALOGV("cbi %d matches bq slot %d, handle=%p",
                 cbi, id, mBufferSlot[id]->handle);
 
-        mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber,
-                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        if (id == mLatestSubmittedBufferId) {
+            CHECK_GT(mLatestSubmittedBufferUseCount--, 0);
+        } else {
+            mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber,
+                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        }
     } else {
         ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
                 cbi);
@@ -232,7 +267,16 @@
         // send that.
         ALOGV("buffer freed, EOS pending");
         submitEndOfInputStream_l();
+    } else if (mRepeatBufferDeferred) {
+        bool success = repeatLatestSubmittedBuffer_l();
+        if (success) {
+            ALOGV("deferred repeatLatestSubmittedBuffer_l SUCCESS");
+        } else {
+            ALOGV("deferred repeatLatestSubmittedBuffer_l FAILURE");
+        }
+        mRepeatBufferDeferred = false;
     }
+
     return;
 }
 
@@ -264,6 +308,16 @@
     }
 
     mSuspended = false;
+
+    if (mExecuting && mNumFramesAvailable == 0 && mRepeatBufferDeferred) {
+        if (repeatLatestSubmittedBuffer_l()) {
+            ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l SUCCESS");
+
+            mRepeatBufferDeferred = false;
+        } else {
+            ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l FAILURE");
+        }
+    }
 }
 
 bool GraphicBufferSource::fillCodecBuffer_l() {
@@ -318,11 +372,68 @@
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
     } else {
         ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
+        setLatestSubmittedBuffer_l(item);
     }
 
     return true;
 }
 
+bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() {
+    CHECK(mExecuting && mNumFramesAvailable == 0);
+
+    if (mLatestSubmittedBufferId < 0 || mSuspended) {
+        return false;
+    }
+
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        // No buffers available, bail.
+        ALOGV("repeatLatestSubmittedBuffer_l: no codec buffers.");
+        return false;
+    }
+
+    BufferQueue::BufferItem item;
+    item.mBuf = mLatestSubmittedBufferId;
+    item.mFrameNumber = mLatestSubmittedBufferFrameNum;
+
+    status_t err = submitBuffer_l(item, cbi);
+
+    if (err != OK) {
+        return false;
+    }
+
+    ++mLatestSubmittedBufferUseCount;
+
+    return true;
+}
+
+void GraphicBufferSource::setLatestSubmittedBuffer_l(
+        const BufferQueue::BufferItem &item) {
+    ALOGV("setLatestSubmittedBuffer_l");
+
+    if (mLatestSubmittedBufferId >= 0) {
+        if (mLatestSubmittedBufferUseCount == 0) {
+            mBufferQueue->releaseBuffer(
+                    mLatestSubmittedBufferId,
+                    mLatestSubmittedBufferFrameNum,
+                    EGL_NO_DISPLAY,
+                    EGL_NO_SYNC_KHR,
+                    Fence::NO_FENCE);
+        }
+    }
+
+    mLatestSubmittedBufferId = item.mBuf;
+    mLatestSubmittedBufferFrameNum = item.mFrameNumber;
+    mLatestSubmittedBufferUseCount = 1;
+    mRepeatBufferDeferred = false;
+
+    if (mReflector != NULL) {
+        sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id());
+        msg->setInt32("generation", ++mRepeatLastFrameGeneration);
+        msg->post(mRepeatAfterUs);
+    }
+}
+
 status_t GraphicBufferSource::signalEndOfInputStream() {
     Mutex::Autolock autoLock(mMutex);
     ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d",
@@ -470,6 +581,9 @@
 
     mNumFramesAvailable++;
 
+    mRepeatBufferDeferred = false;
+    ++mRepeatLastFrameGeneration;
+
     if (mExecuting) {
         fillCodecBuffer_l();
     }
@@ -495,4 +609,51 @@
     }
 }
 
+status_t GraphicBufferSource::setRepeatPreviousFrameDelayUs(
+        int64_t repeatAfterUs) {
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting || repeatAfterUs <= 0ll) {
+        return INVALID_OPERATION;
+    }
+
+    mRepeatAfterUs = repeatAfterUs;
+
+    return 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 = repeatLatestSubmittedBuffer_l();
+
+            if (success) {
+                ALOGV("repeatLatestSubmittedBuffer_l SUCCESS");
+            } else {
+                ALOGV("repeatLatestSubmittedBuffer_l FAILURE");
+                mRepeatBufferDeferred = true;
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index ac73770..244a843 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -25,6 +25,8 @@
 #include <OMX_Core.h>
 #include "../include/OMXNodeInstance.h"
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ALooper.h>
 
 namespace android {
 
@@ -89,6 +91,15 @@
     // in the BufferQueue) will be discarded until the suspension is lifted.
     void suspend(bool suspend);
 
+    // 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_t setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs);
+
 protected:
     // BufferQueue::ConsumerListener interface, called when a new frame of
     // data is available.  If we're executing and a codec buffer is
@@ -147,6 +158,9 @@
     // doing anything if we don't have a codec buffer available.
     void submitEndOfInputStream_l();
 
+    void setLatestSubmittedBuffer_l(const BufferQueue::BufferItem &item);
+    bool repeatLatestSubmittedBuffer_l();
+
     // Lock, covers all member variables.
     mutable Mutex mMutex;
 
@@ -181,6 +195,30 @@
     // Tracks codec buffers.
     Vector<CodecBuffer> mCodecBuffers;
 
+    ////
+    friend class AHandlerReflector<GraphicBufferSource>;
+
+    enum {
+        kWhatRepeatLastFrame,
+    };
+
+    int64_t mRepeatAfterUs;
+
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<GraphicBufferSource> > mReflector;
+
+    int32_t mRepeatLastFrameGeneration;
+
+    int mLatestSubmittedBufferId;
+    uint64_t mLatestSubmittedBufferFrameNum;
+    int32_t mLatestSubmittedBufferUseCount;
+
+    // The previously submitted buffer should've been repeated but
+    // no codec buffer was available at the time.
+    bool mRepeatBufferDeferred;
+
+    void onMessageReceived(const sp<AMessage> &msg);
+
     DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
 };
 
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 8d100f1..ef683a0 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -809,6 +809,7 @@
         size_t size) {
     switch (type) {
         case IOMX::INTERNAL_OPTION_SUSPEND:
+        case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY:
         {
             const sp<GraphicBufferSource> &bufferSource =
                 getGraphicBufferSource();
@@ -817,12 +818,22 @@
                 return ERROR_UNSUPPORTED;
             }
 
-            if (size != sizeof(bool)) {
-                return INVALID_OPERATION;
-            }
+            if (type == IOMX::INTERNAL_OPTION_SUSPEND) {
+                if (size != sizeof(bool)) {
+                    return INVALID_OPERATION;
+                }
 
-            bool suspend = *(bool *)data;
-            bufferSource->suspend(suspend);
+                bool suspend = *(bool *)data;
+                bufferSource->suspend(suspend);
+            } else {
+                if (size != sizeof(int64_t)) {
+                    return INVALID_OPERATION;
+                }
+
+                int64_t delayUs = *(int64_t *)data;
+
+                return bufferSource->setRepeatPreviousFrameDelayUs(delayUs);
+            }
 
             return OK;
         }
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index a5459fe..49ffcd6 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -23,6 +23,8 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include <GLES2/gl2.h>
+
 #include <media/stagefright/SurfaceMediaSource.h>
 #include <media/mediarecorder.h>