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 *****************************************************************************/
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
)
40 u
.discontinuity
.sc
= s
;
43 SegmentTrackerEvent::SegmentTrackerEvent(BaseRepresentation
*prev
, BaseRepresentation
*next
)
46 u
.switching
.prev
= prev
;
47 u
.switching
.next
= next
;
50 SegmentTrackerEvent::SegmentTrackerEvent(const StreamFormat
*fmt
)
56 SegmentTrackerEvent::SegmentTrackerEvent(const ID
&id
, bool enabled
)
58 type
= BUFFERING_STATE
;
59 u
.buffering
.enabled
= enabled
;
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
;
72 SegmentTrackerEvent::SegmentTrackerEvent(const ID
&id
, mtime_t duration
)
74 type
= SEGMENT_CHANGE
;
75 u
.segment
.duration
= duration
;
79 SegmentTracker::SegmentTracker(AbstractAdaptationLogic
*logic_
, BaseAdaptationSet
*adaptSet
)
86 curRepresentation
= NULL
;
87 setAdaptationLogic(logic_
);
88 adaptationSet
= adaptSet
;
89 format
= StreamFormat::UNSUPPORTED
;
92 SegmentTracker::~SegmentTracker()
97 void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic
*logic_
)
100 registerListener(logic
);
103 StreamFormat
SegmentTracker::getCurrentFormat() const
105 BaseRepresentation
*rep
= curRepresentation
;
107 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
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
;
122 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
123 if(rep
&& rep
->getPlaylist()->isLive())
124 return rep
->getMinAheadTime(curNumber
) > 0;
128 void SegmentTracker::reset()
130 notify(SegmentTrackerEvent(curRepresentation
, NULL
));
131 curRepresentation
= NULL
;
135 format
= StreamFormat::UNSUPPORTED
;
138 SegmentChunk
* SegmentTracker::getNextChunk(bool switch_allowed
,
139 AbstractConnectionManager
*connManager
)
141 BaseRepresentation
*rep
= NULL
, *prevRep
= NULL
;
147 /* Ensure we don't keep chaining init/index without data */
150 if( curRepresentation
)
151 switch_allowed
= false;
153 switch_allowed
= true;
156 if( !switch_allowed
||
157 (curRepresentation
&& curRepresentation
->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE
) )
158 rep
= curRepresentation
;
160 rep
= logic
->getNextRepresentation(adaptationSet
, curRepresentation
);
166 if(rep
!= curRepresentation
)
168 notify(SegmentTrackerEvent(curRepresentation
, rep
));
169 prevRep
= curRepresentation
;
170 curRepresentation
= rep
;
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
);
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();
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 */
222 segment
= rep
->getSegment(BaseRepresentation::INFOTYPE_INIT
);
224 return segment
->toChunk(next
, rep
, connManager
);
230 segment
= rep
->getSegment(BaseRepresentation::INFOTYPE_INDEX
);
232 return segment
->toChunk(next
, rep
, connManager
);
236 segment
= rep
->getNextSegment(BaseRepresentation::INFOTYPE_MEDIA
, next
, &next
, &b_gap
);
245 /* stop initializing after 1st chunk */
246 initializing
= false;
249 SegmentChunk
*chunk
= segment
->toChunk(next
, rep
, connManager
);
251 /* Notify new segment length for stats / logic */
254 const Timescale timescale
= rep
->inheritTimescale();
255 notify(SegmentTrackerEvent(rep
->getAdaptationSet()->getID(),
256 timescale
.ToTime(segment
->duration
.Get())));
259 /* We need to check segment/chunk format changes, as we can't rely on representation's (HLS)*/
260 if(chunk
&& format
!= chunk
->getStreamFormat())
262 format
= chunk
->getStreamFormat();
263 notify(SegmentTrackerEvent(&format
));
266 /* Handle both implicit and explicit discontinuities */
267 if( (b_gap
&& next
) || (chunk
&& chunk
->discontinuity
) )
269 notify(SegmentTrackerEvent(chunk
));
281 bool SegmentTracker::setPositionByTime(mtime_t time
, bool restarted
, bool tryonly
)
284 BaseRepresentation
*rep
= curRepresentation
;
286 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
289 rep
->getSegmentNumberByTime(time
, &segnumber
))
292 setPositionByNumber(segnumber
, restarted
);
298 void SegmentTracker::setPositionByNumber(uint64_t segnumber
, bool restarted
)
306 curNumber
= next
= segnumber
;
309 mtime_t
SegmentTracker::getPlaybackTime() const
311 mtime_t time
, duration
;
313 BaseRepresentation
*rep
= curRepresentation
;
315 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
318 rep
->getPlaybackTimeDurationBySegmentNumber(next
, &time
, &duration
))
325 mtime_t
SegmentTracker::getMinAheadTime() const
327 BaseRepresentation
*rep
= curRepresentation
;
329 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
331 return rep
->getMinAheadTime(curNumber
);
335 void SegmentTracker::notifyBufferingState(bool enabled
) const
337 notify(SegmentTrackerEvent(adaptationSet
->getID(), enabled
));
340 void SegmentTracker::notifyBufferingLevel(mtime_t min
, mtime_t current
, mtime_t target
) const
342 notify(SegmentTrackerEvent(adaptationSet
->getID(), min
, current
, target
));
345 void SegmentTracker::registerListener(SegmentTrackerListenerInterface
*listener
)
347 listeners
.push_back(listener
);
350 void SegmentTracker::updateSelected()
352 if(curRepresentation
&& curRepresentation
->needsUpdate())
354 curRepresentation
->runLocalUpdates(getPlaybackTime(), curNumber
, true);
355 curRepresentation
->scheduleNextUpdate(curNumber
);
359 void SegmentTracker::notify(const SegmentTrackerEvent
&event
) const
361 std::list
<SegmentTrackerListenerInterface
*>::const_iterator it
;
362 for(it
=listeners
.begin();it
!= listeners
.end(); ++it
)
363 (*it
)->trackerEvent(event
);