]> git.sesse.net Git - vlc/blob - src/input/input_programs.c
cc66a626920c74dc65420477eab7a3981c9dede2
[vlc] / src / input / input_programs.c
1 /*****************************************************************************
2  * input_programs.c: es_descriptor_t, pgrm_descriptor_t management
3  *****************************************************************************
4  * Copyright (C) 1999-2002 VideoLAN
5  * $Id: input_programs.c,v 1.104 2003/04/13 20:00:21 fenrir Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <string.h>                                    /* memcpy(), memset() */
29
30 #include <vlc/vlc.h>
31
32 #include "stream_control.h"
33 #include "input_ext-intf.h"
34 #include "input_ext-dec.h"
35 #include "input_ext-plugins.h"
36
37 /*
38  * NOTICE : all of these functions expect you to have taken the lock on
39  * p_input->stream.lock
40  */
41
42 /* Navigation callbacks */
43 static int ProgramCallback( vlc_object_t *, char const *,
44                             vlc_value_t, vlc_value_t, void * );
45 static int TitleCallback( vlc_object_t *, char const *,
46                           vlc_value_t, vlc_value_t, void * );
47 static int ChapterCallback( vlc_object_t *, char const *,
48                             vlc_value_t, vlc_value_t, void * );
49 static int NavigationCallback( vlc_object_t *, char const *,
50                                vlc_value_t, vlc_value_t, void * );
51
52 /*****************************************************************************
53  * input_InitStream: init the stream descriptor of the given input
54  *****************************************************************************/
55 int input_InitStream( input_thread_t * p_input, size_t i_data_len )
56 {
57
58     p_input->stream.i_stream_id = 0;
59
60     /* initialized to 0 since we don't give the signal to the interface
61      * before the end of input initialization */
62     p_input->stream.b_changed = 0;
63     p_input->stream.pp_es = NULL;
64     p_input->stream.pp_selected_es = NULL;
65     p_input->stream.p_removed_es = NULL;
66     p_input->stream.p_newly_selected_es = NULL;
67     p_input->stream.pp_programs = NULL;
68     p_input->stream.p_selected_program = NULL;
69     p_input->stream.p_new_program = NULL;
70
71     if( i_data_len )
72     {
73         if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL )
74         {
75             msg_Err( p_input, "out of memory" );
76             return 1;
77         }
78         memset( p_input->stream.p_demux_data, 0, i_data_len );
79     }
80     else
81     {
82         p_input->stream.p_demux_data = NULL;
83     }
84
85     /* Create a few object variables used for navigation in the interfaces */
86     var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
87     var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
88     var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
89     var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE );
90     var_AddCallback( p_input, "program", ProgramCallback, NULL );
91     var_AddCallback( p_input, "title", TitleCallback, NULL );
92     var_AddCallback( p_input, "chapter", ChapterCallback, NULL );
93
94     return 0;
95 }
96
97 /*****************************************************************************
98  * input_EndStream: free all stream descriptors
99  *****************************************************************************/
100 void input_EndStream( input_thread_t * p_input )
101 {
102     /* Free navigation variables */
103     var_Destroy( p_input, "program" );
104     var_Destroy( p_input, "title" );
105     var_Destroy( p_input, "chapter" );
106
107     /* Free all programs and associated ES, and associated decoders. */
108     while( p_input->stream.i_pgrm_number )
109     {
110         input_DelProgram( p_input, p_input->stream.pp_programs[0] );
111     }
112
113     /* Free standalone ES */
114     while( p_input->stream.i_es_number )
115     {
116         input_DelES( p_input, p_input->stream.pp_es[0] );
117     }
118
119     /* Free all areas */
120     while( p_input->stream.i_area_nb )
121     {
122         input_DelArea( p_input, p_input->stream.pp_areas[0] );
123     }
124
125     /* Free selected ES */
126     if( p_input->stream.pp_selected_es != NULL )
127     {
128         free( p_input->stream.pp_selected_es );
129     }
130
131     if( p_input->stream.p_demux_data != NULL )
132     {
133         free( p_input->stream.p_demux_data );
134     }
135 }
136
137 /*****************************************************************************
138  * input_FindProgram: returns a pointer to a program described by its ID
139  *****************************************************************************/
140 pgrm_descriptor_t * input_FindProgram( input_thread_t * p_input,
141                                        uint16_t i_pgrm_id )
142 {
143     unsigned int i;
144
145     for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
146     {
147         if( p_input->stream.pp_programs[i]->i_number == i_pgrm_id )
148         {
149             return p_input->stream.pp_programs[i];
150         }
151     }
152
153     return NULL;
154 }
155
156 /*****************************************************************************
157  * input_AddProgram: add and init a program descriptor
158  *****************************************************************************
159  * This program descriptor will be referenced in the given stream descriptor
160  *****************************************************************************/
161 pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input,
162                                       u16 i_pgrm_id, size_t i_data_len )
163 {
164     /* Where to add the pgrm */
165     pgrm_descriptor_t * p_pgrm = malloc( sizeof(pgrm_descriptor_t) );
166     vlc_value_t val;
167
168     if( p_pgrm == NULL )
169     {
170         msg_Err( p_input, "out of memory" );
171         return NULL;
172     }
173
174     /* Init this entry */
175     p_pgrm->i_number = i_pgrm_id;
176     p_pgrm->b_is_ok = 0;
177     p_pgrm->i_version = 0;
178
179     p_pgrm->i_es_number = 0;
180     p_pgrm->pp_es = NULL;
181
182     input_ClockInit( p_pgrm );
183
184     p_pgrm->i_synchro_state = SYNCHRO_START;
185
186     if( i_data_len )
187     {
188         p_pgrm->p_demux_data = malloc( i_data_len );
189         if( p_pgrm->p_demux_data == NULL )
190         {
191             msg_Err( p_input, "out of memory" );
192             return NULL;
193         }
194         memset( p_pgrm->p_demux_data, 0, i_data_len );
195     }
196     else
197     {
198         p_pgrm->p_demux_data = NULL;
199     }
200
201     /* Add an entry to the list of program associated with the stream */
202     INSERT_ELEM( p_input->stream.pp_programs,
203                  p_input->stream.i_pgrm_number,
204                  p_input->stream.i_pgrm_number,
205                  p_pgrm );
206
207     val.i_int = i_pgrm_id;
208     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val );
209
210     return p_pgrm;
211 }
212
213 /*****************************************************************************
214  * input_DelProgram: destroy a program descriptor
215  *****************************************************************************
216  * All ES descriptions referenced in the descriptor will be deleted.
217  *****************************************************************************/
218 void input_DelProgram( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm )
219 {
220     unsigned int i_pgrm_index;
221     vlc_value_t val;
222
223     /* Find the program in the programs table */
224     for( i_pgrm_index = 0; i_pgrm_index < p_input->stream.i_pgrm_number;
225          i_pgrm_index++ )
226     {
227         if( p_input->stream.pp_programs[i_pgrm_index] == p_pgrm )
228             break;
229     }
230
231     /* If the program wasn't found, do nothing */
232     if( i_pgrm_index == p_input->stream.i_pgrm_number )
233     {
234         msg_Err( p_input, "program does not belong to this input" );
235         return;
236     }
237
238     val.i_int = i_pgrm_index;
239     var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val );
240
241     /* Free the structures that describe the es that belongs to that program */
242     while( p_pgrm->i_es_number )
243     {
244         input_DelES( p_input, p_pgrm->pp_es[0] );
245     }
246
247     /* Free the demux data */
248     if( p_pgrm->p_demux_data != NULL )
249     {
250         free( p_pgrm->p_demux_data );
251     }
252
253     /* Remove this program from the stream's list of programs */
254     REMOVE_ELEM( p_input->stream.pp_programs,
255                  p_input->stream.i_pgrm_number,
256                  i_pgrm_index );
257
258     /* Free the description of this program */
259     free( p_pgrm );
260 }
261
262 /*****************************************************************************
263  * input_AddArea: add and init an area descriptor
264  *****************************************************************************
265  * This area descriptor will be referenced in the given stream descriptor
266  *****************************************************************************/
267 input_area_t * input_AddArea( input_thread_t * p_input,
268                               uint16_t i_area_id, uint16_t i_part_nb )
269 {
270     /* Where to add the pgrm */
271     input_area_t * p_area = malloc( sizeof(input_area_t) );
272     vlc_value_t val;
273     int i;
274
275     if( p_area == NULL )
276     {
277         msg_Err( p_input, "out of memory" );
278         return NULL;
279     }
280
281     /* Init this entry */
282     p_area->i_id = i_area_id;
283     p_area->i_part_nb = i_part_nb;
284     p_area->i_part= 0;
285     p_area->i_start = 0;
286     p_area->i_size = 0;
287     p_area->i_tell = 0;
288     p_area->i_seek = NO_SEEK;
289
290     /* Add an entry to the list of program associated with the stream */
291     INSERT_ELEM( p_input->stream.pp_areas,
292                  p_input->stream.i_area_nb,
293                  p_input->stream.i_area_nb,
294                  p_area );
295
296     /* Don't add empty areas */
297     if( i_part_nb == 0 )
298         return NULL;
299
300     /* Take care of the navigation variables */
301     val.i_int = i_area_id;
302     var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val );
303
304     val.psz_string = malloc( sizeof("title ") + 5 );
305     if( val.psz_string )
306     {
307         vlc_value_t val2;
308
309         sprintf( val.psz_string, "title %2i", i_area_id );
310         var_Destroy( p_input, val.psz_string );
311         var_Create( p_input, val.psz_string, VLC_VAR_INTEGER |
312                     VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
313         var_AddCallback( p_input, val.psz_string, NavigationCallback,
314                          (void *)(int)i_area_id );
315
316         var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val );
317
318         for( i = 1; i <= i_part_nb; i++ )
319         {
320             val2.i_int = i;
321             var_Change( p_input, val.psz_string, VLC_VAR_ADDCHOICE, &val2 );
322         }
323     }
324
325     return p_area;
326 }
327
328 /*****************************************************************************
329  * input_SetProgram: changes the current program
330  *****************************************************************************/
331 int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg )
332 {
333     unsigned int i_es_index;
334     int i_required_audio_es;
335     int i_required_spu_es;
336     int i_audio_es = 0;
337     int i_spu_es = 0;
338     vlc_value_t val;
339
340     if ( p_input->stream.p_selected_program )
341     {
342         for ( i_es_index = 1 ; /* 0 should be the PMT */
343                 i_es_index < p_input->stream.p_selected_program->
344                 i_es_number ;
345                 i_es_index ++ )
346         {
347 #define p_es p_input->stream.p_selected_program->pp_es[i_es_index]
348             if ( p_es->p_decoder_fifo ) /* if the ES was selected */
349             {
350                 input_UnselectES( p_input , p_es );
351             }
352 #undef p_es
353         }
354     }
355     /* Get the number of the required audio stream */
356     if( config_GetInt( p_input, "audio" ) )
357     {
358         /* Default is the first one */
359         i_required_audio_es = config_GetInt( p_input, "audio-channel" );
360         if( i_required_audio_es < 0 )
361         {
362             i_required_audio_es = 1;
363         }
364     }
365     else
366     {
367         i_required_audio_es = 0;
368     }
369
370     /* Same thing for subtitles */
371     if( config_GetInt( p_input, "video" ) )
372     {
373         /* for spu, default is none */
374         i_required_spu_es = config_GetInt( p_input, "spu-channel" );
375         if( i_required_spu_es < 0 )
376         {
377             i_required_spu_es = 0;
378         }
379     }
380     else
381     {
382         i_required_spu_es = 0;
383     }
384
385     for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
386     {
387         switch( p_new_prg->pp_es[i_es_index]->i_cat )
388         {
389             case VIDEO_ES:
390                 msg_Dbg( p_input, "selecting ES %x",
391                          p_new_prg->pp_es[i_es_index]->i_id );
392                 input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
393                 break;
394             case AUDIO_ES:
395                 i_audio_es += 1;
396                 if( i_audio_es <= i_required_audio_es )
397                 {
398                     msg_Dbg( p_input, "selecting ES %x",
399                              p_new_prg->pp_es[i_es_index]->i_id );
400                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
401                 }
402                 break;
403             /* Not sure this one is fully specification-compliant */
404             case SPU_ES :
405                 i_spu_es += 1;
406                 if( i_spu_es <= i_required_spu_es )
407                 {
408                     msg_Dbg( p_input, "selecting ES %x",
409                              p_new_prg->pp_es[i_es_index]->i_id );
410                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
411                 }
412             break;
413             default :
414                 msg_Dbg( p_input, "ES %x has unknown type",
415                          p_new_prg->pp_es[i_es_index]->i_id );
416                 break;
417         }
418
419     }
420
421
422     p_input->stream.p_selected_program = p_new_prg;
423
424     /* Update the navigation variables without triggering a callback */
425     val.i_int = p_new_prg->i_number;
426     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val );
427
428     return( 0 );
429 }
430
431 /*****************************************************************************
432  * input_DelArea: destroy a area descriptor
433  *****************************************************************************
434  * All ES descriptions referenced in the descriptor will be deleted.
435  *****************************************************************************/
436 void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
437 {
438     unsigned int i_area_index;
439     vlc_value_t val;
440
441     /* Find the area in the areas table */
442     for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
443          i_area_index++ )
444     {
445         if( p_input->stream.pp_areas[i_area_index] == p_area )
446             break;
447     }
448
449     /* If the area wasn't found, do nothing */
450     if( i_area_index == p_input->stream.i_area_nb )
451     {
452         msg_Err( p_input, "area does not belong to this input" );
453         return;
454     }
455
456     /* Take care of the navigation variables */
457     val.psz_string = malloc( sizeof("title ") + 5 );
458     if( val.psz_string )
459     {
460         sprintf( val.psz_string, "title %i", p_area->i_id );
461         var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val );
462         var_Destroy( p_input, val.psz_string );
463     }
464
465     /* Remove this area from the stream's list of areas */
466     REMOVE_ELEM( p_input->stream.pp_areas,
467                  p_input->stream.i_area_nb,
468                  i_area_index );
469
470     /* Free the description of this area */
471     free( p_area );
472 }
473
474
475 /*****************************************************************************
476  * input_FindES: returns a pointer to an ES described by its ID
477  *****************************************************************************/
478 es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
479 {
480     unsigned int i;
481
482     for( i = 0; i < p_input->stream.i_es_number; i++ )
483     {
484         if( p_input->stream.pp_es[i]->i_id == i_es_id )
485         {
486             return p_input->stream.pp_es[i];
487         }
488     }
489
490     return NULL;
491 }
492
493 /*****************************************************************************
494  * input_AddES:
495  *****************************************************************************
496  * Reserve a slot in the table of ES descriptors for the ES and add it to the
497  * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
498  * alone (PSI ?)
499  *****************************************************************************/
500 es_descriptor_t * input_AddES( input_thread_t * p_input,
501                                pgrm_descriptor_t * p_pgrm, u16 i_es_id,
502                                size_t i_data_len )
503 {
504     es_descriptor_t * p_es;
505
506     p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
507     if( p_es == NULL )
508     {
509         msg_Err( p_input, "out of memory" );
510         return( NULL);
511     }
512
513     INSERT_ELEM( p_input->stream.pp_es,
514                  p_input->stream.i_es_number,
515                  p_input->stream.i_es_number,
516                  p_es );
517
518     /* Init its values */
519     p_es->i_id = i_es_id;
520     p_es->psz_desc[0] = '\0';
521     p_es->p_pes = NULL;
522     p_es->p_decoder_fifo = NULL;
523     p_es->i_cat = UNKNOWN_ES;
524     p_es->i_demux_fd = 0;
525     p_es->c_packets = 0;
526     p_es->c_invalid_packets = 0;
527     p_es->b_force_decoder = VLC_FALSE;
528
529     if( i_data_len )
530     {
531         p_es->p_demux_data = malloc( i_data_len );
532         if( p_es->p_demux_data == NULL )
533         {
534             msg_Err( p_input, "out of memory" );
535             return( NULL );
536         }
537         memset( p_es->p_demux_data, 0, i_data_len );
538     }
539     else
540     {
541         p_es->p_demux_data = NULL;
542     }
543     p_es->p_waveformatex     = NULL;
544     p_es->p_bitmapinfoheader = NULL;
545
546     /* Add this ES to the program definition if one is given */
547     if( p_pgrm )
548     {
549         INSERT_ELEM( p_pgrm->pp_es,
550                      p_pgrm->i_es_number,
551                      p_pgrm->i_es_number,
552                      p_es );
553         p_es->p_pgrm = p_pgrm;
554     }
555     else
556     {
557         p_es->p_pgrm = NULL;
558     }
559
560     return p_es;
561 }
562
563 /*****************************************************************************
564  * input_DelES:
565  *****************************************************************************/
566 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
567 {
568     unsigned int            i_index, i_es_index;
569     pgrm_descriptor_t *     p_pgrm;
570
571     /* Find the ES in the ES table */
572     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
573          i_es_index++ )
574     {
575         if( p_input->stream.pp_es[i_es_index] == p_es )
576             break;
577     }
578
579     /* If the ES wasn't found, do nothing */
580     if( i_es_index == p_input->stream.i_es_number )
581     {
582         msg_Err( p_input, "ES does not belong to this input" );
583         return;
584     }
585
586     p_pgrm = p_es->p_pgrm;
587
588     /* Kill associated decoder, if any. */
589     if( p_es->p_decoder_fifo != NULL )
590     {
591         input_EndDecoder( p_input, p_es );
592     }
593
594     /* Remove this ES from the description of the program if it is associated to
595      * one */
596     if( p_pgrm )
597     {
598         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
599         {
600             if( p_pgrm->pp_es[i_index] == p_es )
601             {
602                 REMOVE_ELEM( p_pgrm->pp_es,
603                              p_pgrm->i_es_number,
604                              i_index );
605                 break;
606             }
607         }
608     }
609
610     /* Free the demux data */
611     if( p_es->p_demux_data != NULL )
612     {
613         free( p_es->p_demux_data );
614     }
615     if( p_es->p_waveformatex )
616     {
617         free( p_es->p_waveformatex );
618     }
619     if( p_es->p_bitmapinfoheader )
620     {
621         free( p_es->p_bitmapinfoheader );
622     }
623
624     /* Find the ES in the ES table */
625     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
626          i_es_index++ )
627     {
628         if( p_input->stream.pp_es[i_es_index] == p_es )
629             break;
630     }
631
632     /* Remove this ES from the stream's list of ES */
633     REMOVE_ELEM( p_input->stream.pp_es,
634                  p_input->stream.i_es_number,
635                  i_es_index );
636
637     /* Free the ES */
638     free( p_es );
639 }
640
641 /*****************************************************************************
642  * input_SelectES: selects an ES and spawns the associated decoder
643  *****************************************************************************
644  * Remember we are still supposed to have stream_lock when entering this
645  * function ?
646  *****************************************************************************/
647 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
648 {
649     if( p_es == NULL )
650     {
651         msg_Err( p_input, "nothing to do in input_SelectES" );
652         return -1;
653     }
654
655     if( ((p_es->i_cat == VIDEO_ES) || (p_es->i_cat == SPU_ES))
656         && !config_GetInt( p_input, "video" ) )
657     {
658         msg_Dbg( p_input,
659                  "video is disabled, not selecting ES 0x%x", p_es->i_id );
660         return -1;
661     }
662
663     if( (p_es->i_cat == AUDIO_ES) && !config_GetInt( p_input, "audio" ) )
664     {
665         msg_Dbg( p_input,
666                  "audio is disabled, not selecting ES 0x%x", p_es->i_id );
667         return -1;
668     }
669
670     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
671
672     if( p_es->p_decoder_fifo != NULL )
673     {
674         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
675         return -1;
676     }
677
678     /* Release the lock, not to block the input thread during
679      * the creation of the thread. */
680     vlc_mutex_unlock( &p_input->stream.stream_lock );
681     p_es->p_decoder_fifo = input_RunDecoder( p_input, p_es );
682     vlc_mutex_lock( &p_input->stream.stream_lock );
683
684     if( p_es->p_decoder_fifo == NULL )
685     {
686         return -1;
687     }
688
689     return 0;
690 }
691
692 /*****************************************************************************
693  * input_UnselectES: removes an ES from the list of selected ES
694  *****************************************************************************/
695 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
696 {
697
698     unsigned int i_index = 0;
699
700     if( p_es == NULL )
701     {
702         msg_Err( p_input, "nothing to do in input_UnselectES" );
703         return -1;
704     }
705
706     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
707
708     if( p_es->p_decoder_fifo == NULL )
709     {
710         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
711         return( -1 );
712     }
713
714     input_EndDecoder( p_input, p_es );
715     p_es->p_pes = NULL;
716
717     if( ( p_es->p_decoder_fifo == NULL ) &&
718         ( p_input->stream.i_selected_es_number > 0 ) )
719     {
720         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
721                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
722         {
723             i_index++;
724         }
725
726         /* XXX: no need to memmove, we have unsorted data */
727         REMOVE_ELEM( p_input->stream.pp_selected_es,
728                      p_input->stream.i_selected_es_number,
729                      i_index );
730
731         if( p_input->stream.i_selected_es_number == 0 )
732         {
733             msg_Dbg( p_input, "no more selected ES" );
734             return 1;
735         }
736     }
737
738     return 0;
739 }
740
741 /*****************************************************************************
742  * Navigation callback: a bunch of navigation variables are used as an
743  *  alternative to the navigation API.
744  *****************************************************************************/
745 static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
746                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
747 {
748     input_thread_t *p_input = (input_thread_t *)p_this;
749
750     if( oldval.i_int == newval.i_int )
751        return VLC_SUCCESS;
752
753     vlc_mutex_lock( &p_input->stream.stream_lock );
754     if( ( newval.i_int > 0 ) )
755     {
756         vlc_mutex_unlock( &p_input->stream.stream_lock );
757         input_ChangeProgram( p_input, (uint16_t)newval.i_int );
758         input_SetStatus( p_input, INPUT_STATUS_PLAY );
759         vlc_mutex_lock( &p_input->stream.stream_lock );
760     }
761     vlc_mutex_unlock( &p_input->stream.stream_lock );
762
763     return VLC_SUCCESS;
764 }
765
766 static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
767                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
768 {
769     input_thread_t *p_input = (input_thread_t *)p_this;
770     input_area_t *p_area;
771
772     if( oldval.i_int == newval.i_int )
773        return VLC_SUCCESS;
774
775     /* Sanity check should have already be done by var_Set(). */
776     vlc_mutex_lock( &p_input->stream.stream_lock );
777     p_area = p_input->stream.pp_areas[newval.i_int];
778     p_area->i_part = 1;
779     vlc_mutex_unlock( &p_input->stream.stream_lock );
780     input_ChangeArea( p_input, p_area );
781     input_SetStatus( p_input, INPUT_STATUS_PLAY );
782
783     return VLC_SUCCESS;
784 }
785
786 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
787                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
788 {
789     input_thread_t *p_input = (input_thread_t *)p_this;
790     input_area_t *p_area;
791
792     if( oldval.i_int == newval.i_int )
793        return VLC_SUCCESS;
794
795     /* Sanity check will have already be done by var_Set(). */
796     vlc_mutex_lock( &p_input->stream.stream_lock );
797     p_area = p_input->stream.p_selected_area;
798     p_input->stream.p_selected_area->i_part = newval.i_int;
799     vlc_mutex_unlock( &p_input->stream.stream_lock );
800
801     input_ChangeArea( p_input, p_area );
802     input_SetStatus( p_input, INPUT_STATUS_PLAY );
803
804     return VLC_SUCCESS;
805 }
806
807 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
808                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
809 {
810     input_thread_t *p_input = (input_thread_t *)p_this;
811     uint16_t i_area_id = (int)p_data;
812
813     vlc_mutex_lock( &p_input->stream.stream_lock );
814
815     if( p_input->stream.p_selected_area->i_id == i_area_id &&
816         oldval.i_int == newval.i_int )
817     {
818         /* Nothing to do */
819         vlc_mutex_unlock( &p_input->stream.stream_lock );
820         return VLC_SUCCESS;
821     }
822
823     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
824         ( (uint16_t)newval.i_int <=
825           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
826     {
827         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
828         p_input->stream.p_selected_area->i_part = newval.i_int;
829         vlc_mutex_unlock( &p_input->stream.stream_lock );
830         input_ChangeArea( p_input, p_area );
831         input_SetStatus( p_input, INPUT_STATUS_PLAY );
832         vlc_mutex_lock( &p_input->stream.stream_lock );
833     }
834     vlc_mutex_unlock( &p_input->stream.stream_lock );
835
836     return VLC_SUCCESS;
837 }