summaryrefslogtreecommitdiffstats
path: root/services/audioflinger
diff options
context:
space:
mode:
authorGlenn Kasten <gkasten@google.com>2013-01-10 12:31:01 -0800
committerGlenn Kasten <gkasten@google.com>2013-02-22 16:50:36 -0800
commitda6ef1320d0161b1640dc84d7a9c5a25860c3619 (patch)
tree86b24f64ed0bd55de10deb193290009687886192 /services/audioflinger
parent99c2f923f6b04efffe949d1daf9cb7148e3cc201 (diff)
downloadframeworks_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.mk8
-rw-r--r--services/audioflinger/AudioFlinger.cpp111
-rw-r--r--services/audioflinger/AudioFlinger.h17
-rw-r--r--services/audioflinger/Threads.cpp26
-rw-r--r--services/audioflinger/TrackBase.h3
-rw-r--r--services/audioflinger/Tracks.cpp31
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()