summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/Threads.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger/Threads.cpp')
-rw-r--r--services/audioflinger/Threads.cpp793
1 files changed, 460 insertions, 333 deletions
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2f71db7..2b37761 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -269,8 +269,8 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio
: Thread(false /*canCallJava*/),
mType(type),
mAudioFlinger(audioFlinger),
- // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are
- // set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
+ // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize
+ // are set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
mParamStatus(NO_ERROR),
//FIXME: mStandby should be true here. Is this some kind of hack?
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
@@ -297,6 +297,17 @@ AudioFlinger::ThreadBase::~ThreadBase()
}
}
+status_t AudioFlinger::ThreadBase::readyToRun()
+{
+ status_t status = initCheck();
+ if (status == NO_ERROR) {
+ ALOGI("AudioFlinger's thread %p ready to run", this);
+ } else {
+ ALOGE("No working audio driver found.");
+ }
+ return status;
+}
+
void AudioFlinger::ThreadBase::exit()
{
ALOGV("ThreadBase::exit");
@@ -369,7 +380,13 @@ void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32
void AudioFlinger::ThreadBase::processConfigEvents()
{
- mLock.lock();
+ Mutex::Autolock _l(mLock);
+ processConfigEvents_l();
+}
+
+// post condition: mConfigEvents.isEmpty()
+void AudioFlinger::ThreadBase::processConfigEvents_l()
+{
while (!mConfigEvents.isEmpty()) {
ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
ConfigEvent *event = mConfigEvents[0];
@@ -377,32 +394,31 @@ void AudioFlinger::ThreadBase::processConfigEvents()
// release mLock before locking AudioFlinger mLock: lock order is always
// AudioFlinger then ThreadBase to avoid cross deadlock
mLock.unlock();
- switch(event->type()) {
- case CFG_EVENT_PRIO: {
- PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event);
- // FIXME Need to understand why this has be done asynchronously
- int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(),
- true /*asynchronous*/);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; "
- "error %d",
- prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err);
- }
- } break;
- case CFG_EVENT_IO: {
- IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event);
- mAudioFlinger->mLock.lock();
+ switch (event->type()) {
+ case CFG_EVENT_PRIO: {
+ PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event);
+ // FIXME Need to understand why this has be done asynchronously
+ int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(),
+ true /*asynchronous*/);
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+ prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err);
+ }
+ } break;
+ case CFG_EVENT_IO: {
+ IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event);
+ {
+ Mutex::Autolock _l(mAudioFlinger->mLock);
audioConfigChanged_l(ioEvent->event(), ioEvent->param());
- mAudioFlinger->mLock.unlock();
- } break;
- default:
- ALOGE("processConfigEvents() unknown event type %d", event->type());
- break;
+ }
+ } break;
+ default:
+ ALOGE("processConfigEvents() unknown event type %d", event->type());
+ break;
}
delete event;
mLock.lock();
}
- mLock.unlock();
}
void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
@@ -427,6 +443,8 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "HAL frame count: %d\n", mFrameCount);
result.append(buffer);
+ snprintf(buffer, SIZE, "HAL buffer size: %u bytes\n", mBufferSize);
+ result.append(buffer);
snprintf(buffer, SIZE, "Channel Count: %u\n", mChannelCount);
result.append(buffer);
snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
@@ -739,8 +757,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
int sessionId,
effect_descriptor_t *desc,
int *enabled,
- status_t *status
- )
+ status_t *status)
{
sp<EffectModule> effect;
sp<EffectHandle> handle;
@@ -829,7 +846,10 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
}
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
- lStatus = effect->addHandle(handle.get());
+ lStatus = handle->initCheck();
+ if (lStatus == OK) {
+ lStatus = effect->addHandle(handle.get());
+ }
if (enabled != NULL) {
*enabled = (int)effect->isEnabled();
}
@@ -850,9 +870,7 @@ Exit:
handle.clear();
}
- if (status != NULL) {
- *status = lStatus;
- }
+ *status = lStatus;
return handle;
}
@@ -1002,7 +1020,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
type_t type)
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
mNormalFrameCount(0), mMixBuffer(NULL),
- mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ mSuspended(0), mBytesWritten(0),
mActiveTracksGeneration(0),
// mStreamTypes[] initialized in constructor body
mOutput(output),
@@ -1060,7 +1078,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
AudioFlinger::PlaybackThread::~PlaybackThread()
{
mAudioFlinger->unregisterWriter(mNBLogWriter);
- delete [] mAllocMixBuffer;
+ delete[] mMixBuffer;
}
void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
@@ -1150,16 +1168,6 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>&
}
// Thread virtuals
-status_t AudioFlinger::PlaybackThread::readyToRun()
-{
- status_t status = initCheck();
- if (status == NO_ERROR) {
- ALOGI("AudioFlinger's thread %p ready to run", this);
- } else {
- ALOGE("No working audio driver found.");
- }
- return status;
-}
void AudioFlinger::PlaybackThread::onFirstRef()
{
@@ -1182,7 +1190,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
IAudioFlinger::track_flags_t *flags,
@@ -1190,6 +1198,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
int uid,
status_t *status)
{
+ size_t frameCount = *pFrameCount;
sp<Track> track;
status_t lStatus;
@@ -1258,6 +1267,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
}
}
}
+ *pFrameCount = frameCount;
if (mType == DIRECT) {
if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
@@ -1326,8 +1336,13 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
track = TimedTrack::create(this, client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, sessionId, uid);
}
- if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
- lStatus = NO_MEMORY;
+
+ // new Track always returns non-NULL,
+ // but TimedTrack::create() is a factory that could fail by returning NULL
+ lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
+ if (lStatus != NO_ERROR) {
+ ALOGE("createTrack_l() initCheck failed %d; no control block?", lStatus);
+ track.clear();
goto Exit;
}
@@ -1352,9 +1367,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
lStatus = NO_ERROR;
Exit:
- if (status) {
- *status = lStatus;
- }
+ *status = lStatus;
return track;
}
@@ -1642,7 +1655,8 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
mFormat);
}
mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
- mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
+ mBufferSize = mOutput->stream->common.get_buffer_size(&mOutput->stream->common);
+ mFrameCount = mBufferSize / mFrameSize;
if (mFrameCount & 15) {
ALOGW("HAL output buffer size is %u frames but AudioMixer requires multiples of 16 frames",
mFrameCount);
@@ -1699,11 +1713,11 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount,
mNormalFrameCount);
- delete[] mAllocMixBuffer;
- size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize;
- mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1];
- mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align);
- memset(mMixBuffer, 0, mNormalFrameCount * mFrameSize);
+ delete[] mMixBuffer;
+ size_t normalBufferSize = mNormalFrameCount * mFrameSize;
+ // For historical reasons mMixBuffer is int16_t[], but mFrameSize can be odd (such as 1)
+ mMixBuffer = new int16_t[(normalBufferSize + 1) >> 1];
+ memset(mMixBuffer, 0, normalBufferSize);
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
@@ -1837,7 +1851,7 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
const Vector< sp<Track> >& tracksToRemove)
{
size_t count = tracksToRemove.size();
- if (count) {
+ if (count > 0) {
for (size_t i = 0 ; i < count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
if (!track->isOutputTrack()) {
@@ -1913,7 +1927,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
// otherwise use the HAL / AudioStreamOut directly
} else {
// Direct output and offload threads
- size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
+ size_t offset = (mCurrentWriteLength - mBytesRemaining);
if (mUseAsyncWrite) {
ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
mWriteAckSequence += 2;
@@ -1924,7 +1938,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
// FIXME We should have an implementation of timestamps for direct output threads.
// They are used e.g for multichannel PCM playback over HDMI.
bytesWritten = mOutput->stream->write(mOutput->stream,
- mMixBuffer + offset, mBytesRemaining);
+ (char *)mMixBuffer + offset, mBytesRemaining);
if (mUseAsyncWrite &&
((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
// do not wait for async callback in case of error of full write
@@ -2405,7 +2419,7 @@ if (mType == MIXER) {
void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
{
size_t count = tracksToRemove.size();
- if (count) {
+ if (count > 0) {
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
mActiveTracks.remove(track);
@@ -2798,7 +2812,7 @@ void AudioFlinger::MixerThread::threadLoop_sleepTime()
sleepTime = idleSleepTime;
}
} else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
- memset (mMixBuffer, 0, mixBufferSize);
+ memset(mMixBuffer, 0, mixBufferSize);
sleepTime = 0;
ALOGV_IF(mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED),
"anticipated start");
@@ -3024,27 +3038,22 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// +1 for rounding and +1 for additional sample needed for interpolation
desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
// add frames already consumed but not yet released by the resampler
- // because cblk->framesReady() will include these frames
+ // because mAudioTrackServerProxy->framesReady() will include these frames
desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+#if 0
// the minimum track buffer size is normally twice the number of frames necessary
// to fill one buffer and the resampler should not leave more than one buffer worth
// of unreleased frames after each pass, but just in case...
ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
+#endif
}
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
minFrames = desiredFrames;
}
- // It's not safe to call framesReady() for a static buffer track, so assume it's ready
- size_t framesReady;
- if (track->sharedBuffer() == 0) {
- framesReady = track->framesReady();
- } else if (track->isStopped()) {
- framesReady = 0;
- } else {
- framesReady = 1;
- }
+
+ size_t framesReady = track->framesReady();
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
@@ -3362,6 +3371,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
status = BAD_VALUE;
} else {
+ // no need to save value, since it's constant
reconfig = true;
}
}
@@ -3369,6 +3379,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) {
status = BAD_VALUE;
} else {
+ // no need to save value, since it's constant
reconfig = true;
}
}
@@ -4168,15 +4179,15 @@ bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
// must be called with thread mutex locked
bool AudioFlinger::OffloadThread::shouldStandby_l()
{
- bool TrackPaused = false;
+ 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();
+ trackPaused = mTracks[mTracks.size() - 1]->isPaused();
}
- return !mStandby && !TrackPaused;
+ return !mStandby && !trackPaused;
}
@@ -4383,8 +4394,10 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
#endif
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
- mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
- // mRsmpInIndex and mBufferSize set by readInputParameters()
+ mInput(input), mActiveTracksGen(0), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ // mRsmpInFrames, mRsmpInFramesP2, mRsmpInUnrel, mRsmpInFront, and mRsmpInRear
+ // are set by readInputParameters()
+ // mRsmpInIndex LEGACY
mReqChannelCount(popcount(channelMask)),
mReqSampleRate(sampleRate)
// mBytesRead is only meaningful while active, and so is cleared in start()
@@ -4394,6 +4407,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
#endif
{
snprintf(mName, kNameLength, "AudioIn_%X", id);
+ mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
readInputParameters();
}
@@ -4401,6 +4415,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
AudioFlinger::RecordThread::~RecordThread()
{
+ mAudioFlinger->unregisterWriter(mNBLogWriter);
delete[] mRsmpInBuffer;
delete mResampler;
delete[] mRsmpOutBuffer;
@@ -4411,230 +4426,320 @@ void AudioFlinger::RecordThread::onFirstRef()
run(mName, PRIORITY_URGENT_AUDIO);
}
-status_t AudioFlinger::RecordThread::readyToRun()
-{
- status_t status = initCheck();
- ALOGW_IF(status != NO_ERROR,"RecordThread %p could not initialize", this);
- return status;
-}
-
bool AudioFlinger::RecordThread::threadLoop()
{
- AudioBufferProvider::Buffer buffer;
- sp<RecordTrack> activeTrack;
- Vector< sp<EffectChain> > effectChains;
-
nsecs_t lastWarning = 0;
inputStandBy();
- {
- Mutex::Autolock _l(mLock);
- activeTrack = mActiveTrack;
- acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1);
- }
// used to verify we've read at least once before evaluating how many bytes were read
bool readOnce = false;
- // start recording
- while (!exitPending()) {
-
- processConfigEvents();
+ // used to request a deferred sleep, to be executed later while mutex is unlocked
+ bool doSleep = false;
- { // scope for mLock
- Mutex::Autolock _l(mLock);
- checkForNewParameters_l();
- if (mActiveTrack != 0 && activeTrack != mActiveTrack) {
+reacquire_wakelock:
+ sp<RecordTrack> activeTrack;
+ int activeTracksGen;
+ {
+ Mutex::Autolock _l(mLock);
+ size_t size = mActiveTracks.size();
+ activeTracksGen = mActiveTracksGen;
+ if (size > 0) {
+ // FIXME an arbitrary choice
+ activeTrack = mActiveTracks[0];
+ acquireWakeLock_l(activeTrack->uid());
+ if (size > 1) {
SortedVector<int> tmp;
- tmp.add(mActiveTrack->uid());
+ for (size_t i = 0; i < size; i++) {
+ tmp.add(mActiveTracks[i]->uid());
+ }
updateWakeLockUids_l(tmp);
}
- activeTrack = mActiveTrack;
- if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
- standby();
+ } else {
+ acquireWakeLock_l(-1);
+ }
+ }
- if (exitPending()) {
- break;
- }
+ // start recording
+ for (;;) {
+ TrackBase::track_state activeTrackState;
+ Vector< sp<EffectChain> > effectChains;
+ // sleep with mutex unlocked
+ if (doSleep) {
+ doSleep = false;
+ usleep(kRecordThreadSleepUs);
+ }
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ if (exitPending()) {
+ break;
+ }
+ processConfigEvents_l();
+ // return value 'reconfig' is currently unused
+ bool reconfig = checkForNewParameters_l();
+
+ // if no active track(s), then standby and release wakelock
+ size_t size = mActiveTracks.size();
+ if (size == 0) {
+ standbyIfNotAlreadyInStandby();
+ // exitPending() can't become true here
releaseWakeLock_l();
ALOGV("RecordThread: loop stopping");
// go to sleep
mWaitWorkCV.wait(mLock);
ALOGV("RecordThread: loop starting");
- acquireWakeLock_l(mActiveTrack != 0 ? mActiveTrack->uid() : -1);
+ goto reacquire_wakelock;
+ }
+
+ if (mActiveTracksGen != activeTracksGen) {
+ activeTracksGen = mActiveTracksGen;
+ SortedVector<int> tmp;
+ for (size_t i = 0; i < size; i++) {
+ tmp.add(mActiveTracks[i]->uid());
+ }
+ updateWakeLockUids_l(tmp);
+ // FIXME an arbitrary choice
+ activeTrack = mActiveTracks[0];
+ }
+
+ if (activeTrack->isTerminated()) {
+ removeTrack_l(activeTrack);
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
continue;
}
- if (mActiveTrack != 0) {
- if (mActiveTrack->isTerminated()) {
- removeTrack_l(mActiveTrack);
- mActiveTrack.clear();
- } else if (mActiveTrack->mState == TrackBase::PAUSING) {
- standby();
- mActiveTrack.clear();
+
+ activeTrackState = activeTrack->mState;
+ switch (activeTrackState) {
+ case TrackBase::PAUSING:
+ standbyIfNotAlreadyInStandby();
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
+ mStartStopCond.broadcast();
+ doSleep = true;
+ continue;
+
+ case TrackBase::RESUMING:
+ mStandby = false;
+ if (mReqChannelCount != activeTrack->channelCount()) {
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
mStartStopCond.broadcast();
- } else if (mActiveTrack->mState == TrackBase::RESUMING) {
- if (mReqChannelCount != mActiveTrack->channelCount()) {
- mActiveTrack.clear();
- mStartStopCond.broadcast();
- } else if (readOnce) {
- // record start succeeds only if first read from audio input
- // succeeds
- if (mBytesRead >= 0) {
- mActiveTrack->mState = TrackBase::ACTIVE;
- } else {
- mActiveTrack.clear();
- }
- mStartStopCond.broadcast();
+ continue;
+ }
+ if (readOnce) {
+ mStartStopCond.broadcast();
+ // record start succeeds only if first read from audio input succeeds
+ if (mBytesRead < 0) {
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
+ continue;
}
- mStandby = false;
+ activeTrack->mState = TrackBase::ACTIVE;
}
+ break;
+
+ case TrackBase::ACTIVE:
+ break;
+
+ case TrackBase::IDLE:
+ doSleep = true;
+ continue;
+
+ default:
+ LOG_FATAL("Unexpected activeTrackState %d", activeTrackState);
}
lockEffectChains_l(effectChains);
}
- if (mActiveTrack != 0) {
- if (mActiveTrack->mState != TrackBase::ACTIVE &&
- mActiveTrack->mState != TrackBase::RESUMING) {
- unlockEffectChains(effectChains);
- usleep(kRecordThreadSleepUs);
- continue;
- }
- for (size_t i = 0; i < effectChains.size(); i ++) {
- effectChains[i]->process_l();
- }
+ // thread mutex is now unlocked, mActiveTracks unknown, activeTrack != 0, kept, immutable
+ // activeTrack->mState unknown, activeTrackState immutable and is ACTIVE or RESUMING
- buffer.frameCount = mFrameCount;
- status_t status = mActiveTrack->getNextBuffer(&buffer);
- if (status == NO_ERROR) {
- readOnce = true;
- size_t framesOut = buffer.frameCount;
- if (mResampler == NULL) {
- // no resampling
- while (framesOut) {
- size_t framesIn = mFrameCount - mRsmpInIndex;
- if (framesIn) {
- int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
- int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) *
- mActiveTrack->mFrameSize;
- if (framesIn > framesOut)
- framesIn = framesOut;
- mRsmpInIndex += framesIn;
- framesOut -= framesIn;
- if (mChannelCount == mReqChannelCount) {
- memcpy(dst, src, framesIn * mFrameSize);
- } else {
- if (mChannelCount == 1) {
- upmix_to_stereo_i16_from_mono_i16((int16_t *)dst,
- (int16_t *)src, framesIn);
- } else {
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
- (int16_t *)src, framesIn);
- }
- }
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ // thread mutex is not locked, but effect chain is locked
+ effectChains[i]->process_l();
+ }
+
+ AudioBufferProvider::Buffer buffer;
+ buffer.frameCount = mFrameCount;
+ status_t status = activeTrack->getNextBuffer(&buffer);
+ if (status == NO_ERROR) {
+ readOnce = true;
+ size_t framesOut = buffer.frameCount;
+ if (mResampler == NULL) {
+ // no resampling
+ while (framesOut) {
+ size_t framesIn = mFrameCount - mRsmpInIndex;
+ if (framesIn > 0) {
+ int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
+ int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) *
+ activeTrack->mFrameSize;
+ if (framesIn > framesOut) {
+ framesIn = framesOut;
}
- if (framesOut && mFrameCount == mRsmpInIndex) {
- void *readInto;
- if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
- readInto = buffer.raw;
- framesOut = 0;
+ mRsmpInIndex += framesIn;
+ framesOut -= framesIn;
+ if (mChannelCount == mReqChannelCount) {
+ memcpy(dst, src, framesIn * mFrameSize);
+ } else {
+ if (mChannelCount == 1) {
+ upmix_to_stereo_i16_from_mono_i16((int16_t *)dst,
+ (int16_t *)src, framesIn);
} else {
- readInto = mRsmpInBuffer;
- mRsmpInIndex = 0;
+ downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
+ (int16_t *)src, framesIn);
}
- mBytesRead = mInput->stream->read(mInput->stream, readInto,
- mBufferSize);
- if (mBytesRead <= 0) {
- if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
- {
- ALOGE("Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- usleep(kRecordThreadSleepUs);
- }
- mRsmpInIndex = mFrameCount;
- framesOut = 0;
- buffer.frameCount = 0;
+ }
+ }
+ if (framesOut > 0 && mFrameCount == mRsmpInIndex) {
+ void *readInto;
+ if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
+ readInto = buffer.raw;
+ framesOut = 0;
+ } else {
+ readInto = mRsmpInBuffer;
+ mRsmpInIndex = 0;
+ }
+ mBytesRead = mInput->stream->read(mInput->stream, readInto,
+ mBufferSize);
+ if (mBytesRead <= 0) {
+ // TODO: verify that it's benign to use a stale track state
+ if ((mBytesRead < 0) && (activeTrackState == TrackBase::ACTIVE))
+ {
+ ALOGE("Error reading audio input");
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ inputStandBy();
+ doSleep = true;
}
+ mRsmpInIndex = mFrameCount;
+ framesOut = 0;
+ buffer.frameCount = 0;
+ }
#ifdef TEE_SINK
- else if (mTeeSink != 0) {
- (void) mTeeSink->write(readInto,
- mBytesRead >> Format_frameBitShift(mTeeSink->format()));
- }
-#endif
+ else if (mTeeSink != 0) {
+ (void) mTeeSink->write(readInto,
+ mBytesRead >> Format_frameBitShift(mTeeSink->format()));
}
+#endif
}
- } else {
- // resampling
-
- // resampler accumulates, but we only have one source track
- memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
- // alter output frame count as if we were expecting stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- framesOut >>= 1;
+ }
+ } else {
+ // resampling
+
+ // avoid busy-waiting if client doesn't keep up
+ bool madeProgress = false;
+
+ // keep mRsmpInBuffer full so resampler always has sufficient input
+ for (;;) {
+ int32_t rear = mRsmpInRear;
+ ssize_t filled = rear - mRsmpInFront;
+ ALOG_ASSERT(0 <= filled && (size_t) filled <= mRsmpInFramesP2);
+ // exit once there is enough data in buffer for resampler
+ if ((size_t) filled >= mRsmpInFrames) {
+ break;
}
- mResampler->resample(mRsmpOutBuffer, framesOut,
- this /* AudioBufferProvider* */);
- // ditherAndClamp() works as long as all buffers returned by
- // mActiveTrack->getNextBuffer() are 32 bit aligned which should be always true.
- if (mChannelCount == 2 && mReqChannelCount == 1) {
- // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
- ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
- // the resampler always outputs stereo samples:
- // do post stereo to mono conversion
- downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer,
- framesOut);
- } else {
- ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
+ size_t avail = mRsmpInFramesP2 - filled;
+ // Only try to read full HAL buffers.
+ // But if the HAL read returns a partial buffer, use it.
+ if (avail < mFrameCount) {
+ ALOGE("insufficient space to read: avail %d < mFrameCount %d",
+ avail, mFrameCount);
+ break;
}
- // now done with mRsmpOutBuffer
+ // If 'avail' is non-contiguous, first read past the nominal end of buffer, then
+ // copy to the right place. Permitted because mRsmpInBuffer was over-allocated.
+ rear &= mRsmpInFramesP2 - 1;
+ mBytesRead = mInput->stream->read(mInput->stream,
+ &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+ if (mBytesRead <= 0) {
+ ALOGE("read failed: mBytesRead=%d < %u", mBytesRead, mBufferSize);
+ break;
+ }
+ ALOG_ASSERT((size_t) mBytesRead <= mBufferSize);
+ size_t framesRead = mBytesRead / mFrameSize;
+ ALOG_ASSERT(framesRead > 0);
+ madeProgress = true;
+ // If 'avail' was non-contiguous, we now correct for reading past end of buffer.
+ size_t part1 = mRsmpInFramesP2 - rear;
+ if (framesRead > part1) {
+ memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
+ (framesRead - part1) * mFrameSize);
+ }
+ mRsmpInRear += framesRead;
+ }
+ if (!madeProgress) {
+ ALOGV("Did not make progress");
+ usleep(((mFrameCount * 1000) / mSampleRate) * 1000);
}
- if (mFramestoDrop == 0) {
- mActiveTrack->releaseBuffer(&buffer);
+
+ // resampler accumulates, but we only have one source track
+ memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
+ mResampler->resample(mRsmpOutBuffer, framesOut,
+ this /* AudioBufferProvider* */);
+ // ditherAndClamp() works as long as all buffers returned by
+ // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
+ if (mReqChannelCount == 1) {
+ // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
+ ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
+ // the resampler always outputs stereo samples:
+ // do post stereo to mono conversion
+ downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer,
+ framesOut);
} else {
- if (mFramestoDrop > 0) {
- mFramestoDrop -= buffer.frameCount;
- if (mFramestoDrop <= 0) {
- clearSyncStartEvent();
- }
- } else {
- mFramestoDrop += buffer.frameCount;
- if (mFramestoDrop >= 0 || mSyncStartEvent == 0 ||
- mSyncStartEvent->isCancelled()) {
- ALOGW("Synced record %s, session %d, trigger session %d",
- (mFramestoDrop >= 0) ? "timed out" : "cancelled",
- mActiveTrack->sessionId(),
- (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0);
- clearSyncStartEvent();
- }
- }
+ ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
}
- mActiveTrack->clearOverflow();
+ // now done with mRsmpOutBuffer
+
}
- // client isn't retrieving buffers fast enough
- else {
- if (!mActiveTrack->setOverflow()) {
- nsecs_t now = systemTime();
- if ((now - lastWarning) > kWarningThrottleNs) {
- ALOGW("RecordThread: buffer overflow");
- lastWarning = now;
+ if (mFramestoDrop == 0) {
+ activeTrack->releaseBuffer(&buffer);
+ } else {
+ if (mFramestoDrop > 0) {
+ mFramestoDrop -= buffer.frameCount;
+ if (mFramestoDrop <= 0) {
+ clearSyncStartEvent();
+ }
+ } else {
+ mFramestoDrop += buffer.frameCount;
+ if (mFramestoDrop >= 0 || mSyncStartEvent == 0 ||
+ mSyncStartEvent->isCancelled()) {
+ ALOGW("Synced record %s, session %d, trigger session %d",
+ (mFramestoDrop >= 0) ? "timed out" : "cancelled",
+ activeTrack->sessionId(),
+ (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0);
+ clearSyncStartEvent();
}
}
- // Release the processor for a while before asking for a new buffer.
- // This will give the application more chance to read from the buffer and
- // clear the overflow.
- usleep(kRecordThreadSleepUs);
}
+ activeTrack->clearOverflow();
}
+ // client isn't retrieving buffers fast enough
+ else {
+ if (!activeTrack->setOverflow()) {
+ nsecs_t now = systemTime();
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ALOGW("RecordThread: buffer overflow");
+ lastWarning = now;
+ }
+ }
+ // Release the processor for a while before asking for a new buffer.
+ // This will give the application more chance to read from the buffer and
+ // clear the overflow.
+ doSleep = true;
+ }
+
// enable changes in effect chain
unlockEffectChains(effectChains);
- effectChains.clear();
+ // effectChains doesn't need to be cleared, since it is cleared by destructor at scope end
}
- standby();
+ standbyIfNotAlreadyInStandby();
{
Mutex::Autolock _l(mLock);
@@ -4642,7 +4747,8 @@ bool AudioFlinger::RecordThread::threadLoop()
sp<RecordTrack> track = mTracks[i];
track->invalidate();
}
- mActiveTrack.clear();
+ mActiveTracks.clear();
+ mActiveTracksGen++;
mStartStopCond.broadcast();
}
@@ -4652,7 +4758,7 @@ bool AudioFlinger::RecordThread::threadLoop()
return false;
}
-void AudioFlinger::RecordThread::standby()
+void AudioFlinger::RecordThread::standbyIfNotAlreadyInStandby()
{
if (!mStandby) {
inputStandBy();
@@ -4665,18 +4771,19 @@ void AudioFlinger::RecordThread::inputStandBy()
mInput->stream->common.standby(&mInput->stream->common);
}
-sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
+sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
int sessionId,
int uid,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status)
{
+ size_t frameCount = *pFrameCount;
sp<RecordTrack> track;
status_t lStatus;
@@ -4735,6 +4842,7 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
}
}
}
+ *pFrameCount = frameCount;
// FIXME use flags and tid similar to createTrack_l()
@@ -4744,9 +4852,9 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
track = new RecordTrack(this, client, sampleRate,
format, channelMask, frameCount, sessionId, uid);
- if (track->getCblk() == 0) {
- ALOGE("createRecordTrack_l() no control block");
- lStatus = NO_MEMORY;
+ lStatus = track->initCheck();
+ if (lStatus != NO_ERROR) {
+ ALOGE("createRecordTrack_l() initCheck failed %d; no control block?", lStatus);
track.clear();
goto Exit;
}
@@ -4768,9 +4876,7 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
lStatus = NO_ERROR;
Exit:
- if (status) {
- *status = lStatus;
- }
+ *status = lStatus;
return track;
}
@@ -4801,43 +4907,57 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
}
{
+ // This section is a rendezvous between binder thread executing start() and RecordThread
AutoMutex lock(mLock);
- if (mActiveTrack != 0) {
- if (recordTrack != mActiveTrack.get()) {
+ if (mActiveTracks.size() > 0) {
+ // FIXME does not work for multiple active tracks
+ if (mActiveTracks.indexOf(recordTrack) != 0) {
status = -EBUSY;
- } else if (mActiveTrack->mState == TrackBase::PAUSING) {
- mActiveTrack->mState = TrackBase::ACTIVE;
+ } else if (recordTrack->mState == TrackBase::PAUSING) {
+ recordTrack->mState = TrackBase::ACTIVE;
}
return status;
}
+ // FIXME why? already set in constructor, 'STARTING_1' would be more accurate
recordTrack->mState = TrackBase::IDLE;
- mActiveTrack = recordTrack;
+ mActiveTracks.add(recordTrack);
+ mActiveTracksGen++;
mLock.unlock();
status_t status = AudioSystem::startInput(mId);
mLock.lock();
+ // FIXME should verify that mActiveTrack is still == recordTrack
if (status != NO_ERROR) {
- mActiveTrack.clear();
+ mActiveTracks.remove(recordTrack);
+ mActiveTracksGen++;
clearSyncStartEvent();
return status;
}
+ // FIXME LEGACY
mRsmpInIndex = mFrameCount;
+ mRsmpInFront = 0;
+ mRsmpInRear = 0;
+ mRsmpInUnrel = 0;
mBytesRead = 0;
if (mResampler != NULL) {
mResampler->reset();
}
- mActiveTrack->mState = TrackBase::RESUMING;
+ // FIXME hijacking a playback track state name which was intended for start after pause;
+ // here 'STARTING_2' would be more accurate
+ recordTrack->mState = TrackBase::RESUMING;
// signal thread to start
ALOGV("Signal record thread");
mWaitWorkCV.broadcast();
// do not wait for mStartStopCond if exiting
if (exitPending()) {
- mActiveTrack.clear();
+ mActiveTracks.remove(recordTrack);
+ mActiveTracksGen++;
status = INVALID_OPERATION;
goto startError;
}
+ // FIXME incorrect usage of wait: no explicit predicate or loop
mStartStopCond.wait(mLock);
- if (mActiveTrack == 0) {
+ if (mActiveTracks.indexOf(recordTrack) < 0) {
ALOGV("Record failed to start");
status = BAD_VALUE;
goto startError;
@@ -4883,17 +5003,19 @@ void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event
bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
AutoMutex _l(mLock);
- if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
+ if (mActiveTracks.indexOf(recordTrack) != 0 || recordTrack->mState == TrackBase::PAUSING) {
return false;
}
+ // note that threadLoop may still be processing the track at this point [without lock]
recordTrack->mState = TrackBase::PAUSING;
// do not wait for mStartStopCond if exiting
if (exitPending()) {
return true;
}
+ // FIXME incorrect usage of wait: no explicit predicate or loop
mStartStopCond.wait(mLock);
- // if we have been restarted, recordTrack == mActiveTrack.get() here
- if (exitPending() || recordTrack != mActiveTrack.get()) {
+ // if we have been restarted, recordTrack is in mActiveTracks here
+ if (exitPending() || mActiveTracks.indexOf(recordTrack) != 0) {
ALOGV("Record stopped OK");
return true;
}
@@ -4936,7 +5058,7 @@ void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
track->terminate();
track->mState = TrackBase::STOPPED;
// active tracks are removed by threadLoop()
- if (mActiveTrack != track) {
+ if (mActiveTracks.indexOf(track) < 0) {
removeTrack_l(track);
}
}
@@ -4963,7 +5085,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& a
snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
result.append(buffer);
- if (mActiveTrack != 0) {
+ if (mActiveTracks.size() > 0) {
snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
result.append(buffer);
snprintf(buffer, SIZE, "Buffer size: %u bytes\n", mBufferSize);
@@ -5000,12 +5122,16 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args
}
}
- if (mActiveTrack != 0) {
+ size_t size = mActiveTracks.size();
+ if (size > 0) {
snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
result.append(buffer);
RecordTrack::appendDumpHeader(result);
- mActiveTrack->dump(buffer, SIZE);
- result.append(buffer);
+ for (size_t i = 0; i < size; ++i) {
+ sp<RecordTrack> track = mActiveTracks[i];
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
}
write(fd, result.string(), result.size());
@@ -5014,46 +5140,47 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
{
- size_t framesReq = buffer->frameCount;
- size_t framesReady = mFrameCount - mRsmpInIndex;
- int channelCount;
-
- if (framesReady == 0) {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize);
- if (mBytesRead <= 0) {
- if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
- ALOGE("RecordThread::getNextBuffer() Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- usleep(kRecordThreadSleepUs);
- }
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
- }
- mRsmpInIndex = 0;
- framesReady = mFrameCount;
- }
-
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
-
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
- buffer->frameCount = framesReq;
+ int32_t rear = mRsmpInRear;
+ int32_t front = mRsmpInFront;
+ ssize_t filled = rear - front;
+ ALOG_ASSERT(0 <= filled && (size_t) filled <= mRsmpInFramesP2);
+ // 'filled' may be non-contiguous, so return only the first contiguous chunk
+ front &= mRsmpInFramesP2 - 1;
+ size_t part1 = mRsmpInFramesP2 - front;
+ if (part1 > (size_t) filled) {
+ part1 = filled;
+ }
+ size_t ask = buffer->frameCount;
+ ALOG_ASSERT(ask > 0);
+ if (part1 > ask) {
+ part1 = ask;
+ }
+ if (part1 == 0) {
+ // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
+ ALOGE("RecordThread::getNextBuffer() starved");
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ mRsmpInUnrel = 0;
+ return NOT_ENOUGH_DATA;
+ }
+
+ buffer->raw = mRsmpInBuffer + front * mChannelCount;
+ buffer->frameCount = part1;
+ mRsmpInUnrel = part1;
return NO_ERROR;
}
// AudioBufferProvider interface
void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
- mRsmpInIndex += buffer->frameCount;
+ size_t stepCount = buffer->frameCount;
+ if (stepCount == 0) {
+ return;
+ }
+ ALOG_ASSERT(stepCount <= mRsmpInUnrel);
+ mRsmpInUnrel -= stepCount;
+ mRsmpInFront += stepCount;
+ buffer->raw = NULL;
buffer->frameCount = 0;
}
@@ -5068,7 +5195,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
int value;
audio_format_t reqFormat = mFormat;
uint32_t reqSamplingRate = mReqSampleRate;
- uint32_t reqChannelCount = mReqChannelCount;
+ audio_channel_mask_t reqChannelMask = audio_channel_in_mask_from_count(mReqChannelCount);
if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
reqSamplingRate = value;
@@ -5083,14 +5210,19 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- reqChannelCount = popcount(value);
- reconfig = true;
+ audio_channel_mask_t mask = (audio_channel_mask_t) value;
+ if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
+ status = BAD_VALUE;
+ } else {
+ reqChannelMask = mask;
+ reconfig = true;
+ }
}
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
// do not accept frame count changes if tracks are open as the track buffer
// size depends on frame count and correct behavior would not be guaranteed
// if frame count is changed after track creation
- if (mActiveTrack != 0) {
+ if (mActiveTracks.size() > 0) {
status = INVALID_OPERATION;
} else {
reconfig = true;
@@ -5133,6 +5265,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
}
mAudioSource = (audio_source_t)value;
}
+
if (status == NO_ERROR) {
status = mInput->stream->common.set_parameters(&mInput->stream->common,
keyValuePair.string());
@@ -5149,7 +5282,8 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
<= (2 * reqSamplingRate)) &&
popcount(mInput->stream->common.get_channels(&mInput->stream->common))
<= FCC_2 &&
- (reqChannelCount <= FCC_2)) {
+ (reqChannelMask == AUDIO_CHANNEL_IN_MONO ||
+ reqChannelMask == AUDIO_CHANNEL_IN_STEREO)) {
status = NO_ERROR;
}
if (status == NO_ERROR) {
@@ -5185,7 +5319,7 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
- void *param2 = NULL;
+ const void *param2 = NULL;
switch (event) {
case AudioSystem::INPUT_OPENED:
@@ -5224,34 +5358,27 @@ void AudioFlinger::RecordThread::readInputParameters()
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mBufferSize / mFrameSize;
- mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
-
- if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2)
- {
- int channelCount;
- // optimization: if mono to mono, use the resampler in stereo to stereo mode to avoid
- // stereo to mono post process as the resampler always outputs stereo.
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
+ // With 3 HAL buffers, we can guarantee ability to down-sample the input by ratio of 2:1 to
+ // 1 full output buffer, regardless of the alignment of the available input.
+ mRsmpInFrames = mFrameCount * 3;
+ mRsmpInFramesP2 = roundup(mRsmpInFrames);
+ // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
+ mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount];
+ mRsmpInFront = 0;
+ mRsmpInRear = 0;
+ mRsmpInUnrel = 0;
+
+ if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2) {
+ mResampler = AudioResampler::create(16, (int) mChannelCount, mReqSampleRate);
mResampler->setSampleRate(mSampleRate);
mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ // resampler always outputs stereo
mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2];
-
- // optmization: if mono to mono, alter input frame count as if we were inputing
- // stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- mFrameCount >>= 1;
- }
-
}
mRsmpInIndex = mFrameCount;
}
-unsigned int AudioFlinger::RecordThread::getInputFramesLost()
+uint32_t AudioFlinger::RecordThread::getInputFramesLost()
{
Mutex::Autolock _l(mLock);
if (initCheck() != NO_ERROR) {