refactor AudioTrack and AudioFlinger createTrack()
Refactor the mechanism used by audio tracks to query and attach
to an output mixer/stream in audio flinger. This will:
- reduce the number of binder transactions needed to create a track
- move sample rate, framecount and flags validations to audio server
side
- move audio session allocation to audio server side
- prepare restriction of certain binder transactions to audioserver only
Test: CTS tests for AudioTrack
Change-Id: If4369aad6c080a56c0b42fbfcc97c8ade17a7439
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 79e540a..9cb0357 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -641,38 +641,52 @@
// IAudioFlinger interface
-
-sp<IAudioTrack> AudioFlinger::createTrack(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t *frameCount,
- audio_output_flags_t *flags,
- const sp<IMemory>& sharedBuffer,
- audio_io_handle_t output,
- pid_t pid,
- pid_t tid,
- audio_session_t *sessionId,
- int clientUid,
- status_t *status,
- audio_port_handle_t portId)
+sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
+ CreateTrackOutput& output,
+ status_t *status)
{
sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
sp<Client> client;
status_t lStatus;
- audio_session_t lSessionId;
+ audio_stream_type_t streamType;
+ audio_port_handle_t portId;
+ bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (pid == -1 || !isTrustedCallingUid(callingUid)) {
+ uid_t clientUid = input.clientInfo.clientUid;
+ if (!isTrustedCallingUid(callingUid)) {
+ ALOGW_IF(clientUid != callingUid,
+ "%s uid %d tried to pass itself off as %d",
+ __FUNCTION__, callingUid, clientUid);
+ clientUid = callingUid;
+ updatePid = true;
+ }
+ pid_t clientPid = input.clientInfo.clientPid;
+ if (updatePid) {
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- ALOGW_IF(pid != -1 && pid != callingPid,
+ ALOGW_IF(clientPid != -1 && clientPid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, pid);
- pid = callingPid;
+ __func__, callingUid, callingPid, clientPid);
+ clientPid = callingPid;
}
+ audio_session_t sessionId = input.sessionId;
+ if (sessionId == AUDIO_SESSION_ALLOCATE) {
+ sessionId = (audio_session_t) newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ }
+ output.sessionId = sessionId;
+ output.outputId = AUDIO_IO_HANDLE_NONE;
+ output.selectedDeviceId = input.selectedDeviceId;
+
+ lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
+ clientUid, &input.config, input.flags,
+ &output.selectedDeviceId, &portId);
+
+ if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
+ ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
+ goto Exit;
+ }
// client AudioTrack::set already implements AUDIO_STREAM_DEFAULT => AUDIO_STREAM_MUSIC,
// but if someone uses binder directly they could bypass that and cause us to crash
if (uint32_t(streamType) >= AUDIO_STREAM_CNT) {
@@ -681,91 +695,76 @@
goto Exit;
}
- // further sample rate checks are performed by createTrack_l() depending on the thread type
- if (sampleRate == 0) {
- ALOGE("createTrack() invalid sample rate %u", sampleRate);
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
// further channel mask checks are performed by createTrack_l() depending on the thread type
- if (!audio_is_output_channel(channelMask)) {
- ALOGE("createTrack() invalid channel mask %#x", channelMask);
+ if (!audio_is_output_channel(input.config.channel_mask)) {
+ ALOGE("createTrack() invalid channel mask %#x", input.config.channel_mask);
lStatus = BAD_VALUE;
goto Exit;
}
// further format checks are performed by createTrack_l() depending on the thread type
- if (!audio_is_valid_format(format)) {
- ALOGE("createTrack() invalid format %#x", format);
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
- if (sharedBuffer != 0 && sharedBuffer->pointer() == NULL) {
- ALOGE("createTrack() sharedBuffer is non-0 but has NULL pointer()");
+ if (!audio_is_valid_format(input.config.format)) {
+ ALOGE("createTrack() invalid format %#x", input.config.format);
lStatus = BAD_VALUE;
goto Exit;
}
{
Mutex::Autolock _l(mLock);
- PlaybackThread *thread = checkPlaybackThread_l(output);
+ PlaybackThread *thread = checkPlaybackThread_l(output.outputId);
if (thread == NULL) {
- ALOGE("no playback thread found for output handle %d", output);
+ ALOGE("no playback thread found for output handle %d", output.outputId);
lStatus = BAD_VALUE;
goto Exit;
}
- client = registerPid(pid);
+ client = registerPid(clientPid);
PlaybackThread *effectThread = NULL;
- if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) {
- if (audio_unique_id_get_use(*sessionId) != AUDIO_UNIQUE_ID_USE_SESSION) {
- ALOGE("createTrack() invalid session ID %d", *sessionId);
- lStatus = BAD_VALUE;
- goto Exit;
- }
- lSessionId = *sessionId;
- // check if an effect chain with the same session ID is present on another
- // output thread and move it here.
- for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
- if (mPlaybackThreads.keyAt(i) != output) {
- uint32_t sessions = t->hasAudioSession(lSessionId);
- if (sessions & ThreadBase::EFFECT_SESSION) {
- effectThread = t.get();
- break;
- }
+ // check if an effect chain with the same session ID is present on another
+ // output thread and move it here.
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (mPlaybackThreads.keyAt(i) != output.outputId) {
+ uint32_t sessions = t->hasAudioSession(sessionId);
+ if (sessions & ThreadBase::EFFECT_SESSION) {
+ effectThread = t.get();
+ break;
}
}
- } else {
- // if no audio session id is provided, create one here
- lSessionId = (audio_session_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
- if (sessionId != NULL) {
- *sessionId = lSessionId;
- }
}
- ALOGV("createTrack() lSessionId: %d", lSessionId);
+ ALOGV("createTrack() sessionId: %d", sessionId);
- track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, lSessionId, flags, tid,
- clientUid, &lStatus, portId);
+ output.sampleRate = input.config.sample_rate;
+ output.frameCount = input.frameCount;
+ output.notificationFrameCount = input.notificationFrameCount;
+ output.flags = input.flags;
+
+ track = thread->createTrack_l(client, streamType, &output.sampleRate, input.config.format,
+ input.config.channel_mask,
+ &output.frameCount, &output.notificationFrameCount,
+ input.notificationsPerBuffer, input.speed,
+ input.sharedBuffer, sessionId, &output.flags,
+ input.clientInfo.clientTid, clientUid, &lStatus, portId);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
+ output.afFrameCount = thread->frameCount();
+ output.afSampleRate = thread->sampleRate();
+ output.afLatencyMs = thread->latency();
+
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
if (lStatus == NO_ERROR && effectThread != NULL) {
// no risk of deadlock because AudioFlinger::mLock is held
Mutex::Autolock _dl(thread->mLock);
Mutex::Autolock _sl(effectThread->mLock);
- moveEffectChain_l(lSessionId, effectThread, thread, true);
+ moveEffectChain_l(sessionId, effectThread, thread, true);
}
// Look for sync events awaiting for a session to be used.
for (size_t i = 0; i < mPendingSyncEvents.size(); i++) {
- if (mPendingSyncEvents[i]->triggerSession() == lSessionId) {
+ if (mPendingSyncEvents[i]->triggerSession() == sessionId) {
if (thread->isValidSyncEvent(mPendingSyncEvents[i])) {
if (lStatus == NO_ERROR) {
(void) track->setSyncEvent(mPendingSyncEvents[i]);
@@ -778,7 +777,7 @@
}
}
- setAudioHwSyncForSession_l(thread, lSessionId);
+ setAudioHwSyncForSession_l(thread, sessionId);
}
if (lStatus != NO_ERROR) {
@@ -798,6 +797,9 @@
trackHandle = new TrackHandle(track);
Exit:
+ if (lStatus != NO_ERROR && output.outputId != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::releaseOutput(output.outputId, streamType, sessionId);
+ }
*status = lStatus;
return trackHandle;
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index dff94d2..7e9ef26 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -114,21 +114,9 @@
virtual status_t dump(int fd, const Vector<String16>& args);
// IAudioFlinger interface, in binder opcode order
- virtual sp<IAudioTrack> createTrack(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t *pFrameCount,
- audio_output_flags_t *flags,
- const sp<IMemory>& sharedBuffer,
- audio_io_handle_t output,
- pid_t pid,
- pid_t tid,
- audio_session_t *sessionId,
- int clientUid,
- status_t *status /*non-NULL*/,
- audio_port_handle_t portId);
+ virtual sp<IAudioTrack> createTrack(const CreateTrackInput& input,
+ CreateTrackOutput& output,
+ status_t *status);
virtual sp<media::IAudioRecord> openRecord(
audio_io_handle_t input,
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8c7c830..8e6c720 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1837,10 +1837,13 @@
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
audio_stream_type_t streamType,
- uint32_t sampleRate,
+ uint32_t *pSampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
+ size_t *pNotificationFrameCount,
+ uint32_t notificationsPerBuffer,
+ float speed,
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
audio_output_flags_t *flags,
@@ -1850,9 +1853,16 @@
audio_port_handle_t portId)
{
size_t frameCount = *pFrameCount;
+ size_t notificationFrameCount = *pNotificationFrameCount;
sp<Track> track;
status_t lStatus;
audio_output_flags_t outputFlags = mOutput->flags;
+ audio_output_flags_t requestedFlags = *flags;
+
+ if (*pSampleRate == 0) {
+ *pSampleRate = mSampleRate;
+ }
+ uint32_t sampleRate = *pSampleRate;
// special case for FAST flag considered OK if fast mixer is present
if (hasFastMixer()) {
@@ -1929,36 +1939,114 @@
*flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
}
}
- // For normal PCM streaming tracks, update minimum frame count.
- // For compatibility with AudioTrack calculation, buffer depth is forced
- // to be at least 2 x the normal mixer frame count and cover audio hardware latency.
- // This is probably too conservative, but legacy application code may depend on it.
- // If you change this calculation, also review the start threshold which is related.
- if (!(*flags & AUDIO_OUTPUT_FLAG_FAST)
- && audio_has_proportional_frames(format) && sharedBuffer == 0) {
- // this must match AudioTrack.cpp calculateMinFrameCount().
- // TODO: Move to a common library
- uint32_t latencyMs = 0;
- lStatus = mOutput->stream->getLatency(&latencyMs);
- if (lStatus != OK) {
- ALOGE("Error when retrieving output stream latency: %d", lStatus);
+
+ if (!audio_has_proportional_frames(format)) {
+ if (sharedBuffer != 0) {
+ // Same comment as below about ignoring frameCount parameter for set()
+ frameCount = sharedBuffer->size();
+ } else if (frameCount == 0) {
+ frameCount = mNormalFrameCount;
+ }
+ if (notificationFrameCount != frameCount) {
+ notificationFrameCount = frameCount;
+ }
+ } else if (sharedBuffer != 0) {
+ // FIXME: Ensure client side memory buffers need
+ // not have additional alignment beyond sample
+ // (e.g. 16 bit stereo accessed as 32 bit frame).
+ size_t alignment = audio_bytes_per_sample(format);
+ if (alignment & 1) {
+ // for AUDIO_FORMAT_PCM_24_BIT_PACKED (not exposed through Java).
+ alignment = 1;
+ }
+ uint32_t channelCount = audio_channel_count_from_out_mask(channelMask);
+ size_t frameSize = channelCount * audio_bytes_per_sample(format);
+ if (channelCount > 1) {
+ // More than 2 channels does not require stronger alignment than stereo
+ alignment <<= 1;
+ }
+ if (((uintptr_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
+ ALOGE("Invalid buffer alignment: address %p, channel count %u",
+ sharedBuffer->pointer(), channelCount);
+ lStatus = BAD_VALUE;
goto Exit;
}
- uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
- if (minBufCount < 2) {
- minBufCount = 2;
+
+ // When initializing a shared buffer AudioTrack via constructors,
+ // there's no frameCount parameter.
+ // But when initializing a shared buffer AudioTrack via set(),
+ // there _is_ a frameCount parameter. We silently ignore it.
+ frameCount = sharedBuffer->size() / frameSize;
+ } else {
+ size_t minFrameCount = 0;
+ // For fast tracks we try to respect the application's request for notifications per buffer.
+ if (*flags & AUDIO_OUTPUT_FLAG_FAST) {
+ if (notificationsPerBuffer > 0) {
+ // Avoid possible arithmetic overflow during multiplication.
+ if (notificationsPerBuffer > SIZE_MAX / mFrameCount) {
+ ALOGE("Requested notificationPerBuffer=%u ignored for HAL frameCount=%zu",
+ notificationsPerBuffer, mFrameCount);
+ } else {
+ minFrameCount = mFrameCount * notificationsPerBuffer;
+ }
+ }
+ } else {
+ // For normal PCM streaming tracks, update minimum frame count.
+ // Buffer depth is forced to be at least 2 x the normal mixer frame count and
+ // cover audio hardware latency.
+ // This is probably too conservative, but legacy application code may depend on it.
+ // If you change this calculation, also review the start threshold which is related.
+ uint32_t latencyMs = latency_l();
+ if (latencyMs == 0) {
+ ALOGE("Error when retrieving output stream latency");
+ lStatus = UNKNOWN_ERROR;
+ goto Exit;
+ }
+
+ minFrameCount = AudioSystem::calculateMinFrameCount(latencyMs, mNormalFrameCount,
+ mSampleRate, sampleRate, speed /*, 0 mNotificationsPerBufferReq*/);
+
}
- // For normal mixing tracks, if speed is > 1.0f (normal), AudioTrack
- // or the client should compute and pass in a larger buffer request.
- size_t minFrameCount =
- minBufCount * sourceFramesNeededWithTimestretch(
- sampleRate, mNormalFrameCount,
- mSampleRate, AUDIO_TIMESTRETCH_SPEED_NORMAL /*speed*/);
- if (frameCount < minFrameCount) { // including frameCount == 0
+ if (frameCount < minFrameCount) {
frameCount = minFrameCount;
}
}
+
+ // Make sure that application is notified with sufficient margin before underrun.
+ // The client can divide the AudioTrack buffer into sub-buffers,
+ // and expresses its desire to server as the notification frame count.
+ if (sharedBuffer == 0 && audio_is_linear_pcm(format)) {
+ size_t maxNotificationFrames;
+ if (*flags & AUDIO_OUTPUT_FLAG_FAST) {
+ // notify every HAL buffer, regardless of the size of the track buffer
+ maxNotificationFrames = mFrameCount;
+ } else {
+ // For normal tracks, use at least double-buffering if no sample rate conversion,
+ // or at least triple-buffering if there is sample rate conversion
+ const int nBuffering = sampleRate == mSampleRate ? 2 : 3;
+ maxNotificationFrames = frameCount / nBuffering;
+ // If client requested a fast track but this was denied, then use the smaller maximum.
+ if (requestedFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ size_t maxNotificationFramesFastDenied = FMS_20 * sampleRate / 1000;
+ if (maxNotificationFrames > maxNotificationFramesFastDenied) {
+ maxNotificationFrames = maxNotificationFramesFastDenied;
+ }
+ }
+ }
+ if (notificationFrameCount == 0 || notificationFrameCount > maxNotificationFrames) {
+ if (notificationFrameCount == 0) {
+ ALOGD("Client defaulted notificationFrames to %zu for frameCount %zu",
+ maxNotificationFrames, frameCount);
+ } else {
+ ALOGW("Client adjusted notificationFrames from %zu to %zu for frameCount %zu",
+ notificationFrameCount, maxNotificationFrames, frameCount);
+ }
+ notificationFrameCount = maxNotificationFrames;
+ }
+ }
+
*pFrameCount = frameCount;
+ *pNotificationFrameCount = notificationFrameCount;
switch (mType) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index b685e1b..2ca273f 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -706,10 +706,13 @@
sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,
audio_stream_type_t streamType,
- uint32_t sampleRate,
+ uint32_t *sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
+ size_t *pNotificationFrameCount,
+ uint32_t notificationsPerBuffer,
+ float speed,
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
audio_output_flags_t *flags,