summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp70
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h5
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp62
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h15
-rw-r--r--services/audioflinger/AudioFlinger.cpp78
-rw-r--r--services/audioflinger/AudioFlinger.h20
-rw-r--r--services/audioflinger/Effects.cpp28
-rw-r--r--services/audioflinger/Effects.h3
-rw-r--r--services/audioflinger/Threads.cpp20
-rw-r--r--services/audioflinger/Threads.h3
-rw-r--r--services/audioflinger/TrackBase.h1
-rw-r--r--services/audioflinger/Tracks.cpp26
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.cpp4
14 files changed, 237 insertions, 104 deletions
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index d8ed836..511871d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1176,12 +1176,14 @@ void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) {
void NuPlayer::GenericSource::readBuffer(
media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
Track *track;
+ size_t maxBuffers = 1;
switch (trackType) {
case MEDIA_TRACK_TYPE_VIDEO:
track = &mVideoTrack;
break;
case MEDIA_TRACK_TYPE_AUDIO:
track = &mAudioTrack;
+ maxBuffers = 64;
break;
case MEDIA_TRACK_TYPE_SUBTITLE:
track = &mSubtitleTrack;
@@ -1214,7 +1216,7 @@ void NuPlayer::GenericSource::readBuffer(
options.setNonBlocking();
}
- for (;;) {
+ for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
MediaBuffer *mbuf;
status_t err = track->mSource->read(&mbuf, &options);
@@ -1245,7 +1247,7 @@ void NuPlayer::GenericSource::readBuffer(
sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs);
track->mPackets->queueAccessUnit(buffer);
- break;
+ ++numBuffers;
} else if (err == WOULD_BLOCK) {
break;
} else if (err == INFO_FORMAT_CHANGED) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index df3e992..9020a8d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -50,6 +50,10 @@
namespace android {
+// TODO optimize buffer size for power consumption
+// The offload read buffer size is 32 KB but 24 KB uses less power.
+const size_t NuPlayer::kAggregateBufferSizeBytes = 24 * 1024;
+
struct NuPlayer::Action : public RefBase {
Action() {}
@@ -730,7 +734,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
if (err == -EWOULDBLOCK) {
if (mSource->feedMoreTSData() == OK) {
- msg->post(10000ll);
+ msg->post(10 * 1000ll);
}
}
} else if (what == Decoder::kWhatEOS) {
@@ -995,6 +999,7 @@ void NuPlayer::finishFlushIfPossible() {
ALOGV("both audio and video are flushed now.");
mPendingAudioAccessUnit.clear();
+ mAggregateBuffer.clear();
if (mTimeDiscontinuityPending) {
mRenderer->signalTimeDiscontinuity();
@@ -1256,14 +1261,8 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
// Aggregate smaller buffers into a larger buffer.
// The goal is to reduce power consumption.
// Unfortunately this does not work with the software AAC decoder.
- // TODO optimize buffer size for power consumption
- // The offload read buffer size is 32 KB but 24 KB uses less power.
- const int kAudioBigBufferSizeBytes = 24 * 1024;
- bool doBufferAggregation = (audio && mOffloadAudio);
- sp<ABuffer> biggerBuffer;
+ bool doBufferAggregation = (audio && mOffloadAudio);;
bool needMoreData = false;
- int numSmallBuffers = 0;
- bool gotTime = false;
bool dropAccessUnit;
do {
@@ -1279,14 +1278,10 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
}
if (err == -EWOULDBLOCK) {
- if (biggerBuffer == NULL) {
- return err;
- } else {
- break; // Reply with data that we already have.
- }
+ return err;
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
- if (biggerBuffer != NULL) {
+ if (mAggregateBuffer != NULL) {
// We already have some data so save this for later.
mPendingAudioErr = err;
mPendingAudioAccessUnit = accessUnit;
@@ -1401,46 +1396,45 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
size_t smallSize = accessUnit->size();
needMoreData = false;
- if (doBufferAggregation && (biggerBuffer == NULL)
+ if (doBufferAggregation && (mAggregateBuffer == NULL)
// Don't bother if only room for a few small buffers.
- && (smallSize < (kAudioBigBufferSizeBytes / 3))) {
+ && (smallSize < (kAggregateBufferSizeBytes / 3))) {
// Create a larger buffer for combining smaller buffers from the extractor.
- biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes);
- biggerBuffer->setRange(0, 0); // start empty
+ mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes);
+ mAggregateBuffer->setRange(0, 0); // start empty
}
- if (biggerBuffer != NULL) {
+ if (mAggregateBuffer != NULL) {
int64_t timeUs;
+ int64_t dummy;
bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs);
+ bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy);
// Will the smaller buffer fit?
- size_t bigSize = biggerBuffer->size();
- size_t roomLeft = biggerBuffer->capacity() - bigSize;
+ size_t bigSize = mAggregateBuffer->size();
+ size_t roomLeft = mAggregateBuffer->capacity() - bigSize;
// Should we save this small buffer for the next big buffer?
// If the first small buffer did not have a timestamp then save
// any buffer that does have a timestamp until the next big buffer.
if ((smallSize > roomLeft)
- || (!gotTime && (numSmallBuffers > 0) && smallTimestampValid)) {
+ || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) {
mPendingAudioErr = err;
mPendingAudioAccessUnit = accessUnit;
accessUnit.clear();
} else {
+ // Grab time from first small buffer if available.
+ if ((bigSize == 0) && smallTimestampValid) {
+ mAggregateBuffer->meta()->setInt64("timeUs", timeUs);
+ }
// Append small buffer to the bigger buffer.
- memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize);
+ memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize);
bigSize += smallSize;
- biggerBuffer->setRange(0, bigSize);
+ mAggregateBuffer->setRange(0, bigSize);
- // Keep looping until we run out of room in the biggerBuffer.
+ // Keep looping until we run out of room in the mAggregateBuffer.
needMoreData = true;
- // Grab time from first small buffer if available.
- if ((numSmallBuffers == 0) && smallTimestampValid) {
- biggerBuffer->meta()->setInt64("timeUs", timeUs);
- gotTime = true;
- }
-
- ALOGV("feedDecoderInputData() #%d, smallSize = %zu, bigSize = %zu, capacity = %zu",
- numSmallBuffers, smallSize, bigSize, biggerBuffer->capacity());
- numSmallBuffers++;
+ ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu",
+ smallSize, bigSize, mAggregateBuffer->capacity());
}
}
} while (dropAccessUnit || needMoreData);
@@ -1459,9 +1453,11 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
mCCDecoder->decode(accessUnit);
}
- if (biggerBuffer != NULL) {
- ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers);
- reply->setBuffer("buffer", biggerBuffer);
+ if (mAggregateBuffer != NULL) {
+ ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu",
+ mAggregateBuffer->size());
+ reply->setBuffer("buffer", mAggregateBuffer);
+ mAggregateBuffer.clear();
} else {
reply->setBuffer("buffer", accessUnit);
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 89ae11c..2e951bd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -67,6 +67,8 @@ struct NuPlayer : public AHandler {
status_t getSelectedTrack(int32_t type, Parcel* reply) const;
status_t selectTrack(size_t trackIndex, bool select);
+ static const size_t kAggregateBufferSizeBytes;
+
protected:
virtual ~NuPlayer();
@@ -158,8 +160,11 @@ private:
// notion of time has changed.
bool mTimeDiscontinuityPending;
+ // Used by feedDecoderInputData to aggregate small buffers into
+ // one large buffer.
sp<ABuffer> mPendingAudioAccessUnit;
status_t mPendingAudioErr;
+ sp<ABuffer> mAggregateBuffer;
FlushStatus mFlushingAudio;
FlushStatus mFlushingVideo;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index ab7906a..f7aacdd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -30,8 +30,10 @@
namespace android {
-static const int kMaxPendingBuffers = 10;
-static const int kMaxCachedBytes = 200000;
+static const size_t kMaxCachedBytes = 200000;
+// The buffers will contain a bit less than kAggregateBufferSizeBytes.
+// So we can start off with just enough buffers to keep the cache full.
+static const size_t kMaxPendingBuffers = 1 + (kMaxCachedBytes / NuPlayer::kAggregateBufferSizeBytes);
NuPlayer::DecoderPassThrough::DecoderPassThrough(
const sp<AMessage> &notify)
@@ -39,7 +41,8 @@ NuPlayer::DecoderPassThrough::DecoderPassThrough(
mNotify(notify),
mBufferGeneration(0),
mReachedEOS(true),
- mPendingBuffers(0),
+ mPendingBuffersToFill(0),
+ mPendingBuffersToDrain(0),
mCachedBytes(0),
mComponentName("pass through decoder") {
mDecoderLooper = new ALooper;
@@ -79,12 +82,13 @@ bool NuPlayer::DecoderPassThrough::supportsSeamlessFormatChange(
void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) {
ALOGV("[%s] onConfigure", mComponentName.c_str());
- mPendingBuffers = 0;
mCachedBytes = 0;
+ mPendingBuffersToFill = 0;
+ mPendingBuffersToDrain = 0;
mReachedEOS = false;
++mBufferGeneration;
- requestABuffer();
+ requestMaxBuffers();
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatOutputFormatChanged);
@@ -98,12 +102,15 @@ bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) {
return generation != mBufferGeneration;
}
-void NuPlayer::DecoderPassThrough::requestABuffer() {
- if (mCachedBytes >= kMaxCachedBytes || mReachedEOS) {
- ALOGV("[%s] mReachedEOS=%d, max pending buffers(%d:%d)",
- mComponentName.c_str(), (mReachedEOS ? 1 : 0),
- mPendingBuffers, kMaxPendingBuffers);
- return;
+bool NuPlayer::DecoderPassThrough::requestABuffer() {
+ if (mCachedBytes >= kMaxCachedBytes) {
+ ALOGV("[%s] mCachedBytes = %zu",
+ mComponentName.c_str(), mCachedBytes);
+ return false;
+ }
+ if (mReachedEOS) {
+ ALOGV("[%s] reached EOS", mComponentName.c_str());
+ return false;
}
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
@@ -113,19 +120,16 @@ void NuPlayer::DecoderPassThrough::requestABuffer() {
notify->setInt32("what", kWhatFillThisBuffer);
notify->setMessage("reply", reply);
notify->post();
- mPendingBuffers++;
+ mPendingBuffersToFill++;
+ ALOGV("requestABuffer: #ToFill = %zu, #ToDrain = %zu", mPendingBuffersToFill,
+ mPendingBuffersToDrain);
- // pending buffers will already result in requestABuffer
- if (mPendingBuffers < kMaxPendingBuffers) {
- sp<AMessage> message = new AMessage(kWhatRequestABuffer, id());
- message->setInt32("generation", mBufferGeneration);
- message->post();
- }
- return;
+ return true;
}
void android::NuPlayer::DecoderPassThrough::onInputBufferFilled(
const sp<AMessage> &msg) {
+ --mPendingBuffersToFill;
if (mReachedEOS) {
return;
}
@@ -153,11 +157,16 @@ void android::NuPlayer::DecoderPassThrough::onInputBufferFilled(
notify->setBuffer("buffer", buffer);
notify->setMessage("reply", reply);
notify->post();
+ ++mPendingBuffersToDrain;
+ ALOGV("onInputBufferFilled: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu",
+ mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes);
}
void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) {
- mPendingBuffers--;
+ --mPendingBuffersToDrain;
mCachedBytes -= size;
+ ALOGV("onBufferConsumed: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu",
+ mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes);
requestABuffer();
}
@@ -167,11 +176,20 @@ void NuPlayer::DecoderPassThrough::onFlush() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
- mPendingBuffers = 0;
+ mPendingBuffersToFill = 0;
+ mPendingBuffersToDrain = 0;
mCachedBytes = 0;
mReachedEOS = false;
}
+void NuPlayer::DecoderPassThrough::requestMaxBuffers() {
+ for (size_t i = 0; i < kMaxPendingBuffers; i++) {
+ if (!requestABuffer()) {
+ break;
+ }
+ }
+}
+
void NuPlayer::DecoderPassThrough::onShutdown() {
++mBufferGeneration;
@@ -229,7 +247,7 @@ void NuPlayer::DecoderPassThrough::onMessageReceived(const sp<AMessage> &msg) {
case kWhatResume:
{
- requestABuffer();
+ requestMaxBuffers();
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 8590856..fb20257 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -55,19 +55,26 @@ private:
sp<AMessage> mNotify;
sp<ALooper> mDecoderLooper;
- void requestABuffer();
+ /** Returns true if a buffer was requested.
+ * Returns false if at EOS or cache already full.
+ */
+ bool requestABuffer();
bool isStaleReply(const sp<AMessage> &msg);
void onConfigure(const sp<AMessage> &format);
void onFlush();
void onInputBufferFilled(const sp<AMessage> &msg);
void onBufferConsumed(int32_t size);
+ void requestMaxBuffers();
void onShutdown();
int32_t mBufferGeneration;
- bool mReachedEOS;
- int32_t mPendingBuffers;
- int32_t mCachedBytes;
+ bool mReachedEOS;
+ // TODO mPendingBuffersToFill and mPendingBuffersToDrain are only for
+ // debugging. They can be removed when the power investigation is done.
+ size_t mPendingBuffersToFill;
+ size_t mPendingBuffersToDrain;
+ size_t mCachedBytes;
AString mComponentName;
DISALLOW_EVIL_CONSTRUCTORS(DecoderPassThrough);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1843722..e200857 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -418,6 +418,13 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
mRecordThreads.valueAt(i)->dump(fd, args);
}
+ // dump orphan effect chains
+ if (mOrphanEffectChains.size() != 0) {
+ write(fd, " Orphan Effect Chains\n", strlen(" Orphan Effect Chains\n"));
+ for (size_t i = 0; i < mOrphanEffectChains.size(); i++) {
+ mOrphanEffectChains.valueAt(i)->dump(fd, args);
+ }
+ }
// dump all hardware devs
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
@@ -796,9 +803,14 @@ status_t AudioFlinger::setMicMute(bool state)
}
AutoMutex lock(mHardwareLock);
- audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
- ret = dev->set_mic_mute(dev, state);
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
+ status_t result = dev->set_mic_mute(dev, state);
+ if (result != NO_ERROR) {
+ ret = result;
+ }
+ }
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
@@ -1416,7 +1428,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
*sessionId = lSessionId;
}
}
- ALOGV("openRecord() lSessionId: %d", lSessionId);
+ ALOGV("openRecord() lSessionId: %d input %d", lSessionId, input);
// TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
@@ -2022,6 +2034,16 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input)
}
ALOGV("closeInput() %d", input);
+ {
+ // If we still have effect chains, it means that a client still holds a handle
+ // on at least one effect. We must keep the chain alive in case a new record
+ // thread is opened for a new capture on the same session
+ Mutex::Autolock _sl(thread->mLock);
+ Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i++) {
+ putOrphanEffectChain_l(effectChains[i]);
+ }
+ }
audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
mRecordThreads.removeItem(input);
}
@@ -2451,6 +2473,13 @@ sp<IEffect> AudioFlinger::createEffect(
lStatus = BAD_VALUE;
goto Exit;
}
+ } else {
+ // Check if one effect chain was awaiting for an effect to be created on this
+ // session and used it instead of creating a new one.
+ sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)sessionId);
+ if (chain != 0) {
+ thread->addEffectChain_l(chain);
+ }
}
sp<Client> client = registerPid(pid);
@@ -2623,6 +2652,49 @@ void AudioFlinger::onNonOffloadableGlobalEffectEnable()
}
+status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain)
+{
+ audio_session_t session = (audio_session_t)chain->sessionId();
+ ssize_t index = mOrphanEffectChains.indexOfKey(session);
+ ALOGV("putOrphanEffectChain_l session %d index %d", session, index);
+ if (index >= 0) {
+ ALOGW("putOrphanEffectChain_l chain for session %d already present", session);
+ return ALREADY_EXISTS;
+ }
+ mOrphanEffectChains.add(session, chain);
+ return NO_ERROR;
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::getOrphanEffectChain_l(audio_session_t session)
+{
+ sp<EffectChain> chain;
+ ssize_t index = mOrphanEffectChains.indexOfKey(session);
+ ALOGV("getOrphanEffectChain_l session %d index %d", session, index);
+ if (index >= 0) {
+ chain = mOrphanEffectChains.valueAt(index);
+ mOrphanEffectChains.removeItemsAt(index);
+ }
+ return chain;
+}
+
+bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule>& effect)
+{
+ Mutex::Autolock _l(mLock);
+ audio_session_t session = (audio_session_t)effect->sessionId();
+ ssize_t index = mOrphanEffectChains.indexOfKey(session);
+ ALOGV("updateOrphanEffectChains session %d index %d", session, index);
+ if (index >= 0) {
+ sp<EffectChain> chain = mOrphanEffectChains.valueAt(index);
+ if (chain->removeEffect_l(effect) == 0) {
+ ALOGV("updateOrphanEffectChains removing effect chain at index %d", index);
+ mOrphanEffectChains.removeItemsAt(index);
+ }
+ return true;
+ }
+ return false;
+}
+
+
struct Entry {
#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav
char mName[MAX_NAME];
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 753314f..1003017 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -569,6 +569,23 @@ private:
bool isNonOffloadableGlobalEffectEnabled_l();
void onNonOffloadableGlobalEffectEnable();
+ // Store an effect chain to mOrphanEffectChains keyed vector.
+ // Called when a thread exits and effects are still attached to it.
+ // If effects are later created on the same session, they will reuse the same
+ // effect chain and same instances in the effect library.
+ // return ALREADY_EXISTS if a chain with the same session already exists in
+ // mOrphanEffectChains. Note that this should never happen as there is only one
+ // chain for a given session and it is attached to only one thread at a time.
+ status_t putOrphanEffectChain_l(const sp<EffectChain>& chain);
+ // Get an effect chain for the specified session in mOrphanEffectChains and remove
+ // it if found. Returns 0 if not found (this is the most common case).
+ sp<EffectChain> getOrphanEffectChain_l(audio_session_t session);
+ // Called when the last effect handle on an effect instance is removed. If this
+ // effect belongs to an effect chain in mOrphanEffectChains, the chain is updated
+ // and removed from mOrphanEffectChains if it does not contain any effect.
+ // Return true if the effect was found in mOrphanEffectChains, false otherwise.
+ bool updateOrphanEffectChains(const sp<EffectModule>& effect);
+
class AudioHwDevice {
public:
enum Flags {
@@ -713,6 +730,9 @@ private:
Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session
// to be created
+ // Effect chains without a valid thread
+ DefaultKeyedVector< audio_session_t , sp<EffectChain> > mOrphanEffectChains;
+
private:
sp<Client> registerPid(pid_t pid); // always returns non-0
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 365f271..15f1f23 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -68,7 +68,8 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
mStatus(NO_INIT), mState(IDLE),
// mMaxDisableWaitCnt is set by configure() and not used before then
// mDisableWaitCnt is set by process() and updateState() and not used before then
- mSuspended(false)
+ mSuspended(false),
+ mAudioFlinger(thread->mAudioFlinger)
{
ALOGV("Constructor %p", this);
int lStatus;
@@ -197,9 +198,19 @@ size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIf
// destructor before we exit
sp<EffectModule> keep(this);
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- thread->disconnectEffect(keep, handle, unpinIfLast);
+ if (removeHandle(handle) == 0) {
+ if (!isPinned() || unpinIfLast) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ thread->removeEffect_l(this);
+ }
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af != 0) {
+ af->updateOrphanEffectChains(this);
+ }
+ AudioSystem::unregisterEffect(mId);
+ }
}
}
return mHandles.size();
@@ -1911,4 +1922,13 @@ bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
return false;
}
+void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
+{
+ Mutex::Autolock _l(mLock);
+ mThread = thread;
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ mEffects[i]->setThread(thread);
+ }
+}
+
}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 4170fd4..eaf90e7 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -153,6 +153,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
+ wp<AudioFlinger> mAudioFlinger;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -347,6 +348,8 @@ protected:
void clearInputBuffer_l(sp<ThreadBase> thread);
+ void setThread(const sp<ThreadBase>& thread);
+
wp<ThreadBase> mThread; // parent mixer thread
Mutex mLock; // mutex protecting effect list
Vector< sp<EffectModule> > mEffects; // list of effect modules
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 97b1753..3d17c89 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1147,21 +1147,6 @@ void AudioFlinger::ThreadBase::setMode(audio_mode_t mode)
}
}
-void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
- EffectHandle *handle,
- bool unpinIfLast) {
-
- Mutex::Autolock _l(mLock);
- ALOGV("disconnectEffect() %p effect %p", this, effect.get());
- // delete the effect module if removing last handle on it
- if (effect->removeHandle(handle) == 0) {
- if (!effect->isPinned() || unpinIfLast) {
- removeEffect_l(effect);
- AudioSystem::unregisterEffect(effect->id());
- }
- }
-}
-
void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config)
{
config->type = AUDIO_PORT_TYPE_MIX;
@@ -2278,7 +2263,7 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c
}
}
}
-
+ chain->setThread(this);
chain->setInBuffer(buffer, ownsBuffer);
chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled
? mEffectBuffer : mSinkBuffer));
@@ -6188,10 +6173,11 @@ status_t AudioFlinger::RecordThread::addEffectChain_l(const sp<EffectChain>& cha
{
// only one chain per input thread
if (mEffectChains.size() != 0) {
+ ALOGW("addEffectChain_l() already one chain %p on thread %p", chain.get(), this);
return INVALID_OPERATION;
}
ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this);
-
+ chain->setThread(this);
chain->setInBuffer(NULL);
chain->setOutBuffer(NULL);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 648502b..fd025b5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -283,9 +283,6 @@ public:
effect_descriptor_t *desc,
int *enabled,
status_t *status /*non-NULL*/);
- void disconnectEffect(const sp< EffectModule>& effect,
- EffectHandle *handle,
- bool unpinIfLast);
// return values for hasAudioSession (bit field)
enum effect_state {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 864daa5..98bf96e 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -166,6 +166,7 @@ protected:
sp<NBAIO_Source> mTeeSource;
bool mTerminated;
track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ...
+ audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to
};
// PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6cbab04..c0a75b9 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -96,7 +96,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mServerProxy(NULL),
mId(android_atomic_inc(&nextTrackId)),
mTerminated(false),
- mType(type)
+ mType(type),
+ mThreadIoHandle(thread->id())
{
// if the caller is us, trust the specified uid
if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
@@ -482,14 +483,15 @@ void AudioFlinger::PlaybackThread::Track::destroy()
// this Track with its member mTrack.
sp<Track> keep(this);
{ // scope for mLock
+ bool wasActive = false;
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- bool wasActive = playbackThread->destroyTrack_l(this);
- if (isExternalTrack() && !wasActive) {
- AudioSystem::releaseOutput(thread->id());
- }
+ wasActive = playbackThread->destroyTrack_l(this);
+ }
+ if (isExternalTrack() && !wasActive) {
+ AudioSystem::releaseOutput(mThreadIoHandle);
}
}
}
@@ -2050,7 +2052,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
if (recordThread->stop(this) && isExternalTrack()) {
- AudioSystem::stopInput(recordThread->id(), (audio_session_t)mSessionId);
+ AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
}
}
}
@@ -2060,14 +2062,14 @@ void AudioFlinger::RecordThread::RecordTrack::destroy()
// see comments at AudioFlinger::PlaybackThread::Track::destroy()
sp<RecordTrack> keep(this);
{
+ if (isExternalTrack()) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
+ }
+ AudioSystem::releaseInput(mThreadIoHandle, (audio_session_t)mSessionId);
+ }
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- if (isExternalTrack()) {
- if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopInput(thread->id(), (audio_session_t)mSessionId);
- }
- AudioSystem::releaseInput(thread->id(), (audio_session_t)mSessionId);
- }
Mutex::Autolock _l(thread->mLock);
RecordThread *recordThread = (RecordThread *) thread.get();
recordThread->destroyTrack_l(this);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 690f675..6a7f9e7 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1092,6 +1092,10 @@ status_t Camera3Device::waitUntilDrainedLocked() {
ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId);
status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ if (res != OK) {
+ SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
+ res);
+ }
return res;
}