es_out.c 32.4 KB
Newer Older
1 2 3
/*****************************************************************************
 * es_out.c: Es Out handler for input.
 *****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
4
 * Copyright (C) 2003-2004 VideoLAN
5
 * $Id$
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 31 32
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>

#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/decoder.h>

Laurent Aimar's avatar
Laurent Aimar committed
33 34
#include "input_internal.h"

35
#include "vlc_playlist.h"
36
#include "iso_lang.h"
37 38 39 40

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
typedef struct
{
    /* Program ID */
    int i_id;

    /* Number of es for this pgrm */
    int i_es;

    vlc_bool_t b_selected;

    /* Clock for this program */
    input_clock_t clock;

} es_out_pgrm_t;

56 57
struct es_out_id_t
{
Laurent Aimar's avatar
Laurent Aimar committed
58 59 60 61 62 63 64 65 66
    /* ES ID */
    int       i_id;
    es_out_pgrm_t *p_pgrm;

    /* Channel in the track type */
    int         i_channel;
    es_format_t fmt;
    char        *psz_description;
    decoder_t   *p_dec;
67 68
};

69 70 71 72
struct es_out_sys_t
{
    input_thread_t *p_input;

Laurent Aimar's avatar
Laurent Aimar committed
73 74 75
    /* all programs */
    int           i_pgrm;
    es_out_pgrm_t **pgrm;
76
    es_out_pgrm_t **pp_selected_pgrm; /* --programs */
Laurent Aimar's avatar
Laurent Aimar committed
77 78
    es_out_pgrm_t *p_pgrm;  /* Master program */

79
    /* all es */
80
    int         i_id;
81 82
    int         i_es;
    es_out_id_t **es;
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    /* mode gestion */
    vlc_bool_t  b_active;
    int         i_mode;

    /* es count */
    int         i_audio;
    int         i_video;
    int         i_sub;

    /* es to select */
    int         i_audio_last;
    int         i_sub_last;

    /* current main es */
    es_out_id_t *p_es_audio;
    es_out_id_t *p_es_video;
    es_out_id_t *p_es_sub;
101 102 103 104

    /* delay */
    int64_t i_audio_delay;
    int64_t i_spu_delay;
105 106 107 108 109
};

static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );
static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );
static void         EsOutDel    ( es_out_t *, es_out_id_t * );
Laurent Aimar's avatar
Laurent Aimar committed
110
static void         EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force );
111 112
static int          EsOutControl( es_out_t *, int i_query, va_list );

113
static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
Laurent Aimar's avatar
Laurent Aimar committed
114 115 116

static void EsSelect( es_out_t *out, es_out_id_t *es );
static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );
117
static char *LanguageGetName( const char *psz_code );
118

Laurent Aimar's avatar
Laurent Aimar committed
119 120 121
/*****************************************************************************
 * input_EsOutNew:
 *****************************************************************************/
122 123
es_out_t *input_EsOutNew( input_thread_t *p_input )
{
124 125 126
    es_out_t     *out = malloc( sizeof( es_out_t ) );
    es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
    vlc_value_t  val;
127 128 129 130 131

    out->pf_add     = EsOutAdd;
    out->pf_send    = EsOutSend;
    out->pf_del     = EsOutDel;
    out->pf_control = EsOutControl;
132 133 134 135 136 137 138 139
    out->p_sys      = p_sys;

    p_sys->p_input = p_input;

    p_sys->b_active = VLC_FALSE;
    p_sys->i_mode   = ES_OUT_MODE_AUTO;


Laurent Aimar's avatar
Laurent Aimar committed
140 141 142 143 144
    p_sys->i_pgrm   = 0;
    p_sys->pgrm     = NULL;
    p_sys->p_pgrm   = NULL;

    p_sys->i_id    = 0;
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    p_sys->i_es    = 0;
    p_sys->es      = NULL;

    p_sys->i_audio = 0;
    p_sys->i_video = 0;
    p_sys->i_sub   = 0;

    var_Get( p_input, "audio-channel", &val );
    p_sys->i_audio_last = val.i_int;

    var_Get( p_input, "spu-channel", &val );
    p_sys->i_sub_last = val.i_int;

    p_sys->p_es_audio = NULL;
    p_sys->p_es_video = NULL;
    p_sys->p_es_sub   = NULL;
161

162 163 164
    p_sys->i_audio_delay= 0;
    p_sys->i_spu_delay  = 0;

165 166 167
    return out;
}

Laurent Aimar's avatar
Laurent Aimar committed
168 169 170
/*****************************************************************************
 * input_EsOutDelete:
 *****************************************************************************/
171 172 173 174 175
void input_EsOutDelete( es_out_t *out )
{
    es_out_sys_t *p_sys = out->p_sys;
    int i;

176
    for( i = 0; i < p_sys->i_es; i++ )
177
    {
Laurent Aimar's avatar
Laurent Aimar committed
178 179 180 181 182 183 184 185
        if( p_sys->es[i]->p_dec )
        {
            input_DecoderDelete( p_sys->es[i]->p_dec );
        }
        if( p_sys->es[i]->psz_description )
            free( p_sys->es[i]->psz_description );
        es_format_Clean( &p_sys->es[i]->fmt );

186
        free( p_sys->es[i] );
187
    }
188 189
    if( p_sys->es )
        free( p_sys->es );
Laurent Aimar's avatar
Laurent Aimar committed
190 191 192 193

    for( i = 0; i < p_sys->i_pgrm; i++ )
    {
        free( p_sys->pgrm[i] );
194
    }
Laurent Aimar's avatar
Laurent Aimar committed
195 196 197
    if( p_sys->pgrm )
        free( p_sys->pgrm );

198 199 200
    free( p_sys );
    free( out );
}
Clément Stenac's avatar
Clément Stenac committed
201

Laurent Aimar's avatar
Laurent Aimar committed
202
es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
203
{
Laurent Aimar's avatar
Laurent Aimar committed
204 205 206 207 208 209
    int i;
    if( i_id < 0 )
    {
        /* Special HACK, -i_id is tha cat of the stream */
        return (es_out_id_t*)((uint8_t*)NULL-i_id);
    }
210

Laurent Aimar's avatar
Laurent Aimar committed
211 212 213 214 215 216 217
    for( i = 0; i < out->p_sys->i_es; i++ )
    {
        if( out->p_sys->es[i]->i_id == i_id )
            return out->p_sys->es[i];
    }
    return NULL;
}
218

Laurent Aimar's avatar
Laurent Aimar committed
219 220 221 222
void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio )
{
    es_out_sys_t      *p_sys = out->p_sys;
    int i;
223

Laurent Aimar's avatar
Laurent Aimar committed
224
    for( i = 0; i < p_sys->i_es; i++ )
225
    {
Laurent Aimar's avatar
Laurent Aimar committed
226
        es_out_id_t *es = p_sys->es[i];
227

Laurent Aimar's avatar
Laurent Aimar committed
228 229 230 231 232 233 234
        /* Send a dummy block to let decoder know that
         * there is a discontinuity */
        if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )
        {
            input_DecoderDiscontinuity( es->p_dec );
        }
    }
235
}
236

237 238 239 240 241 242 243 244 245 246
void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
{
    es_out_sys_t *p_sys = out->p_sys;

    if( i_cat == AUDIO_ES )
        p_sys->i_audio_delay = i_delay;
    else if( i_cat == SPU_ES )
        p_sys->i_spu_delay = i_delay;
}

Laurent Aimar's avatar
 
Laurent Aimar committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out )
{
    es_out_sys_t      *p_sys = out->p_sys;
    int i;

    for( i = 0; i < p_sys->i_es; i++ )
    {
        es_out_id_t *es = p_sys->es[i];

        if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )
            return VLC_FALSE;
    }
    return VLC_TRUE;
}

Laurent Aimar's avatar
Laurent Aimar committed
262
/*****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
263
 *
Laurent Aimar's avatar
Laurent Aimar committed
264
 *****************************************************************************/
265 266
static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
                              vlc_bool_t b_delete )
267 268 269
{
    es_out_sys_t      *p_sys = out->p_sys;
    input_thread_t    *p_input = p_sys->p_input;
Laurent Aimar's avatar
Laurent Aimar committed
270
    vlc_value_t       val, text;
271

Laurent Aimar's avatar
Laurent Aimar committed
272
    char *psz_var;
273

Laurent Aimar's avatar
Laurent Aimar committed
274 275 276 277 278 279 280
    if( es->fmt.i_cat == AUDIO_ES )
        psz_var = "audio-es";
    else if( es->fmt.i_cat == VIDEO_ES )
        psz_var = "video-es";
    else if( es->fmt.i_cat == SPU_ES )
        psz_var = "spu-es";
    else
281
        return;
Laurent Aimar's avatar
Laurent Aimar committed
282

283 284 285 286 287 288 289 290
    if( b_delete )
    {
        val.i_int = es->i_id;
        var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
        var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
        return;
    }

Laurent Aimar's avatar
Laurent Aimar committed
291 292 293 294 295 296 297 298 299 300
    /* Get the number of ES already added */
    var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
    if( val.i_int == 0 )
    {
        vlc_value_t val2;

        /* First one, we need to add the "Disable" choice */
        val2.i_int = -1; text.psz_string = _("Disable");
        var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
        val.i_int++;
301 302
    }

Laurent Aimar's avatar
Laurent Aimar committed
303 304
    /* Take care of the ES description */
    if( es->psz_description && *es->psz_description )
305
    {
Laurent Aimar's avatar
Laurent Aimar committed
306
        text.psz_string = strdup( es->psz_description );
307
    }
Laurent Aimar's avatar
Laurent Aimar committed
308
    else
309
    {
Laurent Aimar's avatar
Laurent Aimar committed
310 311 312
        text.psz_string = malloc( strlen( _("Track %i") ) + 20 );
        sprintf( text.psz_string, _("Track %i"), val.i_int );
    }
313

Laurent Aimar's avatar
Laurent Aimar committed
314 315
    val.i_int = es->i_id;
    var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
316

Laurent Aimar's avatar
Laurent Aimar committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
    free( text.psz_string );

    var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}

/* EsOutProgramSelect:
 *  Select a program and update the object variable
 */
static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
{
    es_out_sys_t      *p_sys = out->p_sys;
    input_thread_t    *p_input = p_sys->p_input;
    vlc_value_t       val;
    int               i;

    if( p_sys->p_pgrm == p_pgrm )
        return; /* Nothing to do */

    if( p_sys->p_pgrm )
    {
        es_out_pgrm_t *old = p_sys->p_pgrm;
        msg_Dbg( p_input, "Unselecting program id=%d", old->i_id );
339

Laurent Aimar's avatar
Laurent Aimar committed
340
        for( i = 0; i < p_sys->i_es; i++ )
341
        {
Laurent Aimar's avatar
Laurent Aimar committed
342 343 344
            if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec &&
                p_sys->i_mode != ES_OUT_MODE_ALL )
                EsUnselect( out, p_sys->es[i], VLC_TRUE );
345 346 347
        }
    }

Laurent Aimar's avatar
Laurent Aimar committed
348 349 350 351 352 353 354
    msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id );

    /* Mark it selected */
    p_pgrm->b_selected = VLC_TRUE;

    /* Switch master stream */
    if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )
355
    {
Laurent Aimar's avatar
Laurent Aimar committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
        p_sys->p_pgrm->clock.b_master = VLC_FALSE;
    }
    p_pgrm->clock.b_master = VLC_TRUE;
    p_sys->p_pgrm = p_pgrm;

    /* Update "program" */
    val.i_int = p_pgrm->i_id;
    var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );

    /* Update "es-*" */
    var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
    var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
    var_Change( p_input, "spu-es",   VLC_VAR_CLEARCHOICES, NULL, NULL );
    for( i = 0; i < p_sys->i_es; i++ )
    {
371
        EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE );
Laurent Aimar's avatar
Laurent Aimar committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
        EsOutSelect( out, p_sys->es[i], VLC_FALSE );
    }

    var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}

/* EsOutAddProgram:
 *  Add a program
 */
static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
{
    es_out_sys_t      *p_sys = out->p_sys;
    input_thread_t    *p_input = p_sys->p_input;
    vlc_value_t       val;

    es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );

    /* Init */
    p_pgrm->i_id = i_group;
    p_pgrm->i_es = 0;
    p_pgrm->b_selected = VLC_FALSE;
    input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average );

    /* Append it */
    TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );

    /* Update "program" variable */
    val.i_int = i_group;
    var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );

    if( i_group == var_GetInteger( p_input, "program" ) )
    {
        EsOutProgramSelect( out, p_pgrm );
    }
    else
    {
        var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
409
    }
Laurent Aimar's avatar
Laurent Aimar committed
410
    return p_pgrm;
411 412
}

Laurent Aimar's avatar
Laurent Aimar committed
413 414
/* EsOutAdd:
 *  Add an es_out
Clément Stenac's avatar
Clément Stenac committed
415
 */
416 417 418 419
static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
{
    es_out_sys_t      *p_sys = out->p_sys;
    input_thread_t    *p_input = p_sys->p_input;
Laurent Aimar's avatar
Laurent Aimar committed
420

421
    es_out_id_t       *es = malloc( sizeof( es_out_id_t ) );
Laurent Aimar's avatar
Laurent Aimar committed
422 423
    es_out_pgrm_t     *p_pgrm = NULL;
    int i;
424

Laurent Aimar's avatar
Laurent Aimar committed
425
    if( fmt->i_group < 0 )
426
    {
427
        msg_Err( p_input, "invalid group number" );
Laurent Aimar's avatar
Laurent Aimar committed
428 429
        return NULL;
    }
430

Laurent Aimar's avatar
Laurent Aimar committed
431 432 433 434
    /* Search the program */
    for( i = 0; i < p_sys->i_pgrm; i++ )
    {
        if( fmt->i_group == p_sys->pgrm[i]->i_id )
435
        {
Laurent Aimar's avatar
Laurent Aimar committed
436 437
            p_pgrm = p_sys->pgrm[i];
            break;
438 439
        }
    }
Laurent Aimar's avatar
Laurent Aimar committed
440
    if( p_pgrm == NULL )
441
    {
Laurent Aimar's avatar
Laurent Aimar committed
442 443
        /* Create a new one */
        p_pgrm = EsOutProgramAdd( out, fmt->i_group );
444
    }
445

Laurent Aimar's avatar
Laurent Aimar committed
446 447
    /* Increase ref count for program */
    p_pgrm->i_es++;
448

Laurent Aimar's avatar
Laurent Aimar committed
449 450 451 452 453 454
    /* Set up ES */
    if( fmt->i_id < 0 )
        fmt->i_id = out->p_sys->i_id;
    es->i_id = fmt->i_id;
    es->p_pgrm = p_pgrm;
    es_format_Copy( &es->fmt, fmt );
455 456
    switch( fmt->i_cat )
    {
457 458 459
    case AUDIO_ES:
        es->i_channel = p_sys->i_audio;
        break;
460

461 462 463
    case VIDEO_ES:
        es->i_channel = p_sys->i_video;
        break;
464

465
    case SPU_ES:
466 467
        es->i_channel = p_sys->i_sub;
        break;
468

469 470 471
    default:
        es->i_channel = 0;
        break;
472
    }
Laurent Aimar's avatar
Laurent Aimar committed
473 474 475 476
    es->psz_description = LanguageGetName( fmt->psz_language );
    es->p_dec = NULL;

    if( es->p_pgrm == p_sys->p_pgrm )
477
        EsOutESVarUpdate( out, es, VLC_FALSE );
478

Laurent Aimar's avatar
Laurent Aimar committed
479 480
    /* Select it if needed */
    EsOutSelect( out, es, VLC_FALSE );
481

482 483 484 485

    TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
    p_sys->i_id++;  /* always incremented */
    switch( fmt->i_cat )
486
    {
487 488 489 490 491 492 493 494 495
        case AUDIO_ES:
            p_sys->i_audio++;
            break;
        case SPU_ES:
            p_sys->i_sub++;
            break;
        case VIDEO_ES:
            p_sys->i_video++;
            break;
496
    }
497

498 499
    EsOutAddInfo( out, es );

500
    return es;
501 502
}

Laurent Aimar's avatar
Laurent Aimar committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
static void EsSelect( es_out_t *out, es_out_id_t *es )
{
    es_out_sys_t   *p_sys = out->p_sys;
    input_thread_t *p_input = p_sys->p_input;
    vlc_value_t    val;
    char           *psz_var;

    if( es->p_dec )
    {
        msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
        return;
    }

    if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
    {
518 519
        if( !var_GetBool( p_input, "video" ) ||
            ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) )
Laurent Aimar's avatar
Laurent Aimar committed
520
        {
521 522
            msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
                     es->i_id );
Laurent Aimar's avatar
Laurent Aimar committed
523 524 525 526 527 528
            return;
        }
    }
    else if( es->fmt.i_cat == AUDIO_ES )
    {
        var_Get( p_input, "audio", &val );
529 530
        if( !var_GetBool( p_input, "audio" ) ||
            ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) )
Laurent Aimar's avatar
Laurent Aimar committed
531
        {
532 533
            msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
                     es->i_id );
Laurent Aimar's avatar
Laurent Aimar committed
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 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
            return;
        }
    }

    es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE );
    if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
        return;

    if( es->fmt.i_cat == VIDEO_ES )
        psz_var = "video-es";
    else if( es->fmt.i_cat == AUDIO_ES )
        psz_var = "audio-es";
    else if( es->fmt.i_cat == SPU_ES )
        psz_var = "spu-es";
    else
        return;

    /* Mark it as selected */
    val.i_int = es->i_id;
    var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );


    var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}

static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update )
{
    es_out_sys_t   *p_sys = out->p_sys;
    input_thread_t *p_input = p_sys->p_input;
    vlc_value_t    val;
    char           *psz_var;

    if( es->p_dec == NULL )
    {
        msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
        return;
    }

    input_DecoderDelete( es->p_dec );
    es->p_dec = NULL;

    if( !b_update )
        return;

    /* Update var */
    if( es->p_dec == NULL )
        return;
    if( es->fmt.i_cat == VIDEO_ES )
        psz_var = "video-es";
    else if( es->fmt.i_cat == AUDIO_ES )
        psz_var = "audio-es";
    else if( es->fmt.i_cat == SPU_ES )
        psz_var = "spu-es";
    else
        return;

    /* Mark it as selected */
    val.i_int = -1;
    var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );

    var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}

/**
 * Select an ES given the current mode
 * XXX: you need to take a the lock before (stream.stream_lock)
 *
 * \param out The es_out structure
 * \param es es_out_id structure
 * \param b_force ...
 * \return nothing
 */
static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )
{
    es_out_sys_t      *p_sys = out->p_sys;

    int i_cat = es->fmt.i_cat;

    if( !p_sys->b_active ||
        ( !b_force && es->fmt.i_priority < 0 ) )
    {
        return;
    }

    if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
    {
        if( !es->p_dec )
            EsSelect( out, es );
    }
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
    else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
    {
        vlc_value_t val;
        int i;
        var_Get( p_sys->p_input, "programs", &val );
        for ( i = 0; i < val.p_list->i_count; i++ )
        {
            if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )
            {
                if( !es->p_dec )
                    EsSelect( out, es );
                break;
            }
        }
        var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );
    }
Laurent Aimar's avatar
Laurent Aimar committed
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
    else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
    {
        int i_wanted  = -1;

        if( es->p_pgrm != p_sys->p_pgrm )
            return;

        if( i_cat == AUDIO_ES )
        {
            if( p_sys->p_es_audio &&
                p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
            {
                return;
            }
            i_wanted  = p_sys->i_audio_last >= 0 ?
                            p_sys->i_audio_last : es->i_channel;
        }
        else if( i_cat == SPU_ES )
        {
            if( p_sys->p_es_sub &&
                p_sys->p_es_sub->fmt.i_priority >=
                    es->fmt.i_priority )
            {
                return;
            }
            i_wanted  = p_sys->i_sub_last;
        }
        else if( i_cat == VIDEO_ES )
        {
            i_wanted  = es->i_channel;
        }

        if( i_wanted == es->i_channel && es->p_dec == NULL )
            EsSelect( out, es );
    }

    /* FIXME TODO handle priority here */
    if( es->p_dec )
    {
        if( i_cat == AUDIO_ES )
        {
            if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
681 682 683
                p_sys->p_es_audio &&
                p_sys->p_es_audio != es &&
                p_sys->p_es_audio->p_dec )
Laurent Aimar's avatar
Laurent Aimar committed
684 685 686 687 688 689 690 691
            {
                EsUnselect( out, p_sys->p_es_audio, VLC_FALSE );
            }
            p_sys->p_es_audio = es;
        }
        else if( i_cat == SPU_ES )
        {
            if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
692 693 694
                p_sys->p_es_sub &&
                p_sys->p_es_sub != es &&
                p_sys->p_es_sub->p_dec )
Laurent Aimar's avatar
Laurent Aimar committed
695 696 697 698 699 700 701 702 703 704 705 706
            {
                EsUnselect( out, p_sys->p_es_sub, VLC_FALSE );
            }
            p_sys->p_es_sub = es;
        }
        else if( i_cat == VIDEO_ES )
        {
            p_sys->p_es_video = es;
        }
    }
}

Clément Stenac's avatar
Clément Stenac committed
707 708 709 710 711 712 713
/**
 * Send a block for the given es_out
 *
 * \param out the es_out to send from
 * \param es the es_out_id
 * \param p_block the data block to send
 */
714
static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
715
{
716
    es_out_sys_t *p_sys = out->p_sys;
Laurent Aimar's avatar
Laurent Aimar committed
717
    input_thread_t    *p_input = p_sys->p_input;
Laurent Aimar's avatar
Laurent Aimar committed
718
    es_out_pgrm_t *p_pgrm = es->p_pgrm;
719 720 721 722 723 724 725 726
    int64_t i_delay;

    if( es->fmt.i_cat == AUDIO_ES )
        i_delay = p_sys->i_audio_delay;
    else if( es->fmt.i_cat == SPU_ES )
        i_delay = p_sys->i_spu_delay;
    else
        i_delay = 0;
727

Laurent Aimar's avatar
Laurent Aimar committed
728
    /* +11 -> avoid null value with non null dts/pts */
Laurent Aimar's avatar
Laurent Aimar committed
729
    if( p_block->i_dts > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
730 731
    {
        p_block->i_dts =
732 733
            input_ClockGetTS( p_input, &p_pgrm->clock,
                              ( p_block->i_dts + 11 ) * 9 / 100 ) + i_delay;
Laurent Aimar's avatar
Laurent Aimar committed
734
    }
Laurent Aimar's avatar
Laurent Aimar committed
735
    if( p_block->i_pts > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
736 737
    {
        p_block->i_pts =
738 739
            input_ClockGetTS( p_input, &p_pgrm->clock,
                              ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay;
740
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
741

Laurent Aimar's avatar
Laurent Aimar committed
742 743 744
    p_block->i_rate = p_input->i_rate;

    /* TODO handle mute */
745 746
    if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES ||
        p_input->i_rate == INPUT_RATE_DEFAULT ) )
747
    {
Laurent Aimar's avatar
Laurent Aimar committed
748
        input_DecoderDecode( es->p_dec, p_block );
749 750 751
    }
    else
    {
752
        block_Release( p_block );
753
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
754

755 756 757 758 759 760
    return VLC_SUCCESS;
}

/*****************************************************************************
 * EsOutDel:
 *****************************************************************************/
761
static void EsOutDel( es_out_t *out, es_out_id_t *es )
762 763 764
{
    es_out_sys_t *p_sys = out->p_sys;

Laurent Aimar's avatar
Laurent Aimar committed
765 766 767 768
    /* We don't try to reselect */
    if( es->p_dec )
        EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );

769 770 771
    if( es->p_pgrm == p_sys->p_pgrm )
        EsOutESVarUpdate( out, es, VLC_TRUE );

772 773
    TAB_REMOVE( p_sys->i_es, p_sys->es, es );

Laurent Aimar's avatar
Laurent Aimar committed
774 775 776
    es->p_pgrm->i_es--;
    if( es->p_pgrm->i_es == 0 )
    {
777 778
        msg_Warn( p_sys->p_input, "Program doesn't contain anymore ES, "
                  "TODO cleaning ?" );
Laurent Aimar's avatar
Laurent Aimar committed
779 780 781 782 783 784 785
    }

    if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
    if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
    if( p_sys->p_es_sub   == es ) p_sys->p_es_sub   = NULL;

    switch( es->fmt.i_cat )
786 787 788 789 790 791 792 793 794 795 796
    {
        case AUDIO_ES:
            p_sys->i_audio--;
            break;
        case SPU_ES:
            p_sys->i_sub--;
            break;
        case VIDEO_ES:
            p_sys->i_video--;
            break;
    }
797

Laurent Aimar's avatar
Laurent Aimar committed
798 799
    if( es->psz_description )
        free( es->psz_description );
800

Laurent Aimar's avatar
Laurent Aimar committed
801
    es_format_Clean( &es->fmt );
802

803
    free( es );
804 805
}

Clément Stenac's avatar
Clément Stenac committed
806 807 808 809 810 811 812 813
/**
 * Control query handler
 *
 * \param out the es_out to control
 * \param i_query A es_out query as defined in include/ninput.h
 * \param args a variable list of arguments for the query
 * \return VLC_SUCCESS or an error code
 */
814 815 816 817
static int EsOutControl( es_out_t *out, int i_query, va_list args )
{
    es_out_sys_t *p_sys = out->p_sys;
    vlc_bool_t  b, *pb;
818 819 820 821
    int         i, *pi;

    es_out_id_t *es;

822 823
    switch( i_query )
    {
824 825
        case ES_OUT_SET_ES_STATE:
            es = (es_out_id_t*) va_arg( args, es_out_id_t * );
826
            b = (vlc_bool_t) va_arg( args, vlc_bool_t );
Laurent Aimar's avatar
Laurent Aimar committed
827
            if( b && es->p_dec == NULL )
828
            {
Laurent Aimar's avatar
Laurent Aimar committed
829 830
                EsSelect( out, es );
                return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;
831
            }
Laurent Aimar's avatar
Laurent Aimar committed
832
            else if( !b && es->p_dec )
833
            {
Laurent Aimar's avatar
Laurent Aimar committed
834
                EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
835 836
                return VLC_SUCCESS;
            }
Laurent Aimar's avatar
Laurent Aimar committed
837 838
            return VLC_SUCCESS;

839 840
        case ES_OUT_GET_ES_STATE:
            es = (es_out_id_t*) va_arg( args, es_out_id_t * );
841 842
            pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );

Laurent Aimar's avatar
Laurent Aimar committed
843
            *pb = es->p_dec ? VLC_TRUE : VLC_FALSE;
844 845 846
            return VLC_SUCCESS;

        case ES_OUT_SET_ACTIVE:
847
        {
848 849
            b = (vlc_bool_t) va_arg( args, vlc_bool_t );
            p_sys->b_active = b;
Laurent Aimar's avatar
Laurent Aimar committed
850
            /* Needed ? */
851
            if( b )
Laurent Aimar's avatar
Laurent Aimar committed
852
                var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
853
            return VLC_SUCCESS;
854
        }
855 856 857 858 859 860 861 862

        case ES_OUT_GET_ACTIVE:
            pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
            *pb = p_sys->b_active;
            return VLC_SUCCESS;

        case ES_OUT_SET_MODE:
            i = (int) va_arg( args, int );
Gildas Bazin's avatar
 
Gildas Bazin committed
863
            if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
864
                i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )
865 866 867 868 869 870
            {
                p_sys->i_mode = i;

                /* Reapply policy mode */
                for( i = 0; i < p_sys->i_es; i++ )
                {
Laurent Aimar's avatar
Laurent Aimar committed
871
                    if( p_sys->es[i]->p_dec )
872
                    {
873 874
                        EsUnselect( out, p_sys->es[i],
                                    p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
                    }
                }
                for( i = 0; i < p_sys->i_es; i++ )
                {
                    EsOutSelect( out, p_sys->es[i], VLC_FALSE );
                }
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

        case ES_OUT_GET_MODE:
            pi = (int*) va_arg( args, int* );
            *pi = p_sys->i_mode;
            return VLC_SUCCESS;

        case ES_OUT_SET_ES:
            es = (es_out_id_t*) va_arg( args, es_out_id_t * );
Laurent Aimar's avatar
Laurent Aimar committed
892
            /* Special case NULL, NULL+i_cat */
893 894 895 896
            if( es == NULL )
            {
                for( i = 0; i < p_sys->i_es; i++ )
                {
Laurent Aimar's avatar
Laurent Aimar committed
897 898 899 900 901 902 903 904 905
                    if( p_sys->es[i]->p_dec )
                        EsUnselect( out, p_sys->es[i],
                                    p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
                }
            }
            else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
            {
                for( i = 0; i < p_sys->i_es; i++ )
                {
906 907 908 909
                    if( p_sys->es[i]->p_dec &&
                        p_sys->es[i]->fmt.i_cat == AUDIO_ES )
                        EsUnselect( out, p_sys->es[i],
                                    p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
Laurent Aimar's avatar
Laurent Aimar committed
910 911 912 913 914 915
                }
            }
            else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
            {
                for( i = 0; i < p_sys->i_es; i++ )
                {
916 917 918 919
                    if( p_sys->es[i]->p_dec &&
                        p_sys->es[i]->fmt.i_cat == VIDEO_ES )
                        EsUnselect( out, p_sys->es[i],
                                    p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
Laurent Aimar's avatar
Laurent Aimar committed
920 921 922 923 924 925
                }
            }
            else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
            {
                for( i = 0; i < p_sys->i_es; i++ )
                {
926 927 928 929
                    if( p_sys->es[i]->p_dec &&
                        p_sys->es[i]->fmt.i_cat == SPU_ES )
                        EsUnselect( out, p_sys->es[i],
                                    p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
930 931 932 933
                }
            }
            else
            {
Laurent Aimar's avatar
Laurent Aimar committed
934 935 936 937 938 939 940 941
                for( i = 0; i < p_sys->i_es; i++ )
                {
                    if( es == p_sys->es[i] )
                    {
                        EsOutSelect( out, es, VLC_TRUE );
                        break;
                    }
                }
942
            }
943 944
            return VLC_SUCCESS;

945
        case ES_OUT_SET_PCR:
946
        case ES_OUT_SET_GROUP_PCR:
947
        {
Laurent Aimar's avatar
Laurent Aimar committed
948 949 950
            es_out_pgrm_t *p_pgrm = NULL;
            int            i_group = 0;
            int64_t        i_pcr;
951

952 953
            if( i_query == ES_OUT_SET_PCR )
            {
Laurent Aimar's avatar
Laurent Aimar committed
954
                p_pgrm = p_sys->p_pgrm;
955 956 957
            }
            else
            {
Laurent Aimar's avatar
Laurent Aimar committed
958 959 960
                int i;
                i_group = (int)va_arg( args, int );
                for( i = 0; i < p_sys->i_pgrm; i++ )
961
                {
Laurent Aimar's avatar
Laurent Aimar committed
962 963 964 965 966
                    if( p_sys->pgrm[i]->i_id == i_group )
                    {
                        p_pgrm = p_sys->pgrm[i];
                        break;
                    }
967
                }
968
            }
Laurent Aimar's avatar
Laurent Aimar committed
969 970 971
            if( p_pgrm == NULL )
                p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */

972
            i_pcr = (int64_t)va_arg( args, int64_t );
973
            /* search program */
974 975 976
            /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */
            input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock,
                               (i_pcr + 11 ) * 9 / 100);
977 978 979 980
            return VLC_SUCCESS;
        }

        case ES_OUT_RESET_PCR:
Laurent Aimar's avatar
Laurent Aimar committed
981
            for( i = 0; i < p_sys->i_pgrm; i++ )
982
            {
Laurent Aimar's avatar
Laurent Aimar committed
983 984
                p_sys->pgrm[i]->clock.i_synchro_state =  SYNCHRO_REINIT;
                p_sys->pgrm[i]->clock.last_pts = 0;
985 986 987
            }
            return VLC_SUCCESS;

988 989 990 991 992 993 994 995 996 997 998 999
        case ES_OUT_GET_TS:
            if( p_sys->p_pgrm )
            {
                int64_t i_ts = (int64_t)va_arg( args, int64_t );
                int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );
                *pi_ts = input_ClockGetTS( p_sys->p_input,
                                           &p_sys->p_pgrm->clock,
                                           ( i_ts + 11 ) * 9 / 100 );
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;

Laurent Aimar's avatar
Laurent Aimar committed
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
        case ES_OUT_GET_GROUP:
            pi = (int*) va_arg( args, int* );
            if( p_sys->p_pgrm )
                *pi = p_sys->p_pgrm->i_id;
            else
                *pi = -1;    /* FIXME */
            return VLC_SUCCESS;

        case ES_OUT_SET_GROUP:
        {
            int j;
            i = (int) va_arg( args, int );
            for( j = 0; j < p_sys->i_pgrm; j++ )
            {
                es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
                if( p_pgrm->i_id == i )
                {
                    EsOutProgramSelect( out, p_pgrm );
                    return VLC_SUCCESS;
                }
            }
            return VLC_EGENERIC;
        }

1024 1025 1026 1027
        case ES_OUT_SET_FMT:
        {
            /* This ain't pretty but is need by some demuxers (eg. Ogg )
             * to update the p_extra data */
Eric Petit's avatar
Eric Petit committed
1028
            es_format_t *p_fmt;
Eric Petit's avatar
Eric Petit committed
1029
            es = (es_out_id_t*) va_arg( args, es_out_id_t * );
Eric Petit's avatar
Eric Petit committed
1030
            p_fmt = (es_format_t*) va_arg( args, es_format_t * );
1031
            if( es == NULL ) return VLC_EGENERIC;
1032 1033 1034 1035 1036 1037 1038

            if( p_fmt->i_extra )
            {
                es->fmt.i_extra = p_fmt->i_extra;
                es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
                memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );

1039 1040
                if( !es->p_dec ) return VLC_SUCCESS;

1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
                es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
                es->p_dec->fmt_in.p_extra =
                    realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );
                memcpy( es->p_dec->fmt_in.p_extra,
                        p_fmt->p_extra, p_fmt->i_extra );
            }

            return VLC_SUCCESS;
        }

1051 1052 1053 1054 1055
        default:
            msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
            return VLC_EGENERIC;
    }
}
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098

/****************************************************************************
 * LanguageGetName: try to expend iso639 into plain name
 ****************************************************************************/
static char *LanguageGetName( const char *psz_code )
{
    const iso639_lang_t *pl;

    if( psz_code == NULL )
    {
        return strdup( "" );
    }

    if( strlen( psz_code ) == 2 )
    {
        pl = GetLang_1( psz_code );
    }
    else if( strlen( psz_code ) == 3 )
    {
        pl = GetLang_2B( psz_code );
        if( !strcmp( pl->psz_iso639_1, "??" ) )
        {
            pl = GetLang_2T( psz_code );
        }
    }
    else
    {
        return strdup( psz_code );
    }

    if( !strcmp( pl->psz_iso639_1, "??" ) )
    {
       return strdup( psz_code );
    }
    else
    {
        if( *pl->psz_native_name )
        {
            return strdup( pl->psz_native_name );
        }
        return strdup( pl->psz_eng_name );
    }
}
1099 1100 1101 1102 1103 1104 1105

/****************************************************************************
 * EsOutAddInfo:
 * - add meta info to the playlist item
 ****************************************************************************/
static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
{
1106 1107 1108 1109
    es_out_sys_t   *p_sys = out->p_sys;
    input_thread_t *p_input = p_sys->p_input;
    es_format_t    *fmt = &es->fmt;
    char           *psz_cat;
1110 1111

    /* Add stream info */
1112
    asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 );
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169

    input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
                   "%.4s", (char*)&fmt->i_codec );

    input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),
                   "%s", es->psz_description );

    /* Add information */
    switch( fmt->i_cat )
    {
    case AUDIO_ES:
        input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                       _("Type"), _("Audio") );

        if( fmt->audio.i_channels > 0 )
            input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
                           "%d", fmt->audio.i_channels );

        if( fmt->audio.i_rate > 0 )
            input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
                           _("%d Hz"), fmt->audio.i_rate );

        if( fmt->audio.i_bitspersample > 0 )
            input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                           _("Bits per sample"), "%d",
                           fmt->audio.i_bitspersample );

        if( fmt->i_bitrate > 0 )
            input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
                           _("%d kb/s"), fmt->i_bitrate / 1000 );
        break;

    case VIDEO_ES:
        input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                       _("Type"), _("Video") );

        if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
            input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                           _("Resolution"), "%dx%d",
                           fmt->video.i_width, fmt->video.i_height );

        if( fmt->video.i_visible_width > 0 &&
            fmt->video.i_visible_height > 0 )
            input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                           _("Display resolution"), "%dx%d",
                           fmt->video.i_visible_width,
                           fmt->video.i_visible_height);
        break;

    case SPU_ES:
        input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                       _("Type"), _("Subtitle") );
        break;

    default:
        break;
    }
1170 1171

    free( psz_cat );
1172
}