Merge "audioflinger: flush HAL when transitioning to next direct track" into mnc-dev
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 @@
};
- // 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 @@
// 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 @@
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 @@
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 @@
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 @@
delete mCallbackData;
}
+//static
void MediaPlayerService::AudioOutput::setMinBufferCount()
{
char value[PROPERTY_VALUE_MAX];
@@ -1367,92 +1370,105 @@
}
}
+// 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 @@
// 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 @@
}
}
+ Mutex::Autolock lock(mLock);
mCallback = cb;
mCallbackCookie = cookie;
@@ -1577,7 +1599,7 @@
// 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 @@
if (reuse) {
ALOGV("chaining to next output and recycling track");
- close();
+ close_l();
mTrack = mRecycledTrack;
mRecycledTrack.clear();
if (mCallbackData != NULL) {
@@ -1669,7 +1691,7 @@
// 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 @@
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::start()
{
ALOGV("start");
+ Mutex::Autolock lock(mLock);
if (mCallbackData != NULL) {
mCallbackData->endTrackSwitch();
}
@@ -1716,30 +1740,88 @@
}
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 @@
void MediaPlayerService::AudioOutput::stop()
{
ALOGV("stop");
+ Mutex::Autolock lock(mLock);
mBytesWritten = 0;
if (mTrack != 0) mTrack->stop();
}
@@ -1763,6 +1846,7 @@
void MediaPlayerService::AudioOutput::flush()
{
ALOGV("flush");
+ Mutex::Autolock lock(mLock);
mBytesWritten = 0;
if (mTrack != 0) mTrack->flush();
}
@@ -1770,18 +1854,21 @@
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 @@
{
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::getPlaybackRate(AudioPlaybackRate *rate)
{
ALOGV("setPlaybackRate");
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) {
return NO_INIT;
}
@@ -1824,6 +1913,7 @@
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::attachAuxEffect(int effectId)
{
ALOGV("attachAuxEffect(%d)", effectId);
+ Mutex::Autolock lock(mLock);
mAuxEffectId = effectId;
if (mTrack != 0) {
return mTrack->attachAuxEffect(effectId);
@@ -1846,6 +1937,7 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
mBuffer->release();
mBuffer = NULL;
- ALOGV("i/o error");
+ ALOGE("i/o error");
return ERROR_IO;
}
@@ -4392,18 +4400,40 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 = 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 @@
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 @@
void SoftAVC::onReset() {
SoftVideoDecoderOMXComponent::onReset();
+ mSignalledError = false;
resetDecoder();
resetPlugin();
}
@@ -520,7 +526,12 @@
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::onQueueFilled(OMX_U32 portIndex) {
UNUSED(portIndex);
+ if (mSignalledError) {
+ return;
+ }
if (mOutputPortSettingsChange != NONE) {
return;
}
@@ -627,6 +641,11 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
}
-
- BufferInfo *outInfo = *it;
- outInfo->mOwnedByUs = false;
- outQueue.erase(it);
- outInfo = 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 @@
// 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,8 +76,18 @@
}
}
- // update debug level
- def = (Level)min(level, maxLevel);
+ // update value
+ def = level;
+ }
+ return def;
+}
+
+//static
+long ADebug::GetLevelFromProperty(
+ const char *name, const char *propertyName, long def) {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get(propertyName, value, NULL)) {
+ def = GetLevelFromSettingsString(name, value, def);
}
return def;
}
@@ -86,11 +95,8 @@
//static
ADebug::Level ADebug::GetDebugLevelFromProperty(
const char *name, const char *propertyName, ADebug::Level def) {
- char value[PROPERTY_VALUE_MAX];
- if (property_get(propertyName, value, NULL)) {
- return GetDebugLevelFromString(name, value, def);
- }
- return def;
+ long level = GetLevelFromProperty(name, propertyName, (long)def);
+ return (Level)min(max(level, (long)kDebugNone), (long)kDebugMax);
}
//static
@@ -118,6 +124,15 @@
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 @@
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 @@
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 @@
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, 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 @@
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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
} 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 @@
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 @@
default:
break;
}
- mAudioFlinger->ioConfigChanged(event, desc);
+ mAudioFlinger->ioConfigChanged(event, desc, pid);
}
void AudioFlinger::PlaybackThread::writeCallback()
@@ -3133,7 +3134,10 @@
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 @@
*handle = AUDIO_PATCH_HANDLE_NONE;
}
if (configChanged) {
+ mPrevOutDevice = type;
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
return status;
@@ -6871,7 +6876,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{ 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 @@
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 @@
// - 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 4eef02f2..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 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 @@
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 @@
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 @@
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 @@
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 @@
(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 @@
}
}
-// --- 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 @@
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
{