<div dir="ltr">From f9b9daf8d32134ca60c5af6c73be1a8fd49a73a5 Mon Sep 17 00:00:00 2001<br>From: Abdullah Alansari <<a href="mailto:ahimta@gmail.com">ahimta@gmail.com</a>><br>Date: Mon, 30 Sep 2019 20:32:16 +0300<br>Subject: [PATCH] demux: hls: fix consistent live delay (#21614 WIP2)<br><br>The bug was first observed on<br><a href="https://www.youtube.com/watch?v=WI86J46VRUc">https://www.youtube.com/watch?v=WI86J46VRUc</a><br><br>This YouTube live-stream contains a wall-clock in the bottom-right<br>corner which makes it very convenient for testing.<br><br>Before this patch/fix, playback with VLC is 1minute+ behind the YouTube<br>website:sweat_smile: and after the patch, YouTube is the one<br>behind:smiley:.<br><br>This bug should manifest in all platforms in any YouTube live video.<br><br>The initial analysis of this bug showed that the reason for this delay<br>is that VLC plays the first video in the M38U file which can be 1minute+<br>before the current wall-clock time:sweat_smile:.<br><br>This was observed on <a href="http://www.tv.wataan.com/?video=show&show=104">http://www.tv.wataan.com/?video=show&show=104</a><br>where the delay was non-existent on Firefox and existent on VLC<br>:sweat_smile:.<br><br>On Firefox, the second-to-last video in the M3U8 was played while on<br>VLC the first video was played. And upon downloading both videos,<br>Firefox's downloaded has the correct wall-clock time while VLC's one<br>was 1minute+ behind:sweat_smile:.<br><br>And thus this patch does something similar to the Firefox observed<br>behavior above (which may just be from the website's own code).<br><br>I have almost the whole related RFC and the 6.3.3 sections is probably<br>the single most relevent:<br><a href="https://tools.ietf.org/html/rfc8216#section-6.3.3">https://tools.ietf.org/html/rfc8216#section-6.3.3</a><br>Specifically "the client SHOULD NOT choose a segment that<br>starts less than three target durations from the end of the<br>Playlist file."<br><br>This patch may possibly break non-HLS and not fix other adaptive-live<br>stuff (e.g: DASH), if they are already affected:sweat_smile:.<br><br>A list of tested samples that had the delay before this patch and don't<br>anymore:<br>1. <a href="https://www.youtube.com/watch?v=WI86J46VRUc">https://www.youtube.com/watch?v=WI86J46VRUc</a><br>2. <a href="http://livecdnh3.tvanywhere.ae/hls/jadeed/03.m3u8">http://livecdnh3.tvanywhere.ae/hls/jadeed/03.m3u8</a><br><br>Related to <a href="https://trac.videolan.org/vlc/ticket/21614#attachments">https://trac.videolan.org/vlc/ticket/21614#attachments</a><br>---<br> modules/demux/adaptive/SegmentTracker.cpp       |  8 ++++----<br> .../adaptive/playlist/BaseRepresentation.cpp    |  5 ++++-<br> .../adaptive/playlist/BaseRepresentation.h      |  5 ++++-<br> modules/demux/hls/playlist/Parser.cpp           | 17 +++++++++++++----<br> modules/demux/hls/playlist/Parser.hpp           |  6 ++++--<br> modules/demux/hls/playlist/Representation.cpp   |  7 +++++--<br> modules/demux/hls/playlist/Representation.hpp   |  5 ++++-<br> 7 files changed, 38 insertions(+), 15 deletions(-)<br><br>diff --git a/modules/demux/adaptive/SegmentTracker.cpp b/modules/demux/adaptive/SegmentTracker.cpp<br>index 583d977681..d54dcaf2a3 100644<br>--- a/modules/demux/adaptive/SegmentTracker.cpp<br>+++ b/modules/demux/adaptive/SegmentTracker.cpp<br>@@ -111,7 +111,7 @@ StreamFormat SegmentTracker::getCurrentFormat() const<br>     {<br>         /* Ensure ephemere content is updated/loaded */<br>         if(rep->needsUpdate())<br>-            (void) rep->runLocalUpdates(resources, 0, curNumber, false);<br>+            (void) rep->runLocalUpdates(resources, 0, curNumber, false, true);<br>         return rep->getStreamFormat();<br>     }<br>     return StreamFormat();<br>@@ -203,7 +203,7 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed,<br>     bool b_updated = false;<br>     /* Ensure ephemere content is updated/loaded */<br>     if(rep->needsUpdate())<br>-        b_updated = rep->runLocalUpdates(resources, getPlaybackTime(), curNumber, false);<br>+        b_updated = rep->runLocalUpdates(resources, getPlaybackTime(), curNumber, false, false);<br> <br>     if(prevRep && !rep->consistentSegmentNumber())<br>     {<br>@@ -310,7 +310,7 @@ bool SegmentTracker::setPositionByTime(vlc_tick_t time, bool restarted, bool try<br> <br>     /* Stream might not have been loaded at all (HLS) or expired */<br>     if(rep && rep->needsUpdate() &&<br>-       !rep->runLocalUpdates(resources, time, curNumber, false))<br>+       !rep->runLocalUpdates(resources, time, curNumber, false, false))<br>     {<br>         msg_Err(rep->getAdaptationSet()->getPlaylist()->getVLCObject(),<br>                 "Failed to update Representation %s", rep->getID().str().c_str());<br>@@ -391,7 +391,7 @@ void SegmentTracker::updateSelected()<br> {<br>     if(curRepresentation && curRepresentation->needsUpdate())<br>     {<br>-        curRepresentation->runLocalUpdates(resources, getPlaybackTime(), curNumber, true);<br>+        curRepresentation->runLocalUpdates(resources, getPlaybackTime(), curNumber, true, false);<br>         curRepresentation->scheduleNextUpdate(curNumber);<br>     }<br> }<br>diff --git a/modules/demux/adaptive/playlist/BaseRepresentation.cpp b/modules/demux/adaptive/playlist/BaseRepresentation.cpp<br>index 49b8cfcee0..17d33d6f24 100644<br>--- a/modules/demux/adaptive/playlist/BaseRepresentation.cpp<br>+++ b/modules/demux/adaptive/playlist/BaseRepresentation.cpp<br>@@ -94,7 +94,10 @@ bool BaseRepresentation::needsUpdate() const<br> }<br> <br> bool BaseRepresentation::runLocalUpdates(SharedResources *,<br>-                                         vlc_tick_t, uint64_t, bool)<br>+                                         vlc_tick_t,<br>+                                         uint64_t,<br>+                                         bool,<br>+                                         const bool isFirstPlaylist)<br> {<br>     return false;<br> }<br>diff --git a/modules/demux/adaptive/playlist/BaseRepresentation.h b/modules/demux/adaptive/playlist/BaseRepresentation.h<br>index 089ede1299..eeaff8fbdb 100644<br>--- a/modules/demux/adaptive/playlist/BaseRepresentation.h<br>+++ b/modules/demux/adaptive/playlist/BaseRepresentation.h<br>@@ -67,7 +67,10 @@ namespace adaptive<br>                 virtual vlc_tick_t  getMinAheadTime         (uint64_t) const;<br>                 virtual bool        needsUpdate             () const;<br>                 virtual bool        runLocalUpdates         (SharedResources *,<br>-                                                             vlc_tick_t, uint64_t, bool);<br>+                                                             vlc_tick_t,<br>+                                                             uint64_t,<br>+                                                             bool,<br>+                                                             const bool isFirstPlaylist);<br>                 virtual void        scheduleNextUpdate      (uint64_t);<br> <br>                 virtual void        debug                   (vlc_object_t *,int = 0) const;<br>diff --git a/modules/demux/hls/playlist/Parser.cpp b/modules/demux/hls/playlist/Parser.cpp<br>index fddcc9931a..bb72428bb7 100644<br>--- a/modules/demux/hls/playlist/Parser.cpp<br>+++ b/modules/demux/hls/playlist/Parser.cpp<br>@@ -144,7 +144,7 @@ void M3U8Parser::createAndFillRepresentation(vlc_object_t *p_obj, BaseAdaptation<br>     Representation *rep  = createRepresentation(adaptSet, tag);<br>     if(rep)<br>     {<br>-        parseSegments(p_obj, rep, tagslist);<br>+        parseSegments(p_obj, rep, tagslist, true);<br>         if(rep->isLive())<br>         {<br>             /* avoid update playlist immediately */<br>@@ -155,7 +155,9 @@ void M3U8Parser::createAndFillRepresentation(vlc_object_t *p_obj, BaseAdaptation<br>     }<br> }<br> <br>-bool M3U8Parser::appendSegmentsFromPlaylistURI(vlc_object_t *p_obj, Representation *rep)<br>+bool M3U8Parser::appendSegmentsFromPlaylistURI(vlc_object_t *p_obj,<br>+                                               Representation *rep,<br>+                                               const bool isFirstPlaylist)<br> {<br>     block_t *p_block = Retrieve::HTTP(p_obj, resources->getAuthStorage(),<br>                                       rep->getPlaylistUrl().toString());<br>@@ -167,7 +169,7 @@ bool M3U8Parser::appendSegmentsFromPlaylistURI(vlc_object_t *p_obj, Representati<br>             std::list<Tag *> tagslist = parseEntries(substream);<br>             vlc_stream_Delete(substream);<br> <br>-            parseSegments(p_obj, rep, tagslist);<br>+            parseSegments(p_obj, rep, tagslist, isFirstPlaylist);<br> <br>             releaseTagsList(tagslist);<br>         }<br>@@ -212,7 +214,10 @@ static bool parseEncryption(const AttributesTag *keytag, const Url &playlistUrl,<br>     }<br> }<br> <br>-void M3U8Parser::parseSegments(vlc_object_t *, Representation *rep, const std::list<Tag *> &tagslist)<br>+void M3U8Parser::parseSegments(vlc_object_t *,<br>+                               Representation *rep,<br>+                               const std::list<Tag *> &tagslist,<br>+                               const bool isFirstPlaylist)<br> {<br>     SegmentList *segmentList = new (std::nothrow) SegmentList(rep);<br> <br>@@ -365,6 +370,10 @@ void M3U8Parser::parseSegments(vlc_object_t *, Representation *rep, const std::l<br> <br>     if(rep->isLive())<br>     {<br>+        if (isFirstPlaylist) {<br>+            segmentList->pruneBySegmentNumber(sequenceNumber - 1);<br>+        }<br>+<br>         rep->getPlaylist()->duration.Set(0);<br>     }<br>     else if(totalduration > rep->getPlaylist()->duration.Get())<br>diff --git a/modules/demux/hls/playlist/Parser.hpp b/modules/demux/hls/playlist/Parser.hpp<br>index fc0ac6314f..824bfef360 100644<br>--- a/modules/demux/hls/playlist/Parser.hpp<br>+++ b/modules/demux/hls/playlist/Parser.hpp<br>@@ -58,13 +58,15 @@ namespace hls<br>                 virtual ~M3U8Parser    ();<br> <br>                 M3U8 *             parse  (vlc_object_t *p_obj, stream_t *p_stream, const std::string &);<br>-                bool appendSegmentsFromPlaylistURI(vlc_object_t *, Representation *);<br>+                bool appendSegmentsFromPlaylistURI(vlc_object_t *,<br>+                                                   Representation *,<br>+                                                   const bool isFirstPlaylist);<br> <br>             private:<br>                 Representation * createRepresentation(BaseAdaptationSet *, const AttributesTag *);<br>                 void createAndFillRepresentation(vlc_object_t *, BaseAdaptationSet *,<br>                                                  const AttributesTag *, const std::list<Tag *>&);<br>-                void parseSegments(vlc_object_t *, Representation *, const std::list<Tag *>&);<br>+                void parseSegments(vlc_object_t *, Representation *, const std::list<Tag *>&, const bool);<br>                 std::list<Tag *> parseEntries(stream_t *);<br>                 adaptive::SharedResources *resources;<br>         };<br>diff --git a/modules/demux/hls/playlist/Representation.cpp b/modules/demux/hls/playlist/Representation.cpp<br>index 6be82ac9e8..b2cce3f376 100644<br>--- a/modules/demux/hls/playlist/Representation.cpp<br>+++ b/modules/demux/hls/playlist/Representation.cpp<br>@@ -137,14 +137,17 @@ bool Representation::needsUpdate() const<br> }<br> <br> bool Representation::runLocalUpdates(SharedResources *res,<br>-                                     vlc_tick_t, uint64_t, bool)<br>+                                     vlc_tick_t,<br>+                                     uint64_t,<br>+                                     bool,<br>+                                     const bool isFirstPlaylist)<br> {<br>     const time_t now = time(NULL);<br>     AbstractPlaylist *playlist = getPlaylist();<br>     if(!b_loaded || (isLive() && nextUpdateTime < now))<br>     {<br>         M3U8Parser parser(res);<br>-        parser.appendSegmentsFromPlaylistURI(playlist->getVLCObject(), this);<br>+        parser.appendSegmentsFromPlaylistURI(playlist->getVLCObject(), this, isFirstPlaylist);<br>         b_loaded = true;<br> <br>         return true;<br>diff --git a/modules/demux/hls/playlist/Representation.hpp b/modules/demux/hls/playlist/Representation.hpp<br>index 444b2a1481..13f9068881 100644<br>--- a/modules/demux/hls/playlist/Representation.hpp<br>+++ b/modules/demux/hls/playlist/Representation.hpp<br>@@ -51,7 +51,10 @@ namespace hls<br>                 virtual bool needsUpdate() const;  /* reimpl */<br>                 virtual void debug(vlc_object_t *, int) const;  /* reimpl */<br>                 virtual bool runLocalUpdates(SharedResources *,<br>-                                             vlc_tick_t, uint64_t, bool); /* reimpl */<br>+                                             vlc_tick_t,<br>+                                             uint64_t,<br>+                                             bool,<br>+                                             const bool isFirstPlaylist); /* reimpl */<br>                 virtual uint64_t translateSegmentNumber(uint64_t, const SegmentInformation *) const; /* reimpl */<br> <br>             private:<br>-- <br>2.20.1<br><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr">Abdullah Alansari,<div><br></div><div>LinkedIn: <a href="https://linkedin.com/in/ahimta" target="_blank">https://linkedin.com/in/ahimta</a></div><div>GitHub: <a href="https://github.com/Ahimta" target="_blank">https://github.com/Ahimta</a></div><div>Twitter: <a href="https://twitter.com/Ahymta" target="_blank">https://twitter.com/Ahymta</a></div></div></div></div></div></div>