FakeESOut.cpp 12.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * FakeESOut.cpp
 *****************************************************************************
 * Copyright © 2014-2015 VideoLAN and VLC 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 21 22 23 24

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

25 26
#include "FakeESOut.hpp"
#include "FakeESOutID.hpp"
27
#include "CommandsQueue.hpp"
28 29
#include <vlc_es_out.h>
#include <vlc_block.h>
30
#include <cassert>
31

32
using namespace adaptive;
33

34
FakeESOut::FakeESOut( es_out_t *es, CommandsQueue *queue )
35 36 37 38 39 40 41 42 43 44 45
{
    real_es_out = es;
    fakeesout = new es_out_t;

    fakeesout->pf_add = esOutAdd_Callback;
    fakeesout->pf_control = esOutControl_Callback;
    fakeesout->pf_del = esOutDel_Callback;
    fakeesout->pf_destroy = esOutDestroy_Callback;
    fakeesout->pf_send = esOutSend_Callback;
    fakeesout->p_sys = (es_out_sys_t*) this;

46 47
    commandsqueue = queue;

48
    timestamps_offset = 0;
49 50
    timestamps_expected = 0;
    timestamps_check_done = false;
51
    extrainfo = NULL;
52
    vlc_mutex_init(&lock);
53 54 55 56 57 58 59 60 61 62 63 64
}

es_out_t * FakeESOut::getEsOut()
{
    return fakeesout;
}

FakeESOut::~FakeESOut()
{
    recycleAll();
    gc();

65
    delete fakeesout;
66
    vlc_mutex_destroy(&lock);
67 68
}

69 70 71 72 73 74 75 76 77
void FakeESOut::setExpectedTimestampOffset(mtime_t offset)
{
    vlc_mutex_lock(&lock);
    timestamps_offset = 0;
    timestamps_expected = offset;
    timestamps_check_done = false;
    vlc_mutex_unlock(&lock);
}

78 79
void FakeESOut::setTimestampOffset(mtime_t offset)
{
80
    vlc_mutex_lock(&lock);
81
    timestamps_offset = offset;
82
    timestamps_check_done = true;
83
    vlc_mutex_unlock(&lock);
84 85 86 87
}

void FakeESOut::setExtraInfoProvider( ExtraFMTInfoInterface *extra )
{
88
    vlc_mutex_lock(&lock);
89
    extrainfo = extra;
90
    vlc_mutex_unlock(&lock);
91 92
}

93
FakeESOutID * FakeESOut::createNewID( const es_format_t *p_fmt )
94 95 96 97
{
    es_format_t fmtcopy;
    es_format_Init( &fmtcopy, 0, 0 );
    es_format_Copy( &fmtcopy, p_fmt );
98
    fmtcopy.i_group = 0; /* Always ignore group for adaptive */
99
    fmtcopy.i_id = -1;
100

101 102
    vlc_mutex_lock(&lock);

103 104 105
    if( extrainfo )
        extrainfo->fillExtraFMTInfo( &fmtcopy );

106 107 108 109
    FakeESOutID *es_id = new (std::nothrow) FakeESOutID( this, &fmtcopy );
    if(likely(es_id))
        fakeesidlist.push_back( es_id );

110 111
    vlc_mutex_unlock(&lock);

112 113 114 115 116 117 118 119 120 121
    es_format_Clean( &fmtcopy );

    return es_id;
}

void FakeESOut::createOrRecycleRealEsID( FakeESOutID *es_id )
{
    std::list<FakeESOutID *>::iterator it;
    es_out_id_t *realid = NULL;

122 123
    vlc_mutex_lock(&lock);

124
    bool b_select = false;
125 126
    for( it=recycle_candidates.begin(); it!=recycle_candidates.end(); ++it )
    {
127 128
        FakeESOutID *cand = *it;
        if ( cand->isCompatible( es_id ) )
129
        {
130 131 132
            realid = cand->realESID();
            cand->setRealESID( NULL );
            delete *it;
133 134 135
            recycle_candidates.erase( it );
            break;
        }
136
        else if( cand->getFmt()->i_cat == es_id->getFmt()->i_cat && cand->realESID() )
137 138 139 140 141
        {
            /* We need to enforce same selection when not reused
               Otherwise the es will select any other compatible track
               and will end this in a activate/select loop when reactivating a track */
            es_out_Control( real_es_out, ES_OUT_GET_ES_STATE, cand->realESID(), &b_select );
142
            break;
143
        }
144 145
    }

146
    if( !realid )
147
    {
148
        realid = es_out_Add( real_es_out, es_id->getFmt() );
149 150 151
        if( b_select )
            es_out_Control( real_es_out, ES_OUT_SET_ES_STATE, realid, b_select );
    }
152

153
    es_id->setRealESID( realid );
154 155

    vlc_mutex_unlock(&lock);
156 157 158 159
}

mtime_t FakeESOut::getTimestampOffset() const
{
160 161 162 163
    vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
    mtime_t time = timestamps_offset;
    vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
    return time;
164 165 166 167 168 169
}

size_t FakeESOut::esCount() const
{
    size_t i_count = 0;
    std::list<FakeESOutID *>::const_iterator it;
170
    vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
171 172 173
    for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
        if( (*it)->realESID() )
            i_count++;
174
    vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
175 176
    return i_count;
}
177 178 179

void FakeESOut::schedulePCRReset()
{
180
    AbstractCommand *command = commandsqueue->factory()->creatEsOutControlResetPCRCommand();
181
    if( likely(command) )
182
        commandsqueue->Schedule( command );
183 184 185 186 187
}

void FakeESOut::scheduleAllForDeletion()
{
    std::list<FakeESOutID *>::const_iterator it;
188
    vlc_mutex_lock(&lock);
189 190 191 192 193
    for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
    {
        FakeESOutID *es_id = *it;
        if(!es_id->scheduledForDeletion())
        {
194
            AbstractCommand *command = commandsqueue->factory()->createEsOutDelCommand( es_id );
195 196
            if( likely(command) )
            {
197
                commandsqueue->Schedule( command );
198 199 200 201
                es_id->setScheduledForDeletion();
            }
        }
    }
202
    vlc_mutex_unlock(&lock);
203 204
}

205 206
void FakeESOut::recycleAll()
{
207
    /* Only used when demux is killed and commands queue is cancelled */
208 209
    commandsqueue->Abort( true );
    assert(commandsqueue->isEmpty());
210
    vlc_mutex_lock(&lock);
211
    recycle_candidates.splice( recycle_candidates.end(), fakeesidlist );
212
    vlc_mutex_unlock(&lock);
213 214 215 216
}

void FakeESOut::gc()
{
217
    vlc_mutex_lock(&lock);
218
    if( recycle_candidates.empty() )
219 220
    {
        vlc_mutex_unlock(&lock);
221
        return;
222
    }
223 224 225 226 227

    std::list<FakeESOutID *>::iterator it;
    for( it=recycle_candidates.begin(); it!=recycle_candidates.end(); ++it )
    {
        if( (*it)->realESID() )
228 229
        {
            es_out_Control( real_es_out, ES_OUT_SET_ES_STATE, (*it)->realESID(), false );
230
            es_out_Del( real_es_out, (*it)->realESID() );
231
        }
232 233 234
        delete *it;
    }
    recycle_candidates.clear();
235
    vlc_mutex_unlock(&lock);
236 237 238 239 240 241
}

bool FakeESOut::hasSelectedEs() const
{
    bool b_selected = false;
    std::list<FakeESOutID *>::const_iterator it;
242
    vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
243 244 245 246 247 248
    for( it=fakeesidlist.begin(); it!=fakeesidlist.end() && !b_selected; ++it )
    {
        FakeESOutID *esID = *it;
        if( esID->realESID() )
            es_out_Control( real_es_out, ES_OUT_GET_ES_STATE, esID->realESID(), &b_selected );
    }
249
    vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
250 251 252
    return b_selected;
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
bool FakeESOut::drain()
{
    bool b_drained = true;
    std::list<FakeESOutID *>::const_iterator it;
    vlc_mutex_lock(&lock);
    for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
    {
        FakeESOutID *esID = *it;
        if( esID->realESID() )
        {
            bool b_empty;
            es_out_Control( real_es_out, ES_OUT_GET_EMPTY, &b_empty );
            b_drained &= b_empty;
        }
    }
    vlc_mutex_unlock(&lock);
    return b_drained;
}

272 273
bool FakeESOut::restarting() const
{
274 275 276 277
    vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
    bool b = !recycle_candidates.empty();
    vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
    return b;
278 279 280 281
}

void FakeESOut::recycle( FakeESOutID *id )
{
282
    vlc_mutex_lock(&lock);
283 284
    fakeesidlist.remove( id );
    recycle_candidates.push_back( id );
285
    vlc_mutex_unlock(&lock);
286 287 288 289 290 291 292
}

/* Static callbacks */
/* Always pass Fake ES ID to slave demuxes, it is just an opaque struct to them */
es_out_id_t * FakeESOut::esOutAdd_Callback(es_out_t *fakees, const es_format_t *p_fmt)
{
    FakeESOut *me = (FakeESOut *) fakees->p_sys;
293 294 295 296

    if( p_fmt->i_cat != VIDEO_ES && p_fmt->i_cat != AUDIO_ES && p_fmt->i_cat != SPU_ES )
        return NULL;

297 298
    /* Feed the slave demux/stream_Demux with FakeESOutID struct,
     * we'll create real ES later on main demux on execution */
299
    FakeESOutID *es_id = me->createNewID( p_fmt );
300 301
    if( likely(es_id) )
    {
302
        assert(!es_id->scheduledForDeletion());
303
        AbstractCommand *command = me->commandsqueue->factory()->createEsOutAddCommand( es_id );
304 305
        if( likely(command) )
        {
306
            me->commandsqueue->Schedule( command );
307 308 309 310 311 312 313 314 315 316
            return reinterpret_cast<es_out_id_t *>(es_id);
        }
        else
        {
            delete es_id;
        }
    }
    return NULL;
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
void FakeESOut::checkTimestampsStart(mtime_t i_start)
{
    if( i_start == VLC_TS_INVALID )
        return;

    vlc_mutex_lock(&lock);
    if( !timestamps_check_done )
    {
        if( i_start < CLOCK_FREQ ) /* Starts 0 */
            timestamps_offset = timestamps_expected;
        timestamps_check_done = true;
    }
    vlc_mutex_unlock(&lock);
}

332 333 334 335
int FakeESOut::esOutSend_Callback(es_out_t *fakees, es_out_id_t *p_es, block_t *p_block)
{
    FakeESOut *me = (FakeESOut *) fakees->p_sys;
    FakeESOutID *es_id = reinterpret_cast<FakeESOutID *>( p_es );
336
    assert(!es_id->scheduledForDeletion());
337 338 339

    me->checkTimestampsStart( p_block->i_dts );

340 341 342 343 344 345 346
    mtime_t offset = me->getTimestampOffset();
    if( p_block->i_dts > VLC_TS_INVALID )
    {
        p_block->i_dts += offset;
        if( p_block->i_pts > VLC_TS_INVALID )
                p_block->i_pts += offset;
    }
347
    AbstractCommand *command = me->commandsqueue->factory()->createEsOutSendCommand( es_id, p_block );
348 349
    if( likely(command) )
    {
350
        me->commandsqueue->Schedule( command );
351 352 353 354 355 356 357 358 359
        return VLC_SUCCESS;
    }
    return VLC_EGENERIC;
}

void FakeESOut::esOutDel_Callback(es_out_t *fakees, es_out_id_t *p_es)
{
    FakeESOut *me = (FakeESOut *) fakees->p_sys;
    FakeESOutID *es_id = reinterpret_cast<FakeESOutID *>( p_es );
360
    AbstractCommand *command = me->commandsqueue->factory()->createEsOutDelCommand( es_id );
361
    if( likely(command) )
362 363
    {
        es_id->setScheduledForDeletion();
364
        me->commandsqueue->Schedule( command );
365
    }
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
}

int FakeESOut::esOutControl_Callback(es_out_t *fakees, int i_query, va_list args)
{
    FakeESOut *me = (FakeESOut *) fakees->p_sys;

    switch( i_query )
    {
        case ES_OUT_SET_PCR:
        case ES_OUT_SET_GROUP_PCR:
        {
            int i_group;
            if( i_query == ES_OUT_SET_GROUP_PCR )
                i_group = static_cast<int>(va_arg( args, int ));
            else
                i_group = 0;
            int64_t  pcr = static_cast<int64_t>(va_arg( args, int64_t ));
            pcr += me->getTimestampOffset();
384
            AbstractCommand *command = me->commandsqueue->factory()->createEsOutControlPCRCommand( i_group, pcr );
385 386
            if( likely(command) )
            {
387
                me->commandsqueue->Schedule( command );
388 389 390 391 392
                return VLC_SUCCESS;
            }
        }
        break;

393 394 395 396 397 398 399 400 401 402 403 404 405
        case ES_OUT_SET_GROUP_META:
        {
            static_cast<void>(va_arg( args, int )); /* ignore group */
            const vlc_meta_t *p_meta = static_cast<const vlc_meta_t *>(va_arg( args, const vlc_meta_t * ));
            AbstractCommand *command = me->commandsqueue->factory()->createEsOutMetaCommand( -1, p_meta );
            if( likely(command) )
            {
                me->commandsqueue->Schedule( command );
                return VLC_SUCCESS;
            }
        }
        break;

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
        /* For others, we don't have the delorean, so always lie */
        case ES_OUT_GET_ES_STATE:
        {
            static_cast<void*>(va_arg( args, es_out_id_t * ));
            bool *pb = static_cast<bool *>(va_arg( args, bool * ));
            *pb = true;
            // ft
        }
        case ES_OUT_SET_ES:
        case ES_OUT_SET_ES_DEFAULT:
        case ES_OUT_SET_ES_STATE:
            return VLC_SUCCESS;

    }

    return VLC_EGENERIC;
}

void FakeESOut::esOutDestroy_Callback(es_out_t *fakees)
{
    FakeESOut *me = (FakeESOut *) fakees->p_sys;
427
    AbstractCommand *command = me->commandsqueue->factory()->createEsOutDestroyCommand();
428
    if( likely(command) )
429
        me->commandsqueue->Schedule( command );
430 431
}
/* !Static callbacks */