Streams.cpp 11.3 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
 *
 * 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.
 *****************************************************************************/
20 21 22 23 24

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

25
#include "Streams.hpp"
26
#include "http/HTTPConnection.hpp"
27
#include "http/HTTPConnectionManager.h"
28
#include "playlist/BaseRepresentation.h"
29
#include "playlist/SegmentChunk.hpp"
30 31 32
#include "plumbing/SourceStream.hpp"
#include "plumbing/CommandsQueue.hpp"
#include "tools/Debug.hpp"
33
#include <vlc_demux.h>
34

35 36
using namespace adaptive;
using namespace adaptive::http;
37

38
AbstractStream::AbstractStream(demux_t * demux_)
39
{
40
    p_realdemux = demux_;
41
    format = StreamFormat::UNSUPPORTED;
42 43
    currentChunk = NULL;
    eof = false;
44
    dead = false;
45
    disabled = false;
46 47
    flushing = false;
    discontinuity = false;
48
    segmentTracker = NULL;
49
    pcr = VLC_TS_INVALID;
50
    demuxersource = NULL;
51
    commandsqueue = NULL;
52 53
    demuxer = NULL;
    fakeesout = NULL;
54
}
55

56 57 58 59 60
bool AbstractStream::init(const StreamFormat &format_, SegmentTracker *tracker, HTTPConnectionManager *conn)
{
    /* Don't even try if not supported or already init */
    if((unsigned)format_ == StreamFormat::UNSUPPORTED || demuxersource)
        return false;
61 62

    demuxersource = new (std::nothrow) ChunksSourceStream( VLC_OBJECT(p_realdemux), this );
63
    if(demuxersource)
64
    {
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
        CommandsFactory *factory = new (std::nothrow) CommandsFactory();
        if(factory)
        {
            commandsqueue = new (std::nothrow) CommandsQueue(factory);
            if(commandsqueue)
            {
                fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out, commandsqueue);
                if(fakeesout)
                {
                    /* All successfull */
                    fakeesout->setExtraInfoProvider( this );
                    format = format_;
                    segmentTracker = tracker;
                    segmentTracker->registerListener(this);
                    connManager = conn;
                    return true;
                }
                delete commandsqueue;
                commandsqueue = NULL;
            }
            else
            {
                delete factory;
            }
        }
90 91 92
        delete demuxersource;
    }

93
    return false;
94 95
}

96
AbstractStream::~AbstractStream()
97 98
{
    delete currentChunk;
99
    delete segmentTracker;
100 101 102 103

    delete demuxer;
    delete demuxersource;
    delete fakeesout;
104
    delete commandsqueue;
105 106
}

107
void AbstractStream::prepareFormatChange()
108
{
109 110 111 112 113 114 115
    if(demuxer)
    {
        /* Enqueue Del Commands for all current ES */
        demuxer->drain();
        /* Enqueue Del Commands for all current ES */
        fakeesout->scheduleAllForDeletion();
        fakeesout->schedulePCRReset();
116
        commandsqueue->Commit();
117
        /* ignoring demuxer's own Del commands */
118
        commandsqueue->setDrop(true);
119
        delete demuxer;
120
        commandsqueue->setDrop(false);
121 122
        demuxer = NULL;
    }
123 124
}

125
void AbstractStream::setLanguage(const std::string &lang)
126 127
{
    language = lang;
128 129
}

130
void AbstractStream::setDescription(const std::string &desc)
131 132 133 134
{
    description = desc;
}

135 136 137 138 139 140
bool AbstractStream::isEOF() const
{
    return dead;
}

mtime_t AbstractStream::getPCR() const
141
{
142
    return pcr;
143 144
}

145 146 147 148 149 150 151
mtime_t AbstractStream::getMinAheadTime() const
{
    if(!segmentTracker)
        return 0;
    return segmentTracker->getMinAheadTime();
}

152
mtime_t AbstractStream::getBufferingLevel() const
153
{
154
    return commandsqueue->getBufferingLevel();
155 156
}

157
mtime_t AbstractStream::getFirstDTS() const
158
{
159
    return commandsqueue->getFirstDTS();
160 161
}

162
int AbstractStream::esCount() const
163
{
164
    return fakeesout->esCount();
165 166
}

167
bool AbstractStream::seekAble() const
168
{
169 170 171 172
    return (demuxer &&
            !fakeesout->restarting() &&
            !discontinuity &&
            !flushing );
173 174
}

175
bool AbstractStream::isSelected() const
176
{
177
    return fakeesout->hasSelectedEs();
178 179
}

180
bool AbstractStream::reactivate(mtime_t basetime)
181 182 183 184 185 186 187 188 189 190 191 192 193
{
    if(setPosition(basetime, false))
    {
        disabled = false;
        return true;
    }
    else
    {
        eof = true; /* can't reactivate */
        return false;
    }
}

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
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 */
221
        return demuxer->restart(commandsqueue);
222
    }
223
    commandsqueue->Commit();
224 225 226 227
    return true;
}

bool AbstractStream::isDisabled() const
228 229 230 231
{
    return disabled;
}

232
AbstractStream::status AbstractStream::demux(mtime_t nz_deadline, bool send)
233
{
234
    /* Ensure it is configured */
235
    if(!segmentTracker || !connManager || dead)
236
        return AbstractStream::status_eof;
237

238 239 240 241 242
    if(flushing)
    {
        if(!send)
            return AbstractStream::status_buffering;

243 244
        pcr = commandsqueue->Process(p_realdemux->out, VLC_TS_0 + nz_deadline);
        if(!commandsqueue->isEmpty())
245 246
            return AbstractStream::status_demuxed;

247
        commandsqueue->Abort(true); /* reset buffering level */
248 249 250 251 252
        flushing = false;
        pcr = 0;
        return AbstractStream::status_dis;
    }

253
    if(!demuxer)
254
    {
255 256
        format = segmentTracker->initialFormat();
        if(!startDemux())
257
        {
258 259 260 261 262 263 264 265 266 267 268
            /* 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;
269
        }
270
        setTimeOffset();
271 272 273
    }

    if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* not already demuxed */
274
    {
275 276 277
        if(!segmentTracker->segmentsListReady()) /* Live Streams */
            return AbstractStream::status_buffering_ahead;

278
        /* need to read, demuxer still buffering, ... */
279
        if(demuxer->demux(nz_deadline) != VLC_DEMUXER_SUCCESS)
280
        {
281
            if(discontinuity)
282 283 284 285 286 287 288 289
            {
                msg_Dbg( p_realdemux, "Flushing on discontinuity" );
                prepareFormatChange();
                discontinuity = false;
                flushing = true;
                return AbstractStream::status_buffering;
            }

290 291
            commandsqueue->Commit();
            if(commandsqueue->isEmpty())
292
                return AbstractStream::status_eof;
293
        }
294
        else if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* need to read more */
295
        {
296
            return AbstractStream::status_buffering;
297
        }
298 299
    }

300 301 302
    AdvDebug(msg_Dbg(p_realdemux, "Stream %s pcr %ld dts %ld deadline %ld buflevel %ld",
             description.c_str(), getPCR(), getFirstDTS(), nz_deadline, getBufferingLevel()));

303
    if(send)
304
        pcr = commandsqueue->Process( p_realdemux->out, VLC_TS_0 + nz_deadline );
305

306 307 308 309 310 311 312
    /* Disable streams that are not selected (alternate streams) */
    if(esCount() && !isSelected() && !fakeesout->restarting())
    {
        disabled = true;
        segmentTracker->reset();
    }

313
    return AbstractStream::status_demuxed;
314 315
}

316
block_t * AbstractStream::readNextBlock()
317
{
318 319
    if (currentChunk == NULL && !eof)
        currentChunk = segmentTracker->getNextChunk(!fakeesout->restarting(), connManager);
320

321
    if(discontinuity)
322
    {
323 324
        msg_Info(p_realdemux, "Encountered discontinuity");
        /* Force stream/demuxer to end for this call */
325 326 327
        return NULL;
    }

328
    if(currentChunk == NULL)
329
    {
330
        eof = true;
331 332
        return NULL;
    }
333

334
    const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
335

336
    block_t *block = currentChunk->readBlock();
337
    if(block == NULL)
338
    {
339
        delete currentChunk;
340
        currentChunk = NULL;
341
        return NULL;
342 343
    }

344
    if (currentChunk->isEmpty())
345
    {
346
        delete currentChunk;
347
        currentChunk = NULL;
348 349
    }

350
    block = checkBlock(block, b_segment_head_chunk);
351

352
    return block;
353 354
}

355
bool AbstractStream::setPosition(mtime_t time, bool tryonly)
356
{
357
    if(!seekAble())
358 359
        return false;

360
    bool ret = segmentTracker->setPositionByTime(time, demuxer->reinitsOnSeek(), tryonly);
361
    if(!tryonly && ret)
362
    {
363
        if(demuxer->reinitsOnSeek())
364 365 366 367
        {
            if(currentChunk)
                delete currentChunk;
            currentChunk = NULL;
368

369 370
            if( !restartDemux() )
                dead = true;
371

372
            setTimeOffset();
373
        }
374

375
        pcr = VLC_TS_INVALID;
376 377
        es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                       VLC_TS_0 + time);
378
    }
379 380 381
    return ret;
}

382
mtime_t AbstractStream::getPlaybackTime() const
383
{
384
    return segmentTracker->getPlaybackTime();
385
}
386

387
void AbstractStream::runUpdates()
388 389 390 391
{
    if(!isDisabled())
        segmentTracker->updateSelected();
}
392 393 394 395 396 397 398 399 400

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

401 402 403 404 405 406 407 408 409 410
void AbstractStream::setTimeOffset()
{
    /* Check if we need to set an offset as the demuxer
     * will start from zero from seek point */
    if(demuxer && demuxer->alwaysStartsFromZero())
        fakeesout->setTimestampOffset(segmentTracker->getPlaybackTime());
    else
        fakeesout->setTimestampOffset(0);
}

411 412 413 414 415 416 417 418 419 420 421 422 423
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 */
424 425
                msg_Info(p_realdemux, "Changing stream format %s -> %s",
                         format.str().c_str(), event.u.format.f->str().c_str());
426 427 428 429 430 431 432 433 434 435 436 437
                format = *event.u.format.f;

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

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