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