input_programs.c 35.6 KB
Newer Older
1
2
3
/*****************************************************************************
 * input_programs.c: es_descriptor_t, pgrm_descriptor_t management
 *****************************************************************************
4
 * Copyright (C) 1999-2002 VideoLAN
5
 * $Id: input_programs.c,v 1.114 2003/05/18 23:16:57 fenrir Exp $
6
 *
7
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
9
10
11
12
 *
 * 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.
13
 *
14
15
16
17
18
19
20
21
22
23
24
25
26
 * 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
 *****************************************************************************/
27
#include <stdlib.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
28
#include <string.h>                                    /* memcpy(), memset() */
29

30
#include <vlc/vlc.h>
31
32
33
34

#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
35
#include "input_ext-plugins.h"
36
37
38
39
40

/*
 * NOTICE : all of these functions expect you to have taken the lock on
 * p_input->stream.lock
 */
41

gbazin's avatar
   
gbazin committed
42
43
44
45
46
47
48
49
50
/* Navigation callbacks */
static int ProgramCallback( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
static int TitleCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
static int ChapterCallback( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
static int NavigationCallback( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
gbazin's avatar
   
gbazin committed
51
52
static int ESCallback( vlc_object_t *, char const *,
                       vlc_value_t, vlc_value_t, void * );
gbazin's avatar
   
gbazin committed
53

54
55
56
/*****************************************************************************
 * input_InitStream: init the stream descriptor of the given input
 *****************************************************************************/
57
int input_InitStream( input_thread_t * p_input, size_t i_data_len )
58
{
59
    vlc_value_t text,val;
60

61
    p_input->stream.i_stream_id = 0;
62
63
64
65

    /* initialized to 0 since we don't give the signal to the interface
     * before the end of input initialization */
    p_input->stream.b_changed = 0;
66
67
    p_input->stream.pp_es = NULL;
    p_input->stream.pp_selected_es = NULL;
68
69
    p_input->stream.p_removed_es = NULL;
    p_input->stream.p_newly_selected_es = NULL;
70
    p_input->stream.pp_programs = NULL;
71
72
    p_input->stream.p_selected_program = NULL;
    p_input->stream.p_new_program = NULL;
73

74
75
    if( i_data_len )
    {
Henri Fallon's avatar
   
Henri Fallon committed
76
77
        if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL )
        {
78
            msg_Err( p_input, "out of memory" );
79
            return 1;
Henri Fallon's avatar
   
Henri Fallon committed
80
        }
81
82
        memset( p_input->stream.p_demux_data, 0, i_data_len );
    }
83
84
85
86
    else
    {
        p_input->stream.p_demux_data = NULL;
    }
87
88
89
90
    
    var_Create( p_input, "intf-change", VLC_VAR_BOOL );
    val.b_bool = VLC_TRUE;
    var_Set( p_input, "intf-change", val );
91

gbazin's avatar
   
gbazin committed
92
93
    /* Create a few object variables used for navigation in the interfaces */
    var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
gbazin's avatar
   
gbazin committed
94
95
    text.psz_string = _("Program");
    var_Change( p_input, "program", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
   
gbazin committed
96

gbazin's avatar
   
gbazin committed
97
    var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
gbazin's avatar
   
gbazin committed
98
99
    text.psz_string = _("Title");
    var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
   
gbazin committed
100

gbazin's avatar
   
gbazin committed
101
    var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
gbazin's avatar
   
gbazin committed
102
103
    text.psz_string = _("Chapter");
    var_Change( p_input, "chapter", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
   
gbazin committed
104

gbazin's avatar
   
gbazin committed
105
    var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE );
gbazin's avatar
   
gbazin committed
106
107
    text.psz_string = _("Navigation");
    var_Change( p_input, "navigation", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
   
gbazin committed
108

gbazin's avatar
   
gbazin committed
109
110
111
112
113
114
115
    var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
    text.psz_string = _("Video track");
    var_Change( p_input, "video-es", VLC_VAR_SETTEXT, &text, NULL );
    var_Create( p_input, "audio-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
    text.psz_string = _("Audio track");
    var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, &text, NULL );
    var_Create( p_input, "spu-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
gbazin's avatar
   
gbazin committed
116
    text.psz_string = _("Subtitles track");
gbazin's avatar
   
gbazin committed
117
118
    var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, &text, NULL );

gbazin's avatar
   
gbazin committed
119
120
121
    var_AddCallback( p_input, "program", ProgramCallback, NULL );
    var_AddCallback( p_input, "title", TitleCallback, NULL );
    var_AddCallback( p_input, "chapter", ChapterCallback, NULL );
gbazin's avatar
   
gbazin committed
122
123
124
    var_AddCallback( p_input, "video-es", ESCallback, NULL );
    var_AddCallback( p_input, "audio-es", ESCallback, NULL );
    var_AddCallback( p_input, "spu-es", ESCallback, NULL );
gbazin's avatar
   
gbazin committed
125

126
    return 0;
127
128
}

129
130
131
132
133
/*****************************************************************************
 * input_EndStream: free all stream descriptors
 *****************************************************************************/
void input_EndStream( input_thread_t * p_input )
{
134
    /* Free all programs and associated ES, and associated decoders. */
135
    while( p_input->stream.i_pgrm_number )
136
    {
137
138
        input_DelProgram( p_input, p_input->stream.pp_programs[0] );
    }
139

140
    /* Free standalone ES */
141
    while( p_input->stream.i_es_number )
142
143
144
    {
        input_DelES( p_input, p_input->stream.pp_es[0] );
    }
145

146
147
148
149
150
151
    /* Free all areas */
    while( p_input->stream.i_area_nb )
    {
        input_DelArea( p_input, p_input->stream.pp_areas[0] );
    }

152
153
154
155
156
    /* Free selected ES */
    if( p_input->stream.pp_selected_es != NULL )
    {
        free( p_input->stream.pp_selected_es );
    }
157

158
159
160
161
    if( p_input->stream.p_demux_data != NULL )
    {
        free( p_input->stream.p_demux_data );
    }
gbazin's avatar
   
gbazin committed
162
163
164
165
166
167
168
169

    /* Free navigation variables */
    var_Destroy( p_input, "program" );
    var_Destroy( p_input, "title" );
    var_Destroy( p_input, "chapter" );
    var_Destroy( p_input, "video-es" );
    var_Destroy( p_input, "audio-es" );
    var_Destroy( p_input, "spu-es" );
170
    var_Destroy( p_input, "intf-change" );
171
172
173
174
175
}

/*****************************************************************************
 * input_FindProgram: returns a pointer to a program described by its ID
 *****************************************************************************/
176
177
pgrm_descriptor_t * input_FindProgram( input_thread_t * p_input,
                                       uint16_t i_pgrm_id )
178
{
179
    unsigned int i;
180
181
182
183

    for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
    {
        if( p_input->stream.pp_programs[i]->i_number == i_pgrm_id )
184
        {
185
            return p_input->stream.pp_programs[i];
186
187
        }
    }
188

gbazin's avatar
   
gbazin committed
189
    return NULL;
190
191
}

192
193
194
195
196
197
/*****************************************************************************
 * input_AddProgram: add and init a program descriptor
 *****************************************************************************
 * This program descriptor will be referenced in the given stream descriptor
 *****************************************************************************/
pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input,
198
                                      u16 i_pgrm_id, size_t i_data_len )
199
{
200
    /* Where to add the pgrm */
201
    pgrm_descriptor_t * p_pgrm = malloc( sizeof(pgrm_descriptor_t) );
gbazin's avatar
   
gbazin committed
202
    vlc_value_t val;
203

204
    if( p_pgrm == NULL )
Henri Fallon's avatar
   
Henri Fallon committed
205
    {
206
        msg_Err( p_input, "out of memory" );
207
        return NULL;
Henri Fallon's avatar
   
Henri Fallon committed
208
    }
gbazin's avatar
   
gbazin committed
209

210
    /* Init this entry */
211
212
213
    p_pgrm->i_number = i_pgrm_id;
    p_pgrm->b_is_ok = 0;
    p_pgrm->i_version = 0;
214

215
216
    p_pgrm->i_es_number = 0;
    p_pgrm->pp_es = NULL;
217

218
    input_ClockInit( p_pgrm );
219

220
    p_pgrm->i_synchro_state = SYNCHRO_START;
221

222
223
    if( i_data_len )
    {
224
225
        p_pgrm->p_demux_data = malloc( i_data_len );
        if( p_pgrm->p_demux_data == NULL )
Henri Fallon's avatar
   
Henri Fallon committed
226
        {
227
            msg_Err( p_input, "out of memory" );
228
            return NULL;
Henri Fallon's avatar
   
Henri Fallon committed
229
        }
230
        memset( p_pgrm->p_demux_data, 0, i_data_len );
231
    }
232
233
    else
    {
234
        p_pgrm->p_demux_data = NULL;
235
    }
236

237
238
239
240
241
242
    /* Add an entry to the list of program associated with the stream */
    INSERT_ELEM( p_input->stream.pp_programs,
                 p_input->stream.i_pgrm_number,
                 p_input->stream.i_pgrm_number,
                 p_pgrm );

gbazin's avatar
   
gbazin committed
243
    val.i_int = i_pgrm_id;
gbazin's avatar
   
gbazin committed
244
    var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
gbazin's avatar
   
gbazin committed
245

246
    return p_pgrm;
247
248
249
250
251
252
253
}

/*****************************************************************************
 * input_DelProgram: destroy a program descriptor
 *****************************************************************************
 * All ES descriptions referenced in the descriptor will be deleted.
 *****************************************************************************/
254
void input_DelProgram( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm )
255
{
256
    unsigned int i_pgrm_index;
gbazin's avatar
   
gbazin committed
257
    vlc_value_t val;
258

gbazin's avatar
   
gbazin committed
259
260
261
262
263
264
265
266
267
268
269
    /* Find the program in the programs table */
    for( i_pgrm_index = 0; i_pgrm_index < p_input->stream.i_pgrm_number;
         i_pgrm_index++ )
    {
        if( p_input->stream.pp_programs[i_pgrm_index] == p_pgrm )
            break;
    }

    /* If the program wasn't found, do nothing */
    if( i_pgrm_index == p_input->stream.i_pgrm_number )
    {
270
        msg_Err( p_input, "program does not belong to this input" );
gbazin's avatar
   
gbazin committed
271
272
        return;
    }
273

gbazin's avatar
   
gbazin committed
274
    val.i_int = i_pgrm_index;
gbazin's avatar
   
gbazin committed
275
    var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
gbazin's avatar
   
gbazin committed
276

277
    /* Free the structures that describe the es that belongs to that program */
278
    while( p_pgrm->i_es_number )
279
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
280
        input_DelES( p_input, p_pgrm->pp_es[0] );
281
282
    }

283
284
285
286
287
288
    /* Free the demux data */
    if( p_pgrm->p_demux_data != NULL )
    {
        free( p_pgrm->p_demux_data );
    }

289
    /* Remove this program from the stream's list of programs */
290
291
292
    REMOVE_ELEM( p_input->stream.pp_programs,
                 p_input->stream.i_pgrm_number,
                 i_pgrm_index );
293

294
295
296
297
    /* Free the description of this program */
    free( p_pgrm );
}

Stéphane Borel's avatar
   
Stéphane Borel committed
298
299
300
301
302
/*****************************************************************************
 * input_AddArea: add and init an area descriptor
 *****************************************************************************
 * This area descriptor will be referenced in the given stream descriptor
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
303
304
input_area_t * input_AddArea( input_thread_t * p_input,
                              uint16_t i_area_id, uint16_t i_part_nb )
Stéphane Borel's avatar
   
Stéphane Borel committed
305
306
{
    /* Where to add the pgrm */
307
    input_area_t * p_area = malloc( sizeof(input_area_t) );
gbazin's avatar
   
gbazin committed
308
309
    vlc_value_t val;
    int i;
Stéphane Borel's avatar
   
Stéphane Borel committed
310

311
    if( p_area == NULL )
Stéphane Borel's avatar
   
Stéphane Borel committed
312
    {
313
        msg_Err( p_input, "out of memory" );
314
        return NULL;
Stéphane Borel's avatar
   
Stéphane Borel committed
315
    }
gbazin's avatar
   
gbazin committed
316

Stéphane Borel's avatar
   
Stéphane Borel committed
317
    /* Init this entry */
gbazin's avatar
   
gbazin committed
318
319
320
    p_area->i_id = i_area_id;
    p_area->i_part_nb = i_part_nb;
    p_area->i_part= 0;
321
322
323
324
325
326
327
328
329
330
331
    p_area->i_start = 0;
    p_area->i_size = 0;
    p_area->i_tell = 0;
    p_area->i_seek = NO_SEEK;

    /* Add an entry to the list of program associated with the stream */
    INSERT_ELEM( p_input->stream.pp_areas,
                 p_input->stream.i_area_nb,
                 p_input->stream.i_area_nb,
                 p_area );

gbazin's avatar
   
gbazin committed
332
333
334
335
336
337
    /* Don't add empty areas */
    if( i_part_nb == 0 )
        return NULL;

    /* Take care of the navigation variables */
    val.i_int = i_area_id;
gbazin's avatar
   
gbazin committed
338
    var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val, NULL );
gbazin's avatar
   
gbazin committed
339
340
341
342

    val.psz_string = malloc( sizeof("title ") + 5 );
    if( val.psz_string )
    {
gbazin's avatar
   
gbazin committed
343
        vlc_value_t val2, text, text2;
gbazin's avatar
   
gbazin committed
344
345

        sprintf( val.psz_string, "title %2i", i_area_id );
gbazin's avatar
   
gbazin committed
346
347
348
349
350
351
        var_Destroy( p_input, val.psz_string );
        var_Create( p_input, val.psz_string, VLC_VAR_INTEGER |
                    VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
        var_AddCallback( p_input, val.psz_string, NavigationCallback,
                         (void *)(int)i_area_id );

gbazin's avatar
   
gbazin committed
352
353
354
355
356
357
358
359
360
        text.psz_string = malloc( strlen( _("Title %i") ) + 20 );
        if( text.psz_string )
            sprintf( text.psz_string, _("Title %i"), i_area_id );

        var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val, &text );

        if( text.psz_string ) free( text.psz_string );

        text2.psz_string = malloc( strlen( _("Chapter %i") ) + 20 );
gbazin's avatar
   
gbazin committed
361
362
363
364

        for( i = 1; i <= i_part_nb; i++ )
        {
            val2.i_int = i;
gbazin's avatar
   
gbazin committed
365
366
367
368

            if( text2.psz_string )
                sprintf( text2.psz_string, _("Chapter %i"), i );

gbazin's avatar
   
gbazin committed
369
            var_Change( p_input, val.psz_string,
gbazin's avatar
   
gbazin committed
370
                        VLC_VAR_ADDCHOICE, &val2, &text2 );
gbazin's avatar
   
gbazin committed
371
        }
gbazin's avatar
   
gbazin committed
372
373

        if( text2.psz_string ) free( text2.psz_string );
gbazin's avatar
   
gbazin committed
374
375
    }

gbazin's avatar
   
gbazin committed
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
    if( p_input->stream.i_area_nb == 2 )
    {
        vlc_value_t text;

        /* Add another bunch of navigation object variables */
        var_Create( p_input, "next-title", VLC_VAR_VOID );
        text.psz_string = _("Next title");
        var_Change( p_input, "next-title", VLC_VAR_SETTEXT, &text, NULL );
        var_Create( p_input, "prev-title", VLC_VAR_VOID );
        text.psz_string = _("Previous title");
        var_Change( p_input, "prev-title", VLC_VAR_SETTEXT, &text, NULL );
        var_AddCallback( p_input, "next-title", TitleCallback, NULL );
        var_AddCallback( p_input, "prev-title", TitleCallback, NULL );

        var_Create( p_input, "next-chapter", VLC_VAR_VOID );
        text.psz_string = _("Next Chapter");
        var_Change( p_input, "next-chapter", VLC_VAR_SETTEXT, &text, NULL );
        var_Create( p_input, "prev-chapter", VLC_VAR_VOID );
        text.psz_string = _("Previous Chapter");
        var_Change( p_input, "prev-chapter", VLC_VAR_SETTEXT, &text, NULL );
        var_AddCallback( p_input, "next-chapter", ChapterCallback, NULL );
        var_AddCallback( p_input, "prev-chapter", ChapterCallback, NULL );
    }

400
    return p_area;
Stéphane Borel's avatar
   
Stéphane Borel committed
401
402
}

403
404
405
406
407
/*****************************************************************************
 * input_SetProgram: changes the current program
 *****************************************************************************/
int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg )
{
408
    unsigned int i_es_index;
409
410
411
412
    int i_required_audio_es;
    int i_required_spu_es;
    int i_audio_es = 0;
    int i_spu_es = 0;
gbazin's avatar
   
gbazin committed
413
    vlc_value_t val;
Johan Bilien's avatar
   
Johan Bilien committed
414

Johan Bilien's avatar
   
Johan Bilien committed
415
    if ( p_input->stream.p_selected_program )
416
    {
Johan Bilien's avatar
   
Johan Bilien committed
417
418
419
        for ( i_es_index = 1 ; /* 0 should be the PMT */
                i_es_index < p_input->stream.p_selected_program->
                i_es_number ;
Johan Bilien's avatar
   
Johan Bilien committed
420
421
                i_es_index ++ )
        {
Johan Bilien's avatar
   
Johan Bilien committed
422
#define p_es p_input->stream.p_selected_program->pp_es[i_es_index]
423
            if ( p_es->p_decoder_fifo ) /* if the ES was selected */
Johan Bilien's avatar
   
Johan Bilien committed
424
            {
Johan Bilien's avatar
   
Johan Bilien committed
425
                input_UnselectES( p_input , p_es );
Johan Bilien's avatar
   
Johan Bilien committed
426
            }
Johan Bilien's avatar
   
Johan Bilien committed
427
#undef p_es
Johan Bilien's avatar
   
Johan Bilien committed
428
        }
Johan Bilien's avatar
   
Johan Bilien committed
429
430
    }
    /* Get the number of the required audio stream */
431
    if( config_GetInt( p_input, "audio" ) )
Johan Bilien's avatar
   
Johan Bilien committed
432
433
    {
        /* Default is the first one */
434
        i_required_audio_es = config_GetInt( p_input, "audio-channel" );
Johan Bilien's avatar
   
Johan Bilien committed
435
        if( i_required_audio_es < 0 )
Johan Bilien's avatar
   
Johan Bilien committed
436
        {
Johan Bilien's avatar
   
Johan Bilien committed
437
            i_required_audio_es = 1;
Johan Bilien's avatar
   
Johan Bilien committed
438
        }
Johan Bilien's avatar
   
Johan Bilien committed
439
440
441
442
443
    }
    else
    {
        i_required_audio_es = 0;
    }
Johan Bilien's avatar
   
Johan Bilien committed
444

Johan Bilien's avatar
   
Johan Bilien committed
445
    /* Same thing for subtitles */
446
    if( config_GetInt( p_input, "video" ) )
Johan Bilien's avatar
   
Johan Bilien committed
447
448
    {
        /* for spu, default is none */
449
        i_required_spu_es = config_GetInt( p_input, "spu-channel" );
Johan Bilien's avatar
   
Johan Bilien committed
450
        if( i_required_spu_es < 0 )
Johan Bilien's avatar
   
Johan Bilien committed
451
452
453
        {
            i_required_spu_es = 0;
        }
Johan Bilien's avatar
   
Johan Bilien committed
454
455
456
457
458
459
    }
    else
    {
        i_required_spu_es = 0;
    }

460
    for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
Johan Bilien's avatar
   
Johan Bilien committed
461
    {
Johan Bilien's avatar
   
Johan Bilien committed
462
        switch( p_new_prg->pp_es[i_es_index]->i_cat )
463
464
        {
            case VIDEO_ES:
465
466
                msg_Dbg( p_input, "selecting ES %x",
                         p_new_prg->pp_es[i_es_index]->i_id );
467
468
469
470
471
472
                input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
                break;
            case AUDIO_ES:
                i_audio_es += 1;
                if( i_audio_es <= i_required_audio_es )
                {
473
474
                    msg_Dbg( p_input, "selecting ES %x",
                             p_new_prg->pp_es[i_es_index]->i_id );
475
476
477
478
479
480
481
482
                    input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
                }
                break;
            /* Not sure this one is fully specification-compliant */
            case SPU_ES :
                i_spu_es += 1;
                if( i_spu_es <= i_required_spu_es )
                {
483
484
                    msg_Dbg( p_input, "selecting ES %x",
                             p_new_prg->pp_es[i_es_index]->i_id );
485
486
487
488
                    input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
                }
            break;
            default :
489
490
                msg_Dbg( p_input, "ES %x has unknown type",
                         p_new_prg->pp_es[i_es_index]->i_id );
491
492
                break;
        }
Johan Bilien's avatar
   
Johan Bilien committed
493

494
    }
Johan Bilien's avatar
   
Johan Bilien committed
495

496
497
498

    p_input->stream.p_selected_program = p_new_prg;

gbazin's avatar
   
gbazin committed
499
500
    /* Update the navigation variables without triggering a callback */
    val.i_int = p_new_prg->i_number;
gbazin's avatar
   
gbazin committed
501
    var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
gbazin's avatar
   
gbazin committed
502

503
504
505
    return( 0 );
}

Stéphane Borel's avatar
   
Stéphane Borel committed
506
507
508
509
510
511
512
/*****************************************************************************
 * input_DelArea: destroy a area descriptor
 *****************************************************************************
 * All ES descriptions referenced in the descriptor will be deleted.
 *****************************************************************************/
void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
{
513
    unsigned int i_area_index;
gbazin's avatar
   
gbazin committed
514
    vlc_value_t val;
Stéphane Borel's avatar
   
Stéphane Borel committed
515
516
517
518
519
520
521
522
523

    /* Find the area in the areas table */
    for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
         i_area_index++ )
    {
        if( p_input->stream.pp_areas[i_area_index] == p_area )
            break;
    }

gbazin's avatar
   
gbazin committed
524
525
526
    /* If the area wasn't found, do nothing */
    if( i_area_index == p_input->stream.i_area_nb )
    {
527
        msg_Err( p_input, "area does not belong to this input" );
gbazin's avatar
   
gbazin committed
528
529
530
        return;
    }

gbazin's avatar
   
gbazin committed
531
532
533
534
535
    /* Take care of the navigation variables */
    val.psz_string = malloc( sizeof("title ") + 5 );
    if( val.psz_string )
    {
        sprintf( val.psz_string, "title %i", p_area->i_id );
gbazin's avatar
   
gbazin committed
536
537
        var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL );
        var_Destroy( p_input, val.psz_string );
gbazin's avatar
   
gbazin committed
538
539
    }

Stéphane Borel's avatar
   
Stéphane Borel committed
540
    /* Remove this area from the stream's list of areas */
541
542
543
    REMOVE_ELEM( p_input->stream.pp_areas,
                 p_input->stream.i_area_nb,
                 i_area_index );
Stéphane Borel's avatar
   
Stéphane Borel committed
544
545
546

    /* Free the description of this area */
    free( p_area );
gbazin's avatar
   
gbazin committed
547
548
549
550
551
552
553
554
555

    if( p_input->stream.i_area_nb == 1 )
    {
        /* Del unneeded navigation object variables */
        var_Destroy( p_input, "next-title" );
        var_Destroy( p_input, "prev-title" );
        var_Destroy( p_input, "next-chapter" );
        var_Destroy( p_input, "prev-chapter" );
    }
Stéphane Borel's avatar
   
Stéphane Borel committed
556
557
558
}


559
560
561
/*****************************************************************************
 * input_FindES: returns a pointer to an ES described by its ID
 *****************************************************************************/
562
es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
563
{
564
    unsigned int i;
565
566
567
568
569
570
571
572
573

    for( i = 0; i < p_input->stream.i_es_number; i++ )
    {
        if( p_input->stream.pp_es[i]->i_id == i_es_id )
        {
            return p_input->stream.pp_es[i];
        }
    }

gbazin's avatar
   
gbazin committed
574
    return NULL;
575
576
577
578
579
580
581
582
583
584
}

/*****************************************************************************
 * input_AddES:
 *****************************************************************************
 * Reserve a slot in the table of ES descriptors for the ES and add it to the
 * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
 * alone (PSI ?)
 *****************************************************************************/
es_descriptor_t * input_AddES( input_thread_t * p_input,
585
                               pgrm_descriptor_t * p_pgrm, u16 i_es_id,
gbazin's avatar
   
gbazin committed
586
                               int i_category, char const *psz_desc,
587
                               size_t i_data_len )
588
{
589
    es_descriptor_t * p_es;
gbazin's avatar
   
gbazin committed
590
591
    vlc_value_t val, text;
    char *psz_var = NULL;
592

593
    p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
Henri Fallon's avatar
   
Henri Fallon committed
594
595
    if( p_es == NULL )
    {
596
        msg_Err( p_input, "out of memory" );
Henri Fallon's avatar
   
Henri Fallon committed
597
        return( NULL);
Henri Fallon's avatar
   
Henri Fallon committed
598
    }
gbazin's avatar
   
gbazin committed
599

600
601
602
603
    INSERT_ELEM( p_input->stream.pp_es,
                 p_input->stream.i_es_number,
                 p_input->stream.i_es_number,
                 p_es );
604

605
    /* Init its values */
606
    p_es->i_id = i_es_id;
607
608
    p_es->p_pes = NULL;
    p_es->p_decoder_fifo = NULL;
gbazin's avatar
   
gbazin committed
609
    p_es->i_cat = i_category;
610
    p_es->i_demux_fd = 0;
611
612
    p_es->c_packets = 0;
    p_es->c_invalid_packets = 0;
613
    p_es->b_force_decoder = VLC_FALSE;
614
615

    if( i_data_len )
616
    {
617
        p_es->p_demux_data = malloc( i_data_len );
Henri Fallon's avatar
   
Henri Fallon committed
618
619
        if( p_es->p_demux_data == NULL )
        {
620
            msg_Err( p_input, "out of memory" );
Henri Fallon's avatar
   
Henri Fallon committed
621
            return( NULL );
Henri Fallon's avatar
   
Henri Fallon committed
622
        }
623
624
        memset( p_es->p_demux_data, 0, i_data_len );
    }
625
626
627
628
    else
    {
        p_es->p_demux_data = NULL;
    }
629
630
    p_es->p_waveformatex     = NULL;
    p_es->p_bitmapinfoheader = NULL;
631
632
633
634

    /* Add this ES to the program definition if one is given */
    if( p_pgrm )
    {
635
636
637
638
        INSERT_ELEM( p_pgrm->pp_es,
                     p_pgrm->i_es_number,
                     p_pgrm->i_es_number,
                     p_es );
639
        p_es->p_pgrm = p_pgrm;
640
641
642
    }
    else
    {
643
        p_es->p_pgrm = NULL;
644
645
    }

gbazin's avatar
   
gbazin committed
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
    switch( i_category )
    {
    case AUDIO_ES:
        psz_var = "audio-es";
        break;
    case SPU_ES:
        psz_var = "spu-es";
        break;
    case VIDEO_ES:
        psz_var = "video-es";
        break;
    }

    if( psz_var )
    {
gbazin's avatar
   
gbazin committed
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
        /* 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++;
        }

        /* Take care of the ES description */
        if( psz_desc )
        {
            p_es->psz_desc = strdup( psz_desc );
        }
        else
        {
            p_es->psz_desc = malloc( strlen( _("Track %i") ) + 20 );
            if( p_es->psz_desc )
                sprintf( p_es->psz_desc, _("Track %i"), val.i_int );
        }

gbazin's avatar
   
gbazin committed
685
        val.i_int = p_es->i_id;
gbazin's avatar
   
gbazin committed
686
        text.psz_string = p_es->psz_desc;
gbazin's avatar
   
gbazin committed
687
688
        var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
    }
gbazin's avatar
   
gbazin committed
689
    else p_es->psz_desc = NULL;
gbazin's avatar
   
gbazin committed
690

691
692
693
694
695
696
    return p_es;
}

/*****************************************************************************
 * input_DelES:
 *****************************************************************************/
697
void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
698
{
699
    unsigned int            i_index, i_es_index;
700
    pgrm_descriptor_t *     p_pgrm;
gbazin's avatar
   
gbazin committed
701
    char *                  psz_var = NULL;
gbazin's avatar
   
gbazin committed
702
    vlc_value_t             val;
703

gbazin's avatar
   
gbazin committed
704
705
706
707
708
709
710
711
712
713
714
    /* Find the ES in the ES table */
    for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
         i_es_index++ )
    {
        if( p_input->stream.pp_es[i_es_index] == p_es )
            break;
    }

    /* If the ES wasn't found, do nothing */
    if( i_es_index == p_input->stream.i_es_number )
    {
715
        msg_Err( p_input, "ES does not belong to this input" );
gbazin's avatar
   
gbazin committed
716
717
718
        return;
    }

gbazin's avatar
   
gbazin committed
719
720
721
722
723
    /* Remove es from its associated variable */
    switch( p_es->i_cat )
    {
    case AUDIO_ES:
        psz_var = "audio-es";
gbazin's avatar
   
gbazin committed
724
        break;
gbazin's avatar
   
gbazin committed
725
726
    case SPU_ES:
        psz_var = "spu-es";
gbazin's avatar
   
gbazin committed
727
        break;
gbazin's avatar
   
gbazin committed
728
729
    case VIDEO_ES:
        psz_var = "video-es";
gbazin's avatar
   
gbazin committed
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
        break;
    }

    if( psz_var )
    {
        val.i_int = p_es->i_id;
        var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );

        /* Remove the "Disable" entry if needed */
        var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
        if( val.i_int == 1 )
        {
            val.i_int = -1;
            var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
        }
gbazin's avatar
   
gbazin committed
745
    }
746
747
748

    /* Kill associated decoder, if any. */
    if( p_es->p_decoder_fifo != NULL )
749
    {
750
        input_UnselectES( p_input, p_es );
751
752
    }

gbazin's avatar
   
gbazin committed
753
754
755
    /* Remove this ES from the description of the program if it is associated
     * to one */
    p_pgrm = p_es->p_pgrm;
756
757
    if( p_pgrm )
    {
758
        for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
759
        {
760
            if( p_pgrm->pp_es[i_index] == p_es )
761
            {
762
763
764
                REMOVE_ELEM( p_pgrm->pp_es,
                             p_pgrm->i_es_number,
                             i_index );
765
766
767
768
769
                break;
            }
        }
    }

770
771
772
773
774
    /* Free the demux data */
    if( p_es->p_demux_data != NULL )
    {
        free( p_es->p_demux_data );
    }
775
776
777
778
779
780
781
782
    if( p_es->p_waveformatex )
    {
        free( p_es->p_waveformatex );
    }
    if( p_es->p_bitmapinfoheader )
    {
        free( p_es->p_bitmapinfoheader );
    }
783

gbazin's avatar
   
gbazin committed
784
785
786
787
788
789
    /* Free the description string */
    if( p_es->psz_desc != NULL )
    {
        free( p_es->psz_desc );
    }

790
791
792
793
794
795
796
797
    /* Find the ES in the ES table */
    for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
         i_es_index++ )
    {
        if( p_input->stream.pp_es[i_es_index] == p_es )
            break;
    }

gbazin's avatar
   
gbazin committed
798
    /* Remove this ES from the stream's list of ES */
799
800
801
802
    REMOVE_ELEM( p_input->stream.pp_es,
                 p_input->stream.i_es_number,
                 i_es_index );

gbazin's avatar
   
gbazin committed
803
804
    /* Free the ES */
    free( p_es );
805
806
807
808
}

/*****************************************************************************
 * input_SelectES: selects an ES and spawns the associated decoder
809
810
811
 *****************************************************************************
 * Remember we are still supposed to have stream_lock when entering this
 * function ?
812
813
814
 *****************************************************************************/
int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
{
gbazin's avatar
   
gbazin committed
815
    vlc_value_t val;
gbazin's avatar
   
gbazin committed
816
    char *psz_var = NULL;
gbazin's avatar
   
gbazin committed
817

818
819
    if( p_es == NULL )
    {
820
        msg_Err( p_input, "nothing to do in input_SelectES" );
821
822
823
        return -1;
    }

gbazin's avatar
   
gbazin committed
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
    if( ((p_es->i_cat == VIDEO_ES) || (p_es->i_cat == SPU_ES))
        && !config_GetInt( p_input, "video" ) )
    {
        msg_Dbg( p_input,
                 "video is disabled, not selecting ES 0x%x", p_es->i_id );
        return -1;
    }

    if( (p_es->i_cat == AUDIO_ES) && !config_GetInt( p_input, "audio" ) )
    {
        msg_Dbg( p_input,
                 "audio is disabled, not selecting ES 0x%x", p_es->i_id );
        return -1;
    }

839
    msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
840
841
842

    if( p_es->p_decoder_fifo != NULL )
    {
843
        msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
844
        return -1;
845
846
    }

847
848
849
    /* Release the lock, not to block the input thread during
     * the creation of the thread. */
    vlc_mutex_unlock( &p_input->stream.stream_lock );
850
    p_es->p_decoder_fifo = input_RunDecoder( p_input, p_es );
851
    vlc_mutex_lock( &p_input->stream.stream_lock );
gbazin's avatar
   
gbazin committed
852

853
    if( p_es->p_decoder_fifo == NULL )
854
    {
855
        return -1;
856
857
    }

gbazin's avatar
   
gbazin committed
858
    /* Update the es variable without triggering a callback */
gbazin's avatar
   
gbazin committed
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
    switch( p_es->i_cat )
    {
    case AUDIO_ES:
        psz_var = "audio-es";
        break;
    case SPU_ES:
        psz_var = "spu-es";
        break;
    case VIDEO_ES:
        psz_var = "video-es";
        break;
    }

    if( psz_var )
    {
        val.i_int = p_es->i_id;
        var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
    }
gbazin's avatar
   
gbazin committed
877

878
    return 0;
879
}
880
881

/*****************************************************************************
882
 * input_UnselectES: removes an ES from the list of selected ES
883
 *****************************************************************************/
884
int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
885
{
886
    unsigned int i_index = 0;
gbazin's avatar
   
gbazin committed
887
888
    vlc_value_t val;
    char *psz_var = NULL;
889

890
891
    if( p_es == NULL )
    {
892
        msg_Err( p_input, "nothing to do in input_UnselectES" );
893
894
895
        return -1;
    }

896
    msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
897
898
899

    if( p_es->p_decoder_fifo == NULL )
    {
900
        msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
901
902
903
        return( -1 );
    }

gbazin's avatar
   
gbazin committed
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
    /* Update the es variable without triggering a callback */
    switch( p_es->i_cat )
    {
    case AUDIO_ES:
        psz_var = "audio-es";
        break;
    case SPU_ES:
        psz_var = "spu-es";
        break;
    case VIDEO_ES:
        psz_var = "video-es";
        break;
    }

    if( psz_var )
    {
        val.i_int = -1;
        var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
    }

    /* Actually unselect the ES */
925
    input_EndDecoder( p_input, p_es );
Johan Bilien's avatar
   
Johan Bilien committed
926
    p_es->p_pes = NULL;
927

928
929
    if( ( p_es->p_decoder_fifo == NULL ) &&
        ( p_input->stream.i_selected_es_number > 0 ) )
930
    {
931
        while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
932
933
934
935
936
               ( p_input->stream.pp_selected_es[i_index] != p_es ) )
        {
            i_index++;
        }

937
        /* XXX: no need to memmove, we have unsorted data */
938
939
940
        REMOVE_ELEM( p_input->stream.pp_selected_es,
                     p_input->stream.i_selected_es_number,
                     i_index );
941

942
        if( p_input->stream.i_selected_es_number == 0 )
943
        {
944
            msg_Dbg( p_input, "no more selected ES" );
945
            return 1;
946
947
        }
    }
948

949
    return 0;
950
}
gbazin's avatar
   
gbazin committed
951
952
953
954
955
956
957
958
959

/*****************************************************************************
 * Navigation callback: a bunch of navigation variables are used as an
 *  alternative to the navigation API.
 *****************************************************************************/
static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    input_thread_t *p_input = (input_thread_t *)p_this;
960
    vlc_value_t val;
gbazin's avatar
   
gbazin committed
961
962
963
964
965
966
967
968

    if( oldval.i_int == newval.i_int )
       return VLC_SUCCESS;

    vlc_mutex_lock( &p_input->stream.stream_lock );
    if( ( newval.i_int > 0 ) )
    {
        vlc_mutex_unlock( &p_input->stream.stream_lock );
969
        input_ChangeProgram( p_input, (uint16_t)newval.i_int );
gbazin's avatar
   
gbazin committed
970
971
972
973
974
        input_SetStatus( p_input, INPUT_STATUS_PLAY );
        vlc_mutex_lock( &p_input->stream.stream_lock );
    }
    vlc_mutex_unlock( &p_input->stream.stream_lock );

975
976
977
    val.b_bool = VLC_TRUE;
    var_Set( p_input, "intf-change", val );

gbazin's avatar
   
gbazin committed
978
979
980
981
982
983
984
985
    return VLC_SUCCESS;
}

static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    input_thread_t *p_input = (input_thread_t *)p_this;
    input_area_t *p_area;
gbazin's avatar
   
gbazin committed
986
987
    vlc_value_t val, val_list;
    int i, i_step = 0;
gbazin's avatar
   
gbazin committed
988

gbazin's avatar
   
gbazin committed
989
990
991
992
    if( !strcmp( psz_cmd, "next-title" ) ) i_step++;
    else if( !strcmp( psz_cmd, "prev-title" ) ) i_step--;

    if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
993

gbazin's avatar
   
gbazin committed
994
    /* Sanity check should have already been done by var_Set(). */
gbazin's avatar
   
gbazin committed
995
    vlc_mutex_lock( &p_input->stream.stream_lock );
gbazin's avatar
   
gbazin committed
996
997
998
999
1000

    if( i_step )
    {
        var_Get( p_this, "title", &newval );
        var_Change( p_this, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
gbazin's avatar
   
gbazin committed
1001
1002
1003
1004
1005
1006
1007
1008
1009
        for( i = 0; i < val_list.p_list->i_count; i++ )
        {
            if( val_list.p_list->p_values[i].i_int == newval.i_int &&
                i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
            {
                newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
                break;
            }
        }
gbazin's avatar
   
gbazin committed
1010
1011
1012
        var_Change( p_this, "title", VLC_VAR_FREELIST, &val_list, NULL );
    }

gbazin's avatar
   
gbazin committed
1013
1014
    p_area = p_input->stream.pp_areas[newval.i_int];
    p_area->i_part = 1;
gbazin's avatar
   
gbazin committed
1015

gbazin's avatar
   
gbazin committed
1016
    vlc_mutex_unlock( &p_input->stream.stream_lock );
gbazin's avatar
   
gbazin committed
1017

gbazin's avatar
   
gbazin committed
1018
1019
1020
    input_ChangeArea( p_input, p_area );
    input_SetStatus( p_input, INPUT_STATUS_PLAY );

1021
1022
1023
    val.b_bool = VLC_TRUE;
    var_Set( p_input, "intf-change", val );

gbazin's avatar
   
gbazin committed
1024
1025
1026
1027
1028
1029
1030
1031
    return VLC_SUCCESS;
}

static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    input_thread_t *p_input = (input_thread_t *)p_this;
    input_area_t *p_area;
gbazin's avatar
   
gbazin committed
1032
1033
    vlc_value_t val, val_list;
    int i, i_step = 0;
gbazin's avatar
   
gbazin committed
1034

gbazin's avatar
   
gbazin committed
1035
1036
    if( !strcmp( psz_cmd, "next-chapter" ) ) i_step++;
    else if( !strcmp( psz_cmd, "prev-chapter" ) ) i_step--;
gbazin's avatar
   
gbazin committed
1037

gbazin's avatar
   
gbazin committed
1038
1039
1040
    if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;

    /* Sanity check should have already been done by var_Set(). */
gbazin's avatar
   
gbazin committed
1041
    vlc_mutex_lock( &p_input->stream.stream_lock );
gbazin's avatar
   
gbazin committed
1042
1043
1044
1045
1046

    if( i_step )
    {
        var_Get( p_this, "chapter", &newval );
        var_Change( p_this, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
gbazin's avatar
   
gbazin committed
1047
1048
1049
1050
1051
1052
1053
1054
1055
        for( i = 0; i < val_list.p_list->i_count; i++ )
        {
            if( val_list.p_list->p_values[i].i_int == newval.i_int &&
                i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
            {
                newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
                break;
            }
        }
gbazin's avatar
   
gbazin committed
1056
1057
1058
        var_Change( p_this, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
    }

gbazin's avatar
   
gbazin committed
1059
1060
1061
1062
1063
1064
1065
    p_area = p_input->stream.p_selected_area;
    p_input->stream.p_selected_area->i_part = newval.i_int;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

    input_ChangeArea( p_input, p_area );
    input_SetStatus( p_input, INPUT_STATUS_PLAY );

1066
1067
1068
    val.b_bool = VLC_TRUE;
    var_Set( p_input, "intf-change", val );

gbazin's avatar
   
gbazin committed
1069
1070
1071
1072
1073
1074
1075
1076
    return VLC_SUCCESS;
}

static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    input_thread_t *p_input = (input_thread_t *)p_this;
    uint16_t i_area_id = (int)p_data;