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 "SharedResources.hpp"
29 #include "playlist/BasePlaylist.hpp"
30 #include "playlist/BasePeriod.h"
31 #include "playlist/BaseAdaptationSet.h"
32 #include "playlist/BaseRepresentation.h"
33 #include "http/HTTPConnectionManager.h"
34 #include "logic/AlwaysBestAdaptationLogic.h"
35 #include "logic/RateBasedAdaptationLogic.h"
36 #include "logic/AlwaysLowestAdaptationLogic.hpp"
37 #include "logic/PredictiveAdaptationLogic.hpp"
38 #include "logic/NearOptimalAdaptationLogic.hpp"
39 #include "logic/BufferingLogic.hpp"
40 #include "tools/Debug.hpp"
41 #ifdef ADAPTIVE_DEBUGGING_LOGIC
42 # include "logic/RoundRobinLogic.hpp"
44 #include <vlc_stream.h>
45 #include <vlc_demux.h>
46 #include <vlc_threads.h>
51 using namespace adaptive::http
;
52 using namespace adaptive::logic
;
53 using namespace adaptive
;
54 using vlc::threads::mutex_locker
;
56 PlaylistManager::PlaylistManager( demux_t
*p_demux_
,
59 AbstractStreamFactory
*factory
,
60 AbstractAdaptationLogic::LogicType type
) :
64 streamFactory ( factory
),
67 currentPeriod
= playlist
->getFirstPeriod();
69 bufferingLogic
= nullptr;
74 nextPlaylistupdate
= 0;
75 demux
.i_nzpcr
= VLC_TICK_INVALID
;
76 demux
.i_firstpcr
= VLC_TICK_INVALID
;
77 vlc_mutex_init(&demux
.lock
);
78 vlc_cond_init(&demux
.cond
);
79 vlc_mutex_init(&cached
.lock
);
80 cached
.b_live
= false;
81 cached
.f_position
= 0.0;
82 cached
.i_time
= VLC_TICK_INVALID
;
83 cached
.playlistStart
= 0;
84 cached
.playlistEnd
= 0;
85 cached
.playlistLength
= 0;
86 cached
.lastupdate
= 0;
89 PlaylistManager::~PlaylistManager ()
96 delete bufferingLogic
;
99 void PlaylistManager::unsetPeriod()
101 std::vector
<AbstractStream
*>::iterator it
;
102 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
107 bool PlaylistManager::setupPeriod()
112 if(!logic
&& !(logic
= createLogic(logicType
, resources
->getConnManager())))
115 if(!bufferingLogic
&& !(bufferingLogic
= createBufferingLogic()))
118 std::vector
<BaseAdaptationSet
*> sets
= currentPeriod
->getAdaptationSets();
119 for(BaseAdaptationSet
*set
: sets
)
121 if(set
&& streamFactory
)
123 SegmentTracker
*tracker
= new SegmentTracker(resources
, logic
,
124 bufferingLogic
, set
);
128 AbstractStream
*st
= streamFactory
->create(p_demux
, set
->getStreamFormat(),
129 tracker
, resources
->getConnManager());
136 streams
.push_back(st
);
138 /* Generate stream description */
139 if(!set
->getLang().empty())
140 st
->setLanguage(set
->getLang());
142 if(!set
->description
.Get().empty())
143 st
->setDescription(set
->description
.Get());
149 bool PlaylistManager::init()
154 playlist
->playbackStart
.Set(time(nullptr));
155 nextPlaylistupdate
= playlist
->playbackStart
.Get();
157 updateControlsPosition();
162 bool PlaylistManager::start()
167 b_thread
= !vlc_clone(&thread
, managerThread
,
168 static_cast<void *>(this), VLC_THREAD_PRIORITY_INPUT
);
172 setBufferingRunState(true);
177 bool PlaylistManager::started() const
182 void PlaylistManager::stop()
188 mutex_locker locker
{lock
};
193 vlc_join(thread
, nullptr);
197 struct PrioritizedAbstractStream
199 AbstractStream::BufferingStatus status
;
200 vlc_tick_t demuxed_amount
;
204 static bool streamCompare(const PrioritizedAbstractStream
&a
, const PrioritizedAbstractStream
&b
)
206 if( a
.status
>= b
.status
) /* Highest prio is higer value in enum */
208 if ( a
.status
== b
.status
) /* Highest prio is lowest buffering */
209 return a
.demuxed_amount
< b
.demuxed_amount
;
216 AbstractStream::BufferingStatus
PlaylistManager::bufferize(vlc_tick_t i_nzdeadline
,
217 vlc_tick_t i_min_buffering
, vlc_tick_t i_extra_buffering
)
219 AbstractStream::BufferingStatus i_return
= AbstractStream::BufferingStatus::End
;
221 /* First reorder by status >> buffering level */
222 std::vector
<PrioritizedAbstractStream
> prioritized_streams(streams
.size());
223 std::vector
<PrioritizedAbstractStream
>::iterator it
= prioritized_streams
.begin();
224 for(AbstractStream
*stream
: streams
)
226 PrioritizedAbstractStream
&p
= *it
;
228 p
.status
= p
.st
->getLastBufferStatus();
229 p
.demuxed_amount
= p
.st
->getDemuxedAmount(i_nzdeadline
);
232 std::sort(prioritized_streams
.begin(), prioritized_streams
.end(), streamCompare
);
234 for(PrioritizedAbstractStream
&pst
: prioritized_streams
)
236 AbstractStream
*st
= pst
.st
;
243 if (st
->isDisabled() &&
244 (!st
->isSelected() || !reactivateStream(st
)))
252 AbstractStream::BufferingStatus i_ret
= st
->bufferize(i_nzdeadline
,
255 getActiveStreamsCount() <= 1);
256 if(i_return
!= AbstractStream::BufferingStatus::Ongoing
) /* Buffering streams need to keep going */
262 /* Bail out, will start again (high prio could be same starving stream) */
263 if( i_return
== AbstractStream::BufferingStatus::Lessthanmin
)
267 vlc_mutex_lock(&demux
.lock
);
268 if(demux
.i_nzpcr
== VLC_TICK_INVALID
&&
269 i_return
!= AbstractStream::BufferingStatus::Lessthanmin
/* prevents starting before buffering is reached */ )
271 demux
.i_nzpcr
= getFirstDTS();
273 vlc_mutex_unlock(&demux
.lock
);
278 AbstractStream::Status
PlaylistManager::dequeue(vlc_tick_t i_floor
, vlc_tick_t
*pi_nzbarrier
)
280 AbstractStream::Status i_return
= AbstractStream::Status::Eof
;
282 const vlc_tick_t i_nzdeadline
= *pi_nzbarrier
;
284 for(AbstractStream
*st
: streams
)
287 AbstractStream::Status i_ret
= st
->dequeue(i_nzdeadline
, &i_pcr
);
288 if( i_ret
> i_return
)
291 if( i_pcr
> i_floor
)
292 *pi_nzbarrier
= std::min( *pi_nzbarrier
, i_pcr
- VLC_TICK_0
);
298 vlc_tick_t
PlaylistManager::getResumeTime() const
300 vlc_mutex_locker
locker(&demux
.lock
);
301 return demux
.i_nzpcr
;
304 vlc_tick_t
PlaylistManager::getFirstDTS() const
306 vlc_tick_t mindts
= VLC_TICK_INVALID
;
307 for(const AbstractStream
*stream
: streams
)
309 const vlc_tick_t dts
= stream
->getFirstDTS();
310 if(mindts
== VLC_TICK_INVALID
)
312 else if(dts
!= VLC_TICK_INVALID
)
313 mindts
= std::min(mindts
, dts
);
318 unsigned PlaylistManager::getActiveStreamsCount() const
322 for(const AbstractStream
* st
: streams
)
324 if(st
->isValid() && !st
->isDisabled())
330 bool PlaylistManager::setPosition(vlc_tick_t time
)
333 bool hasValidStream
= false;
334 for(int real
= 0; real
< 2; real
++)
336 /* Always probe if we can seek first */
337 for(AbstractStream
* st
: streams
)
339 if(st
->isValid() && !st
->isDisabled())
341 hasValidStream
= true;
342 ret
&= st
->setPosition(time
, !real
);
350 msg_Warn(p_demux
, "there is no valid streams");
356 void PlaylistManager::setLivePause(bool b
)
361 for(AbstractStream
* st
: streams
)
362 if(st
->isValid() && !st
->isDisabled())
366 bool PlaylistManager::needsUpdate() const
368 return playlist
->needsUpdates() &&
369 playlist
->isLive() && (failedupdates
< 3);
372 void PlaylistManager::scheduleNextUpdate()
377 bool PlaylistManager::updatePlaylist()
379 for(AbstractStream
* st
: streams
)
382 updateControlsPosition();
386 vlc_tick_t
PlaylistManager::getFirstPlaybackTime() const
391 vlc_tick_t
PlaylistManager::getCurrentDemuxTime() const
393 vlc_mutex_locker
locker(&demux
.lock
);
394 return demux
.i_nzpcr
;
397 vlc_tick_t
PlaylistManager::getMinAheadTime() const
399 vlc_tick_t minbuffer
= 0;
400 std::for_each(streams
.cbegin(), streams
.cend(),
401 [&minbuffer
](const AbstractStream
*st
) {
402 if(st
->isValid() && !st
->isDisabled() && st
->isSelected())
404 const vlc_tick_t m
= st
->getMinAheadTime();
405 if(m
> 0 && (m
< minbuffer
|| minbuffer
== 0))
412 bool PlaylistManager::reactivateStream(AbstractStream
*stream
)
414 return stream
->reactivate(getResumeTime());
417 #define DEMUX_INCREMENT VLC_TICK_FROM_MS(50)
418 int PlaylistManager::demux_callback(demux_t
*p_demux
)
420 PlaylistManager
*manager
= reinterpret_cast<PlaylistManager
*>(p_demux
->p_sys
);
421 if(!manager
->started() && !manager
->start())
422 return VLC_DEMUXER_EOF
;
423 return manager
->doDemux(DEMUX_INCREMENT
);
426 int PlaylistManager::doDemux(vlc_tick_t increment
)
428 vlc_mutex_lock(&demux
.lock
);
429 if(demux
.i_nzpcr
== VLC_TICK_INVALID
)
432 bool b_all_disabled
= true;
433 std::vector
<AbstractStream
*>::const_iterator it
;
434 for(it
=streams
.begin(); it
!=streams
.end(); ++it
)
436 b_dead
&= !(*it
)->isValid();
437 b_all_disabled
&= (*it
)->isDisabled();
440 vlc_cond_timedwait(&demux
.cond
, &demux
.lock
, vlc_tick_now() + VLC_TICK_FROM_MS(50));
441 vlc_mutex_unlock(&demux
.lock
);
442 return (b_dead
|| b_all_disabled
) ? VLC_DEMUXER_EOF
: VLC_DEMUXER_SUCCESS
;
445 if(demux
.i_firstpcr
== VLC_TICK_INVALID
)
446 demux
.i_firstpcr
= demux
.i_nzpcr
;
448 vlc_tick_t i_nzbarrier
= demux
.i_nzpcr
+ increment
;
449 vlc_mutex_unlock(&demux
.lock
);
451 AbstractStream::Status status
= dequeue(demux
.i_nzpcr
, &i_nzbarrier
);
453 updateControlsPosition();
457 case AbstractStream::Status::Eof
:
459 /* might be end of current period */
462 setBufferingRunState(false);
463 BasePeriod
*nextPeriod
= playlist
->getNextPeriod(currentPeriod
);
465 return VLC_DEMUXER_EOF
;
467 currentPeriod
= nextPeriod
;
469 return VLC_DEMUXER_EOF
;
471 demux
.i_nzpcr
= VLC_TICK_INVALID
;
472 demux
.i_firstpcr
= VLC_TICK_INVALID
;
473 es_out_Control(p_demux
->out
, ES_OUT_RESET_PCR
);
475 setBufferingRunState(true);
479 case AbstractStream::Status::Buffering
:
480 vlc_mutex_lock(&demux
.lock
);
481 vlc_cond_timedwait(&demux
.cond
, &demux
.lock
, vlc_tick_now() + VLC_TICK_FROM_MS(50));
482 vlc_mutex_unlock(&demux
.lock
);
484 case AbstractStream::Status::Discontinuity
:
485 vlc_mutex_lock(&demux
.lock
);
486 demux
.i_nzpcr
= VLC_TICK_INVALID
;
487 demux
.i_firstpcr
= VLC_TICK_INVALID
;
488 es_out_Control(p_demux
->out
, ES_OUT_RESET_PCR
);
489 vlc_mutex_unlock(&demux
.lock
);
491 case AbstractStream::Status::Demuxed
:
492 vlc_mutex_lock(&demux
.lock
);
493 if( demux
.i_nzpcr
!= VLC_TICK_INVALID
&& i_nzbarrier
!= demux
.i_nzpcr
)
495 demux
.i_nzpcr
= i_nzbarrier
;
496 vlc_tick_t pcr
= VLC_TICK_0
+ std::max(INT64_C(0), demux
.i_nzpcr
- VLC_TICK_FROM_MS(100));
497 es_out_Control(p_demux
->out
, ES_OUT_SET_GROUP_PCR
, 0, pcr
);
499 vlc_mutex_unlock(&demux
.lock
);
503 return VLC_DEMUXER_SUCCESS
;
506 int PlaylistManager::control_callback(demux_t
*p_demux
, int i_query
, va_list args
)
508 PlaylistManager
*manager
= reinterpret_cast<PlaylistManager
*>(p_demux
->p_sys
);
509 return manager
->doControl(i_query
, args
);
512 int PlaylistManager::doControl(int i_query
, va_list args
)
517 case DEMUX_CAN_CONTROL_PACE
:
518 *(va_arg (args
, bool *)) = true;
521 case DEMUX_CAN_PAUSE
:
523 /* Always return true then fail late.
524 * See demux.c/demux_vaControl,
525 * misleading and should be DEMUX_CAN_CONTROL_PAUSE */
526 *(va_arg (args
, bool *)) = true;
530 case DEMUX_SET_PAUSE_STATE
:
532 vlc_mutex_locker
locker(&cached
.lock
);
533 bool b_pause
= (bool)va_arg(args
, int);
534 if(playlist
->isLive())
536 setBufferingRunState(false); /* stop downloader first */
537 vlc_tick_t now
= vlc_tick_now();
542 msg_Dbg(p_demux
,"Buffering and playback paused. No timeshift support.");
547 msg_Dbg(p_demux
,"Resuming buffering/playback after %" PRId64
"ms",
548 MS_FROM_VLC_TICK(now
-pause_start
));
549 es_out_Control(p_demux
->out
, ES_OUT_RESET_PCR
);
551 setBufferingRunState(true);
552 demux
.i_nzpcr
= VLC_TICK_INVALID
;
553 cached
.lastupdate
= 0;
560 vlc_mutex_locker
locker(&cached
.lock
);
561 *(va_arg (args
, vlc_tick_t
*)) = cached
.i_time
;
565 case DEMUX_GET_LENGTH
:
567 vlc_mutex_locker
locker(&cached
.lock
);
568 if(cached
.b_live
&& cached
.playlistLength
== 0)
570 *(va_arg (args
, vlc_tick_t
*)) = cached
.playlistLength
;
574 case DEMUX_GET_POSITION
:
576 vlc_mutex_locker
locker(&cached
.lock
);
577 if(cached
.b_live
&& cached
.playlistLength
== 0)
579 *(va_arg (args
, double *)) = cached
.f_position
;
583 case DEMUX_SET_POSITION
:
585 setBufferingRunState(false); /* stop downloader first */
586 vlc_mutex_locker
locker(&cached
.lock
);
588 if(cached
.playlistLength
== 0)
590 setBufferingRunState(true);
594 double pos
= va_arg(args
, double);
595 vlc_tick_t seekTime
= cached
.playlistStart
+ cached
.playlistLength
* pos
;
597 SeekDebug(msg_Dbg(p_demux
, "Seek %f to %ld plstart %ld duration %ld",
598 pos
, seekTime
, cached
.playlistEnd
, cached
.playlistLength
));
600 if(!setPosition(seekTime
))
602 setBufferingRunState(true);
606 demux
.i_nzpcr
= VLC_TICK_INVALID
;
607 cached
.lastupdate
= 0;
608 setBufferingRunState(true);
614 setBufferingRunState(false); /* stop downloader first */
616 vlc_tick_t time
= va_arg(args
, vlc_tick_t
);// + getFirstPlaybackTime();
617 if(!setPosition(time
))
619 setBufferingRunState(true);
623 vlc_mutex_locker
locker(&cached
.lock
);
624 demux
.i_nzpcr
= VLC_TICK_INVALID
;
625 cached
.lastupdate
= 0;
626 setBufferingRunState(true);
630 case DEMUX_GET_PTS_DELAY
:
631 *va_arg (args
, vlc_tick_t
*) = VLC_TICK_FROM_SEC(1);
640 void PlaylistManager::setBufferingRunState(bool b
)
642 mutex_locker locker
{lock
};
647 void PlaylistManager::Run()
649 mutex_locker locker
{lock
};
650 const vlc_tick_t i_min_buffering
= bufferingLogic
->getMinBuffering(playlist
);
651 const vlc_tick_t i_extra_buffering
= bufferingLogic
->getMaxBuffering(playlist
) - i_min_buffering
;
654 while(!b_buffering
&& !b_canceled
)
662 scheduleNextUpdate();
667 vlc_mutex_lock(&demux
.lock
);
668 vlc_tick_t i_nzpcr
= demux
.i_nzpcr
;
669 vlc_mutex_unlock(&demux
.lock
);
671 AbstractStream::BufferingStatus i_return
= bufferize(i_nzpcr
, i_min_buffering
, i_extra_buffering
);
673 if(i_return
!= AbstractStream::BufferingStatus::Lessthanmin
)
675 vlc_tick_t i_deadline
= vlc_tick_now();
676 if(i_return
== AbstractStream::BufferingStatus::Ongoing
)
677 i_deadline
+= VLC_TICK_FROM_MS(10);
678 else if(i_return
== AbstractStream::BufferingStatus::Full
)
679 i_deadline
+= VLC_TICK_FROM_MS(100);
680 else if(i_return
== AbstractStream::BufferingStatus::End
)
681 i_deadline
+= VLC_TICK_FROM_SEC(1);
682 else /*if(i_return == AbstractStream::BufferingStatus::suspended)*/
683 i_deadline
+= VLC_TICK_FROM_MS(250);
685 // TODO: The current function doesn't seem to modify shared
686 // state under demux lock.
687 vlc_cond_signal(&demux
.cond
);
690 waitcond
.timedwait(lock
, i_deadline
) == 0 &&
691 i_deadline
> vlc_tick_now() &&
699 void * PlaylistManager::managerThread(void *opaque
)
701 static_cast<PlaylistManager
*>(opaque
)->Run();
705 void PlaylistManager::updateControlsPosition()
707 vlc_mutex_locker
locker(&cached
.lock
);
709 time_t now
= time(nullptr);
710 if(now
- cached
.lastupdate
< 1)
712 cached
.lastupdate
= now
;
714 vlc_tick_t rapPlaylistStart
= 0;
715 vlc_tick_t rapDemuxStart
= 0;
716 for(AbstractStream
* st
: streams
)
718 if(st
->isValid() && !st
->isDisabled() && st
->isSelected())
720 if(st
->getMediaPlaybackTimes(&cached
.playlistStart
, &cached
.playlistEnd
,
721 &cached
.playlistLength
,
722 &rapPlaylistStart
, &rapDemuxStart
))
729 * -> Elapsed demux time (current demux time - first demux time)
730 * Since PlaylistTime != DemuxTime (HLS crap, TS):
731 * -> Use Playlist<->Demux time offset provided by EsOut
732 * to convert to elapsed playlist time.
733 * But this diff is not available until we have demuxed data...
734 * Fallback on relative seek from playlist start in that case
735 * But also playback might not have started at beginning of playlist
736 * -> Apply relative start from seek point (set on EsOut as ExpectedTime)
738 * All seeks need to be done in playlist time !
741 vlc_tick_t currentDemuxTime
= getCurrentDemuxTime();
742 cached
.b_live
= playlist
->isLive();
744 SeekDebug(msg_Dbg(p_demux
, "playlist Start/End %ld/%ld len %ld"
745 "rap pl/demux (%ld/%ld)",
746 cached
.playlistStart
, cached
.playlistEnd
, cached
.playlistEnd
,
747 rapPlaylistStart
, rapDemuxStart
));
751 /* Special case for live until we can provide relative start to fully match
752 the above description */
753 cached
.i_time
= currentDemuxTime
;
755 if(cached
.playlistStart
!= cached
.playlistEnd
)
757 if(cached
.playlistStart
< 0) /* Live template. Range start = now() - buffering depth */
759 cached
.playlistEnd
= vlc_tick_from_sec(now
);
760 cached
.playlistStart
= cached
.playlistEnd
- cached
.playlistLength
;
763 const vlc_tick_t currentTime
= getCurrentDemuxTime();
764 if(currentTime
> cached
.playlistStart
&&
765 currentTime
<= cached
.playlistEnd
&& cached
.playlistLength
)
767 cached
.f_position
= ((double)(currentTime
- cached
.playlistStart
)) / cached
.playlistLength
;
771 cached
.f_position
= 0.0;
776 if(playlist
->duration
.Get() > cached
.playlistLength
)
777 cached
.playlistLength
= playlist
->duration
.Get();
779 if(cached
.playlistLength
&& currentDemuxTime
)
781 /* convert to playlist time */
782 vlc_tick_t rapRelOffset
= currentDemuxTime
- rapDemuxStart
; /* offset from start/seek */
783 vlc_tick_t absPlaylistTime
= rapPlaylistStart
+ rapRelOffset
; /* converted as abs playlist time */
784 vlc_tick_t relMediaTime
= absPlaylistTime
- cached
.playlistStart
; /* elapsed, in playlist time */
785 cached
.i_time
= absPlaylistTime
;
786 cached
.f_position
= (double) relMediaTime
/ cached
.playlistLength
;
790 cached
.f_position
= 0.0;
794 SeekDebug(msg_Dbg(p_demux
, "cached.i_time (%ld) cur %ld rap start (pl %ld/dmx %ld)",
795 cached
.i_time
, currentDemuxTime
, rapPlaylistStart
, rapDemuxStart
));
798 AbstractAdaptationLogic
*PlaylistManager::createLogic(AbstractAdaptationLogic::LogicType type
, AbstractConnectionManager
*conn
)
800 vlc_object_t
*obj
= VLC_OBJECT(p_demux
);
801 AbstractAdaptationLogic
*logic
= nullptr;
804 case AbstractAdaptationLogic::LogicType::FixedRate
:
806 size_t bps
= var_InheritInteger(p_demux
, "adaptive-bw") * 8192;
807 logic
= new (std::nothrow
) FixedRateAdaptationLogic(obj
, bps
);
810 case AbstractAdaptationLogic::LogicType::AlwaysLowest
:
811 logic
= new (std::nothrow
) AlwaysLowestAdaptationLogic(obj
);
813 case AbstractAdaptationLogic::LogicType::AlwaysBest
:
814 logic
= new (std::nothrow
) AlwaysBestAdaptationLogic(obj
);
816 case AbstractAdaptationLogic::LogicType::RateBased
:
818 RateBasedAdaptationLogic
*ratelogic
=
819 new (std::nothrow
) RateBasedAdaptationLogic(obj
);
821 conn
->setDownloadRateObserver(ratelogic
);
825 case AbstractAdaptationLogic::LogicType::Default
:
826 #ifdef ADAPTIVE_DEBUGGING_LOGIC
827 logic
= new (std::nothrow
) RoundRobinLogic(obj
);
828 msg_Warn(p_demux
, "using RoundRobinLogic every %u", RoundRobinLogic::QUANTUM
);
831 case AbstractAdaptationLogic::LogicType::NearOptimal
:
833 NearOptimalAdaptationLogic
*noplogic
=
834 new (std::nothrow
) NearOptimalAdaptationLogic(obj
);
836 conn
->setDownloadRateObserver(noplogic
);
840 case AbstractAdaptationLogic::LogicType::Predictive
:
842 AbstractAdaptationLogic
*predictivelogic
=
843 new (std::nothrow
) PredictiveAdaptationLogic(obj
);
845 conn
->setDownloadRateObserver(predictivelogic
);
846 logic
= predictivelogic
;
855 int w
= var_InheritInteger(p_demux
, "adaptive-maxwidth");
856 int h
= var_InheritInteger(p_demux
, "adaptive-maxheight");
859 h
= var_InheritInteger(p_demux
, "preferred-resolution");
860 /* Adapt for slightly different minimum/maximum semantics */
867 logic
->setMaxDeviceResolution(w
, h
);
873 AbstractBufferingLogic
*PlaylistManager::createBufferingLogic() const
875 DefaultBufferingLogic
*bl
= new DefaultBufferingLogic();
878 unsigned v
= var_InheritInteger(p_demux
, "adaptive-livedelay");
880 bl
->setUserLiveDelay(VLC_TICK_FROM_MS(v
));
881 v
= var_InheritInteger(p_demux
, "adaptive-maxbuffer");
883 bl
->setUserMaxBuffering(VLC_TICK_FROM_MS(v
));