NuPlayer2: support end position of data source
Test: cts
Bug: 112549021
Change-Id: I5143754eb22fa42c3134474c584d986eb6f2b33d
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 83a10bf..5b7bf4a 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -216,6 +216,7 @@
mAudioDecoderGeneration(0),
mVideoDecoderGeneration(0),
mRendererGeneration(0),
+ mEOSMonitorGeneration(0),
mLastStartedPlayingTimeNs(0),
mPreviousSeekTimeUs(0),
mAudioEOS(false),
@@ -586,6 +587,11 @@
msg->post();
}
+void NuPlayer2::rewind() {
+ sp<AMessage> msg = new AMessage(kWhatRewind, this);
+ msg->post();
+}
+
void NuPlayer2::writeTrackInfo(
PlayerMessage* reply, const sp<AMessage>& format) const {
if (format == NULL) {
@@ -713,6 +719,22 @@
break;
}
+ case kWhatEOSMonitor:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ if (generation != mEOSMonitorGeneration || reason != MediaClock::TIMER_REASON_REACHED) {
+ break; // stale or reset
+ }
+
+ ALOGV("kWhatEOSMonitor");
+ notifyListener(mCurrentSourceInfo.mSrcId, MEDIA2_PLAYBACK_COMPLETE, 0, 0);
+ break;
+ }
+
case kWhatGetBufferingSettings:
{
sp<AReplyToken> replyID;
@@ -1522,6 +1544,42 @@
break;
}
+ case kWhatRewind:
+ {
+ ALOGV("kWhatRewind");
+
+ int64_t seekTimeUs = mCurrentSourceInfo.mStartTimeUs;
+ int32_t mode = MediaPlayer2SeekMode::SEEK_CLOSEST;
+
+ if (!mStarted) {
+ if (!mSourceStarted) {
+ mSourceStarted = true;
+ mCurrentSourceInfo.mSource->start();
+ }
+ performSeek(seekTimeUs, (MediaPlayer2SeekMode)mode);
+ break;
+ }
+
+ // seeks can take a while, so we essentially paused
+ notifyListener(mCurrentSourceInfo.mSrcId, MEDIA2_PAUSED, 0, 0);
+
+ mDeferredActions.push_back(
+ new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
+ FLUSH_CMD_FLUSH /* video */));
+
+ mDeferredActions.push_back(
+ new SeekAction(seekTimeUs, (MediaPlayer2SeekMode)mode));
+
+ // After a flush without shutdown, decoder is paused.
+ // Don't resume it until source seek is done, otherwise it could
+ // start pulling stale data too soon.
+ mDeferredActions.push_back(
+ new ResumeDecoderAction(false /* needNotify */));
+
+ processDeferredActions();
+ break;
+ }
+
case kWhatPause:
{
if (!mStarted) {
@@ -1675,6 +1733,7 @@
mRenderer->setVideoFrameRate(rate);
}
+ addEndTimeMonitor();
// Renderer is created in paused state.
if (play) {
mRenderer->resume();
@@ -1693,6 +1752,13 @@
postScanSources();
}
+void NuPlayer2::addEndTimeMonitor() {
+ sp<AMessage> msg = new AMessage(kWhatEOSMonitor, this);
+ ++mEOSMonitorGeneration;
+ msg->setInt32("generation", mEOSMonitorGeneration);
+ mMediaClock->addTimer(msg, mCurrentSourceInfo.mEndTimeUs);
+}
+
void NuPlayer2::startPlaybackTimer(const char *where) {
Mutex::Autolock autoLock(mPlayingTimeLock);
if (mLastStartedPlayingTimeNs == 0) {
@@ -2473,17 +2539,22 @@
long previousSrcId;
{
Mutex::Autolock autoLock(mSourceLock);
- mCurrentSourceInfo.mSource = mNextSourceInfo.mSource;
- mNextSourceInfo.mSource = NULL;
previousSrcId = mCurrentSourceInfo.mSrcId;
- mCurrentSourceInfo.mSrcId = mNextSourceInfo.mSrcId;
- ++mNextSourceInfo.mSrcId; // to distinguish the two sources.
+
+ mCurrentSourceInfo = mNextSourceInfo;
+ mNextSourceInfo = SourceInfo();
+ mNextSourceInfo.mSrcId = ~mCurrentSourceInfo.mSrcId; // to distinguish the two sources.
}
if (mDriver != NULL) {
sp<NuPlayer2Driver> driver = mDriver.promote();
if (driver != NULL) {
notifyListener(previousSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_END, 0);
+
+ int64_t durationUs;
+ if (mCurrentSourceInfo.mSource->getDuration(&durationUs) == OK) {
+ driver->notifyDuration(mCurrentSourceInfo.mSrcId, durationUs);
+ }
notifyListener(
mCurrentSourceInfo.mSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_START, 0);
}
@@ -2494,6 +2565,8 @@
mResetting = false;
mSourceStarted = false;
+ addEndTimeMonitor();
+
// Modular DRM
if (mCrypto != NULL) {
// decoders will be flushed before this so their mCrypto would go away on their own
@@ -2589,37 +2662,50 @@
switch (what) {
case Source::kWhatPrepared:
{
- ALOGV("NuPlayer2::onSourceNotify Source::kWhatPrepared source: %p",
- mCurrentSourceInfo.mSource.get());
- if (mCurrentSourceInfo.mSource == NULL) {
- // This is a stale notification from a source that was
- // asynchronously preparing when the client called reset().
- // We handled the reset, the source is gone.
- break;
- }
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- if (err != OK) {
- // shut down potential secure codecs in case client never calls reset
- mDeferredActions.push_back(
- new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */,
- FLUSH_CMD_SHUTDOWN /* video */));
- processDeferredActions();
- } else {
- mPrepared = true;
- }
-
- sp<NuPlayer2Driver> driver = mDriver.promote();
- if (driver != NULL) {
- // notify duration first, so that it's definitely set when
- // the app received the "prepare complete" callback.
- int64_t durationUs;
- if (mCurrentSourceInfo.mSource->getDuration(&durationUs) == OK) {
- driver->notifyDuration(srcId, durationUs);
+ ALOGV("NuPlayer2::onSourceNotify Source::kWhatPrepared source:%p, Id(%lld)",
+ mCurrentSourceInfo.mSource.get(), (long long)srcId);
+ if (srcId == mCurrentSourceInfo.mSrcId) {
+ if (mCurrentSourceInfo.mSource == NULL) {
+ // This is a stale notification from a source that was
+ // asynchronously preparing when the client called reset().
+ // We handled the reset, the source is gone.
+ break;
}
- driver->notifyPrepareCompleted(srcId, err);
+
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ if (err != OK) {
+ // shut down potential secure codecs in case client never calls reset
+ mDeferredActions.push_back(
+ new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */,
+ FLUSH_CMD_SHUTDOWN /* video */));
+ processDeferredActions();
+ } else {
+ mPrepared = true;
+ }
+
+ sp<NuPlayer2Driver> driver = mDriver.promote();
+ if (driver != NULL) {
+ // notify duration first, so that it's definitely set when
+ // the app received the "prepare complete" callback.
+ int64_t durationUs;
+ if (mCurrentSourceInfo.mSource->getDuration(&durationUs) == OK) {
+ driver->notifyDuration(srcId, durationUs);
+ }
+ driver->notifyPrepareCompleted(srcId, err);
+ }
+ } else if (srcId == mNextSourceInfo.mSrcId) {
+ if (mNextSourceInfo.mSource == NULL) {
+ break; // stale
+ }
+
+ sp<NuPlayer2Driver> driver = mDriver.promote();
+ if (driver != NULL) {
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+ driver->notifyPrepareCompleted(srcId, err);
+ }
}
break;
@@ -2665,7 +2751,9 @@
driver->notifyListener(
srcId, MEDIA2_INFO, MEDIA2_INFO_NOT_SEEKABLE, 0);
}
- driver->notifyFlagsChanged(srcId, flags);
+ if (srcId == mCurrentSourceInfo.mSrcId) {
+ driver->notifyFlagsChanged(srcId, flags);
+ }
}
if (srcId == mCurrentSourceInfo.mSrcId) {
@@ -3131,4 +3219,14 @@
mEndTimeUs(INT64_MAX) {
}
+NuPlayer2::SourceInfo & NuPlayer2::SourceInfo::operator=(const NuPlayer2::SourceInfo &other) {
+ mSource = other.mSource;
+ mDataSourceType = (DATA_SOURCE_TYPE)other.mDataSourceType;
+ mSrcId = other.mSrcId;
+ mSourceFlags = other.mSourceFlags;
+ mStartTimeUs = other.mStartTimeUs;
+ mEndTimeUs = other.mEndTimeUs;
+ return *this;
+}
+
} // namespace android