|
|
|
|
Quelle PlaylistManager.cpp
Sprache: C
|
|
/*
* PlaylistManager.cpp
*****************************************************************************
* Copyright © 2010 - 2011 Klagenfurt University
* 2015 VideoLAN and VLC Authors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "PlaylistManager.h"
#include "SegmentTracker.hpp"
#include "SharedResources.hpp"
#include "playlist/BasePlaylist.hpp"
#include "playlist/BasePeriod.h"
#include "playlist/BaseAdaptationSet.h"
#include "playlist/BaseRepresentation.h"
#include "http/HTTPConnectionManager.h"
#include "logic/AlwaysBestAdaptationLogic.h"
#include "logic/RateBasedAdaptationLogic.h"
#include "logic/AlwaysLowestAdaptationLogic.hpp"
#include "logic/PredictiveAdaptationLogic.hpp"
#include "logic/NearOptimalAdaptationLogic.hpp"
#include "logic/BufferingLogic.hpp"
#include "tools/Debug.hpp"
#ifdef ADAPTIVE_DEBUGGING_LOGIC
# include "logic/RoundRobinLogic.hpp"
#endif
#include <vlc_stream.h>
#include <vlc_demux.h>
#include <vlc_threads.h>
#include <algorithm>
#include <ctime>
using namespace adaptive::http;
using namespace adaptive::logic;
using namespace adaptive;
using vlc::threads::mutex_locker;
PlaylistManager::PlaylistManager( demux_t *p_demux_,
SharedResources *res,
BasePlaylist *pl,
AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type ) :
logicType ( type ),
logic ( nullptr ),
playlist ( pl ),
streamFactory ( factory ),
p_demux ( p_demux_ )
{
currentPeriod = playlist->getFirstPeriod();
resources = res;
bufferingLogic = nullptr;
failedupdates = 0;
b_thread = false;
b_buffering = false;
b_canceled = false;
b_preparsing = false;
nextPlaylistupdate = 0;
demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
demux.rebuffering = false;
vlc_mutex_init(&demux.lock);
vlc_cond_init(&demux.cond);
vlc_mutex_init(&cached.lock);
cached.b_live = false;
cached.f_position = 0.0;
cached.i_time = VLC_TICK_INVALID;
cached.i_normaltime = VLC_TICK_INVALID;
cached.playlistStart = 0;
cached.playlistEnd = 0;
cached.playlistLength = 0;
cached.lastupdate = 0;
}
PlaylistManager::~PlaylistManager ()
{
delete streamFactory;
unsetPeriod();
delete playlist;
delete resources;
delete logic;
delete bufferingLogic;
}
void PlaylistManager::unsetPeriod()
{
std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); ++it)
delete *it;
streams.clear();
}
bool PlaylistManager::setupPeriod()
{
if(!currentPeriod)
return false;
if(!logic && !(logic = createLogic(logicType, resources->getConnManager())))
return false;
if(!bufferingLogic && !(bufferingLogic = createBufferingLogic()))
return false;
const std::vector<BaseAdaptationSet*> &sets = currentPeriod->getAdaptationSets();
for(BaseAdaptationSet *set : sets)
{
if(set && streamFactory)
{
SegmentTracker *tracker = new SegmentTracker(resources, logic,
bufferingLogic, set,
&synchronizationReferences);
if(!tracker)
continue;
AbstractStream *st = streamFactory->create(p_demux, set->getStreamFormat(),
tracker);
if(!st)
{
delete tracker;
continue;
}
streams.push_back(st);
/* Generate stream description */
if(!set->getLang().empty())
st->setLanguage(set->getLang());
if(!set->description.empty())
st->setDescription(set->description);
}
}
return true;
}
bool PlaylistManager::init(bool b_preparsing)
{
this->b_preparsing = b_preparsing;
if(!setupPeriod())
return false;
playlist->playbackStart = time(nullptr);
nextPlaylistupdate = playlist->playbackStart;
if(b_preparsing)
preparsePlaylist();
updateControlsPosition();
return true;
}
bool PlaylistManager::start()
{
if(b_thread || b_preparsing)
return false;
b_thread = !vlc_clone(&thread, managerThread, static_cast<void *>(this));
if(!b_thread)
return false;
setBufferingRunState(true);
return true;
}
bool PlaylistManager::started() const
{
return b_thread;
}
void PlaylistManager::stop()
{
if(!b_thread)
return;
{
mutex_locker locker {lock};
b_canceled = true;
waitcond.signal();
}
vlc_join(thread, nullptr);
b_thread = false;
}
struct PrioritizedAbstractStream
{
AbstractStream::BufferingStatus status;
vlc_tick_t demuxed_amount;
AbstractStream *st;
};
static bool streamCompare(const PrioritizedAbstractStream &a, const PrioritizedAbstractStream &b)
{
if( a.status >= b.status ) /* Highest prio is higher value in enum */
{
if ( a.status == b.status ) /* Highest prio is lowest buffering */
return a.demuxed_amount < b.demuxed_amount;
else
return true;
}
return false;
}
AbstractStream::BufferingStatus PlaylistManager::bufferize(Times deadline,
vlc_tick_t i_min_buffering,
vlc_tick_t i_max_buffering,
vlc_tick_t i_target_buffering)
{
AbstractStream::BufferingStatus i_return = AbstractStream::BufferingStatus::End;
/* First reorder by status >> buffering level */
std::vector<PrioritizedAbstractStream> prioritized_streams(streams.size());
std::vector<PrioritizedAbstractStream>::iterator it = prioritized_streams.begin();
for(AbstractStream *stream : streams)
{
PrioritizedAbstractStream &p = *it;
p.st = stream;
p.status = p.st->getBufferAndStatus(deadline, i_min_buffering, i_max_buffering, &p.demuxed_amount);
++it;
}
std::sort(prioritized_streams.begin(), prioritized_streams.end(), streamCompare);
for(PrioritizedAbstractStream &pst : prioritized_streams)
{
AbstractStream *st = pst.st;
if(!st->isValid())
continue;
if(st->esCount())
{
if (st->isDisabled() &&
(!st->isSelected() || !reactivateStream(st)))
continue;
}
else
{
/* initial */
}
AbstractStream::BufferingStatus i_ret = st->bufferize(deadline,
i_min_buffering,
i_max_buffering,
i_target_buffering,
getActiveStreamsCount() <= 1);
if(i_return != AbstractStream::BufferingStatus::Ongoing) /* Buffering streams need to keep going */
{
if(i_ret > i_return)
i_return = i_ret;
}
/* Bail out, will start again (high prio could be same starving stream) */
if( i_return == AbstractStream::BufferingStatus::Lessthanmin )
break;
}
vlc_mutex_locker locker(&demux.lock);
if(demux.times.continuous == VLC_TICK_INVALID &&
/* don't wait minbuffer on simple discontinuity or restart */
(demux.pcr_syncpoint == TimestampSynchronizationPoint::Discontinuity ||
/* prevents starting before buffering is reached */
i_return != AbstractStream::BufferingStatus::Lessthanmin ))
{
demux.times = getFirstTimes();
}
return i_return;
}
AbstractStream::Status PlaylistManager::dequeue(Times floor, Times *barrier)
{
AbstractStream::Status i_return = AbstractStream::Status::Eof;
const Times deadline = *barrier;
for(AbstractStream *st : streams)
{
Times pcr;
AbstractStream::Status i_ret = st->dequeue(deadline, &pcr);
if( i_ret > i_return )
i_return = i_ret;
if( pcr.continuous > floor.continuous )
{
if( barrier->continuous > pcr.continuous )
*barrier = pcr;
}
}
return i_return;
}
StreamPosition PlaylistManager::getResumePosition() const
{
vlc_mutex_locker locker(&demux.lock);
StreamPosition pos;
pos.times = demux.times;
return pos;
}
Times PlaylistManager::getFirstTimes() const
{
Times mindts;
for(const AbstractStream *stream : streams)
{
const Times dts = stream->getFirstTimes();
if(mindts.continuous == VLC_TICK_INVALID)
mindts = dts;
else if(dts.continuous != VLC_TICK_INVALID &&
dts.continuous < mindts.continuous)
mindts = dts;
}
return mindts;
}
unsigned PlaylistManager::getActiveStreamsCount() const
{
// TODO improve
unsigned count = 0;
for(const AbstractStream* st : streams)
{
if(st->isValid() && !st->isDisabled())
count++;
}
return count;
}
bool PlaylistManager::setPosition(vlc_tick_t mediatime, double pos, bool accurate)
{
bool ret = true;
bool hasValidStream = false;
StreamPosition streampos;
streampos.times = demux.firsttimes;
if(streampos.times.continuous != VLC_TICK_INVALID)
streampos.times.offsetBy(mediatime - streampos.times.segment.media);
else
streampos.times.segment.media = mediatime;
streampos.pos = pos;
for(int real = 0; real < 2; real++)
{
/* Always probe if we can seek first */
for(AbstractStream* st : streams)
{
if(st->isValid() && !st->isDisabled())
{
hasValidStream = true;
ret &= st->setPosition(streampos, !real);
}
}
if(!ret)
break;
}
if(!hasValidStream)
{
msg_Warn(p_demux, "there is no valid streams");
ret = false;
}
if(accurate && ret && streampos.times.continuous >= VLC_TICK_0)
{
es_out_Control(p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
streampos.times.continuous);
SeekDebug(msg_Dbg(p_demux,"ES_OUT_SET_NEXT_DISPLAY_TIME to %" PRId64,
streampos.times.continuous));
}
return ret;
}
void PlaylistManager::setLivePause(bool b)
{
if(!started())
return;
for(AbstractStream* st : streams)
if(st->isValid() && !st->isDisabled())
st->setLivePause(b);
}
bool PlaylistManager::needsUpdate() const
{
return playlist->needsUpdates() &&
playlist->isLive() && (failedupdates < 3);
}
void PlaylistManager::scheduleNextUpdate()
{
}
bool PlaylistManager::updatePlaylist()
{
for(AbstractStream* st : streams)
st->runUpdates();
updateControlsPosition();
return true;
}
void PlaylistManager::preparsePlaylist()
{
}
Times PlaylistManager::getTimes(bool b_first) const
{
vlc_mutex_locker locker(&demux.lock);
return b_first ? demux.firsttimes : demux.times;
}
vlc_tick_t PlaylistManager::getMinAheadTime() const
{
vlc_tick_t minbuffer = 0;
std::for_each(streams.cbegin(), streams.cend(),
[&minbuffer](const AbstractStream *st) {
if(st->isValid() && !st->isDisabled() && st->isSelected())
{
const vlc_tick_t m = st->getMinAheadTime();
if(m > 0 && (m < minbuffer || minbuffer == 0))
minbuffer = m;
}
});
return minbuffer;
}
bool PlaylistManager::isBufferingSufficient(Times from, vlc_tick_t minimum) const
{
return std::all_of(streams.cbegin(), streams.cend(),
[from, minimum](const AbstractStream *st) {
return !st->isSelected() ||
st->isBufferingSufficient(from, minimum);
});
}
bool PlaylistManager::reactivateStream(AbstractStream *stream)
{
return stream->reactivate(getResumePosition());
}
#define DEMUX_INCREMENT VLC_TICK_FROM_MS(50)
int PlaylistManager::demux_callback(demux_t *p_demux)
{
PlaylistManager *manager = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);
if(!manager->started() && !manager->start())
return VLC_DEMUXER_EOF;
return manager->doDemux(DEMUX_INCREMENT);
}
int PlaylistManager::doDemux(vlc_tick_t increment)
{
vlc_mutex_lock(&demux.lock);
if(demux.times.continuous == VLC_TICK_INVALID)
{
bool b_dead = true;
bool b_all_disabled = true;
std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it)
{
b_dead &= !(*it)->isValid();
b_all_disabled &= (*it)->isDisabled();
}
if(!b_dead)
vlc_cond_timedwait(&demux.cond, &demux.lock, vlc_tick_now() + VLC_TICK_FROM_MS(50));
vlc_mutex_unlock(&demux.lock);
return (b_dead || b_all_disabled) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
}
const Times floor = demux.times;
const bool rebuffering = demux.rebuffering;
if(rebuffering)
{
const vlc_tick_t minimum = bufferingLogic->getMinBuffering(playlist);
if(!isBufferingSufficient(floor, minimum))
{
vlc_cond_timedwait(&demux.cond, &demux.lock, vlc_tick_now() + VLC_TICK_FROM_MS(50));
vlc_mutex_unlock(&demux.lock);
return VLC_DEMUXER_BUFFERING;
}
demux.rebuffering = false;
}
vlc_mutex_unlock(&demux.lock);
Times barrier = floor;
barrier.offsetBy(increment);
AbstractStream::Status status = dequeue(floor, &barrier);
vlc_mutex_lock(&demux.lock);
if(demux.firsttimes.continuous == VLC_TICK_INVALID && barrier.continuous != VLC_TICK_INVALID)
{
demux.firsttimes = barrier;
assert(barrier.segment.media != VLC_TICK_INVALID);
}
vlc_mutex_unlock(&demux.lock);
updateControlsPosition();
switch(status)
{
case AbstractStream::Status::Eof:
{
/* might be end of current period */
if(currentPeriod)
{
setBufferingRunState(false);
BasePeriod *nextPeriod = playlist->getNextPeriod(currentPeriod);
if(!nextPeriod)
return VLC_DEMUXER_EOF;
unsetPeriod();
currentPeriod = nextPeriod;
if (!setupPeriod())
return VLC_DEMUXER_EOF;
demux.times = Times();
demux.firsttimes = Times();
demux.rebuffering = false;
es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
setBufferingRunState(true);
}
}
break;
case AbstractStream::Status::Buffering:
vlc_mutex_lock(&demux.lock);
demux.rebuffering = true;
vlc_cond_timedwait(&demux.cond, &demux.lock, vlc_tick_now() + VLC_TICK_FROM_MS(50));
vlc_mutex_unlock(&demux.lock);
return VLC_DEMUXER_BUFFERING;
case AbstractStream::Status::Discontinuity:
vlc_mutex_lock(&demux.lock);
demux.times = Times();
demux.firsttimes = Times();
demux.rebuffering = false;
demux.pcr_syncpoint = TimestampSynchronizationPoint::Discontinuity;
es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
vlc_mutex_unlock(&demux.lock);
break;
case AbstractStream::Status::Demuxed:
vlc_mutex_lock(&demux.lock);
if( demux.times.continuous != VLC_TICK_INVALID && barrier.continuous != demux.times.continuous )
{
demux.times = barrier;
vlc_tick_t pcr = VLC_TICK_0 + std::max(INT64_C(0), demux.times.continuous - VLC_TICK_FROM_MS(100));
es_out_Control(p_demux->out, ES_OUT_SET_GROUP_PCR, 0, pcr);
}
vlc_mutex_unlock(&demux.lock);
break;
}
return VLC_DEMUXER_SUCCESS;
}
int PlaylistManager::control_callback(demux_t *p_demux, int i_query, va_list args)
{
PlaylistManager *manager = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);
return manager->doControl(i_query, args);
}
int PlaylistManager::doControl(int i_query, va_list args)
{
switch (i_query)
{
case DEMUX_CAN_SEEK:
case DEMUX_CAN_CONTROL_PACE:
*(va_arg (args, bool *)) = true;
break;
case DEMUX_CAN_PAUSE:
{
/* Always return true then fail late.
* See demux.c/demux_vaControl,
* misleading and should be DEMUX_CAN_CONTROL_PAUSE */
*(va_arg (args, bool *)) = true;
break;
}
case DEMUX_SET_PAUSE_STATE:
{
setBufferingRunState(false); /* /!\ always stop buffering process first */
bool b_pause = (bool)va_arg(args, int);
if(playlist->isLive())
{
vlc_tick_t now = vlc_tick_now();
demux.times = Times();
demux.rebuffering = false;
cached.lastupdate = 0;
if(b_pause)
{
setLivePause(true);
pause_start = now;
msg_Dbg(p_demux,"Buffering and playback paused. No timeshift support.");
}
else
{
setLivePause(false);
msg_Dbg(p_demux,"Resuming buffering/playback after %" PRId64 "ms",
MS_FROM_VLC_TICK(now-pause_start));
es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
setBufferingRunState(true);
}
}
else setBufferingRunState(true);
return VLC_SUCCESS;
}
case DEMUX_GET_TIME:
{
vlc_mutex_locker locker(&cached.lock);
*(va_arg (args, vlc_tick_t *)) = cached.i_time;
break;
}
case DEMUX_GET_NORMAL_TIME:
{
vlc_mutex_locker locker(&cached.lock);
if( cached.i_normaltime == VLC_TICK_INVALID )
return VLC_EGENERIC;
*(va_arg (args, vlc_tick_t *)) = cached.i_normaltime;
break;
}
case DEMUX_GET_LENGTH:
{
vlc_mutex_locker locker(&cached.lock);
if(cached.b_live && cached.playlistLength == 0)
return VLC_EGENERIC;
*(va_arg (args, vlc_tick_t *)) = cached.playlistLength;
*(va_arg (args, bool *)) = cached.b_live;
break;
}
case DEMUX_GET_POSITION:
{
vlc_mutex_locker locker(&cached.lock);
if(cached.b_live && cached.playlistLength == 0)
return VLC_EGENERIC;
*(va_arg (args, double *)) = cached.f_position;
break;
}
case DEMUX_SET_POSITION:
{
setBufferingRunState(false); /* /!\ always stop buffering process first */
vlc_mutex_locker locker(&cached.lock);
if(cached.playlistLength == 0)
{
setBufferingRunState(true);
return VLC_EGENERIC;
}
double pos = va_arg(args, double);
bool accurate = va_arg(args, int);
vlc_tick_t seekTime = cached.playlistStart + cached.playlistLength * pos;
SeekDebug(msg_Dbg(p_demux, "Seek %f to %" PRId64 " plstart %" PRId64 " duration %" PRId64,
pos, seekTime, cached.playlistEnd, cached.playlistLength));
if(!setPosition(seekTime, pos, accurate))
{
setBufferingRunState(true);
return VLC_EGENERIC;
}
demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
demux.times = Times();
demux.firsttimes = Times();
demux.rebuffering = false;
cached.lastupdate = 0;
cached.i_normaltime = VLC_TICK_INVALID;
cached.i_time = VLC_TICK_INVALID;
setBufferingRunState(true);
break;
}
case DEMUX_SET_TIME:
{
setBufferingRunState(false); /* stop downloader first */
vlc_tick_t time = va_arg(args, vlc_tick_t);
bool accurate = va_arg(args, int);
if(!setPosition(time, -1, accurate))
{
setBufferingRunState(true);
return VLC_EGENERIC;
}
vlc_mutex_locker locker(&cached.lock);
demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
demux.times = Times();
demux.firsttimes = Times();
demux.rebuffering = false;
cached.lastupdate = 0;
cached.i_normaltime = VLC_TICK_INVALID;
cached.i_time = VLC_TICK_INVALID;
setBufferingRunState(true);
break;
}
case DEMUX_GET_PTS_DELAY:
*va_arg (args, vlc_tick_t *) = VLC_TICK_FROM_SEC(1);
break;
default:
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
void PlaylistManager::setBufferingRunState(bool b)
{
mutex_locker locker {lock};
b_buffering = b;
waitcond.signal();
}
void PlaylistManager::Run()
{
mutex_locker locker {lock};
const vlc_tick_t i_min_buffering = bufferingLogic->getMinBuffering(playlist);
const vlc_tick_t i_max_buffering = bufferingLogic->getMaxBuffering(playlist);
const vlc_tick_t i_target_buffering = bufferingLogic->getStableBuffering(playlist);
while(1)
{
while(!b_buffering && !b_canceled)
waitcond.wait(lock);
if (b_canceled)
break;
if(needsUpdate())
{
if(updatePlaylist())
scheduleNextUpdate();
else
failedupdates++;
}
vlc_mutex_lock(&demux.lock);
Times pcr = demux.times;
vlc_mutex_unlock(&demux.lock);
AbstractStream::BufferingStatus i_return = bufferize(pcr, i_min_buffering,
i_max_buffering, i_target_buffering);
if(i_return != AbstractStream::BufferingStatus::Lessthanmin)
{
vlc_tick_t i_deadline = vlc_tick_now();
if(i_return == AbstractStream::BufferingStatus::Ongoing)
i_deadline += VLC_TICK_FROM_MS(10);
else if(i_return == AbstractStream::BufferingStatus::Full)
i_deadline += VLC_TICK_FROM_MS(100);
else if(i_return == AbstractStream::BufferingStatus::End)
i_deadline += VLC_TICK_FROM_SEC(1);
else /*if(i_return == AbstractStream::BufferingStatus::suspended)*/
i_deadline += VLC_TICK_FROM_MS(250);
// TODO: The current function doesn't seem to modify shared
// state under demux lock.
vlc_cond_signal(&demux.cond);
while(b_buffering &&
waitcond.timedwait(lock, i_deadline) == 0 &&
i_deadline > vlc_tick_now() &&
!b_canceled);
if (b_canceled)
break;
}
}
}
void * PlaylistManager::managerThread(void *opaque)
{
vlc_thread_set_name("vlc-adapt-mngr");
static_cast<PlaylistManager *>(opaque)->Run();
return nullptr;
}
void PlaylistManager::updateControlsPosition()
{
vlc_mutex_locker locker(&cached.lock);
time_t now = time(nullptr);
if(now - cached.lastupdate < 1)
return;
cached.lastupdate = now;
for(AbstractStream* st : streams)
{
if(st->isValid() && !st->isDisabled() && st->isSelected())
{
if(st->getMediaPlaybackTimes(&cached.playlistStart, &cached.playlistEnd,
&cached.playlistLength))
break;
}
}
/*
* Relative position:
* -> Elapsed demux time (current demux time - first demux time)
* Since PlaylistTime != DemuxTime (HLS crap, TS):
* -> Use Playlist<->Demux time offset provided by EsOut
* to convert to elapsed playlist time.
* But this diff is not available until we have demuxed data...
* Fallback on relative seek from playlist start in that case
* But also playback might not have started at beginning of playlist
* -> Apply relative start from seek point (set on EsOut as ExpectedTime)
*
* All seeks need to be done in playlist time !
*/
Times currentTimes = getTimes();
Times startTimes = getTimes(true);
cached.b_live = playlist->isLive();
SeekDebug(msg_Dbg(p_demux, "playlist Start/End %" PRId64 "/%" PRId64 " len %" PRId64
"rap pl/demux (%" PRId64 "/%" PRId64 ")",
cached.playlistStart, cached.playlistEnd, cached.playlistEnd,
startTimes.segment.media, startTimes.segment.demux));
if(cached.b_live)
{
/* Special case for live until we can provide relative start to fully match
the above description */
cached.i_time = currentTimes.segment.media;
if(cached.playlistStart != cached.playlistEnd)
{
if(cached.playlistStart < 0) /* Live template. Range start = now() - buffering depth */
{
cached.playlistEnd = vlc_tick_from_sec(now);
cached.playlistStart = cached.playlistEnd - cached.playlistLength;
}
}
if(cached.i_time > VLC_TICK_0 + cached.playlistStart &&
cached.i_time <= VLC_TICK_0 + cached.playlistEnd && cached.playlistLength)
{
cached.f_position = ((double)(cached.i_time - VLC_TICK_0 - cached.playlistStart)) / cached.playlistLength;
}
else
{
cached.f_position = 0.0;
}
}
else
{
cached.i_normaltime = startTimes.continuous;
cached.i_normaltime += VLC_TICK_0 + cached.playlistStart - startTimes.segment.media;
if(playlist->duration > cached.playlistLength)
cached.playlistLength = playlist->duration;
if(cached.playlistLength && currentTimes.segment.media != VLC_TICK_INVALID)
{
cached.i_time = currentTimes.segment.media;
cached.f_position = (double) (cached.i_time - VLC_TICK_0 - cached.playlistStart) / cached.playlistLength;
}
else
{
cached.f_position = 0.0;
}
}
SeekDebug(msg_Dbg(p_demux, "cached.i_time (%" PRId64 ") cur %" PRId64 " rap start (pl %" PRId64 "/dmx %" PRId64 ") pos %f",
cached.i_time, currentTimes.continuous, startTimes.segment.media,
startTimes.segment.demux, cached.f_position));
}
AbstractAdaptationLogic *PlaylistManager::createLogic(AbstractAdaptationLogic::LogicType type, AbstractConnectionManager *conn)
{
vlc_object_t *obj = VLC_OBJECT(p_demux);
AbstractAdaptationLogic *logic = nullptr;
switch(type)
{
case AbstractAdaptationLogic::LogicType::FixedRate:
{
size_t bps = var_InheritInteger(p_demux, "adaptive-bw") * 8192;
logic = new (std::nothrow) FixedRateAdaptationLogic(obj, bps);
break;
}
case AbstractAdaptationLogic::LogicType::AlwaysLowest:
logic = new (std::nothrow) AlwaysLowestAdaptationLogic(obj);
break;
case AbstractAdaptationLogic::LogicType::AlwaysBest:
logic = new (std::nothrow) AlwaysBestAdaptationLogic(obj);
break;
case AbstractAdaptationLogic::LogicType::RateBased:
{
RateBasedAdaptationLogic *ratelogic =
new (std::nothrow) RateBasedAdaptationLogic(obj);
if(ratelogic)
conn->setDownloadRateObserver(ratelogic);
logic = ratelogic;
break;
}
case AbstractAdaptationLogic::LogicType::Default:
#ifdef ADAPTIVE_DEBUGGING_LOGIC
logic = new (std::nothrow) RoundRobinLogic(obj);
msg_Warn(p_demux, "using RoundRobinLogic every %u", RoundRobinLogic::QUANTUM);
break;
#endif
case AbstractAdaptationLogic::LogicType::NearOptimal:
{
NearOptimalAdaptationLogic *noplogic =
new (std::nothrow) NearOptimalAdaptationLogic(obj);
if(noplogic)
conn->setDownloadRateObserver(noplogic);
logic = noplogic;
break;
}
case AbstractAdaptationLogic::LogicType::Predictive:
{
AbstractAdaptationLogic *predictivelogic =
new (std::nothrow) PredictiveAdaptationLogic(obj);
if(predictivelogic)
conn->setDownloadRateObserver(predictivelogic);
logic = predictivelogic;
}
default:
break;
}
if(logic)
{
int w = var_InheritInteger(p_demux, "adaptive-maxwidth");
int h = var_InheritInteger(p_demux, "adaptive-maxheight");
if(h == 0)
{
h = var_InheritInteger(p_demux, "preferred-resolution");
/* Adapt for slightly different minimum/maximum semantics */
if(h == -1)
h = 0;
else if(h == 0)
h = 1;
}
logic->setMaxDeviceResolution(w, h);
}
return logic;
}
AbstractBufferingLogic *PlaylistManager::createBufferingLogic() const
{
DefaultBufferingLogic *bl = new DefaultBufferingLogic();
if(bl)
{
unsigned v = var_InheritInteger(p_demux, "adaptive-livedelay");
if(v)
bl->setUserLiveDelay(VLC_TICK_FROM_MS(v));
v = var_InheritInteger(p_demux, "adaptive-maxbuffer");
if(v)
bl->setUserMaxBuffering(VLC_TICK_FROM_MS(v));
}
return bl;
}
¤ Dauer der Verarbeitung: 0.6 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104856 |
| #Domains=417772 |
|
|