Can now indicate the desired number of notifications (sub-buffers)
per fast track buffer when using the default buffer size.
There is no change for normal tracks, or fast tracks with a non-default buffer size.
Also fix related bugs:
Notification period was not set correctly when fast multiplier is not 1.
Incorrect warning when client adjusted notification frames.
Bug: 27819623
Bug: 28117362
Change-Id: Ifc2121e874f51718cd56ec04e4bd5f89f4963132
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fc7217a..1107142 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -186,8 +186,21 @@
* and inform of marker, position updates, etc.
* user: Context for use by the callback receiver.
* notificationFrames: The callback function is called each time notificationFrames PCM
- * frames have been consumed from track input buffer.
- * This is expressed in units of frames at the initial source sample rate.
+ * frames have been consumed from track input buffer by server.
+ * Zero means to use a default value, which is typically:
+ * - fast tracks: HAL buffer size, even if track frameCount is larger
+ * - normal tracks: 1/2 of track frameCount
+ * A positive value means that many frames at initial source sample rate.
+ * A negative value for this parameter specifies the negative of the
+ * requested number of notifications (sub-buffers) in the entire buffer.
+ * For fast tracks, the FastMixer will process one sub-buffer at a time.
+ * The size of each sub-buffer is determined by the HAL.
+ * To get "double buffering", for example, one should pass -2.
+ * The minimum number of sub-buffers is 1 (expressed as -1),
+ * and the maximum number of sub-buffers is 8 (expressed as -8).
+ * Negative is only permitted for fast tracks, and if frameCount is zero.
+ * TODO It is ugly to overload a parameter in this way depending on
+ * whether it is positive, negative, or zero. Consider splitting apart.
* sessionId: Specific session ID, or zero to use default.
* transferType: How data is transferred to AudioTrack.
* offloadInfo: If not NULL, provides offload parameters for
@@ -216,7 +229,7 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- uint32_t notificationFrames = 0,
+ int32_t notificationFrames = 0,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
@@ -246,7 +259,7 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- uint32_t notificationFrames = 0,
+ int32_t notificationFrames = 0,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
@@ -290,7 +303,7 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- uint32_t notificationFrames = 0,
+ int32_t notificationFrames = 0,
const sp<IMemory>& sharedBuffer = 0,
bool threadCanCallJava = false,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
@@ -335,6 +348,8 @@
uint32_t channelCount() const { return mChannelCount; }
size_t frameCount() const { return mFrameCount; }
+ // TODO consider notificationFrames() if needed
+
/* Return effective size of audio buffer that an application writes to
* or a negative error if the track is uninitialized.
*/
@@ -977,9 +992,16 @@
void* mUserData;
// for notification APIs
+
+ // next 2 fields are const after constructor or set()
uint32_t mNotificationFramesReq; // requested number of frames between each
// notification callback,
// at initial source sample rate
+ uint32_t mNotificationsPerBufferReq;
+ // requested number of notifications per buffer,
+ // currently only used for fast tracks with
+ // default track buffer size
+
uint32_t mNotificationFramesAct; // actual number of frames between each
// notification callback,
// at initial source sample rate
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index b9138f2..472f6c0 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -90,16 +90,24 @@
// TODO: Move to a common library
static size_t calculateMinFrameCount(
uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
- uint32_t sampleRate, float speed)
+ uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/)
{
// Ensure that buffer depth covers at least audio hardware latency
uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
+#if 0
+ // The notificationsPerBufferReq parameter is not yet used for non-fast tracks,
+ // but keeping the code here to make it easier to add later.
+ if (minBufCount < notificationsPerBufferReq) {
+ minBufCount = notificationsPerBufferReq;
+ }
+#endif
ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u "
- "sampleRate %u speed %f minBufCount: %u",
- afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
+ "sampleRate %u speed %f minBufCount: %u" /*" notificationsPerBufferReq %u"*/,
+ afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount
+ /*, notificationsPerBufferReq*/);
return minBufCount * sourceFramesNeededWithTimestretch(
sampleRate, afFrameCount, afSampleRate, speed);
}
@@ -144,7 +152,8 @@
// When called from createTrack, speed is 1.0f (normal speed).
// This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
- *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);
+ *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f
+ /*, 0 notificationsPerBufferReq*/);
// The formula above should always produce a non-zero value under normal circumstances:
// AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
@@ -184,7 +193,7 @@
audio_output_flags_t flags,
callback_t cbf,
void* user,
- uint32_t notificationFrames,
+ int32_t notificationFrames,
audio_session_t sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
@@ -215,7 +224,7 @@
audio_output_flags_t flags,
callback_t cbf,
void* user,
- uint32_t notificationFrames,
+ int32_t notificationFrames,
audio_session_t sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
@@ -274,7 +283,7 @@
audio_output_flags_t flags,
callback_t cbf,
void* user,
- uint32_t notificationFrames,
+ int32_t notificationFrames,
const sp<IMemory>& sharedBuffer,
bool threadCanCallJava,
audio_session_t sessionId,
@@ -287,7 +296,7 @@
float maxRequiredSpeed)
{
ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
- "flags #%x, notificationFrames %u, sessionId %d, transferType %d, uid %d, pid %d",
+ "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
sessionId, transferType, uid, pid);
@@ -443,7 +452,29 @@
mSendLevel = 0.0f;
// mFrameCount is initialized in createTrack_l
mReqFrameCount = frameCount;
- mNotificationFramesReq = notificationFrames;
+ if (notificationFrames >= 0) {
+ mNotificationFramesReq = notificationFrames;
+ mNotificationsPerBufferReq = 0;
+ } else {
+ if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
+ ALOGE("notificationFrames=%d not permitted for non-fast track",
+ notificationFrames);
+ return BAD_VALUE;
+ }
+ if (frameCount > 0) {
+ ALOGE("notificationFrames=%d not permitted with non-zero frameCount=%zu",
+ notificationFrames, frameCount);
+ return BAD_VALUE;
+ }
+ mNotificationFramesReq = 0;
+ const uint32_t minNotificationsPerBuffer = 1;
+ const uint32_t maxNotificationsPerBuffer = 8;
+ mNotificationsPerBufferReq = min(maxNotificationsPerBuffer,
+ max((uint32_t) -notificationFrames, minNotificationsPerBuffer));
+ ALOGW_IF(mNotificationsPerBufferReq != (uint32_t) -notificationFrames,
+ "notificationFrames=%d clamped to the range -%u to -%u",
+ notificationFrames, minNotificationsPerBuffer, maxNotificationsPerBuffer);
+ }
mNotificationFramesAct = 0;
if (sessionId == AUDIO_SESSION_ALLOCATE) {
mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
@@ -1231,6 +1262,15 @@
goto release;
}
+ // TODO consider making this a member variable if there are other uses for it later
+ size_t afFrameCountHAL;
+ status = AudioSystem::getFrameCountHAL(output, &afFrameCountHAL);
+ if (status != NO_ERROR) {
+ ALOGE("getFrameCountHAL(output=%d) status %d", output, status);
+ goto release;
+ }
+ ALOG_ASSERT(afFrameCountHAL > 0);
+
status = AudioSystem::getSamplingRate(output, &mAfSampleRate);
if (status != NO_ERROR) {
ALOGE("getSamplingRate(output=%d) status %d", output, status);
@@ -1303,18 +1343,30 @@
// there _is_ a frameCount parameter. We silently ignore it.
frameCount = mSharedBuffer->size() / mFrameSize;
} else {
- // For fast tracks the frame count calculations and checks are done by server
-
- if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+ size_t minFrameCount = 0;
+ // For fast tracks the frame count calculations and checks are mostly done by server,
+ // but we try to respect the application's request for notifications per buffer.
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ if (mNotificationsPerBufferReq > 0) {
+ // Avoid possible arithmetic overflow during multiplication.
+ // mNotificationsPerBuffer is clamped to a small integer earlier, so it is unlikely.
+ if (mNotificationsPerBufferReq > SIZE_MAX / afFrameCountHAL) {
+ ALOGE("Requested notificationPerBuffer=%u ignored for HAL frameCount=%zu",
+ mNotificationsPerBufferReq, afFrameCountHAL);
+ } else {
+ minFrameCount = afFrameCountHAL * mNotificationsPerBufferReq;
+ }
+ }
+ } else {
// for normal tracks precompute the frame count based on speed.
const float speed = !isPurePcmData_l() || isOffloadedOrDirect_l() ? 1.0f :
max(mMaxRequiredSpeed, mPlaybackRate.mSpeed);
- const size_t minFrameCount = calculateMinFrameCount(
+ minFrameCount = calculateMinFrameCount(
mAfLatency, mAfFrameCount, mAfSampleRate, mSampleRate,
- speed);
- if (frameCount < minFrameCount) {
- frameCount = minFrameCount;
- }
+ speed /*, 0 mNotificationsPerBufferReq*/);
+ }
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
}
}
@@ -1408,22 +1460,26 @@
}
// Make sure that application is notified with sufficient margin before underrun.
- // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
- // n = 1 fast track with single buffering; nBuffering is ignored
- // n = 2 fast track with double buffering
- // n = 2 normal track, (including those with sample rate conversion)
- // n >= 3 very high latency or very small notification interval (unused).
- // FIXME Move the computation from client side to server side,
- // and allow nBuffering to be larger than 1 for OpenSL ES, like it can be for Java.
+ // The client can divide the AudioTrack buffer into sub-buffers,
+ // and expresses its desire to server as the notification frame count.
if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) {
- size_t maxNotificationFrames = frameCount;
- if (!(trackFlags & IAudioFlinger::TRACK_FAST)) {
- const uint32_t nBuffering = 2;
- maxNotificationFrames /= nBuffering;
+ size_t maxNotificationFrames;
+ if (trackFlags & IAudioFlinger::TRACK_FAST) {
+ // notify every HAL buffer, regardless of the size of the track buffer
+ maxNotificationFrames = afFrameCountHAL;
+ } else {
+ // For normal tracks, use double-buffering
+ const int nBuffering = 2;
+ maxNotificationFrames = frameCount / nBuffering;
}
if (mNotificationFramesAct == 0 || mNotificationFramesAct > maxNotificationFrames) {
- ALOGW("Client adjusted notificationFrames from %u to %zu for frameCount %zu",
+ if (mNotificationFramesAct == 0) {
+ ALOGD("Client defaulted notificationFrames to %zu for frameCount %zu",
+ maxNotificationFrames, frameCount);
+ } else {
+ ALOGW("Client adjusted notificationFrames from %u to %zu for frameCount %zu",
mNotificationFramesAct, maxNotificationFrames, frameCount);
+ }
mNotificationFramesAct = (uint32_t) maxNotificationFrames;
}
}
@@ -2043,6 +2099,7 @@
const nsecs_t datans = mRemainingFrames <= avail ? 0 :
framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed);
// audio flinger thread buffer size (TODO: adjust for fast tracks)
+ // FIXME: use mAfFrameCountHAL instead of mAfFrameCount below for fast tracks.
const nsecs_t afns = framesToNanoseconds(mAfFrameCount, mAfSampleRate, speed);
// add a half the AudioFlinger buffer time to avoid soaking CPU if datans is 0.
myns = datans + (afns / 2);
@@ -2203,7 +2260,8 @@
return true; // static tracks do not have issues with buffer sizing.
}
const size_t minFrameCount =
- calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed);
+ calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed
+ /*, 0 mNotificationsPerBufferReq*/);
ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu",
mFrameCount, minFrameCount);
return mFrameCount >= minFrameCount;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ff67fb2..3fdf99f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -182,13 +182,9 @@
static const int kPriorityFastMixer = 3;
static const int kPriorityFastCapture = 3;
-// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
-// for the track. The client then sub-divides this into smaller buffers for its use.
-// Currently the client uses N-buffering by default, but doesn't tell us about the value of N.
-// So for now we just assume that client is double-buffered for fast tracks.
-// FIXME It would be better for client to tell AudioFlinger the value of N,
-// so AudioFlinger could allocate the right amount of memory.
-// See the client's minBufCount and mNotificationFramesAct calculations for details.
+// IAudioFlinger::createTrack() has an in/out parameter 'pFrameCount' for the total size of the
+// track buffer in shared memory. Zero on input means to use a default value. For fast tracks,
+// AudioFlinger derives the default from HAL buffer size and 'fast track multiplier'.
// This is the default value, if not specified by property.
static const int kFastTrackMultiplier = 2;