Stagefright: use MediaCodec in async mode for recording
Async mode reduces the number of messages posted between
MediaCodec and MediaCodecSource. This reduces thread
wakeups and helps reduce CPU utilization.
Bug: 18246026
Change-Id: I4b0837f309fdd12e323c1dfa72525f5a31971a03
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index 3629c8b..0970b2b 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -85,8 +85,6 @@
status_t initEncoder();
void releaseEncoder();
status_t feedEncoderInputBuffers();
- void scheduleDoMoreWork();
- status_t doMoreWork(int32_t numInput, int32_t numOutput);
void suspend();
void resume(int64_t skipFramesBeforeUs = -1ll);
void signalEOS(status_t err = ERROR_END_OF_STREAM);
@@ -108,8 +106,6 @@
bool mDoMoreWorkPending;
sp<AMessage> mEncoderActivityNotify;
sp<IGraphicBufferProducer> mGraphicBufferProducer;
- Vector<sp<ABuffer> > mEncoderInputBuffers;
- Vector<sp<ABuffer> > mEncoderOutputBuffers;
List<MediaBuffer *> mInputBufferQueue;
List<size_t> mAvailEncoderInputIndices;
List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 0fecda8..c26e909 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -422,24 +422,16 @@
}
}
+ mEncoderActivityNotify = new AMessage(
+ kWhatEncoderActivity, mReflector->id());
+ mEncoder->setCallback(mEncoderActivityNotify);
+
err = mEncoder->start();
if (err != OK) {
return err;
}
- err = mEncoder->getInputBuffers(&mEncoderInputBuffers);
-
- if (err != OK) {
- return err;
- }
-
- err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
-
- if (err != OK) {
- return err;
- }
-
mEncoderReachedEOS = false;
mErrorCode = OK;
@@ -461,14 +453,6 @@
mbuf->release();
}
}
-
- for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
- sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
- accessUnit->setMediaBufferBase(NULL);
- }
-
- mEncoderInputBuffers.clear();
- mEncoderOutputBuffers.clear();
}
status_t MediaCodecSource::postSynchronouslyAndReturnError(
@@ -539,20 +523,6 @@
}
}
-void MediaCodecSource::scheduleDoMoreWork() {
- if (mDoMoreWorkPending) {
- return;
- }
-
- mDoMoreWorkPending = true;
-
- if (mEncoderActivityNotify == NULL) {
- mEncoderActivityNotify = new AMessage(
- kWhatEncoderActivity, mReflector->id());
- }
- mEncoder->requestActivityNotification(mEncoderActivityNotify);
-}
-
status_t MediaCodecSource::feedEncoderInputBuffers() {
while (!mInputBufferQueue.empty()
&& !mAvailEncoderInputIndices.empty()) {
@@ -587,16 +557,22 @@
#endif // DEBUG_DRIFT_TIME
}
+ sp<ABuffer> inbuf;
+ status_t err = mEncoder->getInputBuffer(bufferIndex, &inbuf);
+ if (err != OK || inbuf == NULL) {
+ mbuf->release();
+ signalEOS();
+ break;
+ }
+
size = mbuf->size();
- memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(),
- mbuf->data(), size);
+ memcpy(inbuf->data(), mbuf->data(), size);
if (mIsVideo) {
// video encoder will release MediaBuffer when done
// with underlying data.
- mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase(
- mbuf);
+ inbuf->setMediaBufferBase(mbuf);
} else {
mbuf->release();
}
@@ -615,50 +591,120 @@
return OK;
}
-status_t MediaCodecSource::doMoreWork(int32_t numInput, int32_t numOutput) {
+status_t MediaCodecSource::onStart(MetaData *params) {
+ if (mStopping) {
+ ALOGE("Failed to start while we're stopping");
+ return INVALID_OPERATION;
+ }
+
+ if (mStarted) {
+ ALOGI("MediaCodecSource (%s) resuming", mIsVideo ? "video" : "audio");
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ resume();
+ } else {
+ CHECK(mPuller != NULL);
+ mPuller->resume();
+ }
+ return OK;
+ }
+
+ ALOGI("MediaCodecSource (%s) starting", mIsVideo ? "video" : "audio");
+
status_t err = OK;
- if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
- while (numInput-- > 0) {
- size_t bufferIndex;
- err = mEncoder->dequeueInputBuffer(&bufferIndex);
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ int64_t startTimeUs;
+ if (!params || !params->findInt64(kKeyTime, &startTimeUs)) {
+ startTimeUs = -1ll;
+ }
+ resume(startTimeUs);
+ } else {
+ CHECK(mPuller != NULL);
+ sp<AMessage> notify = new AMessage(
+ kWhatPullerNotify, mReflector->id());
+ err = mPuller->start(params, notify);
+ if (err != OK) {
+ return err;
+ }
+ }
- if (err != OK) {
+ ALOGI("MediaCodecSource (%s) started", mIsVideo ? "video" : "audio");
+
+ mStarted = true;
+ return OK;
+}
+
+void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatPullerNotify:
+ {
+ MediaBuffer *mbuf;
+ CHECK(msg->findPointer("accessUnit", (void**)&mbuf));
+
+ if (mbuf == NULL) {
+ ALOGV("puller (%s) reached EOS",
+ mIsVideo ? "video" : "audio");
+ signalEOS();
+ }
+
+ if (mEncoder == NULL) {
+ ALOGV("got msg '%s' after encoder shutdown.",
+ msg->debugString().c_str());
+
+ if (mbuf != NULL) {
+ mbuf->release();
+ }
+
+ break;
+ }
+
+ mInputBufferQueue.push_back(mbuf);
+
+ feedEncoderInputBuffers();
+
+ break;
+ }
+ case kWhatEncoderActivity:
+ {
+ if (mEncoder == NULL) {
+ break;
+ }
+
+ int32_t cbID;
+ CHECK(msg->findInt32("callbackID", &cbID));
+ if (cbID == MediaCodec::CB_INPUT_AVAILABLE) {
+ int32_t index;
+ CHECK(msg->findInt32("index", &index));
+
+ mAvailEncoderInputIndices.push_back(index);
+ feedEncoderInputBuffers();
+ } else if (cbID == MediaCodec::CB_OUTPUT_AVAILABLE) {
+ int32_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ int32_t flags;
+ native_handle_t* handle = NULL;
+
+ CHECK(msg->findInt32("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", &flags));
+
+ if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+ mEncoder->releaseOutputBuffer(index);
+ signalEOS();
break;
}
- mAvailEncoderInputIndices.push_back(bufferIndex);
- }
-
- feedEncoderInputBuffers();
- }
-
- while (numOutput-- > 0) {
- size_t bufferIndex;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- native_handle_t* handle = NULL;
- err = mEncoder->dequeueOutputBuffer(
- &bufferIndex, &offset, &size, &timeUs, &flags);
-
- if (err != OK) {
- if (err == INFO_FORMAT_CHANGED) {
- continue;
- } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
- mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
- continue;
+ sp<ABuffer> outbuf;
+ status_t err = mEncoder->getOutputBuffer(index, &outbuf);
+ if (err != OK || outbuf == NULL) {
+ signalEOS();
+ break;
}
- if (err == -EAGAIN) {
- err = OK;
- }
- break;
- }
- if (!(flags & MediaCodec::BUFFER_FLAG_EOS)) {
- sp<ABuffer> outbuf = mEncoderOutputBuffers.itemAt(bufferIndex);
-
MediaBuffer *mbuf = new MediaBuffer(outbuf->size());
memcpy(mbuf->data(), outbuf->data(), outbuf->size());
@@ -709,121 +755,16 @@
mOutputBufferQueue.push_back(mbuf);
mOutputBufferCond.signal();
}
- }
- mEncoder->releaseOutputBuffer(bufferIndex);
-
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- err = ERROR_END_OF_STREAM;
- break;
- }
- }
-
- return err;
-}
-
-status_t MediaCodecSource::onStart(MetaData *params) {
- if (mStopping) {
- ALOGE("Failed to start while we're stopping");
- return INVALID_OPERATION;
- }
-
- if (mStarted) {
- ALOGI("MediaCodecSource (%s) resuming", mIsVideo ? "video" : "audio");
- if (mFlags & FLAG_USE_SURFACE_INPUT) {
- resume();
- } else {
- CHECK(mPuller != NULL);
- mPuller->resume();
- }
- return OK;
- }
-
- ALOGI("MediaCodecSource (%s) starting", mIsVideo ? "video" : "audio");
-
- status_t err = OK;
-
- if (mFlags & FLAG_USE_SURFACE_INPUT) {
- int64_t startTimeUs;
- if (!params || !params->findInt64(kKeyTime, &startTimeUs)) {
- startTimeUs = -1ll;
- }
- resume(startTimeUs);
- scheduleDoMoreWork();
- } else {
- CHECK(mPuller != NULL);
- sp<AMessage> notify = new AMessage(
- kWhatPullerNotify, mReflector->id());
- err = mPuller->start(params, notify);
- if (err != OK) {
- return err;
- }
- }
-
- ALOGI("MediaCodecSource (%s) started", mIsVideo ? "video" : "audio");
-
- mStarted = true;
- return OK;
-}
-
-void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatPullerNotify:
- {
- MediaBuffer *mbuf;
- CHECK(msg->findPointer("accessUnit", (void**)&mbuf));
-
- if (mbuf == NULL) {
- ALOGV("puller (%s) reached EOS",
- mIsVideo ? "video" : "audio");
+ mEncoder->releaseOutputBuffer(index);
+ } else if (cbID == MediaCodec::CB_ERROR) {
+ status_t err;
+ CHECK(msg->findInt32("err", &err));
+ ALOGE("Encoder (%s) reported error : 0x%x",
+ mIsVideo ? "video" : "audio", err);
signalEOS();
- }
-
- if (mEncoder == NULL) {
- ALOGV("got msg '%s' after encoder shutdown.",
- msg->debugString().c_str());
-
- if (mbuf != NULL) {
- mbuf->release();
- }
-
- break;
- }
-
- mInputBufferQueue.push_back(mbuf);
-
- feedEncoderInputBuffers();
- scheduleDoMoreWork();
-
- break;
- }
- case kWhatEncoderActivity:
- {
- mDoMoreWorkPending = false;
-
- if (mEncoder == NULL) {
- break;
- }
-
- int32_t numInput, numOutput;
-
- if (!msg->findInt32("input-buffers", &numInput)) {
- numInput = INT32_MAX;
- }
- if (!msg->findInt32("output-buffers", &numOutput)) {
- numOutput = INT32_MAX;
- }
-
- status_t err = doMoreWork(numInput, numOutput);
-
- if (err == OK) {
- scheduleDoMoreWork();
- } else {
- // reached EOS, or error
- signalEOS(err);
- }
-
- break;
+ }
+ break;
}
case kWhatStart:
{