Add CopyBufferProvider class for AudioMixer
AudioMixer::ReformatBufferProvider now uses it as a base class.
Fix ReformatBufferProvider object leak.
Change-Id: If196f844eaaa124a173ffa27afe88098023c2ff9
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index e57cb8a..319d4a8 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -71,9 +71,111 @@
// because of downmix/upmix support.
static const bool kUseFloat = true;
+// Set to default copy buffer size in frames for input processing.
+static const size_t kCopyBufferFrameCount = 256;
+
namespace android {
// ----------------------------------------------------------------------------
+
+template <typename T>
+T min(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
+
+AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
+ size_t outputFrameSize, size_t bufferFrameCount) :
+ mInputFrameSize(inputFrameSize),
+ mOutputFrameSize(outputFrameSize),
+ mLocalBufferFrameCount(bufferFrameCount),
+ mLocalBufferData(NULL),
+ mConsumed(0)
+{
+ ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
+ inputFrameSize, outputFrameSize, bufferFrameCount);
+ LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
+ "Requires local buffer if inputFrameSize(%d) < outputFrameSize(%d)",
+ inputFrameSize, outputFrameSize);
+ if (mLocalBufferFrameCount) {
+ (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
+ }
+ mBuffer.frameCount = 0;
+}
+
+AudioMixer::CopyBufferProvider::~CopyBufferProvider()
+{
+ ALOGV("~CopyBufferProvider(%p)", this);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mLocalBufferData);
+}
+
+status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+ int64_t pts)
+{
+ //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ // this, pBuffer, pBuffer->frameCount, pts);
+ if (mLocalBufferFrameCount == 0) {
+ status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+ if (res == OK) {
+ copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
+ }
+ return res;
+ }
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = pBuffer->frameCount;
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+ // At one time an upstream buffer provider had
+ // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
+ //
+ // By API spec, if res != OK, then mBuffer.frameCount == 0.
+ // but there may be improper implementations.
+ ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+ if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ }
+ mConsumed = 0;
+ }
+ ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+ size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
+ count = min(count, pBuffer->frameCount);
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = count;
+ copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
+ pBuffer->frameCount);
+ return OK;
+}
+
+void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+ //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
+ // this, pBuffer, pBuffer->frameCount);
+ if (mLocalBufferFrameCount == 0) {
+ mTrackBufferProvider->releaseBuffer(pBuffer);
+ return;
+ }
+ // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+ mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
+ if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ ALOG_ASSERT(mBuffer.frameCount == 0);
+ }
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void AudioMixer::CopyBufferProvider::reset()
+{
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ mConsumed = 0;
+}
+
AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider() : AudioBufferProvider(),
mTrackBufferProvider(NULL), mDownmixHandle(NULL)
{
@@ -118,102 +220,23 @@
}
}
-template <typename T>
-T min(const T& a, const T& b)
-{
- return a < b ? a : b;
-}
-
AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
- audio_format_t inputFormat, audio_format_t outputFormat) :
- mTrackBufferProvider(NULL),
+ audio_format_t inputFormat, audio_format_t outputFormat,
+ size_t bufferFrameCount) :
+ CopyBufferProvider(
+ channels * audio_bytes_per_sample(inputFormat),
+ channels * audio_bytes_per_sample(outputFormat),
+ bufferFrameCount),
mChannels(channels),
mInputFormat(inputFormat),
- mOutputFormat(outputFormat),
- mInputFrameSize(channels * audio_bytes_per_sample(inputFormat)),
- mOutputFrameSize(channels * audio_bytes_per_sample(outputFormat)),
- mOutputData(NULL),
- mOutputCount(0),
- mConsumed(0)
+ mOutputFormat(outputFormat)
{
ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
- if (requiresInternalBuffers()) {
- mOutputCount = 256;
- (void)posix_memalign(&mOutputData, 32, mOutputCount * mOutputFrameSize);
- }
- mBuffer.frameCount = 0;
}
-AudioMixer::ReformatBufferProvider::~ReformatBufferProvider()
+void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
- ALOGV("~ReformatBufferProvider(%p)", this);
- if (mBuffer.frameCount != 0) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- }
- free(mOutputData);
-}
-
-status_t AudioMixer::ReformatBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
- int64_t pts) {
- //ALOGV("ReformatBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
- // this, pBuffer, pBuffer->frameCount, pts);
- if (!requiresInternalBuffers()) {
- status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
- if (res == OK) {
- memcpy_by_audio_format(pBuffer->raw, mOutputFormat, pBuffer->raw, mInputFormat,
- pBuffer->frameCount * mChannels);
- }
- return res;
- }
- if (mBuffer.frameCount == 0) {
- mBuffer.frameCount = pBuffer->frameCount;
- status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
- // TODO: Track down a bug in the upstream provider
- // LOG_ALWAYS_FATAL_IF(res == OK && mBuffer.frameCount == 0,
- // "ReformatBufferProvider::getNextBuffer():"
- // " Invalid zero framecount returned from getNextBuffer()");
- if (res != OK || mBuffer.frameCount == 0) {
- pBuffer->raw = NULL;
- pBuffer->frameCount = 0;
- return res;
- }
- }
- ALOG_ASSERT(mConsumed < mBuffer.frameCount);
- size_t count = min(mOutputCount, mBuffer.frameCount - mConsumed);
- count = min(count, pBuffer->frameCount);
- pBuffer->raw = mOutputData;
- pBuffer->frameCount = count;
- //ALOGV("reformatting %d frames from %#x to %#x, %d chan",
- // pBuffer->frameCount, mInputFormat, mOutputFormat, mChannels);
- memcpy_by_audio_format(pBuffer->raw, mOutputFormat,
- (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, mInputFormat,
- pBuffer->frameCount * mChannels);
- return OK;
-}
-
-void AudioMixer::ReformatBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
- //ALOGV("ReformatBufferProvider(%p)::releaseBuffer(%p(%zu))",
- // this, pBuffer, pBuffer->frameCount);
- if (!requiresInternalBuffers()) {
- mTrackBufferProvider->releaseBuffer(pBuffer);
- return;
- }
- // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
- mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
- if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
- mConsumed = 0;
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- // ALOG_ASSERT(mBuffer.frameCount == 0);
- }
- pBuffer->raw = NULL;
- pBuffer->frameCount = 0;
-}
-
-void AudioMixer::ReformatBufferProvider::reset() {
- if (mBuffer.frameCount != 0) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- }
- mConsumed = 0;
+ memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels);
}
// ----------------------------------------------------------------------------
@@ -258,6 +281,7 @@
for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
t->resampler = NULL;
t->downmixerBufferProvider = NULL;
+ t->mReformatBufferProvider = NULL;
t++;
}
@@ -269,6 +293,7 @@
for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
delete t->resampler;
delete t->downmixerBufferProvider;
+ delete t->mReformatBufferProvider;
t++;
}
delete [] mState.outputTemp;
@@ -521,7 +546,8 @@
if (pTrack->mFormat != pTrack->mMixerInFormat) {
pTrack->mReformatBufferProvider = new ReformatBufferProvider(
audio_channel_count_from_out_mask(pTrack->channelMask),
- pTrack->mFormat, pTrack->mMixerInFormat);
+ pTrack->mFormat, pTrack->mMixerInFormat,
+ kCopyBufferFrameCount);
reconfigureBufferProviders(pTrack);
}
return NO_ERROR;
@@ -531,7 +557,7 @@
{
pTrack->bufferProvider = pTrack->mInputBufferProvider;
if (pTrack->mReformatBufferProvider) {
- pTrack->mReformatBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+ pTrack->mReformatBufferProvider->setBufferProvider(pTrack->bufferProvider);
pTrack->bufferProvider = pTrack->mReformatBufferProvider;
}
if (pTrack->downmixerBufferProvider) {
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index a9f4761..f08d9b5 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -154,7 +154,7 @@
struct state_t;
struct track_t;
class DownmixerBufferProvider;
- class ReformatBufferProvider;
+ class CopyBufferProvider;
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
int32_t* aux);
@@ -206,8 +206,8 @@
int32_t* auxBuffer;
// 16-byte boundary
- AudioBufferProvider* mInputBufferProvider; // 4 bytes
- ReformatBufferProvider* mReformatBufferProvider; // 4 bytes
+ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
+ CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
int32_t sessionId;
@@ -253,6 +253,52 @@
track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};
+ // Base AudioBufferProvider class used for ReformatBufferProvider.
+ // It handles a private buffer for use in converting format or channel masks from the
+ // input data to a form acceptable by the mixer.
+ // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
+ // processing pipeline.
+ class CopyBufferProvider : public AudioBufferProvider {
+ public:
+ // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
+ // If bufferFrameCount is 0, no private buffer is created and in-place modification of
+ // the upstream buffer provider's buffers is performed by copyFrames().
+ CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
+ size_t bufferFrameCount);
+ virtual ~CopyBufferProvider();
+
+ // Overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer* buffer);
+
+ // Other public methods
+
+ // call this to release the buffer to the upstream provider.
+ // treat it as an audio discontinuity for future samples.
+ virtual void reset();
+
+ // this function should be supplied by the derived class. It converts
+ // #frames in the *src pointer to the *dst pointer. It is public because
+ // some providers will allow this to work on arbitrary buffers outside
+ // of the internal buffers.
+ virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
+
+ // set the upstream buffer provider. Consider calling "reset" before this function.
+ void setBufferProvider(AudioBufferProvider *p) {
+ mTrackBufferProvider = p;
+ }
+
+ protected:
+ AudioBufferProvider* mTrackBufferProvider;
+ const size_t mInputFrameSize;
+ const size_t mOutputFrameSize;
+ private:
+ AudioBufferProvider::Buffer mBuffer;
+ const size_t mLocalBufferFrameCount;
+ void* mLocalBufferData;
+ size_t mConsumed;
+ };
+
// AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect
class DownmixerBufferProvider : public AudioBufferProvider {
public:
@@ -266,33 +312,19 @@
effect_config_t mDownmixConfig;
};
- // AudioBufferProvider wrapper that reformats track to acceptable mixer input type
- class ReformatBufferProvider : public AudioBufferProvider {
+ // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
+ // to an acceptable mixer input format type.
+ class ReformatBufferProvider : public CopyBufferProvider {
public:
ReformatBufferProvider(int32_t channels,
- audio_format_t inputFormat, audio_format_t outputFormat);
- virtual ~ReformatBufferProvider();
+ audio_format_t inputFormat, audio_format_t outputFormat,
+ size_t bufferFrameCount);
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
- // overrides AudioBufferProvider methods
- virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
- virtual void releaseBuffer(Buffer* buffer);
-
- void reset();
- inline bool requiresInternalBuffers() {
- return true; //mInputFrameSize < mOutputFrameSize;
- }
-
- AudioBufferProvider* mTrackBufferProvider;
- int32_t mChannels;
- audio_format_t mInputFormat;
- audio_format_t mOutputFormat;
- size_t mInputFrameSize;
- size_t mOutputFrameSize;
- // (only) required for reformatting to a larger size.
- AudioBufferProvider::Buffer mBuffer;
- void* mOutputData;
- size_t mOutputCount;
- size_t mConsumed;
+ protected:
+ const int32_t mChannels;
+ const audio_format_t mInputFormat;
+ const audio_format_t mOutputFormat;
};
// bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.