am 7b7f17dc: am b7c8e918: Add support for HLS playlists of type \'event\'.

* commit '7b7f17dc9b30ff4ecdf0aea9bcfa1c518d4ac1e7':
  Add support for HLS playlists of type 'event'.
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 @@
     // 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 @@
     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 @@
 // 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 @@
     ALOGV("constructor");
     mListener = NULL;
     mCookie = NULL;
-    mDuration = -1;
     mStreamType = AUDIO_STREAM_MUSIC;
     mCurrentPosition = -1;
     mSeekPosition = -1;
@@ -90,7 +89,6 @@
 // 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::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 @@
         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 @@
     }
 }
 
-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 @@
 
     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 @@
         } 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 @@
     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 @@
 
     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 756e76a..d3ec122 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -59,6 +59,7 @@
       mVideoEOS(false),
       mScanSourcesPending(false),
       mScanSourcesGeneration(0),
+      mPollDurationGeneration(0),
       mTimeDiscontinuityPending(false),
       mFlushingAudio(NONE),
       mFlushingVideo(NONE),
@@ -210,6 +211,28 @@
             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 @@
             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 @@
                 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) {
@@ -532,6 +569,8 @@
         {
             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
@@ -974,4 +1013,14 @@
     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 @@
         kWhatSeek                       = 'seek',
         kWhatPause                      = 'paus',
         kWhatResume                     = 'rsme',
+        kWhatPollDuration               = 'polD',
     };
 
     wp<NuPlayerDriver> mDriver;
@@ -107,6 +108,8 @@
     bool mScanSourcesPending;
     int32_t mScanSourcesGeneration;
 
+    int32_t mPollDurationGeneration;
+
     enum FlushStatus {
         NONE,
         AWAITING_DISCONTINUITY,
@@ -150,6 +153,9 @@
     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 @@
 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 @@
         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 6df2ddd..afaa5db 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -211,8 +211,8 @@
     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 @@
 
     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 @@
         } 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 @@
     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 @@
 
     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 @@
     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 @@
     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 @@
     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 @@
         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 @@
       mSeqNumber(-1),
       mSeekTimeUs(-1),
       mNumRetries(0),
+      mStartOfPlayback(true),
       mDurationUs(-1),
+      mDurationFixed(false),
       mSeekDone(false),
       mDisconnectPending(false),
       mMonitorQueueGeneration(0),
@@ -311,6 +313,8 @@
 }
 
 sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
+    ALOGV("fetchPlaylist '%s'", url);
+
     *unchanged = false;
 
     sp<ABuffer> buffer;
@@ -364,6 +368,37 @@
     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 @@
             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 @@
             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 @@
 
                     mDurationUs += itemDurationUs;
                 }
+
+                mDurationFixed = mPlaylist->isComplete();
             }
         }
 
@@ -569,7 +605,7 @@
     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 @@
         mCondition.broadcast();
     }
 
-    if (mSeqNumber < 0) {
-        mSeqNumber = firstSeqNumberInPlaylist;
-    }
-
-    int32_t lastSeqNumberInPlaylist =
+    const int32_t lastSeqNumberInPlaylist =
         firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
 
+    if (mSeqNumber < 0) {
+        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;
+            }
+        }
+    }
+
     if (mSeqNumber < firstSeqNumberInPlaylist
             || mSeqNumber > lastSeqNumberInPlaylist) {
         if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) {
@@ -686,6 +730,9 @@
         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 @@
         bandwidthChanged = false;
     }
 
+    if (mStartOfPlayback) {
+        seekDiscontinuity = true;
+        mStartOfPlayback = false;
+    }
+
     if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) {
         // Signal discontinuity.
 
@@ -747,7 +799,19 @@
         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 @@
     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 @@
       mBaseURI(baseURI),
       mIsExtM3U(false),
       mIsVariantPlaylist(false),
-      mIsComplete(false) {
+      mIsComplete(false),
+      mIsEvent(false) {
     mInitCheck = parse(data, size);
 }
 
@@ -55,6 +56,10 @@
     return mIsComplete;
 }
 
+bool M3UParser::isEvent() const {
+    return mIsEvent;
+}
+
 sp<AMessage> M3UParser::meta() {
     return mMeta;
 }
@@ -200,6 +205,8 @@
                 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 @@
     // 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 @@
     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 @@
 
     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 @@
     bool isExtM3U() const;
     bool isVariantPlaylist() const;
     bool isComplete() const;
+    bool isEvent() const;
 
     sp<AMessage> meta();
 
@@ -54,6 +55,7 @@
     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 @@
 
 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 @@
 
 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));