From fbae5dae5187aca9d974cbe15ec818e9c6f56705 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 21 May 2012 09:17:20 -0700 Subject: Keep a copy of most recent audio played Change-Id: I6b2f97881c39998a2fae9ab79d669af6c0a37e94 --- services/audioflinger/Android.mk | 4 ++ services/audioflinger/AudioFlinger.cpp | 73 ++++++++++++++++++++++++++++++++ services/audioflinger/AudioFlinger.h | 3 ++ services/audioflinger/FastMixer.cpp | 5 +++ services/audioflinger/FastMixerState.cpp | 3 +- services/audioflinger/FastMixerState.h | 1 + services/audioflinger/PipeReader.cpp | 4 +- 7 files changed, 90 insertions(+), 3 deletions(-) (limited to 'services') diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 0d7f06ad..4f59a8a5 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -85,4 +85,8 @@ LOCAL_CFLAGS += -DHAVE_REQUEST_PRIORITY -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE # uncomment for systrace # LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO +# uncomment for dumpsys to write most recent audio output to .wav file +# 47.5 seconds at 44.1 kHz, 8 megabytes +# LOCAL_CFLAGS += -DTEE_SINK_FRAMES=0x200000 + include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 339f6ea4..3c60e5a3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -79,6 +79,8 @@ #include "AudioStreamOutSink.h" #include "MonoPipe.h" #include "MonoPipeReader.h" +#include "Pipe.h" +#include "PipeReader.h" #include "SourceAudioBufferProvider.h" #ifdef HAVE_REQUEST_PRIORITY @@ -2217,6 +2219,20 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud ALOG_ASSERT(index == 0); mPipeSink = monoPipe; +#ifdef TEE_SINK_FRAMES + // create a Pipe to archive a copy of FastMixer's output for dumpsys + Pipe *teeSink = new Pipe(TEE_SINK_FRAMES, format); + numCounterOffers = 0; + index = teeSink->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mTeeSink = teeSink; + PipeReader *teeSource = new PipeReader(*teeSink); + numCounterOffers = 0; + index = teeSource->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mTeeSource = teeSource; +#endif + #ifdef SOAKER // create a soaker as workaround for governor issues mSoaker = new Soaker(); @@ -2245,6 +2261,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud state->mColdFutexAddr = &mFastMixerFutex; state->mColdGen++; state->mDumpState = &mFastMixerDumpState; + state->mTeeSink = mTeeSink.get(); sq->end(); sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED); @@ -3440,6 +3457,62 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector FastMixerDumpState copy = mFastMixerDumpState; copy.dump(fd); + // Write the tee output to a .wav file + NBAIO_Source *teeSource = mTeeSource.get(); + if (teeSource != NULL) { + char teePath[64]; + struct timeval tv; + gettimeofday(&tv, NULL); + struct tm tm; + localtime_r(&tv.tv_sec, &tm); + strftime(teePath, sizeof(teePath), "/data/misc/media/%T.wav", &tm); + int teeFd = open(teePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (teeFd >= 0) { + char wavHeader[44]; + memcpy(wavHeader, + "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0", + sizeof(wavHeader)); + NBAIO_Format format = teeSource->format(); + unsigned channelCount = Format_channelCount(format); + ALOG_ASSERT(channelCount <= FCC_2); + unsigned sampleRate = Format_sampleRate(format); + wavHeader[22] = channelCount; // number of channels + wavHeader[24] = sampleRate; // sample rate + wavHeader[25] = sampleRate >> 8; + wavHeader[32] = channelCount * 2; // block alignment + write(teeFd, wavHeader, sizeof(wavHeader)); + size_t total = 0; + bool firstRead = true; + for (;;) { +#define TEE_SINK_READ 1024 + short buffer[TEE_SINK_READ * FCC_2]; + size_t count = TEE_SINK_READ; + ssize_t actual = teeSource->read(buffer, count); + bool wasFirstRead = firstRead; + firstRead = false; + if (actual <= 0) { + if (actual == (ssize_t) OVERRUN && wasFirstRead) { + continue; + } + break; + } + ALOG_ASSERT(actual <= count); + write(teeFd, buffer, actual * channelCount * sizeof(short)); + total += actual; + } + lseek(teeFd, (off_t) 4, SEEK_SET); + uint32_t temp = 44 + total * channelCount * sizeof(short) - 8; + write(teeFd, &temp, sizeof(temp)); + lseek(teeFd, (off_t) 40, SEEK_SET); + temp = total * channelCount * sizeof(short); + write(teeFd, &temp, sizeof(temp)); + close(teeFd); + fdprintf(fd, "FastMixer tee copied to %s\n", teePath); + } else { + fdprintf(fd, "FastMixer unable to create tee %s: \n", strerror(errno)); + } + } + return NO_ERROR; } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index de3561d2..1ae54148 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -1111,6 +1111,9 @@ public: sp mPipeSink; // The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink sp mNormalSink; + // For dumpsys + sp mTeeSink; + sp mTeeSource; public: virtual bool hasFastMixer() const = 0; virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index df9ec8e2..52effb23 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -77,6 +77,7 @@ bool FastMixer::threadLoop() bool isWarm = false; // true means ready to mix, false means wait for warmup before mixing struct timespec measuredWarmupTs = {0, 0}; // how long did it take for warmup to complete uint32_t warmupCycles = 0; // counter of number of loop cycles required to warmup + NBAIO_Sink* teeSink = NULL; // if non-NULL, then duplicate write() to this non-blocking sink for (;;) { @@ -106,6 +107,7 @@ bool FastMixer::threadLoop() // As soon as possible of learning of a new dump area, start using it dumpState = next->mDumpState != NULL ? next->mDumpState : &dummyDumpState; + teeSink = next->mTeeSink; // We want to always have a valid reference to the previous (non-idle) state. // However, the state queue only guarantees access to current and previous states. @@ -398,6 +400,9 @@ bool FastMixer::threadLoop() memset(mixBuffer, 0, frameCount * 2 * sizeof(short)); mixBufferState = ZEROED; } + if (teeSink != NULL) { + (void) teeSink->write(mixBuffer, frameCount); + } // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, // but this code should be modified to handle both non-blocking and blocking sinks dumpState->mWriteSequence++; diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 139a1c89..6305a832 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -30,7 +30,8 @@ FastTrack::~FastTrack() FastMixerState::FastMixerState() : mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0), - mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), mDumpState(NULL) + mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), + mDumpState(NULL), mTeeSink(NULL) { } diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index ce0cdb55..bc69c9c4 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -76,6 +76,7 @@ struct FastMixerState { unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once // This might be a one-time configuration rather than per-state FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically + NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink }; // struct FastMixerState } // namespace android diff --git a/services/audioflinger/PipeReader.cpp b/services/audioflinger/PipeReader.cpp index 43bcb42f..df3ee04f 100644 --- a/services/audioflinger/PipeReader.cpp +++ b/services/audioflinger/PipeReader.cpp @@ -49,9 +49,9 @@ ssize_t PipeReader::availableToRead() // read() is not multi-thread safe w.r.t. itself, so no mutex or atomic op needed to read mFront size_t avail = rear - mFront; if (CC_UNLIKELY(avail > mPipe.mMaxFrames)) { - // Discard all but 3/4 of the most recent data in pipe to avoid another overrun immediately + // Discard 1/16 of the most recent data in pipe to avoid another overrun immediately int32_t oldFront = mFront; - mFront = rear - mPipe.mMaxFrames + (mPipe.mMaxFrames >> 2); + mFront = rear - mPipe.mMaxFrames + (mPipe.mMaxFrames >> 4); mFramesOverrun += (size_t) (mFront - oldFront); ++mOverruns; return OVERRUN; -- cgit v1.2.3