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