diff options
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)); |