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
 *
 * 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_, const StreamFormat &format_)
39
{
40
    p_realdemux = demux_;
41
    format = format_;
42 43
    currentChunk = NULL;
    eof = false;
44
    dead = false;
45
    disabled = false;
46 47
    flushing = false;
    discontinuity = false;
48
    segmentTracker = NULL;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
    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 );
70 71
}

72
AbstractStream::~AbstractStream()
73 74
{
    delete currentChunk;
75
    delete segmentTracker;
76 77 78 79

    delete demuxer;
    delete demuxersource;
    delete fakeesout;
80 81 82
}


83
void AbstractStream::bind(SegmentTracker *tracker, HTTPConnectionManager *conn)
84
{
85
    segmentTracker = tracker;
86
    segmentTracker->registerListener(this);
87
    connManager = conn;
88 89
}

90
void AbstractStream::prepareFormatChange()
91
{
92 93 94 95 96 97 98 99 100 101 102 103 104 105
    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;
    }
106 107
}

108
void AbstractStream::setLanguage(const std::string &lang)
109 110
{
    language = lang;
111 112
}

113
void AbstractStream::setDescription(const std::string &desc)
114 115 116 117
{
    description = desc;
}

118 119 120 121 122 123
bool AbstractStream::isEOF() const
{
    return dead;
}

mtime_t AbstractStream::getPCR() const
124
{
125
    return pcr;
126 127
}

128 129 130 131 132 133 134
mtime_t AbstractStream::getMinAheadTime() const
{
    if(!segmentTracker)
        return 0;
    return segmentTracker->getMinAheadTime();
}

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

140
mtime_t AbstractStream::getFirstDTS() const
141
{
142
    return fakeesout->commandsqueue.getFirstDTS();
143 144
}

145
int AbstractStream::esCount() const
146
{
147
    return fakeesout->esCount();
148 149
}

150
bool AbstractStream::seekAble() const
151
{
152 153 154 155
    return (demuxer &&
            !fakeesout->restarting() &&
            !discontinuity &&
            !flushing );
156 157
}

158
bool AbstractStream::isSelected() const
159
{
160
    return fakeesout->hasSelectedEs();
161 162
}

163
bool AbstractStream::reactivate(mtime_t basetime)
164 165 166 167 168 169 170 171 172 173 174 175 176
{
    if(setPosition(basetime, false))
    {
        disabled = false;
        return true;
    }
    else
    {
        eof = true; /* can't reactivate */
        return false;
    }
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
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);
    }
206
    fakeesout->commandsqueue.Commit();
207 208 209 210
    return true;
}

bool AbstractStream::isDisabled() const
211 212 213 214
{
    return disabled;
}

215
AbstractStream::status AbstractStream::demux(mtime_t nz_deadline, bool send)
216
{
217
    /* Ensure it is configured */
218
    if(!segmentTracker || !connManager || dead)
219
        return AbstractStream::status_eof;
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    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;
    }

236
    if(!demuxer)
237
    {
238 239
        format = segmentTracker->initialFormat();
        if(!startDemux())
240
        {
241 242 243 244 245 246 247 248 249 250 251
            /* 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;
252 253 254 255
        }
    }

    if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* not already demuxed */
256
    {
257 258 259
        if(!segmentTracker->segmentsListReady()) /* Live Streams */
            return AbstractStream::status_buffering_ahead;

260
        /* need to read, demuxer still buffering, ... */
261
        if(demuxer->demux(nz_deadline) != VLC_DEMUXER_SUCCESS)
262
        {
263
            if(discontinuity)
264 265 266 267 268 269 270 271 272 273 274
            {
                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;
275
        }
276
        else if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* need to read more */
277
        {
278
            return AbstractStream::status_buffering;
279
        }
280 281
    }

282 283 284
    AdvDebug(msg_Dbg(p_realdemux, "Stream %s pcr %ld dts %ld deadline %ld buflevel %ld",
             description.c_str(), getPCR(), getFirstDTS(), nz_deadline, getBufferingLevel()));

285
    if(send)
286
        pcr = fakeesout->commandsqueue.Process( p_realdemux->out, VLC_TS_0 + nz_deadline );
287

288 289 290 291 292 293 294
    /* Disable streams that are not selected (alternate streams) */
    if(esCount() && !isSelected() && !fakeesout->restarting())
    {
        disabled = true;
        segmentTracker->reset();
    }

295
    return AbstractStream::status_demuxed;
296 297
}

298
block_t * AbstractStream::readNextBlock()
299
{
300 301
    if (currentChunk == NULL && !eof)
        currentChunk = segmentTracker->getNextChunk(!fakeesout->restarting(), connManager);
302

303
    if(discontinuity)
304
    {
305 306
        msg_Info(p_realdemux, "Encountered discontinuity");
        /* Force stream/demuxer to end for this call */
307 308 309
        return NULL;
    }

310
    if(currentChunk == NULL)
311
    {
312
        eof = true;
313 314
        return NULL;
    }
315

316
    const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
317

318
    block_t *block = currentChunk->readBlock();
319
    if(block == NULL)
320
    {
321
        delete currentChunk;
322
        currentChunk = NULL;
323
        return NULL;
324 325
    }

326
    if (currentChunk->isEmpty())
327
    {
328
        delete currentChunk;
329
        currentChunk = NULL;
330 331
    }

332
    block = checkBlock(block, b_segment_head_chunk);
333

334
    return block;
335 336
}

337
bool AbstractStream::setPosition(mtime_t time, bool tryonly)
338
{
339
    if(!seekAble())
340 341
        return false;

342
    bool ret = segmentTracker->setPositionByTime(time, demuxer->reinitsOnSeek(), tryonly);
343
    if(!tryonly && ret)
344
    {
345
        if(demuxer->reinitsOnSeek())
346 347 348 349
        {
            if(currentChunk)
                delete currentChunk;
            currentChunk = NULL;
350

351 352
            if( !restartDemux() )
                dead = true;
353 354 355 356 357

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

360
        pcr = VLC_TS_INVALID;
361 362
        es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                       VLC_TS_0 + time);
363
    }
364 365 366
    return ret;
}

367
mtime_t AbstractStream::getPlaybackTime() const
368
{
369
    return segmentTracker->getPlaybackTime();
370
}
371

372
void AbstractStream::runUpdates()
373 374 375 376
{
    if(!isDisabled())
        segmentTracker->updateSelected();
}
377 378 379 380 381 382 383 384 385

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

386 387 388 389 390 391 392 393 394 395 396 397 398
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 */
399 400
                msg_Info(p_realdemux, "Changing stream format %s -> %s",
                         format.str().c_str(), event.u.format.f->str().c_str());
401 402 403 404 405 406 407 408 409 410 411 412
                format = *event.u.format.f;

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

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