summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmediaplayerservice')
-rw-r--r--media/libmediaplayerservice/VideoFrameScheduler.cpp31
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp53
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp65
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp10
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp19
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp24
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> &notify,
@@ -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)",