Streams.cpp 10.6 KB
Newer Older
1 2 3
/*
 * Streams.cpp
 *****************************************************************************
4
 * Copyright (C) 2014 - VideoLAN and VLC authors
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 *****************************************************************************/
#include "Streams.hpp"
21
#include "http/HTTPConnection.hpp"
22
#include "http/HTTPConnectionManager.h"
23
#include "playlist/BaseRepresentation.h"
24
#include "playlist/SegmentChunk.hpp"
25 26 27
#include "plumbing/SourceStream.hpp"
#include "plumbing/CommandsQueue.hpp"
#include "tools/Debug.hpp"
28
#include <vlc_demux.h>
29

30 31
using namespace adaptive;
using namespace adaptive::http;
32

33
AbstractStream::AbstractStream(demux_t * demux_, const StreamFormat &format_)
34
{
35
    p_realdemux = demux_;
36
    format = format_;
37 38
    currentChunk = NULL;
    eof = false;
39
    dead = false;
40
    disabled = false;
41 42
    flushing = false;
    discontinuity = false;
43
    segmentTracker = NULL;
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    pcr = VLC_TS_INVALID;

    demuxer = NULL;
    fakeesout = NULL;

    /* Don't even try if not supported */
    if((unsigned)format == StreamFormat::UNSUPPORTED)
        throw VLC_EGENERIC;

    demuxersource = new (std::nothrow) ChunksSourceStream( VLC_OBJECT(p_realdemux), this );
    if(!demuxersource)
        throw VLC_EGENERIC;

    CommandsFactory *factory = new CommandsFactory();
    fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out, factory);
    if(!fakeesout)
    {
        delete demuxersource;
        throw VLC_EGENERIC;
    }
    fakeesout->setExtraInfoProvider( this );
65 66
}

67
AbstractStream::~AbstractStream()
68 69
{
    delete currentChunk;
70
    delete segmentTracker;
71 72 73 74

    delete demuxer;
    delete demuxersource;
    delete fakeesout;
75 76 77
}


78
void AbstractStream::bind(SegmentTracker *tracker, HTTPConnectionManager *conn)
79
{
80
    segmentTracker = tracker;
81
    segmentTracker->registerListener(this);
82
    connManager = conn;
83 84
}

85
void AbstractStream::prepareFormatChange()
86
{
87 88 89 90 91 92 93 94 95 96 97 98 99 100
    if(demuxer)
    {
        /* Enqueue Del Commands for all current ES */
        demuxer->drain();
        /* Enqueue Del Commands for all current ES */
        fakeesout->scheduleAllForDeletion();
        fakeesout->schedulePCRReset();
        fakeesout->commandsqueue.Commit();
        /* ignoring demuxer's own Del commands */
        fakeesout->commandsqueue.setDrop(true);
        delete demuxer;
        fakeesout->commandsqueue.setDrop(false);
        demuxer = NULL;
    }
101 102
}

103
void AbstractStream::setLanguage(const std::string &lang)
104 105
{
    language = lang;
106 107
}

108
void AbstractStream::setDescription(const std::string &desc)
109 110 111 112
{
    description = desc;
}

113 114 115 116 117 118
bool AbstractStream::isEOF() const
{
    return dead;
}

mtime_t AbstractStream::getPCR() const
119
{
120
    return pcr;
121 122
}

123 124 125 126 127 128 129
mtime_t AbstractStream::getMinAheadTime() const
{
    if(!segmentTracker)
        return 0;
    return segmentTracker->getMinAheadTime();
}

130
mtime_t AbstractStream::getBufferingLevel() const
131
{
132
    return fakeesout->commandsqueue.getBufferingLevel();
133 134
}

135
mtime_t AbstractStream::getFirstDTS() const
136
{
137
    return fakeesout->commandsqueue.getFirstDTS();
138 139
}

140
int AbstractStream::esCount() const
141
{
142
    return fakeesout->esCount();
143 144
}

145
bool AbstractStream::seekAble() const
146
{
147 148 149 150
    return (demuxer &&
            !fakeesout->restarting() &&
            !discontinuity &&
            !flushing );
151 152
}

153
bool AbstractStream::isSelected() const
154
{
155
    return fakeesout->hasSelectedEs();
156 157
}

158
bool AbstractStream::reactivate(mtime_t basetime)
159 160 161 162 163 164 165 166 167 168 169 170 171
{
    if(setPosition(basetime, false))
    {
        disabled = false;
        return true;
    }
    else
    {
        eof = true; /* can't reactivate */
        return false;
    }
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
bool AbstractStream::startDemux()
{
    if(demuxer)
        return false;

    try
    {
        demuxersource->Reset();
        demuxer = createDemux(format);
    } catch(int) {
        msg_Err(p_realdemux, "Failed to create demuxer");
    }

    return !!demuxer;
}

bool AbstractStream::restartDemux()
{
    if(!demuxer)
    {
        return startDemux();
    }
    else if(demuxer->reinitsOnSeek())
    {
        /* Push all ES as recycling candidates */
        fakeesout->recycleAll();
        /* Restart with ignoring pushes to queue */
        return demuxer->restart(fakeesout->commandsqueue);
    }
201
    fakeesout->commandsqueue.Commit();
202 203 204 205
    return true;
}

bool AbstractStream::isDisabled() const
206 207 208 209
{
    return disabled;
}

210
AbstractStream::status AbstractStream::demux(mtime_t nz_deadline, bool send)
211
{
212
    /* Ensure it is configured */
213
    if(!segmentTracker || !connManager || dead)
214
        return AbstractStream::status_eof;
215

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    if(flushing)
    {
        if(!send)
            return AbstractStream::status_buffering;

        pcr = fakeesout->commandsqueue.Process(p_realdemux->out, VLC_TS_0 + nz_deadline);
        if(!fakeesout->commandsqueue.isEmpty())
            return AbstractStream::status_demuxed;

        fakeesout->commandsqueue.Abort(true); /* reset buffering level */
        flushing = false;
        pcr = 0;
        return AbstractStream::status_dis;
    }

231
    if(!demuxer)
232
    {
233 234
        format = segmentTracker->initialFormat();
        if(!startDemux())
235
        {
236 237 238 239 240 241 242 243 244 245 246
            /* If demux fails because of probing failure / wrong format*/
            if(discontinuity)
            {
                msg_Dbg( p_realdemux, "Flushing on format change" );
                prepareFormatChange();
                discontinuity = false;
                flushing = true;
                return AbstractStream::status_buffering;
            }
            dead = true; /* Prevent further retries */
            return AbstractStream::status_eof;
247 248 249 250
        }
    }

    if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* not already demuxed */
251
    {
252 253 254
        if(!segmentTracker->segmentsListReady()) /* Live Streams */
            return AbstractStream::status_buffering_ahead;

255
        /* need to read, demuxer still buffering, ... */
256
        if(demuxer->demux(nz_deadline) != VLC_DEMUXER_SUCCESS)
257
        {
258
            if(discontinuity)
259 260 261 262 263 264 265 266 267 268 269
            {
                msg_Dbg( p_realdemux, "Flushing on discontinuity" );
                prepareFormatChange();
                discontinuity = false;
                flushing = true;
                return AbstractStream::status_buffering;
            }

            fakeesout->commandsqueue.Commit();
            if(fakeesout->commandsqueue.isEmpty())
                return AbstractStream::status_eof;
270
        }
271
        else if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* need to read more */
272
        {
273
            return AbstractStream::status_buffering;
274
        }
275 276
    }

277 278 279
    AdvDebug(msg_Dbg(p_realdemux, "Stream %s pcr %ld dts %ld deadline %ld buflevel %ld",
             description.c_str(), getPCR(), getFirstDTS(), nz_deadline, getBufferingLevel()));

280
    if(send)
281
        pcr = fakeesout->commandsqueue.Process( p_realdemux->out, VLC_TS_0 + nz_deadline );
282

283 284 285 286 287 288 289
    /* Disable streams that are not selected (alternate streams) */
    if(esCount() && !isSelected() && !fakeesout->restarting())
    {
        disabled = true;
        segmentTracker->reset();
    }

290
    return AbstractStream::status_demuxed;
291 292
}

293
block_t * AbstractStream::readNextBlock()
294
{
295 296
    if (currentChunk == NULL && !eof)
        currentChunk = segmentTracker->getNextChunk(!fakeesout->restarting(), connManager);
297

298
    if(discontinuity)
299
    {
300 301
        msg_Info(p_realdemux, "Encountered discontinuity");
        /* Force stream/demuxer to end for this call */
302 303 304
        return NULL;
    }

305
    if(currentChunk == NULL)
306
    {
307
        eof = true;
308 309
        return NULL;
    }
310

311
    const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
312

313
    block_t *block = currentChunk->readBlock();
314
    if(block == NULL)
315
    {
316
        delete currentChunk;
317
        currentChunk = NULL;
318
        return NULL;
319 320
    }

321
    if (currentChunk->isEmpty())
322
    {
323
        delete currentChunk;
324
        currentChunk = NULL;
325 326
    }

327
    block = checkBlock(block, b_segment_head_chunk);
328

329
    return block;
330 331
}

332
bool AbstractStream::setPosition(mtime_t time, bool tryonly)
333
{
334
    if(!demuxer)
335 336
        return false;

337
    bool ret = segmentTracker->setPositionByTime(time, demuxer->reinitsOnSeek(), tryonly);
338
    if(!tryonly && ret)
339
    {
340
        if(demuxer->reinitsOnSeek())
341 342 343 344
        {
            if(currentChunk)
                delete currentChunk;
            currentChunk = NULL;
345

346 347
            if( !restartDemux() )
                dead = true;
348 349 350 351 352

            /* Check if we need to set an offset as the demuxer
             * will start from zero from seek point */
            if(demuxer->alwaysStartsFromZero())
                fakeesout->setTimestampOffset(time);
353
        }
354 355 356

        es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                       VLC_TS_0 + time);
357
    }
358 359 360
    return ret;
}

361
mtime_t AbstractStream::getPlaybackTime() const
362
{
363
    return segmentTracker->getPlaybackTime();
364
}
365

366
void AbstractStream::runUpdates()
367 368 369 370
{
    if(!isDisabled())
        segmentTracker->updateSelected();
}
371 372 373 374 375 376 377 378 379

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());
}

380 381 382 383 384 385 386 387 388 389 390 391 392
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 has changed between segments, we need to drain and change demux */
393 394
                msg_Info(p_realdemux, "Changing stream format %s -> %s",
                         format.str().c_str(), event.u.format.f->str().c_str());
395 396 397 398 399 400 401 402 403 404 405 406
                format = *event.u.format.f;

                /* This is an implict discontinuity */
                discontinuity = true;
            }
            break;

        case SegmentTrackerEvent::SWITCHING:
        default:
            break;
    }
}