summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/AudioTrack.h12
-rw-r--r--include/private/media/AudioTrackShared.h3
-rw-r--r--media/libmedia/AudioTrack.cpp225
-rw-r--r--media/libmedia/AudioTrackShared.cpp133
-rw-r--r--media/libmedia/IAudioFlinger.cpp15
-rw-r--r--media/libmedia/IAudioPolicyService.cpp32
6 files changed, 363 insertions, 57 deletions
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 58e0deb..da13a7f 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -277,7 +277,7 @@ public:
* make it active. If set, the callback will start being called.
* If the track was previously paused, volume is ramped up over the first mix buffer.
*/
- void start();
+ status_t start();
/* Stop a track.
* In static buffer mode, the track is stopped immediately.
@@ -635,11 +635,12 @@ protected:
void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
audio_io_handle_t getOutput_l();
- status_t getPosition_l(uint32_t *position);
-
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreTrack_l(const char *from);
+ bool isOffloaded() const
+ { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
+
// may be changed if IAudioTrack is re-created
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -676,7 +677,9 @@ protected:
STATE_ACTIVE,
STATE_STOPPED,
STATE_PAUSED,
+ STATE_PAUSED_STOPPING,
STATE_FLUSHED,
+ STATE_STOPPING,
} mState;
callback_t mCbf; // callback handler for events, or NULL
@@ -694,7 +697,7 @@ protected:
// These are private to processAudioBuffer(), and are not protected by a lock
uint32_t mRemainingFrames; // number of frames to request in obtainBuffer()
bool mRetryOnPartialBuffer; // sleep and retry after partial obtainBuffer()
- int mObservedSequence; // last observed value of mSequence
+ uint32_t mObservedSequence; // last observed value of mSequence
sp<IMemory> mSharedBuffer;
uint32_t mLoopPeriod; // in frames, zero means looping is disabled
@@ -736,6 +739,7 @@ private:
sp<DeathNotifier> mDeathNotifier;
uint32_t mSequence; // incremented for each new IAudioTrack attempt
+ audio_io_handle_t mOutput; // cached output io handle
};
class TimedAudioTrack : public AudioTrack
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 6129c80..b890180 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -296,6 +296,7 @@ public:
bool getStreamEndDone() const;
+ status_t waitStreamEndDone(const struct timespec *requested);
};
class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -379,8 +380,8 @@ public:
protected:
size_t mAvailToClient; // estimated frames available to client prior to releaseBuffer()
-private:
int32_t mFlush; // our copy of cblk->u.mStreaming.mFlush, for streaming output only
+private:
bool mDeferWake; // whether another releaseBuffer() is expected soon
};
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
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index bd43ad2..aa45a2f 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -200,7 +200,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques
ts = &remaining;
break;
default:
- LOG_FATAL("%s timeout=%d", timeout);
+ LOG_FATAL("obtainBuffer() timeout=%d", timeout);
ts = NULL;
break;
}
@@ -259,8 +259,9 @@ end:
requested = &kNonBlocking;
}
if (measure) {
- ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000,
- total.tv_sec, total.tv_nsec / 1000000);
+ ALOGV("requested %ld.%03ld elapsed %ld.%03ld",
+ requested->tv_sec, requested->tv_nsec / 1000000,
+ total.tv_sec, total.tv_nsec / 1000000);
}
return status;
}
@@ -323,13 +324,120 @@ void AudioTrackClientProxy::flush()
}
bool AudioTrackClientProxy::clearStreamEndDone() {
- return android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE;
+ return (android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0;
}
bool AudioTrackClientProxy::getStreamEndDone() const {
return (mCblk->flags & CBLK_STREAM_END_DONE) != 0;
}
+status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested)
+{
+ struct timespec total; // total elapsed time spent waiting
+ total.tv_sec = 0;
+ total.tv_nsec = 0;
+ audio_track_cblk_t* cblk = mCblk;
+ status_t status;
+ enum {
+ TIMEOUT_ZERO, // requested == NULL || *requested == 0
+ TIMEOUT_INFINITE, // *requested == infinity
+ TIMEOUT_FINITE, // 0 < *requested < infinity
+ TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE
+ } timeout;
+ if (requested == NULL) {
+ timeout = TIMEOUT_ZERO;
+ } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
+ timeout = TIMEOUT_ZERO;
+ } else if (requested->tv_sec == INT_MAX) {
+ timeout = TIMEOUT_INFINITE;
+ } else {
+ timeout = TIMEOUT_FINITE;
+ }
+ for (;;) {
+ int32_t flags = android_atomic_and(~(CBLK_INTERRUPT|CBLK_STREAM_END_DONE), &cblk->flags);
+ // check for track invalidation by server, or server death detection
+ if (flags & CBLK_INVALID) {
+ ALOGV("Track invalidated");
+ status = DEAD_OBJECT;
+ goto end;
+ }
+ if (flags & CBLK_STREAM_END_DONE) {
+ ALOGV("stream end received");
+ status = NO_ERROR;
+ goto end;
+ }
+ // check for obtainBuffer interrupted by client
+ // check for obtainBuffer interrupted by client
+ if (flags & CBLK_INTERRUPT) {
+ ALOGV("waitStreamEndDone() interrupted by client");
+ status = -EINTR;
+ goto end;
+ }
+ struct timespec remaining;
+ const struct timespec *ts;
+ switch (timeout) {
+ case TIMEOUT_ZERO:
+ status = WOULD_BLOCK;
+ goto end;
+ case TIMEOUT_INFINITE:
+ ts = NULL;
+ break;
+ case TIMEOUT_FINITE:
+ timeout = TIMEOUT_CONTINUE;
+ if (MAX_SEC == 0) {
+ ts = requested;
+ break;
+ }
+ // fall through
+ case TIMEOUT_CONTINUE:
+ // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
+ if (requested->tv_sec < total.tv_sec ||
+ (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
+ status = TIMED_OUT;
+ goto end;
+ }
+ remaining.tv_sec = requested->tv_sec - total.tv_sec;
+ if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
+ remaining.tv_nsec += 1000000000;
+ remaining.tv_sec++;
+ }
+ if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
+ remaining.tv_sec = MAX_SEC;
+ remaining.tv_nsec = 0;
+ }
+ ts = &remaining;
+ break;
+ default:
+ LOG_FATAL("waitStreamEndDone() timeout=%d", timeout);
+ ts = NULL;
+ break;
+ }
+ int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
+ if (!(old & CBLK_FUTEX_WAKE)) {
+ int rc;
+ int ret = __futex_syscall4(&cblk->mFutex,
+ mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
+ switch (ret) {
+ case 0: // normal wakeup by server, or by binderDied()
+ case -EWOULDBLOCK: // benign race condition with server
+ case -EINTR: // wait was interrupted by signal or other spurious wakeup
+ case -ETIMEDOUT: // time-out expired
+ break;
+ default:
+ ALOGE("%s unexpected error %d", __func__, ret);
+ status = -ret;
+ goto end;
+ }
+ }
+ }
+
+end:
+ if (requested == NULL) {
+ requested = &kNonBlocking;
+ }
+ return status;
+}
+
// ---------------------------------------------------------------------------
StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
@@ -393,13 +501,19 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer)
if (mIsOut) {
int32_t flush = cblk->u.mStreaming.mFlush;
rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+ front = cblk->u.mStreaming.mFront;
if (flush != mFlush) {
- front = rear;
mFlush = flush;
// effectively obtain then release whatever is in the buffer
android_atomic_release_store(rear, &cblk->u.mStreaming.mFront);
- } else {
- front = cblk->u.mStreaming.mFront;
+ if (front != rear) {
+ int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
+ if (!(old & CBLK_FUTEX_WAKE)) {
+ (void) __futex_syscall3(&cblk->mFutex,
+ mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
+ }
+ }
+ front = rear;
}
} else {
front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
@@ -517,6 +631,11 @@ size_t AudioTrackServerProxy::framesReady()
return 0;
}
audio_track_cblk_t* cblk = mCblk;
+
+ int32_t flush = cblk->u.mStreaming.mFlush;
+ if (flush != mFlush) {
+ return mFrameCount;
+ }
// the acquire might not be necessary since not doing a subsequent read
int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
ssize_t filled = rear - cblk->u.mStreaming.mFront;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 2e2c0cc..c670936 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -372,7 +372,6 @@ public:
audio_channel_mask_t channelMask = pChannelMask != NULL ?
*pChannelMask : (audio_channel_mask_t)0;
uint32_t latency = pLatencyMs != NULL ? *pLatencyMs : 0;
-
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(module);
data.writeInt32(devices);
@@ -381,6 +380,12 @@ public:
data.writeInt32(channelMask);
data.writeInt32(latency);
data.writeInt32((int32_t) flags);
+ if (offloadInfo == NULL) {
+ data.writeInt32(0);
+ } else {
+ data.writeInt32(1);
+ data.write(offloadInfo, sizeof(audio_offload_info_t));
+ }
remote()->transact(OPEN_OUTPUT, data, &reply);
audio_io_handle_t output = (audio_io_handle_t) reply.readInt32();
ALOGV("openOutput() returned output, %d", output);
@@ -881,13 +886,19 @@ status_t BnAudioFlinger::onTransact(
audio_channel_mask_t channelMask = (audio_channel_mask_t)data.readInt32();
uint32_t latency = data.readInt32();
audio_output_flags_t flags = (audio_output_flags_t) data.readInt32();
+ bool hasOffloadInfo = data.readInt32() != 0;
+ audio_offload_info_t offloadInfo;
+ if (hasOffloadInfo) {
+ data.read(&offloadInfo, sizeof(audio_offload_info_t));
+ }
audio_io_handle_t output = openOutput(module,
&devices,
&samplingRate,
&format,
&channelMask,
&latency,
- flags);
+ flags,
+ hasOffloadInfo ? &offloadInfo : NULL);
ALOGV("OPEN_OUTPUT output, %p", output);
reply->writeInt32((int32_t) output);
reply->writeInt32(devices);
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 57de58f..4be3c09 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -137,6 +137,12 @@ public:
data.writeInt32(static_cast <uint32_t>(format));
data.writeInt32(channelMask);
data.writeInt32(static_cast <uint32_t>(flags));
+ if (offloadInfo == NULL) {
+ data.writeInt32(0);
+ } else {
+ data.writeInt32(1);
+ data.write(offloadInfo, sizeof(audio_offload_info_t));
+ }
remote()->transact(GET_OUTPUT, data, &reply);
return static_cast <audio_io_handle_t> (reply.readInt32());
}
@@ -379,9 +385,11 @@ public:
virtual bool isOffloadSupported(const audio_offload_info_t& info)
{
- // stub function
- return false;
- }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.write(&info, sizeof(audio_offload_info_t));
+ remote()->transact(IS_OFFLOAD_SUPPORTED, data, &reply);
+ return reply.readInt32(); }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -450,12 +458,17 @@ status_t BnAudioPolicyService::onTransact(
audio_channel_mask_t channelMask = data.readInt32();
audio_output_flags_t flags =
static_cast <audio_output_flags_t>(data.readInt32());
-
+ bool hasOffloadInfo = data.readInt32() != 0;
+ audio_offload_info_t offloadInfo;
+ if (hasOffloadInfo) {
+ data.read(&offloadInfo, sizeof(audio_offload_info_t));
+ }
audio_io_handle_t output = getOutput(stream,
samplingRate,
format,
channelMask,
- flags);
+ flags,
+ hasOffloadInfo ? &offloadInfo : NULL);
reply->writeInt32(static_cast <int>(output));
return NO_ERROR;
} break;
@@ -662,6 +675,15 @@ status_t BnAudioPolicyService::onTransact(
return status;
}
+ case IS_OFFLOAD_SUPPORTED: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_offload_info_t info;
+ data.read(&info, sizeof(audio_offload_info_t));
+ bool isSupported = isOffloadSupported(info);
+ reply->writeInt32(isSupported);
+ return NO_ERROR;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}