diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/libmedia/AudioSystem.cpp | 8 | ||||
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 225 | ||||
-rw-r--r-- | media/libmedia/AudioTrackShared.cpp | 155 | ||||
-rw-r--r-- | media/libmedia/IAudioFlinger.cpp | 15 | ||||
-rw-r--r-- | media/libmedia/IAudioFlingerClient.cpp | 6 | ||||
-rw-r--r-- | media/libmedia/IAudioPolicyService.cpp | 32 | ||||
-rw-r--r-- | media/libmedia/IAudioRecord.cpp | 23 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 284 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 7 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 1 | ||||
-rw-r--r-- | media/libstagefright/AudioPlayer.cpp | 348 | ||||
-rw-r--r-- | media/libstagefright/AwesomePlayer.cpp | 246 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/Utils.cpp | 123 | ||||
-rw-r--r-- | media/libstagefright/include/AwesomePlayer.h | 2 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXNodeInstance.cpp | 3 |
16 files changed, 1208 insertions, 276 deletions
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 0d59af0..a571fe4 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -447,9 +447,9 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle OutputDescriptor *outputDesc = new OutputDescriptor(*desc); gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %u, format %d channels %#x frameCount %u " + ALOGV("ioConfigChanged() new output samplingRate %u, format %d channel mask %#x frameCount %u " "latency %d", - outputDesc->samplingRate, outputDesc->format, outputDesc->channels, + outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask, outputDesc->frameCount, outputDesc->latency); } break; case OUTPUT_CLOSED: { @@ -471,10 +471,10 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (param2 == NULL) break; desc = (const OutputDescriptor *)param2; - ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %d channels %#x " + ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %d channel mask %#x " "frameCount %d latency %d", ioHandle, desc->samplingRate, desc->format, - desc->channels, desc->frameCount, desc->latency); + desc->channelMask, desc->frameCount, desc->latency); OutputDescriptor *outputDesc = gOutputs.valueAt(index); delete outputDesc; outputDesc = new OutputDescriptor(*desc); 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 55bf175..e5f7fcd 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -26,7 +26,7 @@ extern "C" { namespace android { audio_track_cblk_t::audio_track_cblk_t() - : server(0), frameCount_(0), mFutex(0), mMinimum(0), + : mServer(0), frameCount_(0), mFutex(0), mMinimum(0), mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0) { memset(&u, 0, sizeof(u)); @@ -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; } @@ -322,6 +323,121 @@ void AudioTrackClientProxy::flush() mCblk->u.mStreaming.mFlush++; } +bool AudioTrackClientProxy::clearStreamEndDone() { + 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, @@ -385,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); @@ -472,7 +594,7 @@ void ServerProxy::releaseBuffer(Buffer* buffer) android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear); } - mCblk->server += stepCount; + mCblk->mServer += stepCount; size_t half = mFrameCount / 2; if (half == 0) { @@ -509,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; @@ -524,6 +651,16 @@ size_t AudioTrackServerProxy::framesReady() return filled; } +bool AudioTrackServerProxy::setStreamEndDone() { + bool old = + (android_atomic_or(CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0; + if (!old) { + (void) __futex_syscall3(&mCblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, + 1); + } + return old; +} + // --------------------------------------------------------------------------- StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, @@ -668,7 +805,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) } mPosition = newPosition; - cblk->server += stepCount; + cblk->mServer += stepCount; cblk->u.mStatic.mBufferPosition = newPosition; if (setFlags != 0) { (void) android_atomic_or(setFlags, &cblk->flags); 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/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 2d1e0f8..3c0d4cf 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -54,7 +54,7 @@ public: (const AudioSystem::OutputDescriptor *)param2; data.writeInt32(desc->samplingRate); data.writeInt32(desc->format); - data.writeInt32(desc->channels); + data.writeInt32(desc->channelMask); data.writeInt32(desc->frameCount); data.writeInt32(desc->latency); } @@ -83,8 +83,8 @@ status_t BnAudioFlingerClient::onTransact( ALOGV("STREAM_CONFIG_CHANGED stream %d", stream); } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { desc.samplingRate = data.readInt32(); - desc.format = data.readInt32(); - desc.channels = data.readInt32(); + desc.format = (audio_format_t) data.readInt32(); + desc.channelMask = (audio_channel_mask_t) data.readInt32(); desc.frameCount = data.readInt32(); desc.latency = data.readInt32(); param2 = &desc; 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); } diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp index 0d06e98..4a7de65 100644 --- a/media/libmedia/IAudioRecord.cpp +++ b/media/libmedia/IAudioRecord.cpp @@ -42,6 +42,18 @@ public: { } + virtual sp<IMemory> getCblk() const + { + Parcel data, reply; + sp<IMemory> cblk; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_CBLK, data, &reply); + if (status == NO_ERROR) { + cblk = interface_cast<IMemory>(reply.readStrongBinder()); + } + return cblk; + } + virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession) { Parcel data, reply; @@ -64,17 +76,6 @@ public: remote()->transact(STOP, data, &reply); } - virtual sp<IMemory> getCblk() const - { - Parcel data, reply; - sp<IMemory> cblk; - data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_CBLK, data, &reply); - if (status == NO_ERROR) { - cblk = interface_cast<IMemory>(reply.readStrongBinder()); - } - return cblk; - } }; IMPLEMENT_META_INTERFACE(AudioRecord, "android.media.IAudioRecord"); diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index afde373..8833bd7 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -53,6 +53,8 @@ #include <media/AudioTrack.h> #include <media/MemoryLeakTrackUtil.h> #include <media/stagefright/MediaErrors.h> +#include <media/stagefright/AudioPlayer.h> +#include <media/stagefright/foundation/ADebug.h> #include <system/audio.h> @@ -1381,6 +1383,45 @@ status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritt return OK; } +status_t MediaPlayerService::AudioOutput::setParameters(const String8& keyValuePairs) +{ + if (mTrack == 0) return NO_INIT; + return mTrack->setParameters(keyValuePairs); +} + +String8 MediaPlayerService::AudioOutput::getParameters(const String8& keys) +{ + if (mTrack == 0) return String8::empty(); + return mTrack->getParameters(keys); +} + +void MediaPlayerService::AudioOutput::deleteRecycledTrack() +{ + ALOGV("deleteRecycledTrack"); + + if (mRecycledTrack != 0) { + + if (mCallbackData != NULL) { + mCallbackData->setOutput(NULL); + mCallbackData->endTrackSwitch(); + } + + if ((mRecycledTrack->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) { + mRecycledTrack->flush(); + } + // An offloaded track isn't flushed because the STREAM_END is reported + // slightly prematurely to allow time for the gapless track switch + // but this means that if we decide not to recycle the track there + // could be a small amount of residual data still playing. We leave + // AudioFlinger to drain the track. + + mRecycledTrack.clear(); + delete mCallbackData; + mCallbackData = NULL; + close(); + } +} + status_t MediaPlayerService::AudioOutput::open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount, @@ -1397,20 +1438,34 @@ status_t MediaPlayerService::AudioOutput::open( bufferCount = mMinBufferCount; } - ALOGV("open(%u, %d, 0x%x, %d, %d, %d)", sampleRate, channelCount, channelMask, - format, bufferCount, mSessionId); + ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask, + format, bufferCount, mSessionId, flags); uint32_t afSampleRate; size_t afFrameCount; uint32_t frameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { - return NO_INIT; - } - if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) { - return NO_INIT; + // offloading is only supported in callback mode for now. + // offloadInfo must be present if offload flag is set + if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) && + ((cb == NULL) || (offloadInfo == NULL))) { + return BAD_VALUE; } - frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate; + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + frameCount = 0; // AudioTrack will get frame count from AudioFlinger + } else { + uint32_t afSampleRate; + size_t afFrameCount; + + if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { + return NO_INIT; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) { + return NO_INIT; + } + + frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate; + } if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) { channelMask = audio_channel_out_mask_from_count(channelCount); @@ -1420,65 +1475,108 @@ status_t MediaPlayerService::AudioOutput::open( } } - sp<AudioTrack> t; - CallbackData *newcbd = NULL; - if (mCallback != NULL) { - newcbd = new CallbackData(this); - t = new AudioTrack( - mStreamType, - sampleRate, - format, - channelMask, - frameCount, - flags, - CallbackWrapper, - newcbd, - 0, // notification frames - mSessionId); - } else { - t = new AudioTrack( - mStreamType, - sampleRate, - format, - channelMask, - frameCount, - flags, - NULL, - NULL, - 0, - mSessionId); - } - - if ((t == 0) || (t->initCheck() != NO_ERROR)) { - ALOGE("Unable to create audio track"); - delete newcbd; - return NO_INIT; - } - + // Check whether we can recycle the track + bool reuse = false; + bool bothOffloaded = false; if (mRecycledTrack != 0) { + // check whether we are switching between two offloaded tracks + bothOffloaded = (flags & mRecycledTrack->getFlags() + & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; + // check if the existing track can be reused as-is, or if a new track needs to be created. + reuse = true; - bool reuse = true; if ((mCallbackData == NULL && mCallback != NULL) || (mCallbackData != NULL && mCallback == NULL)) { // recycled track uses callbacks but the caller wants to use writes, or vice versa ALOGV("can't chain callback and write"); reuse = false; } else if ((mRecycledTrack->getSampleRate() != sampleRate) || - (mRecycledTrack->channelCount() != channelCount) || - (mRecycledTrack->frameCount() != t->frameCount())) { - ALOGV("samplerate, channelcount or framecount differ: %d/%d Hz, %d/%d ch, %d/%d frames", + (mRecycledTrack->channelCount() != (uint32_t)channelCount) ) { + ALOGV("samplerate, channelcount differ: %u/%u Hz, %u/%d ch", mRecycledTrack->getSampleRate(), sampleRate, - mRecycledTrack->channelCount(), channelCount, - mRecycledTrack->frameCount(), t->frameCount()); + mRecycledTrack->channelCount(), channelCount); reuse = false; } else if (flags != mFlags) { ALOGV("output flags differ %08x/%08x", flags, mFlags); reuse = false; + } else if (mRecycledTrack->format() != format) { + reuse = false; + } + } else { + ALOGV("no track available to recycle"); + } + + ALOGV_IF(bothOffloaded, "both tracks offloaded"); + + // If we can't recycle and both tracks are offloaded + // we must close the previous output before opening a new one + if (bothOffloaded && !reuse) { + ALOGV("both offloaded and not recycling"); + deleteRecycledTrack(); + } + + sp<AudioTrack> t; + CallbackData *newcbd = NULL; + + // We don't attempt to create a new track if we are recycling an + // offloaded track. But, if we are recycling a non-offloaded or we + // are switching where one is offloaded and one isn't then we create + // the new track in advance so that we can read additional stream info + + if (!(reuse && bothOffloaded)) { + ALOGV("creating new AudioTrack"); + + if (mCallback != NULL) { + newcbd = new CallbackData(this); + t = new AudioTrack( + mStreamType, + sampleRate, + format, + channelMask, + frameCount, + flags, + CallbackWrapper, + newcbd, + 0, // notification frames + mSessionId, + AudioTrack::TRANSFER_CALLBACK, + offloadInfo); + } else { + t = new AudioTrack( + mStreamType, + sampleRate, + format, + channelMask, + frameCount, + flags, + NULL, + NULL, + 0, + mSessionId); + } + + if ((t == 0) || (t->initCheck() != NO_ERROR)) { + ALOGE("Unable to create audio track"); + delete newcbd; + return NO_INIT; + } + } + + if (reuse) { + CHECK(mRecycledTrack != NULL); + + if (!bothOffloaded) { + if (mRecycledTrack->frameCount() != t->frameCount()) { + ALOGV("framecount differs: %u/%u frames", + mRecycledTrack->frameCount(), t->frameCount()); + reuse = false; + } } + if (reuse) { - ALOGV("chaining to next output"); + ALOGV("chaining to next output and recycling track"); close(); mTrack = mRecycledTrack; mRecycledTrack.clear(); @@ -1488,19 +1586,16 @@ status_t MediaPlayerService::AudioOutput::open( delete newcbd; return OK; } + } - // if we're not going to reuse the track, unblock and flush it - if (mCallbackData != NULL) { - mCallbackData->setOutput(NULL); - mCallbackData->endTrackSwitch(); - } - mRecycledTrack->flush(); - mRecycledTrack.clear(); - delete mCallbackData; - mCallbackData = NULL; - close(); + // we're not going to reuse the track, unblock and flush it + // this was done earlier if both tracks are offloaded + if (!bothOffloaded) { + deleteRecycledTrack(); } + CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL))); + mCallbackData = newcbd; ALOGV("setVolume"); t->setVolume(mLeftVolume, mRightVolume); @@ -1514,15 +1609,19 @@ status_t MediaPlayerService::AudioOutput::open( } mTrack = t; - status_t res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000); - if (res != NO_ERROR) { - return res; + status_t res = NO_ERROR; + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) { + res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000); + if (res == NO_ERROR) { + t->setAuxEffectSendLevel(mSendLevel); + res = t->attachAuxEffect(mAuxEffectId); + } } - t->setAuxEffectSendLevel(mSendLevel); - return t->attachAuxEffect(mAuxEffectId);; + ALOGV("open() DONE status %d", res); + return res; } -void MediaPlayerService::AudioOutput::start() +status_t MediaPlayerService::AudioOutput::start() { ALOGV("start"); if (mCallbackData != NULL) { @@ -1531,8 +1630,9 @@ void MediaPlayerService::AudioOutput::start() if (mTrack != 0) { mTrack->setVolume(mLeftVolume, mRightVolume); mTrack->setAuxEffectSendLevel(mSendLevel); - mTrack->start(); + return mTrack->start(); } + return NO_INIT; } void MediaPlayerService::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) { @@ -1645,10 +1745,6 @@ status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId) void MediaPlayerService::AudioOutput::CallbackWrapper( int event, void *cookie, void *info) { //ALOGV("callbackwrapper"); - if (event != AudioTrack::EVENT_MORE_DATA) { - return; - } - CallbackData *data = (CallbackData*)cookie; data->lock(); AudioOutput *me = data->getOutput(); @@ -1657,23 +1753,46 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( // no output set, likely because the track was scheduled to be reused // by another player, but the format turned out to be incompatible. data->unlock(); - buffer->size = 0; + if (buffer != NULL) { + buffer->size = 0; + } return; } - size_t actualSize = (*me->mCallback)( - me, buffer->raw, buffer->size, me->mCallbackCookie, - CB_EVENT_FILL_BUFFER); + switch(event) { + case AudioTrack::EVENT_MORE_DATA: { + size_t actualSize = (*me->mCallback)( + me, buffer->raw, buffer->size, me->mCallbackCookie, + CB_EVENT_FILL_BUFFER); + + if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { + // We've reached EOS but the audio track is not stopped yet, + // keep playing silence. + + memset(buffer->raw, 0, buffer->size); + actualSize = buffer->size; + } + + buffer->size = actualSize; + } break; - if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { - // We've reached EOS but the audio track is not stopped yet, - // keep playing silence. - memset(buffer->raw, 0, buffer->size); - actualSize = buffer->size; + case AudioTrack::EVENT_STREAM_END: + ALOGV("callbackwrapper: deliver EVENT_STREAM_END"); + (*me->mCallback)(me, NULL /* buffer */, 0 /* size */, + me->mCallbackCookie, CB_EVENT_STREAM_END); + break; + + case AudioTrack::EVENT_NEW_IAUDIOTRACK : + ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN"); + (*me->mCallback)(me, NULL /* buffer */, 0 /* size */, + me->mCallbackCookie, CB_EVENT_TEAR_DOWN); + break; + + default: + ALOGE("received unknown event type: %d inside CallbackWrapper !", event); } - buffer->size = actualSize; data->unlock(); } @@ -1803,10 +1922,11 @@ status_t MediaPlayerService::AudioCache::open( return NO_ERROR; } -void MediaPlayerService::AudioCache::start() { +status_t MediaPlayerService::AudioCache::start() { if (mCallbackThread != NULL) { mCallbackThread->run("AudioCache callback"); } + return NO_ERROR; } void MediaPlayerService::AudioCache::stop() { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index f7076cc..7d27944 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -94,7 +94,7 @@ class MediaPlayerService : public BnMediaPlayerService audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, const audio_offload_info_t *offloadInfo = NULL); - virtual void start(); + virtual status_t start(); virtual ssize_t write(const void* buffer, size_t size); virtual void stop(); virtual void flush(); @@ -112,11 +112,14 @@ class MediaPlayerService : public BnMediaPlayerService void setNextOutput(const sp<AudioOutput>& nextOutput); void switchToNextOutput(); virtual bool needsTrailingPadding() { return mNextOutput == NULL; } + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); private: static void setMinBufferCount(); static void CallbackWrapper( int event, void *me, void *info); + void deleteRecycledTrack(); sp<AudioTrack> mTrack; sp<AudioTrack> mRecycledTrack; @@ -196,7 +199,7 @@ class MediaPlayerService : public BnMediaPlayerService audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, const audio_offload_info_t *offloadInfo = NULL); - virtual void start(); + virtual status_t start(); virtual ssize_t write(const void* buffer, size_t size); virtual void stop(); virtual void flush() {} diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 90bf324..1f68b51 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -100,6 +100,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_mpeg2ts \ libstagefright_id3 \ libFLAC \ + libmedia_helper LOCAL_SRC_FILES += \ chromium_http_stub.cpp diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 61d6746..2418aab 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioPlayer" #include <utils/Log.h> +#include <cutils/compiler.h> #include <binder/IPCThreadState.h> #include <media/AudioTrack.h> @@ -27,6 +28,7 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> #include "include/AwesomePlayer.h" @@ -47,14 +49,17 @@ AudioPlayer::AudioPlayer( mSeeking(false), mReachedEOS(false), mFinalStatus(OK), + mSeekTimeUs(0), mStarted(false), mIsFirstBuffer(false), mFirstBufferResult(OK), mFirstBuffer(NULL), mAudioSink(audioSink), - mAllowDeepBuffering((flags & ALLOW_DEEP_BUFFERING) != 0), mObserver(observer), - mPinnedTimeUs(-1ll) { + mPinnedTimeUs(-1ll), + mPlaying(false), + mStartPosUs(0), + mCreateFlags(flags) { } AudioPlayer::~AudioPlayer() { @@ -109,7 +114,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { const char *mime; bool success = format->findCString(kKeyMIMEType, &mime); CHECK(success); - CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)); + CHECK(useOffload() || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)); success = format->findInt32(kKeySampleRate, &mSampleRate); CHECK(success); @@ -125,16 +130,74 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; } + audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; + + if (useOffload()) { + if (mapMimeToAudioFormat(audioFormat, mime) != OK) { + ALOGE("Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format", mime); + audioFormat = AUDIO_FORMAT_INVALID; + } else { + ALOGV("Mime type \"%s\" mapped to audio_format 0x%x", mime, audioFormat); + } + } + + int avgBitRate = -1; + format->findInt32(kKeyBitRate, &avgBitRate); + if (mAudioSink.get() != NULL) { + uint32_t flags = AUDIO_OUTPUT_FLAG_NONE; + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + + if (allowDeepBuffering()) { + flags |= AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } + if (useOffload()) { + flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + + int64_t durationUs; + if (format->findInt64(kKeyDuration, &durationUs)) { + offloadInfo.duration_us = durationUs; + } else { + offloadInfo.duration_us = -1; + } + + offloadInfo.sample_rate = mSampleRate; + offloadInfo.channel_mask = channelMask; + offloadInfo.format = audioFormat; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; + offloadInfo.bit_rate = avgBitRate; + offloadInfo.has_video = ((mCreateFlags & HAS_VIDEO) != 0); + offloadInfo.is_streaming = ((mCreateFlags & IS_STREAMING) != 0); + } + status_t err = mAudioSink->open( - mSampleRate, numChannels, channelMask, AUDIO_FORMAT_PCM_16_BIT, + mSampleRate, numChannels, channelMask, audioFormat, DEFAULT_AUDIOSINK_BUFFERCOUNT, &AudioPlayer::AudioSinkCallback, this, - (mAllowDeepBuffering ? - AUDIO_OUTPUT_FLAG_DEEP_BUFFER : - AUDIO_OUTPUT_FLAG_NONE)); + (audio_output_flags_t)flags, + useOffload() ? &offloadInfo : NULL); + + if (err == OK) { + mLatencyUs = (int64_t)mAudioSink->latency() * 1000; + mFrameSize = mAudioSink->frameSize(); + + if (useOffload()) { + // If the playback is offloaded to h/w we pass the + // HAL some metadata information + // We don't want to do this for PCM because it will be going + // through the AudioFlinger mixer before reaching the hardware + sendMetaDataToHal(mAudioSink, format); + } + + err = mAudioSink->start(); + // do not alter behavior for non offloaded tracks: ignore start status. + if (!useOffload()) { + err = OK; + } + } + if (err != OK) { if (mFirstBuffer != NULL) { mFirstBuffer->release(); @@ -148,10 +211,6 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { return err; } - mLatencyUs = (int64_t)mAudioSink->latency() * 1000; - mFrameSize = mAudioSink->frameSize(); - - mAudioSink->start(); } else { // playing to an AudioTrack, set up mask if necessary audio_channel_mask_t audioMask = channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER ? @@ -186,6 +245,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { } mStarted = true; + mPlaying = true; mPinnedTimeUs = -1ll; return OK; @@ -212,27 +272,56 @@ void AudioPlayer::pause(bool playPendingSamples) { mPinnedTimeUs = ALooper::GetNowUs(); } + + mPlaying = false; } -void AudioPlayer::resume() { +status_t AudioPlayer::resume() { CHECK(mStarted); + status_t err; if (mAudioSink.get() != NULL) { - mAudioSink->start(); + err = mAudioSink->start(); } else { - mAudioTrack->start(); + err = mAudioTrack->start(); } + + if (err == OK) { + mPlaying = true; + } + + return err; } void AudioPlayer::reset() { CHECK(mStarted); + ALOGV("reset: mPlaying=%d mReachedEOS=%d useOffload=%d", + mPlaying, mReachedEOS, useOffload() ); + if (mAudioSink.get() != NULL) { mAudioSink->stop(); + // If we're closing and have reached EOS, we don't want to flush + // the track because if it is offloaded there could be a small + // amount of residual data in the hardware buffer which we must + // play to give gapless playback. + // But if we're resetting when paused or before we've reached EOS + // we can't be doing a gapless playback and there could be a large + // amount of data queued in the hardware if the track is offloaded, + // so we must flush to prevent a track switch being delayed playing + // the buffered data that we don't want now + if (!mPlaying || !mReachedEOS) { + mAudioSink->flush(); + } + mAudioSink->close(); } else { mAudioTrack->stop(); + if (!mPlaying || !mReachedEOS) { + mAudioTrack->flush(); + } + mAudioTrack.clear(); } @@ -256,10 +345,16 @@ void AudioPlayer::reset() { // The following hack is necessary to ensure that the OMX // component is completely released by the time we may try // to instantiate it again. - wp<MediaSource> tmp = mSource; - mSource.clear(); - while (tmp.promote() != NULL) { - usleep(1000); + // When offloading, the OMX component is not used so this hack + // is not needed + if (!useOffload()) { + wp<MediaSource> tmp = mSource; + mSource.clear(); + while (tmp.promote() != NULL) { + usleep(1000); + } + } else { + mSource.clear(); } IPCThreadState::self()->flushCommands(); @@ -271,6 +366,8 @@ void AudioPlayer::reset() { mReachedEOS = false; mFinalStatus = OK; mStarted = false; + mPlaying = false; + mStartPosUs = 0; } // static @@ -291,6 +388,15 @@ bool AudioPlayer::reachedEOS(status_t *finalStatus) { return mReachedEOS; } +void AudioPlayer::notifyAudioEOS() { + ALOGV("AudioPlayer@0x%p notifyAudioEOS", this); + + if (mObserver != NULL) { + mObserver->postAudioEOS(0); + ALOGV("Notified observer of EOS!"); + } +} + status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) { if (mAudioSink.get() != NULL) { return mAudioSink->setPlaybackRatePermille(ratePermille); @@ -308,18 +414,40 @@ size_t AudioPlayer::AudioSinkCallback( MediaPlayerBase::AudioSink::cb_event_t event) { AudioPlayer *me = (AudioPlayer *)cookie; - return me->fillBuffer(buffer, size); -} + switch(event) { + case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER: + return me->fillBuffer(buffer, size); -void AudioPlayer::AudioCallback(int event, void *info) { - if (event != AudioTrack::EVENT_MORE_DATA) { - return; + case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END: + ALOGV("AudioSinkCallback: stream end"); + me->mReachedEOS = true; + me->notifyAudioEOS(); + break; + + case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN: + ALOGV("AudioSinkCallback: Tear down event"); + me->mObserver->postAudioTearDown(); + break; } - AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; - size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size); + return 0; +} + +void AudioPlayer::AudioCallback(int event, void *info) { + switch (event) { + case AudioTrack::EVENT_MORE_DATA: + { + AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; + size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size); + buffer->size = numBytesWritten; + } + break; - buffer->size = numBytesWritten; + case AudioTrack::EVENT_STREAM_END: + mReachedEOS = true; + notifyAudioEOS(); + break; + } } uint32_t AudioPlayer::getNumFramesPendingPlayout() const { @@ -359,6 +487,7 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { size_t size_remaining = size; while (size_remaining > 0) { MediaSource::ReadOptions options; + bool refreshSeekTime = false; { Mutex::Autolock autoLock(mLock); @@ -373,6 +502,7 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { } options.setSeekTo(mSeekTimeUs); + refreshSeekTime = true; if (mInputBuffer != NULL) { mInputBuffer->release(); @@ -405,43 +535,56 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { Mutex::Autolock autoLock(mLock); if (err != OK) { - if (mObserver && !mReachedEOS) { - // We don't want to post EOS right away but only - // after all frames have actually been played out. - - // These are the number of frames submitted to the - // AudioTrack that you haven't heard yet. - uint32_t numFramesPendingPlayout = - getNumFramesPendingPlayout(); - - // These are the number of frames we're going to - // submit to the AudioTrack by returning from this - // callback. - uint32_t numAdditionalFrames = size_done / mFrameSize; - - numFramesPendingPlayout += numAdditionalFrames; - - int64_t timeToCompletionUs = - (1000000ll * numFramesPendingPlayout) / mSampleRate; - - ALOGV("total number of frames played: %lld (%lld us)", - (mNumFramesPlayed + numAdditionalFrames), - 1000000ll * (mNumFramesPlayed + numAdditionalFrames) - / mSampleRate); - - ALOGV("%d frames left to play, %lld us (%.2f secs)", - numFramesPendingPlayout, - timeToCompletionUs, timeToCompletionUs / 1E6); - - postEOS = true; - if (mAudioSink->needsTrailingPadding()) { - postEOSDelayUs = timeToCompletionUs + mLatencyUs; + if (!mReachedEOS) { + if (useOffload()) { + // no more buffers to push - stop() and wait for STREAM_END + // don't set mReachedEOS until stream end received + if (mAudioSink != NULL) { + mAudioSink->stop(); + } else { + mAudioTrack->stop(); + } } else { - postEOSDelayUs = 0; + if (mObserver) { + // We don't want to post EOS right away but only + // after all frames have actually been played out. + + // These are the number of frames submitted to the + // AudioTrack that you haven't heard yet. + uint32_t numFramesPendingPlayout = + getNumFramesPendingPlayout(); + + // These are the number of frames we're going to + // submit to the AudioTrack by returning from this + // callback. + uint32_t numAdditionalFrames = size_done / mFrameSize; + + numFramesPendingPlayout += numAdditionalFrames; + + int64_t timeToCompletionUs = + (1000000ll * numFramesPendingPlayout) / mSampleRate; + + ALOGV("total number of frames played: %lld (%lld us)", + (mNumFramesPlayed + numAdditionalFrames), + 1000000ll * (mNumFramesPlayed + numAdditionalFrames) + / mSampleRate); + + ALOGV("%d frames left to play, %lld us (%.2f secs)", + numFramesPendingPlayout, + timeToCompletionUs, timeToCompletionUs / 1E6); + + postEOS = true; + if (mAudioSink->needsTrailingPadding()) { + postEOSDelayUs = timeToCompletionUs + mLatencyUs; + } else { + postEOSDelayUs = 0; + } + } + + mReachedEOS = true; } } - mReachedEOS = true; mFinalStatus = err; break; } @@ -452,17 +595,34 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { mLatencyUs = (int64_t)mAudioTrack->latency() * 1000; } - CHECK(mInputBuffer->meta_data()->findInt64( + if(mInputBuffer->range_length() != 0) { + CHECK(mInputBuffer->meta_data()->findInt64( kKeyTime, &mPositionTimeMediaUs)); + } + + // need to adjust the mStartPosUs for offload decoding since parser + // might not be able to get the exact seek time requested. + if (refreshSeekTime && useOffload()) { + if (postSeekComplete) { + ALOGV("fillBuffer is going to post SEEK_COMPLETE"); + mObserver->postAudioSeekComplete(); + postSeekComplete = false; + } + + mStartPosUs = mPositionTimeMediaUs; + ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6); + } - mPositionTimeRealUs = - ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) - / mSampleRate; + if (!useOffload()) { + mPositionTimeRealUs = + ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) + / mSampleRate; + ALOGV("buffer->size() = %d, " + "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", + mInputBuffer->range_length(), + mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); + } - ALOGV("buffer->size() = %d, " - "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", - mInputBuffer->range_length(), - mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); } if (mInputBuffer->range_length() == 0) { @@ -488,6 +648,13 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { size_remaining -= copy; } + if (useOffload()) { + // We must ask the hardware what it has played + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + ALOGV("mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", + mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); + } + { Mutex::Autolock autoLock(mLock); mNumFramesPlayed += size_done / mFrameSize; @@ -536,9 +703,36 @@ int64_t AudioPlayer::getRealTimeUsLocked() const { return result + diffUs; } +int64_t AudioPlayer::getOutputPlayPositionUs_l() const +{ + uint32_t playedSamples = 0; + if (mAudioSink != NULL) { + mAudioSink->getPosition(&playedSamples); + } else { + mAudioTrack->getPosition(&playedSamples); + } + + const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / mSampleRate; + + // HAL position is relative to the first buffer we sent at mStartPosUs + const int64_t renderedDuration = mStartPosUs + playedUs; + ALOGV("getOutputPlayPositionUs_l %lld", renderedDuration); + return renderedDuration; +} + int64_t AudioPlayer::getMediaTimeUs() { Mutex::Autolock autoLock(mLock); + if (useOffload()) { + if (mSeeking) { + return mSeekTimeUs; + } + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + ALOGV("getMediaTimeUs getOutputPlayPositionUs_l() mPositionTimeRealUs %lld", + mPositionTimeRealUs); + return mPositionTimeRealUs; + } + if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) { if (mSeeking) { return mSeekTimeUs; @@ -547,6 +741,11 @@ int64_t AudioPlayer::getMediaTimeUs() { return 0; } + if (useOffload()) { + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + return mPositionTimeRealUs; + } + int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; if (realTimeOffset < 0) { realTimeOffset = 0; @@ -568,19 +767,34 @@ bool AudioPlayer::getMediaTimeMapping( status_t AudioPlayer::seekTo(int64_t time_us) { Mutex::Autolock autoLock(mLock); + ALOGV("seekTo( %lld )", time_us); + mSeeking = true; mPositionTimeRealUs = mPositionTimeMediaUs = -1; mReachedEOS = false; mSeekTimeUs = time_us; + mStartPosUs = time_us; // Flush resets the number of played frames mNumFramesPlayed = 0; mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); if (mAudioSink != NULL) { + if (mPlaying) { + mAudioSink->pause(); + } mAudioSink->flush(); + if (mPlaying) { + mAudioSink->start(); + } } else { + if (mPlaying) { + mAudioTrack->pause(); + } mAudioTrack->flush(); + if (mPlaying) { + mAudioTrack->start(); + } } return OK; diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b505518..79f2c91 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -47,6 +47,7 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> +#include <media/stagefright/Utils.h> #include <gui/IGraphicBufferProducer.h> #include <gui/Surface.h> @@ -65,6 +66,11 @@ static int64_t kHighWaterMarkUs = 5000000ll; // 5secs static const size_t kLowWaterMarkBytes = 40000; static const size_t kHighWaterMarkBytes = 200000; +// maximum time in paused state when offloading audio decompression. When elapsed, the AudioPlayer +// is destroyed to allow the audio DSP to power down. +static int64_t kOffloadPauseMaxUs = 60000000ll; + + struct AwesomeEvent : public TimedEventQueue::Event { AwesomeEvent( AwesomePlayer *player, @@ -194,7 +200,9 @@ AwesomePlayer::AwesomePlayer() mVideoBuffer(NULL), mDecryptHandle(NULL), mLastVideoTimeUs(-1), - mTextDriver(NULL) { + mTextDriver(NULL), + mOffloadAudio(false), + mAudioTearDown(false) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -206,13 +214,17 @@ AwesomePlayer::AwesomePlayer() mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate); mBufferingEventPending = false; mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate); - mVideoEventPending = false; + mVideoLagEventPending = false; mCheckAudioStatusEvent = new AwesomeEvent( this, &AwesomePlayer::onCheckAudioStatus); mAudioStatusEventPending = false; + mAudioTearDownEvent = new AwesomeEvent(this, + &AwesomePlayer::onAudioTearDownEvent); + mAudioTearDownEventPending = false; + reset(); } @@ -232,6 +244,11 @@ void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) { mQueue.cancelEvent(mVideoLagEvent->eventID()); mVideoLagEventPending = false; + if (mOffloadAudio) { + mQueue.cancelEvent(mAudioTearDownEvent->eventID()); + mAudioTearDownEventPending = false; + } + if (!keepNotifications) { mQueue.cancelEvent(mStreamDoneEvent->eventID()); mStreamDoneEventPending = false; @@ -518,7 +535,7 @@ void AwesomePlayer::reset_l() { mVideoTrack.clear(); mExtractor.clear(); - // Shutdown audio first, so that the respone to the reset request + // Shutdown audio first, so that the response to the reset request // appears to happen instantaneously as far as the user is concerned // If we did this later, audio would continue playing while we // shutdown the video-related resources and the player appear to @@ -531,6 +548,7 @@ void AwesomePlayer::reset_l() { mAudioSource->stop(); } mAudioSource.clear(); + mOmxSource.clear(); mTimeSource = NULL; @@ -586,7 +604,7 @@ void AwesomePlayer::reset_l() { } void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { - if (mListener != NULL) { + if ((mListener != NULL) && !mAudioTearDown) { sp<MediaPlayerBase> listener = mListener.promote(); if (listener != NULL) { @@ -842,6 +860,13 @@ void AwesomePlayer::onStreamDone() { pause_l(true /* at eos */); + // If audio hasn't completed MEDIA_SEEK_COMPLETE yet, + // notify MEDIA_SEEK_COMPLETE to observer immediately for state persistence. + if (mWatchForAudioSeekComplete) { + notifyListener_l(MEDIA_SEEK_COMPLETE); + mWatchForAudioSeekComplete = false; + } + modifyFlags(AT_EOS, SET); } } @@ -883,41 +908,42 @@ status_t AwesomePlayer::play_l() { if (mAudioSource != NULL) { if (mAudioPlayer == NULL) { - if (mAudioSink != NULL) { - bool allowDeepBuffering; - int64_t cachedDurationUs; - bool eos; - if (mVideoSource == NULL - && (mDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US || - (getCachedDuration_l(&cachedDurationUs, &eos) && - cachedDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US))) { - allowDeepBuffering = true; - } else { - allowDeepBuffering = false; - } - - mAudioPlayer = new AudioPlayer(mAudioSink, allowDeepBuffering, this); - mAudioPlayer->setSource(mAudioSource); - - mTimeSource = mAudioPlayer; - - // If there was a seek request before we ever started, - // honor the request now. - // Make sure to do this before starting the audio player - // to avoid a race condition. - seekAudioIfNecessary_l(); - } + createAudioPlayer_l(); } CHECK(!(mFlags & AUDIO_RUNNING)); if (mVideoSource == NULL) { + // We don't want to post an error notification at this point, // the error returned from MediaPlayer::start() will suffice. status_t err = startAudioPlayer_l( false /* sendErrorNotification */); + if ((err != OK) && mOffloadAudio) { + ALOGI("play_l() cannot create offload output, fallback to sw decode"); + delete mAudioPlayer; + mAudioPlayer = NULL; + // if the player was started it will take care of stopping the source when destroyed + if (!(mFlags & AUDIOPLAYER_STARTED)) { + mAudioSource->stop(); + } + modifyFlags((AUDIO_RUNNING | AUDIOPLAYER_STARTED), CLEAR); + mOffloadAudio = false; + mAudioSource = mOmxSource; + if (mAudioSource != NULL) { + err = mAudioSource->start(); + + if (err != OK) { + mAudioSource.clear(); + } else { + createAudioPlayer_l(); + err = startAudioPlayer_l(false); + } + } + } + if (err != OK) { delete mAudioPlayer; mAudioPlayer = NULL; @@ -966,19 +992,58 @@ status_t AwesomePlayer::play_l() { return OK; } +void AwesomePlayer::createAudioPlayer_l() +{ + uint32_t flags = 0; + int64_t cachedDurationUs; + bool eos; + + if (mOffloadAudio) { + flags |= AudioPlayer::USE_OFFLOAD; + } else if (mVideoSource == NULL + && (mDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US || + (getCachedDuration_l(&cachedDurationUs, &eos) && + cachedDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US))) { + flags |= AudioPlayer::ALLOW_DEEP_BUFFERING; + } + if (isStreamingHTTP()) { + flags |= AudioPlayer::IS_STREAMING; + } + if (mVideoSource != NULL) { + flags |= AudioPlayer::HAS_VIDEO; + } + + mAudioPlayer = new AudioPlayer(mAudioSink, flags, this); + mAudioPlayer->setSource(mAudioSource); + + mTimeSource = mAudioPlayer; + + // If there was a seek request before we ever started, + // honor the request now. + // Make sure to do this before starting the audio player + // to avoid a race condition. + seekAudioIfNecessary_l(); +} + status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { CHECK(!(mFlags & AUDIO_RUNNING)); + status_t err = OK; if (mAudioSource == NULL || mAudioPlayer == NULL) { return OK; } + if (mOffloadAudio) { + mQueue.cancelEvent(mAudioTearDownEvent->eventID()); + mAudioTearDownEventPending = false; + } + if (!(mFlags & AUDIOPLAYER_STARTED)) { bool wasSeeking = mAudioPlayer->isSeeking(); // We've already started the MediaSource in order to enable // the prefetcher to read its data. - status_t err = mAudioPlayer->start( + err = mAudioPlayer->start( true /* sourceAlreadyStarted */); if (err != OK) { @@ -998,14 +1063,16 @@ status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { postAudioSeekComplete(); } } else { - mAudioPlayer->resume(); + err = mAudioPlayer->resume(); } - modifyFlags(AUDIO_RUNNING, SET); + if (err == OK) { + modifyFlags(AUDIO_RUNNING, SET); - mWatchForAudioEOS = true; + mWatchForAudioEOS = true; + } - return OK; + return err; } void AwesomePlayer::notifyVideoSize_l() { @@ -1137,15 +1204,14 @@ status_t AwesomePlayer::pause_l(bool at_eos) { cancelPlayerEvents(true /* keepNotifications */); if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { - if (at_eos) { - // If we played the audio stream to completion we - // want to make sure that all samples remaining in the audio - // track's queue are played out. - mAudioPlayer->pause(true /* playPendingSamples */); - } else { - mAudioPlayer->pause(); + // If we played the audio stream to completion we + // want to make sure that all samples remaining in the audio + // track's queue are played out. + mAudioPlayer->pause(at_eos /* playPendingSamples */); + // send us a reminder to tear down the AudioPlayer if paused for too long. + if (mOffloadAudio) { + postAudioTearDownEvent(kOffloadPauseMaxUs); } - modifyFlags(AUDIO_RUNNING, CLEAR); } @@ -1290,7 +1356,6 @@ status_t AwesomePlayer::getPosition(int64_t *positionUs) { } else { *positionUs = 0; } - return OK; } @@ -1385,14 +1450,29 @@ status_t AwesomePlayer::initAudioDecoder() { const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); + // Check whether there is a hardware codec for this stream + // This doesn't guarantee that the hardware has a free stream + // but it avoids us attempting to open (and re-open) an offload + // stream to hardware that doesn't have the necessary codec + mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL), isStreamingHTTP()); if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { + ALOGV("createAudioPlayer: bypass OMX (raw)"); mAudioSource = mAudioTrack; } else { - mAudioSource = OMXCodec::Create( + // If offloading we still create a OMX decoder as a fall-back + // but we don't start it + mOmxSource = OMXCodec::Create( mClient.interface(), mAudioTrack->getFormat(), false, // createEncoder mAudioTrack); + + if (mOffloadAudio) { + ALOGV("createAudioPlayer: bypass OMX (offload)"); + mAudioSource = mAudioTrack; + } else { + mAudioSource = mOmxSource; + } } if (mAudioSource != NULL) { @@ -1408,6 +1488,7 @@ status_t AwesomePlayer::initAudioDecoder() { if (err != OK) { mAudioSource.clear(); + mOmxSource.clear(); return err; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_QCELP)) { @@ -1885,6 +1966,15 @@ void AwesomePlayer::postCheckAudioStatusEvent(int64_t delayUs) { mQueue.postEventWithDelay(mCheckAudioStatusEvent, delayUs); } +void AwesomePlayer::postAudioTearDownEvent(int64_t delayUs) { + Mutex::Autolock autoLock(mAudioLock); + if (mAudioTearDownEventPending) { + return; + } + mAudioTearDownEventPending = true; + mQueue.postEventWithDelay(mAudioTearDownEvent, delayUs); +} + void AwesomePlayer::onCheckAudioStatus() { { Mutex::Autolock autoLock(mAudioLock); @@ -2200,7 +2290,10 @@ bool AwesomePlayer::ContinuePreparation(void *cookie) { void AwesomePlayer::onPrepareAsyncEvent() { Mutex::Autolock autoLock(mLock); + beginPrepareAsync_l(); +} +void AwesomePlayer::beginPrepareAsync_l() { if (mFlags & PREPARE_CANCELLED) { ALOGI("prepare was cancelled before doing anything"); abortPrepare(UNKNOWN_ERROR); @@ -2273,6 +2366,10 @@ void AwesomePlayer::postAudioSeekComplete() { postCheckAudioStatusEvent(0); } +void AwesomePlayer::postAudioTearDown() { + postAudioTearDownEvent(0); +} + status_t AwesomePlayer::setParameter(int key, const Parcel &request) { switch (key) { case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS: @@ -2404,6 +2501,7 @@ status_t AwesomePlayer::selectAudioTrack_l( mAudioSource->stop(); } mAudioSource.clear(); + mOmxSource.clear(); mTimeSource = NULL; @@ -2660,4 +2758,66 @@ void AwesomePlayer::modifyFlags(unsigned value, FlagMode mode) { } } +void AwesomePlayer::onAudioTearDownEvent() { + + Mutex::Autolock autoLock(mLock); + if (!mAudioTearDownEventPending) { + return; + } + mAudioTearDownEventPending = false; + + ALOGV("onAudioTearDownEvent"); + + // stream info is cleared by reset_l() so copy what we need + const bool wasPlaying = (mFlags & PLAYING); + KeyedVector<String8, String8> uriHeaders(mUriHeaders); + sp<DataSource> fileSource(mFileSource); + + mStatsLock.lock(); + String8 uri(mStats.mURI); + mStatsLock.unlock(); + + // get current position so we can start recreated stream from here + int64_t position = 0; + getPosition(&position); + + // Reset and recreate + reset_l(); + mFlags |= PREPARING; + + status_t err; + + if (fileSource != NULL) { + mFileSource = fileSource; + err = setDataSource_l(fileSource); + } else { + err = setDataSource_l(uri, &uriHeaders); + } + + if ( err != OK ) { + // This will force beingPrepareAsync_l() to notify + // a MEDIA_ERROR to the client and abort the prepare + mFlags |= PREPARE_CANCELLED; + } + + mAudioTearDown = true; + mIsAsyncPrepare = true; + + // Call parepare for the host decoding + beginPrepareAsync_l(); + + if (mPrepareResult == OK) { + if (mExtractorFlags & MediaExtractor::CAN_SEEK) { + seekTo_l(position); + } + + if (wasPlaying) { + modifyFlags(CACHE_UNDERRUN, CLEAR); + play_l(); + } + } + + mAudioTearDown = false; +} + } // namespace android diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 42a9c7a..ad985ee 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1924,13 +1924,13 @@ status_t MPEG4Extractor::parseTrackHeader( mtime = U64_AT(&buffer[12]); id = U32_AT(&buffer[20]); duration = U64_AT(&buffer[28]); - } else { - CHECK_EQ((unsigned)version, 0u); - + } else if (version == 0) { ctime = U32_AT(&buffer[4]); mtime = U32_AT(&buffer[8]); id = U32_AT(&buffer[12]); duration = U32_AT(&buffer[20]); + } else { + return ERROR_UNSUPPORTED; } mLastTrack->meta->setInt32(kKeyTrackID, id); diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index e9789d3..4db8e80 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -26,7 +26,12 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaDefs.h> +#include <media/AudioSystem.h> +#include <media/MediaPlayerInterface.h> +#include <hardware/audio.h> #include <media/stagefright/Utils.h> +#include <media/AudioParameter.h> namespace android { @@ -474,20 +479,128 @@ AString MakeUserAgent() { status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta) { - // stub + int32_t sampleRate = 0; + int32_t bitRate = 0; + int32_t channelMask = 0; + int32_t delaySamples = 0; + int32_t paddingSamples = 0; + + AudioParameter param = AudioParameter(); + + if (meta->findInt32(kKeySampleRate, &sampleRate)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate); + } + if (meta->findInt32(kKeyChannelMask, &channelMask)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask); + } + if (meta->findInt32(kKeyBitRate, &bitRate)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate); + } + if (meta->findInt32(kKeyEncoderDelay, &delaySamples)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples); + } + if (meta->findInt32(kKeyEncoderPadding, &paddingSamples)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); + } + + ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d," + "delaySample %d, paddingSample %d", bitRate, sampleRate, + channelMask, delaySamples, paddingSamples); + + sink->setParameters(param.toString()); return OK; } -status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime) +struct mime_conv_t { + const char* mime; + audio_format_t format; +}; + +static const struct mime_conv_t mimeLookup[] = { + { MEDIA_MIMETYPE_AUDIO_MPEG, AUDIO_FORMAT_MP3 }, + { MEDIA_MIMETYPE_AUDIO_RAW, AUDIO_FORMAT_PCM_16_BIT }, + { MEDIA_MIMETYPE_AUDIO_AMR_NB, AUDIO_FORMAT_AMR_NB }, + { MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB }, + { MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC }, + { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS }, + { 0, AUDIO_FORMAT_INVALID } +}; + +status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime ) { - // stub +const struct mime_conv_t* p = &mimeLookup[0]; + while (p->mime != NULL) { + if (0 == strcasecmp(mime, p->mime)) { + format = p->format; + return OK; + } + ++p; + } + return BAD_VALUE; } bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming) { - // stub - return false; + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + audio_offload_info_t info = AUDIO_INFO_INITIALIZER; + + info.format = AUDIO_FORMAT_INVALID; + if (mapMimeToAudioFormat(info.format, mime) != OK) { + ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime); + return false; + } else { + ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format); + } + + if (AUDIO_FORMAT_INVALID == info.format) { + // can't offload if we don't know what the source format is + ALOGE("mime type \"%s\" not a known audio format", mime); + return false; + } + + int32_t srate = -1; + if (!meta->findInt32(kKeySampleRate, &srate)) { + ALOGV("track of type '%s' does not publish sample rate", mime); + } + info.sample_rate = srate; + + int32_t cmask = 0; + if (!meta->findInt32(kKeyChannelMask, &cmask)) { + ALOGV("track of type '%s' does not publish channel mask", mime); + + // Try a channel count instead + int32_t channelCount; + if (!meta->findInt32(kKeyChannelCount, &channelCount)) { + ALOGV("track of type '%s' does not publish channel count", mime); + } else { + cmask = audio_channel_out_mask_from_count(channelCount); + } + } + info.channel_mask = cmask; + + int64_t duration = 0; + if (!meta->findInt64(kKeyDuration, &duration)) { + ALOGV("track of type '%s' does not publish duration", mime); + } + info.duration_us = duration; + + int32_t brate = -1; + if (!meta->findInt32(kKeyBitRate, &brate)) { + ALOGV("track of type '%s' does not publish bitrate", mime); + } + info.bit_rate = brate; + + + info.stream_type = AUDIO_STREAM_MUSIC; + info.has_video = hasVideo; + info.is_streaming = isStreaming; + + // Check if offload is possible for given format, stream type, sample rate, + // bit rate, duration, video and streaming + return AudioSystem::isOffloadSupported(info); } } // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 0d17d65..d3c74e2 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -226,7 +226,7 @@ private: void postStreamDoneEvent_l(status_t status); void postCheckAudioStatusEvent(int64_t delayUs); void postVideoLagEvent_l(); - void postAudioTearDownEvent(); + void postAudioTearDownEvent(int64_t delayUs); status_t play_l(); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 61a866f..525e18d 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -596,7 +596,8 @@ status_t OMXNodeInstance::createInputSurface( CHECK(oerr == OMX_ErrorNone); if (def.format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque) { - ALOGE("createInputSurface requires AndroidOpaque color format"); + ALOGE("createInputSurface requires COLOR_FormatSurface " + "(AndroidOpaque) color format"); return INVALID_OPERATION; } |