diff options
30 files changed, 771 insertions, 414 deletions
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h index 24df85a..65f415a 100644 --- a/include/media/stagefright/foundation/ADebug.h +++ b/include/media/stagefright/foundation/ADebug.h @@ -117,7 +117,7 @@ struct ADebug { }; - // parse the property or string to get the debug level for a component name + // parse the property or string to get a long-type level for a component name // string format is: // <level>[:<glob>][,<level>[:<glob>]...] // - <level> is 0-5 corresponding to ADebug::Level @@ -125,10 +125,14 @@ struct ADebug { // matches all components // - string is read left-to-right, and the last matching level is returned, or // the def if no terms matched + static long GetLevelFromSettingsString( + const char *name, const char *value, long def); + static long GetLevelFromProperty( + const char *name, const char *value, long def); + + // same for ADebug::Level - performs clamping to valid debug ranges static Level GetDebugLevelFromProperty( const char *name, const char *propertyName, Level def = kDebugNone); - static Level GetDebugLevelFromString( - const char *name, const char *value, Level def = kDebugNone); // remove redundant segments of a codec name, and return a newly allocated // string suitable for debugging diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index f13bcf3..3bfb09a 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -476,11 +476,13 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even switch (event) { case AUDIO_OUTPUT_OPENED: case AUDIO_INPUT_OPENED: { - if (getIoDescriptor(ioDesc->mIoHandle) != 0) { - ALOGV("ioConfigChanged() opening already existing output! %d", ioDesc->mIoHandle); - break; + sp<AudioIoDescriptor> oldDesc = getIoDescriptor(ioDesc->mIoHandle); + if (oldDesc == 0) { + mIoDescriptors.add(ioDesc->mIoHandle, ioDesc); + } else { + deviceId = oldDesc->getDeviceId(); + mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); } - mIoDescriptors.add(ioDesc->mIoHandle, ioDesc); if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) { deviceId = ioDesc->getDeviceId(); @@ -1074,7 +1076,14 @@ status_t AudioSystem::addAudioDeviceCallback( if (afc == 0) { return NO_INIT; } - return afc->addAudioDeviceCallback(callback, audioIo); + status_t status = afc->addAudioDeviceCallback(callback, audioIo); + if (status == NO_ERROR) { + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af != 0) { + af->registerClient(afc); + } + } + return status; } status_t AudioSystem::removeAudioDeviceCallback( 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/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index a9d8904..4a1a34d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -639,7 +639,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. diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 7452e4b..9206b5c 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1063,9 +1063,11 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { for (OMX_U32 i = cancelStart; i < cancelEnd; i++) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); - status_t error = cancelBufferToNativeWindow(info); - if (err == 0) { - err = error; + if (info->mStatus == BufferInfo::OWNED_BY_US) { + status_t error = cancelBufferToNativeWindow(info); + if (err == 0) { + err = error; + } } } @@ -1152,9 +1154,11 @@ status_t ACodec::allocateOutputMetadataBuffers() { for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); - status_t error = cancelBufferToNativeWindow(info); - if (err == OK) { - err = error; + if (info->mStatus == BufferInfo::OWNED_BY_US) { + status_t error = cancelBufferToNativeWindow(info); + if (err == OK) { + err = error; + } } } @@ -4864,6 +4868,12 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { case RESUBMIT_BUFFERS: { if (buffer != NULL && !mCodec->mPortEOS[kPortIndexInput]) { + // Do not send empty input buffer w/o EOS to the component. + if (buffer->size() == 0 && !eos) { + postFillThisBuffer(info); + break; + } + int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 8bf47b1..b696746 100755 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -4313,6 +4313,14 @@ status_t MPEG4Source::fragmentedRead( if ((!mIsAVC && !mIsHEVC)|| mWantsNALFragments) { if (newBuffer) { + if (!isInRange((size_t)0u, mBuffer->size(), size)) { + mBuffer->release(); + mBuffer = NULL; + + ALOGE("fragmentedRead ERROR_MALFORMED size %zu", size); + return ERROR_MALFORMED; + } + ssize_t num_bytes_read = mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); @@ -4320,7 +4328,7 @@ status_t MPEG4Source::fragmentedRead( mBuffer->release(); mBuffer = NULL; - ALOGV("i/o error"); + ALOGE("i/o error"); return ERROR_IO; } @@ -4392,18 +4400,40 @@ status_t MPEG4Source::fragmentedRead( ssize_t num_bytes_read = 0; int32_t drm = 0; bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0); + void *data = NULL; + bool isMalFormed = false; if (usesDRM) { - num_bytes_read = - mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size); + if (mBuffer == NULL || !isInRange((size_t)0u, mBuffer->size(), size)) { + isMalFormed = true; + } else { + data = mBuffer->data(); + } } else { - num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size); + int32_t max_size; + if (mFormat == NULL + || !mFormat->findInt32(kKeyMaxInputSize, &max_size) + || !isInRange((size_t)0u, (size_t)max_size, size)) { + isMalFormed = true; + } else { + data = mSrcBuffer; + } } + if (isMalFormed || data == NULL) { + ALOGE("isMalFormed size %zu", size); + if (mBuffer != NULL) { + mBuffer->release(); + mBuffer = NULL; + } + return ERROR_MALFORMED; + } + num_bytes_read = mDataSource->readAt(offset, data, size); + if (num_bytes_read < (ssize_t)size) { mBuffer->release(); mBuffer = NULL; - ALOGV("i/o error"); + ALOGE("i/o error"); return ERROR_IO; } @@ -4417,16 +4447,18 @@ status_t MPEG4Source::fragmentedRead( size_t dstOffset = 0; while (srcOffset < size) { - bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize); + isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize); size_t nalLength = 0; if (!isMalFormed) { nalLength = parseNALSize(&mSrcBuffer[srcOffset]); srcOffset += mNALLengthSize; - isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength); + isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength) + || !isInRange((size_t)0u, mBuffer->size(), dstOffset, (size_t)4u) + || !isInRange((size_t)0u, mBuffer->size(), dstOffset + 4, nalLength); } if (isMalFormed) { - ALOGE("Video is malformed"); + ALOGE("Video is malformed; nalLength %zu", nalLength); mBuffer->release(); mBuffer = NULL; return ERROR_MALFORMED; diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp index f3af777..aab3af7 100644 --- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp @@ -115,6 +115,7 @@ SoftAVC::SoftAVC( kProfileLevels, ARRAY_SIZE(kProfileLevels), 320 /* width */, 240 /* height */, callbacks, appData, component), + mCodecCtx(NULL), mMemRecords(NULL), mFlushOutBuffer(NULL), mOmxColorFormat(OMX_COLOR_FormatYUV420Planar), @@ -122,7 +123,8 @@ SoftAVC::SoftAVC( mNewWidth(mWidth), mNewHeight(mHeight), mNewLevel(0), - mChangingResolution(false) { + mChangingResolution(false), + mSignalledError(false) { initPorts( kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE); @@ -132,7 +134,7 @@ SoftAVC::SoftAVC( GENERATE_FILE_NAMES(); CREATE_DUMP_FILE(mInFile); - CHECK_EQ(initDecoder(), (status_t)OK); + CHECK_EQ(initDecoder(mWidth, mHeight), (status_t)OK); } SoftAVC::~SoftAVC() { @@ -232,6 +234,7 @@ status_t SoftAVC::resetDecoder() { ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code); return UNKNOWN_ERROR; } + mSignalledError = false; /* Set the run-time (dynamic) parameters */ setParams(outputBufferWidth()); @@ -285,7 +288,7 @@ status_t SoftAVC::setFlushMode() { return OK; } -status_t SoftAVC::initDecoder() { +status_t SoftAVC::initDecoder(uint32_t width, uint32_t height) { IV_API_CALL_STATUS_T status; UWORD32 u4_num_reorder_frames; @@ -294,14 +297,15 @@ status_t SoftAVC::initDecoder() { WORD32 i4_level; mNumCores = GetCPUCoreCount(); + mCodecCtx = NULL; /* Initialize number of ref and reorder modes (for H264) */ u4_num_reorder_frames = 16; u4_num_ref_frames = 16; u4_share_disp_buf = 0; - uint32_t displayStride = outputBufferWidth(); - uint32_t displayHeight = outputBufferHeight(); + uint32_t displayStride = mIsAdaptive ? mAdaptiveMaxWidth : width; + uint32_t displayHeight = mIsAdaptive ? mAdaptiveMaxHeight : height; uint32_t displaySizeY = displayStride * displayHeight; if(mNewLevel == 0){ @@ -435,6 +439,7 @@ status_t SoftAVC::initDecoder() { status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op); if (status != IV_SUCCESS) { + mCodecCtx = NULL; ALOGE("Error in init: 0x%x", s_init_op.s_ivd_init_op_t.u4_error_code); return UNKNOWN_ERROR; @@ -494,12 +499,12 @@ status_t SoftAVC::deInitDecoder() { return OK; } -status_t SoftAVC::reInitDecoder() { +status_t SoftAVC::reInitDecoder(uint32_t width, uint32_t height) { status_t ret; deInitDecoder(); - ret = initDecoder(); + ret = initDecoder(width, height); if (OK != ret) { ALOGE("Create failure"); deInitDecoder(); @@ -511,6 +516,7 @@ status_t SoftAVC::reInitDecoder() { void SoftAVC::onReset() { SoftVideoDecoderOMXComponent::onReset(); + mSignalledError = false; resetDecoder(); resetPlugin(); } @@ -520,7 +526,12 @@ OMX_ERRORTYPE SoftAVC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR p const uint32_t oldHeight = mHeight; OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params); if (mWidth != oldWidth || mHeight != oldHeight) { - reInitDecoder(); + status_t err = reInitDecoder(mNewWidth, mNewHeight); + if (err != OK) { + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); + mSignalledError = true; + return OMX_ErrorUnsupportedSetting; + } } return ret; } @@ -595,6 +606,9 @@ void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) { void SoftAVC::onQueueFilled(OMX_U32 portIndex) { UNUSED(portIndex); + if (mSignalledError) { + return; + } if (mOutputPortSettingsChange != NONE) { return; } @@ -627,6 +641,11 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { if (!inQueue.empty()) { inInfo = *inQueue.begin(); inHeader = inInfo->mHeader; + if (inHeader == NULL) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + continue; + } } else { break; } @@ -638,14 +657,21 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { outHeader->nTimeStamp = 0; outHeader->nOffset = 0; - if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { - mReceivedEOS = true; + if (inHeader != NULL) { if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); + + if (!(inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + continue; + } + + mReceivedEOS = true; inHeader = NULL; setFlushMode(); + } else if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mReceivedEOS = true; } } @@ -653,9 +679,15 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { // update output port's definition and reinitialize decoder. if (mInitNeeded && !mIsInFlush) { bool portWillReset = false; - handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); - CHECK_EQ(reInitDecoder(), (status_t)OK); + status_t err = reInitDecoder(mNewWidth, mNewHeight); + if (err != OK) { + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); + mSignalledError = true; + return; + } + + handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); return; } @@ -714,13 +746,22 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { mTimeStampsValid[timeStampIx] = false; } + // This is needed to handle CTS DecoderTest testCodecResetsH264WithoutSurface, // which is not sending SPS/PPS after port reconfiguration and flush to the codec. if (unsupportedDimensions && !mFlushNeeded) { bool portWillReset = false; - handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht); + mNewWidth = s_dec_op.u4_pic_wd; + mNewHeight = s_dec_op.u4_pic_ht; - CHECK_EQ(reInitDecoder(), (status_t)OK); + status_t err = reInitDecoder(mNewWidth, mNewHeight); + if (err != OK) { + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); + mSignalledError = true; + return; + } + + handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); @@ -732,7 +773,12 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { mNewLevel = 51; - CHECK_EQ(reInitDecoder(), (status_t)OK); + status_t err = reInitDecoder(mNewWidth, mNewHeight); + if (err != OK) { + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); + mSignalledError = true; + return; + } setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h index 2067810..1ec8991 100644 --- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h @@ -105,8 +105,9 @@ private: // codec. So the codec is switching to decode the new resolution. bool mChangingResolution; bool mFlushNeeded; + bool mSignalledError; - status_t initDecoder(); + status_t initDecoder(uint32_t width, uint32_t height); status_t deInitDecoder(); status_t setFlushMode(); status_t setParams(size_t stride); @@ -114,7 +115,7 @@ private: status_t setNumCores(); status_t resetDecoder(); status_t resetPlugin(); - status_t reInitDecoder(); + status_t reInitDecoder(uint32_t width, uint32_t height); void setDecodeArgs( ivd_video_decode_ip_t *ps_dec_ip, diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index ede645c..0c1a149 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -103,34 +103,41 @@ void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) { while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + if (inHeader == NULL) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + continue; + } PortInfo *port = editPortInfo(1); OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; - if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { + if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); ++mInputBufferCount; - outHeader->nFilledLen = 0; - outHeader->nFlags = OMX_BUFFERFLAG_EOS; + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; - List<BufferInfo *>::iterator it = outQueue.begin(); - while ((*it)->mHeader != outHeader) { - ++it; - } + List<BufferInfo *>::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } - BufferInfo *outInfo = *it; - outInfo->mOwnedByUs = false; - outQueue.erase(it); - outInfo = NULL; + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; - notifyFillBufferDone(outHeader); - outHeader = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } return; } diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp index 0d1cea4..24fa561 100644 --- a/media/libstagefright/foundation/ADebug.cpp +++ b/media/libstagefright/foundation/ADebug.cpp @@ -32,11 +32,10 @@ namespace android { //static -ADebug::Level ADebug::GetDebugLevelFromString( - const char *name, const char *value, ADebug::Level def) { +long ADebug::GetLevelFromSettingsString( + const char *name, const char *value, long def) { // split on , const char *next = value, *current; - const unsigned long maxLevel = (unsigned long)kDebugMax; while (next != NULL) { current = next; next = strchr(current, ','); @@ -52,8 +51,8 @@ ADebug::Level ADebug::GetDebugLevelFromString( // get level char *end; - errno = 0; // strtoul does not clear errno, but it can be set for any return value - unsigned long level = strtoul(current, &end, 10); + errno = 0; // strtol does not clear errno, but it can be set for any return value + long level = strtol(current, &end, 10); while (isspace(*end)) { ++end; } @@ -77,23 +76,30 @@ ADebug::Level ADebug::GetDebugLevelFromString( } } - // update debug level - def = (Level)min(level, maxLevel); + // update value + def = level; } return def; } //static -ADebug::Level ADebug::GetDebugLevelFromProperty( - const char *name, const char *propertyName, ADebug::Level def) { +long ADebug::GetLevelFromProperty( + const char *name, const char *propertyName, long def) { char value[PROPERTY_VALUE_MAX]; if (property_get(propertyName, value, NULL)) { - return GetDebugLevelFromString(name, value, def); + def = GetLevelFromSettingsString(name, value, def); } return def; } //static +ADebug::Level ADebug::GetDebugLevelFromProperty( + const char *name, const char *propertyName, ADebug::Level def) { + long level = GetLevelFromProperty(name, propertyName, (long)def); + return (Level)min(max(level, (long)kDebugNone), (long)kDebugMax); +} + +//static char *ADebug::GetDebugName(const char *name) { char *debugName = strdup(name); const char *terms[] = { "omx", "video", "audio" }; @@ -118,6 +124,15 @@ char *ADebug::GetDebugName(const char *name) { bool ADebug::getExperimentFlag( bool allow, const char *name, uint64_t modulo, uint64_t limit, uint64_t plus, uint64_t timeDivisor) { + // see if this experiment should be disabled/enabled based on properties. + // default to 2 to allow 0/1 specification + const int undefined = 2; + long level = GetLevelFromProperty(name, "debug.stagefright.experiments", undefined); + if (level != undefined) { + ALOGI("experiment '%s': %s from property", name, level ? "ENABLED" : "disabled"); + return level != 0; + } + static volatile int32_t haveSerial = 0; static uint64_t serialNum; if (!android_atomic_acquire_load(&haveSerial)) { @@ -138,11 +153,10 @@ bool ADebug::getExperimentFlag( num = num * 256 + c; } } - ALOGI("got serial"); serialNum = num; android_atomic_release_store(1, &haveSerial); } - ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL)); + ALOGD("serial: %llu, time: %lld", (long long unsigned)serialNum, (long long)time(NULL)); // MINOR: use modulo for counter and time, so that their sum does not // roll over, and mess up the correlation between related experiments. // e.g. keep (a mod 2N) = 0 impl (a mod N) = 0 diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index aae3e9f..cbe9673 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -264,8 +264,19 @@ status_t MPEG2TSExtractor::feedMore() { if (event.isInit()) { for (size_t i = 0; i < mSourceImpls.size(); ++i) { if (mSourceImpls[i].get() == event.getMediaSource().get()) { - mSyncPoints.editItemAt(i).add( - event.getTimeUs(), event.getOffset()); + KeyedVector<int64_t, off64_t> *syncPoints = &mSyncPoints.editItemAt(i); + syncPoints->add(event.getTimeUs(), event.getOffset()); + // We're keeping the size of the sync points at most 5mb per a track. + size_t size = syncPoints->size(); + if (size >= 327680) { + int64_t firstTimeUs = syncPoints->keyAt(0); + int64_t lastTimeUs = syncPoints->keyAt(size - 1); + if (event.getTimeUs() - firstTimeUs > lastTimeUs - event.getTimeUs()) { + syncPoints->removeItemsAt(0, 4096); + } else { + syncPoints->removeItemsAt(size - 4096, 4096); + } + } break; } } diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp index 04303c4..e6a0c49 100644 --- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -505,7 +505,15 @@ void SimpleSoftOMXComponent::onPortFlush( CHECK_LT(portIndex, mPorts.size()); PortInfo *port = &mPorts.editItemAt(portIndex); - CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); + // Ideally, the port should not in transitioning state when flushing. + // However, in error handling case, e.g., the client can't allocate buffers + // when it tries to re-enable the port, the port will be stuck in ENABLING. + // The client will then transition the component from Executing to Idle, + // which leads to flushing ports. At this time, it should be ok to notify + // the client of the error and still clear all buffers on the port. + if (port->mTransition != PortInfo::NONE) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + } for (size_t i = 0; i < port->mBuffers.size(); ++i) { BufferInfo *buffer = &port->mBuffers.editItemAt(i); diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp index c1e663c..d736501 100644 --- a/media/libstagefright/tests/Utils_test.cpp +++ b/media/libstagefright/tests/Utils_test.cpp @@ -109,21 +109,21 @@ TEST_F(UtilsTest, TestStringUtils) { TEST_F(UtilsTest, TestDebug) { #define LVL(x) (ADebug::Level)(x) - ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "", LVL(5)), LVL(5)); - ASSERT_EQ(ADebug::GetDebugLevelFromString("video", " \t \n ", LVL(2)), LVL(2)); - ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3", LVL(5)), LVL(3)); - ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo", LVL(5)), LVL(3)); - ASSERT_EQ(ADebug::GetDebugLevelFromString( + ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "", LVL(5)), LVL(5)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", " \t \n ", LVL(2)), LVL(2)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "3", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "3:*deo", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString( "video", "\t\n 3 \t\n:\t\n video \t\n", LVL(5)), LVL(3)); - ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo,2:vid*", LVL(5)), LVL(2)); - ASSERT_EQ(ADebug::GetDebugLevelFromString( + ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "3:*deo,2:vid*", LVL(5)), LVL(2)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString( "avideo", "\t\n 3 \t\n:\t\n avideo \t\n,\t\n 2 \t\n:\t\n video \t\n", LVL(5)), LVL(3)); - ASSERT_EQ(ADebug::GetDebugLevelFromString( + ASSERT_EQ(ADebug::GetLevelFromSettingsString( "audio.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(2)); - ASSERT_EQ(ADebug::GetDebugLevelFromString( + ASSERT_EQ(ADebug::GetLevelFromSettingsString( "video.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); - ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); - ASSERT_EQ(ADebug::GetDebugLevelFromString("omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(4)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetLevelFromSettingsString("omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(4)); #undef LVL } diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 52fce34..8f1e050 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1252,11 +1252,9 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) if (client == 0) { return; } - bool clientAdded = false; + pid_t pid = IPCThreadState::self()->getCallingPid(); { Mutex::Autolock _cl(mClientLock); - - pid_t pid = IPCThreadState::self()->getCallingPid(); if (mNotificationClients.indexOfKey(pid) < 0) { sp<NotificationClient> notificationClient = new NotificationClient(this, client, @@ -1267,22 +1265,19 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) sp<IBinder> binder = IInterface::asBinder(client); binder->linkToDeath(notificationClient); - clientAdded = true; } } // mClientLock should not be held here because ThreadBase::sendIoConfigEvent() will lock the // ThreadBase mutex and the locking order is ThreadBase::mLock then AudioFlinger::mClientLock. - if (clientAdded) { - // the config change is always sent from playback or record threads to avoid deadlock - // with AudioSystem::gLock - for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED); - } + // the config change is always sent from playback or record threads to avoid deadlock + // with AudioSystem::gLock + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED, pid); + } - for (size_t i = 0; i < mRecordThreads.size(); i++) { - mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED); - } + for (size_t i = 0; i < mRecordThreads.size(); i++) { + mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED, pid); } } @@ -1316,12 +1311,15 @@ void AudioFlinger::removeNotificationClient(pid_t pid) } void AudioFlinger::ioConfigChanged(audio_io_config_event event, - const sp<AudioIoDescriptor>& ioDesc) + const sp<AudioIoDescriptor>& ioDesc, + pid_t pid) { Mutex::Autolock _l(mClientLock); size_t size = mNotificationClients.size(); for (size_t i = 0; i < size; i++) { - mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc); + if ((pid == 0) || (mNotificationClients.keyAt(i) == pid)) { + mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc); + } } } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index d087ced..4f7e27d 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -559,7 +559,8 @@ private: float streamVolume_l(audio_stream_type_t stream) const { return mStreamTypes[stream].volume; } void ioConfigChanged(audio_io_config_event event, - const sp<AudioIoDescriptor>& ioDesc); + const sp<AudioIoDescriptor>& ioDesc, + pid_t pid = 0); // Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t. // They all share the same ID space, but the namespaces are actually independent diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp index e6d8f09..f953cc8 100644 --- a/services/audioflinger/AudioStreamOut.cpp +++ b/services/audioflinger/AudioStreamOut.cpp @@ -27,25 +27,59 @@ namespace android { // ---------------------------------------------------------------------------- - AudioStreamOut::AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags) : audioHwDev(dev) , stream(NULL) , flags(flags) + , mFramesWritten(0) + , mFramesWrittenAtStandby(0) + , mRenderPosition(0) + , mRateMultiplier(1) + , mHalFormatIsLinearPcm(false) + , mHalFrameSize(0) { } -audio_hw_device_t* AudioStreamOut::hwDev() const +audio_hw_device_t *AudioStreamOut::hwDev() const { return audioHwDev->hwDevice(); } -status_t AudioStreamOut::getRenderPosition(uint32_t *frames) +status_t AudioStreamOut::getRenderPosition(uint64_t *frames) { if (stream == NULL) { return NO_INIT; } - return stream->get_render_position(stream, frames); + + uint32_t halPosition = 0; + status_t status = stream->get_render_position(stream, &halPosition); + if (status != NO_ERROR) { + return status; + } + + // Maintain a 64-bit render position using the 32-bit result from the HAL. + // This delta calculation relies on the arithmetic overflow behavior + // of integers. For example (100 - 0xFFFFFFF0) = 116. + uint32_t truncatedPosition = (uint32_t)mRenderPosition; + int32_t deltaHalPosition = (int32_t)(halPosition - truncatedPosition); + if (deltaHalPosition > 0) { + mRenderPosition += deltaHalPosition; + } + // Scale from HAL sample rate to application rate. + *frames = mRenderPosition / mRateMultiplier; + + return status; +} + +// return bottom 32-bits of the render position +status_t AudioStreamOut::getRenderPosition(uint32_t *frames) +{ + uint64_t position64 = 0; + status_t status = getRenderPosition(&position64); + if (status == NO_ERROR) { + *frames = (uint32_t)position64; + } + return status; } status_t AudioStreamOut::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) @@ -53,7 +87,26 @@ status_t AudioStreamOut::getPresentationPosition(uint64_t *frames, struct timesp if (stream == NULL) { return NO_INIT; } - return stream->get_presentation_position(stream, frames, timestamp); + + uint64_t halPosition = 0; + status_t status = stream->get_presentation_position(stream, &halPosition, timestamp); + if (status != NO_ERROR) { + return status; + } + + // Adjust for standby using HAL rate frames. + // Only apply this correction if the HAL is getting PCM frames. + if (mHalFormatIsLinearPcm) { + uint64_t adjustedPosition = (halPosition <= mFramesWrittenAtStandby) ? + 0 : (halPosition - mFramesWrittenAtStandby); + // Scale from HAL sample rate to application rate. + *frames = adjustedPosition / mRateMultiplier; + } else { + // For offloaded MP3 and other compressed formats. + *frames = halPosition; + } + + return status; } status_t AudioStreamOut::open( @@ -62,7 +115,7 @@ status_t AudioStreamOut::open( struct audio_config *config, const char *address) { - audio_stream_out_t* outStream; + audio_stream_out_t *outStream; int status = hwDev()->open_output_stream( hwDev(), handle, @@ -82,6 +135,9 @@ status_t AudioStreamOut::open( if (status == NO_ERROR) { stream = outStream; + mHalFormatIsLinearPcm = audio_is_linear_pcm(config->format); + ALOGI("AudioStreamOut::open(), mHalFormatIsLinearPcm = %d", (int)mHalFormatIsLinearPcm); + mHalFrameSize = audio_stream_out_frame_size(stream); } return status; @@ -89,13 +145,15 @@ status_t AudioStreamOut::open( size_t AudioStreamOut::getFrameSize() { - ALOG_ASSERT(stream != NULL); - return audio_stream_out_frame_size(stream); + return mHalFrameSize; } int AudioStreamOut::flush() { ALOG_ASSERT(stream != NULL); + mRenderPosition = 0; + mFramesWritten = 0; + mFramesWrittenAtStandby = 0; if (stream->flush != NULL) { return stream->flush(stream); } @@ -105,13 +163,20 @@ int AudioStreamOut::flush() int AudioStreamOut::standby() { ALOG_ASSERT(stream != NULL); + mRenderPosition = 0; + mFramesWrittenAtStandby = mFramesWritten; + ALOGI("AudioStreamOut::standby(), mFramesWrittenAtStandby = %llu", mFramesWrittenAtStandby); return stream->common.standby(&stream->common); } -ssize_t AudioStreamOut::write(const void* buffer, size_t bytes) +ssize_t AudioStreamOut::write(const void *buffer, size_t numBytes) { ALOG_ASSERT(stream != NULL); - return stream->write(stream, buffer, bytes); + ssize_t bytesWritten = stream->write(stream, buffer, numBytes); + if (bytesWritten > 0 && mHalFrameSize > 0) { + mFramesWritten += bytesWritten / mHalFrameSize; + } + return bytesWritten; } } // namespace android diff --git a/services/audioflinger/AudioStreamOut.h b/services/audioflinger/AudioStreamOut.h index e91ca9c..761e771 100644 --- a/services/audioflinger/AudioStreamOut.h +++ b/services/audioflinger/AudioStreamOut.h @@ -53,7 +53,10 @@ public: virtual ~AudioStreamOut() { } - virtual status_t getRenderPosition(uint32_t *frames); + // Get the bottom 32-bits of the 64-bit render position. + status_t getRenderPosition(uint32_t *frames); + + virtual status_t getRenderPosition(uint64_t *frames); virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); @@ -76,6 +79,14 @@ public: virtual status_t flush(); virtual status_t standby(); + +protected: + uint64_t mFramesWritten; // reset by flush + uint64_t mFramesWrittenAtStandby; + uint64_t mRenderPosition; // reset by flush or standby + int mRateMultiplier; + bool mHalFormatIsLinearPcm; + size_t mHalFrameSize; }; } // namespace android diff --git a/services/audioflinger/SpdifStreamOut.cpp b/services/audioflinger/SpdifStreamOut.cpp index ac637ef..6af7bce 100644 --- a/services/audioflinger/SpdifStreamOut.cpp +++ b/services/audioflinger/SpdifStreamOut.cpp @@ -36,10 +36,7 @@ SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags, audio_format_t format) : AudioStreamOut(dev,flags) - , mRateMultiplier(1) , mSpdifEncoder(this, format) - , mRenderPositionHal(0) - , mPreviousHalPosition32(0) { } @@ -97,62 +94,18 @@ status_t SpdifStreamOut::open( return status; } -// Account for possibly higher sample rate. -status_t SpdifStreamOut::getRenderPosition(uint32_t *frames) -{ - uint32_t halPosition = 0; - status_t status = AudioStreamOut::getRenderPosition(&halPosition); - if (status != NO_ERROR) { - return status; - } - - // Accumulate a 64-bit position so that we wrap at the right place. - if (mRateMultiplier != 1) { - // Maintain a 64-bit render position. - int32_t deltaHalPosition = (int32_t)(halPosition - mPreviousHalPosition32); - mPreviousHalPosition32 = halPosition; - mRenderPositionHal += deltaHalPosition; - - // Scale from device sample rate to application rate. - uint64_t renderPositionApp = mRenderPositionHal / mRateMultiplier; - ALOGV("SpdifStreamOut::getRenderPosition() " - "renderPositionAppRate = %llu = %llu / %u\n", - renderPositionApp, mRenderPositionHal, mRateMultiplier); - - *frames = (uint32_t)renderPositionApp; - } else { - *frames = halPosition; - } - return status; -} - int SpdifStreamOut::flush() { mSpdifEncoder.reset(); - mRenderPositionHal = 0; - mPreviousHalPosition32 = 0; return AudioStreamOut::flush(); } int SpdifStreamOut::standby() { mSpdifEncoder.reset(); - mRenderPositionHal = 0; - mPreviousHalPosition32 = 0; return AudioStreamOut::standby(); } -// Account for possibly higher sample rate. -// This is much easier when all the values are 64-bit. -status_t SpdifStreamOut::getPresentationPosition(uint64_t *frames, - struct timespec *timestamp) -{ - uint64_t halFrames = 0; - status_t status = AudioStreamOut::getPresentationPosition(&halFrames, timestamp); - *frames = halFrames / mRateMultiplier; - return status; -} - size_t SpdifStreamOut::getFrameSize() { return sizeof(int8_t); diff --git a/services/audioflinger/SpdifStreamOut.h b/services/audioflinger/SpdifStreamOut.h index d81c064..a61a7bd 100644 --- a/services/audioflinger/SpdifStreamOut.h +++ b/services/audioflinger/SpdifStreamOut.h @@ -49,10 +49,6 @@ public: struct audio_config *config, const char *address); - virtual status_t getRenderPosition(uint32_t *frames); - - virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); - /** * Write audio buffer to driver. Returns number of bytes written, or a * negative status_t. If at least one frame was written successfully prior to the error, @@ -92,13 +88,8 @@ private: SpdifStreamOut * const mSpdifStreamOut; }; - int mRateMultiplier; MySPDIFEncoder mSpdifEncoder; - // Used to implement getRenderPosition() - int64_t mRenderPositionHal; - uint32_t mPreviousHalPosition32; - ssize_t writeDataBurst(const void* data, size_t bytes); ssize_t writeInternal(const void* buffer, size_t bytes); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 966fb5c..d9f1a83 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -532,7 +532,8 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio // RecordThread::readInputParameters_l() //FIXME: mStandby should be true here. Is this some kind of hack? mStandby(false), mOutDevice(outDevice), mInDevice(inDevice), - mPrevInDevice(AUDIO_DEVICE_NONE), mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id), + mPrevOutDevice(AUDIO_DEVICE_NONE), mPrevInDevice(AUDIO_DEVICE_NONE), + mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id), // mName will be set by concrete (non-virtual) subclass mDeathRecipient(new PMDeathRecipient(this)), mSystemReady(systemReady) @@ -627,16 +628,16 @@ status_t AudioFlinger::ThreadBase::sendConfigEvent_l(sp<ConfigEvent>& event) return status; } -void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event) +void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event, pid_t pid) { Mutex::Autolock _l(mLock); - sendIoConfigEvent_l(event); + sendIoConfigEvent_l(event, pid); } // sendIoConfigEvent_l() must be called with ThreadBase::mLock held -void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event) +void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event, pid_t pid) { - sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event); + sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, pid); sendConfigEvent_l(configEvent); } @@ -706,7 +707,7 @@ void AudioFlinger::ThreadBase::processConfigEvents_l() } break; case CFG_EVENT_IO: { IoConfigEventData *data = (IoConfigEventData *)event->mData.get(); - ioConfigChanged(data->mEvent); + ioConfigChanged(data->mEvent, data->mPid); } break; case CFG_EVENT_SET_PARAMETER: { SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get(); @@ -1999,7 +2000,7 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) return out_s8; } -void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event) { +void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event, pid_t pid) { sp<AudioIoDescriptor> desc = new AudioIoDescriptor(); ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event); @@ -2021,7 +2022,7 @@ void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event) default: break; } - mAudioFlinger->ioConfigChanged(event, desc); + mAudioFlinger->ioConfigChanged(event, desc, pid); } void AudioFlinger::PlaybackThread::writeCallback() @@ -3133,7 +3134,10 @@ status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_pat for (size_t i = 0; i < mEffectChains.size(); i++) { mEffectChains[i]->setDevice_l(type); } - bool configChanged = mOutDevice != type; + + // mPrevOutDevice is the latest device set by createAudioPatch_l(). It is not set when + // the thread is created so that the first patch creation triggers an ioConfigChanged callback + bool configChanged = mPrevOutDevice != type; mOutDevice = type; mPatch = *patch; @@ -3163,6 +3167,7 @@ status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_pat *handle = AUDIO_PATCH_HANDLE_NONE; } if (configChanged) { + mPrevOutDevice = type; sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED); } return status; @@ -6871,7 +6876,7 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) return out_s8; } -void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event) { +void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event, pid_t pid) { sp<AudioIoDescriptor> desc = new AudioIoDescriptor(); desc->mIoHandle = mId; @@ -6891,7 +6896,7 @@ void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event) { default: break; } - mAudioFlinger->ioConfigChanged(event, desc); + mAudioFlinger->ioConfigChanged(event, desc, pid); } void AudioFlinger::RecordThread::readInputParameters_l() diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 0783371..46ac300 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -104,21 +104,22 @@ public: class IoConfigEventData : public ConfigEventData { public: - IoConfigEventData(audio_io_config_event event) : - mEvent(event) {} + IoConfigEventData(audio_io_config_event event, pid_t pid) : + mEvent(event), mPid(pid) {} virtual void dump(char *buffer, size_t size) { snprintf(buffer, size, "IO event: event %d\n", mEvent); } const audio_io_config_event mEvent; + const pid_t mPid; }; class IoConfigEvent : public ConfigEvent { public: - IoConfigEvent(audio_io_config_event event) : + IoConfigEvent(audio_io_config_event event, pid_t pid) : ConfigEvent(CFG_EVENT_IO) { - mData = new IoConfigEventData(event); + mData = new IoConfigEventData(event, pid); } virtual ~IoConfigEvent() {} }; @@ -255,13 +256,13 @@ public: status_t& status) = 0; virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; - virtual void ioConfigChanged(audio_io_config_event event) = 0; + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0) = 0; // sendConfigEvent_l() must be called with ThreadBase::mLock held // Can temporarily release the lock if waiting for a reply from // processConfigEvents_l(). status_t sendConfigEvent_l(sp<ConfigEvent>& event); - void sendIoConfigEvent(audio_io_config_event event); - void sendIoConfigEvent_l(audio_io_config_event event); + void sendIoConfigEvent(audio_io_config_event event, pid_t pid = 0); + void sendIoConfigEvent_l(audio_io_config_event event, pid_t pid = 0); void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio); void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio); status_t sendSetParameterConfigEvent_l(const String8& keyValuePair); @@ -436,6 +437,7 @@ protected: bool mStandby; // Whether thread is currently in standby. audio_devices_t mOutDevice; // output device audio_devices_t mInDevice; // input device + audio_devices_t mPrevOutDevice; // previous output device audio_devices_t mPrevInDevice; // previous input device struct audio_patch mPatch; audio_source_t mAudioSource; @@ -572,7 +574,7 @@ public: { return android_atomic_acquire_load(&mSuspended) > 0; } virtual String8 getParameters(const String8& keys); - virtual void ioConfigChanged(audio_io_config_event event); + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0); status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); // FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency. // Consider also removing and passing an explicit mMainBuffer initialization @@ -1254,7 +1256,7 @@ public: status_t& status); virtual void cacheParameters_l() {} virtual String8 getParameters(const String8& keys); - virtual void ioConfigChanged(audio_io_config_event event); + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0); virtual status_t createAudioPatch_l(const struct audio_patch *patch, audio_patch_handle_t *handle); virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h index 4205589..712f7a7 100755 --- a/services/audiopolicy/common/include/Volume.h +++ b/services/audiopolicy/common/include/Volume.h @@ -82,6 +82,8 @@ public: // - HDMI-CEC system audio mode only output: give priority to available item in order. if (device & AUDIO_DEVICE_OUT_SPEAKER) { device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (device & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { device = AUDIO_DEVICE_OUT_HDMI_ARC; } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 4eef02f..4b73e3c 100755 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -37,8 +37,9 @@ * A device mask for all audio input and output devices where matching inputs/outputs on device * type alone is not enough: the address must match too */ -#define APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | \ - AUDIO_DEVICE_OUT_REMOTE_SUBMIX) +#define APM_AUDIO_DEVICE_OUT_MATCH_ADDRESS_ALL (AUDIO_DEVICE_OUT_REMOTE_SUBMIX) + +#define APM_AUDIO_DEVICE_IN_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX) /** * Check if the state given correspond to an in call state. @@ -80,5 +81,8 @@ static inline bool is_virtual_input_device(audio_devices_t device) */ static inline bool device_distinguishes_on_address(audio_devices_t device) { - return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0); + return (((device & AUDIO_DEVICE_BIT_IN) != 0) && + ((~AUDIO_DEVICE_BIT_IN & device & APM_AUDIO_DEVICE_IN_MATCH_ADDRESS_ALL) != 0)) || + (((device & AUDIO_DEVICE_BIT_IN) == 0) && + ((device & APM_AUDIO_DEVICE_OUT_MATCH_ADDRESS_ALL) != 0)); } diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index 7c265aa..8728ff3 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -16,6 +16,7 @@ LOCAL_SRC_FILES:= \ src/EffectDescriptor.cpp \ src/ConfigParsingUtils.cpp \ src/SoundTriggerSession.cpp \ + src/SessionRoute.cpp \ LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h new file mode 100644 index 0000000..b4feaf0 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <system/audio.h> +#include <utils/KeyedVector.h> +#include <utils/RefBase.h> +#include <utils/Errors.h> + +namespace android { + +class DeviceDescriptor; + +class SessionRoute : public RefBase +{ +public: + // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the + // streamType argument + static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT; + + // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the + // source argument + + static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT; + + SessionRoute(audio_session_t session, + audio_stream_type_t streamType, + audio_source_t source, + sp<DeviceDescriptor> deviceDescriptor, + uid_t uid) + : mUid(uid), + mSession(session), + mDeviceDescriptor(deviceDescriptor), + mRefCount(0), + mActivityCount(0), + mChanged(false), + mStreamType(streamType), + mSource(source) + {} + + void log(const char* prefix); + + bool isActive() { + return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0)); + } + + uid_t mUid; + audio_session_t mSession; + sp<DeviceDescriptor> mDeviceDescriptor; + + // "reference" counting + int mRefCount; // +/- on references + int mActivityCount; // +/- on start/stop + bool mChanged; + // for outputs + const audio_stream_type_t mStreamType; + // for inputs + const audio_source_t mSource; +}; + +class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute> > +{ +public: + // These constants identify the SessionRoutMap as holding EITHER input routes, + // or output routes. An error will occur if an attempt is made to add a SessionRoute + // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a + // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT) + // and similarly for output SessionRoutes and Input SessionRouteMaps. + typedef enum + { + MAPTYPE_INPUT = 0, + MAPTYPE_OUTPUT = 1 + } session_route_map_type_t; + + SessionRouteMap(session_route_map_type_t mapType) : + mMapType(mapType) + {} + + bool hasRoute(audio_session_t session); + + void removeRoute(audio_session_t session); + + int incRouteActivity(audio_session_t session); + int decRouteActivity(audio_session_t session); + bool hasRouteChanged(audio_session_t session); // also clears the changed flag + void log(const char* caption); + + // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the + // source argument. + // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT + // in the streamType argument. + void addRoute(audio_session_t session, + audio_stream_type_t streamType, + audio_source_t source, + sp<DeviceDescriptor> deviceDescriptor, + uid_t uid); + +private: + // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input) + // or outputs (mMapType == kSessionRouteMap_Output) + const session_route_map_type_t mMapType; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp new file mode 100644 index 0000000..7ecfa44 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "APM::SessionRoute" +//#define LOG_NDEBUG 0 + +#include "SessionRoute.h" +#include "HwModule.h" +#include "AudioGain.h" +#include "DeviceDescriptor.h" +#include <utils/Log.h> + +namespace android { + +// --- SessionRoute class implementation +void SessionRoute::log(const char* prefix) +{ + ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d", + prefix, mStreamType, mSource, mSession, + mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE, + mRefCount, mActivityCount); +} + +// --- SessionRouteMap class implementation +bool SessionRouteMap::hasRoute(audio_session_t session) +{ + return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0; +} + +bool SessionRouteMap::hasRouteChanged(audio_session_t session) +{ + if (indexOfKey(session) >= 0) { + if (valueFor(session)->mChanged) { + valueFor(session)->mChanged = false; + return true; + } + } + return false; +} + +void SessionRouteMap::removeRoute(audio_session_t session) +{ + sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; + if (route != 0) { + ALOG_ASSERT(route->mRefCount > 0); + --route->mRefCount; + if (route->mRefCount <= 0) { + removeItem(session); + } + } +} + +int SessionRouteMap::incRouteActivity(audio_session_t session) +{ + sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; + return route != 0 ? ++(route->mActivityCount) : -1; +} + +int SessionRouteMap::decRouteActivity(audio_session_t session) +{ + sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; + if (route != 0 && route->mActivityCount > 0) { + return --(route->mActivityCount); + } else { + return -1; + } +} + +void SessionRouteMap::log(const char* caption) +{ + ALOGI("%s ----", caption); + for(size_t index = 0; index < size(); index++) { + valueAt(index)->log(" "); + } +} + +void SessionRouteMap::addRoute(audio_session_t session, + audio_stream_type_t streamType, + audio_source_t source, + sp<DeviceDescriptor> descriptor, + uid_t uid) +{ + if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) { + ALOGE("Adding Output Route to InputRouteMap"); + return; + } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) { + ALOGE("Adding Input Route to OutputRouteMap"); + return; + } + + sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; + + if (route != 0) { + if (((route->mDeviceDescriptor == 0) && (descriptor != 0)) || + ((route->mDeviceDescriptor != 0) && + ((descriptor == 0) || (!route->mDeviceDescriptor->equals(descriptor))))) { + route->mChanged = true; + } + route->mRefCount++; + route->mDeviceDescriptor = descriptor; + } else { + route = new SessionRoute(session, streamType, source, descriptor, uid); + route->mRefCount++; + add(session, route); + if (descriptor != 0) { + route->mChanged = true; + } + } +} + +} // namespace android diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 7a785eb..0686414 100755 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -319,8 +319,11 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const device = getDeviceForStrategy(STRATEGY_SONIFICATION); //user "safe" speaker if available instead of normal speaker to avoid triggering //other acoustic safety mechanisms for notification - if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) - device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + if ((device & AUDIO_DEVICE_OUT_SPEAKER) && + (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { + device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE; + device &= ~AUDIO_DEVICE_OUT_SPEAKER; + } } else if (outputs.isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { // while media is playing (or has recently played), use the same device device = getDeviceForStrategy(STRATEGY_MEDIA); @@ -329,8 +332,11 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const device = getDeviceForStrategy(STRATEGY_SONIFICATION); //user "safe" speaker if available instead of normal speaker to avoid triggering //other acoustic safety mechanisms for notification - if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) - device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + if ((device & AUDIO_DEVICE_OUT_SPEAKER) && + (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { + device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE; + device &= ~AUDIO_DEVICE_OUT_SPEAKER; + } } break; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6983b5c..6d99640 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1076,6 +1076,9 @@ status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc, beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT); } + // check active before incrementing usage count + bool force = !outputDesc->isActive(); + // increment usage count for this stream on the requested output: // NOTE that the usage count is the same for duplicated output and hardware output which is // necessary for a correct control of hardware output routing by startOutput() and stopOutput() @@ -1091,7 +1094,6 @@ status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc, (strategy == STRATEGY_SONIFICATION_RESPECTFUL) || (beaconMuteLatency > 0); uint32_t waitMs = beaconMuteLatency; - bool force = false; for (size_t i = 0; i < mOutputs.size(); i++) { sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); if (desc != outputDesc) { @@ -4721,99 +4723,7 @@ void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream, } } -// --- SessionRoute class implementation -void AudioPolicyManager::SessionRoute::log(const char* prefix) { - ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d", - prefix, mStreamType, mSource, mSession, - mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE, - mRefCount, mActivityCount); -} - -// --- SessionRouteMap class implementation -bool AudioPolicyManager::SessionRouteMap::hasRoute(audio_session_t session) -{ - return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0; -} - -bool AudioPolicyManager::SessionRouteMap::hasRouteChanged(audio_session_t session) -{ - if (indexOfKey(session) >= 0) { - if (valueFor(session)->mChanged) { - valueFor(session)->mChanged = false; - return true; - } - } - return false; -} - -void AudioPolicyManager::SessionRouteMap::removeRoute(audio_session_t session) -{ - sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - if (route != 0) { - ALOG_ASSERT(route->mRefCount > 0); - --route->mRefCount; - if (route->mRefCount <= 0) { - removeItem(session); - } - } -} - -int AudioPolicyManager::SessionRouteMap::incRouteActivity(audio_session_t session) -{ - sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - return route != 0 ? ++(route->mActivityCount) : -1; -} -int AudioPolicyManager::SessionRouteMap::decRouteActivity(audio_session_t session) -{ - sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - if (route != 0 && route->mActivityCount > 0) { - return --(route->mActivityCount); - } else { - return -1; - } -} - -void AudioPolicyManager::SessionRouteMap::log(const char* caption) { - ALOGI("%s ----", caption); - for(size_t index = 0; index < size(); index++) { - valueAt(index)->log(" "); - } -} - -void AudioPolicyManager::SessionRouteMap::addRoute(audio_session_t session, - audio_stream_type_t streamType, - audio_source_t source, - sp<DeviceDescriptor> descriptor, - uid_t uid) -{ - if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) { - ALOGE("Adding Output Route to InputRouteMap"); - return; - } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) { - ALOGE("Adding Input Route to OutputRouteMap"); - return; - } - - sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - - if (route != 0) { - if (((route->mDeviceDescriptor == 0) && (descriptor != 0)) || - ((route->mDeviceDescriptor != 0) && - ((descriptor == 0) || (!route->mDeviceDescriptor->equals(descriptor))))) { - route->mChanged = true; - } - route->mRefCount++; - route->mDeviceDescriptor = descriptor; - } else { - route = new AudioPolicyManager::SessionRoute(session, streamType, source, descriptor, uid); - route->mRefCount++; - add(session, route); - if (descriptor != 0) { - route->mChanged = true; - } - } -} void AudioPolicyManager::defaultAudioPolicyConfig(void) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index cf64154..bf3ae4a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -42,6 +42,7 @@ #include <EffectDescriptor.h> #include <SoundTriggerSession.h> #include <StreamDescriptor.h> +#include <SessionRoute.h> namespace android { @@ -237,93 +238,6 @@ public: routing_strategy getStrategy(audio_stream_type_t stream) const; protected: - class SessionRoute : public RefBase { - public: - // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the - // streamType argument - static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT; - - // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the - // source argument - - static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT; - - SessionRoute(audio_session_t session, - audio_stream_type_t streamType, - audio_source_t source, - sp<DeviceDescriptor> deviceDescriptor, - uid_t uid) - : mUid(uid), - mSession(session), - mDeviceDescriptor(deviceDescriptor), - mRefCount(0), - mActivityCount(0), - mChanged(false), - mStreamType(streamType), - mSource(source) - {} - - void log(const char* prefix); - - bool isActive() { - return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0)); - } - - uid_t mUid; - audio_session_t mSession; - sp<DeviceDescriptor> mDeviceDescriptor; - - // "reference" counting - int mRefCount; // +/- on references - int mActivityCount; // +/- on start/stop - bool mChanged; - // for outputs - const audio_stream_type_t mStreamType; - // for inputs - const audio_source_t mSource; - }; - - class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute>> { - public: - // These constants identify the SessionRoutMap as holding EITHER input routes, - // or output routes. An error will occur if an attempt is made to add a SessionRoute - // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a - // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT) - // and similarly for output SessionRoutes and Input SessionRouteMaps. - typedef enum { - MAPTYPE_INPUT = 0, - MAPTYPE_OUTPUT = 1 - } session_route_map_type_t; - - SessionRouteMap(session_route_map_type_t mapType) : - mMapType(mapType) { - } - - bool hasRoute(audio_session_t session); - - void removeRoute(audio_session_t session); - - int incRouteActivity(audio_session_t session); - int decRouteActivity(audio_session_t session); - bool hasRouteChanged(audio_session_t session); // also clears the changed flag - void log(const char* caption); - - // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the - // source argument. - // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT - // in the streamType argument. - void addRoute(audio_session_t session, - audio_stream_type_t streamType, - audio_source_t source, - sp<DeviceDescriptor> deviceDescriptor, - uid_t uid); - - private: - // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input) - // or outputs (mMapType == kSessionRouteMap_Output) - const session_route_map_type_t mMapType; - }; - // From AudioPolicyManagerObserver virtual const AudioPatchCollection &getAudioPatches() const { |
