diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2014-11-06 17:49:48 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2014-11-06 17:49:48 -0800 |
commit | e1a2df553a6d151807a5da738a3cd853bef908d9 (patch) | |
tree | 9015c1c9ad9ec69f1962657f70fe3df386fbb05a /media/libstagefright/MediaCodec.cpp | |
parent | bcf093bfef277a8ec0119da9e84e5abac58ad0b1 (diff) | |
parent | 841daebc75fbf5e7fb4dd71cab559b8f4d7150ae (diff) | |
download | frameworks_av-e1a2df553a6d151807a5da738a3cd853bef908d9.zip frameworks_av-e1a2df553a6d151807a5da738a3cd853bef908d9.tar.gz frameworks_av-e1a2df553a6d151807a5da738a3cd853bef908d9.tar.bz2 |
Resolve conflict
Diffstat (limited to 'media/libstagefright/MediaCodec.cpp')
-rw-r--r-- | media/libstagefright/MediaCodec.cpp | 871 |
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> ¬ify) { 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", ×tampNs)) { + 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", ×tampNs)) { + 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 |