Streams.cpp 11.8 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 23
#include "http/HTTPConnectionManager.h"
#include "logic/AbstractAdaptationLogic.h"
24
#include "playlist/SegmentChunk.hpp"
25
#include "SegmentTracker.hpp"
26 27 28
#include "plumbing/SourceStream.hpp"
#include "plumbing/CommandsQueue.hpp"
#include "tools/Debug.hpp"
29
#include <vlc_demux.h>
30

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

35
AbstractStream::AbstractStream(demux_t * demux_, const StreamFormat &format_)
36
{
37
    p_realdemux = demux_;
38
    type = UNKNOWN;
39
    format = format_;
40
    adaptationLogic = NULL;
41 42
    currentChunk = NULL;
    eof = false;
43
    dead = false;
44
    disabled = false;
45 46 47
    flushing = false;
    restarting_output = 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 75
{
    delete currentChunk;
    delete adaptationLogic;
76
    delete segmentTracker;
77 78 79 80

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

83
StreamType AbstractStream::mimeToType(const std::string &mime)
84
{
85
    StreamType mimetype;
86
    if (!mime.compare(0, 6, "video/"))
87
        mimetype = VIDEO;
88
    else if (!mime.compare(0, 6, "audio/"))
89
        mimetype = AUDIO;
90
    else if (!mime.compare(0, 12, "application/"))
91
        mimetype = APPLICATION;
92
    else /* unknown of unsupported */
93
        mimetype = UNKNOWN;
94 95 96
    return mimetype;
}

97 98
void AbstractStream::bind(AbstractAdaptationLogic *logic, SegmentTracker *tracker,
                    HTTPConnectionManager *conn)
99
{
100 101
    adaptationLogic = logic;
    segmentTracker = tracker;
102
    connManager = conn;
103 104
}

105
void AbstractStream::prepareFormatChange()
106
{
107 108 109 110 111 112 113 114 115 116 117 118 119 120
    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;
    }
121 122
}

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

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

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

mtime_t AbstractStream::getPCR() const
139
{
140
    return pcr;
141 142
}

143
mtime_t AbstractStream::getBufferingLevel() const
144
{
145
    return fakeesout->commandsqueue.getBufferingLevel();
146 147
}

148
mtime_t AbstractStream::getFirstDTS() const
149
{
150
    return fakeesout->commandsqueue.getFirstDTS();
151 152
}

153
int AbstractStream::esCount() const
154
{
155
    return fakeesout->esCount();
156 157
}

158
bool AbstractStream::operator ==(const AbstractStream &stream) const
159 160 161
{
    return stream.type == type;
}
162

163
SegmentChunk * AbstractStream::getChunk()
164
{
165
    if (currentChunk == NULL && !eof)
166
    {
167 168 169 170 171
        if(esCount() && !isSelected())
        {
            disabled = true;
            return NULL;
        }
172
        currentChunk = segmentTracker->getNextChunk(!fakeesout->restarting());
173 174 175 176 177 178
        if (currentChunk == NULL)
            eof = true;
    }
    return currentChunk;
}

179
bool AbstractStream::seekAble() const
180
{
181 182 183 184 185
    return (demuxer &&
            !fakeesout->restarting() &&
            !restarting_output &&
            !discontinuity &&
            !flushing );
186 187
}

188
bool AbstractStream::isSelected() const
189
{
190
    return fakeesout->hasSelectedEs();
191 192
}

193
bool AbstractStream::reactivate(mtime_t basetime)
194 195 196 197 198 199 200 201 202 203 204 205 206
{
    if(setPosition(basetime, false))
    {
        disabled = false;
        return true;
    }
    else
    {
        eof = true; /* can't reactivate */
        return false;
    }
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
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);
    }
    return true;
}

bool AbstractStream::isDisabled() const
240 241 242 243
{
    return disabled;
}

244
AbstractStream::status AbstractStream::demux(mtime_t nz_deadline, bool send)
245
{
246 247 248
    /* Ensure it is configured */
    if(!adaptationLogic || !segmentTracker || !connManager || dead)
        return AbstractStream::status_eof;
249

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    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;
    }

    if(!demuxer && !startDemux())
    {
        /* If demux fails because of probing failure / wrong format*/
        if(restarting_output)
        {
            msg_Dbg( p_realdemux, "Flushing on format change" );
            prepareFormatChange();
            restarting_output = false;
            discontinuity = false;
            flushing = true;
            return AbstractStream::status_buffering;
        }
        dead = true; /* Prevent further retries */
        return AbstractStream::status_eof;
    }

    if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* not already demuxed */
282 283
    {
        /* need to read, demuxer still buffering, ... */
284
        if(demuxer->demux() != VLC_DEMUXER_SUCCESS)
285
        {
286 287 288 289 290 291 292 293 294 295 296 297 298
            if(restarting_output || discontinuity)
            {
                msg_Dbg( p_realdemux, "Flushing on discontinuity" );
                prepareFormatChange();
                restarting_output = false;
                discontinuity = false;
                flushing = true;
                return AbstractStream::status_buffering;
            }

            fakeesout->commandsqueue.Commit();
            if(fakeesout->commandsqueue.isEmpty())
                return AbstractStream::status_eof;
299
        }
300
        else if(nz_deadline + VLC_TS_0 > getBufferingLevel()) /* need to read more */
301
        {
302
            return AbstractStream::status_buffering;
303
        }
304 305
    }

306 307 308
    AdvDebug(msg_Dbg(p_realdemux, "Stream %s pcr %ld dts %ld deadline %ld buflevel %ld",
             description.c_str(), getPCR(), getFirstDTS(), nz_deadline, getBufferingLevel()));

309
    if(send)
310
        pcr = fakeesout->commandsqueue.Process( p_realdemux->out, VLC_TS_0 + nz_deadline );
311

312
    return AbstractStream::status_demuxed;
313 314
}

315
block_t * AbstractStream::readNextBlock(size_t)
316
{
317
    SegmentChunk *chunk = getChunk();
318
    if(!chunk)
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
        return NULL;

    if(format != chunk->getStreamFormat())
    {
        /* Force stream to end for this call */
        msg_Info(p_realdemux, "Changing stream format %u->%u",
                 (unsigned)format, (unsigned)chunk->getStreamFormat());

        restarting_output = true;
        format = chunk->getStreamFormat();
        /* Next stream will use current unused chunk */
        return NULL;
    }

    if(chunk->discontinuity)
    {
        discontinuity = true;
        chunk->discontinuity = false;
        msg_Info(p_realdemux, "Encountered discontinuity");
        return NULL;
    }
340 341 342 343

    if(!chunk->getConnection())
    {
       if(!connManager->connectChunk(chunk))
344
        return NULL;
345 346 347
    }

    size_t readsize = 0;
348
    bool b_segment_head_chunk = false;
349

350
    /* New chunk, do query */
351 352
    if(chunk->getBytesRead() == 0)
    {
353 354 355 356 357
        if(chunk->getConnection()->query(chunk->getPath()) != VLC_SUCCESS)
        {
            chunk->getConnection()->releaseChunk();
            currentChunk = NULL;
            delete chunk;
358
            return NULL;
359
        }
360
        b_segment_head_chunk = true;
361 362
    }

363 364 365 366
    /* Because we don't know Chunk size at start, we need to get size
       from content length */
    readsize = chunk->getBytesToRead();
    if (readsize > 32768)
367 368 369 370
        readsize = 32768;

    block_t *block = block_Alloc(readsize);
    if(!block)
371
        return NULL;
372 373 374 375 376

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

377
    if(ret < 0)
378 379 380 381 382
    {
        block_Release(block);
        chunk->getConnection()->releaseChunk();
        currentChunk = NULL;
        delete chunk;
383
        return NULL;
384 385 386 387 388 389
    }
    else
    {
        block->i_buffer = (size_t)ret;

        adaptationLogic->updateDownloadRate(block->i_buffer, time);
390
        chunk->onDownload(&block);
391 392 393 394 395 396 397 398 399

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

400
    block = checkBlock(block, b_segment_head_chunk);
401

402
    return block;
403 404
}

405
bool AbstractStream::setPosition(mtime_t time, bool tryonly)
406
{
407
    if(!demuxer)
408 409
        return false;

410
    bool ret = segmentTracker->setPositionByTime(time, demuxer->reinitsOnSeek(), tryonly);
411
    if(!tryonly && ret)
412
    {
413
        if(demuxer->reinitsOnSeek())
414 415 416 417 418 419 420
        {
            if(currentChunk)
            {
                currentChunk->getConnection()->releaseChunk();
                delete currentChunk;
            }
            currentChunk = NULL;
421 422 423 424 425 426 427

            restartDemux();

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

        es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                       VLC_TS_0 + time);
432
    }
433 434 435
    return ret;
}

436
mtime_t AbstractStream::getPosition() const
437 438 439 440
{
    return segmentTracker->getSegmentStart();
}

441
void AbstractStream::prune()
442 443 444
{
    segmentTracker->pruneFromCurrent();
}
445

446
void AbstractStream::runUpdates()
447 448 449 450
{
    if(!isDisabled())
        segmentTracker->updateSelected();
}
451 452 453 454 455 456 457 458 459

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