summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/MediaCodec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/MediaCodec.cpp')
-rw-r--r--media/libstagefright/MediaCodec.cpp871
1 files changed, 654 insertions, 217 deletions
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 3cb4217..5f55484 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -16,12 +16,13 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodec"
-#include <utils/Log.h>
-
-#include <media/stagefright/MediaCodec.h>
+#include <inttypes.h>
+#include "include/avc_utils.h"
#include "include/SoftwareRenderer.h"
+#include <binder/IBatteryStats.h>
+#include <binder/IServiceManager.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -31,45 +32,119 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/NativeWindowWrapper.h>
-
-#include "include/avc_utils.h"
+#include <private/android_filesystem_config.h>
+#include <utils/Log.h>
+#include <utils/Singleton.h>
namespace android {
+struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> {
+ BatteryNotifier();
+
+ void noteStartVideo();
+ void noteStopVideo();
+ void noteStartAudio();
+ void noteStopAudio();
+
+private:
+ int32_t mVideoRefCount;
+ int32_t mAudioRefCount;
+ sp<IBatteryStats> mBatteryStatService;
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier)
+
+MediaCodec::BatteryNotifier::BatteryNotifier() :
+ mVideoRefCount(0),
+ mAudioRefCount(0) {
+ // get battery service
+ const sp<IServiceManager> sm(defaultServiceManager());
+ if (sm != NULL) {
+ const String16 name("batterystats");
+ mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
+ if (mBatteryStatService == NULL) {
+ ALOGE("batterystats service unavailable!");
+ }
+ }
+}
+
+void MediaCodec::BatteryNotifier::noteStartVideo() {
+ if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
+ mBatteryStatService->noteStartVideo(AID_MEDIA);
+ }
+ mVideoRefCount++;
+}
+
+void MediaCodec::BatteryNotifier::noteStopVideo() {
+ if (mVideoRefCount == 0) {
+ ALOGW("BatteryNotifier::noteStop(): video refcount is broken!");
+ return;
+ }
+
+ mVideoRefCount--;
+ if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
+ mBatteryStatService->noteStopVideo(AID_MEDIA);
+ }
+}
+
+void MediaCodec::BatteryNotifier::noteStartAudio() {
+ if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
+ mBatteryStatService->noteStartAudio(AID_MEDIA);
+ }
+ mAudioRefCount++;
+}
+
+void MediaCodec::BatteryNotifier::noteStopAudio() {
+ if (mAudioRefCount == 0) {
+ ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!");
+ return;
+ }
+
+ mAudioRefCount--;
+ if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
+ mBatteryStatService->noteStopAudio(AID_MEDIA);
+ }
+}
// static
sp<MediaCodec> MediaCodec::CreateByType(
- const sp<ALooper> &looper, const char *mime, bool encoder) {
+ const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) {
sp<MediaCodec> codec = new MediaCodec(looper);
- if (codec->init(mime, true /* nameIsType */, encoder) != OK) {
- return NULL;
- }
- return codec;
+ const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
+ if (err != NULL) {
+ *err = ret;
+ }
+ return ret == OK ? codec : NULL; // NULL deallocates codec.
}
// static
sp<MediaCodec> MediaCodec::CreateByComponentName(
- const sp<ALooper> &looper, const char *name) {
+ const sp<ALooper> &looper, const char *name, status_t *err) {
sp<MediaCodec> codec = new MediaCodec(looper);
- if (codec->init(name, false /* nameIsType */, false /* encoder */) != OK) {
- return NULL;
- }
- return codec;
+ const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
+ if (err != NULL) {
+ *err = ret;
+ }
+ return ret == OK ? codec : NULL; // NULL deallocates codec.
}
MediaCodec::MediaCodec(const sp<ALooper> &looper)
: mState(UNINITIALIZED),
mLooper(looper),
- mCodec(new ACodec),
+ mCodec(NULL),
mReplyID(0),
mFlags(0),
+ mStickyError(OK),
mSoftRenderer(NULL),
+ mBatteryStatNotified(false),
+ mIsVideo(false),
mDequeueInputTimeoutGeneration(0),
mDequeueInputReplyID(0),
mDequeueOutputTimeoutGeneration(0),
@@ -97,29 +172,42 @@ status_t MediaCodec::PostAndAwaitResponse(
return err;
}
-status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
+// static
+void MediaCodec::PostReplyWithError(int32_t replyID, int32_t err) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+}
+
+status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
+ // save init parameters for reset
+ mInitName = name;
+ mInitNameIsType = nameIsType;
+ mInitIsEncoder = encoder;
+
// Current video decoders do not return from OMX_FillThisBuffer
// quickly, violating the OpenMAX specs, until that is remedied
// we need to invest in an extra looper to free the main event
// queue.
+ mCodec = new ACodec;
bool needDedicatedLooper = false;
- if (nameIsType && !strncasecmp(name, "video/", 6)) {
+ if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) {
needDedicatedLooper = true;
} else {
AString tmp = name;
if (tmp.endsWith(".secure")) {
tmp.erase(tmp.size() - 7, 7);
}
- const MediaCodecList *mcl = MediaCodecList::getInstance();
+ const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
if (codecIdx >= 0) {
- Vector<AString> types;
- if (mcl->getSupportedTypes(codecIdx, &types) == OK) {
- for (size_t i = 0; i < types.size(); i++) {
- if (types[i].startsWith("video/")) {
- needDedicatedLooper = true;
- break;
- }
+ const sp<MediaCodecInfo> info = mcl->getCodecInfo(codecIdx);
+ Vector<AString> mimes;
+ info->getSupportedMimes(&mimes);
+ for (size_t i = 0; i < mimes.size(); i++) {
+ if (mimes[i].startsWith("video/")) {
+ needDedicatedLooper = true;
+ break;
}
}
}
@@ -153,6 +241,14 @@ status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
+ sp<AMessage> msg = new AMessage(kWhatSetCallback, id());
+ msg->setMessage("callback", callback);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
@@ -174,7 +270,20 @@ status_t MediaCodec::configure(
}
sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
+ status_t err = PostAndAwaitResponse(msg, &response);
+
+ if (err != OK && err != INVALID_OPERATION) {
+ // MediaCodec now set state to UNINITIALIZED upon any fatal error.
+ // To maintain backward-compatibility, do a reset() to put codec
+ // back into INITIALIZED state.
+ // But don't reset if the err is INVALID_OPERATION, which means
+ // the configure failure is due to wrong state.
+
+ ALOGE("configure failed with err 0x%08x, resetting...", err);
+ reset();
+ }
+
+ return err;
}
status_t MediaCodec::createInputSurface(
@@ -218,6 +327,41 @@ status_t MediaCodec::release() {
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::reset() {
+ /* When external-facing MediaCodec object is created,
+ it is already initialized. Thus, reset is essentially
+ release() followed by init(), plus clearing the state */
+
+ status_t err = release();
+
+ // unregister handlers
+ if (mCodec != NULL) {
+ if (mCodecLooper != NULL) {
+ mCodecLooper->unregisterHandler(mCodec->id());
+ } else {
+ mLooper->unregisterHandler(mCodec->id());
+ }
+ mCodec = NULL;
+ }
+ mLooper->unregisterHandler(id());
+
+ mFlags = 0; // clear all flags
+ mStickyError = OK;
+
+ // reset state not reset by setState(UNINITIALIZED)
+ mReplyID = 0;
+ mDequeueInputReplyID = 0;
+ mDequeueOutputReplyID = 0;
+ mDequeueInputTimeoutGeneration = 0;
+ mDequeueOutputTimeoutGeneration = 0;
+ mHaveInputSurface = false;
+
+ if (err == OK) {
+ err = init(mInitName, mInitNameIsType, mInitIsEncoder);
+ }
+ return err;
+}
+
status_t MediaCodec::queueInputBuffer(
size_t index,
size_t offset,
@@ -323,6 +467,16 @@ status_t MediaCodec::renderOutputBufferAndRelease(size_t index) {
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) {
+ sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+ msg->setSize("index", index);
+ msg->setInt32("render", true);
+ msg->setInt64("timestampNs", timestampNs);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::releaseOutputBuffer(size_t index) {
sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
msg->setSize("index", index);
@@ -352,6 +506,20 @@ status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
return OK;
}
+status_t MediaCodec::getInputFormat(sp<AMessage> *format) const {
+ sp<AMessage> msg = new AMessage(kWhatGetInputFormat, id());
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findMessage("format", format));
+
+ return OK;
+}
+
status_t MediaCodec::getName(AString *name) const {
sp<AMessage> msg = new AMessage(kWhatGetName, id());
@@ -384,6 +552,55 @@ status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const {
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::getOutputBuffer(size_t index, sp<ABuffer> *buffer) {
+ sp<AMessage> format;
+ return getBufferAndFormat(kPortIndexOutput, index, buffer, &format);
+}
+
+status_t MediaCodec::getOutputFormat(size_t index, sp<AMessage> *format) {
+ sp<ABuffer> buffer;
+ return getBufferAndFormat(kPortIndexOutput, index, &buffer, format);
+}
+
+status_t MediaCodec::getInputBuffer(size_t index, sp<ABuffer> *buffer) {
+ sp<AMessage> format;
+ return getBufferAndFormat(kPortIndexInput, index, buffer, &format);
+}
+
+bool MediaCodec::isExecuting() const {
+ return mState == STARTED || mState == FLUSHED;
+}
+
+status_t MediaCodec::getBufferAndFormat(
+ size_t portIndex, size_t index,
+ sp<ABuffer> *buffer, sp<AMessage> *format) {
+ // use mutex instead of a context switch
+
+ buffer->clear();
+ format->clear();
+ if (!isExecuting()) {
+ return INVALID_OPERATION;
+ }
+
+ // we do not want mPortBuffers to change during this section
+ // we also don't want mOwnedByClient to change during this
+ Mutex::Autolock al(mBufferLock);
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+ if (index < buffers->size()) {
+ const BufferInfo &info = buffers->itemAt(index);
+ if (info.mOwnedByClient) {
+ // by the time buffers array is initialized, crypto is set
+ if (portIndex == kPortIndexInput && mCrypto != NULL) {
+ *buffer = info.mEncryptedData;
+ } else {
+ *buffer = info.mData;
+ }
+ *format = info.mFormat;
+ }
+ }
+ return OK;
+}
+
status_t MediaCodec::flush() {
sp<AMessage> msg = new AMessage(kWhatFlush, id());
@@ -407,9 +624,7 @@ void MediaCodec::requestActivityNotification(const sp<AMessage> &notify) {
void MediaCodec::cancelPendingDequeueOperations() {
if (mFlags & kFlagDequeueInputPending) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
- response->postReply(mDequeueInputReplyID);
+ PostReplyWithError(mDequeueInputReplyID, INVALID_OPERATION);
++mDequeueInputTimeoutGeneration;
mDequeueInputReplyID = 0;
@@ -417,9 +632,7 @@ void MediaCodec::cancelPendingDequeueOperations() {
}
if (mFlags & kFlagDequeueOutputPending) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
- response->postReply(mDequeueOutputReplyID);
+ PostReplyWithError(mDequeueOutputReplyID, INVALID_OPERATION);
++mDequeueOutputTimeoutGeneration;
mDequeueOutputReplyID = 0;
@@ -428,14 +641,12 @@ void MediaCodec::cancelPendingDequeueOperations() {
}
bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
- if (mState != STARTED
- || (mFlags & kFlagStickyError)
+ if (!isExecuting() || (mFlags & kFlagIsAsync)
|| (newRequest && (mFlags & kFlagDequeueInputPending))) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
-
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ return true;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
return true;
}
@@ -456,10 +667,11 @@ bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) {
sp<AMessage> response = new AMessage;
- if (mState != STARTED
- || (mFlags & kFlagStickyError)
+ if (!isExecuting() || (mFlags & kFlagIsAsync)
|| (newRequest && (mFlags & kFlagDequeueOutputPending))) {
response->setInt32("err", INVALID_OPERATION);
+ } else if (mFlags & kFlagStickyError) {
+ response->setInt32("err", getStickyError());
} else if (mFlags & kFlagOutputBuffersChanged) {
response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
mFlags &= ~kFlagOutputBuffersChanged;
@@ -516,22 +728,19 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->findInt32("what", &what));
switch (what) {
- case ACodec::kWhatError:
+ case CodecBase::kWhatError:
{
- int32_t omxError, internalError;
- CHECK(msg->findInt32("omx-error", &omxError));
- CHECK(msg->findInt32("err", &internalError));
-
- ALOGE("Codec reported an error. "
- "(omx error 0x%08x, internalError %d)",
- omxError, internalError);
+ int32_t err, actionCode;
+ CHECK(msg->findInt32("err", &err));
+ CHECK(msg->findInt32("actionCode", &actionCode));
- if (omxError == OMX_ErrorResourcesLost
- && internalError == DEAD_OBJECT) {
+ ALOGE("Codec reported err %#x, actionCode %d, while in state %d",
+ err, actionCode, mState);
+ if (err == DEAD_OBJECT) {
mFlags |= kFlagSawMediaServerDie;
}
- bool sendErrorReponse = true;
+ bool sendErrorResponse = true;
switch (mState) {
case INITIALIZING:
@@ -542,13 +751,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
case CONFIGURING:
{
- setState(INITIALIZED);
+ setState(actionCode == ACTION_CODE_FATAL ?
+ UNINITIALIZED : INITIALIZED);
break;
}
case STARTING:
{
- setState(CONFIGURED);
+ setState(actionCode == ACTION_CODE_FATAL ?
+ UNINITIALIZED : CONFIGURED);
break;
}
@@ -558,7 +769,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// Ignore the error, assuming we'll still get
// the shutdown complete notification.
- sendErrorReponse = false;
+ sendErrorResponse = false;
if (mFlags & kFlagSawMediaServerDie) {
// MediaServer died, there definitely won't
@@ -569,7 +780,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// STOPPING->UNINITIALIZED, instead of the
// usual STOPPING->INITIALIZED state.
setState(UNINITIALIZED);
-
+ if (mState == RELEASING) {
+ mComponentName.clear();
+ }
(new AMessage)->postReply(mReplyID);
}
break;
@@ -577,41 +790,76 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
case FLUSHING:
{
- setState(STARTED);
+ if (actionCode == ACTION_CODE_FATAL) {
+ setState(UNINITIALIZED);
+ } else {
+ setState(
+ (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
+ }
break;
}
+ case FLUSHED:
case STARTED:
{
- sendErrorReponse = false;
+ sendErrorResponse = false;
- mFlags |= kFlagStickyError;
+ setStickyError(err);
postActivityNotificationIfPossible();
cancelPendingDequeueOperations();
+
+ if (mFlags & kFlagIsAsync) {
+ onError(err, actionCode);
+ }
+ switch (actionCode) {
+ case ACTION_CODE_TRANSIENT:
+ break;
+ case ACTION_CODE_RECOVERABLE:
+ setState(INITIALIZED);
+ break;
+ default:
+ setState(UNINITIALIZED);
+ break;
+ }
break;
}
default:
{
- sendErrorReponse = false;
+ sendErrorResponse = false;
- mFlags |= kFlagStickyError;
+ setStickyError(err);
postActivityNotificationIfPossible();
+
+ // actionCode in an uninitialized state is always fatal.
+ if (mState == UNINITIALIZED) {
+ actionCode = ACTION_CODE_FATAL;
+ }
+ if (mFlags & kFlagIsAsync) {
+ onError(err, actionCode);
+ }
+ switch (actionCode) {
+ case ACTION_CODE_TRANSIENT:
+ break;
+ case ACTION_CODE_RECOVERABLE:
+ setState(INITIALIZED);
+ break;
+ default:
+ setState(UNINITIALIZED);
+ break;
+ }
break;
}
}
- if (sendErrorReponse) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", UNKNOWN_ERROR);
-
- response->postReply(mReplyID);
+ if (sendErrorResponse) {
+ PostReplyWithError(mReplyID, err);
}
break;
}
- case ACodec::kWhatComponentAllocated:
+ case CodecBase::kWhatComponentAllocated:
{
CHECK_EQ(mState, INITIALIZING);
setState(INITIALIZED);
@@ -634,21 +882,24 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case ACodec::kWhatComponentConfigured:
+ case CodecBase::kWhatComponentConfigured:
{
CHECK_EQ(mState, CONFIGURING);
- setState(CONFIGURED);
// reset input surface flag
mHaveInputSurface = false;
+ CHECK(msg->findMessage("input-format", &mInputFormat));
+ CHECK(msg->findMessage("output-format", &mOutputFormat));
+
+ setState(CONFIGURED);
(new AMessage)->postReply(mReplyID);
break;
}
- case ACodec::kWhatInputSurfaceCreated:
+ case CodecBase::kWhatInputSurfaceCreated:
{
- // response to ACodec::kWhatCreateInputSurface
+ // response to initiateCreateInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage();
if (!msg->findInt32("err", &err)) {
@@ -664,9 +915,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case ACodec::kWhatSignaledInputEOS:
+ case CodecBase::kWhatSignaledInputEOS:
{
- // response to ACodec::kWhatSignalEndOfInputStream
+ // response to signalEndOfInputStream()
sp<AMessage> response = new AMessage();
status_t err;
if (msg->findInt32("err", &err)) {
@@ -677,8 +928,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
}
- case ACodec::kWhatBuffersAllocated:
+ case CodecBase::kWhatBuffersAllocated:
{
+ Mutex::Autolock al(mBufferLock);
int32_t portIndex;
CHECK(msg->findInt32("portIndex", &portIndex));
@@ -695,8 +947,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
sp<RefBase> obj;
CHECK(msg->findObject("portDesc", &obj));
- sp<ACodec::PortDescription> portDesc =
- static_cast<ACodec::PortDescription *>(obj.get());
+ sp<CodecBase::PortDescription> portDesc =
+ static_cast<CodecBase::PortDescription *>(obj.get());
size_t numBuffers = portDesc->countBuffers();
@@ -729,40 +981,18 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case ACodec::kWhatOutputFormatChanged:
+ case CodecBase::kWhatOutputFormatChanged:
{
ALOGV("codec output format changed");
- if ((mFlags & kFlagIsSoftwareCodec)
- && mNativeWindow != NULL) {
+ if (mSoftRenderer == NULL &&
+ mNativeWindow != NULL &&
+ (mFlags & kFlagIsSoftwareCodec)) {
AString mime;
CHECK(msg->findString("mime", &mime));
- if (!strncasecmp("video/", mime.c_str(), 6)) {
- delete mSoftRenderer;
- mSoftRenderer = NULL;
-
- int32_t width, height;
- CHECK(msg->findInt32("width", &width));
- CHECK(msg->findInt32("height", &height));
-
- int32_t cropLeft, cropTop, cropRight, cropBottom;
- CHECK(msg->findRect("crop",
- &cropLeft, &cropTop, &cropRight, &cropBottom));
-
- int32_t colorFormat;
- CHECK(msg->findInt32(
- "color-format", &colorFormat));
-
- sp<MetaData> meta = new MetaData;
- meta->setInt32(kKeyWidth, width);
- meta->setInt32(kKeyHeight, height);
- meta->setRect(kKeyCropRect,
- cropLeft, cropTop, cropRight, cropBottom);
- meta->setInt32(kKeyColorFormat, colorFormat);
-
- mSoftRenderer =
- new SoftwareRenderer(mNativeWindow, meta);
+ if (mime.startsWithIgnoreCase("video/")) {
+ mSoftRenderer = new SoftwareRenderer(mNativeWindow);
}
}
@@ -773,6 +1003,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// collect codec specific data and amend the output
// format as necessary.
mFlags |= kFlagGatherCodecSpecificData;
+ } else if (mFlags & kFlagIsAsync) {
+ onOutputFormatChanged();
} else {
mFlags |= kFlagOutputFormatChanged;
postActivityNotificationIfPossible();
@@ -780,7 +1012,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case ACodec::kWhatFillThisBuffer:
+ case CodecBase::kWhatFillThisBuffer:
{
/* size_t index = */updateBuffers(kPortIndexInput, msg);
@@ -807,7 +1039,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
ALOGE("queueCSDInputBuffer failed w/ error %d",
err);
- mFlags |= kFlagStickyError;
+ setStickyError(err);
postActivityNotificationIfPossible();
cancelPendingDequeueOperations();
@@ -815,7 +1047,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- if (mFlags & kFlagDequeueInputPending) {
+ if (mFlags & kFlagIsAsync) {
+ if (!mHaveInputSurface) {
+ onInputBufferAvailable();
+ }
+ } else if (mFlags & kFlagDequeueInputPending) {
CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
++mDequeueInputTimeoutGeneration;
@@ -827,7 +1063,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case ACodec::kWhatDrainThisBuffer:
+ case CodecBase::kWhatDrainThisBuffer:
{
/* size_t index = */updateBuffers(kPortIndexOutput, msg);
@@ -862,10 +1098,16 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
}
mFlags &= ~kFlagGatherCodecSpecificData;
- mFlags |= kFlagOutputFormatChanged;
+ if (mFlags & kFlagIsAsync) {
+ onOutputFormatChanged();
+ } else {
+ mFlags |= kFlagOutputFormatChanged;
+ }
}
- if (mFlags & kFlagDequeueOutputPending) {
+ if (mFlags & kFlagIsAsync) {
+ onOutputBufferAvailable();
+ } else if (mFlags & kFlagDequeueOutputPending) {
CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID));
++mDequeueOutputTimeoutGeneration;
@@ -878,32 +1120,41 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case ACodec::kWhatEOS:
+ case CodecBase::kWhatEOS:
{
// We already notify the client of this by using the
// corresponding flag in "onOutputBufferReady".
break;
}
- case ACodec::kWhatShutdownCompleted:
+ case CodecBase::kWhatShutdownCompleted:
{
if (mState == STOPPING) {
setState(INITIALIZED);
} else {
CHECK_EQ(mState, RELEASING);
setState(UNINITIALIZED);
+ mComponentName.clear();
}
(new AMessage)->postReply(mReplyID);
break;
}
- case ACodec::kWhatFlushCompleted:
+ case CodecBase::kWhatFlushCompleted:
{
- CHECK_EQ(mState, FLUSHING);
- setState(STARTED);
+ if (mState != FLUSHING) {
+ ALOGW("received FlushCompleted message in state %d",
+ mState);
+ break;
+ }
- mCodec->signalResume();
+ if (mFlags & kFlagIsAsync) {
+ setState(FLUSHED);
+ } else {
+ setState(STARTED);
+ mCodec->signalResume();
+ }
(new AMessage)->postReply(mReplyID);
break;
@@ -921,10 +1172,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->senderAwaitsResponse(&replyID));
if (mState != UNINITIALIZED) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
@@ -954,16 +1202,45 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatSetCallback:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState == UNINITIALIZED
+ || mState == INITIALIZING
+ || isExecuting()) {
+ // callback can't be set after codec is executing,
+ // or before it's initialized (as the callback
+ // will be cleared when it goes to INITIALIZED)
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+
+ sp<AMessage> callback;
+ CHECK(msg->findMessage("callback", &callback));
+
+ mCallback = callback;
+
+ if (mCallback != NULL) {
+ ALOGI("MediaCodec will operate in async mode");
+ mFlags |= kFlagIsAsync;
+ } else {
+ mFlags &= ~kFlagIsAsync;
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->postReply(replyID);
+ break;
+ }
+
case kWhatConfigure:
{
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
if (mState != INITIALIZED) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
@@ -983,10 +1260,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
->getSurfaceTextureClient());
if (err != OK) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
-
- response->postReply(replyID);
+ PostReplyWithError(replyID, err);
break;
}
} else {
@@ -1024,10 +1298,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// Must be configured, but can't have been started yet.
if (mState != CONFIGURED) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
@@ -1041,11 +1312,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mState != CONFIGURED) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if (mState == FLUSHED) {
+ mCodec->signalResume();
+ PostReplyWithError(replyID, OK);
+ } else if (mState != CONFIGURED) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
@@ -1066,19 +1337,19 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->senderAwaitsResponse(&replyID));
if (mState != INITIALIZED
- && mState != CONFIGURED && mState != STARTED) {
+ && mState != CONFIGURED && !isExecuting()) {
// We may be in "UNINITIALIZED" state already without the
// client being aware of this if media server died while
// we were being stopped. The client would assume that
// after stop() returned, it would be safe to call release()
// and it should be in this case, no harm to allow a release()
// if we're already uninitialized.
- // Similarly stopping a stopped MediaCodec should be benign.
sp<AMessage> response = new AMessage;
- response->setInt32(
- "err",
- mState == targetState ? OK : INVALID_OPERATION);
-
+ status_t err = mState == targetState ? OK : INVALID_OPERATION;
+ response->setInt32("err", err);
+ if (err == OK && targetState == UNINITIALIZED) {
+ mComponentName.clear();
+ }
response->postReply(replyID);
break;
}
@@ -1087,6 +1358,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// It's dead, Jim. Don't expect initiateShutdown to yield
// any useful results now...
setState(UNINITIALIZED);
+ if (targetState == UNINITIALIZED) {
+ mComponentName.clear();
+ }
(new AMessage)->postReply(replyID);
break;
}
@@ -1106,11 +1380,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ if (mFlags & kFlagIsAsync) {
+ ALOGE("dequeueOutputBuffer can't be used in async mode");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+
if (mHaveInputSurface) {
ALOGE("dequeueInputBuffer can't be used with input surface");
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
- response->postReply(replyID);
+ PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
@@ -1122,9 +1400,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->findInt64("timeoutUs", &timeoutUs));
if (timeoutUs == 0ll) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", -EAGAIN);
- response->postReply(replyID);
+ PostReplyWithError(replyID, -EAGAIN);
break;
}
@@ -1153,9 +1429,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(mFlags & kFlagDequeueInputPending);
- sp<AMessage> response = new AMessage;
- response->setInt32("err", -EAGAIN);
- response->postReply(mDequeueInputReplyID);
+ PostReplyWithError(mDequeueInputReplyID, -EAGAIN);
mFlags &= ~kFlagDequeueInputPending;
mDequeueInputReplyID = 0;
@@ -1167,19 +1441,17 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mState != STARTED || (mFlags & kFlagStickyError)) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if (!isExecuting()) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
break;
}
status_t err = onQueueInputBuffer(msg);
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(replyID);
+ PostReplyWithError(replyID, err);
break;
}
@@ -1188,6 +1460,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ if (mFlags & kFlagIsAsync) {
+ ALOGE("dequeueOutputBuffer can't be used in async mode");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+
if (handleDequeueOutputBuffer(replyID, true /* new request */)) {
break;
}
@@ -1196,9 +1474,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->findInt64("timeoutUs", &timeoutUs));
if (timeoutUs == 0ll) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", -EAGAIN);
- response->postReply(replyID);
+ PostReplyWithError(replyID, -EAGAIN);
break;
}
@@ -1227,9 +1503,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(mFlags & kFlagDequeueOutputPending);
- sp<AMessage> response = new AMessage;
- response->setInt32("err", -EAGAIN);
- response->postReply(mDequeueOutputReplyID);
+ PostReplyWithError(mDequeueOutputReplyID, -EAGAIN);
mFlags &= ~kFlagDequeueOutputPending;
mDequeueOutputReplyID = 0;
@@ -1241,19 +1515,17 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mState != STARTED || (mFlags & kFlagStickyError)) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if (!isExecuting()) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
break;
}
status_t err = onReleaseOutputBuffer(msg);
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(replyID);
+ PostReplyWithError(replyID, err);
break;
}
@@ -1262,11 +1534,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mState != STARTED || (mFlags & kFlagStickyError)) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if (!isExecuting()) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
break;
}
@@ -1280,11 +1552,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mState != STARTED || (mFlags & kFlagStickyError)) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if (!isExecuting() || (mFlags & kFlagIsAsync)) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
break;
}
@@ -1314,15 +1586,16 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mState != STARTED || (mFlags & kFlagStickyError)) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if (!isExecuting()) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
break;
}
mReplyID = replyID;
+ // TODO: skip flushing if already FLUSHED
setState(FLUSHING);
mCodec->signalFlush();
@@ -1330,23 +1603,28 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatGetInputFormat:
case kWhatGetOutputFormat:
{
+ sp<AMessage> format =
+ (msg->what() == kWhatGetOutputFormat ? mOutputFormat : mInputFormat);
+
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if ((mState != STARTED && mState != FLUSHING)
- || (mFlags & kFlagStickyError)
- || mOutputFormat == NULL) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ if ((mState != CONFIGURED && mState != STARTING &&
+ mState != STARTED && mState != FLUSHING &&
+ mState != FLUSHED)
+ || format == NULL) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ } else if (mFlags & kFlagStickyError) {
+ PostReplyWithError(replyID, getStickyError());
break;
}
sp<AMessage> response = new AMessage;
- response->setMessage("format", mOutputFormat);
+ response->setMessage("format", format);
response->postReply(replyID);
break;
}
@@ -1372,10 +1650,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->senderAwaitsResponse(&replyID));
if (mComponentName.empty()) {
- sp<AMessage> response = new AMessage;
- response->setInt32("err", INVALID_OPERATION);
-
- response->postReply(replyID);
+ PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
@@ -1395,10 +1670,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
status_t err = onSetParameters(params);
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
-
- response->postReply(replyID);
+ PostReplyWithError(replyID, err);
break;
}
@@ -1421,14 +1693,14 @@ void MediaCodec::extractCSD(const sp<AMessage> &format) {
++i;
}
- ALOGV("Found %u pieces of codec specific data.", mCSD.size());
+ ALOGV("Found %zu pieces of codec specific data.", mCSD.size());
}
status_t MediaCodec::queueCSDInputBuffer(size_t bufferIndex) {
CHECK(!mCSD.empty());
- BufferInfo *info =
- &mPortBuffers[kPortIndexInput].editItemAt(bufferIndex);
+ const BufferInfo *info =
+ &mPortBuffers[kPortIndexInput].itemAt(bufferIndex);
sp<ABuffer> csd = *mCSD.begin();
mCSD.erase(mCSD.begin());
@@ -1463,18 +1735,23 @@ void MediaCodec::setState(State newState) {
mCrypto.clear();
setNativeWindow(NULL);
+ mInputFormat.clear();
mOutputFormat.clear();
mFlags &= ~kFlagOutputFormatChanged;
mFlags &= ~kFlagOutputBuffersChanged;
mFlags &= ~kFlagStickyError;
mFlags &= ~kFlagIsEncoder;
mFlags &= ~kFlagGatherCodecSpecificData;
+ mFlags &= ~kFlagIsAsync;
+ mStickyError = OK;
mActivityNotify.clear();
+ mCallback.clear();
}
if (newState == UNINITIALIZED) {
- mComponentName.clear();
+ // return any straggling buffers, e.g. if we got here on an error
+ returnBuffersToCodec();
// The component is gone, mediaserver's probably back up already
// but should definitely be back up should we try to instantiate
@@ -1485,6 +1762,8 @@ void MediaCodec::setState(State newState) {
mState = newState;
cancelPendingDequeueOperations();
+
+ updateBatteryStat();
}
void MediaCodec::returnBuffersToCodec() {
@@ -1494,6 +1773,7 @@ void MediaCodec::returnBuffersToCodec() {
void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) {
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+ Mutex::Autolock al(mBufferLock);
Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
@@ -1532,6 +1812,8 @@ size_t MediaCodec::updateBuffers(
CHECK(info->mNotify == NULL);
CHECK(msg->findMessage("reply", &info->mNotify));
+ info->mFormat =
+ (portIndex == kPortIndexInput) ? mInputFormat : mOutputFormat;
mAvailPortBuffers[portIndex].push_back(i);
return i;
@@ -1648,11 +1930,15 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
info->mData->setRange(0, result);
}
+ // synchronization boundary for getBufferAndFormat
+ {
+ Mutex::Autolock al(mBufferLock);
+ info->mOwnedByClient = false;
+ }
reply->setBuffer("buffer", info->mData);
reply->post();
info->mNotify = NULL;
- info->mOwnedByClient = false;
return OK;
}
@@ -1666,7 +1952,7 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
render = 0;
}
- if (mState != STARTED) {
+ if (!isExecuting()) {
return -EINVAL;
}
@@ -1680,18 +1966,40 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
return -EACCES;
}
+ // synchronization boundary for getBufferAndFormat
+ {
+ Mutex::Autolock al(mBufferLock);
+ info->mOwnedByClient = false;
+ }
+
if (render && info->mData != NULL && info->mData->size() != 0) {
info->mNotify->setInt32("render", true);
+ int64_t timestampNs = 0;
+ if (msg->findInt64("timestampNs", &timestampNs)) {
+ info->mNotify->setInt64("timestampNs", timestampNs);
+ } else {
+ // TODO: it seems like we should use the timestamp
+ // in the (media)buffer as it potentially came from
+ // an input surface, but we did not propagate it prior to
+ // API 20. Perhaps check for target SDK version.
+#if 0
+ if (info->mData->meta()->findInt64("timeUs", &timestampNs)) {
+ ALOGV("using buffer PTS of %" PRId64, timestampNs);
+ timestampNs *= 1000;
+ }
+#endif
+ }
+
if (mSoftRenderer != NULL) {
mSoftRenderer->render(
- info->mData->data(), info->mData->size(), NULL);
+ info->mData->data(), info->mData->size(),
+ timestampNs, NULL, info->mFormat);
}
}
info->mNotify->post();
info->mNotify = NULL;
- info->mOwnedByClient = false;
return OK;
}
@@ -1710,7 +2018,22 @@ ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index);
CHECK(!info->mOwnedByClient);
- info->mOwnedByClient = true;
+ {
+ Mutex::Autolock al(mBufferLock);
+ info->mOwnedByClient = true;
+
+ // set image-data
+ if (info->mFormat != NULL) {
+ sp<ABuffer> imageData;
+ if (info->mFormat->findBuffer("image-data", &imageData)) {
+ info->mData->meta()->setBuffer("image-data", imageData);
+ }
+ int32_t left, top, right, bottom;
+ if (info->mFormat->findRect("crop", &left, &top, &right, &bottom)) {
+ info->mData->meta()->setRect("crop-rect", left, top, right, bottom);
+ }
+ }
+ }
return index;
}
@@ -1748,16 +2071,100 @@ status_t MediaCodec::setNativeWindow(
return OK;
}
+void MediaCodec::onInputBufferAvailable() {
+ int32_t index;
+ while ((index = dequeuePortBuffer(kPortIndexInput)) >= 0) {
+ sp<AMessage> msg = mCallback->dup();
+ msg->setInt32("callbackID", CB_INPUT_AVAILABLE);
+ msg->setInt32("index", index);
+ msg->post();
+ }
+}
+
+void MediaCodec::onOutputBufferAvailable() {
+ int32_t index;
+ while ((index = dequeuePortBuffer(kPortIndexOutput)) >= 0) {
+ const sp<ABuffer> &buffer =
+ mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+ sp<AMessage> msg = mCallback->dup();
+ msg->setInt32("callbackID", CB_OUTPUT_AVAILABLE);
+ msg->setInt32("index", index);
+ msg->setSize("offset", buffer->offset());
+ msg->setSize("size", buffer->size());
+
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ msg->setInt64("timeUs", timeUs);
+
+ int32_t omxFlags;
+ CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
+
+ uint32_t flags = 0;
+ if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+ flags |= BUFFER_FLAG_SYNCFRAME;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ flags |= BUFFER_FLAG_CODECCONFIG;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_EOS) {
+ flags |= BUFFER_FLAG_EOS;
+ }
+
+ msg->setInt32("flags", flags);
+
+ msg->post();
+ }
+}
+
+void MediaCodec::onError(status_t err, int32_t actionCode, const char *detail) {
+ if (mCallback != NULL) {
+ sp<AMessage> msg = mCallback->dup();
+ msg->setInt32("callbackID", CB_ERROR);
+ msg->setInt32("err", err);
+ msg->setInt32("actionCode", actionCode);
+
+ if (detail != NULL) {
+ msg->setString("detail", detail);
+ }
+
+ msg->post();
+ }
+}
+
+void MediaCodec::onOutputFormatChanged() {
+ if (mCallback != NULL) {
+ sp<AMessage> msg = mCallback->dup();
+ msg->setInt32("callbackID", CB_OUTPUT_FORMAT_CHANGED);
+ msg->setMessage("format", mOutputFormat);
+ msg->post();
+ }
+}
+
+
void MediaCodec::postActivityNotificationIfPossible() {
if (mActivityNotify == NULL) {
return;
}
- if ((mFlags & (kFlagStickyError
+ bool isErrorOrOutputChanged =
+ (mFlags & (kFlagStickyError
| kFlagOutputBuffersChanged
- | kFlagOutputFormatChanged))
+ | kFlagOutputFormatChanged));
+
+ if (isErrorOrOutputChanged
|| !mAvailPortBuffers[kPortIndexInput].empty()
|| !mAvailPortBuffers[kPortIndexOutput].empty()) {
+ mActivityNotify->setInt32("input-buffers",
+ mAvailPortBuffers[kPortIndexInput].size());
+
+ if (isErrorOrOutputChanged) {
+ // we want consumer to dequeue as many times as it can
+ mActivityNotify->setInt32("output-buffers", INT32_MAX);
+ } else {
+ mActivityNotify->setInt32("output-buffers",
+ mAvailPortBuffers[kPortIndexOutput].size());
+ }
mActivityNotify->post();
mActivityNotify.clear();
}
@@ -1818,4 +2225,34 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData(
return OK;
}
+void MediaCodec::updateBatteryStat() {
+ if (mState == CONFIGURED && !mBatteryStatNotified) {
+ AString mime;
+ CHECK(mOutputFormat != NULL &&
+ mOutputFormat->findString("mime", &mime));
+
+ mIsVideo = mime.startsWithIgnoreCase("video/");
+
+ BatteryNotifier& notifier(BatteryNotifier::getInstance());
+
+ if (mIsVideo) {
+ notifier.noteStartVideo();
+ } else {
+ notifier.noteStartAudio();
+ }
+
+ mBatteryStatNotified = true;
+ } else if (mState == UNINITIALIZED && mBatteryStatNotified) {
+ BatteryNotifier& notifier(BatteryNotifier::getInstance());
+
+ if (mIsVideo) {
+ notifier.noteStopVideo();
+ } else {
+ notifier.noteStopAudio();
+ }
+
+ mBatteryStatNotified = false;
+ }
+}
+
} // namespace android