diff options
Diffstat (limited to 'media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp')
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp | 239 |
1 files changed, 179 insertions, 60 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 86ce385..25225a8 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -20,12 +20,11 @@ #include "NuPlayerRenderer.h" -#include <cutils/properties.h> - #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AUtils.h> +#include <media/stagefright/foundation/AWakeLock.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/Utils.h> @@ -38,20 +37,19 @@ namespace android { // Maximum time in paused state when offloading audio decompression. When elapsed, the AudioSink // is closed to allow the audio DSP to power down. -static const int64_t kOffloadPauseMaxUs = 60000000ll; +static const int64_t kOffloadPauseMaxUs = 10000000ll; // static -const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; +const NuPlayer::Renderer::PcmInfo NuPlayer::Renderer::AUDIO_PCMINFO_INITIALIZER = { + AUDIO_CHANNEL_NONE, + AUDIO_OUTPUT_FLAG_NONE, + AUDIO_FORMAT_INVALID, + 0, // mNumChannels + 0 // mSampleRate +}; -static bool sFrameAccurateAVsync = false; - -static void readProperties() { - char value[PROPERTY_VALUE_MAX]; - if (property_get("persist.sys.media.avsync", value, NULL)) { - sFrameAccurateAVsync = - !strcmp("1", value) || !strcasecmp("true", value); - } -} +// static +const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; NuPlayer::Renderer::Renderer( const sp<MediaPlayerBase::AudioSink> &sink, @@ -76,8 +74,11 @@ NuPlayer::Renderer::Renderer( mPauseStartedTimeRealUs(-1), mFlushingAudio(false), mFlushingVideo(false), + mNotifyCompleteAudio(false), + mNotifyCompleteVideo(false), mSyncQueues(false), mPaused(false), + mPausePositionMediaTimeUs(-1), mVideoSampleReceived(false), mVideoRenderingStarted(false), mVideoRenderingStartGeneration(0), @@ -85,9 +86,11 @@ NuPlayer::Renderer::Renderer( mAudioOffloadPauseTimeoutGeneration(0), mAudioOffloadTornDown(false), mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER), + mCurrentPcmInfo(AUDIO_PCMINFO_INITIALIZER), mTotalBuffersQueued(0), - mLastAudioBufferDrained(0) { - readProperties(); + mLastAudioBufferDrained(0), + mWakeLock(new AWakeLock()) { + } NuPlayer::Renderer::~Renderer() { @@ -118,15 +121,17 @@ void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) { msg->post(); } -void NuPlayer::Renderer::flush(bool audio) { +void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) { { Mutex::Autolock autoLock(mFlushLock); if (audio) { + mNotifyCompleteAudio |= notifyComplete; if (mFlushingAudio) { return; } mFlushingAudio = true; } else { + mNotifyCompleteVideo |= notifyComplete; if (mFlushingVideo) { return; } @@ -157,6 +162,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(); } @@ -171,11 +180,48 @@ void NuPlayer::Renderer::setVideoFrameRate(float fps) { msg->post(); } +// Called on any threads, except renderer's thread. status_t NuPlayer::Renderer::getCurrentPosition(int64_t *mediaUs) { - return getCurrentPosition(mediaUs, ALooper::GetNowUs()); + { + Mutex::Autolock autoLock(mLock); + int64_t currentPositionUs; + if (getCurrentPositionIfPaused_l(¤tPositionUs)) { + *mediaUs = currentPositionUs; + return OK; + } + } + return getCurrentPositionFromAnchor(mediaUs, ALooper::GetNowUs()); +} + +// Called on only renderer's thread. +status_t NuPlayer::Renderer::getCurrentPositionOnLooper(int64_t *mediaUs) { + return getCurrentPositionOnLooper(mediaUs, ALooper::GetNowUs()); } -status_t NuPlayer::Renderer::getCurrentPosition( +// Called on only renderer's thread. +// Since mPaused and mPausePositionMediaTimeUs are changed only on renderer's +// thread, no need to acquire mLock. +status_t NuPlayer::Renderer::getCurrentPositionOnLooper( + int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo) { + int64_t currentPositionUs; + if (getCurrentPositionIfPaused_l(¤tPositionUs)) { + *mediaUs = currentPositionUs; + return OK; + } + return getCurrentPositionFromAnchor(mediaUs, nowUs, allowPastQueuedVideo); +} + +// Called either with mLock acquired or on renderer's thread. +bool NuPlayer::Renderer::getCurrentPositionIfPaused_l(int64_t *mediaUs) { + if (!mPaused || mPausePositionMediaTimeUs < 0ll) { + return false; + } + *mediaUs = mPausePositionMediaTimeUs; + return true; +} + +// Called on any threads. +status_t NuPlayer::Renderer::getCurrentPositionFromAnchor( int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo) { Mutex::Autolock autoLock(mTimeLock); if (!mHasAudio && !mHasVideo) { @@ -255,11 +301,12 @@ void NuPlayer::Renderer::setPauseStartedTimeRealUs(int64_t realUs) { mPauseStartedTimeRealUs = realUs; } -bool NuPlayer::Renderer::openAudioSink( +status_t NuPlayer::Renderer::openAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, - uint32_t flags) { + uint32_t flags, + bool *isOffloaded) { sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, id()); msg->setMessage("format", format); msg->setInt32("offload-only", offloadOnly); @@ -269,9 +316,15 @@ bool NuPlayer::Renderer::openAudioSink( sp<AMessage> response; msg->postAndAwaitResponse(&response); - int32_t offload; - CHECK(response->findInt32("offload", &offload)); - return (offload != 0); + int32_t err; + if (!response->findInt32("err", &err)) { + err = INVALID_OPERATION; + } else if (err == OK && isOffloaded != NULL) { + int32_t offload; + CHECK(response->findInt32("offload", &offload)); + *isOffloaded = (offload != 0); + } + return err; } void NuPlayer::Renderer::closeAudioSink() { @@ -297,10 +350,11 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { uint32_t flags; CHECK(msg->findInt32("flags", (int32_t *)&flags)); - bool offload = onOpenAudioSink(format, offloadOnly, hasVideo, flags); + status_t err = onOpenAudioSink(format, offloadOnly, hasVideo, flags); sp<AMessage> response = new AMessage; - response->setInt32("offload", offload); + response->setInt32("err", err); + response->setInt32("offload", offloadingAudio()); uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); @@ -373,7 +427,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { onDrainVideoQueue(); - postDrainVideoQueue(); + Mutex::Autolock autoLock(mLock); + postDrainVideoQueue_l(); break; } @@ -386,7 +441,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { } mDrainVideoQueuePending = false; - postDrainVideoQueue(); + Mutex::Autolock autoLock(mLock); + postDrainVideoQueue_l(); break; } @@ -420,6 +476,12 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatEnableOffloadAudio: + { + onEnableOffloadAudio(); + break; + } + case kWhatPause: { onPause(); @@ -455,6 +517,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { } ALOGV("Audio Offload tear down due to pause timeout."); onAudioOffloadTearDown(kDueToTimeout); + mWakeLock->release(); break; } @@ -629,6 +692,13 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { mAudioQueue.erase(mAudioQueue.begin()); entry = NULL; + if (mAudioSink->needsTrailingPadding()) { + // If we're not in gapless playback (i.e. through setNextPlayer), we + // need to stop the track here, because that will play out the last + // little bit at the end of the file. Otherwise short files won't play. + mAudioSink->stop(); + mNumFramesWritten = 0; + } return false; } @@ -646,8 +716,9 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, copy); if (written < 0) { - // An error in AudioSink write is fatal here. - LOG_ALWAYS_FATAL("AudioSink write error(%zd) when writing %zu bytes", written, copy); + // An error in AudioSink write. Perhaps the AudioSink was not properly opened. + ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy); + break; } entry->mOffset += written; @@ -701,7 +772,8 @@ int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs) { int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) { int64_t currentPositionUs; - if (getCurrentPosition(¤tPositionUs, nowUs, true /* allowPastQueuedVideo */) != OK) { + if (mPaused || getCurrentPositionOnLooper( + ¤tPositionUs, nowUs, true /* allowPastQueuedVideo */) != OK) { // If failed to get current position, e.g. due to audio clock is not ready, then just // play out video immediately without delay. return nowUs; @@ -721,7 +793,7 @@ void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs) { mediaTimeUs, nowUs + getPendingAudioPlayoutDurationUs(nowUs), mNumFramesWritten); } -void NuPlayer::Renderer::postDrainVideoQueue() { +void NuPlayer::Renderer::postDrainVideoQueue_l() { if (mDrainVideoQueuePending || mSyncQueues || (mPaused && mVideoSampleReceived)) { @@ -757,6 +829,8 @@ void NuPlayer::Renderer::postDrainVideoQueue() { if (mAnchorTimeMediaUs < 0) { setAnchorTime(mediaTimeUs, nowUs); + mPausePositionMediaTimeUs = mediaTimeUs; + mAnchorMaxMediaUs = mediaTimeUs; realTimeUs = nowUs; } else { realTimeUs = getRealTimeUs(mediaTimeUs, nowUs); @@ -792,11 +866,6 @@ void NuPlayer::Renderer::postDrainVideoQueue() { ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); // post 2 display refreshes before rendering is due - // FIXME currently this increases power consumption, so unless frame-accurate - // AV sync is requested, post closer to required render time (at 0.63 vsyncs) - if (!sFrameAccurateAVsync) { - twoVsyncsUs >>= 4; - } msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0); mDrainVideoQueuePending = true; @@ -924,16 +993,15 @@ void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) { entry.mFinalResult = OK; entry.mBufferOrdinal = ++mTotalBuffersQueued; + Mutex::Autolock autoLock(mLock); if (audio) { - Mutex::Autolock autoLock(mLock); mAudioQueue.push_back(entry); postDrainAudioQueue_l(); } else { mVideoQueue.push_back(entry); - postDrainVideoQueue(); + postDrainVideoQueue_l(); } - Mutex::Autolock autoLock(mLock); if (!mSyncQueues || mAudioQueue.empty() || mVideoQueue.empty()) { return; } @@ -982,7 +1050,7 @@ void NuPlayer::Renderer::syncQueuesDone_l() { } if (!mVideoQueue.empty()) { - postDrainVideoQueue(); + postDrainVideoQueue_l(); } } @@ -1001,8 +1069,8 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) { entry.mOffset = 0; entry.mFinalResult = finalResult; + Mutex::Autolock autoLock(mLock); if (audio) { - Mutex::Autolock autoLock(mLock); if (mAudioQueue.empty() && mSyncQueues) { syncQueuesDone_l(); } @@ -1010,24 +1078,27 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) { postDrainAudioQueue_l(); } else { if (mVideoQueue.empty() && mSyncQueues) { - Mutex::Autolock autoLock(mLock); syncQueuesDone_l(); } mVideoQueue.push_back(entry); - postDrainVideoQueue(); + postDrainVideoQueue_l(); } } void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { - int32_t audio; + int32_t audio, notifyComplete; CHECK(msg->findInt32("audio", &audio)); { Mutex::Autolock autoLock(mFlushLock); if (audio) { mFlushingAudio = false; + notifyComplete = mNotifyCompleteAudio; + mNotifyCompleteAudio = false; } else { mFlushingVideo = false; + notifyComplete = mNotifyCompleteVideo; + mNotifyCompleteVideo = false; } } @@ -1043,6 +1114,7 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { Mutex::Autolock autoLock(mLock); syncQueuesDone_l(); setPauseStartedTimeRealUs(-1); + setAnchorTime(-1, -1); } ALOGV("flushing %s", audio ? "audio" : "video"); @@ -1080,7 +1152,10 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { } mVideoSampleReceived = false; - notifyFlushComplete(audio); + + if (notifyComplete) { + notifyFlushComplete(audio); + } } void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) { @@ -1147,13 +1222,32 @@ 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!"); return; } + int64_t currentPositionUs; + int64_t pausePositionMediaTimeUs; + if (getCurrentPositionFromAnchor( + ¤tPositionUs, ALooper::GetNowUs()) == OK) { + pausePositionMediaTimeUs = currentPositionUs; + } else { + // Set paused position to -1 (unavailabe) if we don't have anchor time + // This could happen if client does a seekTo() immediately followed by + // pause(). Renderer will be flushed with anchor time cleared. We don't + // want to leave stale value in mPausePositionMediaTimeUs. + pausePositionMediaTimeUs = -1; + } { Mutex::Autolock autoLock(mLock); + mPausePositionMediaTimeUs = pausePositionMediaTimeUs; ++mAudioQueueGeneration; ++mVideoQueueGeneration; prepareForMediaRenderingStart(); @@ -1174,8 +1268,6 @@ void NuPlayer::Renderer::onPause() { } void NuPlayer::Renderer::onResume() { - readProperties(); - if (!mPaused) { return; } @@ -1199,7 +1291,7 @@ void NuPlayer::Renderer::onResume() { } if (!mVideoQueue.empty()) { - postDrainVideoQueue(); + postDrainVideoQueue_l(); } } @@ -1281,7 +1373,7 @@ void NuPlayer::Renderer::onAudioOffloadTearDown(AudioOffloadTearDownReason reaso mAudioOffloadTornDown = true; int64_t currentPositionUs; - if (getCurrentPosition(¤tPositionUs) != OK) { + if (getCurrentPositionOnLooper(¤tPositionUs) != OK) { currentPositionUs = 0; } @@ -1297,6 +1389,7 @@ void NuPlayer::Renderer::onAudioOffloadTearDown(AudioOffloadTearDownReason reaso void NuPlayer::Renderer::startAudioOffloadPauseTimeout() { if (offloadingAudio()) { + mWakeLock->acquire(); sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, id()); msg->setInt32("generation", mAudioOffloadPauseTimeoutGeneration); msg->post(kOffloadPauseMaxUs); @@ -1305,11 +1398,12 @@ void NuPlayer::Renderer::startAudioOffloadPauseTimeout() { void NuPlayer::Renderer::cancelAudioOffloadPauseTimeout() { if (offloadingAudio()) { + mWakeLock->release(true); ++mAudioOffloadPauseTimeoutGeneration; } } -bool NuPlayer::Renderer::onOpenAudioSink( +status_t NuPlayer::Renderer::onOpenAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, @@ -1371,11 +1465,14 @@ bool NuPlayer::Renderer::onOpenAudioSink( if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) { ALOGV("openAudioSink: no change in offload mode"); // no change from previous configuration, everything ok. - return offloadingAudio(); + return OK; } + mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER; + ALOGV("openAudioSink: try to open AudioSink in offload mode"); - flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; - flags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + uint32_t offloadFlags = flags; + offloadFlags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + offloadFlags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER; audioSinkChanged = true; mAudioSink->close(); err = mAudioSink->open( @@ -1386,7 +1483,7 @@ bool NuPlayer::Renderer::onOpenAudioSink( 8 /* bufferCount */, &NuPlayer::Renderer::AudioSinkCallback, this, - (audio_output_flags_t)flags, + (audio_output_flags_t)offloadFlags, &offloadInfo); if (err == OK) { @@ -1410,13 +1507,27 @@ bool NuPlayer::Renderer::onOpenAudioSink( } } if (!offloadOnly && !offloadingAudio()) { - flags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; ALOGV("openAudioSink: open AudioSink in NON-offload mode"); + uint32_t pcmFlags = flags; + pcmFlags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + + const PcmInfo info = { + (audio_channel_mask_t)channelMask, + (audio_output_flags_t)pcmFlags, + AUDIO_FORMAT_PCM_16_BIT, // TODO: change to audioFormat + numChannels, + sampleRate + }; + if (memcmp(&mCurrentPcmInfo, &info, sizeof(info)) == 0) { + ALOGV("openAudioSink: no change in pcm mode"); + // no change from previous configuration, everything ok. + return OK; + } audioSinkChanged = true; mAudioSink->close(); mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; - CHECK_EQ(mAudioSink->open( + status_t err = mAudioSink->open( sampleRate, numChannels, (audio_channel_mask_t)channelMask, @@ -1424,20 +1535,28 @@ bool NuPlayer::Renderer::onOpenAudioSink( 8 /* bufferCount */, NULL, NULL, - (audio_output_flags_t)flags), - (status_t)OK); + (audio_output_flags_t)pcmFlags); + if (err != OK) { + ALOGW("openAudioSink: non offloaded open failed status: %d", err); + mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER; + return err; + } + mCurrentPcmInfo = info; mAudioSink->start(); } if (audioSinkChanged) { onAudioSinkChanged(); } - - return offloadingAudio(); + if (offloadingAudio()) { + mAudioOffloadTornDown = false; + } + return OK; } void NuPlayer::Renderer::onCloseAudioSink() { mAudioSink->close(); mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER; } } // namespace android |