packetizer: hxxx: fix DirectTV extraction
[vlc.git] / modules / demux / adaptive / SegmentTracker.cpp
blobd5b849375edc96816c3f514966cc5f26b45f3f11
1 /*
2 * SegmentTracker.cpp
3 *****************************************************************************
4 * Copyright (C) 2014 - VideoLAN and VLC authors
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.
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.
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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
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"
33 using namespace adaptive;
34 using namespace adaptive::logic;
35 using namespace adaptive::playlist;
37 SegmentTrackerEvent::SegmentTrackerEvent(SegmentChunk *s)
39 type = DISCONTINUITY;
40 u.discontinuity.sc = s;
43 SegmentTrackerEvent::SegmentTrackerEvent(BaseRepresentation *prev, BaseRepresentation *next)
45 type = SWITCHING;
46 u.switching.prev = prev;
47 u.switching.next = next;
50 SegmentTrackerEvent::SegmentTrackerEvent(const StreamFormat *fmt)
52 type = FORMATCHANGE;
53 u.format.f = fmt;
56 SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, bool enabled)
58 type = BUFFERING_STATE;
59 u.buffering.enabled = enabled;
60 u.buffering.id = &id;
63 SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t min, mtime_t current, mtime_t target)
65 type = BUFFERING_LEVEL_CHANGE;
66 u.buffering_level.minimum = min;
67 u.buffering_level.current = current;
68 u.buffering_level.target = target;
69 u.buffering.id = &id;
72 SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t duration)
74 type = SEGMENT_CHANGE;
75 u.segment.duration = duration;
76 u.segment.id = &id;
79 SegmentTracker::SegmentTracker(AbstractAdaptationLogic *logic_, BaseAdaptationSet *adaptSet)
81 first = true;
82 curNumber = next = 0;
83 initializing = true;
84 index_sent = false;
85 init_sent = false;
86 curRepresentation = NULL;
87 setAdaptationLogic(logic_);
88 adaptationSet = adaptSet;
89 format = StreamFormat::UNSUPPORTED;
92 SegmentTracker::~SegmentTracker()
94 reset();
97 void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic *logic_)
99 logic = logic_;
100 registerListener(logic);
103 StreamFormat SegmentTracker::getCurrentFormat() const
105 BaseRepresentation *rep = curRepresentation;
106 if(!rep)
107 rep = logic->getNextRepresentation(adaptationSet, NULL);
108 if(rep)
110 /* Ensure ephemere content is updated/loaded */
111 if(rep->needsUpdate())
112 (void) rep->runLocalUpdates(0, curNumber, false);
113 return rep->getStreamFormat();
115 return StreamFormat();
118 bool SegmentTracker::segmentsListReady() const
120 BaseRepresentation *rep = curRepresentation;
121 if(!rep)
122 rep = logic->getNextRepresentation(adaptationSet, NULL);
123 if(rep && rep->getPlaylist()->isLive())
124 return rep->getMinAheadTime(curNumber) > 0;
125 return true;
128 void SegmentTracker::reset()
130 notify(SegmentTrackerEvent(curRepresentation, NULL));
131 curRepresentation = NULL;
132 init_sent = false;
133 index_sent = false;
134 initializing = true;
135 format = StreamFormat::UNSUPPORTED;
138 SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed,
139 AbstractConnectionManager *connManager)
141 BaseRepresentation *rep = NULL, *prevRep = NULL;
142 ISegment *segment;
144 if(!adaptationSet)
145 return NULL;
147 /* Ensure we don't keep chaining init/index without data */
148 if( initializing )
150 if( curRepresentation )
151 switch_allowed = false;
152 else
153 switch_allowed = true;
156 if( !switch_allowed ||
157 (curRepresentation && curRepresentation->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE) )
158 rep = curRepresentation;
159 else
160 rep = logic->getNextRepresentation(adaptationSet, curRepresentation);
162 if ( rep == NULL )
163 return NULL;
166 if(rep != curRepresentation)
168 notify(SegmentTrackerEvent(curRepresentation, rep));
169 prevRep = curRepresentation;
170 curRepresentation = rep;
171 init_sent = false;
172 index_sent = false;
173 initializing = true;
176 bool b_updated = false;
177 /* Ensure ephemere content is updated/loaded */
178 if(rep->needsUpdate())
179 b_updated = rep->runLocalUpdates(getPlaybackTime(), curNumber, false);
181 if(prevRep && !rep->consistentSegmentNumber())
183 /* Convert our segment number */
184 next = rep->translateSegmentNumber(next, prevRep);
186 else if(first && rep->getPlaylist()->isLive())
188 next = rep->getLiveStartSegmentNumber(next);
189 first = false;
192 if(b_updated)
194 if(!rep->consistentSegmentNumber())
195 curRepresentation->pruneBySegmentNumber(curNumber);
196 curRepresentation->scheduleNextUpdate(next);
199 if(rep->getStreamFormat() != format)
201 /* Initial format ? */
202 if(format == StreamFormat(StreamFormat::UNSUPPORTED))
204 format = rep->getStreamFormat();
206 else
208 format = rep->getStreamFormat();
209 notify(SegmentTrackerEvent(&format)); /* Notify new demux format */
210 return NULL; /* Force current demux to end */
214 if(format == StreamFormat(StreamFormat::UNSUPPORTED))
216 return NULL; /* Can't return chunk because no demux will be created */
219 if(!init_sent)
221 init_sent = true;
222 segment = rep->getSegment(BaseRepresentation::INFOTYPE_INIT);
223 if(segment)
224 return segment->toChunk(next, rep, connManager);
227 if(!index_sent)
229 index_sent = true;
230 segment = rep->getSegment(BaseRepresentation::INFOTYPE_INDEX);
231 if(segment)
232 return segment->toChunk(next, rep, connManager);
235 bool b_gap = false;
236 segment = rep->getNextSegment(BaseRepresentation::INFOTYPE_MEDIA, next, &next, &b_gap);
237 if(!segment)
239 reset();
240 return NULL;
243 if(initializing)
245 b_gap = false;
246 /* stop initializing after 1st chunk */
247 initializing = false;
250 SegmentChunk *chunk = segment->toChunk(next, rep, connManager);
252 /* Notify new segment length for stats / logic */
253 if(chunk)
255 const Timescale timescale = rep->inheritTimescale();
256 notify(SegmentTrackerEvent(rep->getAdaptationSet()->getID(),
257 timescale.ToTime(segment->duration.Get())));
260 /* We need to check segment/chunk format changes, as we can't rely on representation's (HLS)*/
261 if(chunk && format != chunk->getStreamFormat())
263 format = chunk->getStreamFormat();
264 notify(SegmentTrackerEvent(&format));
267 /* Handle both implicit and explicit discontinuities */
268 if( (b_gap && next) || (chunk && chunk->discontinuity) )
270 notify(SegmentTrackerEvent(chunk));
273 if(chunk)
275 curNumber = next;
276 next++;
279 return chunk;
282 bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly)
284 uint64_t segnumber;
285 BaseRepresentation *rep = curRepresentation;
286 if(!rep)
287 rep = logic->getNextRepresentation(adaptationSet, NULL);
289 if(rep &&
290 rep->getSegmentNumberByTime(time, &segnumber))
292 if(!tryonly)
293 setPositionByNumber(segnumber, restarted);
294 return true;
296 return false;
299 void SegmentTracker::setPositionByNumber(uint64_t segnumber, bool restarted)
301 if(restarted)
303 initializing = true;
304 index_sent = false;
305 init_sent = false;
307 curNumber = next = segnumber;
310 mtime_t SegmentTracker::getPlaybackTime() const
312 mtime_t time, duration;
314 BaseRepresentation *rep = curRepresentation;
315 if(!rep)
316 rep = logic->getNextRepresentation(adaptationSet, NULL);
318 if(rep &&
319 rep->getPlaybackTimeDurationBySegmentNumber(next, &time, &duration))
321 return time;
323 return 0;
326 mtime_t SegmentTracker::getMinAheadTime() const
328 BaseRepresentation *rep = curRepresentation;
329 if(!rep)
330 rep = logic->getNextRepresentation(adaptationSet, NULL);
331 if(rep)
332 return rep->getMinAheadTime(curNumber);
333 return 0;
336 void SegmentTracker::notifyBufferingState(bool enabled) const
338 notify(SegmentTrackerEvent(adaptationSet->getID(), enabled));
341 void SegmentTracker::notifyBufferingLevel(mtime_t min, mtime_t current, mtime_t target) const
343 notify(SegmentTrackerEvent(adaptationSet->getID(), min, current, target));
346 void SegmentTracker::registerListener(SegmentTrackerListenerInterface *listener)
348 listeners.push_back(listener);
351 void SegmentTracker::updateSelected()
353 if(curRepresentation && curRepresentation->needsUpdate())
355 curRepresentation->runLocalUpdates(getPlaybackTime(), curNumber, true);
356 curRepresentation->scheduleNextUpdate(curNumber);
360 void SegmentTracker::notify(const SegmentTrackerEvent &event) const
362 std::list<SegmentTrackerListenerInterface *>::const_iterator it;
363 for(it=listeners.begin();it != listeners.end(); ++it)
364 (*it)->trackerEvent(event);