Fix RecordThread running too often if fast capture is active
Bug: 30159297
Change-Id: Ieca804450168391c162fc4862398586fcc5d7e4e
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
index c22c476..3fd97ac 100644
--- a/include/media/nbaio/NBAIO.h
+++ b/include/media/nbaio/NBAIO.h
@@ -35,13 +35,16 @@
// In addition to the usual status_t
enum {
- NEGOTIATE = 0x80000010, // Must (re-)negotiate format. For negotiate() only, the offeree
- // doesn't accept offers, and proposes counter-offers
- OVERRUN = 0x80000011, // availableToRead(), read(), or readVia() detected lost input due
- // to overrun; an event is counted and the caller should re-try
- UNDERRUN = 0x80000012, // availableToWrite(), write(), or writeVia() detected a gap in
- // output due to underrun (not being called often enough, or with
- // enough data); an event is counted and the caller should re-try
+ NEGOTIATE = (UNKNOWN_ERROR + 0x100), // Must (re-)negotiate format. For negotiate() only,
+ // the offeree doesn't accept offers, and proposes
+ // counter-offers
+ OVERRUN = (UNKNOWN_ERROR + 0x101), // availableToRead(), read(), or readVia() detected
+ // lost input due to overrun; an event is counted and
+ // the caller should re-try
+ UNDERRUN = (UNKNOWN_ERROR + 0x102), // availableToWrite(), write(), or writeVia() detected
+ // a gap in output due to underrun (not being called
+ // often enough, or with enough data); an event is
+ // counted and the caller should re-try
};
// Negotiation of format is based on the data provider and data sink, or the data consumer and
diff --git a/media/libnbaio/PipeReader.cpp b/media/libnbaio/PipeReader.cpp
index 799e435..fdea68e 100644
--- a/media/libnbaio/PipeReader.cpp
+++ b/media/libnbaio/PipeReader.cpp
@@ -55,10 +55,9 @@
// 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 1/16 of the most recent data in pipe to avoid another overrun immediately
- int32_t oldFront = mFront;
- mFront = rear - mPipe.mMaxFrames + (mPipe.mMaxFrames >> 4);
- mFramesOverrun += (size_t) (mFront - oldFront);
+ // Discard all data in pipe to avoid another overrun immediately
+ mFront = rear;
+ mFramesOverrun += avail;
++mOverruns;
return OVERRUN;
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 546cb86..229f741 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -143,6 +143,12 @@
// Direct output thread minimum sleep time in idle or active(underrun) state
static const nsecs_t kDirectMinSleepTimeUs = 10000;
+// The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good
+// balance between power consumption and latency, and allows threads to be scheduled reliably
+// by the CFS scheduler.
+// FIXME Express other hardcoded references to 20ms with references to this constant and move
+// it appropriately.
+#define FMS_20 20
// Whether to use fast mixer
static const enum {
@@ -5876,7 +5882,7 @@
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
mInput(input), mActiveTracksGen(0), mRsmpInBuffer(NULL),
- // mRsmpInFrames and mRsmpInFramesP2 are set by readInputParameters_l()
+ // mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
mRsmpInRear(0)
#ifdef TEE_SINK
, mTeeSink(teeSink)
@@ -5928,7 +5934,8 @@
if (initFastCapture) {
// create a Pipe for FastCapture to write to, and for us and fast tracks to read from
NBAIO_Format format = mInputSource->format();
- size_t pipeFramesP2 = roundup(mSampleRate / 25); // double-buffering of 20 ms each
+ // quadruple-buffering of 20 ms each; this ensures we can sleep for 20ms in RecordThread
+ size_t pipeFramesP2 = roundup(4 * FMS_20 * mSampleRate / 1000);
size_t pipeSize = pipeFramesP2 * Format_frameSize(format);
void *pipeBuffer;
const sp<MemoryDealer> roHeap(readOnlyHeap());
@@ -6258,11 +6265,31 @@
// If an NBAIO source is present, use it to read the normal capture's data
if (mPipeSource != 0) {
size_t framesToRead = mBufferSize / mFrameSize;
+ framesToRead = min(mRsmpInFramesOA - rear, mRsmpInFramesP2 / 2);
framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
framesToRead);
- if (framesRead == 0) {
- // since pipe is non-blocking, simulate blocking input
- sleepUs = (framesToRead * 1000000LL) / mSampleRate;
+ // since pipe is non-blocking, simulate blocking input by waiting for 1/2 of
+ // buffer size or at least for 20ms.
+ size_t sleepFrames = max(
+ min(mPipeFramesP2, mRsmpInFramesP2) / 2, FMS_20 * mSampleRate / 1000);
+ if (framesRead <= (ssize_t) sleepFrames) {
+ sleepUs = (sleepFrames * 1000000LL) / mSampleRate;
+ }
+ if (framesRead < 0) {
+ status_t status = (status_t) framesRead;
+ switch (status) {
+ case OVERRUN:
+ ALOGW("overrun on read from pipe");
+ framesRead = 0;
+ break;
+ case NEGOTIATE:
+ ALOGE("re-negotiation is needed");
+ framesRead = -1; // Will cause an attempt to recover.
+ break;
+ default:
+ ALOGE("unknown error %d on read from pipe", status);
+ break;
+ }
}
// otherwise use the HAL / AudioStreamIn directly
} else {
@@ -7450,9 +7477,9 @@
// The current value is higher than necessary. However it should not add to latency.
// Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
- size_t bufferSize = (mRsmpInFramesP2 + mFrameCount - 1) * mFrameSize;
- (void)posix_memalign(&mRsmpInBuffer, 32, bufferSize);
- memset(mRsmpInBuffer, 0, bufferSize); // if posix_memalign fails, will segv here.
+ mRsmpInFramesOA = mRsmpInFramesP2 + mFrameCount - 1;
+ (void)posix_memalign(&mRsmpInBuffer, 32, mRsmpInFramesOA * mFrameSize);
+ memset(mRsmpInBuffer, 0, mRsmpInFramesOA * mFrameSize); // if posix_memalign fails, will segv here.
// AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
// But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index bcc0a2e..15536f1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1353,9 +1353,10 @@
Condition mStartStopCond;
// resampler converts input at HAL Hz to output at AudioRecord client Hz
- void *mRsmpInBuffer; //
+ void *mRsmpInBuffer; // size = mRsmpInFramesOA
size_t mRsmpInFrames; // size of resampler input in frames
size_t mRsmpInFramesP2;// size rounded up to a power-of-2
+ size_t mRsmpInFramesOA;// mRsmpInFramesP2 + over-allocation
// rolling index that is never cleared
int32_t mRsmpInRear; // last filled frame + 1