At most one reader throttles writer.
All other readers must keep up with writer, or are informed of lost frame count.
Refactor reader, writer, and buffer provider APIs as separate classes.
Change-Id: If2007a4f56d40ac892e899632d261fcfee0ea9e4
#include <cutils/log.h>
#include <utils/Errors.h>
-audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer) :
+audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer)
+ __attribute__((no_sanitize("integer"))) :
mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
mFudgeFactor(mFrameCountP2 - mFrameCount), mFrameSize(frameSize), mBuffer(buffer),
- mLocalFront(0), mLocalRear(0), mSharedFront(0), mSharedRear(0),
- mReadObtained(0), mWriteObtained(0)
+ mSharedRear(0), mThrottleFront(NULL)
{
// maximum value of frameCount * frameSize is INT_MAX (2^31 - 1), not 2^31, because we need to
// be able to distinguish successful and error return values from read and write.
}
}
-int32_t audio_utils_fifo::diff(uint32_t rear, uint32_t front)
+int32_t audio_utils_fifo::diff(uint32_t rear, uint32_t front, size_t *lost)
__attribute__((no_sanitize("integer")))
{
uint32_t diff = rear - front;
uint32_t rearMasked = rear & mask;
uint32_t frontMasked = front & mask;
if (rearMasked >= mFrameCount || frontMasked >= mFrameCount) {
- return (int32_t) android::UNKNOWN_ERROR;
+ return -EIO;
}
uint32_t genDiff = (rear & ~mask) - (front & ~mask);
if (genDiff != 0) {
if (genDiff > mFrameCountP2) {
- return (int32_t) android::UNKNOWN_ERROR;
+ if (lost != NULL) {
+ // TODO provide a more accurate estimate
+ *lost = (genDiff / mFrameCountP2) * mFrameCount;
+ }
+ return -EOVERFLOW;
}
diff -= mFudgeFactor;
}
}
// FIFO should not be overfull
if (diff > mFrameCount) {
- return (int32_t) android::UNKNOWN_ERROR;
+ if (lost != NULL) {
+ *lost = diff - mFrameCount;
+ }
+ return -EOVERFLOW;
}
return (int32_t) diff;
}
-ssize_t audio_utils_fifo::write(const void *buffer, size_t count)
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo_provider::audio_utils_fifo_provider() :
+ mObtained(0)
+{
+}
+
+audio_utils_fifo_provider::~audio_utils_fifo_provider()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
+ audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0)
+{
+}
+
+audio_utils_fifo_writer::~audio_utils_fifo_writer()
+{
+}
+
+ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count)
__attribute__((no_sanitize("integer")))
{
audio_utils_iovec iovec[2];
- ssize_t availToWrite = writeObtain(iovec, count);
+ ssize_t availToWrite = obtain(iovec, count);
if (availToWrite > 0) {
- memcpy(iovec[0].mBase, buffer, iovec[0].mLen * mFrameSize);
+ memcpy(iovec[0].mBase, buffer, iovec[0].mLen * mFifo.mFrameSize);
if (iovec[1].mLen > 0) {
- memcpy(iovec[1].mBase, (char *) buffer + (iovec[0].mLen * mFrameSize),
- iovec[1].mLen * mFrameSize);
+ memcpy(iovec[1].mBase, (char *) buffer + (iovec[0].mLen * mFifo.mFrameSize),
+ iovec[1].mLen * mFifo.mFrameSize);
}
- writeRelease(availToWrite);
+ release(availToWrite);
}
return availToWrite;
}
-ssize_t audio_utils_fifo::writeObtain(audio_utils_iovec iovec[2], size_t count)
+ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count)
__attribute__((no_sanitize("integer")))
{
- uint32_t front = (uint32_t) atomic_load_explicit(&mSharedFront, std::memory_order_acquire);
- uint32_t rear = mLocalRear;
- int32_t filled = diff(rear, front);
- if (filled < 0) {
- mWriteObtained = 0;
- return (ssize_t) filled;
+ size_t availToWrite;
+ if (mFifo.mThrottleFront != NULL) {
+ uint32_t front = (uint32_t) atomic_load_explicit(mFifo.mThrottleFront,
+ std::memory_order_acquire);
+ int32_t filled = mFifo.diff(mLocalRear, front, NULL /*lost*/);
+ if (filled < 0) {
+ mObtained = 0;
+ return (ssize_t) filled;
+ }
+ availToWrite = (size_t) mFifo.mFrameCount - (size_t) filled;
+ } else {
+ availToWrite = mFifo.mFrameCount;
}
- size_t availToWrite = (size_t) mFrameCount - (size_t) filled;
if (availToWrite > count) {
availToWrite = count;
}
- uint32_t rearMasked = rear & (mFrameCountP2 - 1);
- size_t part1 = mFrameCount - rearMasked;
+ uint32_t rearMasked = mLocalRear & (mFifo.mFrameCountP2 - 1);
+ size_t part1 = mFifo.mFrameCount - rearMasked;
if (part1 > availToWrite) {
part1 = availToWrite;
}
size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
iovec[0].mLen = part1;
- iovec[0].mBase = part1 > 0 ? (char *) mBuffer + (rearMasked * mFrameSize) : NULL;
+ iovec[0].mBase = part1 > 0 ? (char *) mFifo.mBuffer + (rearMasked * mFifo.mFrameSize) : NULL;
iovec[1].mLen = part2;
- iovec[1].mBase = part2 > 0 ? mBuffer : NULL;
- mWriteObtained = availToWrite;
+ iovec[1].mBase = part2 > 0 ? mFifo.mBuffer : NULL;
+ mObtained = availToWrite;
return availToWrite;
}
-void audio_utils_fifo::writeRelease(size_t count)
+void audio_utils_fifo_writer::release(size_t count)
+ __attribute__((no_sanitize("integer")))
{
if (count > 0) {
- ALOG_ASSERT(count <= mWriteObtained);
- mLocalRear = sum(mLocalRear, count);
- atomic_store_explicit(&mSharedRear, (uint_fast32_t) mLocalRear,
+ ALOG_ASSERT(count <= mObtained);
+ mLocalRear = mFifo.sum(mLocalRear, count);
+ atomic_store_explicit(&mFifo.mSharedRear, (uint_fast32_t) mLocalRear,
std::memory_order_release);
- mWriteObtained -= count;
+ mObtained -= count;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
+ audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0), mSharedFront(0)
+{
+ if (throttlesWriter) {
+ fifo.mThrottleFront = &mSharedFront;
}
}
-ssize_t audio_utils_fifo::read(void *buffer, size_t count)
+audio_utils_fifo_reader::~audio_utils_fifo_reader()
+{
+}
+
+ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, size_t *lost)
__attribute__((no_sanitize("integer")))
{
audio_utils_iovec iovec[2];
- ssize_t availToRead = readObtain(iovec, count);
+ ssize_t availToRead = obtain(iovec, count, lost);
if (availToRead > 0) {
- memcpy(buffer, iovec[0].mBase, iovec[0].mLen * mFrameSize);
+ memcpy(buffer, iovec[0].mBase, iovec[0].mLen * mFifo.mFrameSize);
if (iovec[1].mLen > 0) {
- memcpy((char *) buffer + (iovec[0].mLen * mFrameSize), iovec[1].mBase,
- iovec[1].mLen * mFrameSize);
+ memcpy((char *) buffer + (iovec[0].mLen * mFifo.mFrameSize), iovec[1].mBase,
+ iovec[1].mLen * mFifo.mFrameSize);
}
- readRelease(availToRead);
+ release(availToRead);
}
return availToRead;
}
-ssize_t audio_utils_fifo::readObtain(audio_utils_iovec iovec[2], size_t count)
+ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count)
+ __attribute__((no_sanitize("integer")))
+{
+ return obtain(iovec, count, NULL);
+}
+
+void audio_utils_fifo_reader::release(size_t count)
__attribute__((no_sanitize("integer")))
{
- uint32_t rear = (uint32_t) atomic_load_explicit(&mSharedRear, std::memory_order_acquire);
- uint32_t front = mLocalFront;
- int32_t filled = diff(rear, front);
+ if (count > 0) {
+ ALOG_ASSERT(count <= mObtained);
+ mLocalFront = mFifo.sum(mLocalFront, count);
+ if (mFifo.mThrottleFront == &mSharedFront) {
+ atomic_store_explicit(&mSharedFront, (uint_fast32_t) mLocalFront,
+ std::memory_order_release);
+ }
+ mObtained -= count;
+ }
+}
+
+ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count, size_t *lost)
+ __attribute__((no_sanitize("integer")))
+{
+ uint32_t rear = (uint32_t) atomic_load_explicit(&mFifo.mSharedRear,
+ std::memory_order_acquire);
+ int32_t filled = mFifo.diff(rear, mLocalFront, lost);
if (filled < 0) {
- mReadObtained = 0;
+ if (filled == android::BAD_INDEX) {
+ mLocalFront = rear;
+ }
+ mObtained = 0;
return (ssize_t) filled;
}
size_t availToRead = (size_t) filled;
if (availToRead > count) {
availToRead = count;
}
- uint32_t frontMasked = front & (mFrameCountP2 - 1);
- size_t part1 = mFrameCount - frontMasked;
+ uint32_t frontMasked = mLocalFront & (mFifo.mFrameCountP2 - 1);
+ size_t part1 = mFifo.mFrameCount - frontMasked;
if (part1 > availToRead) {
part1 = availToRead;
}
size_t part2 = part1 > 0 ? availToRead - part1 : 0;
iovec[0].mLen = part1;
- iovec[0].mBase = part1 > 0 ? (char *) mBuffer + (frontMasked * mFrameSize) : NULL;
+ iovec[0].mBase = part1 > 0 ? (char *) mFifo.mBuffer + (frontMasked * mFifo.mFrameSize) : NULL;
iovec[1].mLen = part2;
- iovec[1].mBase = part2 > 0 ? mBuffer : NULL;
- mReadObtained = availToRead;
+ iovec[1].mBase = part2 > 0 ? mFifo.mBuffer : NULL;
+ mObtained = availToRead;
return availToRead;
}
-
-void audio_utils_fifo::readRelease(size_t count)
-{
- if (count > 0) {
- ALOG_ASSERT(count <= mReadObtained);
- mLocalFront = sum(mLocalFront, count);
- atomic_store_explicit(&mSharedFront, (uint_fast32_t) mLocalFront,
- std::memory_order_release);
- mReadObtained -= count;
- }
-}
#error C API is no longer supported
#endif
-// Describes one virtually contiguous fragment of a logically contiguous slice.
-// Compare to struct iovec for readv(2) and writev(2).
-struct audio_utils_iovec {
- void *mBase; // const void * for readObtain()
- size_t mLen; // in frames
-};
-
// Single writer, single reader non-blocking FIFO.
// Writer and reader must be in same process.
// No user-serviceable parts within.
class audio_utils_fifo {
+ friend class audio_utils_fifo_reader;
+ friend class audio_utils_fifo_writer;
+
public:
/**
- * Initialize a FIFO object.
+ * Construct a FIFO object.
*
* \param frameCount Max number of significant frames to be stored in the FIFO > 0.
* If writes and reads always use the same count, and that count is a divisor of
*/
audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer);
-/**
- * De-initialize a FIFO object.
- */
~audio_utils_fifo();
private:
+/** Return a new index as the sum of a validated index and a specified increment.
+ *
+ * \param index Caller should supply a validated mFront or mRear.
+ * \param increment Value to be added to the index <= mFrameCount.
+ *
+ * \return the sum of index plus increment.
+ */
+ uint32_t sum(uint32_t index, uint32_t increment);
+
+/** Return the difference between two indices: rear - front.
+ *
+ * \param rear Caller should supply an unvalidated mRear.
+ * \param front Caller should supply an unvalidated mFront.
+ * \param lost If non-NULL, set to the approximate number of lost frames.
+ *
+ * \return the zero or positive difference <= mFrameCount, or a negative error code.
+ */
+ int32_t diff(uint32_t rear, uint32_t front, size_t *lost);
+
// These fields are const after initialization
const uint32_t mFrameCount; // max number of significant frames to be stored in the FIFO > 0
const uint32_t mFrameCountP2; // roundup(mFrameCount)
// after the end of mBuffer. Only the indices are wasted, not any
// memory.
const uint32_t mFrameSize; // size of each frame in bytes
- void * const mBuffer; // pointer to caller-allocated buffer of size mFrameCount frames
+ void * const mBuffer; // pointer to caller-allocated buffer of size mFrameCount frames
- // These are accessed by one side only using ordinary operations
- uint32_t mLocalFront; // frame index of first frame slot available to read, or read index
- uint32_t mLocalRear; // frame index of next frame slot available to write, or write index
+ std::atomic_uint_fast32_t mSharedRear; // accessed by both sides using atomic operations
- // These are accessed by both sides using atomic operations
- std::atomic_uint_fast32_t mSharedFront;
- std::atomic_uint_fast32_t mSharedRear;
+ // Pointer to the mSharedFront of at most one reader that throttles the writer,
+ // or NULL for no throttling
+ std::atomic_uint_fast32_t *mThrottleFront;
+};
+
+// Describes one virtually contiguous fragment of a logically contiguous slice.
+// Compare to struct iovec for readv(2) and writev(2).
+struct audio_utils_iovec {
+ void *mBase; // const void * for audio_utils_fifo_reader::obtain()
+ size_t mLen; // in frames
+};
+
+////////////////////////////////////////////////////////////////////////////////
- // Number of frames obtained at most recent obtainRead/Write(), less number of frames released
- uint32_t mReadObtained;
- uint32_t mWriteObtained;
+// Based on frameworks/av/include/media/AudioBufferProvider.h
+class audio_utils_fifo_provider {
+public:
+ audio_utils_fifo_provider();
+ virtual ~audio_utils_fifo_provider();
+
+// Error codes for ssize_t return value:
+// -EIO corrupted indices (reader or writer)
+// -EOVERFLOW reader is not keeping up with writer (reader only)
+ virtual ssize_t obtain(audio_utils_iovec iovec[2], size_t count) = 0;
+
+ virtual void release(size_t count) = 0;
+
+protected:
+ // Number of frames obtained at most recent obtain(), less number of frames released
+ uint32_t mObtained;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class audio_utils_fifo_writer : public audio_utils_fifo_provider {
public:
+ audio_utils_fifo_writer(audio_utils_fifo& fifo);
+ ~audio_utils_fifo_writer();
/**
* Write to FIFO.
*/
ssize_t write(const void *buffer, size_t count);
+ // Implement audio_utils_fifo_provider
+ virtual ssize_t obtain(audio_utils_iovec iovec[2], size_t count);
+ virtual void release(size_t count);
+
+private:
+ audio_utils_fifo& mFifo;
+
+ // Accessed by writer only using ordinary operations
+ uint32_t mLocalRear; // frame index of next frame slot available to write, or write index
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class audio_utils_fifo_reader : public audio_utils_fifo_provider {
+
+public:
+ audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter);
+ ~audio_utils_fifo_reader();
+
/** Read from FIFO.
*
* \param buffer Pointer to destination buffer to be filled with up to 'count' frames of data.
* \param count Desired number of frames to read.
+ * \param lost If non-NULL, set to the approximate number of lost frames before re-sync.
*
* \return actual number of frames read <= count.
*
* or partial if the FIFO was almost empty.
* A negative return value indicates an error. Currently there are no errors defined.
*/
- ssize_t read(void *buffer, size_t count);
+ ssize_t read(void *buffer, size_t count, size_t *lost = NULL);
-private:
+ // Implement audio_utils_fifo_provider
+ virtual ssize_t obtain(audio_utils_iovec iovec[2], size_t count);
+ virtual void release(size_t count);
-/** Return a new index as the sum of a validated index and a specified increment.
- *
- * \param index Caller should supply a validated mFront or mRear.
- * \param increment Value to be added to the index <= mFrameCount.
- *
- * \return the sum of index plus increment.
- */
- uint32_t sum(uint32_t index, uint32_t increment);
+ // Extended parameter list for reader only
+ ssize_t obtain(audio_utils_iovec iovec[2], size_t count, size_t *lost);
-/** Return the difference between two indices: rear - front.
- *
- * \param rear Caller should supply an unvalidated mRear.
- * \param front Caller should supply an unvalidated mFront.
- *
- * \return the zero or positive difference <= mFrameCount, or a negative error code.
- */
- int32_t diff(uint32_t rear, uint32_t front);
+private:
+ audio_utils_fifo& mFifo;
+
+ // Accessed by reader only using ordinary operations
+ uint32_t mLocalFront; // frame index of first frame slot available to read, or read index
- // obtain and release are based on frameworks/av/include/media/AudioBufferProvider.h
- ssize_t readObtain(audio_utils_iovec iovec[2], size_t count);
- void readRelease(size_t count);
- ssize_t writeObtain(audio_utils_iovec iovec[2], size_t count);
- void writeRelease(size_t count);
+ // Accessed by a throttling reader and writer using atomic operations
+ std::atomic_uint_fast32_t mSharedFront;
};
#endif // !ANDROID_AUDIO_FIFO_H
// Test program for audio_utils FIFO library.
// This only tests the single-threaded aspects, not the barriers.
+#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
- size_t frameCount = 256;
- size_t maxFramesPerRead = 1;
- size_t maxFramesPerWrite = 1;
+ size_t frameCount = 0;
+ size_t maxFramesPerRead = 0;
+ size_t maxFramesPerWrite = 0;
+ bool readerThrottlesWriter = true;
int i;
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (arg[0] != '-')
break;
switch (arg[1]) {
- case 'c': // FIFO frame count
+ case 'f': // FIFO frame count
frameCount = atoi(&arg[2]);
break;
case 'r': // maximum frame count per read from FIFO
maxFramesPerRead = atoi(&arg[2]);
break;
+ case 't': // disable throttling of writer by reader
+ readerThrottlesWriter = false;
+ break;
case 'w': // maximum frame count per write to FIFO
maxFramesPerWrite = atoi(&arg[2]);
break;
goto usage;
}
}
+ if (frameCount == 0) {
+ frameCount = 256;
+ }
+ if (maxFramesPerRead == 0) {
+ maxFramesPerRead = frameCount;
+ }
+ if (maxFramesPerWrite == 0) {
+ maxFramesPerWrite = frameCount;
+ }
if (argc - i != 2) {
usage:
- fprintf(stderr, "usage: %s [-c#] [-r#] [-w#] in.wav out.wav\n", argv[0]);
+ fprintf(stderr, "usage: %s [-f#] [-r#] [-t] [-w#] in.wav out.wav\n", argv[0]);
return EXIT_FAILURE;
}
char *inputFile = argv[i];
size_t framesRead = 0;
short *fifoBuffer = new short[frameCount * sfinfoin.channels];
audio_utils_fifo fifo(frameCount, frameSize, fifoBuffer);
+ audio_utils_fifo_writer fifoWriter(fifo);
+ audio_utils_fifo_reader fifoReader(fifo, readerThrottlesWriter);
int fifoWriteCount = 0, fifoReadCount = 0;
int fifoFillLevel = 0, minFillLevel = INT_MAX, maxFillLevel = INT_MIN;
for (;;) {
size_t framesToWrite = sfinfoin.frames - framesWritten;
size_t framesToRead = sfinfoin.frames - framesRead;
- if (framesToWrite == 0 && framesToRead == 0) {
+ if (framesToWrite == 0 && (framesToRead == 0 || !readerThrottlesWriter)) {
break;
}
framesToWrite = maxFramesPerWrite;
}
framesToWrite = rand() % (framesToWrite + 1);
- ssize_t actualWritten = fifo.write(
+ ssize_t actualWritten = fifoWriter.write(
&inputBuffer[framesWritten * sfinfoin.channels], framesToWrite);
+ //printf("wrote %d out of %d\n", (int) actualWritten, (int) framesToWrite);
if (actualWritten < 0 || (size_t) actualWritten > framesToWrite) {
fprintf(stderr, "write to FIFO failed\n");
break;
if (fifoFillLevel > maxFillLevel) {
maxFillLevel = fifoFillLevel;
if (maxFillLevel > (int) frameCount) {
- printf("maxFillLevel=%d > frameCount=%d\n", maxFillLevel, (int) frameCount);
- abort();
+ if (readerThrottlesWriter) {
+ printf("maxFillLevel=%d > frameCount=%d\n", maxFillLevel, (int) frameCount);
+ abort();
+ }
}
}
framesToRead = maxFramesPerRead;
}
framesToRead = rand() % (framesToRead + 1);
- ssize_t actualRead = fifo.read(
+ ssize_t actualRead = fifoReader.read(
&outputBuffer[framesRead * sfinfoin.channels], framesToRead);
+ //printf("read %d out of %d\n", (int) actualRead, (int) framesToRead);
if (actualRead < 0 || (size_t) actualRead > framesToRead) {
- fprintf(stderr, "read from FIFO failed\n");
- break;
+ switch (actualRead) {
+ case -EIO:
+ fprintf(stderr, "read from FIFO failed: corrupted indices\n");
+ abort();
+ break;
+ case -EOVERFLOW:
+ if (readerThrottlesWriter) {
+ fprintf(stderr, "read from FIFO failed: unexpected overflow\n");
+ abort();
+ }
+ printf("warning: reader lost frames\n");
+ actualRead = 0;
+ break;
+ default:
+ if (actualRead < 0) {
+ fprintf(stderr, "read from FIFO failed: unexpected error code %d\n",
+ (int) actualRead);
+ } else {
+ fprintf(stderr, "read from FIFO failed: actualRead=%d > framesToRead=%d\n",
+ (int) actualRead, (int) framesToRead);
+ }
+ abort();
+ }
}
if (actualRead < min(fifoFillLevel, (int) framesToRead)) {
- fprintf(stderr, "only read %d when should have read min(%d, %d)\n",
- (int) actualRead, fifoFillLevel, (int) framesToRead);
+ //fprintf(stderr, "only read %d when should have read min(%d, %d)\n",
+ // (int) actualRead, fifoFillLevel, (int) framesToRead);
}
framesRead += actualRead;
if (actualRead > 0) {