diff options
Diffstat (limited to 'media')
22 files changed, 362 insertions, 121 deletions
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index fce4389..1f8e9b6 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -32,6 +32,7 @@ namespace android { // client singleton for AudioFlinger binder interface Mutex AudioSystem::gLock; +Mutex AudioSystem::gLockCache; Mutex AudioSystem::gLockAPS; Mutex AudioSystem::gLockAPC; sp<IAudioFlinger> AudioSystem::gAudioFlinger; @@ -50,33 +51,40 @@ size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback; // establish binder interface to AudioFlinger service -const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() +const sp<IAudioFlinger> AudioSystem::get_audio_flinger() { - Mutex::Autolock _l(gLock); - if (gAudioFlinger == 0) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.audio_flinger")); - if (binder != 0) - break; - ALOGW("AudioFlinger not published, waiting..."); - usleep(500000); // 0.5 s - } while (true); - if (gAudioFlingerClient == NULL) { - gAudioFlingerClient = new AudioFlingerClient(); - } else { - if (gAudioErrorCallback) { - gAudioErrorCallback(NO_ERROR); + sp<IAudioFlinger> af; + sp<AudioFlingerClient> afc; + { + Mutex::Autolock _l(gLock); + if (gAudioFlinger == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("media.audio_flinger")); + if (binder != 0) + break; + ALOGW("AudioFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } while (true); + if (gAudioFlingerClient == NULL) { + gAudioFlingerClient = new AudioFlingerClient(); + } else { + if (gAudioErrorCallback) { + gAudioErrorCallback(NO_ERROR); + } } + binder->linkToDeath(gAudioFlingerClient); + gAudioFlinger = interface_cast<IAudioFlinger>(binder); + LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0); + afc = gAudioFlingerClient; } - binder->linkToDeath(gAudioFlingerClient); - gAudioFlinger = interface_cast<IAudioFlinger>(binder); - LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0); - gAudioFlinger->registerClient(gAudioFlingerClient); + af = gAudioFlinger; } - - return gAudioFlinger; + if (afc != 0) { + af->registerClient(afc); + } + return af; } /* static */ status_t AudioSystem::checkAudioFlinger() @@ -250,20 +258,20 @@ status_t AudioSystem::getOutputSamplingRate(uint32_t* samplingRate, audio_stream status_t AudioSystem::getSamplingRate(audio_io_handle_t output, uint32_t* samplingRate) { - OutputDescriptor *outputDesc; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockCache); - gLock.lock(); - outputDesc = AudioSystem::gOutputs.valueFor(output); + OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); if (outputDesc == NULL) { ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output); - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; + gLockCache.unlock(); *samplingRate = af->sampleRate(output); + gLockCache.lock(); } else { ALOGV("getOutputSamplingRate() reading from output desc"); *samplingRate = outputDesc->samplingRate; - gLock.unlock(); } if (*samplingRate == 0) { ALOGE("AudioSystem::getSamplingRate failed for output %d", output); @@ -294,18 +302,18 @@ status_t AudioSystem::getOutputFrameCount(size_t* frameCount, audio_stream_type_ status_t AudioSystem::getFrameCount(audio_io_handle_t output, size_t* frameCount) { - OutputDescriptor *outputDesc; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; - gLock.lock(); - outputDesc = AudioSystem::gOutputs.valueFor(output); + Mutex::Autolock _l(gLockCache); + + OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); if (outputDesc == NULL) { - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; + gLockCache.unlock(); *frameCount = af->frameCount(output); + gLockCache.lock(); } else { *frameCount = outputDesc->frameCount; - gLock.unlock(); } if (*frameCount == 0) { ALOGE("AudioSystem::getFrameCount failed for output %d", output); @@ -336,18 +344,18 @@ status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t st status_t AudioSystem::getLatency(audio_io_handle_t output, uint32_t* latency) { - OutputDescriptor *outputDesc; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockCache); - gLock.lock(); - outputDesc = AudioSystem::gOutputs.valueFor(output); + OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); if (outputDesc == NULL) { - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; + gLockCache.unlock(); *latency = af->latency(output); + gLockCache.lock(); } else { *latency = outputDesc->latency; - gLock.unlock(); } ALOGV("getLatency() output %d, latency %d", output, *latency); @@ -358,24 +366,24 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t* buffSize) { - gLock.lock(); + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(gLockCache); // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values size_t inBuffSize = gInBuffSize; if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat) || (channelMask != gPrevInChannelMask)) { - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) { - return PERMISSION_DENIED; - } + gLockCache.unlock(); inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask); + gLockCache.lock(); if (inBuffSize == 0) { ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x", sampleRate, format, channelMask); return BAD_VALUE; } // A benign race is possible here: we could overwrite a fresher cache entry - gLock.lock(); // save the request params gPrevInSamplingRate = sampleRate; gPrevInFormat = format; @@ -383,7 +391,6 @@ status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t for gInBuffSize = inBuffSize; } - gLock.unlock(); *buffSize = inBuffSize; return NO_ERROR; @@ -450,14 +457,21 @@ audio_hw_sync_t AudioSystem::getAudioHwSyncForSession(audio_session_t sessionId) void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused) { - Mutex::Autolock _l(AudioSystem::gLock); + audio_error_callback cb = NULL; + { + Mutex::Autolock _l(AudioSystem::gLock); + AudioSystem::gAudioFlinger.clear(); + cb = gAudioErrorCallback; + } - AudioSystem::gAudioFlinger.clear(); - // clear output handles and stream to output map caches - AudioSystem::gOutputs.clear(); + { + // clear output handles and stream to output map caches + Mutex::Autolock _l(gLockCache); + AudioSystem::gOutputs.clear(); + } - if (gAudioErrorCallback) { - gAudioErrorCallback(DEAD_OBJECT); + if (cb) { + cb(DEAD_OBJECT); } ALOGW("AudioFlinger server died!"); } @@ -470,7 +484,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (ioHandle == AUDIO_IO_HANDLE_NONE) return; - Mutex::Autolock _l(AudioSystem::gLock); + Mutex::Autolock _l(AudioSystem::gLockCache); switch (event) { case STREAM_CONFIG_CHANGED: @@ -539,29 +553,37 @@ sp<AudioSystem::AudioPolicyServiceClient> AudioSystem::gAudioPolicyServiceClient // establish binder interface to AudioPolicy service -const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service() -{ - Mutex::Autolock _l(gLockAPS); - if (gAudioPolicyService == 0) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.audio_policy")); - if (binder != 0) - break; - ALOGW("AudioPolicyService not published, waiting..."); - usleep(500000); // 0.5 s - } while (true); - if (gAudioPolicyServiceClient == NULL) { - gAudioPolicyServiceClient = new AudioPolicyServiceClient(); +const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service() +{ + sp<IAudioPolicyService> ap; + sp<AudioPolicyServiceClient> apc; + { + Mutex::Autolock _l(gLockAPS); + if (gAudioPolicyService == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("media.audio_policy")); + if (binder != 0) + break; + ALOGW("AudioPolicyService not published, waiting..."); + usleep(500000); // 0.5 s + } while (true); + if (gAudioPolicyServiceClient == NULL) { + gAudioPolicyServiceClient = new AudioPolicyServiceClient(); + } + binder->linkToDeath(gAudioPolicyServiceClient); + gAudioPolicyService = interface_cast<IAudioPolicyService>(binder); + LOG_ALWAYS_FATAL_IF(gAudioPolicyService == 0); + apc = gAudioPolicyServiceClient; } - binder->linkToDeath(gAudioPolicyServiceClient); - gAudioPolicyService = interface_cast<IAudioPolicyService>(binder); - LOG_ALWAYS_FATAL_IF(gAudioPolicyService == 0); - gAudioPolicyService->registerClient(gAudioPolicyServiceClient); + ap = gAudioPolicyService; + } + if (apc != 0) { + ap->registerClient(apc); } - return gAudioPolicyService; + return ap; } // --------------------------------------------------------------------------- @@ -829,8 +851,11 @@ void AudioSystem::clearAudioConfigCache() // called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances ALOGV("clearAudioConfigCache()"); { - Mutex::Autolock _l(gLock); + Mutex::Autolock _l(gLockCache); gOutputs.clear(); + } + { + Mutex::Autolock _l(gLock); gAudioFlinger.clear(); } { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 2f57b9d..c11050e 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -2149,6 +2149,11 @@ void AudioTrack::setStreamTypeFromAttributes(audio_attributes_t& aa) { mStreamType = AUDIO_STREAM_ALARM; break; } + audio_mode_t phoneState = AudioSystem::getPhoneState(); + if (phoneState == AUDIO_MODE_IN_CALL || phoneState == AUDIO_MODE_IN_COMMUNICATION) { + mStreamType = AUDIO_STREAM_VOICE_CALL; + break; + } } /// FALL THROUGH case AUDIO_USAGE_MEDIA: case AUDIO_USAGE_GAME: diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 561cb24..62362da 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -25,6 +25,12 @@ namespace android { +// used to clamp a value to size_t. TODO: move to another file. +template <typename T> +size_t clampToSize(T x) { + return x > SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x; +} + audio_track_cblk_t::audio_track_cblk_t() : mServer(0), mFutex(0), mMinimum(0), mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0), mFlags(0) @@ -728,7 +734,8 @@ StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cbl size_t frameCount, size_t frameSize) : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize), mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0), - mEnd(frameCount), mFramesReadyIsCalledByMultipleThreads(false) + mFramesReadySafe(frameCount), mFramesReady(frameCount), + mFramesReadyIsCalledByMultipleThreads(false) { mState.mLoopStart = 0; mState.mLoopEnd = 0; @@ -742,20 +749,11 @@ void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() size_t StaticAudioTrackServerProxy::framesReady() { - // FIXME - // This is racy if called by normal mixer thread, - // as we're reading 2 independent variables without a lock. - // Can't call mObserver.poll(), as we might be called from wrong thread. - // If looping is enabled, should return a higher number (since includes non-contiguous). - size_t position = mPosition; + // Can't call pollPosition() from multiple threads. if (!mFramesReadyIsCalledByMultipleThreads) { - ssize_t positionOrStatus = pollPosition(); - if (positionOrStatus >= 0) { - position = (size_t) positionOrStatus; - } + (void) pollPosition(); } - size_t end = mEnd; - return position < end ? end - position : 0; + return mFramesReadySafe; } ssize_t StaticAudioTrackServerProxy::pollPosition() @@ -772,25 +770,35 @@ ssize_t StaticAudioTrackServerProxy::pollPosition() } // ignore loopEnd mPosition = position = loopStart; - mEnd = mFrameCount; + mFramesReady = mFrameCount - mPosition; mState.mLoopCount = 0; valid = true; - } else { + } else if (state.mLoopCount >= -1) { if (loopStart < loopEnd && loopEnd <= mFrameCount && loopEnd - loopStart >= MIN_LOOP) { if (!(loopStart <= position && position < loopEnd)) { mPosition = position = loopStart; } - mEnd = loopEnd; + if (state.mLoopCount == -1) { + mFramesReady = INT64_MAX; + } else { + // mFramesReady is 64 bits to handle the effective number of frames + // that the static audio track contains, including loops. + // TODO: Later consider fixing overflow, but does not seem needed now + // as will not overflow if loopStart and loopEnd are Java "ints". + mFramesReady = int64_t(state.mLoopCount) * (loopEnd - loopStart) + + mFrameCount - mPosition; + } mState = state; valid = true; } } - if (!valid) { + if (!valid || mPosition > mFrameCount) { ALOGE("%s client pushed an invalid state, shutting down", __func__); mIsShutdown = true; return (ssize_t) NO_INIT; } + mFramesReadySafe = clampToSize(mFramesReady); // This may overflow, but client is not supposed to rely on it mCblk->u.mStatic.mBufferPosition = (uint32_t) position; } @@ -815,9 +823,10 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush return (status_t) positionOrStatus; } size_t position = (size_t) positionOrStatus; + size_t end = mState.mLoopCount != 0 ? mState.mLoopEnd : mFrameCount; size_t avail; - if (position < mEnd) { - avail = mEnd - position; + if (position < end) { + avail = end - position; size_t wanted = buffer->mFrameCount; if (avail < wanted) { buffer->mFrameCount = avail; @@ -830,7 +839,10 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush buffer->mFrameCount = 0; buffer->mRaw = NULL; } - buffer->mNonContig = 0; // FIXME should be > 0 for looping + // As mFramesReady is the total remaining frames in the static audio track, + // it is always larger or equal to avail. + LOG_ALWAYS_FATAL_IF(mFramesReady < avail); + buffer->mNonContig = mFramesReady == INT64_MAX ? SIZE_MAX : clampToSize(mFramesReady - avail); mUnreleased = avail; return NO_ERROR; } @@ -838,6 +850,7 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) { size_t stepCount = buffer->mFrameCount; + LOG_ALWAYS_FATAL_IF(!(stepCount <= mFramesReady)); LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased)); if (stepCount == 0) { // prevent accidental re-use of buffer @@ -854,11 +867,10 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, mFrameCount); newPosition = mFrameCount; } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) { + newPosition = mState.mLoopStart; if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) { - newPosition = mState.mLoopStart; setFlags = CBLK_LOOP_CYCLE; } else { - mEnd = mFrameCount; // this is what allows playback to continue after the loop setFlags = CBLK_LOOP_FINAL; } } @@ -866,6 +878,10 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) setFlags |= CBLK_BUFFER_END; } mPosition = newPosition; + if (mFramesReady != INT64_MAX) { + mFramesReady -= stepCount; + } + mFramesReadySafe = clampToSize(mFramesReady); cblk->mServer += stepCount; // This may overflow, but client is not supposed to rely on it diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp index 2a8b2c6..81dad41 100644 --- a/media/libmediaplayerservice/Drm.cpp +++ b/media/libmediaplayerservice/Drm.cpp @@ -674,10 +674,14 @@ status_t Drm::signRSA(Vector<uint8_t> const &sessionId, void Drm::binderDied(const wp<IBinder> &the_late_who) { + mEventLock.lock(); + mListener.clear(); + mEventLock.unlock(); + + Mutex::Autolock autoLock(mLock); delete mPlugin; mPlugin = NULL; closeFactory(); - mListener.clear(); } } // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c120898..d461af3 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -2125,6 +2125,7 @@ ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size) // immutable with respect to future writes. // // It is thus safe for another thread to read the AudioCache. + Mutex::Autolock lock(mLock); mCommandComplete = true; mSignal.signal(); } diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index eb10dda..e7a26b6 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -1005,11 +1005,12 @@ ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const return -1; } -status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) { +status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select, int64_t timeUs) { ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex); sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); msg->setInt32("trackIndex", trackIndex); msg->setInt32("select", select); + msg->setInt64("timeUs", timeUs); sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); @@ -1022,11 +1023,13 @@ status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) { void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) { int32_t trackIndex, select; + int64_t timeUs; CHECK(msg->findInt32("trackIndex", &trackIndex)); CHECK(msg->findInt32("select", &select)); + CHECK(msg->findInt64("timeUs", &timeUs)); sp<AMessage> response = new AMessage; - status_t err = doSelectTrack(trackIndex, select); + status_t err = doSelectTrack(trackIndex, select, timeUs); response->setInt32("err", err); uint32_t replyID; @@ -1034,7 +1037,7 @@ void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) { response->postReply(replyID); } -status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) { +status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select, int64_t timeUs) { if (trackIndex >= mSources.size()) { return BAD_INDEX; } @@ -1087,6 +1090,23 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) mFetchTimedTextDataGeneration++; } + status_t eosResult; // ignored + if (mSubtitleTrack.mSource != NULL + && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) { + sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id()); + msg->setInt64("timeUs", timeUs); + msg->setInt32("generation", mFetchSubtitleDataGeneration); + msg->post(); + } + + if (mTimedTextTrack.mSource != NULL + && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) { + sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id()); + msg->setInt64("timeUs", timeUs); + msg->setInt32("generation", mFetchTimedTextDataGeneration); + msg->post(); + } + return OK; } else if (!strncasecmp(mime, "audio/", 6) || !strncasecmp(mime, "video/", 6)) { bool audio = !strncasecmp(mime, "audio/", 6); diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index 7a03df0..f2528a9 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -67,7 +67,7 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual size_t getTrackCount() const; virtual sp<AMessage> getTrackInfo(size_t trackIndex) const; virtual ssize_t getSelectedTrack(media_track_type type) const; - virtual status_t selectTrack(size_t trackIndex, bool select); + virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); virtual status_t seekTo(int64_t seekTimeUs); virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers); @@ -164,7 +164,7 @@ private: ssize_t doGetSelectedTrack(media_track_type type) const; void onSelectTrack(sp<AMessage> msg); - status_t doSelectTrack(size_t trackIndex, bool select); + status_t doSelectTrack(size_t trackIndex, bool select, int64_t timeUs); void onSeek(sp<AMessage> msg); status_t doSeek(int64_t seekTimeUs); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 02e9caf..a26ef9e 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -139,7 +139,15 @@ sp<AMessage> NuPlayer::HTTPLiveSource::getTrackInfo(size_t trackIndex) const { return mLiveSession->getTrackInfo(trackIndex); } -status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select) { +ssize_t NuPlayer::HTTPLiveSource::getSelectedTrack(media_track_type type) const { + if (mLiveSession == NULL) { + return -1; + } else { + return mLiveSession->getSelectedTrack(type); + } +} + +status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select, int64_t /*timeUs*/) { status_t err = mLiveSession->selectTrack(trackIndex, select); if (err == OK) { diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 6b5f6af..bbb8981 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -42,7 +42,8 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual size_t getTrackCount() const; virtual sp<AMessage> getTrackInfo(size_t trackIndex) const; - virtual status_t selectTrack(size_t trackIndex, bool select); + virtual ssize_t getSelectedTrack(media_track_type /* type */) const; + virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); virtual status_t seekTo(int64_t seekTimeUs); protected: diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 4f88f02..c01f16a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -33,6 +33,8 @@ #include "ATSParser.h" +#include <cutils/properties.h> + #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -453,8 +455,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { size_t trackIndex; int32_t select; + int64_t timeUs; CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK(msg->findInt32("select", &select)); + CHECK(msg->findInt64("timeUs", &timeUs)); status_t err = INVALID_OPERATION; @@ -468,7 +472,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } if (trackIndex < inbandTracks) { - err = mSource->selectTrack(trackIndex, select); + err = mSource->selectTrack(trackIndex, select, timeUs); if (!select && err == OK) { int32_t type; @@ -604,8 +608,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { instantiateDecoder(false, &mVideoDecoder); } - if (mAudioSink != NULL) { - if (mOffloadAudio) { + // Don't try to re-open audio sink if there's an existing decoder. + if (mAudioSink != NULL && mAudioDecoder == NULL) { + sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); + sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); + audio_stream_type_t streamType = mAudioSink->getAudioStreamType(); + bool canOffload = canOffloadStream(audioMeta, (videoFormat != NULL), + true /* is_streaming */, streamType); + if (canOffload) { + if (!mOffloadAudio) { + mRenderer->signalEnableOffloadAudio(); + } // open audio sink early under offload mode. sp<AMessage> format = mSource->getFormat(true /*audio*/); openAudioSink(format, true /*offloadOnly*/); @@ -839,7 +852,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("media rendering started"); notifyListener(MEDIA_STARTED, 0, 0); } else if (what == Renderer::kWhatAudioOffloadTearDown) { - ALOGV("Tear down audio offload, fall back to s/w path"); + ALOGV("Tear down audio offload, fall back to s/w path if due to error."); int64_t positionUs; CHECK(msg->findInt64("positionUs", &positionUs)); int32_t reason; @@ -851,11 +864,11 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { if (mVideoDecoder != NULL) { mRenderer->flush(false /* audio */); } - mRenderer->signalDisableOffloadAudio(); - mOffloadAudio = false; performSeek(positionUs, false /* needNotify */); if (reason == Renderer::kDueToError) { + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; instantiateDecoder(true /* audio */, &mAudioDecoder); } } @@ -1190,6 +1203,17 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { notify->setInt32("generation", mVideoDecoderGeneration); *decoder = new Decoder(notify, mSource, mRenderer, mNativeWindow); + + // enable FRC if high-quality AV sync is requested, even if not + // queuing to native window, as this will even improve textureview + // playback. + { + char value[PROPERTY_VALUE_MAX]; + if (property_get("persist.sys.media.avsync", value, NULL) && + (!strcmp("1", value) || !strcasecmp("true", value))) { + format->setInt32("auto-frc", 1); + } + } } (*decoder)->init(); (*decoder)->configure(format); @@ -1321,6 +1345,12 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { // This stream is unaffected by the discontinuity return -EWOULDBLOCK; } + } else if (err == ERROR_END_OF_STREAM + && doBufferAggregation && (mAggregateBuffer != NULL)) { + // send out the last bit of aggregated data + reply->setBuffer("buffer", mAggregateBuffer); + mAggregateBuffer.clear(); + err = OK; } reply->setInt32("err", err); @@ -1624,10 +1654,11 @@ status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const { return err; } -status_t NuPlayer::selectTrack(size_t trackIndex, bool select) { +status_t NuPlayer::selectTrack(size_t trackIndex, bool select, int64_t timeUs) { sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); msg->setSize("trackIndex", trackIndex); msg->setInt32("select", select); + msg->setInt64("timeUs", timeUs); sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 5f6deee..901cfbd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -65,7 +65,7 @@ struct NuPlayer : public AHandler { status_t setVideoScalingMode(int32_t mode); status_t getTrackInfo(Parcel* reply) const; status_t getSelectedTrack(int32_t type, Parcel* reply) const; - status_t selectTrack(size_t trackIndex, bool select); + status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); status_t getCurrentPosition(int64_t *mediaUs); void getStats(int64_t *mNumFramesTotal, int64_t *mNumFramesDropped); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index b42b480..e09567a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -480,13 +480,16 @@ status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { case INVOKE_ID_SELECT_TRACK: { int trackIndex = request.readInt32(); - return mPlayer->selectTrack(trackIndex, true /* select */); + int msec = 0; + // getCurrentPosition should always return OK + getCurrentPosition(&msec); + return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll); } case INVOKE_ID_UNSELECT_TRACK: { int trackIndex = request.readInt32(); - return mPlayer->selectTrack(trackIndex, false /* select */); + return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */); } case INVOKE_ID_GET_SELECTED_TRACK: @@ -625,6 +628,16 @@ void NuPlayerDriver::notifyListener_l( case MEDIA_PLAYBACK_COMPLETE: { if (mState != STATE_RESET_IN_PROGRESS) { + if (mAutoLoop) { + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + if (streamType == AUDIO_STREAM_NOTIFICATION) { + ALOGW("disabling auto-loop for notification"); + mAutoLoop = false; + } + } if (mLooping || (mAutoLoop && (mAudioSink == NULL || mAudioSink->realtime()))) { mPlayer->seekToAsync(0); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 73bc829..42288a3 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -144,6 +144,10 @@ void NuPlayer::Renderer::signalDisableOffloadAudio() { (new AMessage(kWhatDisableOffloadAudio, id()))->post(); } +void NuPlayer::Renderer::signalEnableOffloadAudio() { + (new AMessage(kWhatEnableOffloadAudio, id()))->post(); +} + void NuPlayer::Renderer::pause() { (new AMessage(kWhatPause, id()))->post(); } @@ -407,6 +411,12 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatEnableOffloadAudio: + { + onEnableOffloadAudio(); + break; + } + case kWhatPause: { onPause(); @@ -1133,6 +1143,12 @@ void NuPlayer::Renderer::onDisableOffloadAudio() { ++mAudioQueueGeneration; } +void NuPlayer::Renderer::onEnableOffloadAudio() { + Mutex::Autolock autoLock(mLock); + mFlags |= FLAG_OFFLOAD_AUDIO; + ++mAudioQueueGeneration; +} + void NuPlayer::Renderer::onPause() { if (mPaused) { ALOGW("Renderer::onPause() called while already paused!"); @@ -1416,6 +1432,9 @@ bool NuPlayer::Renderer::onOpenAudioSink( if (audioSinkChanged) { onAudioSinkChanged(); } + if (offloadingAudio()) { + mAudioOffloadTornDown = false; + } return offloadingAudio(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 7b46a59..985ec49 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -53,6 +53,7 @@ struct NuPlayer::Renderer : public AHandler { void signalAudioSinkChanged(); void signalDisableOffloadAudio(); + void signalEnableOffloadAudio(); void pause(); void resume(); @@ -114,6 +115,7 @@ private: kWhatCloseAudioSink = 'clsA', kWhatStopAudioSink = 'stpA', kWhatDisableOffloadAudio = 'noOA', + kWhatEnableOffloadAudio = 'enOA', kWhatSetVideoFrameRate = 'sVFR', }; @@ -200,6 +202,7 @@ private: void onFlush(const sp<AMessage> &msg); void onAudioSinkChanged(); void onDisableOffloadAudio(); + void onEnableOffloadAudio(); void onPause(); void onResume(); void onSetVideoFrameRate(float fps); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 2f06c31..2b0ac47 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -97,7 +97,7 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } - virtual status_t selectTrack(size_t /* trackIndex */, bool /* select */) { + virtual status_t selectTrack(size_t /* trackIndex */, bool /* select */, int64_t /* timeUs*/) { return INVALID_OPERATION; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 0e9d734..1413635 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1268,6 +1268,24 @@ status_t ACodec::configureCodec( static_cast<NativeWindowWrapper *>(obj.get())); sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow(); + // START of temporary support for automatic FRC - THIS WILL BE REMOVED + int32_t autoFrc; + if (msg->findInt32("auto-frc", &autoFrc)) { + bool enabled = autoFrc; + OMX_CONFIG_BOOLEANTYPE config; + InitOMXParams(&config); + config.bEnabled = (OMX_BOOL)enabled; + status_t temp = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigAutoFramerateConversion, + &config, sizeof(config)); + if (temp == OK) { + outputFormat->setInt32("auto-frc", enabled); + } else if (enabled) { + ALOGI("codec does not support requested auto-frc (err %d)", temp); + } + } + // END of temporary support for automatic FRC + int32_t tunneled; if (msg->findInt32("feature-tunneled-playback", &tunneled) && tunneled != 0) { diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 6a56729..007c090 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -878,6 +878,16 @@ void AwesomePlayer::onStreamDone() { return; } + if (mFlags & AUTO_LOOPING) { + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + if (streamType == AUDIO_STREAM_NOTIFICATION) { + ALOGW("disabling auto-loop for notification"); + modifyFlags(AUTO_LOOPING, CLEAR); + } + } if ((mFlags & LOOPING) || ((mFlags & AUTO_LOOPING) && (mAudioSink == NULL || mAudioSink->realtime()))) { diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 874c118..5eb4652 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1164,6 +1164,14 @@ status_t LiveSession::selectTrack(size_t index, bool select) { return err; } +ssize_t LiveSession::getSelectedTrack(media_track_type type) const { + if (mPlaylist == NULL) { + return -1; + } else { + return mPlaylist->getSelectedTrack(type); + } +} + bool LiveSession::canSwitchUp() { // Allow upwards bandwidth switch when a stream has buffered at least 10 seconds. status_t err = OK; diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index 7aacca6..896a8fc 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -19,6 +19,7 @@ #define LIVE_SESSION_H_ #include <media/stagefright/foundation/AHandler.h> +#include <media/mediaplayer.h> #include <utils/String8.h> @@ -73,6 +74,7 @@ struct LiveSession : public AHandler { size_t getTrackCount() const; sp<AMessage> getTrackInfo(size_t trackIndex) const; status_t selectTrack(size_t index, bool select); + ssize_t getSelectedTrack(media_track_type /* type */) const; bool isSeekable() const; bool hasDynamicDuration() const; diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 1651dee..eb62c7a 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -66,6 +66,9 @@ protected: virtual ~MediaGroup(); private: + + friend struct M3UParser; + struct Media { AString mName; AString mURI; @@ -356,6 +359,38 @@ ssize_t M3UParser::getSelectedIndex() const { return mSelectedIndex; } +ssize_t M3UParser::getSelectedTrack(media_track_type type) const { + MediaGroup::Type groupType; + switch (type) { + case MEDIA_TRACK_TYPE_VIDEO: + groupType = MediaGroup::TYPE_VIDEO; + break; + + case MEDIA_TRACK_TYPE_AUDIO: + groupType = MediaGroup::TYPE_AUDIO; + break; + + case MEDIA_TRACK_TYPE_SUBTITLE: + groupType = MediaGroup::TYPE_SUBS; + break; + + default: + return -1; + } + + for (size_t i = 0, ii = 0; i < mMediaGroups.size(); ++i) { + sp<MediaGroup> group = mMediaGroups.valueAt(i); + size_t tracks = group->countTracks(); + if (groupType != group->mType) { + ii += tracks; + } else if (group->mSelectedIndex >= 0) { + return ii + group->mSelectedIndex; + } + } + + return -1; +} + bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const { if (!mIsVariantPlaylist) { *uri = mBaseURI; diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index d588afe..1cad060 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -21,6 +21,7 @@ #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AString.h> +#include <media/mediaplayer.h> #include <utils/Vector.h> namespace android { @@ -46,6 +47,7 @@ struct M3UParser : public RefBase { size_t getTrackCount() const; sp<AMessage> getTrackInfo(size_t index) const; ssize_t getSelectedIndex() const; + ssize_t getSelectedTrack(media_track_type /* type */) const; bool getTypeURI(size_t index, const char *key, AString *uri) const; diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index e247550..d8eed5b 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -826,6 +826,18 @@ void PlaylistFetcher::onDownloadNext() { " mStartup=%d, was looking for %d in %d-%d", mStartup, mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); + if (mStopParams != NULL) { + // we should have kept on fetching until we hit the boundaries in mStopParams, + // but since the segments we are supposed to fetch have already rolled off + // the playlist, i.e. we have already missed the boat, we inevitably have to + // skip. + for (size_t i = 0; i < mPacketSources.size(); i++) { + sp<ABuffer> formatChange = mSession->createFormatChangeBuffer(); + mPacketSources.valueAt(i)->queueAccessUnit(formatChange); + } + stopAsync(/* clear = */ false); + return; + } mSeqNumber = lastSeqNumberInPlaylist - 3; if (mSeqNumber < firstSeqNumberInPlaylist) { mSeqNumber = firstSeqNumberInPlaylist; @@ -1266,6 +1278,11 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) { + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } int32_t targetDurationSecs; CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); @@ -1276,6 +1293,8 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu // mStartTimeUs. // mSegmentStartTimeUs >= 0 // mSegmentStartTimeUs is non-negative when adapting or switching tracks + // mSeqNumber > firstSeqNumberInPlaylist + // don't decrement mSeqNumber if it already points to the 1st segment // timeUs - mStartTimeUs > targetDurationUs: // This and the 2 above conditions should only happen when adapting in a live // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher @@ -1285,6 +1304,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu // stop as early as possible. The definition of being "too far ahead" is // arbitrary; here we use targetDurationUs as threshold. if (mStartup && mSegmentStartTimeUs >= 0 + && mSeqNumber > firstSeqNumberInPlaylist && timeUs - mStartTimeUs > targetDurationUs) { // we just guessed a starting timestamp that is too high when adapting in a // live stream; re-adjust based on the actual timestamp extracted from the |