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
);
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 */
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
));
282 bool SegmentTracker::setPositionByTime(mtime_t time
, bool restarted
, bool tryonly
)
285 BaseRepresentation
*rep
= curRepresentation
;
287 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
290 rep
->getSegmentNumberByTime(time
, &segnumber
))
293 setPositionByNumber(segnumber
, restarted
);
299 void SegmentTracker::setPositionByNumber(uint64_t segnumber
, bool restarted
)
307 curNumber
= next
= segnumber
;
310 mtime_t
SegmentTracker::getPlaybackTime() const
312 mtime_t time
, duration
;
314 BaseRepresentation
*rep
= curRepresentation
;
316 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
319 rep
->getPlaybackTimeDurationBySegmentNumber(next
, &time
, &duration
))
326 mtime_t
SegmentTracker::getMinAheadTime() const
328 BaseRepresentation
*rep
= curRepresentation
;
330 rep
= logic
->getNextRepresentation(adaptationSet
, NULL
);
332 return rep
->getMinAheadTime(curNumber
);
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
);