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