|
|
|
|
Quelle Streams.cpp
Sprache: C
|
|
/*
* Streams.cpp
*****************************************************************************
* Copyright (C) 2014 - 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 "Streams.hpp"
#include "logic/AbstractAdaptationLogic.h"
#include "http/HTTPConnection.hpp"
#include "http/HTTPConnectionManager.h"
#include "playlist/BaseAdaptationSet.h"
#include "playlist/BaseRepresentation.h"
#include "playlist/SegmentChunk.hpp"
#include "plumbing/SourceStream.hpp"
#include "plumbing/CommandsQueue.hpp"
#include "tools/Debug.hpp"
#include <vlc_demux.h>
#include <algorithm>
using namespace adaptive;
using namespace adaptive::http;
AbstractStream::AbstractStream(demux_t * demux_)
{
p_realdemux = demux_;
format = StreamFormat::Type::Unknown;
currentChunk = nullptr;
eof = false;
valid = true;
disabled = false;
contiguous = true;
segmentgap = false;
discontinuity = false;
needrestart = false;
inrestart = false;
demuxfirstchunk = false;
segmentTracker = nullptr;
demuxersource = nullptr;
demuxer = nullptr;
fakeesout = nullptr;
notfound_sequence = 0;
mightalwaysstartfromzero = false;
last_buffer_status = BufferingStatus::Lessthanmin;
currentrep.width = 0;
currentrep.height = 0;
vlc_mutex_init(&lock);
}
bool AbstractStream::init(const StreamFormat &format_, SegmentTracker *tracker)
{
/* Don't even try if not supported or already init */
if(format_ == StreamFormat::Type::Unsupported || demuxersource)
return false;
demuxersource = new (std::nothrow) BufferedChunksSourceStream( VLC_OBJECT(p_realdemux), this );
if(demuxersource)
{
CommandsFactory *factory = new (std::nothrow) CommandsFactory();
AbstractCommandsQueue *commandsqueue = new (std::nothrow) CommandsQueue();
if(factory && commandsqueue)
{
fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out,
commandsqueue, factory);
if(fakeesout)
{
/* All successful */
fakeesout->setExtraInfoProvider( this );
const Role & streamRole = tracker->getStreamRole();
if(streamRole.isDefault() && streamRole.autoSelectable())
fakeesout->setPriority(ES_PRIORITY_MIN + 10);
else if(!streamRole.autoSelectable())
fakeesout->setPriority(ES_PRIORITY_NOT_DEFAULTABLE);
format = format_;
segmentTracker = tracker;
segmentTracker->registerListener(this);
segmentTracker->notifyBufferingState(true);
if(mightalwaysstartfromzero)
fakeesout->setExpectedTimestamp(VLC_TICK_0 + segmentTracker->getPlaybackTime());
declaredCodecs();
return true;
}
}
delete factory;
delete commandsqueue;
delete demuxersource;
}
return false;
}
AbstractStream::~AbstractStream()
{
delete currentChunk;
if(segmentTracker)
segmentTracker->notifyBufferingState(false);
delete segmentTracker;
delete demuxer;
delete demuxersource;
delete fakeesout;
}
void AbstractStream::prepareRestart(bool b_discontinuity)
{
if(demuxer)
{
/* Enqueue Del Commands for all current ES */
demuxer->drain();
fakeEsOut()->resetTimestamps();
/* Enqueue Del Commands for all current ES */
fakeEsOut()->scheduleAllForDeletion();
if(b_discontinuity)
fakeEsOut()->schedulePCRReset();
fakeEsOut()->commandsQueue()->Commit();
/* ignoring demuxer's own Del commands */
fakeEsOut()->commandsQueue()->setDrop(true);
delete demuxer;
fakeEsOut()->commandsQueue()->setDrop(false);
demuxer = nullptr;
}
}
bool AbstractStream::resetForNewPosition(vlc_tick_t seekMediaTime)
{
// clear eof flag before restartDemux() to prevent readNextBlock() fail
eof = false;
demuxfirstchunk = true;
notfound_sequence = 0;
last_buffer_status = BufferingStatus::Lessthanmin;
inrestart = false;
needrestart = false;
discontinuity = false;
if(!demuxer || demuxer->needsRestartOnSeek()) /* needs (re)start */
{
delete currentChunk;
currentChunk = nullptr;
needrestart = false;
segmentgap = false;
fakeEsOut()->resetTimestamps();
fakeEsOut()->commandsQueue()->Abort( true );
startTimeContext = SegmentTimes();
currentTimeContext = SegmentTimes();
prevEndTimeContext = SegmentTimes();
currentChunk = getNextChunk();
if(mightalwaysstartfromzero)
fakeEsOut()->setExpectedTimestamp(VLC_TICK_0 + seekMediaTime);
if( !restartDemux() )
{
msg_Info(p_realdemux, "Restart demux failed");
eof = true;
valid = false;
return false;
}
else
{
fakeEsOut()->commandsQueue()->setEOF(false);
}
}
else fakeEsOut()->commandsQueue()->Abort( true );
return true;
}
void AbstractStream::setLanguage(const std::string &lang)
{
language = lang;
}
void AbstractStream::setDescription(const std::string &desc)
{
description = desc;
}
vlc_tick_t AbstractStream::getMinAheadTime() const
{
if(!segmentTracker)
return 0;
return segmentTracker->getMinAheadTime();
}
Times AbstractStream::getFirstTimes() const
{
vlc_mutex_locker locker(&lock);
if(!valid || disabled)
return Times();
Times times = fakeEsOut()->commandsQueue()->getFirstTimes();
if(times.continuous == VLC_TICK_INVALID)
times = fakeEsOut()->commandsQueue()->getPCR();
return times;
}
int AbstractStream::esCount() const
{
return fakeEsOut()->esCount();
}
bool AbstractStream::seekAble() const
{
bool restarting = fakeEsOut()->restarting();
bool draining = fakeEsOut()->commandsQueue()->isDraining();
bool eof = fakeEsOut()->commandsQueue()->isEOF();
msg_Dbg(p_realdemux, "demuxer %p, fakeesout restarting %d, "
"discontinuity %d, commandsqueue draining %d, commandsqueue eof %d",
static_cast<void *>(demuxer), restarting, discontinuity, draining, eof);
if(!valid || restarting || discontinuity || (!eof && draining))
{
msg_Warn(p_realdemux, "not seekable");
return false;
}
else
{
return true;
}
}
bool AbstractStream::isSelected() const
{
return fakeEsOut()->hasSelectedEs();
}
AbstractStream::StreamPosition::StreamPosition()
{
number = std::numeric_limits<uint64_t>::max();
pos = -1;
}
bool AbstractStream::reactivate(const StreamPosition &pos)
{
vlc_mutex_locker locker(&lock);
if(setPosition(pos, false))
{
setDisabled(false);
return true;
}
else
{
eof = true; /* can't reactivate */
return false;
}
}
bool AbstractStream::startDemux()
{
if(demuxer)
return false;
if(!currentChunk)
{
segmentgap = false;
currentChunk = getNextChunk();
needrestart = false;
discontinuity = false;
}
demuxersource->Reset();
demuxfirstchunk = true;
fakeEsOut()->setPCRTrusted(format != StreamFormat::Type::MPEG2TS);
demuxer = createDemux(format);
if(!demuxer && format != StreamFormat())
msg_Err(p_realdemux, "Failed to create demuxer %p %s", (void *)demuxer,
format.str().c_str());
return !!demuxer;
}
bool AbstractStream::restartDemux()
{
bool b_ret = true;
if(!demuxer)
{
fakeesout->recycleAll();
b_ret = startDemux();
}
else if(demuxer->needsRestartOnSeek())
{
inrestart = true;
/* Push all ES as recycling candidates */
fakeEsOut()->recycleAll();
/* Restart with ignoring es_Del pushes to queue when terminating demux */
fakeEsOut()->commandsQueue()->setDrop(true);
demuxer->destroy();
fakeEsOut()->commandsQueue()->setDrop(false);
b_ret = demuxer->create();
inrestart = false;
}
else
{
fakeEsOut()->commandsQueue()->Commit();
}
return b_ret;
}
void AbstractStream::setDisabled(bool b)
{
if(disabled != b)
segmentTracker->notifyBufferingState(!b);
disabled = b;
}
bool AbstractStream::isValid() const
{
vlc_mutex_locker locker(&lock);
return valid;
}
bool AbstractStream::isDisabled() const
{
vlc_mutex_locker locker(&lock);
return disabled;
}
void AbstractStream::setLivePause(bool b)
{
vlc_mutex_locker locker(&lock);
if(!b)
{
segmentTracker->setPosition(segmentTracker->getStartPosition(),
!demuxer || demuxer->needsRestartOnSeek());
}
}
vlc_tick_t AbstractStream::getDemuxedAmount(Times from) const
{
vlc_tick_t i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(from).continuous;
if(contiguous)
{
vlc_tick_t i_media_demuxed = fakeEsOut()->commandsQueue()->getDemuxedMediaAmount(from).segment.media;
if(i_media_demuxed > i_demuxed)
i_demuxed = i_media_demuxed;
}
return i_demuxed;
}
bool AbstractStream::isBufferingSufficient(Times from, vlc_tick_t minimum) const
{
vlc_mutex_locker locker(&lock);
if(!valid || disabled || fakeEsOut()->commandsQueue()->isEOF())
return true;
return getDemuxedAmount(from) >= minimum;
}
AbstractStream::BufferingStatus
AbstractStream::getBufferAndStatus(const Times &deadline,
vlc_tick_t i_min_buffering,
vlc_tick_t i_max_buffering,
vlc_tick_t *pi_demuxed)
{
if(last_buffer_status == BufferingStatus::End)
return BufferingStatus::End;
*pi_demuxed = getDemuxedAmount(deadline);
if(*pi_demuxed < i_max_buffering) /* need to read more */
{
if(*pi_demuxed < i_min_buffering)
return BufferingStatus::Lessthanmin; /* high prio */
return BufferingStatus::Ongoing;
}
return BufferingStatus::Full;
}
AbstractStream::BufferingStatus AbstractStream::bufferize(Times deadline,
vlc_tick_t i_min_buffering,
vlc_tick_t i_extra_buffering,
vlc_tick_t i_target_buffering,
bool b_keep_alive)
{
last_buffer_status = doBufferize(deadline, i_min_buffering, i_extra_buffering,
i_target_buffering, b_keep_alive);
return last_buffer_status;
}
AbstractStream::BufferingStatus AbstractStream::doBufferize(Times deadline,
vlc_tick_t i_min_buffering,
vlc_tick_t i_max_buffering,
vlc_tick_t i_target_buffering,
bool b_keep_alive)
{
vlc_mutex_lock(&lock);
/* Ensure it is configured */
if(!segmentTracker || !valid)
{
vlc_mutex_unlock(&lock);
return BufferingStatus::End;
}
/* Disable streams that are not selected (alternate streams) */
if(esCount() && !isSelected() && !fakeEsOut()->restarting() && !b_keep_alive)
{
setDisabled(true);
segmentTracker->reset();
fakeEsOut()->commandsQueue()->Abort(false);
msg_Dbg(p_realdemux, "deactivating %s stream %s",
format.str().c_str(), description.c_str());
vlc_mutex_unlock(&lock);
return BufferingStatus::End;
}
if(fakeEsOut()->commandsQueue()->isDraining())
{
vlc_mutex_unlock(&lock);
return BufferingStatus::Suspended;
}
segmentTracker->setStartPosition();
/* Reached end of live playlist */
if(!segmentTracker->bufferingAvailable())
{
vlc_mutex_unlock(&lock);
return BufferingStatus::Suspended;
}
if(!contiguous)
{
if(!fakeEsOut()->hasSynchronizationReference())
{
if(!demuxer)
{
/* We always need a prepared chunk info for querying a syncref */
if(!currentChunk)
{
currentChunk = getNextChunk();
if(!currentChunk)
{
vlc_mutex_unlock(&lock);
return BufferingStatus::End;
}
segmentgap = false;
needrestart = false;
discontinuity = false;
}
}
SynchronizationReference r;
if(segmentTracker->getSynchronizationReference(currentSequence, startTimeContext.media, r))
{
fakeEsOut()->setSynchronizationReference(r);
}
else
{
msg_Dbg(p_realdemux, "Waiting sync reference for seq %" PRIu64, currentSequence);
vlc_mutex_unlock(&lock);
return BufferingStatus::Suspended;
}
}
}
if(!demuxer)
{
if(!startDemux())
{
valid = false; /* Prevent further retries */
fakeEsOut()->commandsQueue()->setEOF(true);
vlc_mutex_unlock(&lock);
return BufferingStatus::End;
}
}
vlc_tick_t i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(deadline).continuous;
if(!contiguous && prevEndTimeContext.media != VLC_TICK_INVALID
&& deadline.segment.media != VLC_TICK_INVALID)
{
vlc_tick_t i_mediaamount = fakeEsOut()->commandsQueue()->getDemuxedMediaAmount(deadline).segment.media;
if(i_mediaamount > i_demuxed)
i_demuxed = i_mediaamount;
}
segmentTracker->notifyBufferingLevel(i_min_buffering, i_max_buffering, i_demuxed, i_target_buffering);
if(i_demuxed < i_max_buffering) /* not already demuxed */
{
Times extdeadline = fakeEsOut()->commandsQueue()->getBufferingLevel();
extdeadline.offsetBy((i_max_buffering - i_demuxed) / 4);
Times newdeadline = deadline;
newdeadline.offsetBy(VLC_TICK_FROM_SEC(1));
if(extdeadline.continuous < newdeadline.continuous)
deadline = extdeadline;
else
deadline = newdeadline;
/* need to read, demuxer still buffering, ... */
vlc_mutex_unlock(&lock);
Demuxer::Status demuxStatus = demuxer->demux(deadline.continuous);
fakeEsOut()->scheduleNecessaryMilestone();
vlc_mutex_lock(&lock);
if(demuxStatus != Demuxer::Status::Success)
{
if(discontinuity || needrestart)
{
msg_Dbg(p_realdemux, "Restarting demuxer %d %d", needrestart, discontinuity);
prepareRestart(discontinuity);
if(discontinuity)
{
msg_Dbg(p_realdemux, "Draining on discontinuity");
fakeEsOut()->commandsQueue()->setDraining();
fakeEsOut()->setSegmentStartTimes(startTimeContext);
assert(startTimeContext.media);
}
assert(startTimeContext.media);
if(!fakeEsOut()->hasSegmentStartTimes())
fakeEsOut()->setSegmentStartTimes(startTimeContext);
if(!fakeEsOut()->hasSynchronizationReference())
{
SynchronizationReference r(currentSequence, Times());
fakeEsOut()->setSynchronizationReference(r);
}
discontinuity = false;
needrestart = false;
vlc_mutex_unlock(&lock);
return BufferingStatus::Ongoing;
}
fakeEsOut()->commandsQueue()->setEOF(true);
vlc_mutex_unlock(&lock);
return BufferingStatus::End;
}
if(deadline.continuous != VLC_TICK_INVALID)
{
i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(deadline).continuous;
segmentTracker->notifyBufferingLevel(i_min_buffering, i_max_buffering, i_demuxed, i_target_buffering);
}
else
{
/* On initial pass, there's no demux time known, we need to fake it */
if(fakeEsOut()->commandsQueue()->getBufferingLevel().continuous != VLC_TICK_INVALID)
i_demuxed = i_min_buffering;
}
}
vlc_mutex_unlock(&lock);
Times first = fakeEsOut()->commandsQueue()->getFirstTimes();
if(contiguous && first.continuous != VLC_TICK_INVALID && first.segment.demux != VLC_TICK_INVALID)
segmentTracker->updateSynchronizationReference(currentSequence, first);
if(i_demuxed < i_max_buffering) /* need to read more */
{
if(i_demuxed < i_min_buffering)
return BufferingStatus::Lessthanmin; /* high prio */
return BufferingStatus::Ongoing;
}
return BufferingStatus::Full;
}
AbstractStream::Status AbstractStream::dequeue(Times deadline, Times *times)
{
vlc_mutex_locker locker(&lock);
if(fakeEsOut()->commandsQueue()->isDraining())
{
AdvDebug(vlc_tick_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR().continuous;
vlc_tick_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstTimes().continuous;
vlc_tick_t bufferingLevel = fakeEsOut()->commandsQueue()->getBufferingLevel().continuous;
msg_Dbg(p_realdemux, "Stream pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64 "(+%" PRId64 ") [DRAINING] :%s",
pcrvalue, dtsvalue, deadline.continuous, bufferingLevel,
pcrvalue ? bufferingLevel - pcrvalue : 0,
description.c_str()));
*times = fakeEsOut()->commandsQueue()->Process(deadline);
if(!fakeEsOut()->commandsQueue()->isEmpty())
return Status::Demuxed;
if(!fakeEsOut()->commandsQueue()->isEOF())
{
fakeEsOut()->commandsQueue()->Abort(true); /* reset buffering level and flags */
return Status::Discontinuity;
}
}
if(!valid || disabled || fakeEsOut()->commandsQueue()->isEOF())
{
*times = deadline;
return Status::Eof;
}
vlc_tick_t bufferingLevel = fakeEsOut()->commandsQueue()->getBufferingLevel().continuous;
AdvDebug(vlc_tick_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR().continuous;
vlc_tick_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstTimes().continuous;
msg_Dbg(p_realdemux, "Stream pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64 "(+%" PRId64 "): %s",
pcrvalue, dtsvalue, deadline.continuous, bufferingLevel,
pcrvalue ? bufferingLevel - pcrvalue : 0,
description.c_str()));
if(deadline.continuous <= bufferingLevel) /* demuxed */
{
*times = fakeEsOut()->commandsQueue()->Process(deadline);
return Status::Demuxed;
}
else if(!contiguous &&
fakeEsOut()->commandsQueue()->getDemuxedMediaAmount(deadline).segment.media > 0)
{
*times = deadline;
fakeEsOut()->commandsQueue()->Process(Times()); /* handle untimed events (es add) */
return Status::Demuxed;
}
return Status::Buffering;
}
ChunkInterface * AbstractStream::getNextChunk() const
{
const bool b_restarting = fakeEsOut()->restarting();
ChunkInterface *ck = segmentTracker->getNextChunk(!b_restarting);
if(ck && !fakeEsOut()->hasSegmentStartTimes())
fakeEsOut()->setSegmentStartTimes(startTimeContext);
if(ck && !fakeEsOut()->hasSynchronizationReference())
{
if(!fakeEsOut()->hasSegmentStartTimes())
return ck;
assert(fakeEsOut()->hasSegmentStartTimes());
SynchronizationReference r;
if(segmentTracker->getSynchronizationReference(currentSequence, startTimeContext.media, r))
fakeEsOut()->setSynchronizationReference(r);
}
return ck;
}
block_t * AbstractStream::readNextBlock()
{
if (currentChunk == nullptr && !eof)
{
segmentgap = false;
currentChunk = getNextChunk();
}
if(demuxfirstchunk)
{
/* clear up discontinuity on demux start (discontinuity on start segment bug) */
discontinuity = false;
needrestart = false;
fakeesout->setSrcID(nextSrcID);
}
else if(discontinuity || needrestart)
{
msg_Info(p_realdemux, "Ending demuxer stream. %s%s",
discontinuity ? "[discontinuity]" : "",
needrestart ? "[needrestart]" : "");
/* Force stream/demuxer to end for this call */
return nullptr;
}
if(currentChunk == nullptr)
{
eof = true;
return nullptr;
}
const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
block_t *block = currentChunk->readBlock();
if(block == nullptr)
{
if(currentChunk->getRequestStatus() == RequestStatus::NotFound &&
++notfound_sequence < 3)
{
segmentgap = true;
}
delete currentChunk;
currentChunk = nullptr;
return nullptr;
}
else notfound_sequence = 0;
demuxfirstchunk = false;
if (!currentChunk->hasMoreData())
{
delete currentChunk;
currentChunk = nullptr;
}
block = checkBlock(block, b_segment_head_chunk);
return block;
}
bool AbstractStream::setPosition(const StreamPosition &pos, bool tryonly)
{
if(!seekAble())
return false;
bool b_needs_restart = demuxer ? demuxer->needsRestartOnSeek() : true;
bool ret = segmentTracker->setPositionByTime(pos.times.segment.media,
b_needs_restart, tryonly);
if(!tryonly && ret)
{
// in some cases, media time seek != sent dts
// es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
// VLC_TICK_0 + time);
}
return ret;
}
bool AbstractStream::getMediaPlaybackTimes(vlc_tick_t *start, vlc_tick_t *end,
vlc_tick_t *length) const
{
return segmentTracker->getMediaPlaybackRange(start, end, length);
}
bool AbstractStream::getMediaAdvanceAmount(vlc_tick_t *duration) const
{
if(startTimeContext.media == VLC_TICK_INVALID)
return false;
*duration = currentTimeContext.media - startTimeContext.media;
return true;
}
bool AbstractStream::runUpdates(bool)
{
if(!valid)
return false;
if(!disabled)
return segmentTracker->updateSelected();
else
return false;
}
void AbstractStream::fillExtraFMTInfo( es_format_t *p_fmt ) const
{
if(!p_fmt->psz_language && !language.empty())
p_fmt->psz_language = strdup(language.c_str());
if(!p_fmt->psz_description && !description.empty())
p_fmt->psz_description = strdup(description.c_str());
if( p_fmt->i_cat == VIDEO_ES && p_fmt->video.i_visible_width == 0)
{
p_fmt->video.i_visible_width = currentrep.width;
p_fmt->video.i_visible_height = currentrep.height;
}
}
block_t *AbstractStream::checkBlock(block_t *p_block, bool)
{
return p_block;
}
AbstractDemuxer * AbstractStream::createDemux(const StreamFormat &format)
{
AbstractDemuxer *ret = newDemux( VLC_OBJECT(p_realdemux), format,
(es_out_t *)fakeEsOut(), demuxersource );
if(ret && !ret->create())
{
delete ret;
ret = nullptr;
}
else fakeEsOut()->commandsQueue()->Commit();
return ret;
}
AbstractDemuxer *AbstractStream::newDemux(vlc_object_t *p_obj, const StreamFormat &format,
es_out_t *out, AbstractSourceStream *source) const
{
AbstractDemuxer *ret = nullptr;
switch(format)
{
case StreamFormat::Type::MP4:
ret = new Demuxer(p_obj, "mp4", out, source);
break;
case StreamFormat::Type::MPEG2TS:
ret = new Demuxer(p_obj, "ts", out, source);
break;
default:
case StreamFormat::Type::Unsupported:
break;
}
return ret;
}
void AbstractStream::trackerEvent(const TrackerEvent &ev)
{
switch(ev.getType())
{
case TrackerEvent::Type::Discontinuity:
{
const DiscontinuityEvent &event =
static_cast<const DiscontinuityEvent &>(ev);
discontinuity = true;
currentSequence = event.discontinuitySequenceNumber;
nextSrcID = SrcID::make();
}
break;
case TrackerEvent::Type::SegmentGap:
segmentgap = true;
prevEndTimeContext = SegmentTimes();
currentTimeContext = SegmentTimes(); /* fired before segmentchanged */
break;
case TrackerEvent::Type::FormatChange:
{
const FormatChangedEvent &event =
static_cast<const FormatChangedEvent &>(ev);
/* Check if our current demux is still valid */
if(*event.format != format)
{
/* Format has changed between segments, we need to drain and change demux */
msg_Info(p_realdemux, "Changing stream format %s -> %s",
format.str().c_str(), event.format->str().c_str());
format = *event.format;
needrestart = true;
}
}
break;
case TrackerEvent::Type::RepresentationSwitch:
{
const RepresentationSwitchEvent &event =
static_cast<const RepresentationSwitchEvent &>(ev);
if(demuxer && !inrestart && event.prev)
{
if(!demuxer->bitstreamSwitchCompatible() ||
/* HLS variants can move from TS to Raw AAC */
format == StreamFormat(StreamFormat::Type::Unknown) ||
(event.next &&
!event.next->getAdaptationSet()->isBitSwitchable()))
needrestart = true;
}
AdvDebug(msg_Dbg(p_realdemux, "Stream %s switching %s %s to %s %s",
description.c_str(),
event.prev ? event.prev->getID().str().c_str() : "",
event.prev ? event.prev->getStreamFormat().str().c_str() : "",
event.next ? event.next->getID().str().c_str() : "",
event.next ? event.next->getStreamFormat().str().c_str() : ""));
if(event.next)
{
nextSrcID = SrcID::make();
currentrep.width = event.next->getWidth() > 0 ? event.next->getWidth() : 0;
currentrep.height = event.next->getHeight() > 0 ? event.next->getHeight() : 0;
}
else
{
currentrep.width = 0;
currentrep.height = 0;
}
}
break;
case TrackerEvent::Type::RepresentationUpdated:
{
if(last_buffer_status == BufferingStatus::Suspended)
last_buffer_status = BufferingStatus::Lessthanmin;
}
break;
case TrackerEvent::Type::RepresentationUpdateFailed:
{
fakeEsOut()->commandsQueue()->setEOF(true);
msg_Err(p_realdemux, "Could not update %s anymore, disabling", description.c_str());
}
break;
case TrackerEvent::Type::SegmentChange:
{
const SegmentChangedEvent &event =
static_cast<const SegmentChangedEvent &>(ev);
if(demuxer && demuxer->needsRestartOnEachSegment() && !inrestart)
{
needrestart = true;
}
prevEndTimeContext = currentTimeContext;
prevEndTimeContext.offsetBy(currentDuration);
fakeEsOut()->setSegmentProgressTimes(prevEndTimeContext);
currentTimeContext.media = event.starttime;
currentTimeContext.display = event.displaytime;
currentSequence = event.sequence;
currentDuration = event.duration;
if(startTimeContext.media == VLC_TICK_INVALID)
startTimeContext = currentTimeContext;
}
break;
case TrackerEvent::Type::PositionChange:
{
const PositionChangedEvent &event =
static_cast<const PositionChangedEvent &>(ev);
resetForNewPosition(event.resumeTime);
}
break;
default:
break;
}
}
void AbstractStream::declaredCodecs()
{
CodecDescriptionList descs;
segmentTracker->getCodecsDesc(&descs);
for(auto it = descs.cbegin(); it != descs.cend(); ++it)
{
const es_format_t *fmt = (*it)->getFmt();
if(fmt->i_cat != UNKNOWN_ES)
fakeEsOut()->declareEs(fmt);
}
}
FakeESOut::LockedFakeEsOut AbstractStream::fakeEsOut()
{
return fakeesout->WithLock();
}
FakeESOut::LockedFakeEsOut AbstractStream::fakeEsOut() const
{
return fakeesout->WithLock();
}
¤ Dauer der Verarbeitung: 0.5 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 |
|
|