Merge "MediaPlayer: overhaul buffering monitor scheme."
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fe4611c..38adb03 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -305,6 +305,11 @@
*/
uint32_t latency() const { return mLatency; }
+ /* Returns the number of application-level buffer underruns
+ * since the AudioTrack was created.
+ */
+ uint32_t getUnderrunCount() const;
+
/* getters, see constructors and set() */
audio_stream_type_t streamType() const;
@@ -320,6 +325,25 @@
uint32_t channelCount() const { return mChannelCount; }
size_t frameCount() const { return mFrameCount; }
+ /* Return effective size of audio buffer that an application writes to
+ * or a negative error if the track is uninitialized.
+ */
+ ssize_t getBufferSizeInFrames();
+
+ /* Set the effective size of audio buffer that an application writes to.
+ * This is used to determine the amount of available room in the buffer,
+ * which determines when a write will block.
+ * This allows an application to raise and lower the audio latency.
+ * The requested size may be adjusted so that it is
+ * greater or equal to the absolute minimum and
+ * less than or equal to the getBufferCapacityInFrames().
+ * It may also be adjusted slightly for internal reasons.
+ *
+ * Return the final size or a negative error if the track is unitialized
+ * or does not support variable sizes.
+ */
+ ssize_t setBufferSizeInFrames(size_t size);
+
/* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
@@ -784,6 +808,8 @@
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreTrack_l(const char *from);
+ uint32_t getUnderrunCount_l() const;
+
bool isOffloaded() const;
bool isDirect() const;
bool isOffloadedOrDirect() const;
@@ -811,14 +837,18 @@
audio_io_handle_t mOutput; // returned by AudioSystem::getOutput()
sp<AudioTrackThread> mAudioTrackThread;
+ bool mThreadCanCallJava;
float mVolume[2];
float mSendLevel;
mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it
uint32_t mOriginalSampleRate;
AudioPlaybackRate mPlaybackRate;
- size_t mFrameCount; // corresponds to current IAudioTrack, value is
- // reported back by AudioFlinger to the client
+
+ // Corresponds to current IAudioTrack, value is reported back by AudioFlinger to the client.
+ // This allocated buffer size is maintained by the proxy.
+ size_t mFrameCount; // maximum size of buffer
+
size_t mReqFrameCount; // frame count to request the first or next time
// a new IAudioTrack is needed, non-decreasing
@@ -911,6 +941,8 @@
bool mRetrogradeMotionReported; // reduce log spam
AudioTimestamp mPreviousTimestamp; // used to detect retrograde motion
+ uint32_t mUnderrunCountOffset; // updated when restoring tracks
+
audio_output_flags_t mFlags;
// const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
// mLock must be held to read or write those bits reliably.
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index e458f3c..770f007 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -59,7 +59,8 @@
volatile int32_t mRear; // written by producer (output: client, input: server)
volatile int32_t mFlush; // incremented by client to indicate a request to flush;
// server notices and discards all data between mFront and mRear
- volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame
+ volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame
+ volatile uint32_t mUnderrunCount; // server increments for each underrun occurrence
};
// Represents a single state of an AudioTrack that was created in static mode (shared memory buffer
@@ -174,8 +175,6 @@
volatile int32_t mFlags; // combinations of CBLK_*
- // Cache line boundary (32 bytes)
-
public:
union {
AudioTrackSharedStreaming mStreaming;
@@ -204,6 +203,8 @@
size_t mNonContig; // number of additional non-contiguous frames available
};
+ size_t frameCount() const { return mFrameCount; }
+
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.
@@ -285,7 +286,7 @@
return mEpoch + mCblk->mServer;
}
- void setEpoch(const Modulo<uint32_t> &epoch) {
+ void setEpoch(const Modulo<uint32_t> &epoch) {
mEpoch = epoch;
}
@@ -305,6 +306,15 @@
return mEpoch;
}
+ size_t getBufferSizeInFrames() const { return mBufferSizeInFrames; }
+ // See documentation for AudioTrack.setBufferSizeInFrames()
+ size_t setBufferSizeInFrames(size_t requestedSize);
+
+protected:
+ // This is set by AudioTrack.setBufferSizeInFrames().
+ // A write will not fill the buffer above this limit.
+ size_t mBufferSizeInFrames; // effective size of the buffer
+
private:
Modulo<uint32_t> mEpoch;
};
@@ -347,6 +357,9 @@
virtual uint32_t getUnderrunFrames() const {
return mCblk->u.mStreaming.mUnderrunFrames;
}
+ virtual uint32_t getUnderrunCount() const {
+ return mCblk->u.mStreaming.mUnderrunCount;
+ }
bool clearStreamEndDone(); // and return previous value
@@ -471,7 +484,8 @@
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
: ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
- mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
+ mPlaybackRateObserver(&cblk->mPlaybackRateQueue),
+ mUnderrunCount(0), mUnderrunning(false) {
mCblk->mSampleRate = sampleRate;
mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
}
@@ -514,6 +528,10 @@
private:
AudioPlaybackRate mPlaybackRate; // last observed playback rate
PlaybackRateQueue::Observer mPlaybackRateObserver;
+
+ // The server keeps a copy here where it is safe from the client.
+ uint32_t mUnderrunCount; // echoed to mCblk
+ bool mUnderrunning; // used to detect edge of underrun
};
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 5e14940..a13d53a 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -288,6 +288,8 @@
streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
sessionId, transferType, uid, pid);
+ mThreadCanCallJava = threadCanCallJava;
+
switch (transferType) {
case TRANSFER_DEFAULT:
if (sharedBuffer != 0) {
@@ -356,6 +358,9 @@
if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
}
+ if ((mAttributes.flags & AUDIO_FLAG_LOW_LATENCY) != 0) {
+ flags = (audio_output_flags_t) (flags | AUDIO_OUTPUT_FLAG_FAST);
+ }
}
// these below should probably come from the audioFlinger too...
@@ -493,6 +498,7 @@
mPreviousTimestampValid = false;
mTimestampStartupGlitchReported = false;
mRetrogradeMotionReported = false;
+ mUnderrunCountOffset = 0;
return NO_ERROR;
}
@@ -855,6 +861,31 @@
return mPlaybackRate;
}
+ssize_t AudioTrack::getBufferSizeInFrames()
+{
+ AutoMutex lock(mLock);
+ if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) {
+ return NO_INIT;
+ }
+ return mProxy->getBufferSizeInFrames();
+}
+
+ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
+{
+ AutoMutex lock(mLock);
+ if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) {
+ return NO_INIT;
+ }
+ // Reject if timed track or compressed audio.
+ if (mIsTimed || !audio_is_linear_pcm(mFormat)) {
+ return INVALID_OPERATION;
+ }
+ // TODO also need to inform the server side (through mAudioTrack) that
+ // the buffer count is reduced, otherwise the track may never start
+ // because the server thinks it is never filled.
+ return mProxy->setBufferSizeInFrames(bufferSizeInFrames);
+}
+
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -1170,20 +1201,26 @@
}
// Client decides whether the track is TIMED (see below), but can only express a preference
// for FAST. Server will perform additional tests.
- if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) && !((
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ bool useCaseAllowed =
// either of these use cases:
// use case 1: shared buffer
(mSharedBuffer != 0) ||
// use case 2: callback transfer mode
(mTransfer == TRANSFER_CALLBACK) ||
// use case 3: obtain/release mode
- (mTransfer == TRANSFER_OBTAIN)) &&
- // matching sample rate
- (mSampleRate == mAfSampleRate))) {
- ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, output %u Hz",
+ (mTransfer == TRANSFER_OBTAIN) ||
+ // use case 4: synchronous write
+ ((mTransfer == TRANSFER_SYNC) && mThreadCanCallJava);
+ // sample rates must also match
+ bool fastAllowed = useCaseAllowed && (mSampleRate == mAfSampleRate);
+ if (!fastAllowed) {
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client; transfer %d,"
+ "track %u Hz, output %u Hz",
mTransfer, mSampleRate, mAfSampleRate);
- // once denied, do not request again if IAudioTrack is re-created
- mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
+ // once denied, do not request again if IAudioTrack is re-created
+ mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
+ }
}
// The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
@@ -1254,7 +1291,7 @@
pid_t tid = -1;
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
trackFlags |= IAudioFlinger::TRACK_FAST;
- if (mAudioTrackThread != 0) {
+ if (mAudioTrackThread != 0 && !mThreadCanCallJava) {
tid = mAudioTrackThread->getTid();
}
}
@@ -1328,7 +1365,9 @@
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
if (trackFlags & IAudioFlinger::TRACK_FAST) {
ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu", frameCount);
- mAwaitBoost = true;
+ if (!mThreadCanCallJava) {
+ mAwaitBoost = true;
+ }
} else {
ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu", frameCount);
// once denied, do not request again if IAudioTrack is re-created
@@ -2112,6 +2151,9 @@
return DEAD_OBJECT;
}
+ // Save so we can return count since creation.
+ mUnderrunCountOffset = getUnderrunCount_l();
+
// save the old static buffer position
size_t bufferPosition = 0;
int loopCount = 0;
@@ -2427,6 +2469,17 @@
return NO_ERROR;
}
+uint32_t AudioTrack::getUnderrunCount() const
+{
+ AutoMutex lock(mLock);
+ return getUnderrunCount_l();
+}
+
+uint32_t AudioTrack::getUnderrunCount_l() const
+{
+ return mProxy->getUnderrunCount() + mUnderrunCountOffset;
+}
+
uint32_t AudioTrack::getUnderrunFrames() const
{
AutoMutex lock(mLock);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 9fad500..2742db0 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -66,7 +66,9 @@
ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool isOut, bool clientInServer)
- : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mEpoch(0)
+ : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer)
+ , mBufferSizeInFrames(frameCount)
+ , mEpoch(0)
{
}
@@ -151,6 +153,7 @@
rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
front = cblk->u.mStreaming.mFront;
}
+ // write to rear, read from front
ssize_t filled = rear - front;
// pipe should not be overfull
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
@@ -166,11 +169,17 @@
cblk->u.mStreaming.mFront = rear;
(void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
}
- // don't allow filling pipe beyond the nominal size
- size_t avail = mIsOut ? mFrameCount - filled : filled;
- if (avail > 0) {
+ // Don't allow filling pipe beyond the user settable size.
+ // The calculation for avail can go negative if the buffer size
+ // is suddenly dropped below the amount already in the buffer.
+ // So use a signed calculation to prevent a numeric overflow abort.
+ ssize_t adjustableSize = (ssize_t) mBufferSizeInFrames;
+ ssize_t avail = (mIsOut) ? adjustableSize - filled : filled;
+ if (avail < 0) {
+ avail = 0;
+ } else if (avail > 0) {
// 'avail' may be non-contiguous, so return only the first contiguous chunk
- size_t part1;
+ ssize_t part1;
if (mIsOut) {
rear &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - rear;
@@ -181,10 +190,10 @@
if (part1 > avail) {
part1 = avail;
}
- if (part1 > buffer->mFrameCount) {
+ if (part1 > (ssize_t) buffer->mFrameCount) {
part1 = buffer->mFrameCount;
}
- buffer->mFrameCount = part1;
+ buffer->mFrameCount = (size_t) part1;
buffer->mRaw = part1 > 0 ?
&((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
buffer->mNonContig = avail - part1;
@@ -347,6 +356,20 @@
(mFrameCountP2 - 1);
}
+size_t ClientProxy::setBufferSizeInFrames(size_t size)
+{
+ // TODO set minimum to 2X the fast mixer buffer size.
+ size_t minimum = 128 * 2; // arbitrary
+ size_t maximum = frameCount();
+ if (size < minimum) {
+ size = minimum;
+ } else if (size > maximum) {
+ size = maximum;
+ }
+ mBufferSizeInFrames = size;
+ return size;
+}
+
// ---------------------------------------------------------------------------
void AudioTrackClientProxy::flush()
@@ -781,10 +804,25 @@
void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
{
audio_track_cblk_t* cblk = mCblk;
- cblk->u.mStreaming.mUnderrunFrames += frameCount;
+ if (frameCount > 0) {
+ cblk->u.mStreaming.mUnderrunFrames += frameCount;
- // FIXME also wake futex so that underrun is noticed more quickly
- (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
+ if (!mUnderrunning) { // start of underrun?
+ mUnderrunCount++;
+ cblk->u.mStreaming.mUnderrunCount = mUnderrunCount;
+ mUnderrunning = true;
+ ALOGV("tallyUnderrunFrames(%3u) at uf = %u, bump mUnderrunCount = %u",
+ frameCount, cblk->u.mStreaming.mUnderrunFrames, mUnderrunCount);
+ }
+
+ // FIXME also wake futex so that underrun is noticed more quickly
+ (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
+ } else {
+ ALOGV_IF(mUnderrunning,
+ "tallyUnderrunFrames(%3u) at uf = %u, underrun finished",
+ frameCount, cblk->u.mStreaming.mUnderrunFrames);
+ mUnderrunning = false; // so we can detect the next edge
+ }
}
AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate()
@@ -1010,7 +1048,7 @@
buffer->mNonContig = 0;
}
-void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount __unused)
+void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
{
// Unlike AudioTrackServerProxy::tallyUnderrunFrames() used for streaming tracks,
// we don't have a location to count underrun frames. The underrun frame counter
@@ -1018,7 +1056,9 @@
// possible for static buffer tracks other than at end of buffer, so this is not a loss.
// FIXME also wake futex so that underrun is noticed more quickly
- (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+ if (frameCount > 0) {
+ (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+ }
}
// ---------------------------------------------------------------------------
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 65166b0..0458554 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3788,6 +3788,8 @@
recentUnderruns > 0) {
// FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
+ } else {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
// This is similar to the state machine for normal tracks,
@@ -4157,7 +4159,10 @@
ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
track, framesReady, desiredFrames);
track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ } else {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
+
// clear effect chain input buffer if an active track underruns to avoid sending
// previous audio buffer again to effects
chain = getEffectChain_l(track->sessionId());
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b1638ea..c753afd 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -600,7 +600,10 @@
buffer->raw = buf.mRaw;
if (buf.mFrameCount == 0) {
mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ } else {
+ mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
+
return status;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
index e3ff9ae..a3536e5 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
@@ -294,8 +294,15 @@
if (strlen(devTag) != 0) {
audio_devices_t type;
if (DeviceConverter::fromString(devTag, type)) {
- sp<DeviceDescriptor> dev = new DeviceDescriptor(type);
- devices.add(dev);
+ uint32_t inBit = type & AUDIO_DEVICE_BIT_IN;
+ type &= ~AUDIO_DEVICE_BIT_IN;
+ while (type) {
+ audio_devices_t singleType =
+ inBit | (1 << (31 - __builtin_clz(type)));
+ type &= ~singleType;
+ sp<DeviceDescriptor> dev = new DeviceDescriptor(singleType);
+ devices.add(dev);
+ }
} else {
sp<DeviceDescriptor> deviceDesc =
declaredDevices.getDeviceFromTagName(String8(devTag));