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(¶m);
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>