Streams.cpp 12.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 * Streams.cpp
 *****************************************************************************
 * Copyright (C) 2014 - VideoLAN authors
 *
 * 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
#define __STDC_CONSTANT_MACROS
21
#include "Streams.hpp"
22
#include "StreamsType.hpp"
23
#include "http/HTTPConnection.hpp"
24
25
26
#include "http/HTTPConnectionManager.h"
#include "http/Chunk.h"
#include "logic/AbstractAdaptationLogic.h"
27
#include "SegmentTracker.hpp"
28
29
#include <vlc_stream.h>
#include <vlc_demux.h>
30

31
using namespace adaptative;
32
33
34
using namespace adaptative::http;
using namespace adaptative::logic;

35
36
Stream::Stream(const std::string &mime)
{
37
    init(mimeToType(mime), mimeToFormat(mime));
38
39
}

40
Stream::Stream(const StreamType type, const StreamFormat format)
41
{
42
    init(type, format);
43
44
}

45
void Stream::init(const StreamType type_, const StreamFormat format_)
46
47
{
    type = type_;
48
    format = format_;
49
    output = NULL;
50
    adaptationLogic = NULL;
51
52
    currentChunk = NULL;
    eof = false;
53
    segmentTracker = NULL;
54
55
56
57
58
59
60
}

Stream::~Stream()
{
    delete currentChunk;
    delete adaptationLogic;
    delete output;
61
    delete segmentTracker;
62
63
}

64
StreamType Stream::mimeToType(const std::string &mime)
65
{
66
    StreamType mimetype;
67
    if (!mime.compare(0, 6, "video/"))
68
        mimetype = StreamType::VIDEO;
69
    else if (!mime.compare(0, 6, "audio/"))
70
        mimetype = StreamType::AUDIO;
71
    else if (!mime.compare(0, 12, "application/"))
72
        mimetype = StreamType::APPLICATION;
73
    else /* unknown of unsupported */
74
        mimetype = StreamType::UNKNOWN;
75
76
77
    return mimetype;
}

78
StreamFormat Stream::mimeToFormat(const std::string &mime)
79
{
80
    StreamFormat format = StreamFormat::UNSUPPORTED;
81
82
83
84
85
    std::string::size_type pos = mime.find("/");
    if(pos != std::string::npos)
    {
        std::string tail = mime.substr(pos + 1);
        if(tail == "mp4")
86
            format = StreamFormat::MP4;
87
        else if (tail == "mp2t")
88
            format = StreamFormat::MPEG2TS;
89
90
91
92
    }
    return format;
}

93
94
void Stream::create(demux_t *demux, AbstractAdaptationLogic *logic,
                    SegmentTracker *tracker, AbstractStreamOutputFactory &factory)
95
{
96
    output = factory.create(demux, format);
97
98
    adaptationLogic = logic;
    segmentTracker = tracker;
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
}

bool Stream::isEOF() const
{
    return false;
}

mtime_t Stream::getPCR() const
{
    return output->getPCR();
}

int Stream::getGroup() const
{
    return output->getGroup();
}

int Stream::esCount() const
{
    return output->esCount();
}

121
122
123
124
bool Stream::operator ==(const Stream &stream) const
{
    return stream.type == type;
}
125
126
127
128
129

Chunk * Stream::getChunk()
{
    if (currentChunk == NULL)
    {
130
        currentChunk = segmentTracker->getNextChunk(type);
131
132
133
134
135
136
        if (currentChunk == NULL)
            eof = true;
    }
    return currentChunk;
}

137
138
139
140
141
bool Stream::seekAble() const
{
    return (output && output->seekAble());
}

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
Stream::status Stream::demux(HTTPConnectionManager *connManager, mtime_t nz_deadline)
{
    if(nz_deadline + VLC_TS_0 > output->getPCR()) /* not already demuxed */
    {
        /* need to read, demuxer still buffering, ... */
        if(read(connManager) <= 0)
            return Stream::status_eof;

        if(nz_deadline + VLC_TS_0 > output->getPCR()) /* need to read more */
            return Stream::status_buffering;
    }

    output->sendToDecoder(nz_deadline);
    return Stream::status_demuxed;
}

158
159
160
161
162
163
164
165
166
167
168
169
170
171
size_t Stream::read(HTTPConnectionManager *connManager)
{
    Chunk *chunk = getChunk();
    if(!chunk)
        return 0;

    if(!chunk->getConnection())
    {
       if(!connManager->connectChunk(chunk))
        return 0;
    }

    size_t readsize = 0;

172
    /* New chunk, do query */
173
174
    if(chunk->getBytesRead() == 0)
    {
175
176
177
178
179
180
181
        if(chunk->getConnection()->query(chunk->getPath()) != VLC_SUCCESS)
        {
            chunk->getConnection()->releaseChunk();
            currentChunk = NULL;
            delete chunk;
            return 0;
        }
182
183
    }

184
185
186
187
    /* Because we don't know Chunk size at start, we need to get size
       from content length */
    readsize = chunk->getBytesToRead();
    if (readsize > 32768)
188
189
190
191
192
193
194
195
196
197
        readsize = 32768;

    block_t *block = block_Alloc(readsize);
    if(!block)
        return 0;

    mtime_t time = mdate();
    ssize_t ret = chunk->getConnection()->read(block->p_buffer, readsize);
    time = mdate() - time;

198
    if(ret < 0)
199
200
201
202
203
204
205
206
207
208
209
210
    {
        block_Release(block);
        chunk->getConnection()->releaseChunk();
        currentChunk = NULL;
        delete chunk;
        return 0;
    }
    else
    {
        block->i_buffer = (size_t)ret;

        adaptationLogic->updateDownloadRate(block->i_buffer, time);
211
        chunk->onDownload(&block);
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

        if (chunk->getBytesToRead() == 0)
        {
            chunk->getConnection()->releaseChunk();
            currentChunk = NULL;
            delete chunk;
        }
    }

    readsize = block->i_buffer;

    output->pushBlock(block);

    return readsize;
}

228
229
bool Stream::setPosition(mtime_t time, bool tryonly)
{
230
    bool ret = segmentTracker->setPosition(time, output->reinitsOnSeek(), tryonly);
231
    if(!tryonly && ret)
232
    {
233
        output->setPosition(time);
234
235
236
237
238
239
240
241
242
243
        if(output->reinitsOnSeek())
        {
            if(currentChunk)
            {
                currentChunk->getConnection()->releaseChunk();
                delete currentChunk;
            }
            currentChunk = NULL;
        }
    }
244
245
246
    return ret;
}

247
248
249
250
251
mtime_t Stream::getPosition() const
{
    return segmentTracker->getSegmentStart();
}

252
253
254
255
256
void Stream::prune()
{
    segmentTracker->pruneFromCurrent();
}

257
258
259
260
AbstractStreamOutput::AbstractStreamOutput(demux_t *demux)
{
    realdemux = demux;
    pcr = VLC_TS_0;
261
    group = 0;
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
}

AbstractStreamOutput::~AbstractStreamOutput()
{
}

mtime_t AbstractStreamOutput::getPCR() const
{
    return pcr;
}

int AbstractStreamOutput::getGroup() const
{
    return group;
}

BaseStreamOutput::BaseStreamOutput(demux_t *demux, const std::string &name) :
    AbstractStreamOutput(demux)
{
    this->name = name;
282
    seekable = true;
283
    demuxstream = NULL;
284
285
286
287
288

    fakeesout = new es_out_t;
    if (!fakeesout)
        throw VLC_ENOMEM;

289
290
    vlc_mutex_init(&lock);

291
292
293
294
295
296
    fakeesout->pf_add = esOutAdd;
    fakeesout->pf_control = esOutControl;
    fakeesout->pf_del = esOutDel;
    fakeesout->pf_destroy = esOutDestroy;
    fakeesout->pf_send = esOutSend;
    fakeesout->p_sys = (es_out_sys_t*) this;
297
298
299

    if(!restart())
        throw VLC_EGENERIC;
300
301
}

302
BaseStreamOutput::~BaseStreamOutput()
303
304
305
306
{
    if (demuxstream)
        stream_Delete(demuxstream);
    delete fakeesout;
307
308
309
310
311
312
313

    vlc_mutex_destroy(&lock);

    /* shouldn't be any */
    std::list<Demuxed *>::const_iterator it;
    for(it=queues.begin(); it!=queues.end();++it)
        delete *it;
314
315
}

316
int BaseStreamOutput::esCount() const
317
{
318
    return queues.size();
319
320
}

321
void BaseStreamOutput::pushBlock(block_t *block)
322
323
324
325
{
    stream_DemuxSend(demuxstream, block);
}

326
bool BaseStreamOutput::seekAble() const
327
328
329
330
{
    return (demuxstream && seekable);
}

331
void BaseStreamOutput::setPosition(mtime_t nztime)
332
{
333
    vlc_mutex_lock(&lock);
334
335
336
337
338
339
340
    std::list<Demuxed *>::const_iterator it;
    for(it=queues.begin(); it!=queues.end();++it)
    {
        Demuxed *pair = *it;
        if(pair->p_queue && pair->p_queue->i_dts > VLC_TS_0 + nztime)
            pair->drop();
    }
341
    pcr = VLC_TS_INVALID;
342
    vlc_mutex_unlock(&lock);
343
344
    es_out_Control(realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                   VLC_TS_0 + nztime);
345
346
    if(reinitsOnSeek())
        restart();
347
348
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
bool BaseStreamOutput::restart()
{
    stream_t *newdemuxstream = stream_DemuxNew(realdemux, name.c_str(), fakeesout);
    if(!newdemuxstream)
        return false;

    vlc_mutex_lock(&lock);
    stream_t *olddemuxstream = demuxstream;
    demuxstream = newdemuxstream;
    vlc_mutex_unlock(&lock);

    if(olddemuxstream)
        stream_Delete(olddemuxstream);

    return true;
}

bool BaseStreamOutput::reinitsOnSeek() const
{
    return false;
}

void BaseStreamOutput::sendToDecoder(mtime_t nzdeadline)
372
{
373
    vlc_mutex_lock(&lock);
374
375
376
377
    sendToDecoderUnlocked(nzdeadline);
    vlc_mutex_unlock(&lock);
}

378
void BaseStreamOutput::sendToDecoderUnlocked(mtime_t nzdeadline)
379
{
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
    std::list<Demuxed *>::const_iterator it;
    for(it=queues.begin(); it!=queues.end();++it)
    {
        Demuxed *pair = *it;
        while(pair->p_queue && pair->p_queue->i_dts <= VLC_TS_0 + nzdeadline)
        {
            block_t *p_block = pair->p_queue;
            pair->p_queue = pair->p_queue->p_next;
            p_block->p_next = NULL;

            if(pair->pp_queue_last == &p_block->p_next)
                pair->pp_queue_last = &pair->p_queue;

            realdemux->out->pf_send(realdemux->out, pair->es_id, p_block);
        }
    }
}

398
BaseStreamOutput::Demuxed::Demuxed()
399
400
401
402
403
404
{
    p_queue = NULL;
    pp_queue_last = &p_queue;
    es_id = NULL;
}

405
BaseStreamOutput::Demuxed::~Demuxed()
406
407
408
409
{
    drop();
}

410
void BaseStreamOutput::Demuxed::drop()
411
412
413
414
415
416
{
    block_ChainRelease(p_queue);
    p_queue = NULL;
    pp_queue_last = &p_queue;
}

417
/* Static callbacks */
418
es_out_id_t * BaseStreamOutput::esOutAdd(es_out_t *fakees, const es_format_t *p_fmt)
419
{
420
    BaseStreamOutput *me = (BaseStreamOutput *) fakees->p_sys;
421
422
423
    es_out_id_t *p_es = me->realdemux->out->pf_add(me->realdemux->out, p_fmt);
    if(p_es)
    {
424
        vlc_mutex_lock(&me->lock);
425
426
427
428
429
430
        Demuxed *pair = new (std::nothrow) Demuxed();
        if(pair)
        {
            pair->es_id = p_es;
            me->queues.push_back(pair);
        }
431
        vlc_mutex_unlock(&me->lock);
432
433
    }
    return p_es;
434
435
}

436
int BaseStreamOutput::esOutSend(es_out_t *fakees, es_out_id_t *p_es, block_t *p_block)
437
{
438
    BaseStreamOutput *me = (BaseStreamOutput *) fakees->p_sys;
439
    vlc_mutex_lock(&me->lock);
440
441
442
443
444
445
446
447
448
449
    std::list<Demuxed *>::const_iterator it;
    for(it=me->queues.begin(); it!=me->queues.end();++it)
    {
        Demuxed *pair = *it;
        if(pair->es_id == p_es)
        {
            block_ChainLastAppend(&pair->pp_queue_last, p_block);
            break;
        }
    }
450
    vlc_mutex_unlock(&me->lock);
451
    return VLC_SUCCESS;
452
453
}

454
void BaseStreamOutput::esOutDel(es_out_t *fakees, es_out_id_t *p_es)
455
{
456
    BaseStreamOutput *me = (BaseStreamOutput *) fakees->p_sys;
457
    vlc_mutex_lock(&me->lock);
458
459
460
461
462
    std::list<Demuxed *>::iterator it;
    for(it=me->queues.begin(); it!=me->queues.end();++it)
    {
        if((*it)->es_id == p_es)
        {
463
            me->sendToDecoderUnlocked(INT64_MAX - VLC_TS_0);
464
465
466
467
468
            delete *it;
            me->queues.erase(it);
            break;
        }
    }
469
    vlc_mutex_unlock(&me->lock);
470
471
472
    me->realdemux->out->pf_del(me->realdemux->out, p_es);
}

473
int BaseStreamOutput::esOutControl(es_out_t *fakees, int i_query, va_list args)
474
{
475
    BaseStreamOutput *me = (BaseStreamOutput *) fakees->p_sys;
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
    if (i_query == ES_OUT_SET_PCR )
    {
        me->pcr = (int64_t)va_arg( args, int64_t );
        return VLC_SUCCESS;
    }
    else if( i_query == ES_OUT_SET_GROUP_PCR )
    {
        me->group = (int) va_arg( args, int );
        me->pcr = (int64_t)va_arg( args, int64_t );
        return VLC_SUCCESS;
    }

    return me->realdemux->out->pf_control(me->realdemux->out, i_query, args);
}

491
void BaseStreamOutput::esOutDestroy(es_out_t *fakees)
492
{
493
    BaseStreamOutput *me = (BaseStreamOutput *) fakees->p_sys;
494
495
496
497
    me->realdemux->out->pf_destroy(me->realdemux->out);
}
/* !Static callbacks */

498
499
500
501
502
AbstractStreamOutput *DefaultStreamOutputFactory::create(demux_t *demux, int format) const
{
    switch(format)
    {
        case StreamFormat::MP4:
503
            return new BaseStreamOutput(demux, "mp4");
504
505

        case StreamFormat::MPEG2TS:
506
            return new BaseStreamOutput(demux, "ts");
507
508
509
510
511
512
513
514

        default:
            throw VLC_EBADVAR;
            break;
    }
    return NULL;
}