summaryrefslogtreecommitdiffstats
path: root/media/libmedia/AudioTrack.cpp
diff options
context:
space:
mode:
authorRichard Fitzgerald <rf@opensource.wolfsonmicro.com>2013-05-14 12:12:21 +0100
committerEric Laurent <elaurent@google.com>2013-07-25 14:08:09 -0700
commitb1a270d1e926fb9a01b4265a7675ed0c2c8f4868 (patch)
tree091d06b02bc126ff1bb2dd1fabc9be829582ae69 /media/libmedia/AudioTrack.cpp
parentbfb1b832079bbb9426f72f3863199a54aefd02da (diff)
downloadframeworks_av-b1a270d1e926fb9a01b4265a7675ed0c2c8f4868.zip
frameworks_av-b1a270d1e926fb9a01b4265a7675ed0c2c8f4868.tar.gz
frameworks_av-b1a270d1e926fb9a01b4265a7675ed0c2c8f4868.tar.bz2
libmedia: offloaded playback support
- start() returns a status so that upper layers can recreate a non offloaded track in case of error. - Added states to handle offloaded tracks specific: - waiting for stream end (drain) notification by audio flinger - allow pause while waiting for stream end notification - getPosition() queries the render position directly from audio HAL. - disable APIs not applicable to offloaded tracks - Modified track restoring behavior for invalidated offloaded tracks: just send the callback and wait for upper layers to create a new track. - Added wait for stream end management in audio track client proxy. Similar to obtainBuffer and should be factored in. Change-Id: I0fc48117946364cb255afd653195498891f622bd Signed-off-by: Eric Laurent <elaurent@google.com>
Diffstat (limited to 'media/libmedia/AudioTrack.cpp')
-rw-r--r--media/libmedia/AudioTrack.cpp225
1 files changed, 187 insertions, 38 deletions
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 7b6b38d..3653b7f 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -27,7 +27,9 @@
#include <private/media/AudioTrackShared.h>
#include <media/IAudioFlinger.h>
-#define WAIT_PERIOD_MS 10
+#define WAIT_PERIOD_MS 10
+#define WAIT_STREAM_END_TIMEOUT_SEC 120
+
namespace android {
// ---------------------------------------------------------------------------
@@ -141,6 +143,7 @@ AudioTrack::~AudioTrack()
// Otherwise the callback thread will never exit.
stop();
if (mAudioTrackThread != 0) {
+ mProxy->interrupt();
mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
@@ -224,6 +227,8 @@ status_t AudioTrack::set(
return INVALID_OPERATION;
}
+ mOutput = 0;
+
// handle default values first.
if (streamType == AUDIO_STREAM_DEFAULT) {
streamType = AUDIO_STREAM_MUSIC;
@@ -259,7 +264,12 @@ status_t AudioTrack::set(
}
// force direct flag if format is not linear PCM
- if (!audio_is_linear_pcm(format)) {
+ // or offload was requested
+ if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+ || !audio_is_linear_pcm(format)) {
+ ALOGV( (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+ ? "Offload request, forcing to Direct Output"
+ : "Not linear PCM, forcing to Direct Output");
flags = (audio_output_flags_t)
// FIXME why can't we allow direct AND fast?
((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);
@@ -325,9 +335,14 @@ status_t AudioTrack::set(
if (status != NO_ERROR) {
if (mAudioTrackThread != 0) {
- mAudioTrackThread->requestExit();
+ mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
+ mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
+ //Use of direct and offloaded output streams is ref counted by audio policy manager.
+ // As getOutput was called above and resulted in an output stream to be opened,
+ // we need to release it.
+ AudioSystem::releaseOutput(output);
return status;
}
@@ -346,23 +361,29 @@ status_t AudioTrack::set(
mSequence = 1;
mObservedSequence = mSequence;
mInUnderrun = false;
+ mOutput = output;
return NO_ERROR;
}
// -------------------------------------------------------------------------
-void AudioTrack::start()
+status_t AudioTrack::start()
{
AutoMutex lock(mLock);
+
if (mState == STATE_ACTIVE) {
- return;
+ return INVALID_OPERATION;
}
mInUnderrun = true;
State previousState = mState;
- mState = STATE_ACTIVE;
+ if (previousState == STATE_PAUSED_STOPPING) {
+ mState = STATE_STOPPING;
+ } else {
+ mState = STATE_ACTIVE;
+ }
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
@@ -372,7 +393,11 @@ void AudioTrack::start()
sp<AudioTrackThread> t = mAudioTrackThread;
if (t != 0) {
- t->resume();
+ if (previousState == STATE_STOPPING) {
+ mProxy->interrupt();
+ } else {
+ t->resume();
+ }
} else {
mPreviousPriority = getpriority(PRIO_PROCESS, 0);
get_sched_policy(0, &mPreviousSchedulingGroup);
@@ -394,14 +419,16 @@ void AudioTrack::start()
ALOGE("start() status %d", status);
mState = previousState;
if (t != 0) {
- t->pause();
+ if (previousState != STATE_STOPPING) {
+ t->pause();
+ }
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
set_sched_policy(0, mPreviousSchedulingGroup);
}
}
- // FIXME discarding status
+ return status;
}
void AudioTrack::stop()
@@ -412,7 +439,12 @@ void AudioTrack::stop()
return;
}
- mState = STATE_STOPPED;
+ if (isOffloaded()) {
+ mState = STATE_STOPPING;
+ } else {
+ mState = STATE_STOPPED;
+ }
+
mProxy->interrupt();
mAudioTrack->stop();
// the playback head position will reset to 0, so if a marker is set, we need
@@ -426,9 +458,12 @@ void AudioTrack::stop()
flush_l();
}
#endif
+
sp<AudioTrackThread> t = mAudioTrackThread;
if (t != 0) {
- t->pause();
+ if (!isOffloaded()) {
+ t->pause();
+ }
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
set_sched_policy(0, mPreviousSchedulingGroup);
@@ -461,8 +496,12 @@ void AudioTrack::flush_l()
mMarkerPosition = 0;
mMarkerReached = false;
mUpdatePeriod = 0;
+ mRefreshRemaining = true;
mState = STATE_FLUSHED;
+ if (isOffloaded()) {
+ mProxy->interrupt();
+ }
mProxy->flush();
mAudioTrack->flush();
}
@@ -470,10 +509,13 @@ void AudioTrack::flush_l()
void AudioTrack::pause()
{
AutoMutex lock(mLock);
- if (mState != STATE_ACTIVE) {
+ if (mState == STATE_ACTIVE) {
+ mState = STATE_PAUSED;
+ } else if (mState == STATE_STOPPING) {
+ mState = STATE_PAUSED_STOPPING;
+ } else {
return;
}
- mState = STATE_PAUSED;
mProxy->interrupt();
mAudioTrack->pause();
}
@@ -520,7 +562,7 @@ void AudioTrack::getAuxEffectSendLevel(float* level) const
status_t AudioTrack::setSampleRate(uint32_t rate)
{
- if (mIsTimed) {
+ if (mIsTimed || isOffloaded()) {
return INVALID_OPERATION;
}
@@ -552,7 +594,7 @@ uint32_t AudioTrack::getSampleRate() const
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
- if (mSharedBuffer == 0 || mIsTimed) {
+ if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
return INVALID_OPERATION;
}
@@ -586,7 +628,7 @@ void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
status_t AudioTrack::setMarkerPosition(uint32_t marker)
{
// The only purpose of setting marker position is to get a callback
- if (mCbf == NULL) {
+ if (mCbf == NULL || isOffloaded()) {
return INVALID_OPERATION;
}
@@ -599,6 +641,9 @@ status_t AudioTrack::setMarkerPosition(uint32_t marker)
status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
{
+ if (isOffloaded()) {
+ return INVALID_OPERATION;
+ }
if (marker == NULL) {
return BAD_VALUE;
}
@@ -612,19 +657,21 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
{
// The only purpose of setting position update period is to get a callback
- if (mCbf == NULL) {
+ if (mCbf == NULL || isOffloaded()) {
return INVALID_OPERATION;
}
AutoMutex lock(mLock);
mNewPosition = mProxy->getPosition() + updatePeriod;
mUpdatePeriod = updatePeriod;
-
return NO_ERROR;
}
status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
{
+ if (isOffloaded()) {
+ return INVALID_OPERATION;
+ }
if (updatePeriod == NULL) {
return BAD_VALUE;
}
@@ -637,7 +684,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
status_t AudioTrack::setPosition(uint32_t position)
{
- if (mSharedBuffer == 0 || mIsTimed) {
+ if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
return INVALID_OPERATION;
}
if (position > mFrameCount) {
@@ -670,10 +717,19 @@ status_t AudioTrack::getPosition(uint32_t *position) const
}
AutoMutex lock(mLock);
- // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
- *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 :
- mProxy->getPosition();
+ if (isOffloaded()) {
+ uint32_t dspFrames = 0;
+ if (mOutput != 0) {
+ uint32_t halFrames;
+ AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames);
+ }
+ *position = dspFrames;
+ } else {
+ // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
+ *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 :
+ mProxy->getPosition();
+ }
return NO_ERROR;
}
@@ -693,7 +749,7 @@ status_t AudioTrack::getBufferPosition(size_t *position)
status_t AudioTrack::reload()
{
- if (mSharedBuffer == 0 || mIsTimed) {
+ if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
return INVALID_OPERATION;
}
@@ -713,14 +769,18 @@ status_t AudioTrack::reload()
audio_io_handle_t AudioTrack::getOutput()
{
AutoMutex lock(mLock);
- return getOutput_l();
+ return mOutput;
}
// must be called with mLock held
audio_io_handle_t AudioTrack::getOutput_l()
{
- return AudioSystem::getOutput(mStreamType,
- mSampleRate, mFormat, mChannelMask, mFlags);
+ if (mOutput) {
+ return mOutput;
+ } else {
+ return AudioSystem::getOutput(mStreamType,
+ mSampleRate, mFormat, mChannelMask, mFlags);
+ }
}
status_t AudioTrack::attachAuxEffect(int effectId)
@@ -791,7 +851,9 @@ status_t AudioTrack::createTrack_l(
}
frameCount = afFrameCount;
}
-
+ if (mNotificationFramesAct != frameCount) {
+ mNotificationFramesAct = frameCount;
+ }
} else if (sharedBuffer != 0) {
// Ensure that buffer alignment matches channel count
@@ -875,6 +937,10 @@ status_t AudioTrack::createTrack_l(
}
}
+ if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ trackFlags |= IAudioFlinger::TRACK_OFFLOAD;
+ }
+
sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
sampleRate,
// AudioFlinger only sees 16-bit PCM
@@ -937,6 +1003,17 @@ status_t AudioTrack::createTrack_l(
}
}
}
+ if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ if (trackFlags & IAudioFlinger::TRACK_OFFLOAD) {
+ ALOGV("AUDIO_OUTPUT_FLAG_OFFLOAD successful");
+ } else {
+ ALOGW("AUDIO_OUTPUT_FLAG_OFFLOAD denied by server");
+ flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
+ mFlags = flags;
+ return NO_INIT;
+ }
+ }
+
mRefreshRemaining = true;
// Starting address of buffers in shared memory. If there is a shared buffer, buffers
@@ -1040,6 +1117,9 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re
if (newSequence == oldSequence) {
status = restoreTrack_l("obtainBuffer");
if (status != NO_ERROR) {
+ buffer.mFrameCount = 0;
+ buffer.mRaw = NULL;
+ buffer.mNonContig = 0;
break;
}
}
@@ -1050,6 +1130,14 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re
proxy = mProxy;
iMem = mCblkMemory;
+ if (mState == STATE_STOPPING) {
+ status = -EINTR;
+ buffer.mFrameCount = 0;
+ buffer.mRaw = NULL;
+ buffer.mNonContig = 0;
+ break;
+ }
+
// Non-blocking if track is stopped or paused
if (mState != STATE_ACTIVE) {
requested = &ClientProxy::kNonBlocking;
@@ -1255,12 +1343,18 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
// Check for track invalidation
if (flags & CBLK_INVALID) {
- (void) restoreTrack_l("processAudioBuffer");
- mLock.unlock();
- // Run again immediately, but with a new IAudioTrack
- return 0;
+ // for offloaded tracks restoreTrack_l() will just update the sequence and clear
+ // AudioSystem cache. We should not exit here but after calling the callback so
+ // that the upper layers can recreate the track
+ if (!isOffloaded() || (mSequence == mObservedSequence)) {
+ status_t status = restoreTrack_l("processAudioBuffer");
+ mLock.unlock();
+ // Run again immediately, but with a new IAudioTrack
+ return 0;
+ }
}
+ bool waitStreamEnd = mState == STATE_STOPPING;
bool active = mState == STATE_ACTIVE;
// Manage underrun callback, must be done under lock to avoid race with releaseBuffer()
@@ -1314,7 +1408,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
mRetryOnPartialBuffer = false;
}
size_t misalignment = mProxy->getMisalignment();
- int32_t sequence = mSequence;
+ uint32_t sequence = mSequence;
// These fields don't need to be cached, because they are assigned only by set():
// mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags
@@ -1322,6 +1416,38 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
mLock.unlock();
+ if (waitStreamEnd) {
+ AutoMutex lock(mLock);
+
+ sp<AudioTrackClientProxy> proxy = mProxy;
+ sp<IMemory> iMem = mCblkMemory;
+
+ struct timespec timeout;
+ timeout.tv_sec = WAIT_STREAM_END_TIMEOUT_SEC;
+ timeout.tv_nsec = 0;
+
+ mLock.unlock();
+ status_t status = mProxy->waitStreamEndDone(&timeout);
+ mLock.lock();
+ switch (status) {
+ case NO_ERROR:
+ case DEAD_OBJECT:
+ case TIMED_OUT:
+ mLock.unlock();
+ mCbf(EVENT_STREAM_END, mUserData, NULL);
+ mLock.lock();
+ if (mState == STATE_STOPPING) {
+ mState = STATE_STOPPED;
+ if (status != DEAD_OBJECT) {
+ return NS_INACTIVE;
+ }
+ }
+ return 0;
+ default:
+ return 0;
+ }
+ }
+
// perform callbacks while unlocked
if (newUnderrun) {
mCbf(EVENT_UNDERRUN, mUserData, NULL);
@@ -1343,9 +1469,14 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
newPosition += updatePeriod;
newPosCount--;
}
+
if (mObservedSequence != sequence) {
mObservedSequence = sequence;
mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL);
+ // for offloaded tracks, just wait for the upper layers to recreate the track
+ if (isOffloaded()) {
+ return NS_INACTIVE;
+ }
}
// if inactive, then don't run me again until re-started
@@ -1404,10 +1535,11 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
"obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
requested = &ClientProxy::kNonBlocking;
size_t avail = audioBuffer.frameCount + nonContig;
- ALOGV("obtainBuffer(%u) returned %u = %u + %u",
- mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
+ ALOGV("obtainBuffer(%u) returned %u = %u + %u err %d",
+ mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err);
if (err != NO_ERROR) {
- if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
+ if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR ||
+ (isOffloaded() && (err == DEAD_OBJECT))) {
return 0;
}
ALOGE("Error %d obtaining an audio buffer, giving up.", err);
@@ -1500,7 +1632,8 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
status_t AudioTrack::restoreTrack_l(const char *from)
{
- ALOGW("dead IAudioTrack, creating a new one from %s()", from);
+ ALOGW("dead IAudioTrack, %s, creating a new one from %s()",
+ isOffloaded() ? "Offloaded" : "PCM", from);
++mSequence;
status_t result;
@@ -1508,6 +1641,14 @@ status_t AudioTrack::restoreTrack_l(const char *from)
// output parameters in getOutput_l() and createTrack_l()
AudioSystem::clearAudioConfigCache();
+ if (isOffloaded()) {
+ return DEAD_OBJECT;
+ }
+
+ // force new output query from audio policy manager;
+ mOutput = 0;
+ audio_io_handle_t output = getOutput_l();
+
// if the new IAudioTrack is created, createTrack_l() will modify the
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory
@@ -1520,7 +1661,7 @@ status_t AudioTrack::restoreTrack_l(const char *from)
mReqFrameCount, // so that frame count never goes down
mFlags,
mSharedBuffer,
- getOutput_l(),
+ output,
position /*epoch*/);
if (result == NO_ERROR) {
@@ -1549,6 +1690,10 @@ status_t AudioTrack::restoreTrack_l(const char *from)
}
}
if (result != NO_ERROR) {
+ //Use of direct and offloaded output streams is ref counted by audio policy manager.
+ // As getOutput was called above and resulted in an output stream to be opened,
+ // we need to release it.
+ AudioSystem::releaseOutput(output);
ALOGW("restoreTrack_l() failed status %d", result);
mState = STATE_STOPPED;
}
@@ -1568,7 +1713,11 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs)
String8 AudioTrack::getParameters(const String8& keys)
{
- return String8::empty();
+ if (mOutput) {
+ return AudioSystem::getParameters(mOutput, keys);
+ } else {
+ return String8::empty();
+ }
}
status_t AudioTrack::dump(int fd, const Vector<String16>& args) const