1 /*
2  * SegmentInformation.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 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include "SegmentInformation.hpp"
25 
26 #include "Segment.h"
27 #include "SegmentBase.h"
28 #include "SegmentList.h"
29 #include "SegmentTemplate.h"
30 #include "SegmentTimeline.h"
31 #include "AbstractPlaylist.hpp"
32 #include "BaseRepresentation.h"
33 #include "../encryption/CommonEncryption.hpp"
34 
35 #include <algorithm>
36 #include <cassert>
37 #include <limits>
38 
39 using namespace adaptive::playlist;
40 
SegmentInformation(SegmentInformation * parent_)41 SegmentInformation::SegmentInformation(SegmentInformation *parent_) :
42     ICanonicalUrl( parent_ ),
43     TimescaleAble( parent_ )
44 {
45     parent = parent_;
46     init();
47 }
48 
SegmentInformation(AbstractPlaylist * parent_)49 SegmentInformation::SegmentInformation(AbstractPlaylist * parent_) :
50     ICanonicalUrl(parent_),
51     TimescaleAble()
52 {
53     parent = NULL;
54     init();
55 }
56 
init()57 void SegmentInformation::init()
58 {
59     baseUrl.Set(NULL);
60     segmentBase = NULL;
61     segmentList = NULL;
62     mediaSegmentTemplate = NULL;
63 }
64 
~SegmentInformation()65 SegmentInformation::~SegmentInformation()
66 {
67     delete segmentBase;
68     delete segmentList;
69     delete mediaSegmentTemplate;
70     delete baseUrl.Get();
71 }
72 
getPlaylist() const73 AbstractPlaylist * SegmentInformation::getPlaylist() const
74 {
75     if(parent)
76         return parent->getPlaylist();
77     else
78         return NULL;
79 }
80 
getSegments(SegmentInfoType type,std::vector<ISegment * > & retSegments) const81 std::size_t SegmentInformation::getSegments(SegmentInfoType type, std::vector<ISegment *> &retSegments) const
82 {
83     switch (type)
84     {
85         case INFOTYPE_INIT:
86         {
87             /* init segments are always single segment */
88             if( segmentBase && segmentBase->initialisationSegment.Get() )
89             {
90                 retSegments.push_back( segmentBase->initialisationSegment.Get() );
91             }
92             else if( segmentList && segmentList->initialisationSegment.Get() )
93             {
94                 retSegments.push_back( segmentList->initialisationSegment.Get() );
95             }
96             else if( mediaSegmentTemplate && mediaSegmentTemplate->initialisationSegment.Get() )
97             {
98                 retSegments.push_back( mediaSegmentTemplate->initialisationSegment.Get() );
99             }
100         }
101         break;
102 
103         case INFOTYPE_MEDIA:
104         {
105             if( mediaSegmentTemplate )
106             {
107                 retSegments.push_back( mediaSegmentTemplate );
108             }
109             else if ( segmentList && !segmentList->getSegments().empty() )
110             {
111                 std::vector<ISegment *>::const_iterator it;
112                 for(it=segmentList->getSegments().begin();
113                     it!=segmentList->getSegments().end(); ++it)
114                 {
115                     std::vector<ISegment *> list = (*it)->subSegments();
116                     retSegments.insert( retSegments.end(), list.begin(), list.end() );
117                 }
118             }
119             else if( segmentBase )
120             {
121                 std::vector<ISegment *> list = segmentBase->subSegments();
122                 retSegments.insert( retSegments.end(), list.begin(), list.end() );
123             }
124         }
125         break;
126 
127         case INFOTYPE_INDEX:
128         {
129             /* index segments are always single segment */
130             if( segmentBase && segmentBase->indexSegment.Get() )
131             {
132                 retSegments.push_back( segmentBase->indexSegment.Get() );
133             }
134             else if( segmentList && segmentList->indexSegment.Get() )
135             {
136                 retSegments.push_back( segmentList->indexSegment.Get() );
137             }
138             // templated index ?
139         }
140 
141         default:
142         break;
143     }
144 
145     if( retSegments.empty() && parent )
146     {
147         return parent->getSegments( type, retSegments );
148     }
149     else
150     {
151         return retSegments.size();
152     }
153 }
154 
getAllSegments(std::vector<ISegment * > & retSegments) const155 std::size_t SegmentInformation::getAllSegments(std::vector<ISegment *> &retSegments) const
156 {
157     for(int i=0; i<InfoTypeCount; i++)
158     {
159         std::vector<ISegment *> segs;
160         if( getSegments(static_cast<SegmentInfoType>(i), segs) )
161             retSegments.insert( retSegments.end(), segs.begin(), segs.end() );
162     }
163     return retSegments.size();
164 }
165 
getMediaPlaybackRange(mtime_t * rangeBegin,mtime_t * rangeEnd,mtime_t * rangeLength) const166 bool SegmentInformation::getMediaPlaybackRange(mtime_t *rangeBegin,
167                                                mtime_t *rangeEnd,
168                                                mtime_t *rangeLength) const
169 {
170     if( mediaSegmentTemplate )
171     {
172         const Timescale timescale = mediaSegmentTemplate->inheritTimescale();
173         const SegmentTimeline *timeline = mediaSegmentTemplate->inheritSegmentTimeline();
174         if( timeline )
175         {
176             stime_t startTime, endTime, duration;
177             if(!timeline->getScaledPlaybackTimeDurationBySegmentNumber(timeline->minElementNumber(),
178                                                                        &startTime, &duration) ||
179                !timeline->getScaledPlaybackTimeDurationBySegmentNumber(timeline->maxElementNumber(),
180                                                                        &endTime, &duration))
181                 return false;
182 
183             *rangeBegin = timescale.ToTime(startTime);
184             *rangeEnd = timescale.ToTime(endTime+duration);
185             *rangeLength = timescale.ToTime(timeline->getTotalLength());
186             return true;
187         }
188         /* Else compute, current time and timeshiftdepth based */
189         else if( mediaSegmentTemplate->duration.Get() )
190         {
191             *rangeEnd = 0;
192             *rangeBegin = -1 * getPlaylist()->timeShiftBufferDepth.Get();
193             *rangeLength = getPlaylist()->timeShiftBufferDepth.Get();
194             return true;
195         }
196     }
197     else if ( segmentList && !segmentList->getSegments().empty() )
198     {
199         const Timescale timescale = segmentList->inheritTimescale();
200         const std::vector<ISegment *> list = segmentList->getSegments();
201 
202         const ISegment *back = list.back();
203         const stime_t startTime = list.front()->startTime.Get();
204         const stime_t endTime = back->startTime.Get() + back->duration.Get();
205         *rangeBegin = timescale.ToTime(startTime);
206         *rangeEnd = timescale.ToTime(endTime);
207         *rangeLength = timescale.ToTime(segmentList->getTotalLength());
208         return true;
209     }
210     else if( segmentBase )
211     {
212         const std::vector<ISegment *> list = segmentBase->subSegments();
213         if(list.empty())
214             return false;
215 
216         const Timescale timescale = inheritTimescale();
217         const ISegment *back = list.back();
218         const stime_t startTime = list.front()->startTime.Get();
219         const stime_t endTime = back->startTime.Get() + back->duration.Get();
220         *rangeBegin = timescale.ToTime(startTime);
221         *rangeEnd = timescale.ToTime(endTime);
222         *rangeLength = 0;
223         return true;
224     }
225 
226     if(parent)
227         return parent->getMediaPlaybackRange(rangeBegin, rangeEnd, rangeLength);
228     else
229         return false;
230 }
231 
232 /* Returns wanted segment, or next in sequence if not found */
getNextSegment(SegmentInfoType type,uint64_t i_pos,uint64_t * pi_newpos,bool * pb_gap) const233 ISegment * SegmentInformation::getNextSegment(SegmentInfoType type, uint64_t i_pos,
234                                               uint64_t *pi_newpos, bool *pb_gap) const
235 {
236     *pb_gap = false;
237     *pi_newpos = i_pos;
238     if( type != INFOTYPE_MEDIA )
239         return NULL;
240 
241     std::vector<ISegment *> retSegments;
242     const size_t size = getSegments( type, retSegments );
243     if( size )
244     {
245         std::vector<ISegment *>::const_iterator it;
246         for(it = retSegments.begin(); it != retSegments.end(); ++it)
247         {
248             ISegment *seg = *it;
249             if(seg->isTemplate()) /* we don't care about seq number */
250             {
251                 /* Check if we don't exceed timeline */
252                 MediaSegmentTemplate *templ = dynamic_cast<MediaSegmentTemplate*>(retSegments[0]);
253                 const SegmentTimeline *timeline = (templ) ? templ->inheritSegmentTimeline() : NULL;
254                 if(timeline)
255                 {
256                     *pi_newpos = std::max(timeline->minElementNumber(), i_pos);
257                     if(timeline->maxElementNumber() < i_pos)
258                         return NULL;
259                 }
260                 else
261                 {
262                     /* check template upper bound */
263                     if(!getPlaylist()->isLive())
264                     {
265                         const Timescale timescale = templ->inheritTimescale();
266                         const stime_t segmentduration = templ->inheritDuration();
267                         mtime_t totalduration = getPeriodDuration();
268                         if(totalduration == 0)
269                             totalduration = getPlaylist()->duration.Get();
270                         if(totalduration && segmentduration)
271                         {
272                             uint64_t endnum = templ->inheritStartNumber() +
273                                     (timescale.ToScaled(totalduration) + segmentduration - 1) / segmentduration;
274                             if(i_pos >= endnum)
275                             {
276                                 *pi_newpos = i_pos;
277                                 return NULL;
278                             }
279                         }
280                     }
281                     *pi_newpos = i_pos;
282                     /* start number */
283                     *pi_newpos = std::max(templ->inheritStartNumber(), i_pos);
284                 }
285                 return seg;
286             }
287             else if(seg->getSequenceNumber() >= i_pos)
288             {
289                 *pi_newpos = seg->getSequenceNumber();
290                 *pb_gap = (*pi_newpos != i_pos);
291                 return seg;
292             }
293         }
294     }
295 
296     return NULL;
297 }
298 
getSegment(SegmentInfoType type,uint64_t pos) const299 ISegment * SegmentInformation::getSegment(SegmentInfoType type, uint64_t pos) const
300 {
301     std::vector<ISegment *> retSegments;
302     const size_t size = getSegments( type, retSegments );
303     if( size )
304     {
305         if(size == 1 && retSegments[0]->isTemplate())
306         {
307             MediaSegmentTemplate *templ = dynamic_cast<MediaSegmentTemplate*>(retSegments[0]);
308             const SegmentTimeline *tl = templ->inheritSegmentTimeline();
309             if(!templ || tl == NULL || tl->maxElementNumber() > pos)
310                 return templ;
311         }
312         else
313         {
314             std::vector<ISegment *>::const_iterator it;
315             for(it = retSegments.begin(); it != retSegments.end(); ++it)
316             {
317                 ISegment *seg = *it;
318                 if(seg->getSequenceNumber() >= pos)
319                 {
320                     if(seg->getSequenceNumber() == pos)
321                         return seg;
322                     else
323                         return NULL;
324                 }
325             }
326         }
327     }
328 
329     return NULL;
330 }
331 
getSegmentNumberByTime(mtime_t time,uint64_t * ret) const332 bool SegmentInformation::getSegmentNumberByTime(mtime_t time, uint64_t *ret) const
333 {
334     if( mediaSegmentTemplate )
335     {
336         const SegmentTimeline *timeline = mediaSegmentTemplate->inheritSegmentTimeline();
337         if(timeline)
338         {
339             const Timescale timescale = timeline->getTimescale().isValid()
340                                       ? timeline->getTimescale()
341                                       : mediaSegmentTemplate->inheritTimescale();
342             stime_t st = timescale.ToScaled(time);
343             *ret = timeline->getElementNumberByScaledPlaybackTime(st);
344             return true;
345         }
346 
347         const stime_t duration = mediaSegmentTemplate->duration.Get();
348         if( duration )
349         {
350             if( getPlaylist()->isLive() )
351             {
352                 mtime_t now = CLOCK_FREQ * ::time(NULL);
353                 if(getPlaylist()->availabilityStartTime.Get())
354                 {
355                     if(time >= getPlaylist()->availabilityStartTime.Get() && time < now)
356                         *ret = mediaSegmentTemplate->getLiveTemplateNumber(time, true);
357                     else if(now - getPlaylist()->availabilityStartTime.Get() > time)
358                         *ret = mediaSegmentTemplate->getLiveTemplateNumber(time, false);
359                 }
360                 else return false;
361             }
362             else
363             {
364                 const Timescale timescale = mediaSegmentTemplate->inheritTimescale();
365                 *ret = mediaSegmentTemplate->inheritStartNumber();
366                 *ret += timescale.ToScaled(time) / duration;
367             }
368             return true;
369         }
370     }
371     else if ( segmentList && !segmentList->getSegments().empty() )
372     {
373         const Timescale timescale = segmentList->inheritTimescale();
374         time = timescale.ToScaled(time);
375         return segmentList->getSegmentNumberByScaledTime(time, ret);
376     }
377     else if( segmentBase )
378     {
379         const Timescale timescale = inheritTimescale();
380         time = timescale.ToScaled(time);
381         *ret = 0;
382         const std::vector<ISegment *> list = segmentBase->subSegments();
383         return SegmentInfoCommon::getSegmentNumberByScaledTime(list, time, ret);
384     }
385 
386     if(parent)
387         return parent->getSegmentNumberByTime(time, ret);
388     else
389         return false;
390 }
391 
getPlaybackTimeDurationBySegmentNumber(uint64_t number,mtime_t * time,mtime_t * duration) const392 bool SegmentInformation::getPlaybackTimeDurationBySegmentNumber(uint64_t number,
393                                                                 mtime_t *time, mtime_t *duration) const
394 {
395     SegmentList *segList;
396     MediaSegmentTemplate *mediaTemplate;
397 
398     if(number == std::numeric_limits<uint64_t>::max())
399         return false;
400 
401     if( (mediaTemplate = inheritSegmentTemplate()) )
402     {
403         const Timescale timescale = mediaTemplate->inheritTimescale();
404         const SegmentTimeline * timeline = mediaTemplate->inheritSegmentTimeline();
405 
406         stime_t stime, sduration;
407         if(timeline)
408         {
409             if(!timeline->getScaledPlaybackTimeDurationBySegmentNumber(number, &stime, &sduration))
410                 return false;
411         }
412         else
413         {
414             uint64_t startNumber = mediaTemplate->inheritStartNumber();
415             if(number < startNumber)
416                 return false;
417             sduration = mediaTemplate->inheritDuration();
418             stime = (number - startNumber) * sduration;
419         }
420         *time = timescale.ToTime(stime);
421         *duration = timescale.ToTime(sduration);
422         return true;
423     }
424     else if ( (segList = inheritSegmentList()) )
425     {
426         return segList->getPlaybackTimeDurationBySegmentNumber(number, time, duration);
427     }
428     else
429     {
430         const Timescale timescale = inheritTimescale();
431         const ISegment *segment = getSegment(INFOTYPE_MEDIA, number);
432         if( segment )
433         {
434             *time = timescale.ToTime(segment->startTime.Get());
435             *duration = timescale.ToTime(segment->duration.Get());
436             return true;
437         }
438     }
439 
440     if(parent)
441         return parent->getPlaybackTimeDurationBySegmentNumber(number, time, duration);
442 
443     return false;
444 }
445 
getChildByID(const adaptive::ID & id)446 SegmentInformation * SegmentInformation::getChildByID(const adaptive::ID &id)
447 {
448     std::vector<SegmentInformation *>::const_iterator it;
449     for(it=childs.begin(); it!=childs.end(); ++it)
450     {
451         if( (*it)->getID() == id )
452             return *it;
453     }
454     return NULL;
455 }
456 
updateWith(SegmentInformation * updated)457 void SegmentInformation::updateWith(SegmentInformation *updated)
458 {
459     /* Support Segment List for now */
460     if(segmentList && updated->segmentList)
461         segmentList->updateWith(updated->segmentList);
462 
463     if(mediaSegmentTemplate && updated->mediaSegmentTemplate)
464         mediaSegmentTemplate->updateWith(updated->mediaSegmentTemplate);
465 
466     std::vector<SegmentInformation *>::const_iterator it;
467     for(it=childs.begin(); it!=childs.end(); ++it)
468     {
469         SegmentInformation *child = *it;
470         SegmentInformation *updatedChild = updated->getChildByID(child->getID());
471         if(updatedChild)
472             child->updateWith(updatedChild);
473     }
474     /* FIXME: handle difference */
475 }
476 
mergeWithTimeline(SegmentTimeline * updated)477 void SegmentInformation::mergeWithTimeline(SegmentTimeline *updated)
478 {
479     MediaSegmentTemplate *templ = inheritSegmentTemplate();
480     if(templ)
481     {
482         SegmentTimeline *timeline = templ->inheritSegmentTimeline();
483         if(timeline)
484             timeline->updateWith(*updated);
485     }
486 }
487 
pruneByPlaybackTime(mtime_t time)488 void SegmentInformation::pruneByPlaybackTime(mtime_t time)
489 {
490     if(segmentList)
491         segmentList->pruneByPlaybackTime(time);
492 
493     if(mediaSegmentTemplate)
494         mediaSegmentTemplate->pruneByPlaybackTime(time);
495 
496     std::vector<SegmentInformation *>::const_iterator it;
497     for(it=childs.begin(); it!=childs.end(); ++it)
498         (*it)->pruneByPlaybackTime(time);
499 }
500 
pruneBySegmentNumber(uint64_t num)501 void SegmentInformation::pruneBySegmentNumber(uint64_t num)
502 {
503     assert(dynamic_cast<BaseRepresentation *>(this));
504 
505     if(segmentList)
506         segmentList->pruneBySegmentNumber(num);
507 
508     if(mediaSegmentTemplate)
509          mediaSegmentTemplate->pruneBySequenceNumber(num);
510 }
511 
translateSegmentNumber(uint64_t num,const SegmentInformation * from) const512 uint64_t SegmentInformation::translateSegmentNumber(uint64_t num, const SegmentInformation *from) const
513 {
514     mtime_t time, duration;
515     if( from->getPlaybackTimeDurationBySegmentNumber(num, &time, &duration) )
516         getSegmentNumberByTime(time, &num);
517     return num;
518 }
519 
intheritEncryption() const520 const CommonEncryption & SegmentInformation::intheritEncryption() const
521 {
522     if(parent && commonEncryption.method == CommonEncryption::Method::NONE)
523         return parent->intheritEncryption();
524     return commonEncryption;
525 }
526 
setEncryption(const CommonEncryption & enc)527 void SegmentInformation::setEncryption(const CommonEncryption &enc)
528 {
529     commonEncryption = enc;
530 }
531 
getPeriodStart() const532 mtime_t SegmentInformation::getPeriodStart() const
533 {
534     if(parent)
535         return parent->getPeriodStart();
536     else
537         return 0;
538 }
539 
getPeriodDuration() const540 mtime_t SegmentInformation::getPeriodDuration() const
541 {
542     if(parent)
543         return parent->getPeriodDuration();
544     else
545         return 0;
546 }
547 
updateSegmentList(SegmentList * list,bool restamp)548 void SegmentInformation::updateSegmentList(SegmentList *list, bool restamp)
549 {
550     if(segmentList && restamp)
551     {
552         segmentList->updateWith(list, restamp);
553         delete list;
554     }
555     else
556     {
557         delete segmentList;
558         segmentList = list;
559     }
560 }
561 
setSegmentBase(SegmentBase * base)562 void SegmentInformation::setSegmentBase(SegmentBase *base)
563 {
564     if(segmentBase)
565         delete segmentBase;
566     segmentBase = base;
567 }
568 
setSegmentTemplate(MediaSegmentTemplate * templ)569 void SegmentInformation::setSegmentTemplate(MediaSegmentTemplate *templ)
570 {
571     if(mediaSegmentTemplate)
572     {
573         mediaSegmentTemplate->updateWith(templ);
574         delete templ;
575     }
576     else
577         mediaSegmentTemplate = templ;
578 }
579 
insertIntoSegment(std::vector<ISegment * > & seglist,size_t start,size_t end,stime_t time,stime_t duration)580 static void insertIntoSegment(std::vector<ISegment *> &seglist, size_t start,
581                               size_t end, stime_t time, stime_t duration)
582 {
583     std::vector<ISegment *>::iterator segIt;
584     for(segIt = seglist.begin(); segIt < seglist.end(); ++segIt)
585     {
586         ISegment *segment = *segIt;
587         if(segment->getClassId() == Segment::CLASSID_SEGMENT &&
588            (end == 0 || segment->contains(end)))
589         {
590             SubSegment *subsegment = new SubSegment(segment, start, (end != 0) ? end : 0);
591             subsegment->startTime.Set(time);
592             subsegment->duration.Set(duration);
593             segment->addSubSegment(subsegment);
594             break;
595         }
596     }
597 }
598 
SplitUsingIndex(std::vector<SplitPoint> & splitlist)599 void SegmentInformation::SplitUsingIndex(std::vector<SplitPoint> &splitlist)
600 {
601     std::vector<ISegment *> seglist;
602     getSegments(INFOTYPE_MEDIA, seglist);
603     size_t prevstart = 0;
604     stime_t prevtime = 0;
605 
606     SplitPoint split = {0,0,0};
607     std::vector<SplitPoint>::const_iterator splitIt;
608     for(splitIt = splitlist.begin(); splitIt < splitlist.end(); ++splitIt)
609     {
610         split = *splitIt;
611         if(splitIt != splitlist.begin())
612         {
613             /* do previous splitpoint */
614             insertIntoSegment(seglist, prevstart, split.offset - 1, prevtime, split.duration);
615         }
616         prevstart = split.offset;
617         prevtime = split.time;
618     }
619 
620     if(splitlist.size() == 1)
621     {
622         insertIntoSegment(seglist, prevstart, 0, prevtime, split.duration);
623     }
624     else if(splitlist.size() > 1)
625     {
626         insertIntoSegment(seglist, prevstart, split.offset - 1, prevtime, split.duration);
627     }
628 }
629 
getUrlSegment() const630 Url SegmentInformation::getUrlSegment() const
631 {
632     if(baseUrl.Get() && baseUrl.Get()->hasScheme())
633     {
634         return *(baseUrl.Get());
635     }
636     else
637     {
638         Url ret = getParentUrlSegment();
639         if (baseUrl.Get())
640             ret.append(*(baseUrl.Get()));
641         return ret;
642     }
643 }
644 
inheritSegmentBase() const645 SegmentBase * SegmentInformation::inheritSegmentBase() const
646 {
647     if(segmentBase)
648         return segmentBase;
649     else if (parent)
650         return parent->inheritSegmentBase();
651     else
652         return NULL;
653 }
654 
inheritSegmentList() const655 SegmentList * SegmentInformation::inheritSegmentList() const
656 {
657     if(segmentList)
658         return segmentList;
659     else if (parent)
660         return parent->inheritSegmentList();
661     else
662         return NULL;
663 }
664 
inheritSegmentTemplate() const665 MediaSegmentTemplate * SegmentInformation::inheritSegmentTemplate() const
666 {
667     if(mediaSegmentTemplate)
668         return mediaSegmentTemplate;
669     else if (parent)
670         return parent->inheritSegmentTemplate();
671     else
672         return NULL;
673 }
674 
setAvailabilityTimeOffset(mtime_t t)675 void SegmentInformation::setAvailabilityTimeOffset(mtime_t t)
676 {
677     availabilityTimeOffset = t;
678 }
679 
setAvailabilityTimeComplete(bool b)680 void SegmentInformation::setAvailabilityTimeComplete(bool b)
681 {
682     availabilityTimeComplete = b;
683 }
684 
inheritAvailabilityTimeOffset() const685 mtime_t SegmentInformation::inheritAvailabilityTimeOffset() const
686 {
687     for(const SegmentInformation *p = this; p; p = p->parent)
688     {
689         if(availabilityTimeOffset.isSet())
690             return availabilityTimeOffset.value();
691     }
692     return getPlaylist()->getAvailabilityTimeOffset();
693 }
694 
inheritAvailabilityTimeComplete() const695 bool SegmentInformation::inheritAvailabilityTimeComplete() const
696 {
697     for(const SegmentInformation *p = this; p; p = p->parent)
698     {
699         if(availabilityTimeComplete.isSet())
700             return availabilityTimeComplete.value();
701     }
702     return getPlaylist()->getAvailabilityTimeComplete();
703 }
704