diff options
Diffstat (limited to 'media/libmediaplayerservice')
10 files changed, 291 insertions, 83 deletions
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index ae869d6..0ecfb1e 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1335,21 +1335,23 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid, mCallbackCookie(NULL), mCallbackData(NULL), mBytesWritten(0), + mStreamType(AUDIO_STREAM_MUSIC), + mAttributes(attr), + mLeftVolume(1.0), + mRightVolume(1.0), + mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT), + mSampleRateHz(0), + mMsecsPerFrame(0), + mFrameSize(0), mSessionId(sessionId), mUid(uid), mPid(pid), - mFlags(AUDIO_OUTPUT_FLAG_NONE) { + mSendLevel(0.0), + mAuxEffectId(0), + mFlags(AUDIO_OUTPUT_FLAG_NONE) +{ ALOGV("AudioOutput(%d)", sessionId); - mStreamType = AUDIO_STREAM_MUSIC; - mLeftVolume = 1.0; - mRightVolume = 1.0; - mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; - mSampleRateHz = 0; - mMsecsPerFrame = 0; - mAuxEffectId = 0; - mSendLevel = 0.0; setMinBufferCount(); - mAttributes = attr; } MediaPlayerService::AudioOutput::~AudioOutput() @@ -1358,6 +1360,7 @@ MediaPlayerService::AudioOutput::~AudioOutput() delete mCallbackData; } +//static void MediaPlayerService::AudioOutput::setMinBufferCount() { char value[PROPERTY_VALUE_MAX]; @@ -1367,92 +1370,105 @@ void MediaPlayerService::AudioOutput::setMinBufferCount() } } +// static bool MediaPlayerService::AudioOutput::isOnEmulator() { - setMinBufferCount(); + setMinBufferCount(); // benign race wrt other threads return mIsOnEmulator; } +// static int MediaPlayerService::AudioOutput::getMinBufferCount() { - setMinBufferCount(); + setMinBufferCount(); // benign race wrt other threads return mMinBufferCount; } ssize_t MediaPlayerService::AudioOutput::bufferSize() const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; - return mTrack->frameCount() * frameSize(); + return mTrack->frameCount() * mFrameSize; } ssize_t MediaPlayerService::AudioOutput::frameCount() const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; return mTrack->frameCount(); } ssize_t MediaPlayerService::AudioOutput::channelCount() const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; return mTrack->channelCount(); } ssize_t MediaPlayerService::AudioOutput::frameSize() const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; - return mTrack->frameSize(); + return mFrameSize; } uint32_t MediaPlayerService::AudioOutput::latency () const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return 0; return mTrack->latency(); } float MediaPlayerService::AudioOutput::msecsPerFrame() const { + Mutex::Autolock lock(mLock); return mMsecsPerFrame; } status_t MediaPlayerService::AudioOutput::getPosition(uint32_t *position) const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; return mTrack->getPosition(position); } status_t MediaPlayerService::AudioOutput::getTimestamp(AudioTimestamp &ts) const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; return mTrack->getTimestamp(ts); } status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritten) const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; - *frameswritten = mBytesWritten / frameSize(); + *frameswritten = mBytesWritten / mFrameSize; return OK; } status_t MediaPlayerService::AudioOutput::setParameters(const String8& keyValuePairs) { + Mutex::Autolock lock(mLock); if (mTrack == 0) return NO_INIT; return mTrack->setParameters(keyValuePairs); } String8 MediaPlayerService::AudioOutput::getParameters(const String8& keys) { + Mutex::Autolock lock(mLock); if (mTrack == 0) return String8::empty(); return mTrack->getParameters(keys); } void MediaPlayerService::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) { + Mutex::Autolock lock(mLock); mAttributes = attributes; } -void MediaPlayerService::AudioOutput::deleteRecycledTrack() +void MediaPlayerService::AudioOutput::deleteRecycledTrack_l() { - ALOGV("deleteRecycledTrack"); - + ALOGV("deleteRecycledTrack_l"); if (mRecycledTrack != 0) { if (mCallbackData != NULL) { @@ -1470,12 +1486,17 @@ void MediaPlayerService::AudioOutput::deleteRecycledTrack() // AudioFlinger to drain the track. mRecycledTrack.clear(); + close_l(); delete mCallbackData; mCallbackData = NULL; - close(); } } +void MediaPlayerService::AudioOutput::close_l() +{ + mTrack.clear(); +} + status_t MediaPlayerService::AudioOutput::open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount, @@ -1535,6 +1556,7 @@ status_t MediaPlayerService::AudioOutput::open( } } + Mutex::Autolock lock(mLock); mCallback = cb; mCallbackCookie = cookie; @@ -1577,7 +1599,7 @@ status_t MediaPlayerService::AudioOutput::open( // we must close the previous output before opening a new one if (bothOffloaded && !reuse) { ALOGV("both offloaded and not recycling"); - deleteRecycledTrack(); + deleteRecycledTrack_l(); } sp<AudioTrack> t; @@ -1655,7 +1677,7 @@ status_t MediaPlayerService::AudioOutput::open( if (reuse) { ALOGV("chaining to next output and recycling track"); - close(); + close_l(); mTrack = mRecycledTrack; mRecycledTrack.clear(); if (mCallbackData != NULL) { @@ -1669,7 +1691,7 @@ status_t MediaPlayerService::AudioOutput::open( // we're not going to reuse the track, unblock and flush it // this was done earlier if both tracks are offloaded if (!bothOffloaded) { - deleteRecycledTrack(); + deleteRecycledTrack_l(); } CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL))); @@ -1681,9 +1703,10 @@ status_t MediaPlayerService::AudioOutput::open( mSampleRateHz = sampleRate; mFlags = t->getFlags(); // we suggest the flags above, but new AudioTrack() may not grant it. mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate); + mFrameSize = t->frameSize(); uint32_t pos; if (t->getPosition(&pos) == OK) { - mBytesWritten = uint64_t(pos) * t->frameSize(); + mBytesWritten = uint64_t(pos) * mFrameSize; } mTrack = t; @@ -1704,6 +1727,7 @@ status_t MediaPlayerService::AudioOutput::open( status_t MediaPlayerService::AudioOutput::start() { ALOGV("start"); + Mutex::Autolock lock(mLock); if (mCallbackData != NULL) { mCallbackData->endTrackSwitch(); } @@ -1716,30 +1740,88 @@ status_t MediaPlayerService::AudioOutput::start() } void MediaPlayerService::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) { + Mutex::Autolock lock(mLock); mNextOutput = nextOutput; } - void MediaPlayerService::AudioOutput::switchToNextOutput() { ALOGV("switchToNextOutput"); - if (mNextOutput != NULL) { - if (mCallbackData != NULL) { - mCallbackData->beginTrackSwitch(); + + // Try to acquire the callback lock before moving track (without incurring deadlock). + const unsigned kMaxSwitchTries = 100; + Mutex::Autolock lock(mLock); + for (unsigned tries = 0;;) { + if (mTrack == 0) { + return; } - delete mNextOutput->mCallbackData; - mNextOutput->mCallbackData = mCallbackData; - mCallbackData = NULL; - mNextOutput->mRecycledTrack = mTrack; - mTrack.clear(); - mNextOutput->mSampleRateHz = mSampleRateHz; - mNextOutput->mMsecsPerFrame = mMsecsPerFrame; - mNextOutput->mBytesWritten = mBytesWritten; - mNextOutput->mFlags = mFlags; + if (mNextOutput != NULL && mNextOutput != this) { + if (mCallbackData != NULL) { + // two alternative approaches +#if 1 + CallbackData *callbackData = mCallbackData; + mLock.unlock(); + // proper acquisition sequence + callbackData->lock(); + mLock.lock(); + // Caution: it is unlikely that someone deleted our callback or changed our target + if (callbackData != mCallbackData || mNextOutput == NULL || mNextOutput == this) { + // fatal if we are starved out. + LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries, + "switchToNextOutput() cannot obtain correct lock sequence"); + callbackData->unlock(); + continue; + } + callbackData->mSwitching = true; // begin track switch +#else + // tryBeginTrackSwitch() returns false if the callback has the lock. + if (!mCallbackData->tryBeginTrackSwitch()) { + // fatal if we are starved out. + LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries, + "switchToNextOutput() cannot obtain callback lock"); + mLock.unlock(); + usleep(5 * 1000 /* usec */); // allow callback to use AudioOutput + mLock.lock(); + continue; + } +#endif + } + + Mutex::Autolock nextLock(mNextOutput->mLock); + + // If the next output track is not NULL, then it has been + // opened already for playback. + // This is possible even without the next player being started, + // for example, the next player could be prepared and seeked. + // + // Presuming it isn't advisable to force the track over. + if (mNextOutput->mTrack == NULL) { + ALOGD("Recycling track for gapless playback"); + delete mNextOutput->mCallbackData; + mNextOutput->mCallbackData = mCallbackData; + mNextOutput->mRecycledTrack = mTrack; + mNextOutput->mSampleRateHz = mSampleRateHz; + mNextOutput->mMsecsPerFrame = mMsecsPerFrame; + mNextOutput->mBytesWritten = mBytesWritten; + mNextOutput->mFlags = mFlags; + mNextOutput->mFrameSize = mFrameSize; + close_l(); + mCallbackData = NULL; // destruction handled by mNextOutput + } else { + ALOGW("Ignoring gapless playback because next player has already started"); + // remove track in case resource needed for future players. + if (mCallbackData != NULL) { + mCallbackData->endTrackSwitch(); // release lock for callbacks before close. + } + close_l(); + } + } + break; } } ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size, bool blocking) { + Mutex::Autolock lock(mLock); LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback."); //ALOGV("write(%p, %u)", buffer, size); @@ -1756,6 +1838,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size, void MediaPlayerService::AudioOutput::stop() { ALOGV("stop"); + Mutex::Autolock lock(mLock); mBytesWritten = 0; if (mTrack != 0) mTrack->stop(); } @@ -1763,6 +1846,7 @@ void MediaPlayerService::AudioOutput::stop() void MediaPlayerService::AudioOutput::flush() { ALOGV("flush"); + Mutex::Autolock lock(mLock); mBytesWritten = 0; if (mTrack != 0) mTrack->flush(); } @@ -1770,18 +1854,21 @@ void MediaPlayerService::AudioOutput::flush() void MediaPlayerService::AudioOutput::pause() { ALOGV("pause"); + Mutex::Autolock lock(mLock); if (mTrack != 0) mTrack->pause(); } void MediaPlayerService::AudioOutput::close() { ALOGV("close"); - mTrack.clear(); + Mutex::Autolock lock(mLock); + close_l(); } void MediaPlayerService::AudioOutput::setVolume(float left, float right) { ALOGV("setVolume(%f, %f)", left, right); + Mutex::Autolock lock(mLock); mLeftVolume = left; mRightVolume = right; if (mTrack != 0) { @@ -1793,6 +1880,7 @@ status_t MediaPlayerService::AudioOutput::setPlaybackRate(const AudioPlaybackRat { ALOGV("setPlaybackRate(%f %f %d %d)", rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode); + Mutex::Autolock lock(mLock); if (mTrack == 0) { // remember rate so that we can set it when the track is opened mPlaybackRate = rate; @@ -1814,6 +1902,7 @@ status_t MediaPlayerService::AudioOutput::setPlaybackRate(const AudioPlaybackRat status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate) { ALOGV("setPlaybackRate"); + Mutex::Autolock lock(mLock); if (mTrack == 0) { return NO_INIT; } @@ -1824,6 +1913,7 @@ status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rat status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level) { ALOGV("setAuxEffectSendLevel(%f)", level); + Mutex::Autolock lock(mLock); mSendLevel = level; if (mTrack != 0) { return mTrack->setAuxEffectSendLevel(level); @@ -1834,6 +1924,7 @@ status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level) status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId) { ALOGV("attachAuxEffect(%d)", effectId); + Mutex::Autolock lock(mLock); mAuxEffectId = effectId; if (mTrack != 0) { return mTrack->attachAuxEffect(effectId); @@ -1846,6 +1937,7 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( int event, void *cookie, void *info) { //ALOGV("callbackwrapper"); CallbackData *data = (CallbackData*)cookie; + // lock to ensure we aren't caught in the middle of a track switch. data->lock(); AudioOutput *me = data->getOutput(); AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; @@ -1915,11 +2007,13 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( int MediaPlayerService::AudioOutput::getSessionId() const { + Mutex::Autolock lock(mLock); return mSessionId; } uint32_t MediaPlayerService::AudioOutput::getSampleRate() const { + Mutex::Autolock lock(mLock); if (mTrack == 0) return 0; return mTrack->getSampleRate(); } diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 7527506..9e6ca52 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -132,7 +132,8 @@ class MediaPlayerService : public BnMediaPlayerService static void setMinBufferCount(); static void CallbackWrapper( int event, void *me, void *info); - void deleteRecycledTrack(); + void deleteRecycledTrack_l(); + void close_l(); sp<AudioTrack> mTrack; sp<AudioTrack> mRecycledTrack; @@ -148,32 +149,47 @@ class MediaPlayerService : public BnMediaPlayerService AudioPlaybackRate mPlaybackRate; uint32_t mSampleRateHz; // sample rate of the content, as set in open() float mMsecsPerFrame; + size_t mFrameSize; int mSessionId; int mUid; int mPid; float mSendLevel; int mAuxEffectId; + audio_output_flags_t mFlags; + mutable Mutex mLock; + + // static variables below not protected by mutex static bool mIsOnEmulator; static int mMinBufferCount; // 12 for emulator; otherwise 4 - audio_output_flags_t mFlags; // CallbackData is what is passed to the AudioTrack as the "user" data. // We need to be able to target this to a different Output on the fly, // so we can't use the Output itself for this. class CallbackData { + friend AudioOutput; public: CallbackData(AudioOutput *cookie) { mData = cookie; mSwitching = false; } - AudioOutput * getOutput() { return mData;} + AudioOutput * getOutput() const { return mData; } void setOutput(AudioOutput* newcookie) { mData = newcookie; } // lock/unlock are used by the callback before accessing the payload of this object - void lock() { mLock.lock(); } - void unlock() { mLock.unlock(); } - // beginTrackSwitch/endTrackSwitch are used when this object is being handed over + void lock() const { mLock.lock(); } + void unlock() const { mLock.unlock(); } + + // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over // to the next sink. - void beginTrackSwitch() { mLock.lock(); mSwitching = true; } + + // tryBeginTrackSwitch() returns true only if it obtains the lock. + bool tryBeginTrackSwitch() { + LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called"); + if (mLock.tryLock() != OK) { + return false; + } + mSwitching = true; + return true; + } void endTrackSwitch() { if (mSwitching) { mLock.unlock(); @@ -182,7 +198,7 @@ class MediaPlayerService : public BnMediaPlayerService } private: AudioOutput * mData; - mutable Mutex mLock; + mutable Mutex mLock; // a recursive mutex might make this unnecessary. bool mSwitching; DISALLOW_EVIL_CONSTRUCTORS(CallbackData); }; diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk index 20193c3..cc6f743 100644 --- a/media/libmediaplayerservice/nuplayer/Android.mk +++ b/media/libmediaplayerservice/nuplayer/Android.mk @@ -25,7 +25,8 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libmediaplayerservice \ $(TOP)/frameworks/native/include/media/openmax -LOCAL_CFLAGS += -Werror -Wall +LOCAL_CFLAGS += -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS + LOCAL_CLANG := true LOCAL_MODULE:= libstagefright_nuplayer diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 64d172e..7dc9be7 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -1005,7 +1005,9 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( status_t result = track->mPackets->dequeueAccessUnit(accessUnit); - if (!track->mPackets->hasBufferAvailable(&finalResult)) { + // start pulling in more buffers if we only have one (or no) buffer left + // so that decoder has less chance of being starved + if (track->mPackets->getAvailableBufferCount(&finalResult) < 2) { postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO); } @@ -1458,6 +1460,8 @@ void NuPlayer::GenericSource::readBuffer( track = &mVideoTrack; if (mIsWidevine) { maxBuffers = 2; + } else { + maxBuffers = 4; } break; case MEDIA_TRACK_TYPE_AUDIO: diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index a9d8904..2fdc196 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -623,12 +623,19 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { case kWhatSetVideoSurface: { - ALOGV("kWhatSetVideoSurface"); sp<RefBase> obj; CHECK(msg->findObject("surface", &obj)); sp<Surface> surface = static_cast<Surface *>(obj.get()); - if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL) { + + ALOGD("onSetVideoSurface(%p, %s video decoder)", + surface.get(), + (mSource != NULL && mSource->getFormat(false /* audio */) != NULL + && mVideoDecoder != NULL) ? "have" : "no"); + + if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL + // NOTE: mVideoDecoder's mSurface is always non-null + || (mVideoDecoder != NULL && mVideoDecoder->setVideoSurface(surface) == OK)) { performSetSurface(surface); break; } @@ -639,7 +646,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mDeferredActions.push_back(new SetSurfaceAction(surface)); - if (obj != NULL) { + if (obj != NULL || mAudioDecoder != NULL) { if (mStarted) { // Issue a seek to refresh the video screen only if started otherwise // the extractor may not yet be started and will assert. @@ -830,20 +837,6 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { // Don't try to re-open audio sink if there's an existing decoder. if (mAudioSink != NULL && mAudioDecoder == NULL) { - sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); - sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); - audio_stream_type_t streamType = mAudioSink->getAudioStreamType(); - const bool hasVideo = (videoFormat != NULL); - const bool canOffload = canOffloadStream( - audioMeta, hasVideo, mSource->isStreaming(), streamType); - if (canOffload) { - if (!mOffloadAudio) { - mRenderer->signalEnableOffloadAudio(); - } - // open audio sink early under offload mode. - sp<AMessage> format = mSource->getFormat(true /*audio*/); - tryOpenAudioSinkForOffload(format, hasVideo); - } instantiateDecoder(true, &mAudioDecoder); } @@ -1078,6 +1071,11 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findInt32("audio", &audio)); ALOGV("renderer %s flush completed.", audio ? "audio" : "video"); + if (audio && (mFlushingAudio == NONE || mFlushingAudio == FLUSHED + || mFlushingAudio == SHUT_DOWN)) { + // Flush has been handled by tear down. + break; + } handleFlushComplete(audio, false /* isDecoder */); finishFlushIfPossible(); } else if (what == Renderer::kWhatVideoRenderingStart) { @@ -1086,14 +1084,27 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("media rendering started"); notifyListener(MEDIA_STARTED, 0, 0); } else if (what == Renderer::kWhatAudioTearDown) { - int64_t positionUs; - CHECK(msg->findInt64("positionUs", &positionUs)); int32_t reason; CHECK(msg->findInt32("reason", &reason)); ALOGV("Tear down audio with reason %d.", reason); - closeAudioSink(); mAudioDecoder.clear(); ++mAudioDecoderGeneration; + bool needsToCreateAudioDecoder = true; + if (mFlushingAudio == FLUSHING_DECODER) { + mFlushComplete[1 /* audio */][1 /* isDecoder */] = true; + mFlushingAudio = FLUSHED; + finishFlushIfPossible(); + } else if (mFlushingAudio == FLUSHING_DECODER_SHUTDOWN + || mFlushingAudio == SHUTTING_DOWN_DECODER) { + mFlushComplete[1 /* audio */][1 /* isDecoder */] = true; + mFlushingAudio = SHUT_DOWN; + finishFlushIfPossible(); + needsToCreateAudioDecoder = false; + } + if (mRenderer == NULL) { + break; + } + closeAudioSink(); mRenderer->flush( true /* audio */, false /* notifyComplete */); if (mVideoDecoder != NULL) { @@ -1101,23 +1112,11 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { false /* audio */, false /* notifyComplete */); } + int64_t positionUs; + CHECK(msg->findInt64("positionUs", &positionUs)); performSeek(positionUs); - if (reason == Renderer::kDueToError) { - sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); - sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); - audio_stream_type_t streamType = mAudioSink->getAudioStreamType(); - const bool hasVideo = (videoFormat != NULL); - const bool canOffload = canOffloadStream( - audioMeta, hasVideo, mSource->isStreaming(), streamType); - if (canOffload) { - mRenderer->signalEnableOffloadAudio(); - sp<AMessage> format = mSource->getFormat(true /*audio*/); - tryOpenAudioSinkForOffload(format, hasVideo); - } else { - mRenderer->signalDisableOffloadAudio(); - mOffloadAudio = false; - } + if (reason == Renderer::kDueToError && needsToCreateAudioDecoder) { instantiateDecoder(true /* audio */, &mAudioDecoder); } } @@ -1449,6 +1448,36 @@ void NuPlayer::closeAudioSink() { mRenderer->closeAudioSink(); } +void NuPlayer::determineAudioModeChange() { + if (mSource == NULL || mAudioSink == NULL) { + return; + } + + if (mRenderer == NULL) { + ALOGW("No renderer can be used to determine audio mode. Use non-offload for safety."); + mOffloadAudio = false; + return; + } + + sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); + sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); + audio_stream_type_t streamType = mAudioSink->getAudioStreamType(); + const bool hasVideo = (videoFormat != NULL); + const bool canOffload = canOffloadStream( + audioMeta, hasVideo, mSource->isStreaming(), streamType); + if (canOffload) { + if (!mOffloadAudio) { + mRenderer->signalEnableOffloadAudio(); + } + // open audio sink early under offload mode. + sp<AMessage> format = mSource->getFormat(true /*audio*/); + tryOpenAudioSinkForOffload(format, hasVideo); + } else { + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; + } +} + status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) { if (*decoder != NULL) { return OK; @@ -1490,6 +1519,7 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) { ++mAudioDecoderGeneration; notify->setInt32("generation", mAudioDecoderGeneration); + determineAudioModeChange(); if (mOffloadAudio) { const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL); format->setInt32("has-video", hasVideo); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 38904bc..9f4c462 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -220,6 +220,7 @@ private: void tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVideo); void closeAudioSink(); + void determineAudioModeChange(); status_t instantiateDecoder(bool audio, sp<DecoderBase> *decoder); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 99a2a84..dcc28c4 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -87,6 +87,22 @@ sp<AMessage> NuPlayer::Decoder::getStats() const { return mStats; } +status_t NuPlayer::Decoder::setVideoSurface(const sp<Surface> &surface) { + if (surface == NULL || ADebug::isExperimentEnabled("legacy-setsurface")) { + return BAD_VALUE; + } + + sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this); + + msg->setObject("surface", surface); + sp<AMessage> response; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findInt32("err", &err)); + } + return err; +} + void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str()); @@ -169,6 +185,46 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatSetVideoSurface: + { + sp<AReplyToken> replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<RefBase> obj; + CHECK(msg->findObject("surface", &obj)); + sp<Surface> surface = static_cast<Surface *>(obj.get()); // non-null + int32_t err = INVALID_OPERATION; + // NOTE: in practice mSurface is always non-null, but checking here for completeness + if (mCodec != NULL && mSurface != NULL) { + // TODO: once AwesomePlayer is removed, remove this automatic connecting + // to the surface by MediaPlayerService. + // + // at this point MediaPlayerService::client has already connected to the + // surface, which MediaCodec does not expect + err = native_window_api_disconnect(surface.get(), NATIVE_WINDOW_API_MEDIA); + if (err == OK) { + err = mCodec->setSurface(surface); + ALOGI_IF(err, "codec setSurface returned: %d", err); + if (err == OK) { + // reconnect to the old surface as MPS::Client will expect to + // be able to disconnect from it. + (void)native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_MEDIA); + mSurface = surface; + } + } + if (err != OK) { + // reconnect to the new surface on error as MPS::Client will expect to + // be able to disconnect from it. + (void)native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA); + } + } + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + default: DecoderBase::onMessageReceived(msg); break; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index ceccb7a..ed0be62 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -32,6 +32,9 @@ struct NuPlayer::Decoder : public DecoderBase { virtual sp<AMessage> getStats() const; + // sets the output surface of video decoders. + virtual status_t setVideoSurface(const sp<Surface> &surface); + protected: virtual ~Decoder(); @@ -50,6 +53,7 @@ private: enum { kWhatCodecNotify = 'cdcN', kWhatRenderBuffer = 'rndr', + kWhatSetVideoSurface = 'sSur' }; sp<Surface> mSurface; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h index 8f030f0..b0dc01d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h @@ -27,6 +27,7 @@ namespace android { struct ABuffer; struct MediaCodec; class MediaBuffer; +class Surface; struct NuPlayer::DecoderBase : public AHandler { DecoderBase(const sp<AMessage> ¬ify); @@ -36,6 +37,7 @@ struct NuPlayer::DecoderBase : public AHandler { void setParameters(const sp<AMessage> ¶ms); void setRenderer(const sp<Renderer> &renderer); + virtual status_t setVideoSurface(const sp<Surface> &) { return INVALID_OPERATION; } status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; void signalFlush(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 7e55aac..13a7d94 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -266,6 +266,7 @@ void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) { Mutex::Autolock autoLock(mLock); if (audio) { mNotifyCompleteAudio |= notifyComplete; + clearAudioFirstAnchorTime_l(); ++mAudioQueueGeneration; ++mAudioDrainGeneration; } else { @@ -275,7 +276,6 @@ void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) { } clearAnchorTime_l(); - clearAudioFirstAnchorTime_l(); mVideoLateByUs = 0; mSyncQueues = false; } |