summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/IStreamSource.h5
-rw-r--r--include/media/mediaplayer.h1
-rw-r--r--media/libmedia/IStreamSource.cpp3
-rw-r--r--media/libmedia/mediaplayer.cpp36
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp28
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp49
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h9
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.cpp20
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/MP4Source.h2
-rw-r--r--media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp7
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp92
-rw-r--r--media/libstagefright/httplive/M3UParser.cpp9
-rw-r--r--media/libstagefright/include/LiveSession.h14
-rw-r--r--media/libstagefright/include/M3UParser.h2
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp16
23 files changed, 276 insertions, 46 deletions
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 61b9d5a..39e0a9e 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -73,6 +73,11 @@ struct IStreamListener : public IInterface {
// ATSParser::DiscontinuityType.
static const char *const kKeyDiscontinuityMask;
+ // Optionally signalled as part of a discontinuity that includes
+ // DISCONTINUITY_TIME. It indicates the media time (in us) to be associated
+ // with the next PTS occuring in the stream. The value is of type int64_t.
+ static const char *const kKeyMediaTimeUs;
+
virtual void issueCommand(
Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0;
};
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index f7cebc5..d753eba 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -249,7 +249,6 @@ private:
sp<MediaPlayerListener> mListener;
void* mCookie;
media_player_states mCurrentState;
- int mDuration;
int mCurrentPosition;
int mSeekPosition;
bool mPrepareSync;
diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp
index 78d810d..68ffca8 100644
--- a/media/libmedia/IStreamSource.cpp
+++ b/media/libmedia/IStreamSource.cpp
@@ -32,6 +32,9 @@ const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS";
// static
const char *const IStreamListener::kKeyDiscontinuityMask = "discontinuity-mask";
+// static
+const char *const IStreamListener::kKeyMediaTimeUs = "media-time-us";
+
enum {
// IStreamSource
SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index b52a37d..bbbf4b6 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -47,7 +47,6 @@ MediaPlayer::MediaPlayer()
ALOGV("constructor");
mListener = NULL;
mCookie = NULL;
- mDuration = -1;
mStreamType = AUDIO_STREAM_MUSIC;
mCurrentPosition = -1;
mSeekPosition = -1;
@@ -90,7 +89,6 @@ void MediaPlayer::disconnect()
// always call with lock held
void MediaPlayer::clear_l()
{
- mDuration = -1;
mCurrentPosition = -1;
mSeekPosition = -1;
mVideoWidth = mVideoHeight = 0;
@@ -395,14 +393,14 @@ status_t MediaPlayer::getCurrentPosition(int *msec)
status_t MediaPlayer::getDuration_l(int *msec)
{
- ALOGV("getDuration");
+ ALOGV("getDuration_l");
bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE));
if (mPlayer != 0 && isValidState) {
- status_t ret = NO_ERROR;
- if (mDuration <= 0)
- ret = mPlayer->getDuration(&mDuration);
- if (msec)
- *msec = mDuration;
+ int durationMs;
+ status_t ret = mPlayer->getDuration(&durationMs);
+ if (msec) {
+ *msec = durationMs;
+ }
return ret;
}
ALOGE("Attempt to call getDuration without a valid mediaplayer");
@@ -422,14 +420,28 @@ status_t MediaPlayer::seekTo_l(int msec)
if ( msec < 0 ) {
ALOGW("Attempt to seek to invalid position: %d", msec);
msec = 0;
- } else if ((mDuration > 0) && (msec > mDuration)) {
- ALOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration);
- msec = mDuration;
}
+
+ int durationMs;
+ status_t err = mPlayer->getDuration(&durationMs);
+
+ if (err != OK) {
+ ALOGW("Stream has no duration and is therefore not seekable.");
+ return err;
+ }
+
+ if (msec > durationMs) {
+ ALOGW("Attempt to seek to past end of file: request = %d, "
+ "durationMs = %d",
+ msec,
+ durationMs);
+
+ msec = durationMs;
+ }
+
// cache duration
mCurrentPosition = msec;
if (mSeekPosition < 0) {
- getDuration_l(NULL);
mSeekPosition = msec;
return mPlayer->seekTo(msec);
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index f0c3240..f281879 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -258,8 +258,8 @@ void NuPlayer::GenericSource::readBuffer(
}
}
-bool NuPlayer::GenericSource::isSeekable() {
- return true;
+uint32_t NuPlayer::GenericSource::flags() const {
+ return FLAG_SEEKABLE;
}
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index e50b855..e1ce2c1 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -47,7 +47,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source {
virtual status_t getDuration(int64_t *durationUs);
virtual status_t seekTo(int64_t seekTimeUs);
- virtual bool isSeekable();
+
+ virtual uint32_t flags() const;
protected:
virtual ~GenericSource();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 1e98f35..5dcca12 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -121,9 +121,20 @@ status_t NuPlayer::HTTPLiveSource::feedMoreTSData() {
} else {
if (buffer[0] == 0x00) {
// XXX legacy
- sp<AMessage> extra;
+
+ uint8_t type = buffer[1];
+
+ sp<AMessage> extra = new AMessage;
+
+ if (type & 2) {
+ int64_t mediaTimeUs;
+ memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
+
+ extra->setInt64(IStreamListener::kKeyMediaTimeUs, mediaTimeUs);
+ }
+
mTSParser->signalDiscontinuity(
- buffer[1] == 0x00
+ ((type & 1) == 0)
? ATSParser::DISCONTINUITY_SEEK
: ATSParser::DISCONTINUITY_FORMATCHANGE,
extra);
@@ -181,8 +192,17 @@ status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) {
return OK;
}
-bool NuPlayer::HTTPLiveSource::isSeekable() {
- return mLiveSession->isSeekable();
+uint32_t NuPlayer::HTTPLiveSource::flags() const {
+ uint32_t flags = 0;
+ if (mLiveSession->isSeekable()) {
+ flags |= FLAG_SEEKABLE;
+ }
+
+ if (mLiveSession->hasDynamicDuration()) {
+ flags |= FLAG_DYNAMIC_DURATION;
+ }
+
+ return flags;
}
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index 9950a9e..79f4ab8 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -41,7 +41,8 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source {
virtual status_t getDuration(int64_t *durationUs);
virtual status_t seekTo(int64_t seekTimeUs);
- virtual bool isSeekable();
+
+ virtual uint32_t flags() const;
protected:
virtual ~HTTPLiveSource();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 1ddf775..ff27873 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -59,6 +59,7 @@ NuPlayer::NuPlayer()
mVideoEOS(false),
mScanSourcesPending(false),
mScanSourcesGeneration(0),
+ mPollDurationGeneration(0),
mTimeDiscontinuityPending(false),
mFlushingAudio(NONE),
mFlushingVideo(NONE),
@@ -210,6 +211,28 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatPollDuration:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mPollDurationGeneration) {
+ // stale
+ break;
+ }
+
+ int64_t durationUs;
+ if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
+ sp<NuPlayerDriver> driver = mDriver.promote();
+ if (driver != NULL) {
+ driver->notifyDuration(durationUs);
+ }
+ }
+
+ msg->post(1000000ll); // poll again in a second.
+ break;
+ }
+
case kWhatSetVideoNativeWindow:
{
ALOGV("kWhatSetVideoNativeWindow");
@@ -274,6 +297,9 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
ALOGV("scanning sources haveAudio=%d, haveVideo=%d",
mAudioDecoder != NULL, mVideoDecoder != NULL);
+ bool mHadAnySourcesBefore =
+ (mAudioDecoder != NULL) || (mVideoDecoder != NULL);
+
if (mNativeWindow != NULL) {
instantiateDecoder(false, &mVideoDecoder);
}
@@ -282,6 +308,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
instantiateDecoder(true, &mAudioDecoder);
}
+ if (!mHadAnySourcesBefore
+ && (mAudioDecoder != NULL || mVideoDecoder != NULL)) {
+ // This is the first time we've found anything playable.
+
+ uint32_t flags = mSource->flags();
+
+ if (flags & Source::FLAG_DYNAMIC_DURATION) {
+ schedulePollDuration();
+ }
+ }
+
status_t err;
if ((err = mSource->feedMoreTSData()) != OK) {
if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
@@ -534,6 +571,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
{
ALOGV("kWhatReset");
+ cancelPollDuration();
+
if (mRenderer != NULL) {
// There's an edge case where the renderer owns all output
// buffers and is paused, therefore the decoder will not read
@@ -976,4 +1015,14 @@ status_t NuPlayer::setVideoScalingMode(int32_t mode) {
return OK;
}
+void NuPlayer::schedulePollDuration() {
+ sp<AMessage> msg = new AMessage(kWhatPollDuration, id());
+ msg->setInt32("generation", mPollDurationGeneration);
+ msg->post();
+}
+
+void NuPlayer::cancelPollDuration() {
+ ++mPollDurationGeneration;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 36d3a9c..31efb2e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -88,6 +88,7 @@ private:
kWhatSeek = 'seek',
kWhatPause = 'paus',
kWhatResume = 'rsme',
+ kWhatPollDuration = 'polD',
};
wp<NuPlayerDriver> mDriver;
@@ -107,6 +108,8 @@ private:
bool mScanSourcesPending;
int32_t mScanSourcesGeneration;
+ int32_t mPollDurationGeneration;
+
enum FlushStatus {
NONE,
AWAITING_DISCONTINUITY,
@@ -150,6 +153,9 @@ private:
void finishReset();
void postScanSources();
+ void schedulePollDuration();
+ void cancelPollDuration();
+
DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 66aeff3..a635340 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -25,6 +25,11 @@ namespace android {
struct ABuffer;
struct NuPlayer::Source : public RefBase {
+ enum Flags {
+ FLAG_SEEKABLE = 1,
+ FLAG_DYNAMIC_DURATION = 2,
+ };
+
Source() {}
virtual void start() = 0;
@@ -47,9 +52,7 @@ struct NuPlayer::Source : public RefBase {
return INVALID_OPERATION;
}
- virtual bool isSeekable() {
- return false;
- }
+ virtual uint32_t flags() const = 0;
protected:
virtual ~Source() {}
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 5a7a785..cf455bd 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -210,8 +210,8 @@ void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) {
mHandler->seek(seekTimeUs);
}
-bool NuPlayer::RTSPSource::isSeekable() {
- return true;
+uint32_t NuPlayer::RTSPSource::flags() const {
+ return FLAG_SEEKABLE;
}
void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index f07c724..779d791 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -46,7 +46,8 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source {
virtual status_t getDuration(int64_t *durationUs);
virtual status_t seekTo(int64_t seekTimeUs);
- virtual bool isSeekable();
+
+ virtual uint32_t flags() const;
void onMessageReceived(const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index a1fd2ed..7159404 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -93,8 +93,22 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() {
} else {
if (buffer[0] == 0x00) {
// XXX legacy
+
+ if (extra == NULL) {
+ extra = new AMessage;
+ }
+
+ uint8_t type = buffer[1];
+
+ if (type & 2) {
+ int64_t mediaTimeUs;
+ memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
+
+ extra->setInt64(IStreamListener::kKeyMediaTimeUs, mediaTimeUs);
+ }
+
mTSParser->signalDiscontinuity(
- buffer[1] == 0x00
+ ((type & 1) == 0)
? ATSParser::DISCONTINUITY_SEEK
: ATSParser::DISCONTINUITY_FORMATCHANGE,
extra);
@@ -159,5 +173,9 @@ status_t NuPlayer::StreamingSource::dequeueAccessUnit(
return err;
}
+uint32_t NuPlayer::StreamingSource::flags() const {
+ return 0;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h
index 3971e2a..a27b58a 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.h
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h
@@ -35,6 +35,8 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source {
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
+ virtual uint32_t flags() const;
+
protected:
virtual ~StreamingSource();
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
index ffb3a65..a62d5a2 100644
--- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
+++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
@@ -133,4 +133,8 @@ status_t MP4Source::dequeueAccessUnit(
return mParser->dequeueAccessUnit(audio, accessUnit);
}
+uint32_t MP4Source::flags() const {
+ return 0;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
index 4e927af..abca236 100644
--- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
+++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
@@ -35,6 +35,8 @@ struct MP4Source : public NuPlayer::Source {
virtual status_t dequeueAccessUnit(
bool audio, sp<ABuffer> *accessUnit);
+ virtual uint32_t flags() const;
+
protected:
virtual ~MP4Source();
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
index 32a0ec8..91ce175 100644
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -65,7 +65,10 @@ status_t ChromiumHTTPDataSource::connect(
if (getUID(&uid)) {
mDelegate->setUID(uid);
}
+
+#if defined(LOG_NDEBUG) && !LOG_NDEBUG
LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect on behalf of uid %d", uid);
+#endif
return connect_l(uri, headers, offset);
}
@@ -78,8 +81,10 @@ status_t ChromiumHTTPDataSource::connect_l(
disconnect_l();
}
- LOG_PRI(ANDROID_LOG_INFO, LOG_TAG,
+#if defined(LOG_NDEBUG) && !LOG_NDEBUG
+ LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG,
"connect to <URL suppressed> @%lld", offset);
+#endif
mURI = uri;
mContentType = String8("application/octet-stream");
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 93d6429..733753b 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -55,7 +55,9 @@ LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
mSeqNumber(-1),
mSeekTimeUs(-1),
mNumRetries(0),
+ mStartOfPlayback(true),
mDurationUs(-1),
+ mDurationFixed(false),
mSeekDone(false),
mDisconnectPending(false),
mMonitorQueueGeneration(0),
@@ -311,6 +313,8 @@ status_t LiveSession::fetchFile(
}
sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
+ ALOGV("fetchPlaylist '%s'", url);
+
*unchanged = false;
sp<ABuffer> buffer;
@@ -364,6 +368,37 @@ sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
return playlist;
}
+int64_t LiveSession::getSegmentStartTimeUs(int32_t seqNumber) const {
+ CHECK(mPlaylist != NULL);
+
+ int32_t firstSeqNumberInPlaylist;
+ if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
+ "media-sequence", &firstSeqNumberInPlaylist)) {
+ firstSeqNumberInPlaylist = 0;
+ }
+
+ int32_t lastSeqNumberInPlaylist =
+ firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+
+ CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
+ CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
+
+ int64_t segmentStartUs = 0ll;
+ for (int32_t index = 0;
+ index < seqNumber - firstSeqNumberInPlaylist; ++index) {
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(
+ index, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ segmentStartUs += itemDurationUs;
+ }
+
+ return segmentStartUs;
+}
+
static double uniformRand() {
return (double)rand() / RAND_MAX;
}
@@ -512,8 +547,6 @@ rinse_repeat:
url = mMasterURL;
}
- bool firstTime = (mPlaylist == NULL);
-
if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) {
// If we switch bandwidths, do not pay any heed to whether
// playlists changed since the last time...
@@ -535,11 +568,12 @@ rinse_repeat:
mPlaylist = playlist;
}
- if (firstTime) {
+ if (!mDurationFixed) {
Mutex::Autolock autoLock(mLock);
- if (!mPlaylist->isComplete()) {
+ if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) {
mDurationUs = -1;
+ mDurationFixed = true;
} else {
mDurationUs = 0;
for (size_t i = 0; i < mPlaylist->size(); ++i) {
@@ -552,6 +586,8 @@ rinse_repeat:
mDurationUs += itemDurationUs;
}
+
+ mDurationFixed = mPlaylist->isComplete();
}
}
@@ -569,7 +605,7 @@ rinse_repeat:
bool bandwidthChanged = false;
if (mSeekTimeUs >= 0) {
- if (mPlaylist->isComplete()) {
+ if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
size_t index = 0;
int64_t segmentStartUs = 0;
while (index < mPlaylist->size()) {
@@ -617,13 +653,21 @@ rinse_repeat:
mCondition.broadcast();
}
+ const int32_t lastSeqNumberInPlaylist =
+ firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+
if (mSeqNumber < 0) {
- mSeqNumber = firstSeqNumberInPlaylist;
+ if (mPlaylist->isComplete()) {
+ mSeqNumber = firstSeqNumberInPlaylist;
+ } else {
+ // If this is a live session, start 3 segments from the end.
+ mSeqNumber = lastSeqNumberInPlaylist - 3;
+ if (mSeqNumber < firstSeqNumberInPlaylist) {
+ mSeqNumber = firstSeqNumberInPlaylist;
+ }
+ }
}
- int32_t lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
-
if (mSeqNumber < firstSeqNumberInPlaylist
|| mSeqNumber > lastSeqNumberInPlaylist) {
if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) {
@@ -686,6 +730,9 @@ rinse_repeat:
range_length = -1;
}
+ ALOGV("fetching segment %d from (%d .. %d)",
+ mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
+
sp<ABuffer> buffer;
status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length);
if (err != OK) {
@@ -737,6 +784,11 @@ rinse_repeat:
bandwidthChanged = false;
}
+ if (mStartOfPlayback) {
+ seekDiscontinuity = true;
+ mStartOfPlayback = false;
+ }
+
if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) {
// Signal discontinuity.
@@ -747,7 +799,19 @@ rinse_repeat:
memset(tmp->data(), 0, tmp->size());
// signal a 'hard' discontinuity for explicit or bandwidthChanged.
- tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0;
+ uint8_t type = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0;
+
+ if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
+ // If this was a live event this made no sense since
+ // we don't have access to all the segment before the current
+ // one.
+ int64_t segmentStartTimeUs = getSegmentStartTimeUs(mSeqNumber);
+ memcpy(tmp->data() + 2, &segmentStartTimeUs, sizeof(segmentStartTimeUs));
+
+ type |= 2;
+ }
+
+ tmp->data()[1] = type;
mDataSource->queueBuffer(tmp);
}
@@ -923,17 +987,21 @@ void LiveSession::onSeek(const sp<AMessage> &msg) {
postMonitorQueue();
}
-status_t LiveSession::getDuration(int64_t *durationUs) {
+status_t LiveSession::getDuration(int64_t *durationUs) const {
Mutex::Autolock autoLock(mLock);
*durationUs = mDurationUs;
return OK;
}
-bool LiveSession::isSeekable() {
+bool LiveSession::isSeekable() const {
int64_t durationUs;
return getDuration(&durationUs) == OK && durationUs >= 0;
}
+bool LiveSession::hasDynamicDuration() const {
+ return !mDurationFixed;
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 7d3cf05..44e03dc 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -32,7 +32,8 @@ M3UParser::M3UParser(
mBaseURI(baseURI),
mIsExtM3U(false),
mIsVariantPlaylist(false),
- mIsComplete(false) {
+ mIsComplete(false),
+ mIsEvent(false) {
mInitCheck = parse(data, size);
}
@@ -55,6 +56,10 @@ bool M3UParser::isComplete() const {
return mIsComplete;
}
+bool M3UParser::isEvent() const {
+ return mIsEvent;
+}
+
sp<AMessage> M3UParser::meta() {
return mMeta;
}
@@ -200,6 +205,8 @@ status_t M3UParser::parse(const void *_data, size_t size) {
err = parseCipherInfo(line, &itemMeta, mBaseURI);
} else if (line.startsWith("#EXT-X-ENDLIST")) {
mIsComplete = true;
+ } else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:EVENT")) {
+ mIsEvent = true;
} else if (line.startsWith("#EXTINF")) {
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
index 3a11612..f329cc9 100644
--- a/media/libstagefright/include/LiveSession.h
+++ b/media/libstagefright/include/LiveSession.h
@@ -48,8 +48,10 @@ struct LiveSession : public AHandler {
// Blocks until seek is complete.
void seekTo(int64_t timeUs);
- status_t getDuration(int64_t *durationUs);
- bool isSeekable();
+ status_t getDuration(int64_t *durationUs) const;
+
+ bool isSeekable() const;
+ bool hasDynamicDuration() const;
protected:
virtual ~LiveSession();
@@ -95,10 +97,12 @@ private:
int32_t mSeqNumber;
int64_t mSeekTimeUs;
int32_t mNumRetries;
+ bool mStartOfPlayback;
- Mutex mLock;
+ mutable Mutex mLock;
Condition mCondition;
int64_t mDurationUs;
+ bool mDurationFixed; // Duration has been determined once and for all.
bool mSeekDone;
bool mDisconnectPending;
@@ -136,6 +140,10 @@ private:
static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
+ // Returns the media time in us of the segment specified by seqNumber.
+ // This is computed by summing the durations of all segments before it.
+ int64_t getSegmentStartTimeUs(int32_t seqNumber) const;
+
DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
};
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index e30d6fd..2d2f50f 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -33,6 +33,7 @@ struct M3UParser : public RefBase {
bool isExtM3U() const;
bool isVariantPlaylist() const;
bool isComplete() const;
+ bool isEvent() const;
sp<AMessage> meta();
@@ -54,6 +55,7 @@ private:
bool mIsExtM3U;
bool mIsVariantPlaylist;
bool mIsComplete;
+ bool mIsEvent;
sp<AMessage> mMeta;
Vector<Item> mItems;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 9faa6bc..4f6c4b2 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -215,6 +215,14 @@ bool ATSParser::Program::parsePID(
void ATSParser::Program::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
+ int64_t mediaTimeUs;
+ if ((type & DISCONTINUITY_TIME)
+ && extra != NULL
+ && extra->findInt64(
+ IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
+ mFirstPTSValid = false;
+ }
+
for (size_t i = 0; i < mStreams.size(); ++i) {
mStreams.editValueAt(i)->signalDiscontinuity(type, extra);
}
@@ -929,7 +937,13 @@ status_t ATSParser::feedTSPacket(const void *data, size_t size) {
void ATSParser::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
- if (type == DISCONTINUITY_ABSOLUTE_TIME) {
+ int64_t mediaTimeUs;
+ if ((type & DISCONTINUITY_TIME)
+ && extra != NULL
+ && extra->findInt64(
+ IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
+ mAbsoluteTimeAnchorUs = mediaTimeUs;
+ } else if (type == DISCONTINUITY_ABSOLUTE_TIME) {
int64_t timeUs;
CHECK(extra->findInt64("timeUs", &timeUs));