1 /*
2  * SegmentTracker.cpp
3  *****************************************************************************
4  * Copyright (C) 2014 - VideoLAN and VLC authors
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include "SegmentTracker.hpp"
26 #include "playlist/AbstractPlaylist.hpp"
27 #include "playlist/BaseRepresentation.h"
28 #include "playlist/BaseAdaptationSet.h"
29 #include "playlist/Segment.h"
30 #include "playlist/SegmentChunk.hpp"
31 #include "logic/AbstractAdaptationLogic.h"
32 #include "logic/BufferingLogic.hpp"
33 
34 #include <cassert>
35 #include <limits>
36 
37 using namespace adaptive;
38 using namespace adaptive::logic;
39 using namespace adaptive::playlist;
40 
SegmentTrackerEvent(SegmentChunk * s)41 SegmentTrackerEvent::SegmentTrackerEvent(SegmentChunk *s)
42 {
43     type = DISCONTINUITY;
44     u.discontinuity.sc = s;
45 }
46 
SegmentTrackerEvent(BaseRepresentation * prev,BaseRepresentation * next)47 SegmentTrackerEvent::SegmentTrackerEvent(BaseRepresentation *prev, BaseRepresentation *next)
48 {
49     type = SWITCHING;
50     u.switching.prev = prev;
51     u.switching.next = next;
52 }
53 
SegmentTrackerEvent(const StreamFormat * fmt)54 SegmentTrackerEvent::SegmentTrackerEvent(const StreamFormat *fmt)
55 {
56     type = FORMATCHANGE;
57     u.format.f = fmt;
58 }
59 
SegmentTrackerEvent(const ID & id,bool enabled)60 SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, bool enabled)
61 {
62     type = BUFFERING_STATE;
63     u.buffering.enabled = enabled;
64     u.buffering.id = &id;
65 }
66 
SegmentTrackerEvent(const ID & id,mtime_t min,mtime_t current,mtime_t target)67 SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t min, mtime_t current, mtime_t target)
68 {
69     type = BUFFERING_LEVEL_CHANGE;
70     u.buffering_level.minimum = min;
71     u.buffering_level.current = current;
72     u.buffering_level.target = target;
73     u.buffering.id = &id;
74 }
75 
SegmentTrackerEvent(const ID & id,mtime_t duration)76 SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t duration)
77 {
78     type = SEGMENT_CHANGE;
79     u.segment.duration = duration;
80     u.segment.id = &id;
81 }
82 
SegmentTracker(SharedResources * res,AbstractAdaptationLogic * logic_,const AbstractBufferingLogic * bl,BaseAdaptationSet * adaptSet)83 SegmentTracker::SegmentTracker(SharedResources *res,
84         AbstractAdaptationLogic *logic_,
85         const AbstractBufferingLogic *bl,
86         BaseAdaptationSet *adaptSet)
87 {
88     resources = res;
89     first = true;
90     initializing = true;
91     bufferingLogic = bl;
92     setAdaptationLogic(logic_);
93     adaptationSet = adaptSet;
94     format = StreamFormat::UNKNOWN;
95 }
96 
~SegmentTracker()97 SegmentTracker::~SegmentTracker()
98 {
99     reset();
100 }
101 
Position()102 SegmentTracker::Position::Position()
103 {
104     number = std::numeric_limits<uint64_t>::max();
105     rep = NULL;
106     init_sent = false;
107     index_sent = false;
108 }
109 
Position(BaseRepresentation * rep,uint64_t number)110 SegmentTracker::Position::Position(BaseRepresentation *rep, uint64_t number)
111 {
112     this->rep = rep;
113     this->number = number;
114     init_sent = false;
115     index_sent = false;
116 }
117 
isValid() const118 bool SegmentTracker::Position::isValid() const
119 {
120     return number != std::numeric_limits<uint64_t>::max() &&
121            rep != NULL;
122 }
123 
toString() const124 std::string SegmentTracker::Position::toString() const
125 {
126     std::stringstream ss;
127     ss.imbue(std::locale("C"));
128     if(isValid())
129         ss << "seg# " << number
130            << " " << init_sent
131            << ":" << index_sent
132            << " " << rep->getID().str();
133     else
134         ss << "invalid";
135     return ss.str();
136 }
137 
operator ++()138 SegmentTracker::Position & SegmentTracker::Position::operator ++()
139 {
140     if(isValid())
141     {
142         if(index_sent)
143             ++number;
144         else if(init_sent)
145             index_sent = true;
146         else
147             init_sent = true;
148         return *this;
149     }
150     return *this;
151 }
152 
setAdaptationLogic(AbstractAdaptationLogic * logic_)153 void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic *logic_)
154 {
155     logic = logic_;
156     registerListener(logic);
157 }
158 
getCurrentFormat() const159 StreamFormat SegmentTracker::getCurrentFormat() const
160 {
161     BaseRepresentation *rep = current.rep;
162     if(!rep)
163         rep = logic->getNextRepresentation(adaptationSet, NULL);
164     if(rep)
165     {
166         /* Ensure ephemere content is updated/loaded */
167         if(rep->needsUpdate(next.number))
168             (void) rep->runLocalUpdates(resources);
169         return rep->getStreamFormat();
170     }
171     return StreamFormat();
172 }
173 
getCurrentCodecs() const174 std::list<std::string> SegmentTracker::getCurrentCodecs() const
175 {
176     BaseRepresentation *rep = current.rep;
177     if(!rep)
178         rep = logic->getNextRepresentation(adaptationSet, NULL);
179     if(rep)
180         return rep->getCodecs();
181     return std::list<std::string>();
182 }
183 
getStreamDescription() const184 const std::string & SegmentTracker::getStreamDescription() const
185 {
186     return adaptationSet->description.Get();
187 }
188 
getStreamLanguage() const189 const std::string & SegmentTracker::getStreamLanguage() const
190 {
191     return adaptationSet->getLang();
192 }
193 
getStreamRole() const194 const Role & SegmentTracker::getStreamRole() const
195 {
196     return adaptationSet->getRole();
197 }
198 
reset()199 void SegmentTracker::reset()
200 {
201     notify(SegmentTrackerEvent(current.rep, NULL));
202     current = Position();
203     next = Position();
204     initializing = true;
205     format = StreamFormat::UNKNOWN;
206 }
207 
getNextChunk(bool switch_allowed,AbstractConnectionManager * connManager)208 SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed,
209                                             AbstractConnectionManager *connManager)
210 {
211     ISegment *segment;
212 
213     if(!adaptationSet)
214         return NULL;
215 
216     bool b_updated = false;
217     bool b_switched = false;
218 
219     /* starting */
220     if(!next.isValid())
221     {
222         next = getStartPosition();
223         b_switched = true;
224     }
225     else /* continuing, or seek */
226     {
227         if(!current.isValid() || !adaptationSet->isSegmentAligned() || initializing)
228             switch_allowed = false;
229 
230         if(switch_allowed)
231         {
232             Position temp;
233             temp.rep = logic->getNextRepresentation(adaptationSet, next.rep);
234             if(temp.rep && temp.rep != next.rep)
235             {
236                 /* Ensure ephemere content is updated/loaded */
237                 if(temp.rep->needsUpdate(next.number))
238                     b_updated = temp.rep->runLocalUpdates(resources);
239                 /* if we need to translate pos */
240                 if(!temp.rep->consistentSegmentNumber())
241                 {
242                     /* Convert our segment number */
243                     temp.number = temp.rep->translateSegmentNumber(next.number, next.rep);
244                 }
245                 else temp.number = next.number;
246             }
247             if(temp.isValid())
248             {
249                 next = temp;
250                 b_switched = current.isValid();
251             }
252         }
253     }
254 
255     if(!next.isValid())
256         return NULL;
257 
258     if(b_switched)
259     {
260         notify(SegmentTrackerEvent(current.rep, next.rep));
261         initializing = true;
262         assert(!next.index_sent);
263         assert(!next.init_sent);
264     }
265 
266     next.rep->scheduleNextUpdate(next.number, b_updated);
267     current = next;
268 
269     if(current.rep->getStreamFormat() != format)
270     {
271         /* Initial format ? */
272         if(format == StreamFormat(StreamFormat::UNKNOWN))
273         {
274             format = current.rep->getStreamFormat();
275         }
276         else
277         {
278             format = current.rep->getStreamFormat();
279             notify(SegmentTrackerEvent(&format)); /* Notify new demux format */
280             return NULL; /* Force current demux to end */
281         }
282     }
283     else if(format == StreamFormat(StreamFormat::UNKNOWN) && b_switched)
284     {
285         /* Handle the corner case when only the demuxer can know the format and
286          * demuxer starts after the format change (Probe != buffering) */
287         notify(SegmentTrackerEvent(&format)); /* Notify new demux format */
288         return NULL; /* Force current demux to end */
289     }
290 
291     if(format == StreamFormat(StreamFormat::UNSUPPORTED))
292     {
293         return NULL; /* Can't return chunk because no demux will be created */
294     }
295 
296     if(!current.init_sent)
297     {
298         ++next;
299         segment = current.rep->getSegment(BaseRepresentation::INFOTYPE_INIT);
300         if(segment)
301             return segment->toChunk(resources, connManager, current.number, current.rep);
302         current = next;
303     }
304 
305     if(!current.index_sent)
306     {
307         ++next;
308         segment = current.rep->getSegment(BaseRepresentation::INFOTYPE_INDEX);
309         if(segment)
310             return segment->toChunk(resources, connManager, current.number, current.rep);
311         current = next;
312     }
313 
314     bool b_gap = false;
315     segment = current.rep->getNextSegment(BaseRepresentation::INFOTYPE_MEDIA,
316                                           current.number, &current.number, &b_gap);
317     if(!segment)
318         return NULL;
319     if(b_gap)
320         next = current;
321 
322     if(initializing)
323     {
324         b_gap = false;
325         /* stop initializing after 1st chunk */
326         initializing = false;
327     }
328 
329     SegmentChunk *chunk = segment->toChunk(resources, connManager, next.number, next.rep);
330 
331     /* Notify new segment length for stats / logic */
332     if(chunk)
333     {
334         const Timescale timescale = next.rep->inheritTimescale();
335         notify(SegmentTrackerEvent(next.rep->getAdaptationSet()->getID(),
336                                    timescale.ToTime(segment->duration.Get())));
337     }
338 
339     /* We need to check segment/chunk format changes, as we can't rely on representation's (HLS)*/
340     if(chunk && format != chunk->getStreamFormat())
341     {
342         format = chunk->getStreamFormat();
343         notify(SegmentTrackerEvent(&format));
344     }
345 
346     /* Handle both implicit and explicit discontinuities */
347     if( (b_gap && next.number) || (chunk && chunk->discontinuity) )
348     {
349         notify(SegmentTrackerEvent(chunk));
350     }
351 
352     if(chunk)
353         ++next;
354 
355     return chunk;
356 }
357 
setPositionByTime(mtime_t time,bool restarted,bool tryonly)358 bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly)
359 {
360     Position pos = Position(current.rep, current.number);
361     if(!pos.isValid())
362         pos.rep = logic->getNextRepresentation(adaptationSet, NULL);
363 
364     if(!pos.rep)
365         return false;
366 
367     /* Stream might not have been loaded at all (HLS) or expired */
368     if(pos.rep->needsUpdate(pos.number) && !pos.rep->runLocalUpdates(resources))
369     {
370         msg_Err(adaptationSet->getPlaylist()->getVLCObject(),
371                 "Failed to update Representation %s",
372                 pos.rep->getID().str().c_str());
373         return false;
374     }
375 
376     if(pos.rep->getSegmentNumberByTime(time, &pos.number))
377     {
378         if(!tryonly)
379             setPosition(pos, restarted);
380         return true;
381     }
382     return false;
383 }
384 
setPosition(const Position & pos,bool restarted)385 void SegmentTracker::setPosition(const Position &pos, bool restarted)
386 {
387     if(restarted)
388         initializing = true;
389     current = Position();
390     next = pos;
391 }
392 
getStartPosition()393 SegmentTracker::Position SegmentTracker::getStartPosition()
394 {
395     Position pos;
396     pos.rep = logic->getNextRepresentation(adaptationSet, NULL);
397     if(pos.rep)
398     {
399         /* Ensure ephemere content is updated/loaded */
400         if(pos.rep->needsUpdate(pos.number))
401             pos.rep->runLocalUpdates(resources);
402         pos.number = bufferingLogic->getStartSegmentNumber(pos.rep);
403     }
404     return pos;
405 }
406 
setStartPosition()407 bool SegmentTracker::setStartPosition()
408 {
409     if(next.isValid())
410         return true;
411 
412     Position pos = getStartPosition();
413     if(!pos.isValid())
414         return false;
415 
416     next = pos;
417     return true;
418 }
419 
getPlaybackTime(bool b_next) const420 mtime_t SegmentTracker::getPlaybackTime(bool b_next) const
421 {
422     mtime_t time, duration;
423 
424     BaseRepresentation *rep = current.rep;
425     if(!rep)
426         rep = logic->getNextRepresentation(adaptationSet, NULL);
427 
428     if(rep &&
429        rep->getPlaybackTimeDurationBySegmentNumber(b_next ? next.number : current.number, &time, &duration))
430     {
431         return time;
432     }
433     return 0;
434 }
435 
getMediaPlaybackRange(mtime_t * start,mtime_t * end,mtime_t * length) const436 bool SegmentTracker::getMediaPlaybackRange(mtime_t *start, mtime_t *end,
437                                            mtime_t *length) const
438 {
439     if(!current.rep)
440         return false;
441     return current.rep->getMediaPlaybackRange(start, end, length);
442 }
443 
getMinAheadTime() const444 mtime_t SegmentTracker::getMinAheadTime() const
445 {
446     BaseRepresentation *rep = current.rep;
447     if(!rep)
448         rep = logic->getNextRepresentation(adaptationSet, NULL);
449     if(rep)
450     {
451         /* Ensure ephemere content is updated/loaded */
452         if(rep->needsUpdate(next.number))
453             (void) rep->runLocalUpdates(resources);
454 
455         uint64_t startnumber = current.number;
456         if(startnumber == std::numeric_limits<uint64_t>::max())
457             startnumber = bufferingLogic->getStartSegmentNumber(rep);
458         if(startnumber != std::numeric_limits<uint64_t>::max())
459             return rep->getMinAheadTime(startnumber);
460     }
461     return 0;
462 }
463 
notifyBufferingState(bool enabled) const464 void SegmentTracker::notifyBufferingState(bool enabled) const
465 {
466     notify(SegmentTrackerEvent(adaptationSet->getID(), enabled));
467 }
468 
notifyBufferingLevel(mtime_t min,mtime_t current,mtime_t target) const469 void SegmentTracker::notifyBufferingLevel(mtime_t min, mtime_t current, mtime_t target) const
470 {
471     notify(SegmentTrackerEvent(adaptationSet->getID(), min, current, target));
472 }
473 
registerListener(SegmentTrackerListenerInterface * listener)474 void SegmentTracker::registerListener(SegmentTrackerListenerInterface *listener)
475 {
476     listeners.push_back(listener);
477 }
478 
bufferingAvailable() const479 bool SegmentTracker::bufferingAvailable() const
480 {
481     if(adaptationSet->getPlaylist()->isLive())
482         return getMinAheadTime() > 0;
483     return true;
484 }
485 
updateSelected()486 void SegmentTracker::updateSelected()
487 {
488     if(current.rep && current.rep->needsUpdate(next.number))
489     {
490         bool b_updated = current.rep->runLocalUpdates(resources);
491         current.rep->scheduleNextUpdate(current.number, b_updated);
492     }
493 }
494 
notify(const SegmentTrackerEvent & event) const495 void SegmentTracker::notify(const SegmentTrackerEvent &event) const
496 {
497     std::list<SegmentTrackerListenerInterface *>::const_iterator it;
498     for(it=listeners.begin();it != listeners.end(); ++it)
499         (*it)->trackerEvent(event);
500 }
501