You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
22 KiB
730 lines
22 KiB
/*
|
|
* 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/FormatNamespace.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::UNKNOWN;
|
|
currentChunk = NULL;
|
|
eof = false;
|
|
valid = true;
|
|
disabled = false;
|
|
discontinuity = false;
|
|
needrestart = false;
|
|
inrestart = false;
|
|
demuxfirstchunk = false;
|
|
segmentTracker = NULL;
|
|
demuxersource = NULL;
|
|
demuxer = NULL;
|
|
fakeesout = NULL;
|
|
notfound_sequence = 0;
|
|
last_buffer_status = buffering_lessthanmin;
|
|
vlc_mutex_init(&lock);
|
|
}
|
|
|
|
bool AbstractStream::init(const StreamFormat &format_, SegmentTracker *tracker, AbstractConnectionManager *conn)
|
|
{
|
|
/* Don't even try if not supported or already init */
|
|
if((unsigned)format_ == StreamFormat::UNSUPPORTED || demuxersource)
|
|
return false;
|
|
|
|
demuxersource = new (std::nothrow) BufferedChunksSourceStream( VLC_OBJECT(p_realdemux), this );
|
|
if(demuxersource)
|
|
{
|
|
CommandsFactory *factory = new (std::nothrow) CommandsFactory();
|
|
if(factory)
|
|
{
|
|
CommandsQueue *commandsqueue = new (std::nothrow) CommandsQueue(factory);
|
|
if(commandsqueue)
|
|
{
|
|
fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out, commandsqueue);
|
|
if(fakeesout)
|
|
{
|
|
/* All successfull */
|
|
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);
|
|
connManager = conn;
|
|
fakeesout->setExpectedTimestamp(segmentTracker->getPlaybackTime());
|
|
declaredCodecs();
|
|
return true;
|
|
}
|
|
delete commandsqueue;
|
|
commandsqueue = NULL;
|
|
}
|
|
else
|
|
{
|
|
delete factory;
|
|
}
|
|
}
|
|
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 = NULL;
|
|
}
|
|
}
|
|
|
|
void AbstractStream::setLanguage(const std::string &lang)
|
|
{
|
|
language = lang;
|
|
}
|
|
|
|
void AbstractStream::setDescription(const std::string &desc)
|
|
{
|
|
description = desc;
|
|
}
|
|
|
|
vlc_tick_t AbstractStream::getPCR() const
|
|
{
|
|
vlc_mutex_locker locker(&lock);
|
|
if(!valid || disabled)
|
|
return VLC_TICK_INVALID;
|
|
return fakeEsOut()->commandsQueue()->getPCR();
|
|
}
|
|
|
|
vlc_tick_t AbstractStream::getMinAheadTime() const
|
|
{
|
|
if(!segmentTracker)
|
|
return 0;
|
|
return segmentTracker->getMinAheadTime();
|
|
}
|
|
|
|
vlc_tick_t AbstractStream::getFirstDTS() const
|
|
{
|
|
vlc_mutex_locker locker(&lock);
|
|
|
|
if(!valid || disabled)
|
|
return VLC_TICK_INVALID;
|
|
|
|
vlc_tick_t dts = fakeEsOut()->commandsQueue()->getFirstDTS();
|
|
if(dts == VLC_TICK_INVALID)
|
|
dts = fakeEsOut()->commandsQueue()->getPCR();
|
|
return dts;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool AbstractStream::reactivate(vlc_tick_t basetime)
|
|
{
|
|
vlc_mutex_locker locker(&lock);
|
|
if(setPosition(basetime, false))
|
|
{
|
|
setDisabled(false);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
eof = true; /* can't reactivate */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool AbstractStream::startDemux()
|
|
{
|
|
if(demuxer)
|
|
return false;
|
|
|
|
demuxersource->Reset();
|
|
demuxer = createDemux(format);
|
|
if(!demuxer && format != StreamFormat())
|
|
msg_Err(p_realdemux, "Failed to create demuxer %p %s", (void *)demuxer,
|
|
format.str().c_str());
|
|
demuxfirstchunk = true;
|
|
|
|
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;
|
|
}
|
|
|
|
bool AbstractStream::decodersDrained()
|
|
{
|
|
return fakeEsOut()->decodersDrained();
|
|
}
|
|
|
|
AbstractStream::buffering_status AbstractStream::getLastBufferStatus() const
|
|
{
|
|
return last_buffer_status;
|
|
}
|
|
|
|
vlc_tick_t AbstractStream::getDemuxedAmount() const
|
|
{
|
|
return fakeEsOut()->commandsQueue()->getDemuxedAmount();
|
|
}
|
|
|
|
AbstractStream::buffering_status AbstractStream::bufferize(vlc_tick_t nz_deadline,
|
|
vlc_tick_t i_min_buffering, vlc_tick_t i_extra_buffering)
|
|
{
|
|
last_buffer_status = doBufferize(nz_deadline, i_min_buffering, i_extra_buffering);
|
|
return last_buffer_status;
|
|
}
|
|
|
|
AbstractStream::buffering_status AbstractStream::doBufferize(vlc_tick_t nz_deadline,
|
|
vlc_tick_t i_min_buffering, vlc_tick_t i_extra_buffering)
|
|
{
|
|
vlc_mutex_lock(&lock);
|
|
|
|
/* Ensure it is configured */
|
|
if(!segmentTracker || !connManager || !valid)
|
|
{
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_end;
|
|
}
|
|
|
|
/* Disable streams that are not selected (alternate streams) */
|
|
if(esCount() && !isSelected() && !fakeEsOut()->restarting())
|
|
{
|
|
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 AbstractStream::buffering_end;
|
|
}
|
|
|
|
if(fakeEsOut()->commandsQueue()->isDraining())
|
|
{
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_suspended;
|
|
}
|
|
|
|
if(!demuxer)
|
|
{
|
|
format = segmentTracker->getCurrentFormat();
|
|
if(!startDemux())
|
|
{
|
|
/* If demux fails because of probing failure / wrong format*/
|
|
if(discontinuity)
|
|
{
|
|
msg_Dbg( p_realdemux, "Draining on format change" );
|
|
prepareRestart();
|
|
discontinuity = false;
|
|
fakeEsOut()->commandsQueue()->setDraining();
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_ongoing;
|
|
}
|
|
valid = false; /* Prevent further retries */
|
|
fakeEsOut()->commandsQueue()->setEOF(true);
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_end;
|
|
}
|
|
}
|
|
|
|
const vlc_tick_t i_total_buffering = i_min_buffering + i_extra_buffering;
|
|
|
|
vlc_tick_t i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount();
|
|
segmentTracker->notifyBufferingLevel(i_min_buffering, i_demuxed, i_total_buffering);
|
|
if(i_demuxed < i_total_buffering) /* not already demuxed */
|
|
{
|
|
if(!segmentTracker->segmentsListReady()) /* Live Streams */
|
|
{
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_suspended;
|
|
}
|
|
|
|
vlc_tick_t nz_extdeadline = fakeEsOut()->commandsQueue()->getBufferingLevel() +
|
|
(i_total_buffering - i_demuxed) / 4;
|
|
nz_deadline = std::max(nz_deadline, nz_extdeadline);
|
|
|
|
/* need to read, demuxer still buffering, ... */
|
|
vlc_mutex_unlock(&lock);
|
|
Demuxer::Status demuxStatus = demuxer->demux(nz_deadline);
|
|
vlc_mutex_lock(&lock);
|
|
if(demuxStatus != Demuxer::Status::STATUS_SUCCESS)
|
|
{
|
|
if(discontinuity || needrestart)
|
|
{
|
|
msg_Dbg(p_realdemux, "Restarting demuxer");
|
|
prepareRestart(discontinuity);
|
|
if(discontinuity)
|
|
{
|
|
msg_Dbg(p_realdemux, "Draining on discontinuity");
|
|
fakeEsOut()->commandsQueue()->setDraining();
|
|
discontinuity = false;
|
|
}
|
|
needrestart = false;
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_ongoing;
|
|
}
|
|
fakeEsOut()->commandsQueue()->setEOF(true);
|
|
vlc_mutex_unlock(&lock);
|
|
return AbstractStream::buffering_end;
|
|
}
|
|
i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount();
|
|
segmentTracker->notifyBufferingLevel(i_min_buffering, i_demuxed, i_total_buffering);
|
|
}
|
|
vlc_mutex_unlock(&lock);
|
|
|
|
if(i_demuxed < i_total_buffering) /* need to read more */
|
|
{
|
|
if(i_demuxed < i_min_buffering)
|
|
return AbstractStream::buffering_lessthanmin; /* high prio */
|
|
return AbstractStream::buffering_ongoing;
|
|
}
|
|
return AbstractStream::buffering_full;
|
|
}
|
|
|
|
AbstractStream::status AbstractStream::dequeue(vlc_tick_t nz_deadline, vlc_tick_t *pi_pcr)
|
|
{
|
|
vlc_mutex_locker locker(&lock);
|
|
|
|
*pi_pcr = nz_deadline;
|
|
|
|
if(fakeEsOut()->commandsQueue()->isDraining())
|
|
{
|
|
AdvDebug(vlc_tick_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR();
|
|
vlc_tick_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstDTS();
|
|
msg_Dbg(p_realdemux, "Stream %s pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " [DRAINING]",
|
|
description.c_str(), pcrvalue, dtsvalue, nz_deadline));
|
|
|
|
*pi_pcr = fakeEsOut()->commandsQueue()->Process(p_realdemux->out, VLC_TICK_0 + nz_deadline);
|
|
if(!fakeEsOut()->commandsQueue()->isEmpty())
|
|
return AbstractStream::status_demuxed;
|
|
|
|
if(!fakeEsOut()->commandsQueue()->isEOF())
|
|
{
|
|
fakeEsOut()->commandsQueue()->Abort(true); /* reset buffering level and flags */
|
|
return AbstractStream::status_discontinuity;
|
|
}
|
|
}
|
|
|
|
if(!valid || disabled || fakeEsOut()->commandsQueue()->isEOF())
|
|
{
|
|
*pi_pcr = nz_deadline;
|
|
return AbstractStream::status_eof;
|
|
}
|
|
|
|
vlc_tick_t bufferingLevel = fakeEsOut()->commandsQueue()->getBufferingLevel();
|
|
|
|
AdvDebug(vlc_tick_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR();
|
|
vlc_tick_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstDTS();
|
|
msg_Dbg(p_realdemux, "Stream %s pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64,
|
|
description.c_str(), pcrvalue, dtsvalue, nz_deadline, bufferingLevel));
|
|
|
|
if(nz_deadline + VLC_TICK_0 <= bufferingLevel) /* demuxed */
|
|
{
|
|
*pi_pcr = fakeEsOut()->commandsQueue()->Process( p_realdemux->out, VLC_TICK_0 + nz_deadline );
|
|
return AbstractStream::status_demuxed;
|
|
}
|
|
|
|
return AbstractStream::status_buffering;
|
|
}
|
|
|
|
std::string AbstractStream::getContentType()
|
|
{
|
|
if (currentChunk == NULL && !eof)
|
|
currentChunk = segmentTracker->getNextChunk(!fakeEsOut()->restarting(), connManager);
|
|
if(currentChunk)
|
|
return currentChunk->getContentType();
|
|
else
|
|
return std::string();
|
|
}
|
|
|
|
block_t * AbstractStream::readNextBlock()
|
|
{
|
|
if (currentChunk == NULL && !eof)
|
|
currentChunk = segmentTracker->getNextChunk(!fakeEsOut()->restarting(), connManager);
|
|
|
|
if(discontinuity && demuxfirstchunk)
|
|
{
|
|
/* clear up discontinuity on demux start (discontinuity on start segment bug) */
|
|
discontinuity = false;
|
|
}
|
|
|
|
if(discontinuity || needrestart)
|
|
{
|
|
msg_Info(p_realdemux, "Encountered discontinuity");
|
|
/* Force stream/demuxer to end for this call */
|
|
return NULL;
|
|
}
|
|
|
|
if(currentChunk == NULL)
|
|
{
|
|
eof = true;
|
|
return NULL;
|
|
}
|
|
|
|
const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
|
|
|
|
block_t *block = currentChunk->readBlock();
|
|
if(block == NULL)
|
|
{
|
|
if(currentChunk->getRequestStatus() == RequestStatus::NotFound &&
|
|
++notfound_sequence < 3)
|
|
{
|
|
discontinuity = true;
|
|
}
|
|
delete currentChunk;
|
|
currentChunk = NULL;
|
|
return NULL;
|
|
}
|
|
else notfound_sequence = 0;
|
|
|
|
demuxfirstchunk = false;
|
|
|
|
if (currentChunk->isEmpty())
|
|
{
|
|
delete currentChunk;
|
|
currentChunk = NULL;
|
|
}
|
|
|
|
block = checkBlock(block, b_segment_head_chunk);
|
|
|
|
return block;
|
|
}
|
|
|
|
bool AbstractStream::setPosition(vlc_tick_t time, bool tryonly)
|
|
{
|
|
if(!seekAble())
|
|
return false;
|
|
|
|
bool b_needs_restart = demuxer ? demuxer->needsRestartOnSeek() : true;
|
|
bool ret = segmentTracker->setPositionByTime(time, b_needs_restart, tryonly);
|
|
if(!tryonly && ret)
|
|
{
|
|
// clear eof flag before restartDemux() to prevent readNextBlock() fail
|
|
eof = false;
|
|
demuxfirstchunk = true;
|
|
notfound_sequence = 0;
|
|
if(b_needs_restart)
|
|
{
|
|
if(currentChunk)
|
|
delete currentChunk;
|
|
currentChunk = NULL;
|
|
needrestart = false;
|
|
|
|
fakeEsOut()->resetTimestamps();
|
|
|
|
vlc_tick_t seekMediaTime = segmentTracker->getPlaybackTime();
|
|
fakeEsOut()->setExpectedTimestamp(seekMediaTime);
|
|
if( !restartDemux() )
|
|
{
|
|
msg_Info(p_realdemux, "Restart demux failed");
|
|
eof = true;
|
|
valid = false;
|
|
ret = false;
|
|
}
|
|
else
|
|
{
|
|
fakeEsOut()->commandsQueue()->setEOF(false);
|
|
}
|
|
}
|
|
else fakeEsOut()->commandsQueue()->Abort( true );
|
|
|
|
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,
|
|
vlc_tick_t *mediaStart,
|
|
vlc_tick_t *demuxStart) const
|
|
{
|
|
return (segmentTracker->getMediaPlaybackRange(start, end, length) &&
|
|
fakeEsOut()->getStartTimestamps(mediaStart, demuxStart));
|
|
}
|
|
|
|
void AbstractStream::runUpdates()
|
|
{
|
|
if(valid && !disabled)
|
|
segmentTracker->updateSelected();
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
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 = NULL;
|
|
}
|
|
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 = NULL;
|
|
switch((unsigned)format)
|
|
{
|
|
case StreamFormat::MP4:
|
|
ret = new Demuxer(p_obj, "mp4", out, source);
|
|
break;
|
|
|
|
case StreamFormat::MPEG2TS:
|
|
ret = new Demuxer(p_obj, "ts", out, source);
|
|
break;
|
|
|
|
default:
|
|
case StreamFormat::UNSUPPORTED:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void AbstractStream::trackerEvent(const SegmentTrackerEvent &event)
|
|
{
|
|
switch(event.type)
|
|
{
|
|
case SegmentTrackerEvent::DISCONTINUITY:
|
|
discontinuity = true;
|
|
break;
|
|
|
|
case SegmentTrackerEvent::FORMATCHANGE:
|
|
/* Check if our current demux is still valid */
|
|
if(*event.u.format.f != format || format == StreamFormat(StreamFormat::UNKNOWN))
|
|
{
|
|
/* 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.u.format.f->str().c_str());
|
|
format = *event.u.format.f;
|
|
|
|
/* This is an implict discontinuity */
|
|
discontinuity = true;
|
|
}
|
|
break;
|
|
|
|
case SegmentTrackerEvent::SWITCHING:
|
|
if(demuxer && !inrestart)
|
|
{
|
|
if(!demuxer->bitstreamSwitchCompatible() ||
|
|
(event.u.switching.next &&
|
|
!event.u.switching.next->getAdaptationSet()->isBitSwitchable()))
|
|
needrestart = true;
|
|
}
|
|
break;
|
|
|
|
case SegmentTrackerEvent::SEGMENT_CHANGE:
|
|
if(demuxer && demuxer->needsRestartOnEachSegment() && !inrestart)
|
|
{
|
|
needrestart = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void add_codec_string_from_fourcc(vlc_fourcc_t fourcc,
|
|
std::list<std::string> &codecs)
|
|
{
|
|
std::string codec;
|
|
codec.insert(0, reinterpret_cast<const char *>(&fourcc), 4);
|
|
codecs.push_back(codec);
|
|
}
|
|
|
|
void AbstractStream::declaredCodecs()
|
|
{
|
|
const std::string & streamDesc = segmentTracker->getStreamDescription();
|
|
const std::string & streamLang = segmentTracker->getStreamLanguage();
|
|
std::list<std::string> codecs = segmentTracker->getCurrentCodecs();
|
|
|
|
if(codecs.empty())
|
|
{
|
|
const StreamFormat format = segmentTracker->getCurrentFormat();
|
|
switch(format)
|
|
{
|
|
case StreamFormat::TTML:
|
|
add_codec_string_from_fourcc(VLC_CODEC_TTML, codecs);
|
|
break;
|
|
case StreamFormat::WEBVTT:
|
|
add_codec_string_from_fourcc(VLC_CODEC_WEBVTT, codecs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(std::list<std::string>::const_iterator it = codecs.begin();
|
|
it != codecs.end(); ++it)
|
|
{
|
|
FormatNamespace fnsp(*it);
|
|
|
|
es_format_t fmt;
|
|
es_format_Init(&fmt, fnsp.getFmt()->i_cat, fnsp.getFmt()->i_codec);
|
|
es_format_Copy(&fmt, fnsp.getFmt());
|
|
|
|
if(!streamLang.empty())
|
|
fmt.psz_language = strdup(streamLang.c_str());
|
|
if(!streamDesc.empty())
|
|
fmt.psz_description = strdup(streamDesc.c_str());
|
|
|
|
fakeEsOut()->declareEs( &fmt );
|
|
|
|
es_format_Clean(&fmt);
|
|
}
|
|
}
|
|
|
|
FakeESOut::LockedFakeEsOut AbstractStream::fakeEsOut()
|
|
{
|
|
return fakeesout->WithLock();
|
|
}
|
|
|
|
FakeESOut::LockedFakeEsOut AbstractStream::fakeEsOut() const
|
|
{
|
|
return fakeesout->WithLock();
|
|
}
|
|
|