diff options
Diffstat (limited to 'media/libmediaplayerservice')
7 files changed, 145 insertions, 60 deletions
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.cpp b/media/libmediaplayerservice/VideoFrameScheduler.cpp index 1a5f3e0..ce5f5fe 100644 --- a/media/libmediaplayerservice/VideoFrameScheduler.cpp +++ b/media/libmediaplayerservice/VideoFrameScheduler.cpp @@ -27,6 +27,7 @@ #include <ui/DisplayStatInfo.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AUtils.h> #include "VideoFrameScheduler.h" @@ -35,36 +36,6 @@ namespace android { static const nsecs_t kNanosIn1s = 1000000000; template<class T> -inline static const T divRound(const T &nom, const T &den) { - if ((nom >= 0) ^ (den >= 0)) { - return (nom - den / 2) / den; - } else { - return (nom + den / 2) / den; - } -} - -template<class T> -inline static T abs(const T &a) { - return a < 0 ? -a : a; -} - -template<class T> -inline static const T &min(const T &a, const T &b) { - return a < b ? a : b; -} - -template<class T> -inline static const T &max(const T &a, const T &b) { - return a > b ? a : b; -} - -template<class T> -inline static T periodicError(const T &val, const T &period) { - T err = abs(val) % period; - return (err < (period / 2)) ? err : (period - err); -} - -template<class T> static int compare(const T *lhs, const T *rhs) { if (*lhs < *rhs) { return -1; diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index f84decd..6859a1a 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -36,6 +36,7 @@ #include "../../libstagefright/include/DRMExtractor.h" #include "../../libstagefright/include/NuCachedSource2.h" #include "../../libstagefright/include/WVMExtractor.h" +#include "../../libstagefright/include/HTTPBase.h" namespace android { @@ -64,6 +65,7 @@ void NuPlayer::GenericSource::resetDataSource() { mAudioTimeUs = 0; mVideoTimeUs = 0; mHTTPService.clear(); + mHttpSource.clear(); mUri.clear(); mUriHeaders.clear(); mFd = -1; @@ -73,6 +75,7 @@ void NuPlayer::GenericSource::resetDataSource() { mDecryptHandle = NULL; mDrmManagerClient = NULL; mStarted = false; + mStopRead = true; } status_t NuPlayer::GenericSource::setDataSource( @@ -284,10 +287,23 @@ void NuPlayer::GenericSource::onPrepareAsync() { // delayed data source creation if (mDataSource == NULL) { if (!mUri.empty()) { - mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11); + const char* uri = mUri.c_str(); + mIsWidevine = !strncasecmp(uri, "widevine://", 11); + + if (!strncasecmp("http://", uri, 7) + || !strncasecmp("https://", uri, 8) + || mIsWidevine) { + mHttpSource = DataSource::CreateMediaHTTP(mHTTPService); + if (mHttpSource == NULL) { + ALOGE("Failed to create http source!"); + notifyPreparedAndCleanup(UNKNOWN_ERROR); + return; + } + } mDataSource = DataSource::CreateFromURI( - mHTTPService, mUri.c_str(), &mUriHeaders, &mContentType); + mHTTPService, uri, &mUriHeaders, &mContentType, + static_cast<HTTPBase *>(mHttpSource.get())); } else { // set to false first, if the extractor // comes back as secure, set it to true then. @@ -360,6 +376,7 @@ void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) { mSniffedMIME = ""; mDataSource.clear(); mCachedSource.clear(); + mHttpSource.clear(); cancelPollBuffering(); } @@ -439,6 +456,7 @@ status_t NuPlayer::GenericSource::prefillCacheIfNecessary() { void NuPlayer::GenericSource::start() { ALOGI("start"); + mStopRead = false; if (mAudioTrack.mSource != NULL) { CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK); @@ -459,6 +477,12 @@ void NuPlayer::GenericSource::stop() { // nothing to do, just account for DRM playback status setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); mStarted = false; + if (mIsWidevine) { + // For a widevine source we need to prevent any further reads. + sp<AMessage> msg = new AMessage(kWhatStopWidevine, id()); + sp<AMessage> response; + (void) msg->postAndAwaitResponse(&response); + } } void NuPlayer::GenericSource::pause() { @@ -479,6 +503,8 @@ void NuPlayer::GenericSource::disconnect() { if (mDataSource->flags() & DataSource::kIsCachingDataSource) { static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect(); } + } else if (mHttpSource != NULL) { + static_cast<HTTPBase *>(mHttpSource.get())->disconnect(); } } @@ -675,6 +701,20 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatStopWidevine: + { + // mStopRead is only used for Widevine to prevent the video source + // from being read while the associated video decoder is shutting down. + mStopRead = true; + if (mVideoTrack.mSource != NULL) { + mVideoTrack.mPackets->clear(); + } + sp<AMessage> response = new AMessage; + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + response->postReply(replyID); + break; + } default: Source::onMessageReceived(msg); break; @@ -1082,6 +1122,11 @@ void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) { } status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) { + // If the Widevine source is stopped, do not attempt to read any + // more buffers. + if (mStopRead) { + return INVALID_OPERATION; + } if (mVideoTrack.mSource != NULL) { int64_t actualTimeUs; readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs); @@ -1193,6 +1238,10 @@ void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) { void NuPlayer::GenericSource::readBuffer( media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) { + // Do not read data if Widevine source is stopped + if (mStopRead) { + return; + } Track *track; size_t maxBuffers = 1; switch (trackType) { diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index 24bb6af..f8601ea 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -93,6 +93,7 @@ private: kWhatSelectTrack, kWhatSeek, kWhatReadBuffer, + kWhatStopWidevine, }; Vector<sp<MediaSource> > mSources; @@ -126,11 +127,13 @@ private: sp<DataSource> mDataSource; sp<NuCachedSource2> mCachedSource; + sp<DataSource> mHttpSource; sp<WVMExtractor> mWVMExtractor; sp<MetaData> mFileMeta; DrmManagerClient *mDrmManagerClient; sp<DecryptHandle> mDecryptHandle; bool mStarted; + bool mStopRead; String8 mContentType; AString mSniffedMIME; off64_t mMetaDataSize; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index ca596fd..d225851 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -791,6 +791,11 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("initiating %s decoder shutdown", audio ? "audio" : "video"); + // Widevine source reads must stop before releasing the video decoder. + if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) { + mSource->stop(); + } + getDecoder(audio)->initiateShutdown(); if (audio) { @@ -833,30 +838,50 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { finishFlushIfPossible(); } else if (what == Decoder::kWhatError) { status_t err; - if (!msg->findInt32("err", &err)) { + if (!msg->findInt32("err", &err) || err == OK) { err = UNKNOWN_ERROR; } - ALOGE("received error from %s decoder %#x", audio ? "audio" : "video", err); - ALOGI("shutting down %s", audio ? "audio" : "video"); - if (audio && mFlushingAudio != NONE) { - mRenderer->queueEOS(audio, err); - mAudioDecoder.clear(); - ++mAudioDecoderGeneration; - mFlushingAudio = SHUT_DOWN; - finishFlushIfPossible(); - } else if (!audio && mFlushingVideo != NONE) { - mRenderer->queueEOS(audio, err); - mVideoDecoder.clear(); - ++mVideoDecoderGeneration; - mFlushingVideo = SHUT_DOWN; - finishFlushIfPossible(); - } else { - mDeferredActions.push_back( - new ShutdownDecoderAction(audio, !audio /* video */)); - processDeferredActions(); - notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + // Decoder errors can be due to Source (e.g. from streaming), + // or from decoding corrupted bitstreams, or from other decoder + // MediaCodec operations (e.g. from an ongoing reset or seek). + // + // We try to gracefully shut down the affected decoder if possible, + // rather than trying to force the shutdown with something + // similar to performReset(). This method can lead to a hang + // if MediaCodec functions block after an error, but they should + // typically return INVALID_OPERATION instead of blocking. + + FlushStatus *flushing = audio ? &mFlushingAudio : &mFlushingVideo; + ALOGE("received error(%#x) from %s decoder, flushing(%d), now shutting down", + err, audio ? "audio" : "video", *flushing); + + switch (*flushing) { + case NONE: + mDeferredActions.push_back( + new ShutdownDecoderAction(audio, !audio /* video */)); + processDeferredActions(); + break; + case FLUSHING_DECODER: + *flushing = FLUSHING_DECODER_SHUTDOWN; // initiate shutdown after flush. + break; // Wait for flush to complete. + case FLUSHING_DECODER_SHUTDOWN: + break; // Wait for flush to complete. + case SHUTTING_DOWN_DECODER: + break; // Wait for shutdown to complete. + case FLUSHED: + // Widevine source reads must stop before releasing the video decoder. + if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) { + mSource->stop(); + } + getDecoder(audio)->initiateShutdown(); // In the middle of a seek. + *flushing = SHUTTING_DOWN_DECODER; // Shut down. + break; + case SHUT_DOWN: + finishFlushIfPossible(); // Should not occur. + break; // Finish anyways. } + notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); } else if (what == Decoder::kWhatDrainThisBuffer) { renderBuffer(audio, msg); } else { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 037b5d2..f131b1f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -140,6 +140,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { format, surface, NULL /* crypto */, 0 /* flags */); if (err != OK) { ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err); + mCodec->release(); + mCodec.clear(); handleError(err); return; } @@ -152,6 +154,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { err = mCodec->start(); if (err != OK) { ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err); + mCodec->release(); + mCodec.clear(); handleError(err); return; } @@ -529,9 +533,9 @@ void NuPlayer::Decoder::onFlush() { if (err != OK) { ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err); handleError(err); - return; + // finish with posting kWhatFlushCompleted. + // we attempt to release the buffers even if flush fails. } - releaseAndResetMediaBuffers(); sp<AMessage> notify = mNotify->dup(); @@ -569,7 +573,7 @@ void NuPlayer::Decoder::onShutdown() { if (err != OK) { ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); handleError(err); - return; + // finish with posting kWhatShutdownCompleted. } sp<AMessage> notify = mNotify->dup(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index a9bca49..1a01d52 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -27,6 +27,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> namespace android { @@ -47,6 +48,7 @@ NuPlayerDriver::NuPlayerDriver() mLooping(false), mAutoLoop(false), mStartupSeekTimeUs(-1) { + ALOGV("NuPlayerDriver(%p)", this); mLooper->setName("NuPlayerDriver Looper"); mLooper->start( @@ -61,6 +63,7 @@ NuPlayerDriver::NuPlayerDriver() } NuPlayerDriver::~NuPlayerDriver() { + ALOGV("~NuPlayerDriver(%p)", this); mLooper->stop(); } @@ -78,9 +81,9 @@ status_t NuPlayerDriver::setDataSource( const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers) { + ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str()); Mutex::Autolock autoLock(mLock); - ALOGV("setDataSource: url=%s", url); if (mState != STATE_IDLE) { return INVALID_OPERATION; } @@ -97,9 +100,9 @@ status_t NuPlayerDriver::setDataSource( } status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { + ALOGV("setDataSource(%p) file(%d)", this, fd); Mutex::Autolock autoLock(mLock); - ALOGV("setDataSource: fd=%d", fd); if (mState != STATE_IDLE) { return INVALID_OPERATION; } @@ -116,9 +119,9 @@ status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { } status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) { + ALOGV("setDataSource(%p) stream source", this); Mutex::Autolock autoLock(mLock); - ALOGV("setDataSource: stream source"); if (mState != STATE_IDLE) { return INVALID_OPERATION; } @@ -136,6 +139,7 @@ status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) { status_t NuPlayerDriver::setVideoSurfaceTexture( const sp<IGraphicBufferProducer> &bufferProducer) { + ALOGV("setVideoSurfaceTexture(%p)", this); Mutex::Autolock autoLock(mLock); if (mSetSurfaceInProgress) { @@ -163,6 +167,7 @@ status_t NuPlayerDriver::setVideoSurfaceTexture( } status_t NuPlayerDriver::prepare() { + ALOGV("prepare(%p)", this); Mutex::Autolock autoLock(mLock); return prepare_l(); } @@ -197,6 +202,7 @@ status_t NuPlayerDriver::prepare_l() { } status_t NuPlayerDriver::prepareAsync() { + ALOGV("prepareAsync(%p)", this); Mutex::Autolock autoLock(mLock); switch (mState) { @@ -218,6 +224,7 @@ status_t NuPlayerDriver::prepareAsync() { } status_t NuPlayerDriver::start() { + ALOGD("start(%p)", this); Mutex::Autolock autoLock(mLock); switch (mState) { @@ -292,6 +299,7 @@ status_t NuPlayerDriver::start() { } status_t NuPlayerDriver::stop() { + ALOGD("stop(%p)", this); Mutex::Autolock autoLock(mLock); switch (mState) { @@ -346,6 +354,7 @@ bool NuPlayerDriver::isPlaying() { } status_t NuPlayerDriver::seekTo(int msec) { + ALOGD("seekTo(%p) %d ms", this, msec); Mutex::Autolock autoLock(mLock); int64_t seekTimeUs = msec * 1000ll; @@ -430,6 +439,7 @@ status_t NuPlayerDriver::getDuration(int *msec) { } status_t NuPlayerDriver::reset() { + ALOGD("reset(%p)", this); Mutex::Autolock autoLock(mLock); switch (mState) { @@ -572,6 +582,7 @@ status_t NuPlayerDriver::getMetadata( } void NuPlayerDriver::notifyResetComplete() { + ALOGI("notifyResetComplete(%p)", this); Mutex::Autolock autoLock(mLock); CHECK_EQ(mState, STATE_RESET_IN_PROGRESS); @@ -580,6 +591,7 @@ void NuPlayerDriver::notifyResetComplete() { } void NuPlayerDriver::notifySetSurfaceComplete() { + ALOGV("notifySetSurfaceComplete(%p)", this); Mutex::Autolock autoLock(mLock); CHECK(mSetSurfaceInProgress); @@ -602,6 +614,7 @@ void NuPlayerDriver::notifyPosition(int64_t positionUs) { } void NuPlayerDriver::notifySeekComplete() { + ALOGV("notifySeekComplete(%p)", this); Mutex::Autolock autoLock(mLock); notifySeekComplete_l(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 73ac057..7e5087f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -20,6 +20,8 @@ #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> @@ -39,6 +41,16 @@ static const int64_t kOffloadPauseMaxUs = 60000000ll; // static const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; +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); + } +} + NuPlayer::Renderer::Renderer( const sp<MediaPlayerBase::AudioSink> &sink, const sp<AMessage> ¬ify, @@ -68,6 +80,7 @@ NuPlayer::Renderer::Renderer( mVideoLateByUs(0ll), mAudioOffloadPauseTimeoutGeneration(0), mAudioOffloadTornDown(false) { + readProperties(); } NuPlayer::Renderer::~Renderer() { @@ -576,6 +589,11 @@ 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; @@ -976,6 +994,8 @@ void NuPlayer::Renderer::onPause() { } void NuPlayer::Renderer::onResume() { + readProperties(); + if (!mPaused) { return; } @@ -1031,7 +1051,7 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { // become stale. Assuming that the MixerThread runs 20ms, with FastMixer at 5ms, // the max latency should be about 25ms with an average around 12ms (to be verified). // For safety we use 100ms. - ALOGW("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)", + ALOGV("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)", (long long)nowUs, (long long)numFramesPlayedAt); numFramesPlayedAt = nowUs - kStaleTimestamp100ms; } @@ -1061,7 +1081,7 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { // numFramesPlayedAt, by a time amount greater than numFramesPlayed. // // Both of these are transitory conditions. - ALOGW("getPlayedOutAudioDurationUs: negative timestamp %lld set to zero", (long long)durationUs); + ALOGV("getPlayedOutAudioDurationUs: negative duration %lld set to zero", (long long)durationUs); durationUs = 0; } ALOGV("getPlayedOutAudioDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)", |