summaryrefslogtreecommitdiffstats
path: root/services/audioflinger
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger')
-rw-r--r--services/audioflinger/AudioFlinger.cpp117
-rw-r--r--services/audioflinger/AudioFlinger.h5
-rw-r--r--services/audioflinger/ServiceUtilities.cpp7
-rw-r--r--services/audioflinger/ServiceUtilities.h1
-rw-r--r--services/audioflinger/Threads.cpp188
-rw-r--r--services/audioflinger/Threads.h13
-rw-r--r--services/audioflinger/Tracks.cpp36
7 files changed, 274 insertions, 93 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index c571cf5..993db73 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -650,6 +650,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
}
}
+ setAudioHwSyncForSession_l(thread, (audio_session_t)lSessionId);
}
if (lStatus != NO_ERROR) {
@@ -890,6 +891,21 @@ bool AudioFlinger::masterMute_l() const
return mMasterMute;
}
+status_t AudioFlinger::checkStreamType(audio_stream_type_t stream) const
+{
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
+ ALOGW("setStreamVolume() invalid stream %d", stream);
+ return BAD_VALUE;
+ }
+ pid_t caller = IPCThreadState::self()->getCallingPid();
+ if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && caller != getpid_cached) {
+ ALOGW("setStreamVolume() pid %d cannot use internal stream type %d", caller, stream);
+ return PERMISSION_DENIED;
+ }
+
+ return NO_ERROR;
+}
+
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
@@ -898,10 +914,11 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
return PERMISSION_DENIED;
}
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
- ALOGE("setStreamVolume() invalid stream %d", stream);
- return BAD_VALUE;
+ status_t status = checkStreamType(stream);
+ if (status != NO_ERROR) {
+ return status;
}
+ ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume");
AutoMutex lock(mLock);
PlaybackThread *thread = NULL;
@@ -932,8 +949,13 @@ status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted)
return PERMISSION_DENIED;
}
- if (uint32_t(stream) >= AUDIO_STREAM_CNT ||
- uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) {
+ status_t status = checkStreamType(stream);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to mute AUDIO_STREAM_PATCH");
+
+ if (uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) {
ALOGE("setStreamMute() invalid stream %d", stream);
return BAD_VALUE;
}
@@ -948,7 +970,8 @@ status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted)
float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const
{
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
+ status_t status = checkStreamType(stream);
+ if (status != NO_ERROR) {
return 0.0f;
}
@@ -969,7 +992,8 @@ float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t o
bool AudioFlinger::streamMute(audio_stream_type_t stream) const
{
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
+ status_t status = checkStreamType(stream);
+ if (status != NO_ERROR) {
return true;
}
@@ -1409,13 +1433,6 @@ sp<IAudioRecord> AudioFlinger::openRecord(
goto Exit;
}
- if (deviceRequiresCaptureAudioOutputPermission(thread->inDevice())
- && !captureAudioOutputAllowed()) {
- ALOGE("openRecord() permission denied: capture not allowed");
- lStatus = PERMISSION_DENIED;
- goto Exit;
- }
-
pid_t pid = IPCThreadState::self()->getCallingPid();
client = registerPid(pid);
@@ -1604,22 +1621,69 @@ status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice)
audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId)
{
Mutex::Autolock _l(mLock);
+
+ ssize_t index = mHwAvSyncIds.indexOfKey(sessionId);
+ if (index >= 0) {
+ ALOGV("getAudioHwSyncForSession found ID %d for session %d",
+ mHwAvSyncIds.valueAt(index), sessionId);
+ return mHwAvSyncIds.valueAt(index);
+ }
+
+ audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
+ if (dev == NULL) {
+ return AUDIO_HW_SYNC_INVALID;
+ }
+ char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
+ AudioParameter param = AudioParameter(String8(reply));
+ free(reply);
+
+ int value;
+ if (param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), value) != NO_ERROR) {
+ ALOGW("getAudioHwSyncForSession error getting sync for session %d", sessionId);
+ return AUDIO_HW_SYNC_INVALID;
+ }
+
+ // allow only one session for a given HW A/V sync ID.
+ for (size_t i = 0; i < mHwAvSyncIds.size(); i++) {
+ if (mHwAvSyncIds.valueAt(i) == (audio_hw_sync_t)value) {
+ ALOGV("getAudioHwSyncForSession removing ID %d for session %d",
+ value, mHwAvSyncIds.keyAt(i));
+ mHwAvSyncIds.removeItemsAt(i);
+ break;
+ }
+ }
+
+ mHwAvSyncIds.add(sessionId, value);
+
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> thread = mPlaybackThreads.valueAt(i);
- if ((thread->hasAudioSession(sessionId) & ThreadBase::TRACK_SESSION) != 0) {
- // A session can only be on one thread, so exit after first match
- String8 reply = thread->getParameters(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC));
- AudioParameter param = AudioParameter(reply);
- int value;
- if (param.getInt(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC), value) == NO_ERROR) {
- return value;
- }
+ uint32_t sessions = thread->hasAudioSession(sessionId);
+ if (sessions & PlaybackThread::TRACK_SESSION) {
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC), value);
+ thread->setParameters(param.toString());
break;
}
}
- return AUDIO_HW_SYNC_INVALID;
+
+ ALOGV("getAudioHwSyncForSession adding ID %d for session %d", value, sessionId);
+ return (audio_hw_sync_t)value;
+}
+
+// setAudioHwSyncForSession_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId)
+{
+ ssize_t index = mHwAvSyncIds.indexOfKey(sessionId);
+ if (index >= 0) {
+ audio_hw_sync_t syncId = mHwAvSyncIds.valueAt(index);
+ ALOGV("setAudioHwSyncForSession_l found ID %d for session %d", syncId, sessionId);
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC), syncId);
+ thread->setParameters(param.toString());
+ }
}
+
// ----------------------------------------------------------------------------
@@ -1928,13 +1992,13 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m
status_t status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig,
&inStream, flags, address.string(), source);
ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d"
- ", Format %#x, Channels %x, flags %#x, status %d",
+ ", Format %#x, Channels %x, flags %#x, status %d addr %s",
inStream,
halconfig.sample_rate,
halconfig.format,
halconfig.channel_mask,
flags,
- status);
+ status, address.string());
// If the input could not be opened with the requested parameters and we can handle the
// conversion internally, try to open again with the proposed parameters. The AudioFlinger can
@@ -2585,7 +2649,8 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
// Check whether the destination thread has a channel count of FCC_2, which is
// currently required for (most) effects. Prevent moving the effect chain here rather
// than disabling the addEffect_l() call in dstThread below.
- if (dstThread->mChannelCount != FCC_2) {
+ if ((dstThread->type() == ThreadBase::MIXER || dstThread->type() == ThreadBase::DUPLICATING) &&
+ dstThread->mChannelCount != FCC_2) {
ALOGW("moveEffectChain_l() effect chain failed because"
" destination thread %p channel count(%u) != %u",
dstThread, dstThread->mChannelCount, FCC_2);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 1003017..aa0af1f 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -733,6 +733,8 @@ private:
// Effect chains without a valid thread
DefaultKeyedVector< audio_session_t , sp<EffectChain> > mOrphanEffectChains;
+ // list of sessions for which a valid HW A/V sync ID was retrieved from the HAL
+ DefaultKeyedVector< audio_session_t , audio_hw_sync_t >mHwAvSyncIds;
private:
sp<Client> registerPid(pid_t pid); // always returns non-0
@@ -741,6 +743,9 @@ private:
void closeOutputInternal_l(sp<PlaybackThread> thread);
status_t closeInput_nonvirtual(audio_io_handle_t input);
void closeInputInternal_l(sp<RecordThread> thread);
+ void setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId);
+
+ status_t checkStreamType(audio_stream_type_t stream) const;
#ifdef TEE_SINK
// all record threads serially share a common tee sink, which is re-created on format change
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index 8246fef..fae19a1 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -50,6 +50,13 @@ bool captureHotwordAllowed() {
return ok;
}
+bool captureFmTunerAllowed() {
+ static const String16 sCaptureFmTunerAllowed("android.permission.ACCESS_FM_RADIO");
+ bool ok = checkCallingPermission(sCaptureFmTunerAllowed);
+ if (!ok) ALOGE("android.permission.ACCESS_FM_RADIO");
+ return ok;
+}
+
bool settingsAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index df6f6f4..ce18a90 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -23,6 +23,7 @@ extern pid_t getpid_cached;
bool recordingAllowed();
bool captureAudioOutputAllowed();
bool captureHotwordAllowed();
+bool captureFmTunerAllowed();
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
bool dumpAllowed();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ed8293d..d5bb0af 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1197,6 +1197,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
mScreenState(AudioFlinger::mScreenState),
// index 0 is reserved for normal mixer's submix
mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+ mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
// mLatchD, mLatchQ,
mLatchDValid(false), mLatchQValid(false)
{
@@ -1224,15 +1225,12 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
readOutputParameters_l();
- // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
- // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
+ // ++ operator does not compile
for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT;
stream = (audio_stream_type_t) (stream + 1)) {
mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
- // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here,
- // because mAudioFlinger doesn't have one to copy from
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -1625,13 +1623,15 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
if (track->isExternalTrack()) {
TrackBase::track_state state = track->mState;
mLock.unlock();
- status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
+ status = AudioSystem::startOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
mLock.lock();
// abort track was stopped/paused while we released the lock
if (state != track->mState) {
if (status == NO_ERROR) {
mLock.unlock();
- AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ AudioSystem::stopOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
mLock.lock();
}
return INVALID_OPERATION;
@@ -1848,6 +1848,19 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()
}
}
+ mHwSupportsPause = false;
+ if (mOutput->flags & AUDIO_OUTPUT_FLAG_DIRECT) {
+ if (mOutput->stream->pause != NULL) {
+ if (mOutput->stream->resume != NULL) {
+ mHwSupportsPause = true;
+ } else {
+ ALOGW("direct output implements pause but not resume");
+ }
+ } else if (mOutput->stream->resume != NULL) {
+ ALOGW("direct output implements resume but not pause");
+ }
+ }
+
// Calculate size of normal sink buffer relative to the HAL output buffer size
double multiplier = 1.0;
if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
@@ -2060,13 +2073,15 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
for (size_t i = 0 ; i < count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
if (track->isExternalTrack()) {
- AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ AudioSystem::stopOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
#ifdef ADD_BATTERY_DATA
// to track the speaker usage
addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
#endif
if (track->isTerminated()) {
- AudioSystem::releaseOutput(mId);
+ AudioSystem::releaseOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
}
}
}
@@ -2179,7 +2194,13 @@ void AudioFlinger::PlaybackThread::threadLoop_drain()
void AudioFlinger::PlaybackThread::threadLoop_exit()
{
- // Default implementation has nothing to do
+ {
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<Track> track = mTracks[i];
+ track->invalidate();
+ }
+ }
}
/*
@@ -2687,7 +2708,8 @@ status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
if (mNormalSink != 0) {
return mNormalSink->getTimestamp(timestamp);
}
- if ((mType == OFFLOAD || mType == DIRECT) && mOutput->stream->get_presentation_position) {
+ if ((mType == OFFLOAD || mType == DIRECT)
+ && mOutput != NULL && mOutput->stream->get_presentation_position) {
uint64_t position64;
int ret = mOutput->stream->get_presentation_position(
mOutput->stream, &position64, &timestamp.mTime);
@@ -3070,6 +3092,7 @@ void AudioFlinger::PlaybackThread::threadLoop_standby()
mCallbackThread->setWriteBlocked(mWriteAckSequence);
mCallbackThread->setDraining(mDrainSequence);
}
+ mHwPaused = false;
}
void AudioFlinger::PlaybackThread::onAddNewTrack_l()
@@ -3982,6 +4005,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
{
size_t count = mActiveTracks.size();
mixer_state mixerStatus = MIXER_IDLE;
+ bool doHwPause = false;
+ bool doHwResume = false;
+ bool flushPending = false;
// find out which tracks need to be processed
for (size_t i = 0; i < count; i++) {
@@ -4000,10 +4026,37 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
sp<Track> l = mLatestActiveTrack.promote();
bool last = l.get() == track;
+ if (mHwSupportsPause && track->isPausing()) {
+ track->setPaused();
+ if (last && !mHwPaused) {
+ doHwPause = true;
+ mHwPaused = true;
+ }
+ tracksToRemove->add(track);
+ } else if (track->isFlushPending()) {
+ track->flushAck();
+ if (last) {
+ flushPending = true;
+ }
+ } else if (mHwSupportsPause && track->isResumePending()){
+ track->resumeAck();
+ if (last) {
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ }
+ }
+ }
+
// The first time a track is added we wait
- // for all its buffers to be filled before processing it
+ // for all its buffers to be filled before processing it.
+ // Allow draining the buffer in case the client
+ // app does not call stop() and relies on underrun to stop:
+ // hence the test on (track->mRetryCount > 1).
+ // If retryCount<=1 then track is about to underrun and be removed.
uint32_t minFrames;
- if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()) {
+ if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()
+ && (track->mRetryCount > 1)) {
minFrames = mNormalFrameCount;
} else {
minFrames = 1;
@@ -4018,8 +4071,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
track->mFillingUpStatus = Track::FS_ACTIVE;
// make sure processVolume_l() will apply new volume even if 0
mLeftVolFloat = mRightVolFloat = -1.0;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
+ if (!mHwSupportsPause) {
+ track->resumeAck();
}
}
@@ -4082,6 +4135,30 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
}
}
+ // if an active track did not command a flush, check for pending flush on stopped tracks
+ if (!flushPending) {
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ if (mTracks[i]->isFlushPending()) {
+ mTracks[i]->flushAck();
+ flushPending = true;
+ }
+ }
+ }
+
+ // make sure the pause/flush/resume sequence is executed in the right order.
+ // If a flush is pending and a track is active but the HW is not paused, force a HW pause
+ // before flush and then resume HW. This can happen in case of pause/flush/resume
+ // if resume is received before pause is executed.
+ if (mHwSupportsPause && !mStandby &&
+ (doHwPause || (flushPending && !mHwPaused && (count != 0)))) {
+ mOutput->stream->pause(mOutput->stream);
+ }
+ if (flushPending) {
+ flushHw_l();
+ }
+ if (mHwSupportsPause && !mStandby && doHwResume) {
+ mOutput->stream->resume(mOutput->stream);
+ }
// remove all the tracks that need to be...
removeTracks_l(*tracksToRemove);
@@ -4114,6 +4191,11 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix()
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
{
+ // do not write to HAL when paused
+ if (mHwPaused) {
+ sleepTime = idleSleepTime;
+ return;
+ }
if (sleepTime == 0) {
if (mMixerStatus == MIXER_TRACKS_ENABLED) {
sleepTime = activeSleepTime;
@@ -4126,6 +4208,38 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
}
}
+void AudioFlinger::DirectOutputThread::threadLoop_exit()
+{
+ {
+ Mutex::Autolock _l(mLock);
+ bool flushPending = false;
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ if (mTracks[i]->isFlushPending()) {
+ mTracks[i]->flushAck();
+ flushPending = true;
+ }
+ }
+ if (flushPending) {
+ flushHw_l();
+ }
+ }
+ PlaybackThread::threadLoop_exit();
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::DirectOutputThread::shouldStandby_l()
+{
+ bool trackPaused = false;
+
+ // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
+ // after a timeout and we will enter standby then.
+ if (mTracks.size() > 0) {
+ trackPaused = mTracks[mTracks.size() - 1]->isPaused();
+ }
+
+ return !mStandby && !trackPaused;
+}
+
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
audio_format_t format __unused, int sessionId __unused)
@@ -4235,8 +4349,10 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l()
void AudioFlinger::DirectOutputThread::flushHw_l()
{
- if (mOutput->stream->flush != NULL)
+ if (mOutput->stream->flush != NULL) {
mOutput->stream->flush(mOutput->stream);
+ }
+ mHwPaused = false;
}
// ----------------------------------------------------------------------------
@@ -4345,8 +4461,6 @@ void AudioFlinger::AsyncCallbackThread::resetDraining()
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
: DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
- mHwPaused(false),
- mFlushPending(false),
mPausedBytesRemaining(0)
{
//FIXME: mStandby should be set to true by ThreadBase constructor
@@ -4583,21 +4697,6 @@ bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
return false;
}
-// must be called with thread mutex locked
-bool AudioFlinger::OffloadThread::shouldStandby_l()
-{
- bool trackPaused = false;
-
- // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
- // after a timeout and we will enter standby then.
- if (mTracks.size() > 0) {
- trackPaused = mTracks[mTracks.size() - 1]->isPaused();
- }
-
- return !mStandby && !trackPaused;
-}
-
-
bool AudioFlinger::OffloadThread::waitingAsyncCallback()
{
Mutex::Autolock _l(mLock);
@@ -4612,7 +4711,6 @@ void AudioFlinger::OffloadThread::flushHw_l()
mBytesRemaining = 0;
mPausedWriteLength = 0;
mPausedBytesRemaining = 0;
- mHwPaused = false;
if (mUseAsyncWrite) {
// discard any pending drain or write ack by incrementing sequence
@@ -4660,7 +4758,11 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix()
if (outputsReady(outputTracks)) {
mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
} else {
- memset(mSinkBuffer, 0, mSinkBufferSize);
+ if (mMixerBufferValid) {
+ memset(mMixerBuffer, 0, mMixerBufferSize);
+ } else {
+ memset(mSinkBuffer, 0, mSinkBufferSize);
+ }
}
sleepTime = 0;
writeFrames = mNormalFrameCount;
@@ -4690,15 +4792,15 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
{
+ // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT
+ // for delivery downstream as needed. This in-place conversion is safe as
+ // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format
+ // (AUDIO_FORMAT_PCM_8_BIT is not allowed here).
+ if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT,
+ mSinkBuffer, mFormat, writeFrames * mChannelCount);
+ }
for (size_t i = 0; i < outputTracks.size(); i++) {
- // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT
- // for delivery downstream as needed. This in-place conversion is safe as
- // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format
- // (AUDIO_FORMAT_PCM_8_BIT is not allowed here).
- if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT,
- mSinkBuffer, mFormat, writeFrames * mChannelCount);
- }
outputTracks[i]->write(reinterpret_cast<int16_t*>(mSinkBuffer), writeFrames);
}
mStandby = false;
@@ -4742,7 +4844,7 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
frameCount,
IPCThreadState::self()->getCallingUid());
if (outputTrack->cblk() != NULL) {
- thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
+ thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f);
mOutputTracks.add(outputTrack);
ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
updateWaitTime_l();
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index bb9aa18..f5d0e27 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -727,9 +727,7 @@ private:
void dumpTracks(int fd, const Vector<String16>& args);
SortedVector< sp<Track> > mTracks;
- // mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by
- // DuplicatingThread
- stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
+ stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
AudioStreamOut *mOutput;
float mMasterVolume;
@@ -811,7 +809,9 @@ public:
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
-
+ bool mHwSupportsPause;
+ bool mHwPaused;
+ bool mFlushPending;
private:
// timestamp latch:
// D input is written by threadLoop_write while mutex is unlocked, and read while locked
@@ -912,6 +912,8 @@ protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
+ virtual void threadLoop_exit();
+ virtual bool shouldStandby_l();
// volumes last sent to audio HAL with stream->set_volume()
float mLeftVolFloat;
@@ -942,12 +944,9 @@ protected:
virtual bool waitingAsyncCallback();
virtual bool waitingAsyncCallback_l();
- virtual bool shouldStandby_l();
virtual void onAddNewTrack_l();
private:
- bool mHwPaused;
- bool mFlushPending;
size_t mPausedWriteLength; // length in bytes of write interrupted by pause
size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
wp<Track> mPreviousTrack; // used to detect track switch
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b9308fa..e970036 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -491,7 +491,7 @@ void AudioFlinger::PlaybackThread::Track::destroy()
wasActive = playbackThread->destroyTrack_l(this);
}
if (isExternalTrack() && !wasActive) {
- AudioSystem::releaseOutput(mThreadIoHandle);
+ AudioSystem::releaseOutput(mThreadIoHandle, mStreamType, (audio_session_t)mSessionId);
}
}
}
@@ -611,15 +611,16 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
// ExtendedAudioBufferProvider interface
-// Note that framesReady() takes a mutex on the control block using tryLock().
-// This could result in priority inversion if framesReady() is called by the normal mixer,
-// as the normal mixer thread runs at lower
-// priority than the client's callback thread: there is a short window within framesReady()
-// during which the normal mixer could be preempted, and the client callback would block.
-// Another problem can occur if framesReady() is called by the fast mixer:
-// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
-// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
+// framesReady() may return an approximation of the number of frames if called
+// from a different thread than the one calling Proxy->obtainBuffer() and
+// Proxy->releaseBuffer(). Also note there is no mutual exclusion in the
+// AudioTrackServerProxy so be especially careful calling with FastTracks.
size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
+ if (mSharedBuffer != 0 && (isStopped() || isStopping())) {
+ // Static tracks return zero frames immediately upon stopping (for FastTracks).
+ // The remainder of the buffer is not drained.
+ return 0;
+ }
return mAudioTrackServerProxy->framesReady();
}
@@ -822,12 +823,11 @@ void AudioFlinger::PlaybackThread::Track::flush()
// this will be done by prepareTracks_l() when the track is stopped.
// prepareTracks_l() will see mState == FLUSHED, then
// remove from active track list, reset(), and trigger presentation complete
+ if (isDirect()) {
+ mFlushHwPending = true;
+ }
if (playbackThread->mActiveTracks.indexOf(this) < 0) {
reset();
- if (thread->type() == ThreadBase::DIRECT) {
- DirectOutputThread *t = (DirectOutputThread *)playbackThread;
- t->flushHw_l();
- }
}
}
// Prevent flush being lost if the track is flushed and then resumed
@@ -840,7 +840,7 @@ void AudioFlinger::PlaybackThread::Track::flush()
// must be called with thread lock held
void AudioFlinger::PlaybackThread::Track::flushAck()
{
- if (!isOffloaded())
+ if (!isOffloaded() && !isDirect())
return;
mFlushHwPending = false;
@@ -1656,8 +1656,9 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
audio_channel_mask_t channelMask,
size_t frameCount,
int uid)
- : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
- NULL, 0, 0, uid, IAudioFlinger::TRACK_DEFAULT, TYPE_OUTPUT),
+ : Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
+ sampleRate, format, channelMask, frameCount,
+ NULL, 0, 0, uid, IAudioFlinger::TRACK_DEFAULT, TYPE_OUTPUT),
mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
@@ -1872,7 +1873,8 @@ AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThr
size_t frameCount,
void *buffer,
IAudioFlinger::track_flags_t flags)
- : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
+ : Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
+ sampleRate, format, channelMask, frameCount,
buffer, 0, 0, getuid(), flags, TYPE_PATCH),
mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
{