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, ¤t.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