Loading services/audioflinger/Android.mk +4 −0 Original line number Diff line number Diff line Loading @@ -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) services/audioflinger/AudioFlinger.cpp +73 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -2217,6 +2219,20 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& 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(); Loading Loading @@ -2245,6 +2261,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud state->mColdFutexAddr = &mFastMixerFutex; state->mColdGen++; state->mDumpState = &mFastMixerDumpState; state->mTeeSink = mTeeSink.get(); sq->end(); sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED); Loading Loading @@ -3440,6 +3457,62 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> 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; } Loading services/audioflinger/AudioFlinger.h +3 −0 Original line number Diff line number Diff line Loading @@ -1111,6 +1111,9 @@ public: sp<NBAIO_Sink> mPipeSink; // The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink sp<NBAIO_Sink> mNormalSink; // For dumpsys sp<NBAIO_Sink> mTeeSink; sp<NBAIO_Source> mTeeSource; public: virtual bool hasFastMixer() const = 0; virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const Loading services/audioflinger/FastMixer.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -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 (;;) { Loading Loading @@ -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. Loading Loading @@ -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++; Loading services/audioflinger/FastMixerState.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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) { } Loading Loading
services/audioflinger/Android.mk +4 −0 Original line number Diff line number Diff line Loading @@ -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)
services/audioflinger/AudioFlinger.cpp +73 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -2217,6 +2219,20 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& 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(); Loading Loading @@ -2245,6 +2261,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud state->mColdFutexAddr = &mFastMixerFutex; state->mColdGen++; state->mDumpState = &mFastMixerDumpState; state->mTeeSink = mTeeSink.get(); sq->end(); sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED); Loading Loading @@ -3440,6 +3457,62 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> 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; } Loading
services/audioflinger/AudioFlinger.h +3 −0 Original line number Diff line number Diff line Loading @@ -1111,6 +1111,9 @@ public: sp<NBAIO_Sink> mPipeSink; // The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink sp<NBAIO_Sink> mNormalSink; // For dumpsys sp<NBAIO_Sink> mTeeSink; sp<NBAIO_Source> mTeeSource; public: virtual bool hasFastMixer() const = 0; virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const Loading
services/audioflinger/FastMixer.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -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 (;;) { Loading Loading @@ -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. Loading Loading @@ -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++; Loading
services/audioflinger/FastMixerState.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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) { } Loading