diff options
author | Glenn Kasten <gkasten@google.com> | 2013-01-10 12:31:01 -0800 |
---|---|---|
committer | Glenn Kasten <gkasten@google.com> | 2013-02-22 16:50:36 -0800 |
commit | da6ef1320d0161b1640dc84d7a9c5a25860c3619 (patch) | |
tree | 86b24f64ed0bd55de10deb193290009687886192 /services/audioflinger | |
parent | 99c2f923f6b04efffe949d1daf9cb7148e3cc201 (diff) | |
download | frameworks_av-da6ef1320d0161b1640dc84d7a9c5a25860c3619.zip frameworks_av-da6ef1320d0161b1640dc84d7a9c5a25860c3619.tar.gz frameworks_av-da6ef1320d0161b1640dc84d7a9c5a25860c3619.tar.bz2 |
Update tee sink
Implement rotation to reduce long-term storage use.
Implement optional per-track tee.
Dynamically enable at runtime based on property, instead of at compile-time.
Dynamic frame count not yet implemented.
Bug: 8223560
Change-Id: I3706443c6ec0cb0c6656dc288715a02ad5fea63a
Diffstat (limited to 'services/audioflinger')
-rw-r--r-- | services/audioflinger/Android.mk | 8 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 111 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 17 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 26 | ||||
-rw-r--r-- | services/audioflinger/TrackBase.h | 3 | ||||
-rw-r--r-- | services/audioflinger/Tracks.cpp | 31 |
6 files changed, 164 insertions, 32 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 6d42143..0855db6 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -66,14 +66,6 @@ LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE -# 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 - -# uncomment for dumpsys to write most recent audio input to .wav file -# 47.5 seconds at 44.1 kHz, 8 megabytes -# LOCAL_CFLAGS += -DTEE_SINK_INPUT_FRAMES=0x200000 - # uncomment to enable the audio watchdog # LOCAL_SRC_FILES += AudioWatchdog.cpp # LOCAL_CFLAGS += -DAUDIO_WATCHDOG diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 47c2772..e0ab8cd 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "AudioFlinger" //#define LOG_NDEBUG 0 +#include <dirent.h> #include <math.h> #include <signal.h> #include <sys/time.h> @@ -61,6 +62,9 @@ #include <media/IMediaLogService.h> +#include <media/nbaio/Pipe.h> +#include <media/nbaio/PipeReader.h> + // ---------------------------------------------------------------------------- // Note: the following macro is used for extremely verbose logging message. In @@ -86,6 +90,14 @@ nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs; uint32_t AudioFlinger::mScreenState; +bool AudioFlinger::mTeeSinkInputEnabled = false; +bool AudioFlinger::mTeeSinkOutputEnabled = false; +bool AudioFlinger::mTeeSinkTrackEnabled = false; + +size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault; +size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault; +size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault; + // ---------------------------------------------------------------------------- static int load_audio_interface(const char *if_name, audio_hw_device_t **dev) @@ -134,6 +146,19 @@ AudioFlinger::AudioFlinger() if (doLog) { mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters"); } + (void) property_get("ro.debuggable", value, "0"); + int debuggable = atoi(value); + int teeEnabled = 0; + if (debuggable) { + (void) property_get("af.tee", value, "0"); + teeEnabled = atoi(value); + } + if (teeEnabled & 1) + mTeeSinkInputEnabled = true; + if (teeEnabled & 2) + mTeeSinkOutputEnabled = true; + if (teeEnabled & 4) + mTeeSinkTrackEnabled = true; } void AudioFlinger::onFirstRef() @@ -1602,7 +1627,6 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, // Try to re-use most recently used Pipe to archive a copy of input for dumpsys, // or (re-)create if current Pipe is idle and does not match the new format sp<NBAIO_Sink> teeSink; -#ifdef TEE_SINK_INPUT_FRAMES enum { TEE_SINK_NO, // don't copy input TEE_SINK_NEW, // copy input using a new pipe @@ -1610,7 +1634,9 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, } kind; NBAIO_Format format = Format_from_SR_C(inStream->common.get_sample_rate(&inStream->common), popcount(inStream->common.get_channels(&inStream->common))); - if (format == Format_Invalid) { + if (!mTeeSinkInputEnabled) { + kind = TEE_SINK_NO; + } else if (format == Format_Invalid) { kind = TEE_SINK_NO; } else if (mRecordTeeSink == 0) { kind = TEE_SINK_NEW; @@ -1623,7 +1649,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, } switch (kind) { case TEE_SINK_NEW: { - Pipe *pipe = new Pipe(TEE_SINK_INPUT_FRAMES, format); + Pipe *pipe = new Pipe(mTeeSinkInputFrames, format); size_t numCounterOffers = 0; const NBAIO_Format offers[1] = {format}; ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); @@ -1644,7 +1670,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, default: break; } -#endif + AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream); // Start record thread @@ -2199,19 +2225,80 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId, return NO_ERROR; } +struct Entry { +#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav + char mName[MAX_NAME]; +}; + +int comparEntry(const void *p1, const void *p2) +{ + return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName); +} + void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id) { NBAIO_Source *teeSource = source.get(); if (teeSource != NULL) { + // .wav rotation + // There is a benign race condition if 2 threads call this simultaneously. + // They would both traverse the directory, but the result would simply be + // failures at unlink() which are ignored. It's also unlikely since + // normally dumpsys is only done by bugreport or from the command line. + char teePath[32+256]; + strcpy(teePath, "/data/misc/media"); + size_t teePathLen = strlen(teePath); + DIR *dir = opendir(teePath); + teePath[teePathLen++] = '/'; + if (dir != NULL) { +#define MAX_SORT 20 // number of entries to sort +#define MAX_KEEP 10 // number of entries to keep + struct Entry entries[MAX_SORT]; + size_t entryCount = 0; + while (entryCount < MAX_SORT) { + struct dirent de; + struct dirent *result = NULL; + int rc = readdir_r(dir, &de, &result); + if (rc != 0) { + ALOGW("readdir_r failed %d", rc); + break; + } + if (result == NULL) { + break; + } + if (result != &de) { + ALOGW("readdir_r returned unexpected result %p != %p", result, &de); + break; + } + // ignore non .wav file entries + size_t nameLen = strlen(de.d_name); + if (nameLen <= 4 || nameLen >= MAX_NAME || + strcmp(&de.d_name[nameLen - 4], ".wav")) { + continue; + } + strcpy(entries[entryCount++].mName, de.d_name); + } + (void) closedir(dir); + if (entryCount > MAX_KEEP) { + qsort(entries, entryCount, sizeof(Entry), comparEntry); + for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) { + strcpy(&teePath[teePathLen], entries[i].mName); + (void) unlink(teePath); + } + } + } else { + if (fd >= 0) { + fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); + } + } char teeTime[16]; struct timeval tv; gettimeofday(&tv, NULL); struct tm tm; localtime_r(&tv.tv_sec, &tm); - strftime(teeTime, sizeof(teeTime), "%T", &tm); - char teePath[64]; - sprintf(teePath, "/data/misc/media/%s_%d.wav", teeTime, id); - int teeFd = open(teePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm); + snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d.wav", teeTime, id); + // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd + int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR); if (teeFd >= 0) { char wavHeader[44]; memcpy(wavHeader, @@ -2253,9 +2340,13 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand temp = total * channelCount * sizeof(short); write(teeFd, &temp, sizeof(temp)); close(teeFd); - fdprintf(fd, "FastMixer tee copied to %s\n", teePath); + if (fd >= 0) { + fdprintf(fd, "tee copied to %s\n", teePath); + } } else { - fdprintf(fd, "FastMixer unable to create tee %s: \n", strerror(errno)); + if (fd >= 0) { + fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); + } } } } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index c3f08f6..44bd260 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -593,7 +593,24 @@ private: sp<NBAIO_Source> mRecordTeeSource; public: + // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id = 0); + + // whether tee sink is enabled by property + static bool mTeeSinkInputEnabled; + static bool mTeeSinkOutputEnabled; + static bool mTeeSinkTrackEnabled; + + // runtime configured size of each tee sink pipe, in frames + static size_t mTeeSinkInputFrames; + static size_t mTeeSinkOutputFrames; + static size_t mTeeSinkTrackFrames; + + // compile-time default size of tee sink pipes, in frames + // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes + static const size_t kTeeSinkInputFramesDefault = 0x200000; + static const size_t kTeeSinkOutputFramesDefault = 0x200000; + static const size_t kTeeSinkTrackFramesDefault = 0x1000; }; #undef INCLUDING_FROM_AUDIOFLINGER_H diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index ba848d7..1209ea6 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2124,19 +2124,19 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2); 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 + if (mTeeSinkOutputEnabled) { + // create a Pipe to archive a copy of FastMixer's output for dumpsys + Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, 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; + } // create fast mixer and configure it initially with just one fast track for our submix mFastMixer = new FastMixer(); diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index e0bd97a..fecbfda 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -141,4 +141,7 @@ protected: Vector < sp<SyncEvent> >mSyncEvents; const bool mIsOut; ServerProxy* mServerProxy; + const int mId; + sp<NBAIO_Sink> mTeeSink; + sp<NBAIO_Source> mTeeSource; }; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 315cbbc..724ce38 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -32,6 +32,9 @@ #include "AudioFlinger.h" #include "ServiceUtilities.h" +#include <media/nbaio/Pipe.h> +#include <media/nbaio/PipeReader.h> + // ---------------------------------------------------------------------------- // Note: the following macro is used for extremely verbose logging message. In @@ -53,6 +56,8 @@ namespace android { // TrackBase // ---------------------------------------------------------------------------- +static volatile int32_t nextTrackId = 55; + // TrackBase constructor must be called with AudioFlinger::mLock held AudioFlinger::ThreadBase::TrackBase::TrackBase( ThreadBase *thread, @@ -82,7 +87,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mStepServerFailed(false), mSessionId(sessionId), mIsOut(isOut), - mServerProxy(NULL) + mServerProxy(NULL), + mId(android_atomic_inc(&nextTrackId)) { // client == 0 implies sharedBuffer == 0 ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); @@ -134,11 +140,30 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( } mBufferEnd = (uint8_t *)mBuffer + bufferSize; mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut); + + if (mTeeSinkTrackEnabled) { + NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount); + if (pipeFormat != Format_Invalid) { + Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat); + size_t numCounterOffers = 0; + const NBAIO_Format offers[1] = {pipeFormat}; + ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + PipeReader *pipeReader = new PipeReader(*pipe); + numCounterOffers = 0; + index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mTeeSink = pipe; + mTeeSource = pipeReader; + } + } + } } AudioFlinger::ThreadBase::TrackBase::~TrackBase() { + dumpTee(-1, mTeeSource, mId); // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference delete mServerProxy; if (mCblk != NULL) { @@ -164,6 +189,10 @@ AudioFlinger::ThreadBase::TrackBase::~TrackBase() // This implementation of releaseBuffer() is used by Track and RecordTrack, but not TimedTrack void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) { + if (mTeeSink != 0) { + (void) mTeeSink->write(buffer->raw, buffer->frameCount); + } + buffer->raw = NULL; mStepCount = buffer->frameCount; // FIXME See note at getNextBuffer() |