Merge "Camera2: Copy metadata buffers for streaming."
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 44d05cd..d3c69f4 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -52,6 +52,8 @@
// that the framework has stopped playing audio and we must start returning silence
#define MAX_STALL_TIME_MS 1000
+#define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone"
+
struct VisualizerContext {
const struct effect_interface_s *mItfe;
effect_config_t mConfig;
@@ -59,10 +61,10 @@
uint32_t mCaptureSize;
uint32_t mScalingMode;
uint8_t mState;
- uint8_t mCurrentBuf;
- uint8_t mLastBuf;
+ uint8_t mLastCaptureIdx;
+ uint32_t mLatency;
struct timespec mBufferUpdateTime;
- uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
+ uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
};
//
@@ -72,11 +74,10 @@
void Visualizer_reset(VisualizerContext *pContext)
{
pContext->mCaptureIdx = 0;
- pContext->mCurrentBuf = 0;
- pContext->mLastBuf = 1;
+ pContext->mLastCaptureIdx = 0;
pContext->mBufferUpdateTime.tv_sec = 0;
- memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
- memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
+ pContext->mLatency = 0;
+ memset(pContext->mCaptureBuf, 0x80, CAPTURE_BUF_SIZE);
}
//----------------------------------------------------------------------------
@@ -316,25 +317,25 @@
uint32_t captIdx;
uint32_t inIdx;
- uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
+ uint8_t *buf = pContext->mCaptureBuf;
for (inIdx = 0, captIdx = pContext->mCaptureIdx;
- inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
+ inIdx < inBuffer->frameCount;
inIdx++, captIdx++) {
+ if (captIdx >= CAPTURE_BUF_SIZE) {
+ // wrap around
+ captIdx = 0;
+ }
int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
smp = smp >> shift;
buf[captIdx] = ((uint8_t)smp)^0x80;
}
+
+ // XXX the following two should really be atomic, though it probably doesn't
+ // matter much for visualization purposes
pContext->mCaptureIdx = captIdx;
-
- // go to next buffer when buffer full
- if (pContext->mCaptureIdx == pContext->mCaptureSize) {
- pContext->mCurrentBuf ^= 1;
- pContext->mCaptureIdx = 0;
-
- // update last buffer update time stamp
- if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) {
- pContext->mBufferUpdateTime.tv_sec = 0;
- }
+ // update last buffer update time stamp
+ if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) {
+ pContext->mBufferUpdateTime.tv_sec = 0;
}
if (inBuffer->raw != outBuffer->raw) {
@@ -464,6 +465,10 @@
pContext->mScalingMode = *((uint32_t *)p->data + 1);
ALOGV("set mScalingMode = %d", pContext->mScalingMode);
break;
+ case VISUALIZER_PARAM_LATENCY:
+ pContext->mLatency = *((uint32_t *)p->data + 1);
+ ALOGV("set mLatency = %d", pContext->mLatency);
+ break;
default:
*(int32_t *)pReplyData = -EINVAL;
}
@@ -481,13 +486,9 @@
return -EINVAL;
}
if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
- memcpy(pReplyData,
- pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
- pContext->mCaptureSize);
- // if audio framework has stopped playing audio although the effect is still
- // active we must clear the capture buffer to return silence
- if ((pContext->mLastBuf == pContext->mCurrentBuf) &&
- (pContext->mBufferUpdateTime.tv_sec != 0)) {
+ int32_t latencyMs = pContext->mLatency;
+ uint32_t deltaMs = 0;
+ if (pContext->mBufferUpdateTime.tv_sec != 0) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec;
@@ -496,17 +497,45 @@
--secs;
nsec += 1000000000;
}
- uint32_t deltaMs = secs * 1000 + nsec / 1000000;
- if (deltaMs > MAX_STALL_TIME_MS) {
- ALOGV("capture going to idle");
- pContext->mBufferUpdateTime.tv_sec = 0;
- memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
- 0x80,
- pContext->mCaptureSize);
+ deltaMs = secs * 1000 + nsec / 1000000;
+ latencyMs -= deltaMs;
+ if (latencyMs < 0) {
+ latencyMs = 0;
}
}
}
- pContext->mLastBuf = pContext->mCurrentBuf;
+ uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
+
+ int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl;
+ int32_t captureSize = pContext->mCaptureSize;
+ if (capturePoint < 0) {
+ int32_t size = -capturePoint;
+ if (size > captureSize) {
+ size = captureSize;
+ }
+ memcpy(pReplyData,
+ pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
+ size);
+ pReplyData += size;
+ captureSize -= size;
+ capturePoint = 0;
+ }
+ memcpy(pReplyData,
+ pContext->mCaptureBuf + capturePoint,
+ captureSize);
+
+
+ // if audio framework has stopped playing audio although the effect is still
+ // active we must clear the capture buffer to return silence
+ if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) &&
+ (pContext->mBufferUpdateTime.tv_sec != 0)) {
+ if (deltaMs > MAX_STALL_TIME_MS) {
+ ALOGV("capture going to idle");
+ pContext->mBufferUpdateTime.tv_sec = 0;
+ memset(pReplyData, 0x80, pContext->mCaptureSize);
+ }
+ }
+ pContext->mLastCaptureIdx = pContext->mCaptureIdx;
} else {
memset(pReplyData, 0x80, pContext->mCaptureSize);
}
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 4f59a8a..14b7fc1 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -47,6 +47,9 @@
LOCAL_SRC_FILES += StateQueue.cpp
+# uncomment for debugging timing problems related to StateQueue::push()
+LOCAL_CFLAGS += -DSTATE_QUEUE_DUMP
+
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d9c5d55..d944d32 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1605,6 +1605,7 @@
snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer);
result.append(buffer);
write(fd, result.string(), result.size());
+ fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask);
dumpBase(fd, args);
@@ -1817,6 +1818,10 @@
uint32_t AudioFlinger::PlaybackThread::latency() const
{
Mutex::Autolock _l(mLock);
+ return latency_l();
+}
+uint32_t AudioFlinger::PlaybackThread::latency_l() const
+{
if (initCheck() == NO_ERROR) {
return correctLatency(mOutput->stream->get_latency(mOutput->stream));
} else {
@@ -2012,11 +2017,9 @@
mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount, mNormalFrameCount);
- // FIXME - Current mixer implementation only supports stereo output: Always
- // Allocate a stereo buffer even if HW output is mono.
delete[] mMixBuffer;
- mMixBuffer = new int16_t[mNormalFrameCount * 2];
- memset(mMixBuffer, 0, mNormalFrameCount * 2 * sizeof(int16_t));
+ mMixBuffer = new int16_t[mNormalFrameCount * mChannelCount];
+ memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
@@ -2113,14 +2116,7 @@
uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs() const
{
- // A2DP output latency is not due only to buffering capacity. It also reflects encoding,
- // decoding and transfer time. So sleeping for half of the latency would likely cause
- // underruns
- if (audio_is_a2dp_device((audio_devices_t)mDevice)) {
- return (uint32_t)((uint32_t)((mNormalFrameCount * 1000) / mSampleRate) * 1000);
- } else {
- return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2;
- }
+ return (uint32_t)((uint32_t)((mNormalFrameCount * 1000) / mSampleRate) * 1000);
}
status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event)
@@ -2254,6 +2250,10 @@
// create fast mixer and configure it initially with just one fast track for our submix
mFastMixer = new FastMixer();
FastMixerStateQueue *sq = mFastMixer->sq();
+#ifdef STATE_QUEUE_DUMP
+ sq->setObserverDump(&mStateQueueObserverDump);
+ sq->setMutatorDump(&mStateQueueMutatorDump);
+#endif
FastMixerState *state = sq->begin();
FastTrack *fastTrack = &state->mFastTracks[0];
// wrap the source side of the MonoPipe to make it an AudioBufferProvider
@@ -2677,21 +2677,31 @@
// FIXME rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
+ int bytesWritten;
+ // If an NBAIO sink is present, use it to write the normal mixer's submix
+ if (mNormalSink != 0) {
#define mBitShift 2 // FIXME
- size_t count = mixBufferSize >> mBitShift;
+ size_t count = mixBufferSize >> mBitShift;
#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- Tracer::traceBegin(ATRACE_TAG, "write");
+ Tracer::traceBegin(ATRACE_TAG, "write");
#endif
- ssize_t framesWritten = mNormalSink->write(mMixBuffer, count);
+ ssize_t framesWritten = mNormalSink->write(mMixBuffer, count);
#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- Tracer::traceEnd(ATRACE_TAG);
+ Tracer::traceEnd(ATRACE_TAG);
#endif
- if (framesWritten > 0) {
- size_t bytesWritten = framesWritten << mBitShift;
- mBytesWritten += bytesWritten;
+ if (framesWritten > 0) {
+ bytesWritten = framesWritten << mBitShift;
+ } else {
+ bytesWritten = framesWritten;
+ }
+ // otherwise use the HAL / AudioStreamOut directly
+ } else {
+ // Direct output thread.
+ bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
}
+ if (bytesWritten > 0) mBytesWritten += mixBufferSize;
mNumWrites++;
mInWrite = false;
}
@@ -2841,11 +2851,14 @@
// at the identical fast mixer slot within the same normal mix cycle,
// is impossible because the slot isn't marked available until the end of each cycle.
int j = track->mFastIndex;
+ ALOG_ASSERT(0 < j && j < (int)FastMixerState::kMaxFastTracks);
+ ALOG_ASSERT(!(mFastTrackAvailMask & (1 << j)));
FastTrack *fastTrack = &state->mFastTracks[j];
// Determine whether the track is currently in underrun condition,
// and whether it had a recent underrun.
- FastTrackUnderruns underruns = mFastMixerDumpState.mTracks[j].mUnderruns;
+ FastTrackDump *ftDump = &mFastMixerDumpState.mTracks[j];
+ FastTrackUnderruns underruns = ftDump->mUnderruns;
uint32_t recentFull = (underruns.mBitFields.mFull -
track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;
uint32_t recentPartial = (underruns.mBitFields.mPartial -
@@ -3172,6 +3185,7 @@
tracksToRemove->add(track);
}
} else {
+ track->mUnderrunCount++;
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
if (--(track->mRetryCount) <= 0) {
@@ -3475,6 +3489,14 @@
FastMixerDumpState copy = mFastMixerDumpState;
copy.dump(fd);
+#ifdef STATE_QUEUE_DUMP
+ // Similar for state queue
+ StateQueueObserverDump observerCopy = mStateQueueObserverDump;
+ observerCopy.dump(fd);
+ StateQueueMutatorDump mutatorCopy = mStateQueueMutatorDump;
+ mutatorCopy.dump(fd);
+#endif
+
// Write the tee output to a .wav file
NBAIO_Source *teeSource = mTeeSource.get();
if (teeSource != NULL) {
@@ -3560,7 +3582,6 @@
AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
: PlaybackThread(audioFlinger, output, id, device, DIRECT)
// mLeftVolFloat, mRightVolFloat
- // mLeftVolShort, mRightVolShort
{
}
@@ -3587,7 +3608,13 @@
// The first time a track is added we wait
// for all its buffers to be filled before processing it
- if (cblk->framesReady() && track->isReady() &&
+ uint32_t minFrames;
+ if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing()) {
+ minFrames = mNormalFrameCount;
+ } else {
+ minFrames = 1;
+ }
+ if ((track->framesReady() >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
//ALOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
@@ -3595,16 +3622,11 @@
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
mLeftVolFloat = mRightVolFloat = 0;
- mLeftVolShort = mRightVolShort = 0;
if (track->mState == TrackBase::RESUMING) {
track->mState = TrackBase::ACTIVE;
- rampVolume = true;
}
- } else if (cblk->server != 0) {
- // If the track is stopped before the first frame was mixed,
- // do not apply ramp
- rampVolume = true;
}
+
// compute volume for this track
float left, right;
if (track->isMuted() || mMasterMute || track->isPausing() ||
@@ -3629,13 +3651,6 @@
mLeftVolFloat = left;
mRightVolFloat = right;
- // If audio HAL implements volume control,
- // force software volume to nominal value
- if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
- left = 1.0f;
- right = 1.0f;
- }
-
// Convert volumes from float to 8.24
uint32_t vl = (uint32_t)(left * (1 << 24));
uint32_t vr = (uint32_t)(right * (1 << 24));
@@ -3645,22 +3660,11 @@
// there is one, the track is connected to it
if (!mEffectChains.isEmpty()) {
// Do not ramp volume if volume is controlled by effect
- if (mEffectChains[0]->setVolume_l(&vl, &vr)) {
- rampVolume = false;
- }
+ mEffectChains[0]->setVolume_l(&vl, &vr);
+ left = (float)vl / (1 << 24);
+ right = (float)vr / (1 << 24);
}
-
- // Convert volumes from 8.24 to 4.12 format
- uint32_t v_clamped = (vl + (1 << 11)) >> 12;
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- leftVol = (uint16_t)v_clamped;
- v_clamped = (vr + (1 << 11)) >> 12;
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- rightVol = (uint16_t)v_clamped;
- } else {
- leftVol = mLeftVolShort;
- rightVol = mRightVolShort;
- rampVolume = false;
+ mOutput->stream->set_volume(mOutput->stream, left, right);
}
// reset retry count
@@ -3675,7 +3679,8 @@
}
//ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
- if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+ if ((track->sharedBuffer() != 0) || track->isTerminated() ||
+ track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
// TODO: implement behavior for compressed audio
@@ -3742,78 +3747,6 @@
standbyTime = systemTime() + standbyDelay;
mActiveTrack.clear();
- // apply volume
-
- // Do not apply volume on compressed audio
- if (!audio_is_linear_pcm(mFormat)) {
- return;
- }
-
- // convert to signed 16 bit before volume calculation
- if (mFormat == AUDIO_FORMAT_PCM_8_BIT) {
- size_t count = mFrameCount * mChannelCount;
- uint8_t *src = (uint8_t *)mMixBuffer + count-1;
- int16_t *dst = mMixBuffer + count-1;
- while (count--) {
- *dst-- = (int16_t)(*src--^0x80) << 8;
- }
- }
-
- frameCount = mFrameCount;
- int16_t *out = mMixBuffer;
- if (rampVolume) {
- if (mChannelCount == 1) {
- int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
- int32_t vlInc = d / (int32_t)frameCount;
- int32_t vl = ((int32_t)mLeftVolShort << 16);
- do {
- out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
- out++;
- vl += vlInc;
- } while (--frameCount);
-
- } else {
- int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
- int32_t vlInc = d / (int32_t)frameCount;
- d = ((int32_t)rightVol - (int32_t)mRightVolShort) << 16;
- int32_t vrInc = d / (int32_t)frameCount;
- int32_t vl = ((int32_t)mLeftVolShort << 16);
- int32_t vr = ((int32_t)mRightVolShort << 16);
- do {
- out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
- out[1] = clamp16(mul(out[1], vr >> 16) >> 12);
- out += 2;
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
- }
- } else {
- if (mChannelCount == 1) {
- do {
- out[0] = clamp16(mul(out[0], leftVol) >> 12);
- out++;
- } while (--frameCount);
- } else {
- do {
- out[0] = clamp16(mul(out[0], leftVol) >> 12);
- out[1] = clamp16(mul(out[1], rightVol) >> 12);
- out += 2;
- } while (--frameCount);
- }
- }
-
- // convert back to unsigned 8 bit after volume calculation
- if (mFormat == AUDIO_FORMAT_PCM_8_BIT) {
- size_t count = mFrameCount * mChannelCount;
- int16_t *src = mMixBuffer;
- uint8_t *dst = (uint8_t *)mMixBuffer;
- while (count--) {
- *dst++ = (uint8_t)(((int32_t)*src++ + (1<<7)) >> 8)^0x80;
- }
- }
-
- mLeftVolShort = leftVol;
- mRightVolShort = rightVol;
}
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
@@ -3958,6 +3891,7 @@
}
sleepTime = 0;
writeFrames = mNormalFrameCount;
+ standbyTime = systemTime() + standbyDelay;
}
void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
@@ -3969,21 +3903,19 @@
sleepTime = idleSleepTime;
}
} else if (mBytesWritten != 0) {
- // flush remaining overflow buffers in output tracks
- for (size_t i = 0; i < outputTracks.size(); i++) {
- if (outputTracks[i]->isActive()) {
- sleepTime = 0;
- writeFrames = 0;
- memset(mMixBuffer, 0, mixBufferSize);
- break;
- }
+ if (mMixerStatus == MIXER_TRACKS_ENABLED) {
+ writeFrames = mNormalFrameCount;
+ memset(mMixBuffer, 0, mixBufferSize);
+ } else {
+ // flush remaining overflow buffers in output tracks
+ writeFrames = 0;
}
+ sleepTime = 0;
}
}
void AudioFlinger::DuplicatingThread::threadLoop_write()
{
- standbyTime = systemTime() + standbyDelay;
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->write(mMixBuffer, writeFrames);
}
@@ -4295,6 +4227,13 @@
// NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t);
+ // to avoid leaking a track name, do not allocate one unless there is an mCblk
+ mName = thread->getTrackName_l((audio_channel_mask_t)channelMask);
+ if (mName < 0) {
+ ALOGE("no more track names available");
+ return;
+ }
+ // only allocate a fast track index if we were able to allocate a normal track name
if (flags & IAudioFlinger::TRACK_FAST) {
mCblk->flags |= CBLK_FAST; // atomic op not needed yet
ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
@@ -4309,14 +4248,6 @@
mObservedUnderruns = thread->getFastTrackUnderruns(i);
thread->mFastTrackAvailMask &= ~(1 << i);
}
- // to avoid leaking a track name, do not allocate one unless there is an mCblk
- mName = thread->getTrackName_l((audio_channel_mask_t)channelMask);
- if (mName < 0) {
- ALOGE("no more track names available");
- // FIXME bug - if sufficient fast track indices, but insufficient normal mixer names,
- // then we leak a fast track index. Should swap these two sections, or better yet
- // only allocate a normal mixer name for normal tracks.
- }
}
ALOGV("Track constructor name %d, calling pid %d", mName, IPCThreadState::self()->getCallingPid());
}
@@ -4366,7 +4297,7 @@
/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
{
result.append(" Name Client Type Fmt Chn mask Session mFrCnt fCount S M F SRate L dB R dB "
- " Server User Main buf Aux Buf Flags FastUnder\n");
+ " Server User Main buf Aux Buf Flags Underruns\n");
}
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
@@ -8259,6 +8190,31 @@
status = cmdStatus;
}
+ if (status == 0 &&
+ (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0)) {
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ size = sizeof(int);
+ *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
+
+ uint32_t latency = 0;
+ PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
+ if (pbt != NULL) {
+ latency = pbt->latency_l();
+ }
+
+ *((int32_t *)p->data + 1)= latency;
+ (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_PARAM,
+ sizeof(effect_param_t) + 8,
+ &buf32,
+ &size,
+ &cmdStatus);
+ }
+
mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
(1000 * mConfig.outputCfg.buffer.frameCount);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 19390b1..1c44f2f 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -778,6 +778,7 @@
int mName; // track name on the normal mixer,
// allocated statically at track creation time,
// and is even allocated (though unused) for fast tracks
+ // FIXME don't allocate track name for fast tracks
int16_t *mMainBuffer;
int32_t *mAuxBuffer;
int mAuxEffectId;
@@ -789,7 +790,7 @@
// The following fields are only for fast tracks, and should be in a subclass
int mFastIndex; // index within FastMixerState::mFastTracks[];
- // either mFastIndex == -1
+ // either mFastIndex == -1 if not isFastTrack()
// or 0 < mFastIndex < FastMixerState::kMaxFast because
// index 0 is reserved for normal mixer's submix;
// index is allocated statically at track creation time
@@ -964,6 +965,8 @@
// return estimated latency in milliseconds, as reported by HAL
uint32_t latency() const;
+ // same, but lock must already be held
+ uint32_t latency_l() const;
void setMasterVolume(float value);
void setMasterMute(bool muted);
@@ -1168,6 +1171,10 @@
// contents are not guaranteed to be consistent, no locks required
FastMixerDumpState mFastMixerDumpState;
+#ifdef STATE_QUEUE_DUMP
+ StateQueueObserverDump mStateQueueObserverDump;
+ StateQueueMutatorDump mStateQueueMutatorDump;
+#endif
// accessible only within the threadLoop(), no locks required
// mFastMixer->sq() // for mutating and pushing state
@@ -1206,17 +1213,8 @@
virtual void threadLoop_sleepTime();
// volumes last sent to audio HAL with stream->set_volume()
- // FIXME use standard representation and names
float mLeftVolFloat;
float mRightVolFloat;
- uint16_t mLeftVolShort;
- uint16_t mRightVolShort;
-
- // FIXME rename these former local variables of threadLoop to standard names
- // next 3 were local to the while !exitingPending loop
- bool rampVolume;
- uint16_t leftVol;
- uint16_t rightVol;
private:
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index ca25ba9..0d13970 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -1375,7 +1375,6 @@
ALOGW("%s: could not get AudioFlinger", __func__);
return 0;
}
- ALOGW("%s: %d", __func__, module);
return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask,
pLatencyMs, flags);
}
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 52effb2..3bb7b44 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -161,6 +161,7 @@
coldGen = current->mColdGen;
bounds = 0;
full = false;
+ oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs);
} else {
sleepNs = FAST_HOT_IDLE_NS;
}
@@ -238,6 +239,7 @@
// check for change in active track set
unsigned currentTrackMask = current->mTrackMask;
+ dumpState->mTrackMask = currentTrackMask;
if (current->mFastTracksGen != fastTracksGen) {
ALOG_ASSERT(mixBuffer != NULL);
int name;
@@ -386,6 +388,7 @@
mixer->enable(name);
}
ftDump->mUnderruns = underruns;
+ ftDump->mFramesReady = framesReady;
}
// process() is CPU-bound
mixer->process(AudioBufferProvider::kInvalidPTS);
@@ -480,7 +483,10 @@
(int) sec, nsec / 1000000L);
dumpState->mOverruns++;
}
- sleepNs = periodNs - overrunNs;
+ // Code for non blocking audio HAL. Sleep time must be tuned to allow
+ // catching up after an underrun
+ // sleepNs = periodNs - overrunNs;
+ sleepNs = -1;
} else {
sleepNs = -1;
ignoreNextOverrun = false;
@@ -561,7 +567,8 @@
FastMixerDumpState::FastMixerDumpState() :
mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0),
mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0),
- mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0)
+ mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0),
+ mTrackMask(0)
#ifdef FAST_MIXER_STATISTICS
, mBounds(0)
#endif
@@ -670,6 +677,40 @@
" mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
#endif
+ // The active track mask and track states are updated non-atomically.
+ // So if we relied on isActive to decide whether to display,
+ // then we might display an obsolete track or omit an active track.
+ // Instead we always display all tracks, with an indication
+ // of whether we think the track is active.
+ uint32_t trackMask = mTrackMask;
+ fdprintf(fd, "Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
+ FastMixerState::kMaxFastTracks, trackMask);
+ fdprintf(fd, "Index Active Full Partial Empty Recent Ready\n");
+ for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) {
+ bool isActive = trackMask & 1;
+ const FastTrackDump *ftDump = &mTracks[i];
+ const FastTrackUnderruns& underruns = ftDump->mUnderruns;
+ const char *mostRecent;
+ switch (underruns.mBitFields.mMostRecent) {
+ case UNDERRUN_FULL:
+ mostRecent = "full";
+ break;
+ case UNDERRUN_PARTIAL:
+ mostRecent = "partial";
+ break;
+ case UNDERRUN_EMPTY:
+ mostRecent = "empty";
+ break;
+ default:
+ mostRecent = "?";
+ break;
+ }
+ fdprintf(fd, "%5u %6s %4u %7u %5u %7s %5u\n", i, isActive ? "yes" : "no",
+ (underruns.mBitFields.mFull) & UNDERRUN_MASK,
+ (underruns.mBitFields.mPartial) & UNDERRUN_MASK,
+ (underruns.mBitFields.mEmpty) & UNDERRUN_MASK,
+ mostRecent, ftDump->mFramesReady);
+ }
}
} // namespace android
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index e95abf6..06e76d5 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -72,9 +72,10 @@
// Represents the dump state of a fast track
struct FastTrackDump {
- FastTrackDump() { }
+ FastTrackDump() : mFramesReady(0) { }
/*virtual*/ ~FastTrackDump() { }
FastTrackUnderruns mUnderruns;
+ size_t mFramesReady; // most recent value only; no long-term statistics kept
};
// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys.
@@ -100,6 +101,7 @@
size_t mFrameCount;
struct timespec mMeasuredWarmupTs; // measured warmup time
uint32_t mWarmupCycles; // number of loop cycles required to warmup
+ uint32_t mTrackMask; // mask of active tracks
FastTrackDump mTracks[FastMixerState::kMaxFastTracks];
#ifdef FAST_MIXER_STATISTICS
diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp
index fd16e92..6efb8b1 100644
--- a/services/audioflinger/MonoPipe.cpp
+++ b/services/audioflinger/MonoPipe.cpp
@@ -25,9 +25,10 @@
namespace android {
-MonoPipe::MonoPipe(size_t maxFrames, NBAIO_Format format, bool writeCanBlock) :
+MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) :
NBAIO_Sink(format),
- mMaxFrames(roundup(maxFrames)),
+ mReqFrames(reqFrames),
+ mMaxFrames(roundup(reqFrames)),
mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
mFront(0),
mRear(0),
@@ -45,6 +46,7 @@
if (CC_UNLIKELY(!mNegotiated)) {
return NEGOTIATE;
}
+ // uses mMaxFrames not mReqFrames, so allows "over-filling" the pipe beyond requested limit
ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront));
ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
return ret;
@@ -57,6 +59,7 @@
}
size_t totalFramesWritten = 0;
while (count > 0) {
+ // can't return a negative value, as we already checked for !mNegotiated
size_t avail = availableToWrite();
size_t written = avail;
if (CC_LIKELY(written > count)) {
@@ -84,50 +87,29 @@
count -= written;
buffer = (char *) buffer + (written << mBitShift);
// Simulate blocking I/O by sleeping at different rates, depending on a throttle.
- // The throttle tries to keep the pipe about 5/8 full on average, with a slight jitter.
- uint64_t ns;
- enum {
- THROTTLE_VERY_FAST, // pipe is (nearly) empty, fill quickly
- THROTTLE_FAST, // pipe is normal, fill at slightly faster rate
- THROTTLE_NOMINAL, // pipe is normal, fill at nominal rate
- THROTTLE_SLOW, // pipe is normal, fill at slightly slower rate
- THROTTLE_VERY_SLOW, // pipe is (nearly) full, fill slowly
- } throttle;
- avail -= written;
- // FIXME cache these values to avoid re-computation
- if (avail >= (mMaxFrames * 3) / 4) {
- throttle = THROTTLE_VERY_FAST;
- } else if (avail >= mMaxFrames / 2) {
- throttle = THROTTLE_FAST;
- } else if (avail >= (mMaxFrames * 3) / 8) {
- throttle = THROTTLE_NOMINAL;
- } else if (avail >= mMaxFrames / 4) {
- throttle = THROTTLE_SLOW;
- } else {
- throttle = THROTTLE_VERY_SLOW;
- }
+ // The throttle tries to keep the pipe about 11/16 full on average, with a slight jitter.
+ uint32_t ns;
if (written > 0) {
- // FIXME cache these values also
- switch (throttle) {
- case THROTTLE_VERY_FAST:
- default:
+ size_t filled = (mMaxFrames - avail) + written;
+ // FIXME cache these values to avoid re-computation
+ if (filled <= mReqFrames / 4) {
+ // pipe is (nearly) empty, fill quickly
ns = written * ( 500000000 / Format_sampleRate(mFormat));
- break;
- case THROTTLE_FAST:
+ } else if (filled <= mReqFrames / 2) {
+ // pipe is normal, fill at slightly faster rate
ns = written * ( 750000000 / Format_sampleRate(mFormat));
- break;
- case THROTTLE_NOMINAL:
+ } else if (filled <= (mReqFrames * 5) / 8) {
+ // pipe is normal, fill at nominal rate
ns = written * (1000000000 / Format_sampleRate(mFormat));
- break;
- case THROTTLE_SLOW:
+ } else if (filled <= (mReqFrames * 3) / 4) {
+ // pipe is normal, fill at slightly slower rate
ns = written * (1100000000 / Format_sampleRate(mFormat));
- break;
- case THROTTLE_VERY_SLOW:
+ } else {
+ // pipe is (nearly) full, fill slowly
ns = written * (1250000000 / Format_sampleRate(mFormat));
- break;
}
} else {
- ns = mMaxFrames * (250000000 / Format_sampleRate(mFormat));
+ ns = mReqFrames * (250000000 / Format_sampleRate(mFormat));
}
if (ns > 999999999) {
ns = 999999999;
diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h
index 545d6ac..aaaa51f 100644
--- a/services/audioflinger/MonoPipe.h
+++ b/services/audioflinger/MonoPipe.h
@@ -33,11 +33,11 @@
friend class MonoPipeReader;
public:
- // maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
+ // reqFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
// Note: whatever shares this object with another thread needs to do so in an SMP-safe way (like
// creating it the object before creating the other thread, or storing the object with a
// release_store). Otherwise the other thread could see a partially-constructed object.
- MonoPipe(size_t maxFrames, NBAIO_Format format, bool writeCanBlock = false);
+ MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock = false);
virtual ~MonoPipe();
// NBAIO_Port interface
@@ -58,9 +58,10 @@
// average number of frames present in the pipe under normal conditions.
// See throttling mechanism in MonoPipe::write()
- size_t getAvgFrames() const { return (mMaxFrames * 11) / 16; }
+ size_t getAvgFrames() const { return (mReqFrames * 11) / 16; }
private:
+ const size_t mReqFrames; // as requested in constructor, unrounded
const size_t mMaxFrames; // always a power of 2
void * const mBuffer;
// mFront and mRear will never be separated by more than mMaxFrames.
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index ae263f5..3e891a5 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -24,12 +24,28 @@
namespace android {
+#ifdef STATE_QUEUE_DUMP
+void StateQueueObserverDump::dump(int fd)
+{
+ fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
+}
+
+void StateQueueMutatorDump::dump(int fd)
+{
+ fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
+ mPushDirty, mPushAck, mBlockedSequence);
+}
+#endif
+
// Constructor and destructor
template<typename T> StateQueue<T>::StateQueue() :
mNext(NULL), mAck(NULL), mCurrent(NULL),
mMutating(&mStates[0]), mExpecting(NULL),
mInMutation(false), mIsDirty(false), mIsInitialized(false)
+#ifdef STATE_QUEUE_DUMP
+ , mObserverDump(&mObserverDummyDump), mMutatorDump(&mMutatorDummyDump)
+#endif
{
}
@@ -45,6 +61,9 @@
if (next != mCurrent) {
mAck = next; // no additional barrier needed
mCurrent = next;
+#ifdef STATE_QUEUE_DUMP
+ mObserverDump->mStateChanges++;
+#endif
}
return next;
}
@@ -77,10 +96,23 @@
ALOG_ASSERT(!mInMutation, "push() called when in a mutation");
+#ifdef STATE_QUEUE_DUMP
+ if (block == BLOCK_UNTIL_ACKED) {
+ mMutatorDump->mPushAck++;
+ }
+#endif
+
if (mIsDirty) {
+#ifdef STATE_QUEUE_DUMP
+ mMutatorDump->mPushDirty++;
+#endif
+
// wait for prior push to be acknowledged
if (mExpecting != NULL) {
+#ifdef STATE_QUEUE_DUMP
+ unsigned count = 0;
+#endif
for (;;) {
const T *ack = (const T *) mAck; // no additional barrier needed
if (ack == mExpecting) {
@@ -91,8 +123,19 @@
if (block == BLOCK_NEVER) {
return false;
}
+#ifdef STATE_QUEUE_DUMP
+ if (count == 1) {
+ mMutatorDump->mBlockedSequence++;
+ }
+ ++count;
+#endif
nanosleep(&req, NULL);
}
+#ifdef STATE_QUEUE_DUMP
+ if (count > 1) {
+ mMutatorDump->mBlockedSequence++;
+ }
+#endif
}
// publish
@@ -111,14 +154,28 @@
// optionally wait for this push or a prior push to be acknowledged
if (block == BLOCK_UNTIL_ACKED) {
if (mExpecting != NULL) {
+#ifdef STATE_QUEUE_DUMP
+ unsigned count = 0;
+#endif
for (;;) {
const T *ack = (const T *) mAck; // no additional barrier needed
if (ack == mExpecting) {
mExpecting = NULL;
break;
}
+#ifdef STATE_QUEUE_DUMP
+ if (count == 1) {
+ mMutatorDump->mBlockedSequence++;
+ }
+ ++count;
+#endif
nanosleep(&req, NULL);
}
+#ifdef STATE_QUEUE_DUMP
+ if (count > 1) {
+ mMutatorDump->mBlockedSequence++;
+ }
+#endif
}
}
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
index fe72ddc..eba190c 100644
--- a/services/audioflinger/StateQueue.h
+++ b/services/audioflinger/StateQueue.h
@@ -19,6 +19,34 @@
namespace android {
+#ifdef STATE_QUEUE_DUMP
+// The StateQueueObserverDump and StateQueueMutatorDump keep
+// a cache of StateQueue statistics that can be logged by dumpsys.
+// Each individual native word-sized field is accessed atomically. But the
+// overall structure is non-atomic, that is there may be an inconsistency between fields.
+// No barriers or locks are used for either writing or reading.
+// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks).
+// It has a different lifetime than the StateQueue, and so it can't be a member of StateQueue.
+
+struct StateQueueObserverDump {
+ StateQueueObserverDump() : mStateChanges(0) { }
+ /*virtual*/ ~StateQueueObserverDump() { }
+ unsigned mStateChanges; // incremented each time poll() detects a state change
+ void dump(int fd);
+};
+
+struct StateQueueMutatorDump {
+ StateQueueMutatorDump() : mPushDirty(0), mPushAck(0), mBlockedSequence(0) { }
+ /*virtual*/ ~StateQueueMutatorDump() { }
+ unsigned mPushDirty; // incremented each time push() is called with a dirty state
+ unsigned mPushAck; // incremented each time push(BLOCK_UNTIL_ACKED) is called
+ unsigned mBlockedSequence; // incremented before and after each time that push()
+ // blocks for more than one PUSH_BLOCK_ACK_NS;
+ // if odd, then mutator is currently blocked inside push()
+ void dump(int fd);
+};
+#endif
+
// manages a FIFO queue of states
template<typename T> class StateQueue {
@@ -69,6 +97,16 @@
// Return whether the current state is dirty (modified and not pushed).
bool isDirty() const { return mIsDirty; }
+#ifdef STATE_QUEUE_DUMP
+ // Register location of observer dump area
+ void setObserverDump(StateQueueObserverDump *dump)
+ { mObserverDump = dump != NULL ? dump : &mObserverDummyDump; }
+
+ // Register location of mutator dump area
+ void setMutatorDump(StateQueueMutatorDump *dump)
+ { mMutatorDump = dump != NULL ? dump : &mMutatorDummyDump; }
+#endif
+
private:
static const unsigned kN = 4; // values != 4 are not supported by this code
T mStates[kN]; // written by mutator, read by observer
@@ -87,6 +125,13 @@
bool mIsDirty; // whether mutating state has been modified since last push
bool mIsInitialized; // whether mutating state has been initialized yet
+#ifdef STATE_QUEUE_DUMP
+ StateQueueObserverDump mObserverDummyDump; // default area for observer dump if not set
+ StateQueueObserverDump* mObserverDump; // pointer to active observer dump, always non-NULL
+ StateQueueMutatorDump mMutatorDummyDump; // default area for mutator dump if not set
+ StateQueueMutatorDump* mMutatorDump; // pointer to active mutator dump, always non-NULL
+#endif
+
}; // class StateQueue
} // namespace android