3 *****************************************************************************
4 * Copyright © 2010 - 2011 Klagenfurt University
5 * 2015 VideoLAN and VLC Authors
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
26 #include "PlaylistManager.h"
27 #include "SegmentTracker.hpp"
28 #include "playlist/AbstractPlaylist.hpp"
29 #include "playlist/BasePeriod.h"
30 #include "playlist/BaseAdaptationSet.h"
31 #include "playlist/BaseRepresentation.h"
32 #include "http/HTTPConnectionManager.h"
33 #include "logic/AlwaysBestAdaptationLogic.h"
34 #include "logic/RateBasedAdaptationLogic.h"
35 #include "logic/AlwaysLowestAdaptationLogic.hpp"
36 #include "logic/PredictiveAdaptationLogic.hpp"
37 #include "logic/NearOptimalAdaptationLogic.hpp"
38 #include "tools/Debug.hpp"
39 #include <vlc_stream.h>
40 #include <vlc_demux.h>
41 #include <vlc_threads.h>
46 using namespace adaptive::http
;
47 using namespace adaptive::logic
;
48 using namespace adaptive
;
50 PlaylistManager::PlaylistManager( demux_t
*p_demux_
,
52 AbstractStreamFactory
*factory
,
53 AbstractAdaptationLogic::LogicType type
) :
58 streamFactory ( factory
),
61 currentPeriod
= playlist
->getFirstPeriod();
65 nextPlaylistupdate
= 0;
66 demux
.i_nzpcr
= VLC_TS_INVALID
;
67 demux
.i_firstpcr
= VLC_TS_INVALID
;
68 vlc_mutex_init(&demux
.lock
);
69 vlc_cond_init(&demux
.cond
);
70 vlc_mutex_init(&lock
);
71 vlc_cond_init(&waitcond
);
72 vlc_mutex_init(&cached
.lock
);
73 cached
.b_live
= false;
75 cached
.f_position
= 0.0;
76 cached
.i_time
= VLC_TS_INVALID
;
79 PlaylistManager::~PlaylistManager ()
86 vlc_cond_destroy(&waitcond
);
87 vlc_mutex_destroy(&lock
);
88 vlc_mutex_destroy(&demux
.lock
);
89 vlc_cond_destroy(&demux
.cond
);
90 vlc_mutex_destroy(&cached
.lock
);
93 void PlaylistManager::unsetPeriod()
95 std::vector
<AbstractStream
*>::iterator it
;
96 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
101 bool PlaylistManager::setupPeriod()
106 if(!logic
&& !(logic
= createLogic(logicType
, conManager
)))
109 std::vector
<BaseAdaptationSet
*> sets
= currentPeriod
->getAdaptationSets();
110 std::vector
<BaseAdaptationSet
*>::iterator it
;
111 for(it
=sets
.begin();it
!=sets
.end();++it
)
113 BaseAdaptationSet
*set
= *it
;
114 if(set
&& streamFactory
)
116 SegmentTracker
*tracker
= new (std::nothrow
) SegmentTracker(logic
, set
);
120 AbstractStream
*st
= streamFactory
->create(p_demux
, set
->getStreamFormat(),
121 tracker
, conManager
);
128 streams
.push_back(st
);
130 /* Generate stream description */
131 std::list
<std::string
> languages
;
132 if(!set
->getLang().empty())
134 languages
= set
->getLang();
136 else if(!set
->getRepresentations().empty())
138 languages
= set
->getRepresentations().front()->getLang();
141 if(!languages
.empty())
142 st
->setLanguage(languages
.front());
144 if(!set
->description
.Get().empty())
145 st
->setDescription(set
->description
.Get());
151 bool PlaylistManager::start()
153 if(!conManager
&& !(conManager
= new (std::nothrow
) HTTPConnectionManager(VLC_OBJECT(p_demux
->s
))))
159 playlist
->playbackStart
.Set(time(NULL
));
160 nextPlaylistupdate
= playlist
->playbackStart
.Get();
162 updateControlsContentType();
163 updateControlsPosition();
165 b_thread
= !vlc_clone(&thread
, managerThread
,
166 static_cast<void *>(this), VLC_THREAD_PRIORITY_INPUT
);
170 setBufferingRunState(true);
175 void PlaylistManager::stop()
180 vlc_join(thread
, NULL
);
185 struct PrioritizedAbstractStream
187 AbstractStream::buffering_status status
;
188 mtime_t demuxed_amount
;
192 static bool streamCompare(PrioritizedAbstractStream a
, PrioritizedAbstractStream b
)
194 if( a
.status
>= b
.status
) /* Highest prio is higer value in enum */
196 if ( a
.status
== b
.status
) /* Highest prio is lowest buffering */
197 return a
.demuxed_amount
< b
.demuxed_amount
;
204 AbstractStream::buffering_status
PlaylistManager::bufferize(mtime_t i_nzdeadline
,
205 unsigned i_min_buffering
, unsigned i_extra_buffering
)
207 AbstractStream::buffering_status i_return
= AbstractStream::buffering_end
;
209 /* First reorder by status >> buffering level */
210 std::vector
<PrioritizedAbstractStream
> prioritized_streams(streams
.size());
211 std::vector
<PrioritizedAbstractStream
>::iterator it
= prioritized_streams
.begin();
212 std::vector
<AbstractStream
*>::iterator sit
= streams
.begin();
213 for( ; sit
!=streams
.end(); ++sit
)
215 PrioritizedAbstractStream
&p
= *it
;
217 p
.status
= p
.st
->getLastBufferStatus();
218 p
.demuxed_amount
= p
.st
->getDemuxedAmount();
221 std::sort(prioritized_streams
.begin(), prioritized_streams
.end(), streamCompare
);
223 for(it
=prioritized_streams
.begin(); it
!=prioritized_streams
.end(); ++it
)
225 AbstractStream
*st
= (*it
).st
;
227 if (st
->isDisabled() &&
228 (!st
->isSelected() || !st
->canActivate() || !reactivateStream(st
)))
231 AbstractStream::buffering_status i_ret
= st
->bufferize(i_nzdeadline
, i_min_buffering
, i_extra_buffering
);
232 if(i_return
!= AbstractStream::buffering_ongoing
) /* Buffering streams need to keep going */
238 /* Bail out, will start again (high prio could be same starving stream) */
239 if( i_return
== AbstractStream::buffering_lessthanmin
)
243 vlc_mutex_lock(&demux
.lock
);
244 if(demux
.i_nzpcr
== VLC_TS_INVALID
&&
245 i_return
!= AbstractStream::buffering_lessthanmin
/* prevents starting before buffering is reached */ )
247 demux
.i_nzpcr
= getFirstDTS();
249 vlc_mutex_unlock(&demux
.lock
);
254 AbstractStream::status
PlaylistManager::dequeue(mtime_t i_floor
, mtime_t
*pi_nzbarrier
)
256 AbstractStream::status i_return
= AbstractStream::status_eof
;
258 const mtime_t i_nzdeadline
= *pi_nzbarrier
;
260 std::vector
<AbstractStream
*>::iterator it
;
261 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
263 AbstractStream
*st
= *it
;
266 AbstractStream::status i_ret
= st
->dequeue(i_nzdeadline
, &i_pcr
);
267 if( i_ret
> i_return
)
270 if( i_pcr
> i_floor
)
271 *pi_nzbarrier
= std::min( *pi_nzbarrier
, i_pcr
- VLC_TS_0
);
277 void PlaylistManager::drain()
281 bool b_drained
= true;
282 std::vector
<AbstractStream
*>::iterator it
;
283 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
285 AbstractStream
*st
= *it
;
287 if (st
->isDisabled())
290 b_drained
&= st
->decodersDrained();
296 msleep(20*1000); /* ugly, but we have no way to get feedback */
298 es_out_Control(p_demux
->out
, ES_OUT_RESET_PCR
);
301 mtime_t
PlaylistManager::getPCR() const
303 mtime_t minpcr
= VLC_TS_INVALID
;
304 std::vector
<AbstractStream
*>::const_iterator it
;
305 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
307 const mtime_t pcr
= (*it
)->getPCR();
308 if(minpcr
== VLC_TS_INVALID
)
310 else if(pcr
> VLC_TS_INVALID
)
311 minpcr
= std::min(minpcr
, pcr
);
316 mtime_t
PlaylistManager::getFirstDTS() const
318 mtime_t mindts
= VLC_TS_INVALID
;
319 std::vector
<AbstractStream
*>::const_iterator it
;
320 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
322 const mtime_t dts
= (*it
)->getFirstDTS();
323 if(mindts
== VLC_TS_INVALID
)
325 else if(dts
> VLC_TS_INVALID
)
326 mindts
= std::min(mindts
, dts
);
331 mtime_t
PlaylistManager::getDuration() const
333 if (playlist
->isLive())
336 return playlist
->duration
.Get();
339 bool PlaylistManager::setPosition(mtime_t time
)
342 for(int real
= 0; real
< 2; real
++)
344 /* Always probe if we can seek first */
345 std::vector
<AbstractStream
*>::iterator it
;
346 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
348 AbstractStream
*st
= *it
;
349 if(!st
->isDisabled())
350 ret
&= st
->setPosition(time
, !real
);
358 bool PlaylistManager::needsUpdate() const
360 return playlist
->isLive() && (failedupdates
< 3);
363 void PlaylistManager::scheduleNextUpdate()
368 bool PlaylistManager::updatePlaylist()
370 std::vector
<AbstractStream
*>::const_iterator it
;
371 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
374 updateControlsContentType();
375 updateControlsPosition();
379 mtime_t
PlaylistManager::getFirstPlaybackTime() const
384 mtime_t
PlaylistManager::getCurrentPlaybackTime() const
386 return demux
.i_nzpcr
;
389 void PlaylistManager::pruneLiveStream()
391 mtime_t minValidPos
= 0;
392 std::vector
<AbstractStream
*>::const_iterator it
;
393 for(it
=streams
.begin(); it
!=streams
.end(); it
++)
395 const AbstractStream
*st
= *it
;
396 if(st
->isDisabled() || !st
->isSelected())
398 const mtime_t t
= st
->getPlaybackTime();
399 if(minValidPos
== 0 || t
< minValidPos
)
404 playlist
->pruneByPlaybackTime(minValidPos
);
407 bool PlaylistManager::reactivateStream(AbstractStream
*stream
)
409 return stream
->reactivate(getPCR());
412 #define DEMUX_INCREMENT (CLOCK_FREQ / 20)
413 int PlaylistManager::demux_callback(demux_t
*p_demux
)
415 PlaylistManager
*manager
= reinterpret_cast<PlaylistManager
*>(p_demux
->p_sys
);
416 return manager
->doDemux(DEMUX_INCREMENT
);
419 int PlaylistManager::doDemux(int64_t increment
)
421 vlc_mutex_lock(&demux
.lock
);
422 if(demux
.i_nzpcr
== VLC_TS_INVALID
)
425 std::vector
<AbstractStream
*>::const_iterator it
;
426 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
427 b_dead
&= !(*it
)->canActivate();
429 vlc_cond_timedwait(&demux
.cond
, &demux
.lock
, mdate() + CLOCK_FREQ
/ 20);
430 vlc_mutex_unlock(&demux
.lock
);
431 return (b_dead
) ? AbstractStream::status_eof
: AbstractStream::status_buffering
;
434 if(demux
.i_firstpcr
== VLC_TS_INVALID
)
435 demux
.i_firstpcr
= demux
.i_nzpcr
;
437 mtime_t i_nzbarrier
= demux
.i_nzpcr
+ increment
;
438 vlc_mutex_unlock(&demux
.lock
);
440 AbstractStream::status status
= dequeue(demux
.i_nzpcr
, &i_nzbarrier
);
442 updateControlsContentType();
443 updateControlsPosition();
447 case AbstractStream::status_eof
:
449 /* might be end of current period */
452 setBufferingRunState(false);
453 BasePeriod
*nextPeriod
= playlist
->getNextPeriod(currentPeriod
);
455 return VLC_DEMUXER_EOF
;
457 currentPeriod
= nextPeriod
;
459 return VLC_DEMUXER_EOF
;
461 demux
.i_nzpcr
= VLC_TS_INVALID
;
462 demux
.i_firstpcr
= VLC_TS_INVALID
;
463 es_out_Control(p_demux
->out
, ES_OUT_RESET_PCR
);
465 setBufferingRunState(true);
469 case AbstractStream::status_buffering
:
470 vlc_mutex_lock(&demux
.lock
);
471 vlc_cond_timedwait(&demux
.cond
, &demux
.lock
, mdate() + CLOCK_FREQ
/ 20);
472 vlc_mutex_unlock(&demux
.lock
);
474 case AbstractStream::status_discontinuity
:
475 vlc_mutex_lock(&demux
.lock
);
476 demux
.i_nzpcr
= VLC_TS_INVALID
;
477 demux
.i_firstpcr
= VLC_TS_INVALID
;
478 es_out_Control(p_demux
->out
, ES_OUT_RESET_PCR
);
479 vlc_mutex_unlock(&demux
.lock
);
481 case AbstractStream::status_demuxed
:
482 vlc_mutex_lock(&demux
.lock
);
483 if( demux
.i_nzpcr
!= VLC_TS_INVALID
&& i_nzbarrier
!= demux
.i_nzpcr
)
485 demux
.i_nzpcr
= i_nzbarrier
;
486 mtime_t pcr
= VLC_TS_0
+ std::max(INT64_C(0), demux
.i_nzpcr
- CLOCK_FREQ
/ 10);
487 es_out_Control(p_demux
->out
, ES_OUT_SET_GROUP_PCR
, 0, pcr
);
489 vlc_mutex_unlock(&demux
.lock
);
493 return VLC_DEMUXER_SUCCESS
;
496 int PlaylistManager::control_callback(demux_t
*p_demux
, int i_query
, va_list args
)
498 PlaylistManager
*manager
= reinterpret_cast<PlaylistManager
*>(p_demux
->p_sys
);
499 return manager
->doControl(i_query
, args
);
502 int PlaylistManager::doControl(int i_query
, va_list args
)
508 vlc_mutex_locker
locker(&cached
.lock
);
509 *(va_arg (args
, bool *)) = ! cached
.b_live
;
513 case DEMUX_CAN_CONTROL_PACE
:
514 *(va_arg (args
, bool *)) = true;
517 case DEMUX_CAN_PAUSE
:
519 /* Always return true then fail late.
520 * See demux.c/demux_vaControl,
521 * misleading and should be DEMUX_CAN_CONTROL_PAUSE */
522 *(va_arg (args
, bool *)) = true;
526 case DEMUX_SET_PAUSE_STATE
:
528 vlc_mutex_locker
locker(&cached
.lock
);
529 return cached
.b_live
? VLC_EGENERIC
: VLC_SUCCESS
;
534 vlc_mutex_locker
locker(&cached
.lock
);
535 *(va_arg (args
, int64_t *)) = cached
.i_time
;
539 case DEMUX_GET_LENGTH
:
541 vlc_mutex_locker
locker(&cached
.lock
);
544 *(va_arg (args
, int64_t *)) = cached
.i_length
;
548 case DEMUX_GET_POSITION
:
550 vlc_mutex_locker
locker(&cached
.lock
);
553 *(va_arg (args
, double *)) = cached
.f_position
;
557 case DEMUX_SET_POSITION
:
559 setBufferingRunState(false); /* stop downloader first */
561 const mtime_t i_duration
= getDuration();
562 if(i_duration
== 0) /* == playlist->isLive() */
564 setBufferingRunState(true);
568 int64_t time
= i_duration
* va_arg(args
, double);
569 time
+= getFirstPlaybackTime();
571 if(!setPosition(time
))
573 setBufferingRunState(true);
577 demux
.i_nzpcr
= VLC_TS_INVALID
;
578 setBufferingRunState(true);
584 setBufferingRunState(false); /* stop downloader first */
585 if(playlist
->isLive())
587 setBufferingRunState(true);
591 int64_t time
= va_arg(args
, int64_t);// + getFirstPlaybackTime();
592 if(!setPosition(time
))
594 setBufferingRunState(true);
598 demux
.i_nzpcr
= VLC_TS_INVALID
;
599 setBufferingRunState(true);
603 case DEMUX_GET_PTS_DELAY
:
604 *va_arg (args
, int64_t *) = 1000 * INT64_C(1000);
613 void PlaylistManager::setBufferingRunState(bool b
)
615 vlc_mutex_lock(&lock
);
617 vlc_cond_signal(&waitcond
);
618 vlc_mutex_unlock(&lock
);
621 void PlaylistManager::Run()
623 vlc_mutex_lock(&lock
);
624 const unsigned i_min_buffering
= playlist
->getMinBuffering();
625 const unsigned i_extra_buffering
= playlist
->getMaxBuffering() - i_min_buffering
;
628 mutex_cleanup_push(&lock
);
630 vlc_cond_wait(&waitcond
, &lock
);
637 scheduleNextUpdate();
642 vlc_mutex_lock(&demux
.lock
);
643 mtime_t i_nzpcr
= demux
.i_nzpcr
;
644 vlc_mutex_unlock(&demux
.lock
);
646 int canc
= vlc_savecancel();
647 AbstractStream::buffering_status i_return
= bufferize(i_nzpcr
, i_min_buffering
, i_extra_buffering
);
648 vlc_restorecancel( canc
);
650 if(i_return
!= AbstractStream::buffering_lessthanmin
)
652 mtime_t i_deadline
= mdate();
653 if(i_return
== AbstractStream::buffering_ongoing
)
654 i_deadline
+= (CLOCK_FREQ
/ 20);
655 else if(i_return
== AbstractStream::buffering_full
)
656 i_deadline
+= (CLOCK_FREQ
/ 10);
657 else if(i_return
== AbstractStream::buffering_end
)
658 i_deadline
+= (CLOCK_FREQ
);
659 else /*if(i_return == AbstractStream::buffering_suspended)*/
660 i_deadline
+= (CLOCK_FREQ
/ 4);
662 vlc_mutex_lock(&demux
.lock
);
663 vlc_cond_signal(&demux
.cond
);
664 vlc_mutex_unlock(&demux
.lock
);
666 mutex_cleanup_push(&lock
);
668 vlc_cond_timedwait(&waitcond
, &lock
, i_deadline
) == 0 &&
669 i_deadline
> mdate());
673 vlc_mutex_unlock(&lock
);
676 void * PlaylistManager::managerThread(void *opaque
)
678 static_cast<PlaylistManager
*>(opaque
)->Run();
682 void PlaylistManager::updateControlsPosition()
684 vlc_mutex_locker
locker(&cached
.lock
);
685 const mtime_t i_duration
= cached
.i_length
;
688 cached
.f_position
= 0.0;
692 const mtime_t i_length
= getCurrentPlaybackTime() - getFirstPlaybackTime();
693 cached
.f_position
= (double) i_length
/ i_duration
;
696 mtime_t i_time
= getCurrentPlaybackTime();
697 if(!playlist
->isLive())
698 i_time
-= getFirstPlaybackTime();
699 cached
.i_time
= i_time
;
702 void PlaylistManager::updateControlsContentType()
704 vlc_mutex_locker
locker(&cached
.lock
);
705 if(playlist
->isLive())
707 cached
.b_live
= true;
712 cached
.b_live
= false;
713 cached
.i_length
= getDuration();
717 AbstractAdaptationLogic
*PlaylistManager::createLogic(AbstractAdaptationLogic::LogicType type
, AbstractConnectionManager
*conn
)
719 AbstractAdaptationLogic
*logic
= NULL
;
722 case AbstractAdaptationLogic::FixedRate
:
724 size_t bps
= var_InheritInteger(p_demux
, "adaptive-bw") * 8192;
725 logic
= new (std::nothrow
) FixedRateAdaptationLogic(bps
);
728 case AbstractAdaptationLogic::AlwaysLowest
:
729 logic
= new (std::nothrow
) AlwaysLowestAdaptationLogic();
731 case AbstractAdaptationLogic::AlwaysBest
:
732 logic
= new (std::nothrow
) AlwaysBestAdaptationLogic();
734 case AbstractAdaptationLogic::RateBased
:
736 RateBasedAdaptationLogic
*ratelogic
=
737 new (std::nothrow
) RateBasedAdaptationLogic(VLC_OBJECT(p_demux
));
739 conn
->setDownloadRateObserver(ratelogic
);
743 case AbstractAdaptationLogic::Default
:
744 case AbstractAdaptationLogic::NearOptimal
:
746 NearOptimalAdaptationLogic
*noplogic
=
747 new (std::nothrow
) NearOptimalAdaptationLogic(VLC_OBJECT(p_demux
));
749 conn
->setDownloadRateObserver(noplogic
);
753 case AbstractAdaptationLogic::Predictive
:
755 AbstractAdaptationLogic
*predictivelogic
=
756 new (std::nothrow
) PredictiveAdaptationLogic(VLC_OBJECT(p_demux
));
758 conn
->setDownloadRateObserver(predictivelogic
);
759 logic
= predictivelogic
;
768 logic
->setMaxDeviceResolution( var_InheritInteger(p_demux
, "adaptive-maxwidth"),
769 var_InheritInteger(p_demux
, "adaptive-maxheight") );