demux.c 20.2 KB
Newer Older
1 2 3
/*****************************************************************************
 * demux.c
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5
 * $Id$
6
 *
7
 * Author: Laurent Aimar <fenrir@via.ecp.fr>
8
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
9 10 11
 * 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
12 13 14 15
 * (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
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
19 20 21
 * 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.
22 23
 *****************************************************************************/

24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <assert.h>
29
#include <limits.h>
30

31
#include "demux.h"
32 33
#include <libvlc.h>
#include <vlc_codec.h>
34
#include <vlc_meta.h>
35
#include <vlc_url.h>
36
#include <vlc_modules.h>
37
#include <vlc_strings.h>
38

39
typedef const struct
40
{
41 42
    char const key[20];
    char const name[8];
43 44

} demux_mapping;
45

46
static int demux_mapping_cmp( const void *k, const void *v )
47
{
48 49
    demux_mapping* entry = v;
    return vlc_ascii_strcasecmp( k, entry->key );
50 51
}

52 53 54 55 56 57
static demux_mapping* demux_lookup( char const* key,
                                    demux_mapping* data, size_t size )
{
    return bsearch( key, data, size, sizeof( *data ), demux_mapping_cmp );
}

58
static const char *demux_NameFromMimeType(const char *mime)
59
{
60
    static demux_mapping types[] =
61 62 63 64
    {   /* Must be sorted in ascending ASCII order */
        { "audio/aac",           "m4a"     },
        { "audio/aacp",          "m4a"     },
        { "audio/mpeg",          "mp3"     },
65
        //{ "video/MP1S",          "es,mpgv" }, !b_force
66
        { "video/dv",            "rawdv"   },
67
        { "video/MP2P",          "ps"      },
68 69 70 71
        { "video/MP2T",          "ts"      },
        { "video/nsa",           "nsv"     },
        { "video/nsv",           "nsv"     },
    };
72
    demux_mapping *type = demux_lookup( mime, types, ARRAY_SIZE( types ) );
73
    return (type != NULL) ? type->name : "any";
74 75
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static const char* DemuxNameFromExtension( char const* ext,
                                           bool b_preparsing )
{
    /* NOTE: Add only file without any problems here and with strong detection:
     * - no .mp3, .a52, ...
     *  - wav can't be added 'cause of a52 and dts in them as raw audio
     */
    static demux_mapping strong[] =
    { /* NOTE: must be sorted in asc order */
        { "aiff", "aiff" },
        { "asf",  "asf" },
        { "au",   "au" },
        { "avi",  "avi" },
        { "drc",  "dirac" },
        { "dv",   "dv" },
        { "flac", "flac" },
        { "h264", "h264" },
        { "kar", "smf" },
        { "m3u",  "m3u" },
        { "m4a",  "mp4" },
        { "m4v",  "m4v" },
        { "mid",  "smf" },
        { "mka",  "mkv" },
        { "mks",  "mkv" },
        { "mkv",  "mkv" },
        { "moov", "mp4" },
        { "mov",  "mp4" },
        { "mp4",  "mp4" },
        { "nsv",  "nsv" },
        { "oga",  "ogg" },
        { "ogg",  "ogg" },
        { "ogm",  "ogg" },
        { "ogv",  "ogg" },
        { "ogx",  "ogg" }, /*RFC5334*/
        { "opus", "ogg" }, /*draft-terriberry-oggopus-01*/
        { "pva",  "pva" },
        { "rm",   "avformat" },
        { "rmi",  "smf" },
        { "spx",  "ogg" },
        { "voc",  "voc" },
        { "wma",  "asf" },
        { "wmv",  "asf" },
    };

    /* Here, we don't mind if it does not work, it must be quick */
    static demux_mapping quick[] =
    { /* NOTE: shall be sorted in asc order */
        { "mp3", "mpga" },
        { "ogg", "ogg" },
        { "wma", "asf" },
    };

    struct {
        demux_mapping* data;
        size_t size;

    } lookup = {
        .data = b_preparsing ? quick : strong,
        .size = b_preparsing ? ARRAY_SIZE( quick ) : ARRAY_SIZE( strong )
    };

137
    demux_mapping* result = demux_lookup( ext, lookup.data, lookup.size );
138 139 140
    return result ? result->name : NULL;
}

141
/*****************************************************************************
142
 * demux_New:
143
 *  if s is NULL then load a access_demux
144
 *****************************************************************************/
François Cartegnie's avatar
François Cartegnie committed
145 146 147 148 149 150 151 152 153
demux_t *demux_New( vlc_object_t *p_obj, const char *psz_name,
                    const char *psz_location, stream_t *s, es_out_t *out )
{
    return demux_NewAdvanced( p_obj, NULL,
                              (s == NULL) ? psz_name : "",
                              (s != NULL) ? psz_name : "",
                              psz_location, s, out, false );
}

154 155 156 157 158 159 160 161 162
typedef struct demux_priv_t
{
    demux_t demux;
    void (*destroy)(demux_t *);
} demux_priv_t;

static void demux_DestroyDemux(demux_t *demux)
{
    assert(demux->s != NULL);
163
    vlc_stream_Delete(demux->s);
164 165 166 167 168 169 170 171
}

static void demux_DestroyAccessDemux(demux_t *demux)
{
    assert(demux->s == NULL);
    (void) demux;
}

172 173 174
static void demux_DestroyDemuxFilter(demux_t *demux)
{
    assert(demux->p_next != NULL);
175
    demux_Delete(demux->p_next);
176 177
}

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
static int demux_Probe(void *func, va_list ap)
{
    int (*probe)(vlc_object_t *) = func;
    demux_t *demux = va_arg(ap, demux_t *);

    /* Restore input stream offset (in case previous probed demux failed to
     * to do so). */
    if (vlc_stream_Tell(demux->s) != 0 && vlc_stream_Seek(demux->s, 0))
    {
        msg_Err(demux, "seek failure before probing");
        return VLC_EGENERIC;
    }

    return probe(VLC_OBJECT(demux));
}

François Cartegnie's avatar
François Cartegnie committed
194 195 196 197 198 199 200 201
/*****************************************************************************
 * demux_NewAdvanced:
 *  if s is NULL then load a access_demux
 *****************************************************************************/
#undef demux_NewAdvanced
demux_t *demux_NewAdvanced( vlc_object_t *p_obj, input_thread_t *p_parent_input,
                            const char *psz_access, const char *psz_demux,
                            const char *psz_location,
202
                            stream_t *s, es_out_t *out, bool b_preparsing )
203
{
204 205
    demux_priv_t *priv = vlc_custom_create(p_obj, sizeof (*priv), "demux");
    if (unlikely(priv == NULL))
206
        return NULL;
207

208 209
    demux_t *p_demux = &priv->demux;

210
    if( s != NULL && (!strcasecmp( psz_demux, "any" ) || !psz_demux[0]) )
211 212
    {   /* Look up demux by mime-type for hard to detect formats */
        char *type = stream_MimeType( s );
213 214
        if( type != NULL )
        {
215
            psz_demux = demux_NameFromMimeType( type );
216 217 218 219
            free( type );
        }
    }

220
    p_demux->p_input = p_parent_input;
221
    p_demux->psz_access = strdup( psz_access );
222
    p_demux->psz_demux = strdup( psz_demux );
223
    p_demux->psz_location = strdup( psz_location );
224
    p_demux->psz_file = get_path( psz_location ); /* parse URL */
225

226 227 228 229
    if( unlikely(p_demux->psz_access == NULL
              || p_demux->psz_demux == NULL
              || p_demux->psz_location == NULL) )
        goto error;
230

231
    if( !b_preparsing )
232 233 234 235
        msg_Dbg( p_obj, "creating demux: access='%s' demux='%s' "
                 "location='%s' file='%s'",
                 p_demux->psz_access, p_demux->psz_demux,
                 p_demux->psz_location, p_demux->psz_file );
236

237 238 239
    p_demux->s              = s;
    p_demux->out            = out;
    p_demux->b_preparsing   = b_preparsing;
240 241 242 243

    p_demux->pf_demux   = NULL;
    p_demux->pf_control = NULL;
    p_demux->p_sys      = NULL;
244 245 246
    p_demux->info.i_update = 0;
    p_demux->info.i_title  = 0;
    p_demux->info.i_seekpoint = 0;
247
    priv->destroy = s ? demux_DestroyDemux : demux_DestroyAccessDemux;
248

249 250
    if( s != NULL )
    {
251
        const char *psz_module = NULL;
252

253
        if( !strcmp( p_demux->psz_demux, "any" ) && p_demux->psz_file )
254
        {
255 256 257 258
            char const* psz_ext = strrchr( p_demux->psz_file, '.' );

            if( psz_ext )
                psz_module = DemuxNameFromExtension( psz_ext + 1, b_preparsing );
259 260
        }

261 262 263
        if( psz_module == NULL )
            psz_module = p_demux->psz_demux;

264 265
        p_demux->p_module = vlc_module_load(p_demux, "demux", psz_module,
             !strcmp(psz_module, p_demux->psz_demux), demux_Probe, p_demux);
266 267 268 269
    }
    else
    {
        p_demux->p_module =
270
            module_need( p_demux, "access_demux", p_demux->psz_access, true );
271
    }
272

273
    if( p_demux->p_module == NULL )
274
        goto error;
275 276

    return p_demux;
277 278 279 280 281 282 283
error:
    free( p_demux->psz_file );
    free( p_demux->psz_location );
    free( p_demux->psz_demux );
    free( p_demux->psz_access );
    vlc_object_release( p_demux );
    return NULL;
284 285 286
}

/*****************************************************************************
287
 * demux_Delete:
288
 *****************************************************************************/
289
void demux_Delete( demux_t *p_demux )
290
{
291
    demux_priv_t *priv = (demux_priv_t *)p_demux;
292

293
    module_unneed( p_demux, p_demux->p_module );
294 295

    priv->destroy(p_demux);
296 297
    free( p_demux->psz_file );
    free( p_demux->psz_location );
298 299
    free( p_demux->psz_demux );
    free( p_demux->psz_access );
300
    vlc_object_release( p_demux );
301
}
302

303 304 305
#define static_control_match(foo) \
    static_assert((unsigned) DEMUX_##foo == STREAM_##foo, "Mismatch")

306 307 308 309 310 311 312 313 314 315 316
static int demux_ControlInternal( demux_t *demux, int query, ... )
{
    int ret;
    va_list ap;

    va_start( ap, query );
    ret = demux->pf_control( demux, query, ap );
    va_end( ap );
    return ret;
}

317 318
int demux_vaControl( demux_t *demux, int query, va_list args )
{
319 320 321 322 323 324 325 326 327 328 329 330 331 332
    if( demux->s != NULL )
        switch( query )
        {
            /* Legacy fallback for missing getters in synchronous demuxers */
            case DEMUX_CAN_PAUSE:
            case DEMUX_CAN_CONTROL_PACE:
            case DEMUX_GET_PTS_DELAY:
            {
                int ret;
                va_list ap;

                va_copy( ap, args );
                ret = demux->pf_control( demux, query, args );
                if( ret != VLC_SUCCESS )
333
                    ret = vlc_stream_vaControl( demux->s, query, ap );
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
                va_end( ap );
                return ret;
            }

            /* Some demuxers need to control pause directly (e.g. adaptive),
             * but many legacy demuxers do not understand pause at all.
             * If DEMUX_CAN_PAUSE is not implemented, bypass the demuxer and
             * byte stream. If DEMUX_CAN_PAUSE is implemented and pause is
             * supported, pause the demuxer normally. Else, something went very
             * wrong.
             *
             * Note that this requires asynchronous/threaded demuxers to
             * always return VLC_SUCCESS for DEMUX_CAN_PAUSE, so that they are
             * never bypassed. Otherwise, we would reenter demux->s callbacks
             * and break thread safety. At the time of writing, asynchronous or
             * threaded *non-access* demuxers do not exist and are not fully
             * supported by the input thread, so this is theoretical. */
            case DEMUX_SET_PAUSE_STATE:
            {
                bool can_pause;

                if( demux_ControlInternal( demux, DEMUX_CAN_PAUSE,
                                           &can_pause ) )
357
                    return vlc_stream_vaControl( demux->s, query, args );
358 359 360 361 362 363 364

                /* The caller shall not pause if pause is unsupported. */
                assert( can_pause );
                break;
            }
        }

365 366 367
    return demux->pf_control( demux, query, args );
}

368
/*****************************************************************************
369
 * demux_vaControlHelper:
370
 *****************************************************************************/
371
int demux_vaControlHelper( stream_t *s,
372
                            int64_t i_start, int64_t i_end,
373
                            int64_t i_bitrate, int i_align,
374 375 376 377 378 379 380 381 382
                            int i_query, va_list args )
{
    int64_t i_tell;
    double  f, *pf;
    int64_t i64, *pi64;

    if( i_end < 0 )    i_end   = stream_Size( s );
    if( i_start < 0 )  i_start = 0;
    if( i_align <= 0 ) i_align = 1;
383
    i_tell = vlc_stream_Tell( s );
384

385 386 387 388 389 390 391
    static_control_match(CAN_PAUSE);
    static_control_match(CAN_CONTROL_PACE);
    static_control_match(GET_PTS_DELAY);
    static_control_match(GET_META);
    static_control_match(GET_SIGNAL);
    static_control_match(SET_PAUSE_STATE);

392 393
    switch( i_query )
    {
394 395 396 397 398
        case DEMUX_CAN_SEEK:
        {
            bool *b = va_arg( args, bool * );

            if( (i_bitrate <= 0 && i_start >= i_end)
399
             || vlc_stream_Control( s, STREAM_CAN_SEEK, b ) )
400 401 402 403 404 405 406 407 408 409
                *b = false;
            break;
        }

        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_CONTROL_PACE:
        case DEMUX_GET_PTS_DELAY:
        case DEMUX_GET_META:
        case DEMUX_GET_SIGNAL:
        case DEMUX_SET_PAUSE_STATE:
410
            return vlc_stream_vaControl( s, i_query, args );
411

412 413 414 415
        case DEMUX_GET_LENGTH:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            if( i_bitrate > 0 && i_end > i_start )
            {
416
                *pi64 = INT64_C(8000000) * (i_end - i_start) / i_bitrate;
417 418 419 420 421 422
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
423
            if( i_bitrate > 0 && i_tell >= i_start )
424
            {
425
                *pi64 = INT64_C(8000000) * (i_tell - i_start) / i_bitrate;
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

        case DEMUX_GET_POSITION:
            pf = (double*)va_arg( args, double * );
            if( i_start < i_end )
            {
                *pf = (double)( i_tell - i_start ) /
                      (double)( i_end  - i_start );
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;


        case DEMUX_SET_POSITION:
            f = (double)va_arg( args, double );
            if( i_start < i_end && f >= 0.0 && f <= 1.0 )
            {
                int64_t i_block = (f * ( i_end - i_start )) / i_align;

447
                if( vlc_stream_Seek( s, i_start + i_block * i_align ) )
448 449 450 451 452 453 454 455 456 457 458
                {
                    return VLC_EGENERIC;
                }
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

        case DEMUX_SET_TIME:
            i64 = (int64_t)va_arg( args, int64_t );
            if( i_bitrate > 0 && i64 >= 0 )
            {
459
                int64_t i_block = i64 * i_bitrate / INT64_C(8000000) / i_align;
460
                if( vlc_stream_Seek( s, i_start + i_block * i_align ) )
461 462 463 464 465 466 467
                {
                    return VLC_EGENERIC;
                }
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

468
        case DEMUX_IS_PLAYLIST:
469 470
            *va_arg( args, bool * ) = false;
            return VLC_SUCCESS;
471

472
        case DEMUX_GET_FPS:
473
        case DEMUX_HAS_UNSUPPORTED_META:
474 475
        case DEMUX_SET_NEXT_DEMUX_TIME:
        case DEMUX_GET_TITLE_INFO:
476
        case DEMUX_SET_GROUP:
477
        case DEMUX_SET_ES:
478
        case DEMUX_GET_ATTACHMENTS:
479
        case DEMUX_CAN_RECORD:
480
        case DEMUX_TEST_AND_CLEAR_FLAGS:
481 482
        case DEMUX_GET_TITLE:
        case DEMUX_GET_SEEKPOINT:
483 484 485 486 487 488 489
        case DEMUX_NAV_ACTIVATE:
        case DEMUX_NAV_UP:
        case DEMUX_NAV_DOWN:
        case DEMUX_NAV_LEFT:
        case DEMUX_NAV_RIGHT:
        case DEMUX_NAV_POPUP:
        case DEMUX_NAV_MENU:
490 491
        case DEMUX_FILTER_ENABLE:
        case DEMUX_FILTER_DISABLE:
492 493
            return VLC_EGENERIC;

494 495 496 497
        case DEMUX_SET_TITLE:
        case DEMUX_SET_SEEKPOINT:
        case DEMUX_SET_RECORD_STATE:
            assert(0);
498
        default:
499
            msg_Err( s, "unknown query 0x%x in %s", i_query, __func__ );
500 501
            return VLC_EGENERIC;
    }
502
    return VLC_SUCCESS;
503 504
}

505 506 507
/****************************************************************************
 * Utility functions
 ****************************************************************************/
508 509
decoder_t *demux_PacketizerNew( demux_t *p_demux, es_format_t *p_fmt, const char *psz_msg )
{
510 511
    decoder_t *p_packetizer;
    p_packetizer = vlc_custom_create( p_demux, sizeof( *p_packetizer ),
512
                                      "demux packetizer" );
513 514 515 516 517
    if( !p_packetizer )
    {
        es_format_Clean( p_fmt );
        return NULL;
    }
518
    p_fmt->b_packetized = false;
519

520
    p_packetizer->pf_decode = NULL;
521 522 523
    p_packetizer->pf_packetize = NULL;

    p_packetizer->fmt_in = *p_fmt;
524
    es_format_Init( &p_packetizer->fmt_out, p_fmt->i_cat, 0 );
525

526
    p_packetizer->p_module = module_need( p_packetizer, "packetizer", NULL, false );
527 528 529 530 531 532 533 534 535 536
    if( !p_packetizer->p_module )
    {
        es_format_Clean( p_fmt );
        vlc_object_release( p_packetizer );
        msg_Err( p_demux, "cannot find packetizer for %s", psz_msg );
        return NULL;
    }

    return p_packetizer;
}
537

538 539 540
void demux_PacketizerDestroy( decoder_t *p_packetizer )
{
    if( p_packetizer->p_module )
541
        module_unneed( p_packetizer, p_packetizer->p_module );
542
    es_format_Clean( &p_packetizer->fmt_in );
543
    es_format_Clean( &p_packetizer->fmt_out );
544 545
    if( p_packetizer->p_description )
        vlc_meta_Delete( p_packetizer->p_description );
546 547 548
    vlc_object_release( p_packetizer );
}

549
unsigned demux_TestAndClearFlags( demux_t *p_demux, unsigned flags )
550
{
551 552 553 554 555 556
    unsigned update = flags;

    if ( demux_Control( p_demux, DEMUX_TEST_AND_CLEAR_FLAGS, &update ) == VLC_SUCCESS )
        return update;

    update = p_demux->info.i_update & flags;
557
    p_demux->info.i_update &= ~flags;
558
    return update;
559 560 561 562
}

int demux_GetTitle( demux_t *p_demux )
{
563 564 565
    int i_title;
    if ( demux_Control( p_demux, DEMUX_GET_TITLE, &i_title ) == VLC_SUCCESS )
        return i_title;
566 567 568 569 570
    return p_demux->info.i_title;
}

int demux_GetSeekpoint( demux_t *p_demux )
{
571 572 573
    int i_seekpoint;
    if ( demux_Control( p_demux, DEMUX_GET_SEEKPOINT, &i_seekpoint ) == VLC_SUCCESS  )
        return i_seekpoint;
574 575
    return p_demux->info.i_seekpoint;
}
576 577 578

static demux_t *demux_FilterNew( demux_t *p_next, const char *p_name )
{
579 580
    demux_priv_t *priv = vlc_custom_create(p_next, sizeof (*priv), "demux_filter");
    if (unlikely(priv == NULL))
581 582
        return NULL;

583 584
    demux_t *p_demux = &priv->demux;

585 586 587 588 589 590 591 592
    p_demux->p_next       = p_next;
    p_demux->p_input      = NULL;
    p_demux->p_sys        = NULL;
    p_demux->psz_access   = NULL;
    p_demux->psz_demux    = NULL;
    p_demux->psz_location = NULL;
    p_demux->psz_file     = NULL;
    p_demux->out          = NULL;
593
    priv->destroy         = demux_DestroyDemuxFilter;
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
    p_demux->p_module =
        module_need( p_demux, "demux_filter", p_name, p_name != NULL );

    if( p_demux->p_module == NULL )
        goto error;

    return p_demux;
error:
    vlc_object_release( p_demux );
    return NULL;
}

demux_t *demux_FilterChainNew( demux_t *p_demux, const char *psz_chain )
{
    if( !psz_chain || !*psz_chain )
        return NULL;

    char *psz_parser = strdup(psz_chain);
    if(!psz_parser)
        return NULL;

    /* parse chain */
    while(psz_parser)
    {
        config_chain_t *p_cfg;
        char *psz_name;
        char *psz_rest_chain = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
        free( psz_parser );
        psz_parser = psz_rest_chain;

624 625 626
        demux_t *filter = demux_FilterNew(p_demux, psz_name);
        if (filter != NULL)
            p_demux = filter;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
627

628 629
        free(psz_name);
        config_ChainDestroy(p_cfg);
630 631 632 633
    }

    return p_demux;
}
634 635 636 637 638

static bool demux_filter_enable_disable( demux_t *p_demux_chain,
                                          const char* psz_demux, bool b_enable )
{
    demux_t *p_demux = p_demux_chain;
639 640 641 642 643 644 645

     if( strcmp( module_get_name( p_demux->p_module, false ), psz_demux) == 0 ||
         strcmp( module_get_name( p_demux->p_module, true ), psz_demux ) == 0 )
     {
        demux_Control( p_demux,
                       b_enable ? DEMUX_FILTER_ENABLE : DEMUX_FILTER_DISABLE );
        return true;
646 647 648 649 650 651 652 653 654 655 656 657 658
    }
    return false;
}

bool demux_FilterEnable( demux_t *p_demux_chain, const char* psz_demux )
{
    return demux_filter_enable_disable( p_demux_chain, psz_demux, true );
}

bool demux_FilterDisable( demux_t *p_demux_chain, const char* psz_demux )
{
    return demux_filter_enable_disable( p_demux_chain, psz_demux, false );
}