PlaylistManager.cpp 20.1 KB
Newer Older
1
2
3
4
/*
 * PlaylistManager.cpp
 *****************************************************************************
 * Copyright © 2010 - 2011 Klagenfurt University
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
5
 *             2015 VideoLAN and VLC Authors
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * 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.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "PlaylistManager.h"
#include "SegmentTracker.hpp"
#include "playlist/AbstractPlaylist.hpp"
#include "playlist/BasePeriod.h"
#include "playlist/BaseAdaptationSet.h"
31
#include "playlist/BaseRepresentation.h"
32
33
34
35
#include "http/HTTPConnectionManager.h"
#include "logic/AlwaysBestAdaptationLogic.h"
#include "logic/RateBasedAdaptationLogic.h"
#include "logic/AlwaysLowestAdaptationLogic.hpp"
36
#include "tools/Debug.hpp"
37
#include <vlc_stream.h>
38
#include <vlc_demux.h>
39
#include <vlc_threads.h>
40

41
#include <algorithm>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
42
43
#include <ctime>

44
45
46
using namespace adaptive::http;
using namespace adaptive::logic;
using namespace adaptive;
47

48
49
PlaylistManager::PlaylistManager( demux_t *p_demux_,
                                  AbstractPlaylist *pl,
50
                                  AbstractStreamFactory *factory,
51
                                  AbstractAdaptationLogic::LogicType type ) :
52
53
             conManager     ( NULL ),
             logicType      ( type ),
54
             logic          ( NULL ),
55
             playlist       ( pl ),
56
             streamFactory  ( factory ),
57
             p_demux        ( p_demux_ )
58
{
59
    currentPeriod = playlist->getFirstPeriod();
60
    failedupdates = 0;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    thread = 0;
    b_buffering = false;
    nextPlaylistupdate = 0;
    demux.i_nzpcr = VLC_TS_INVALID;
    demux.i_firstpcr = VLC_TS_INVALID;
    vlc_mutex_init(&demux.lock);
    vlc_cond_init(&demux.cond);
    vlc_mutex_init(&lock);
    vlc_cond_init(&waitcond);
    vlc_mutex_init(&cached.lock);
    cached.b_live = false;
    cached.i_length = 0;
    cached.f_position = 0.0;
    cached.i_time = VLC_TS_INVALID;
75
76
77
78
}

PlaylistManager::~PlaylistManager   ()
{
79
    delete streamFactory;
80
    unsetPeriod();
81
    delete playlist;
82
    delete conManager;
83
    delete logic;
84
85
86
87
88
    vlc_cond_destroy(&waitcond);
    vlc_mutex_destroy(&lock);
    vlc_mutex_destroy(&demux.lock);
    vlc_cond_destroy(&demux.cond);
    vlc_mutex_destroy(&cached.lock);
89
90
91
92
}

void PlaylistManager::unsetPeriod()
{
93
    std::vector<AbstractStream *>::iterator it;
94
95
96
    for(it=streams.begin(); it!=streams.end(); ++it)
        delete *it;
    streams.clear();
97
98
}

99
bool PlaylistManager::setupPeriod()
100
{
101
    if(!currentPeriod)
102
103
        return false;

104
105
106
    if(!logic && !(logic = createLogic(logicType, conManager)))
        return false;

107
108
109
    std::vector<BaseAdaptationSet*> sets = currentPeriod->getAdaptationSets();
    std::vector<BaseAdaptationSet*>::iterator it;
    for(it=sets.begin();it!=sets.end();++it)
110
    {
111
        BaseAdaptationSet *set = *it;
112
        if(set && streamFactory)
113
        {
114
115
            SegmentTracker *tracker = new (std::nothrow) SegmentTracker(logic, set);
            if(!tracker)
116
117
                continue;

118
            AbstractStream *st = streamFactory->create(p_demux, set->getStreamFormat(),
119
                                                       tracker, conManager);
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
            if(!st)
            {
                delete tracker;
                continue;
            }

            streams.push_back(st);

            /* Generate stream description */
            std::list<std::string> languages;
            if(!set->getLang().empty())
            {
                languages = set->getLang();
            }
            else if(!set->getRepresentations().empty())
135
            {
136
                languages = set->getRepresentations().front()->getLang();
137
            }
138
139
140
141
142
143

            if(!languages.empty())
                st->setLanguage(languages.front());

            if(!set->description.Get().empty())
                st->setDescription(set->description.Get());
144
145
        }
    }
146
147
148
    return true;
}

149
bool PlaylistManager::start()
150
{
151
    if(!conManager && !(conManager = new (std::nothrow) HTTPConnectionManager(VLC_OBJECT(p_demux->s))))
152
        return false;
153

154
    if(!setupPeriod())
155
156
157
158
159
        return false;

    playlist->playbackStart.Set(time(NULL));
    nextPlaylistupdate = playlist->playbackStart.Get();

160
161
162
163
164
165
166
167
    updateControlsContentType();
    updateControlsPosition();

    if(vlc_clone(&thread, managerThread, reinterpret_cast<void *>(this), VLC_THREAD_PRIORITY_INPUT))
        return false;

    setBufferingRunState(true);

168
169
170
    return true;
}

171
void PlaylistManager::stop()
172
{
173
174
175
176
177
178
179
180
181
182
183
184
    if(thread)
    {
        vlc_cancel(thread);
        vlc_join(thread, NULL);
        thread = 0;
    }
}

AbstractStream::buffering_status PlaylistManager::bufferize(mtime_t i_nzdeadline,
                                                            unsigned i_min_buffering, unsigned i_extra_buffering)
{
    AbstractStream::buffering_status i_return = AbstractStream::buffering_end;
185

186
    std::vector<AbstractStream *>::iterator it;
187
    for(it=streams.begin(); it!=streams.end(); ++it)
188
    {
189
        AbstractStream *st = *it;
190
191
192

        if (st->isDisabled())
        {
193
            if(st->isSelected() && !st->isDead())
194
                reactivateStream(st);
195
196
197
198
            else
                continue;
        }

199
200
        AbstractStream::buffering_status i_ret = st->bufferize(i_nzdeadline, i_min_buffering, i_extra_buffering);
        if(i_return != AbstractStream::buffering_ongoing) /* Buffering streams need to keep going */
201
        {
202
203
            if(i_ret > i_return)
                i_return = i_ret;
204
        }
205
    }
206

207
208
209
210
211
    vlc_mutex_lock(&demux.lock);
    if(demux.i_nzpcr == VLC_TS_INVALID &&
       i_return != AbstractStream::buffering_lessthanmin /* prevents starting before buffering is reached */ )
    {
        demux.i_nzpcr = getFirstDTS();
212
    }
213
214
215
216
217
218
219
220
    vlc_mutex_unlock(&demux.lock);

    return i_return;
}

AbstractStream::status PlaylistManager::dequeue(mtime_t *pi_nzbarrier)
{
    AbstractStream::status i_return = AbstractStream::status_eof;
221

222
223
224
225
    const mtime_t i_nzdeadline = *pi_nzbarrier;

    std::vector<AbstractStream *>::iterator it;
    for(it=streams.begin(); it!=streams.end(); ++it)
226
    {
227
228
229
230
231
232
233
234
235
        AbstractStream *st = *it;

        mtime_t i_pcr;
        AbstractStream::status i_ret = st->dequeue(i_nzdeadline, &i_pcr);
        if( i_ret > i_return )
            i_return = i_ret;

        if( i_pcr > VLC_TS_INVALID )
            *pi_nzbarrier = std::min( *pi_nzbarrier, i_pcr - VLC_TS_0 );
236
237
    }

238
    return i_return;
239
240
}

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
void PlaylistManager::drain()
{
    for(;;)
    {
        bool b_drained = true;
        std::vector<AbstractStream *>::iterator it;
        for(it=streams.begin(); it!=streams.end(); ++it)
        {
            AbstractStream *st = *it;

            if (st->isDisabled())
                continue;

            b_drained &= st->drain();
        }

        if(b_drained)
            break;

        msleep(20*1000); /* ugly, but we have no way to get feedback */
    }
    es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
}

265
266
mtime_t PlaylistManager::getPCR() const
{
267
    mtime_t minpcr = VLC_TS_INVALID;
268
    std::vector<AbstractStream *>::const_iterator it;
269
    for(it=streams.begin(); it!=streams.end(); ++it)
270
    {
271
272
273
274
275
        const mtime_t pcr = (*it)->getPCR();
        if(minpcr == VLC_TS_INVALID)
            minpcr = pcr;
        else if(pcr > VLC_TS_INVALID)
            minpcr = std::min(minpcr, pcr);
276
    }
277
    return minpcr;
278
279
}

280
281
mtime_t PlaylistManager::getFirstDTS() const
{
282
    mtime_t mindts = VLC_TS_INVALID;
283
    std::vector<AbstractStream *>::const_iterator it;
284
    for(it=streams.begin(); it!=streams.end(); ++it)
285
    {
286
287
288
289
290
        const mtime_t dts = (*it)->getFirstDTS();
        if(mindts == VLC_TS_INVALID)
            mindts = dts;
        else if(dts > VLC_TS_INVALID)
            mindts = std::min(mindts, dts);
291
    }
292
    return mindts;
293
294
}

295
296
297
298
299
mtime_t PlaylistManager::getDuration() const
{
    if (playlist->isLive())
        return 0;
    else
300
        return playlist->duration.Get();
301
302
303
304
305
306
307
308
}

bool PlaylistManager::setPosition(mtime_t time)
{
    bool ret = true;
    for(int real = 0; real < 2; real++)
    {
        /* Always probe if we can seek first */
309
        std::vector<AbstractStream *>::iterator it;
310
        for(it=streams.begin(); it!=streams.end(); ++it)
311
        {
312
            AbstractStream *st = *it;
313
            if(!st->isDisabled() && !st->isDead())
314
                ret &= st->setPosition(time, !real);
315
316
317
318
319
320
321
        }
        if(!ret)
            break;
    }
    return ret;
}

322
323
324
325
326
327
328
329
330
331
bool PlaylistManager::needsUpdate() const
{
    return playlist->isLive() && (failedupdates < 3);
}

void PlaylistManager::scheduleNextUpdate()
{

}

332
333
bool PlaylistManager::updatePlaylist()
{
334
    std::vector<AbstractStream *>::const_iterator it;
335
336
337
    for(it=streams.begin(); it!=streams.end(); ++it)
        (*it)->runUpdates();

338
339
    updateControlsContentType();
    updateControlsPosition();
340
341
342
    return true;
}

343
344
345
346
347
348
349
mtime_t PlaylistManager::getFirstPlaybackTime() const
{
    return 0;
}

mtime_t PlaylistManager::getCurrentPlaybackTime() const
{
350
    return demux.i_nzpcr;
351
352
}

353
354
355
356
357
358
359
void PlaylistManager::pruneLiveStream()
{
    mtime_t minValidPos = 0;
    std::vector<AbstractStream *>::const_iterator it;
    for(it=streams.begin(); it!=streams.end(); it++)
    {
        const AbstractStream *st = *it;
360
        if(st->isDisabled() || !st->isSelected() || st->isDead())
361
362
363
364
365
366
367
368
369
370
            continue;
        const mtime_t t = st->getPlaybackTime();
        if(minValidPos == 0 || t < minValidPos)
            minValidPos = t;
    }

    if(minValidPos)
        playlist->pruneByPlaybackTime(minValidPos);
}

371
372
373
374
375
bool PlaylistManager::reactivateStream(AbstractStream *stream)
{
    return stream->reactivate(getPCR());
}

376
377
378
379
380
381
382
383
384
#define DEMUX_INCREMENT (CLOCK_FREQ / 20)
int PlaylistManager::demux_callback(demux_t *p_demux)
{
    PlaylistManager *manager = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);
    return manager->doDemux(DEMUX_INCREMENT);
}

int PlaylistManager::doDemux(int64_t increment)
{
385
386
    vlc_mutex_lock(&demux.lock);
    if(demux.i_nzpcr == VLC_TS_INVALID)
387
    {
388
389
390
391
392
393
        bool b_dead = true;
        std::vector<AbstractStream *>::const_iterator it;
        for(it=streams.begin(); it!=streams.end(); ++it)
            b_dead &= (*it)->isDead();
        if(!b_dead)
            vlc_cond_timedwait(&demux.cond, &demux.lock, mdate() + CLOCK_FREQ / 20);
394
        vlc_mutex_unlock(&demux.lock);
395
        return (b_dead) ? AbstractStream::status_eof : AbstractStream::status_buffering;
396
397
    }

398
399
400
401
402
403
404
405
406
407
408
    if(demux.i_firstpcr == VLC_TS_INVALID)
        demux.i_firstpcr = demux.i_nzpcr;

    mtime_t i_nzbarrier = demux.i_nzpcr + increment;
    vlc_mutex_unlock(&demux.lock);

    AbstractStream::status status = dequeue(&i_nzbarrier);

    updateControlsContentType();
    updateControlsPosition();

409
410
    switch(status)
    {
411
    case AbstractStream::status_eof:
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
        {
            /* might be end of current period */
            if(currentPeriod)
            {
                setBufferingRunState(false);
                BasePeriod *nextPeriod = playlist->getNextPeriod(currentPeriod);
                if(!nextPeriod)
                    return VLC_DEMUXER_EOF;
                unsetPeriod();
                currentPeriod = nextPeriod;
                if (!setupPeriod())
                    return VLC_DEMUXER_EOF;

                demux.i_nzpcr = VLC_TS_INVALID;
                demux.i_firstpcr = VLC_TS_INVALID;
                es_out_Control(p_demux->out, ES_OUT_RESET_PCR);

                setBufferingRunState(true);
            }
        }
        break;
433
    case AbstractStream::status_buffering:
434
435
436
        vlc_mutex_lock(&demux.lock);
        vlc_cond_timedwait(&demux.cond, &demux.lock, mdate() + CLOCK_FREQ / 20);
        vlc_mutex_unlock(&demux.lock);
437
        break;
438
439
440
441
    case AbstractStream::status_discontinuity:
        vlc_mutex_lock(&demux.lock);
        demux.i_nzpcr = VLC_TS_INVALID;
        demux.i_firstpcr = VLC_TS_INVALID;
442
        es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
443
        vlc_mutex_unlock(&demux.lock);
444
        break;
445
    case AbstractStream::status_demuxed:
446
447
        vlc_mutex_lock(&demux.lock);
        if( demux.i_nzpcr != VLC_TS_INVALID && i_nzbarrier != demux.i_nzpcr )
448
        {
449
450
451
            demux.i_nzpcr = i_nzbarrier;
            mtime_t pcr = VLC_TS_0 + std::max(INT64_C(0), demux.i_nzpcr - CLOCK_FREQ / 10);
            es_out_Control(p_demux->out, ES_OUT_SET_GROUP_PCR, 0, pcr);
452
        }
453
        vlc_mutex_unlock(&demux.lock);
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
        break;
    }

    return VLC_DEMUXER_SUCCESS;
}

int PlaylistManager::control_callback(demux_t *p_demux, int i_query, va_list args)
{
    PlaylistManager *manager = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);
    return manager->doControl(i_query, args);
}

int PlaylistManager::doControl(int i_query, va_list args)
{
    switch (i_query)
    {
        case DEMUX_CAN_SEEK:
471
472
473
        {
            vlc_mutex_locker locker(&cached.lock);
            *(va_arg (args, bool *)) = ! cached.b_live;
474
            break;
475
        }
476
477
478
479
480
481

        case DEMUX_CAN_CONTROL_PACE:
            *(va_arg (args, bool *)) = true;
            break;

        case DEMUX_CAN_PAUSE:
482
483
484
        {
            vlc_mutex_locker locker(&cached.lock);
            *(va_arg (args, bool *)) = ! cached.b_live;
485
            break;
486
        }
487

488
        case DEMUX_SET_PAUSE_STATE:
489
490
491
492
        {
            vlc_mutex_locker locker(&cached.lock);
            return cached.b_live ? VLC_EGENERIC : VLC_SUCCESS;
        }
493

494
        case DEMUX_GET_TIME:
495
        {
496
497
            vlc_mutex_locker locker(&cached.lock);
            *(va_arg (args, int64_t *)) = cached.i_time;
498
            break;
499
        }
500
501

        case DEMUX_GET_LENGTH:
502
503
504
        {
            vlc_mutex_locker locker(&cached.lock);
            if(cached.b_live)
505
                return VLC_EGENERIC;
506
            *(va_arg (args, int64_t *)) = cached.i_length;
507
            break;
508
        }
509
510

        case DEMUX_GET_POSITION:
511
        {
512
513
            vlc_mutex_locker locker(&cached.lock);
            if(cached.b_live)
514
                return VLC_EGENERIC;
515
            *(va_arg (args, double *)) = cached.f_position;
516
            break;
517
        }
518
519
520

        case DEMUX_SET_POSITION:
        {
521
522
            setBufferingRunState(false); /* stop downloader first */

523
524
            const mtime_t i_duration = getDuration();
            if(i_duration == 0) /* == playlist->isLive() */
525
526
            {
                setBufferingRunState(true);
527
                return VLC_EGENERIC;
528
            }
529
530
531
532
533

            int64_t time = i_duration * va_arg(args, double);
            time += getFirstPlaybackTime();

            if(!setPosition(time))
534
535
            {
                setBufferingRunState(true);
536
                return VLC_EGENERIC;
537
            }
538

539
540
            demux.i_nzpcr = VLC_TS_INVALID;
            setBufferingRunState(true);
541
542
543
544
545
            break;
        }

        case DEMUX_SET_TIME:
        {
546
            setBufferingRunState(false); /* stop downloader first */
547
            if(playlist->isLive())
548
549
            {
                setBufferingRunState(true);
550
                return VLC_EGENERIC;
551
            }
552
553
554

            int64_t time = va_arg(args, int64_t);// + getFirstPlaybackTime();
            if(!setPosition(time))
555
556
            {
                setBufferingRunState(true);
557
                return VLC_EGENERIC;
558
            }
559

560
561
            demux.i_nzpcr = VLC_TS_INVALID;
            setBufferingRunState(true);
562
563
564
565
            break;
        }

        case DEMUX_GET_PTS_DELAY:
566
567
568
569
570
571
572
573
574
575
            if( logicType != AbstractAdaptationLogic::RateBased )
            {
                *va_arg (args, int64_t *) = INT64_C(1000) *
                    var_InheritInteger(p_demux, "network-caching");
            }
            else
            {
                *va_arg (args, int64_t *) = 1000 * INT64_C(1000);
            }
            break;
576
577
578
579
580
581
582

        default:
            return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
void PlaylistManager::setBufferingRunState(bool b)
{
    vlc_mutex_lock(&lock);
    b_buffering = b;
    if(b_buffering)
        vlc_cond_signal(&waitcond);
    vlc_mutex_unlock(&lock);
}

void PlaylistManager::Run()
{
    vlc_mutex_lock(&lock);
    const unsigned i_min_buffering = playlist->getMinBuffering();
    const unsigned i_extra_buffering = playlist->getMaxBuffering() - i_min_buffering;
    while(1)
    {
        mutex_cleanup_push(&lock);
        while(!b_buffering)
            vlc_cond_wait(&waitcond, &lock);
        vlc_cleanup_pop();

        if(needsUpdate())
        {
            if(updatePlaylist())
                scheduleNextUpdate();
            else
                failedupdates++;
        }

        vlc_mutex_lock(&demux.lock);
        mtime_t i_nzpcr = demux.i_nzpcr;
        vlc_mutex_unlock(&demux.lock);

        int canc = vlc_savecancel();
        AbstractStream::buffering_status i_return = bufferize(i_nzpcr, i_min_buffering, i_extra_buffering);
        vlc_restorecancel( canc );

        if(i_return != AbstractStream::buffering_lessthanmin)
        {
            mtime_t i_deadline = mdate();
            if (i_return == AbstractStream::buffering_ongoing)
                i_deadline += (CLOCK_FREQ / 20);
            if (i_return == AbstractStream::buffering_full)
                i_deadline += (CLOCK_FREQ / 10);
            else if(i_return == AbstractStream::buffering_end)
                i_deadline += (CLOCK_FREQ);
            else /*if(i_return == AbstractStream::buffering_suspended)*/
                i_deadline += (CLOCK_FREQ / 4);

            vlc_mutex_lock(&demux.lock);
            vlc_cond_signal(&demux.cond);
            vlc_mutex_unlock(&demux.lock);

            mutex_cleanup_push(&lock);
            while(vlc_cond_timedwait(&waitcond, &lock, i_deadline) == 0
                 && i_deadline < mdate());
            vlc_cleanup_pop();
        }
    }
    vlc_mutex_unlock(&lock);
}

void * PlaylistManager::managerThread(void *opaque)
{
    (reinterpret_cast<PlaylistManager *>(opaque))->Run();
    return NULL;
}

void PlaylistManager::updateControlsPosition()
{
    vlc_mutex_locker locker(&cached.lock);
    const mtime_t i_duration = cached.i_length;
    if(i_duration == 0)
    {
        cached.f_position = 0.0;
    }
    else
    {
        const mtime_t i_length = getCurrentPlaybackTime() - getFirstPlaybackTime();
        cached.f_position = (double) i_length / i_duration;
    }

    mtime_t i_time = getCurrentPlaybackTime();
    if(!playlist->isLive())
        i_time -= getFirstPlaybackTime();
    cached.i_time = i_time;
}

void PlaylistManager::updateControlsContentType()
{
    vlc_mutex_locker locker(&cached.lock);
    if(playlist->isLive())
    {
        cached.b_live = true;
        cached.i_length = 0;
    }
    else
    {
        cached.b_live = false;
        cached.i_length = getDuration();
    }
}

686
AbstractAdaptationLogic *PlaylistManager::createLogic(AbstractAdaptationLogic::LogicType type, HTTPConnectionManager *conn)
687
688
689
690
{
    switch(type)
    {
        case AbstractAdaptationLogic::FixedRate:
691
        {
692
            size_t bps = var_InheritInteger(p_demux, "adaptive-bw") * 8192;
693
694
            return new (std::nothrow) FixedRateAdaptationLogic(bps);
        }
695
696
        case AbstractAdaptationLogic::AlwaysLowest:
            return new (std::nothrow) AlwaysLowestAdaptationLogic();
697
698
        case AbstractAdaptationLogic::AlwaysBest:
            return new (std::nothrow) AlwaysBestAdaptationLogic();
699
700
        case AbstractAdaptationLogic::Default:
        case AbstractAdaptationLogic::RateBased:
701
        {
702
703
            int width = var_InheritInteger(p_demux, "adaptive-width");
            int height = var_InheritInteger(p_demux, "adaptive-height");
704
705
            RateBasedAdaptationLogic *logic =
                    new (std::nothrow) RateBasedAdaptationLogic(VLC_OBJECT(p_demux), width, height);
706
707
708
            conn->setDownloadRateObserver(logic);
            return logic;
        }
709
710
711
712
        default:
            return NULL;
    }
}