Start isolating control block accesses in a proxy
The proxy object will eventually be the only code that understands the
details of the control block. This should make it easier to change the
control block in the future.
Initial set of control block fields that are isolated:
- sample rate
- send level
- volume
Prepare for streaming/static separation by adding a union to the control
block for the new fields.
Fix bug in handling of max sample rate on a track. It was only checking
at re-configuration, not at each mix.
Simplify OutputTrack::obtainBuffer.
Change-Id: I2249f9d04f73a911a922ad1d7f6197292c74cd92
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index ae444c3..38c6548 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -29,6 +29,7 @@
namespace android {
class audio_track_cblk_t;
+class AudioRecordClientProxy;
// ----------------------------------------------------------------------------
@@ -374,6 +375,7 @@
uint32_t mUpdatePeriod; // in ms
// constant after constructor or set()
+ uint32_t mSampleRate;
size_t mFrameCount;
audio_format_t mFormat;
uint8_t mChannelCount;
@@ -393,6 +395,7 @@
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
+ AudioRecordClientProxy* mProxy;
};
}; // namespace android
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 6f85527..9d07ed5 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -36,6 +36,7 @@
// ----------------------------------------------------------------------------
class audio_track_cblk_t;
+class AudioTrackClientProxy;
// ----------------------------------------------------------------------------
@@ -538,6 +539,7 @@
float mVolume[2];
float mSendLevel;
+ uint32_t mSampleRate;
size_t mFrameCount; // corresponds to current IAudioTrack
size_t mReqFrameCount; // frame count to request the next time a new
// IAudioTrack is needed
@@ -596,6 +598,7 @@
bool mIsTimed;
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
+ AudioTrackClientProxy* mProxy;
};
class TimedAudioTrack : public AudioTrack
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 48b6b21..41e20f8 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <utils/threads.h>
+#include <utils/Log.h>
namespace android {
@@ -38,9 +39,26 @@
#define CBLK_INVALID 0x04 // track buffer invalidated by AudioFlinger, need to re-create
#define CBLK_DISABLED 0x08 // track disabled by AudioFlinger due to underrun, need to re-start
+struct AudioTrackSharedStreaming {
+ // similar to NBAIO MonoPipe
+ volatile int32_t mFront;
+ volatile int32_t mRear;
+};
+
+// future
+struct AudioTrackSharedStatic {
+ int mReserved;
+};
+
+// ----------------------------------------------------------------------------
+
// Important: do not add any virtual methods, including ~
struct audio_track_cblk_t
{
+ friend class Proxy;
+ friend class AudioTrackClientProxy;
+ friend class AudioRecordClientProxy;
+ friend class ServerProxy;
// The data members are grouped so that members accessed frequently and in the same context
// are in the same line of data cache.
@@ -72,12 +90,13 @@
// For AudioTrack only, not used by AudioRecord.
private:
uint32_t mVolumeLR;
-public:
- uint32_t sampleRate;
+ uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz
+ // or 0 == default. Write-only client, read-only server.
uint8_t mPad2; // unused
+public:
// read-only for client, server writes once at initialization and is then read-only
uint8_t mName; // normal tracks: track name, fast tracks: track index
@@ -94,65 +113,184 @@
// Cache line boundary (32 bytes)
+#if 0
+ union {
+ AudioTrackSharedStreaming mStreaming;
+ AudioTrackSharedStatic mStatic;
+ int mAlign[8];
+ } u;
+
+ // Cache line boundary (32 bytes)
+#endif
+
// Since the control block is always located in shared memory, this constructor
// is only used for placement new(). It is never used for regular new() or stack.
audio_track_cblk_t();
- // called by client only, where client includes regular
- // AudioTrack and AudioFlinger::PlaybackThread::OutputTrack
- uint32_t stepUserIn(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, false); }
- uint32_t stepUserOut(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, true); }
-
- bool stepServer(size_t stepCount, size_t frameCount, bool isOut);
-
+private:
// if there is a shared buffer, "buffers" is the value of pointer() for the shared
// buffer, otherwise "buffers" points immediately after the control block
- void* buffer(void *buffers, uint32_t frameSize, uint32_t offset) const;
-
- uint32_t framesAvailableIn(size_t frameCount)
- { return framesAvailable(frameCount, false); }
- uint32_t framesAvailableOut(size_t frameCount)
- { return framesAvailable(frameCount, true); }
- uint32_t framesAvailableIn_l(size_t frameCount)
- { return framesAvailable_l(frameCount, false); }
- uint32_t framesAvailableOut_l(size_t frameCount)
- { return framesAvailable_l(frameCount, true); }
- uint32_t framesReadyIn() { return framesReady(false); }
- uint32_t framesReadyOut() { return framesReady(true); }
+ void* buffer(void *buffers, uint32_t frameSize, size_t offset) const;
bool tryLock();
- // No barriers on the following operations, so the ordering of loads/stores
- // with respect to other parameters is UNPREDICTABLE. That's considered safe.
-
- // for AudioTrack client only, caller must limit to 0.0 <= sendLevel <= 1.0
- void setSendLevel(float sendLevel) {
- mSendLevel = uint16_t(sendLevel * 0x1000);
- }
-
- // for AudioFlinger only; the return value must be validated by the caller
- uint16_t getSendLevel_U4_12() const {
- return mSendLevel;
- }
-
- // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
- void setVolumeLR(uint32_t volumeLR) {
- mVolumeLR = volumeLR;
- }
-
- // for AudioFlinger only; the return value must be validated by the caller
- uint32_t getVolumeLR() const {
- return mVolumeLR;
- }
-
-private:
// isOut == true means AudioTrack, isOut == false means AudioRecord
+ bool stepServer(size_t stepCount, size_t frameCount, bool isOut);
uint32_t stepUser(size_t stepCount, size_t frameCount, bool isOut);
uint32_t framesAvailable(size_t frameCount, bool isOut);
uint32_t framesAvailable_l(size_t frameCount, bool isOut);
uint32_t framesReady(bool isOut);
};
+// ----------------------------------------------------------------------------
+
+// Proxy for shared memory control block, to isolate callers from needing to know the details.
+// There is exactly one ClientProxy and one ServerProxy per shared memory control block.
+// The proxies are located in normal memory, and are not multi-thread safe within a given side.
+class Proxy {
+protected:
+ Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize) { }
+ virtual ~Proxy() { }
+
+public:
+ void* buffer(size_t offset) const {
+ return mCblk->buffer(mBuffers, mFrameSize, offset);
+ }
+
+protected:
+ // These refer to shared memory, and are virtual addresses with respect to the current process.
+ // They may have different virtual addresses within the other process.
+ audio_track_cblk_t* const mCblk; // the control block
+ void* const mBuffers; // starting address of buffers
+
+ const size_t mFrameCount; // not necessarily a power of 2
+ const size_t mFrameSize; // in bytes
+#if 0
+ const size_t mFrameCountP2; // mFrameCount rounded to power of 2, streaming mode
+#endif
+
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy seen by AudioTrack client and AudioRecord client
+class ClientProxy : public Proxy {
+protected:
+ ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : Proxy(cblk, buffers, frameCount, frameSize) { }
+ virtual ~ClientProxy() { }
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy used by AudioTrack client, which also includes AudioFlinger::PlaybackThread::OutputTrack
+class AudioTrackClientProxy : public ClientProxy {
+public:
+ AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : ClientProxy(cblk, buffers, frameCount, frameSize) { }
+ virtual ~AudioTrackClientProxy() { }
+
+ // No barriers on the following operations, so the ordering of loads/stores
+ // with respect to other parameters is UNPREDICTABLE. That's considered safe.
+
+ // caller must limit to 0.0 <= sendLevel <= 1.0
+ void setSendLevel(float sendLevel) {
+ mCblk->mSendLevel = uint16_t(sendLevel * 0x1000);
+ }
+
+ // caller must limit to 0 <= volumeLR <= 0x10001000
+ void setVolumeLR(uint32_t volumeLR) {
+ mCblk->mVolumeLR = volumeLR;
+ }
+
+ void setSampleRate(uint32_t sampleRate) {
+ mCblk->mSampleRate = sampleRate;
+ }
+
+ // called by:
+ // PlaybackThread::OutputTrack::write
+ // AudioTrack::createTrack_l
+ // AudioTrack::releaseBuffer
+ // AudioTrack::reload
+ // AudioTrack::restoreTrack_l (2 places)
+ size_t stepUser(size_t stepCount) {
+ return mCblk->stepUser(stepCount, mFrameCount, true /*isOut*/);
+ }
+
+ // called by AudioTrack::obtainBuffer and AudioTrack::processBuffer
+ size_t framesAvailable() {
+ return mCblk->framesAvailable(mFrameCount, true /*isOut*/);
+ }
+
+ // called by AudioTrack::obtainBuffer and PlaybackThread::OutputTrack::obtainBuffer
+ // FIXME remove this API since it assumes a lock that should be invisible to caller
+ size_t framesAvailable_l() {
+ return mCblk->framesAvailable_l(mFrameCount, true /*isOut*/);
+ }
+
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy used by AudioRecord client
+class AudioRecordClientProxy : public ClientProxy {
+public:
+ AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : ClientProxy(cblk, buffers, frameCount, frameSize) { }
+ ~AudioRecordClientProxy() { }
+
+ // called by AudioRecord::releaseBuffer
+ size_t stepUser(size_t stepCount) {
+ return mCblk->stepUser(stepCount, mFrameCount, false /*isOut*/);
+ }
+
+ // called by AudioRecord::processBuffer
+ size_t framesAvailable() {
+ return mCblk->framesAvailable(mFrameCount, false /*isOut*/);
+ }
+
+ // called by AudioRecord::obtainBuffer
+ size_t framesReady() {
+ return mCblk->framesReady(false /*isOut*/);
+ }
+
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy used by AudioFlinger server
+class ServerProxy : public Proxy {
+public:
+ ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut)
+ : Proxy(cblk, buffers, frameCount, frameSize), mIsOut(isOut) { }
+ virtual ~ServerProxy() { }
+
+ // for AudioTrack and AudioRecord
+ bool step(size_t stepCount) { return mCblk->stepServer(stepCount, mFrameCount, mIsOut); }
+
+ // return value of these methods must be validated by the caller
+ uint32_t getSampleRate() const { return mCblk->mSampleRate; }
+ uint16_t getSendLevel_U4_12() const { return mCblk->mSendLevel; }
+ uint32_t getVolumeLR() const { return mCblk->mVolumeLR; }
+
+ // for AudioTrack only
+ size_t framesReady() {
+ ALOG_ASSERT(mIsOut);
+ return mCblk->framesReady(true);
+ }
+
+ // for AudioRecord only, called by RecordThread::RecordTrack::getNextBuffer
+ // FIXME remove this API since it assumes a lock that should be invisible to caller
+ size_t framesAvailableIn_l() {
+ ALOG_ASSERT(!mIsOut);
+ return mCblk->framesAvailable_l(mFrameCount, false);
+ }
+
+private:
+ const bool mIsOut; // true for AudioTrack, false for AudioRecord
+
+};
// ----------------------------------------------------------------------------
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index c2ef68c..8eb1656 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -75,7 +75,8 @@
AudioRecord::AudioRecord()
: mStatus(NO_INIT), mSessionId(0),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
}
@@ -90,7 +91,9 @@
int notificationFrames,
int sessionId)
: mStatus(NO_INIT), mSessionId(0),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
mStatus = set(inputSource, sampleRate, format, channelMask,
frameCount, cbf, user, notificationFrames, sessionId);
@@ -112,6 +115,7 @@
IPCThreadState::self()->flushCommands();
AudioSystem::releaseAudioSessionId(mSessionId);
}
+ delete mProxy;
}
status_t AudioRecord::set(
@@ -149,6 +153,8 @@
if (sampleRate == 0) {
sampleRate = DEFAULT_SAMPLE_RATE;
}
+ mSampleRate = sampleRate;
+
// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
@@ -166,6 +172,12 @@
uint32_t channelCount = popcount(channelMask);
mChannelCount = channelCount;
+ if (audio_is_linear_pcm(mFormat)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ }
+
if (sessionId == 0 ) {
mSessionId = AudioSystem::newAudioSessionId();
} else {
@@ -218,12 +230,6 @@
// Update buffer size in case it has been limited by AudioFlinger during track creation
mFrameCount = mCblk->frameCount_;
- if (audio_is_linear_pcm(mFormat)) {
- mFrameSize = channelCount * audio_bytes_per_sample(format);
- } else {
- mFrameSize = sizeof(uint8_t);
- }
-
mActive = false;
mCbf = cbf;
mNotificationFrames = notificationFrames;
@@ -360,7 +366,7 @@
uint32_t AudioRecord::getSampleRate() const
{
- return mCblk->sampleRate;
+ return mSampleRate;
}
status_t AudioRecord::setMarkerPosition(uint32_t marker)
@@ -473,11 +479,18 @@
mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
cblk->waitTimeMs = 0;
+
+ // update proxy
+ delete mProxy;
+ mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize);
+
return NO_ERROR;
}
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
bool active;
status_t result = NO_ERROR;
@@ -488,7 +501,7 @@
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
- uint32_t framesReady = cblk->framesReadyIn();
+ size_t framesReady = mProxy->framesReady();
if (framesReady == 0) {
cblk->lock.lock();
@@ -551,7 +564,7 @@
}
// read the server count again
start_loop_here:
- framesReady = cblk->framesReadyIn();
+ framesReady = mProxy->framesReady();
}
cblk->lock.unlock();
}
@@ -573,15 +586,17 @@
audioBuffer->frameCount = framesReq;
audioBuffer->size = framesReq * mFrameSize;
- audioBuffer->raw = cblk->buffer(mBuffers, mFrameSize, u);
+ audioBuffer->raw = mProxy->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
- mCblk->stepUserIn(audioBuffer->frameCount, mFrameCount);
+ (void) mProxy->stepUser(audioBuffer->frameCount);
}
audio_io_handle_t AudioRecord::getInput() const
@@ -594,7 +609,7 @@
audio_io_handle_t AudioRecord::getInput_l()
{
mInput = AudioSystem::getInput(mInputSource,
- mCblk->sampleRate,
+ mSampleRate,
mFormat,
mChannelMask,
mSessionId);
@@ -745,7 +760,7 @@
// Manage overrun callback
- if (active && (cblk->framesAvailableIn(mFrameCount) == 0)) {
+ if (active && (mProxy->framesAvailable() == 0)) {
// The value of active is stale, but we are almost sure to be active here because
// otherwise we would have exited when obtainBuffer returned STOPPED earlier.
ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
@@ -781,7 +796,7 @@
// if the new IAudioRecord is created, openRecord_l() will modify the
// following member variables: mAudioRecord, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioRecord and IMemory
- result = openRecord_l(cblk->sampleRate, mFormat, mFrameCount, getInput_l());
+ result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l());
if (result == NO_ERROR) {
newCblk = mCblk;
// callback thread or sync event hasn't changed
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 1d87ff8..86a5579 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -97,7 +97,8 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
}
@@ -115,7 +116,8 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
@@ -136,7 +138,8 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
if (sharedBuffer == 0) {
ALOGE("sharedBuffer must be non-0");
@@ -166,6 +169,7 @@
IPCThreadState::self()->flushCommands();
AudioSystem::releaseAudioSessionId(mSessionId);
}
+ delete mProxy;
}
status_t AudioTrack::set(
@@ -212,6 +216,7 @@
}
sampleRate = afSampleRate;
}
+ mSampleRate = sampleRate;
// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {
@@ -252,6 +257,14 @@
uint32_t channelCount = popcount(channelMask);
mChannelCount = channelCount;
+ if (audio_is_linear_pcm(format)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ mFrameSizeAF = channelCount * sizeof(int16_t);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ mFrameSizeAF = sizeof(uint8_t);
+ }
+
audio_io_handle_t output = AudioSystem::getOutput(
streamType,
sampleRate, format, channelMask,
@@ -300,14 +313,6 @@
mStreamType = streamType;
mFormat = format;
- if (audio_is_linear_pcm(format)) {
- mFrameSize = channelCount * audio_bytes_per_sample(format);
- mFrameSizeAF = channelCount * sizeof(int16_t);
- } else {
- mFrameSize = sizeof(uint8_t);
- mFrameSizeAF = sizeof(uint8_t);
- }
-
mSharedBuffer = sharedBuffer;
mActive = false;
mUserData = user;
@@ -460,6 +465,11 @@
status_t AudioTrack::setVolume(float left, float right)
{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ ALOG_ASSERT(mProxy != NULL);
+
if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) {
return BAD_VALUE;
}
@@ -468,7 +478,7 @@
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
- mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
+ mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
return NO_ERROR;
}
@@ -481,14 +491,19 @@
status_t AudioTrack::setAuxEffectSendLevel(float level)
{
ALOGV("setAuxEffectSendLevel(%f)", level);
+
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ ALOG_ASSERT(mProxy != NULL);
+
if (level < 0.0f || level > 1.0f) {
return BAD_VALUE;
}
AutoMutex lock(mLock);
mSendLevel = level;
-
- mCblk->setSendLevel(level);
+ mProxy->setSendLevel(level);
return NO_ERROR;
}
@@ -517,7 +532,9 @@
}
AutoMutex lock(mLock);
- mCblk->sampleRate = rate;
+ mSampleRate = rate;
+ mProxy->setSampleRate(rate);
+
return NO_ERROR;
}
@@ -528,7 +545,7 @@
}
AutoMutex lock(mLock);
- return mCblk->sampleRate;
+ return mSampleRate;
}
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
@@ -665,6 +682,11 @@
status_t AudioTrack::reload()
{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ ALOG_ASSERT(mProxy != NULL);
+
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
}
@@ -677,8 +699,7 @@
flush_l();
- audio_track_cblk_t* cblk = mCblk;
- cblk->stepUserOut(mFrameCount, mFrameCount);
+ (void) mProxy->stepUser(mFrameCount);
return NO_ERROR;
}
@@ -693,7 +714,7 @@
audio_io_handle_t AudioTrack::getOutput_l()
{
return AudioSystem::getOutput(mStreamType,
- mCblk->sampleRate, mFormat, mChannelMask, mFlags);
+ mSampleRate, mFormat, mChannelMask, mFlags);
}
status_t AudioTrack::attachAuxEffect(int effectId)
@@ -890,13 +911,8 @@
mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
} else {
mBuffers = sharedBuffer->pointer();
- // Force buffer full condition as data is already present in shared memory
- cblk->stepUserOut(frameCount, frameCount);
}
- cblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) |
- uint16_t(mVolume[LEFT] * 0x1000));
- cblk->setSendLevel(mSendLevel);
mAudioTrack->attachAuxEffect(mAuxEffectId);
cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
cblk->waitTimeMs = 0;
@@ -909,11 +925,26 @@
if (frameCount > mReqFrameCount) {
mReqFrameCount = frameCount;
}
+
+ // update proxy
+ delete mProxy;
+ mProxy = new AudioTrackClientProxy(cblk, mBuffers, frameCount, mFrameSizeAF);
+ mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) |
+ uint16_t(mVolume[LEFT] * 0x1000));
+ mProxy->setSendLevel(mSendLevel);
+ mProxy->setSampleRate(mSampleRate);
+ if (sharedBuffer != 0) {
+ // Force buffer full condition as data is already present in shared memory
+ mProxy->stepUser(frameCount);
+ }
+
return NO_ERROR;
}
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
bool active;
status_t result = NO_ERROR;
@@ -924,7 +955,7 @@
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
- uint32_t framesAvail = cblk->framesAvailableOut(mFrameCount);
+ size_t framesAvail = mProxy->framesAvailable();
cblk->lock.lock();
if (cblk->flags & CBLK_INVALID) {
@@ -1000,7 +1031,7 @@
}
// read the server count again
start_loop_here:
- framesAvail = cblk->framesAvailableOut_l(mFrameCount);
+ framesAvail = mProxy->framesAvailable_l();
}
cblk->lock.unlock();
}
@@ -1020,16 +1051,18 @@
audioBuffer->frameCount = framesReq;
audioBuffer->size = framesReq * mFrameSizeAF;
- audioBuffer->raw = cblk->buffer(mBuffers, mFrameSizeAF, u);
+ audioBuffer->raw = mProxy->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
audio_track_cblk_t* cblk = mCblk;
- cblk->stepUserOut(audioBuffer->frameCount, mFrameCount);
+ (void) mProxy->stepUser(audioBuffer->frameCount);
if (audioBuffer->frameCount > 0) {
// restart track if it was disabled by audioflinger due to previous underrun
if (mActive && (cblk->flags & CBLK_DISABLED)) {
@@ -1199,7 +1232,7 @@
// so all cblk references might still refer to old shared memory, but that should be benign
// Manage underrun callback
- if (active && (cblk->framesAvailableOut(mFrameCount) == mFrameCount)) {
+ if (active && (mProxy->framesAvailable() == mFrameCount)) {
ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) {
mCbf(EVENT_UNDERRUN, mUserData, 0);
@@ -1346,7 +1379,7 @@
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory
result = createTrack_l(mStreamType,
- cblk->sampleRate,
+ mSampleRate,
mFormat,
mReqFrameCount, // so that frame count never goes down
mFlags,
@@ -1365,12 +1398,12 @@
// restore loop: this is not guaranteed to succeed if new frame count is not
// compatible with loop length
setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount);
+ size_t frames = 0;
if (!fromStart) {
newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
// Make sure that a client relying on callback events indicating underrun or
// the actual amount of audio frames played (e.g SoundPool) receives them.
if (mSharedBuffer == 0) {
- uint32_t frames = 0;
if (user > server) {
frames = ((user - server) > mFrameCount) ?
mFrameCount : (user - server);
@@ -1378,13 +1411,15 @@
}
// restart playback even if buffer is not completely filled.
android_atomic_or(CBLK_FORCEREADY, &newCblk->flags);
- // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to
- // the client
- newCblk->stepUserOut(frames, mFrameCount);
}
}
if (mSharedBuffer != 0) {
- newCblk->stepUserOut(mFrameCount, mFrameCount);
+ frames = mFrameCount;
+ }
+ if (frames > 0) {
+ // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to
+ // the client
+ mProxy->stepUser(frames);
}
if (mActive) {
result = mAudioTrack->start();
@@ -1416,7 +1451,6 @@
char buffer[SIZE];
String8 result;
- audio_track_cblk_t* cblk = mCblk;
result.append(" AudioTrack::dump\n");
snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType,
mVolume[0], mVolume[1]);
@@ -1424,8 +1458,7 @@
snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat,
mChannelCount, mFrameCount);
result.append(buffer);
- snprintf(buffer, 255, " sample rate(%u), status(%d)\n",
- (cblk == 0) ? 0 : cblk->sampleRate, mStatus);
+ snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
result.append(buffer);
snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency);
result.append(buffer);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index bee13c8..13d47c9 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -26,7 +26,7 @@
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
userBase(0), serverBase(0), frameCount_(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
- mSendLevel(0), flags(0)
+ mSampleRate(0), mSendLevel(0), flags(0)
{
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6d3f0a1..64a9871 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -61,6 +61,7 @@
class AudioBuffer;
class AudioResampler;
class FastMixer;
+class ServerProxy;
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index aaa5333..adec938 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -107,7 +107,6 @@
bool isInvalid() const { return mIsInvalid; }
virtual bool isTimedTrack() const { return false; }
bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
- virtual bool isOut() const;
protected:
@@ -277,5 +276,5 @@
AudioBufferProvider::Buffer mOutBuffer;
bool mActive;
DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
- void* mBuffers; // starting address of buffers in plain memory
+ AudioTrackClientProxy* mClientProxy;
}; // end of OutputTrack
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index fe681d7..6c0d1d3 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -45,8 +45,6 @@
static void appendDumpHeader(String8& result);
void dump(char* buffer, size_t size);
- virtual bool isOut() const;
-
private:
friend class AudioFlinger; // for mState
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d2b2931..82acd3a 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2643,7 +2643,8 @@
// read original volumes with volume control
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = masterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
+ ServerProxy *proxy = track->mServerProxy;
+ uint32_t vlr = proxy->getVolumeLR();
vl = vlr & 0xFFFF;
vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
@@ -2661,7 +2662,7 @@
// assuming master volume and stream type volume each go up to 1.0,
// vl and vr are now in 8.24 format
- uint16_t sendLevel = cblk->getSendLevel_U4_12();
+ uint16_t sendLevel = proxy->getSendLevel_U4_12();
// send level comes from shared memory and so may be corrupt
if (sendLevel > MAX_GAIN_INT) {
ALOGV("Track send level out of range: %04X", sendLevel);
@@ -2713,11 +2714,19 @@
name,
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
+ // limit track sample rate to 2 x output sample rate, which changes at re-configuration
+ uint32_t maxSampleRate = mSampleRate * 2;
+ uint32_t reqSampleRate = track->mServerProxy->getSampleRate();
+ if (reqSampleRate == 0) {
+ reqSampleRate = mSampleRate;
+ } else if (reqSampleRate > maxSampleRate) {
+ reqSampleRate = maxSampleRate;
+ }
mAudioMixer->setParameter(
name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
- (void *)(cblk->sampleRate));
+ (void *)reqSampleRate);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
@@ -2990,10 +2999,6 @@
break;
}
mTracks[i]->mName = name;
- // limit track sample rate to 2 x new output sample rate
- if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) {
- mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
- }
}
sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
}
@@ -3142,7 +3147,7 @@
} else {
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = mMasterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
+ uint32_t vlr = track->mServerProxy->getVolumeLR();
float v_clamped = v * (vlr & 0xFFFF);
if (v_clamped > MAX_GAIN) {
v_clamped = MAX_GAIN;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 17de49b..e0bd97a 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -44,7 +44,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ bool isOut);
virtual ~TrackBase();
virtual status_t start(AudioSystem::sync_event_t event,
@@ -108,7 +109,8 @@
bool step(); // mStepCount is an implicit input
void reset();
- virtual bool isOut() const = 0; // true for Track and TimedTrack, false for RecordTrack,
+ bool isOut() const { return mIsOut; }
+ // true for Track and TimedTrack, false for RecordTrack,
// this could be a track type if needed later
const wp<ThreadBase> mThread;
@@ -116,6 +118,7 @@
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
void* mBuffer; // start of track buffer, typically in shared memory
+ // except for OutputTrack when it is in local memory
void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
// is based on mChannelCount and 16-bit samples
uint32_t mStepCount; // saves AudioBufferProvider::Buffer::frameCount as of
@@ -136,4 +139,6 @@
bool mStepServerFailed;
const int mSessionId;
Vector < sp<SyncEvent> >mSyncEvents;
+ const bool mIsOut;
+ ServerProxy* mServerProxy;
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 9b611d2..c5f0ed7 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -62,7 +62,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId)
+ int sessionId,
+ bool isOut)
: RefBase(),
mThread(thread),
mClient(client),
@@ -79,7 +80,9 @@
mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
mFrameCount(frameCount),
mStepServerFailed(false),
- mSessionId(sessionId)
+ mSessionId(sessionId),
+ mIsOut(isOut),
+ mServerProxy(NULL)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -105,7 +108,8 @@
return;
}
} else {
- mCblk = (audio_track_cblk_t *)(new uint8_t[size]);
+ // this syntax avoids calling the audio_track_cblk_t constructor twice
+ mCblk = (audio_track_cblk_t *) new uint8_t[size];
// assume mCblk != NULL
}
@@ -114,7 +118,6 @@
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount_ = frameCount;
- mCblk->sampleRate = sampleRate;
// uncomment the following lines to quickly test 32-bit wraparound
// mCblk->user = 0xffff0000;
// mCblk->server = 0xffff0000;
@@ -130,11 +133,14 @@
mBuffer = sharedBuffer->pointer();
}
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
+ mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
}
}
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
+ // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
+ delete mServerProxy;
if (mCblk != NULL) {
if (mClient == 0) {
delete mCblk;
@@ -166,10 +172,7 @@
}
bool AudioFlinger::ThreadBase::TrackBase::step() {
- bool result;
- audio_track_cblk_t* cblk = this->cblk();
-
- result = cblk->stepServer(mStepCount, mFrameCount, isOut());
+ bool result = mServerProxy->step(mStepCount);
if (!result) {
ALOGV("stepServer failed acquiring cblk mutex");
mStepServerFailed = true;
@@ -189,7 +192,7 @@
}
uint32_t AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
- return mCblk->sampleRate;
+ return mServerProxy->getSampleRate();
}
void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
@@ -310,7 +313,7 @@
int sessionId,
IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId),
+ sessionId, true /*isOut*/),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
@@ -399,7 +402,7 @@
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- uint32_t vlr = mCblk->getVolumeLR();
+ uint32_t vlr = mServerProxy->getVolumeLR();
if (isFastTrack()) {
sprintf(buffer, " F %2d", mFastIndex);
} else {
@@ -468,7 +471,7 @@
mFrameCount,
stateChar,
mFillingUpStatus,
- mCblk->sampleRate,
+ mServerProxy->getSampleRate(),
20.0 * log10((vlr & 0xFFFF) / 4096.0),
20.0 * log10((vlr >> 16) / 4096.0),
mCblk->server,
@@ -503,7 +506,7 @@
}
// FIXME Same as above
- framesReady = cblk->framesReadyOut();
+ framesReady = mServerProxy->framesReady();
if (CC_LIKELY(framesReady)) {
uint32_t s = cblk->server;
@@ -538,7 +541,7 @@
// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
- return mCblk->framesReadyOut();
+ return mServerProxy->framesReady();
}
// Don't call for fast tracks; the framesReady() could result in priority inversion
@@ -795,7 +798,7 @@
{
// called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
- uint32_t vlr = mCblk->getVolumeLR();
+ uint32_t vlr = mServerProxy->getVolumeLR();
uint32_t vl = vlr & 0xFFFF;
uint32_t vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
@@ -830,11 +833,6 @@
return NO_ERROR;
}
-bool AudioFlinger::PlaybackThread::Track::isOut() const
-{
- return true;
-}
-
void AudioFlinger::PlaybackThread::Track::invalidate()
{
// FIXME should use proxy
@@ -1369,17 +1367,19 @@
size_t frameCount)
: Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
NULL, 0, IAudioFlinger::TRACK_DEFAULT),
- mActive(false), mSourceThread(sourceThread), mBuffers(NULL)
+ mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
if (mCblk != NULL) {
- mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t);
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
- ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mBuffers %p, " \
- "mCblk->frameCount %d, mCblk->sampleRate %u, mChannelMask 0x%08x mBufferEnd %p",
- mCblk, mBuffer, mBuffers,
- mCblk->frameCount, mCblk->sampleRate, mChannelMask, mBufferEnd);
+ ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
+ "mCblk->frameCount_ %u, mChannelMask 0x%08x mBufferEnd %p",
+ mCblk, mBuffer,
+ mCblk->frameCount_, mChannelMask, mBufferEnd);
+ // since client and server are in the same process,
+ // the buffer has the same virtual address on both sides
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
} else {
ALOGW("Error creating output track on thread %p", playbackThread);
}
@@ -1388,6 +1388,8 @@
AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack()
{
clearBufferQueue();
+ delete mClientProxy;
+ // superclass destructor will now delete the server proxy and shared memory both refer to
}
status_t AudioFlinger::PlaybackThread::OutputTrack::start(AudioSystem::sync_event_t event,
@@ -1475,7 +1477,7 @@
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
pInBuffer->frameCount;
memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
- mCblk->stepUserOut(outFrames, mFrameCount);
+ mClientProxy->stepUser(outFrames);
pInBuffer->frameCount -= outFrames;
pInBuffer->i16 += outFrames * channelCount;
mOutBuffer.frameCount -= outFrames;
@@ -1538,40 +1540,29 @@
status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(
AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
{
- int active;
- status_t result;
audio_track_cblk_t* cblk = mCblk;
uint32_t framesReq = buffer->frameCount;
ALOGVV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
buffer->frameCount = 0;
- uint32_t framesAvail = cblk->framesAvailableOut(mFrameCount);
-
-
- if (framesAvail == 0) {
+ size_t framesAvail;
+ {
Mutex::Autolock _l(cblk->lock);
- goto start_loop_here;
- while (framesAvail == 0) {
- active = mActive;
- if (CC_UNLIKELY(!active)) {
+
+ // read the server count again
+ while (!(framesAvail = mClientProxy->framesAvailable_l())) {
+ if (CC_UNLIKELY(!mActive)) {
ALOGV("Not active and NO_MORE_BUFFERS");
return NO_MORE_BUFFERS;
}
- result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+ status_t result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
if (result != NO_ERROR) {
return NO_MORE_BUFFERS;
}
- // read the server count again
- start_loop_here:
- framesAvail = cblk->framesAvailableOut_l(mFrameCount);
}
}
-// if (framesAvail < framesReq) {
-// return NO_MORE_BUFFERS;
-// }
-
if (framesReq > framesAvail) {
framesReq = framesAvail;
}
@@ -1584,7 +1575,7 @@
}
buffer->frameCount = framesReq;
- buffer->raw = cblk->buffer(mBuffers, mFrameSize, u);
+ buffer->raw = mClientProxy->buffer(u);
return NO_ERROR;
}
@@ -1655,7 +1646,7 @@
size_t frameCount,
int sessionId)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId),
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
mOverflow(false)
{
ALOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
@@ -1684,7 +1675,7 @@
}
// FIXME lock is not actually held, so overrun is possible
- framesAvail = cblk->framesAvailableIn_l(mFrameCount);
+ framesAvail = mServerProxy->framesAvailableIn_l();
if (CC_LIKELY(framesAvail)) {
uint32_t s = cblk->server;
@@ -1761,27 +1752,21 @@
/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
{
- result.append(" Clien Fmt Chn mask Session Step S SRate Serv User FrameCount\n");
+ result.append(" Clien Fmt Chn mask Session Step S Serv User FrameCount\n");
}
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x %05d\n",
+ snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %08x %08x %05d\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mFormat,
mChannelMask,
mSessionId,
mStepCount,
mState,
- mCblk->sampleRate,
mCblk->server,
mCblk->user,
mFrameCount);
}
-bool AudioFlinger::RecordThread::RecordTrack::isOut() const
-{
- return false;
-}
-
}; // namespace android