44787ab8f043305f2a476be2f4cefadb78321397
[vlc.git] / 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.105 2003/05/04 22:42:17 gbazin 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, NULL );
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, NULL );
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, NULL );
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, NULL );
317
318         for( i = 1; i <= i_part_nb; i++ )
319         {
320             val2.i_int = i;
321             var_Change( p_input, val.psz_string,
322                         VLC_VAR_ADDCHOICE, &val2, NULL );
323         }
324     }
325
326     return p_area;
327 }
328
329 /*****************************************************************************
330  * input_SetProgram: changes the current program
331  *****************************************************************************/
332 int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg )
333 {
334     unsigned int i_es_index;
335     int i_required_audio_es;
336     int i_required_spu_es;
337     int i_audio_es = 0;
338     int i_spu_es = 0;
339     vlc_value_t val;
340
341     if ( p_input->stream.p_selected_program )
342     {
343         for ( i_es_index = 1 ; /* 0 should be the PMT */
344                 i_es_index < p_input->stream.p_selected_program->
345                 i_es_number ;
346                 i_es_index ++ )
347         {
348 #define p_es p_input->stream.p_selected_program->pp_es[i_es_index]
349             if ( p_es->p_decoder_fifo ) /* if the ES was selected */
350             {
351                 input_UnselectES( p_input , p_es );
352             }
353 #undef p_es
354         }
355     }
356     /* Get the number of the required audio stream */
357     if( config_GetInt( p_input, "audio" ) )
358     {
359         /* Default is the first one */
360         i_required_audio_es = config_GetInt( p_input, "audio-channel" );
361         if( i_required_audio_es < 0 )
362         {
363             i_required_audio_es = 1;
364         }
365     }
366     else
367     {
368         i_required_audio_es = 0;
369     }
370
371     /* Same thing for subtitles */
372     if( config_GetInt( p_input, "video" ) )
373     {
374         /* for spu, default is none */
375         i_required_spu_es = config_GetInt( p_input, "spu-channel" );
376         if( i_required_spu_es < 0 )
377         {
378             i_required_spu_es = 0;
379         }
380     }
381     else
382     {
383         i_required_spu_es = 0;
384     }
385
386     for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
387     {
388         switch( p_new_prg->pp_es[i_es_index]->i_cat )
389         {
390             case VIDEO_ES:
391                 msg_Dbg( p_input, "selecting ES %x",
392                          p_new_prg->pp_es[i_es_index]->i_id );
393                 input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
394                 break;
395             case AUDIO_ES:
396                 i_audio_es += 1;
397                 if( i_audio_es <= i_required_audio_es )
398                 {
399                     msg_Dbg( p_input, "selecting ES %x",
400                              p_new_prg->pp_es[i_es_index]->i_id );
401                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
402                 }
403                 break;
404             /* Not sure this one is fully specification-compliant */
405             case SPU_ES :
406                 i_spu_es += 1;
407                 if( i_spu_es <= i_required_spu_es )
408                 {
409                     msg_Dbg( p_input, "selecting ES %x",
410                              p_new_prg->pp_es[i_es_index]->i_id );
411                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
412                 }
413             break;
414             default :
415                 msg_Dbg( p_input, "ES %x has unknown type",
416                          p_new_prg->pp_es[i_es_index]->i_id );
417                 break;
418         }
419
420     }
421
422
423     p_input->stream.p_selected_program = p_new_prg;
424
425     /* Update the navigation variables without triggering a callback */
426     val.i_int = p_new_prg->i_number;
427     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
428
429     return( 0 );
430 }
431
432 /*****************************************************************************
433  * input_DelArea: destroy a area descriptor
434  *****************************************************************************
435  * All ES descriptions referenced in the descriptor will be deleted.
436  *****************************************************************************/
437 void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
438 {
439     unsigned int i_area_index;
440     vlc_value_t val;
441
442     /* Find the area in the areas table */
443     for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
444          i_area_index++ )
445     {
446         if( p_input->stream.pp_areas[i_area_index] == p_area )
447             break;
448     }
449
450     /* If the area wasn't found, do nothing */
451     if( i_area_index == p_input->stream.i_area_nb )
452     {
453         msg_Err( p_input, "area does not belong to this input" );
454         return;
455     }
456
457     /* Take care of the navigation variables */
458     val.psz_string = malloc( sizeof("title ") + 5 );
459     if( val.psz_string )
460     {
461         sprintf( val.psz_string, "title %i", p_area->i_id );
462         var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL );
463         var_Destroy( p_input, val.psz_string );
464     }
465
466     /* Remove this area from the stream's list of areas */
467     REMOVE_ELEM( p_input->stream.pp_areas,
468                  p_input->stream.i_area_nb,
469                  i_area_index );
470
471     /* Free the description of this area */
472     free( p_area );
473 }
474
475
476 /*****************************************************************************
477  * input_FindES: returns a pointer to an ES described by its ID
478  *****************************************************************************/
479 es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
480 {
481     unsigned int i;
482
483     for( i = 0; i < p_input->stream.i_es_number; i++ )
484     {
485         if( p_input->stream.pp_es[i]->i_id == i_es_id )
486         {
487             return p_input->stream.pp_es[i];
488         }
489     }
490
491     return NULL;
492 }
493
494 /*****************************************************************************
495  * input_AddES:
496  *****************************************************************************
497  * Reserve a slot in the table of ES descriptors for the ES and add it to the
498  * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
499  * alone (PSI ?)
500  *****************************************************************************/
501 es_descriptor_t * input_AddES( input_thread_t * p_input,
502                                pgrm_descriptor_t * p_pgrm, u16 i_es_id,
503                                size_t i_data_len )
504 {
505     es_descriptor_t * p_es;
506
507     p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
508     if( p_es == NULL )
509     {
510         msg_Err( p_input, "out of memory" );
511         return( NULL);
512     }
513
514     INSERT_ELEM( p_input->stream.pp_es,
515                  p_input->stream.i_es_number,
516                  p_input->stream.i_es_number,
517                  p_es );
518
519     /* Init its values */
520     p_es->i_id = i_es_id;
521     p_es->psz_desc[0] = '\0';
522     p_es->p_pes = NULL;
523     p_es->p_decoder_fifo = NULL;
524     p_es->i_cat = UNKNOWN_ES;
525     p_es->i_demux_fd = 0;
526     p_es->c_packets = 0;
527     p_es->c_invalid_packets = 0;
528     p_es->b_force_decoder = VLC_FALSE;
529
530     if( i_data_len )
531     {
532         p_es->p_demux_data = malloc( i_data_len );
533         if( p_es->p_demux_data == NULL )
534         {
535             msg_Err( p_input, "out of memory" );
536             return( NULL );
537         }
538         memset( p_es->p_demux_data, 0, i_data_len );
539     }
540     else
541     {
542         p_es->p_demux_data = NULL;
543     }
544     p_es->p_waveformatex     = NULL;
545     p_es->p_bitmapinfoheader = NULL;
546
547     /* Add this ES to the program definition if one is given */
548     if( p_pgrm )
549     {
550         INSERT_ELEM( p_pgrm->pp_es,
551                      p_pgrm->i_es_number,
552                      p_pgrm->i_es_number,
553                      p_es );
554         p_es->p_pgrm = p_pgrm;
555     }
556     else
557     {
558         p_es->p_pgrm = NULL;
559     }
560
561     return p_es;
562 }
563
564 /*****************************************************************************
565  * input_DelES:
566  *****************************************************************************/
567 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
568 {
569     unsigned int            i_index, i_es_index;
570     pgrm_descriptor_t *     p_pgrm;
571
572     /* Find the ES in the ES table */
573     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
574          i_es_index++ )
575     {
576         if( p_input->stream.pp_es[i_es_index] == p_es )
577             break;
578     }
579
580     /* If the ES wasn't found, do nothing */
581     if( i_es_index == p_input->stream.i_es_number )
582     {
583         msg_Err( p_input, "ES does not belong to this input" );
584         return;
585     }
586
587     p_pgrm = p_es->p_pgrm;
588
589     /* Kill associated decoder, if any. */
590     if( p_es->p_decoder_fifo != NULL )
591     {
592         input_EndDecoder( p_input, p_es );
593     }
594
595     /* Remove this ES from the description of the program if it is associated to
596      * one */
597     if( p_pgrm )
598     {
599         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
600         {
601             if( p_pgrm->pp_es[i_index] == p_es )
602             {
603                 REMOVE_ELEM( p_pgrm->pp_es,
604                              p_pgrm->i_es_number,
605                              i_index );
606                 break;
607             }
608         }
609     }
610
611     /* Free the demux data */
612     if( p_es->p_demux_data != NULL )
613     {
614         free( p_es->p_demux_data );
615     }
616     if( p_es->p_waveformatex )
617     {
618         free( p_es->p_waveformatex );
619     }
620     if( p_es->p_bitmapinfoheader )
621     {
622         free( p_es->p_bitmapinfoheader );
623     }
624
625     /* Find the ES in the ES table */
626     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
627          i_es_index++ )
628     {
629         if( p_input->stream.pp_es[i_es_index] == p_es )
630             break;
631     }
632
633     /* Remove this ES from the stream's list of ES */
634     REMOVE_ELEM( p_input->stream.pp_es,
635                  p_input->stream.i_es_number,
636                  i_es_index );
637
638     /* Free the ES */
639     free( p_es );
640 }
641
642 /*****************************************************************************
643  * input_SelectES: selects an ES and spawns the associated decoder
644  *****************************************************************************
645  * Remember we are still supposed to have stream_lock when entering this
646  * function ?
647  *****************************************************************************/
648 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
649 {
650     if( p_es == NULL )
651     {
652         msg_Err( p_input, "nothing to do in input_SelectES" );
653         return -1;
654     }
655
656     if( ((p_es->i_cat == VIDEO_ES) || (p_es->i_cat == SPU_ES))
657         && !config_GetInt( p_input, "video" ) )
658     {
659         msg_Dbg( p_input,
660                  "video is disabled, not selecting ES 0x%x", p_es->i_id );
661         return -1;
662     }
663
664     if( (p_es->i_cat == AUDIO_ES) && !config_GetInt( p_input, "audio" ) )
665     {
666         msg_Dbg( p_input,
667                  "audio is disabled, not selecting ES 0x%x", p_es->i_id );
668         return -1;
669     }
670
671     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
672
673     if( p_es->p_decoder_fifo != NULL )
674     {
675         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
676         return -1;
677     }
678
679     /* Release the lock, not to block the input thread during
680      * the creation of the thread. */
681     vlc_mutex_unlock( &p_input->stream.stream_lock );
682     p_es->p_decoder_fifo = input_RunDecoder( p_input, p_es );
683     vlc_mutex_lock( &p_input->stream.stream_lock );
684
685     if( p_es->p_decoder_fifo == NULL )
686     {
687         return -1;
688     }
689
690     return 0;
691 }
692
693 /*****************************************************************************
694  * input_UnselectES: removes an ES from the list of selected ES
695  *****************************************************************************/
696 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
697 {
698
699     unsigned int i_index = 0;
700
701     if( p_es == NULL )
702     {
703         msg_Err( p_input, "nothing to do in input_UnselectES" );
704         return -1;
705     }
706
707     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
708
709     if( p_es->p_decoder_fifo == NULL )
710     {
711         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
712         return( -1 );
713     }
714
715     input_EndDecoder( p_input, p_es );
716     p_es->p_pes = NULL;
717
718     if( ( p_es->p_decoder_fifo == NULL ) &&
719         ( p_input->stream.i_selected_es_number > 0 ) )
720     {
721         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
722                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
723         {
724             i_index++;
725         }
726
727         /* XXX: no need to memmove, we have unsorted data */
728         REMOVE_ELEM( p_input->stream.pp_selected_es,
729                      p_input->stream.i_selected_es_number,
730                      i_index );
731
732         if( p_input->stream.i_selected_es_number == 0 )
733         {
734             msg_Dbg( p_input, "no more selected ES" );
735             return 1;
736         }
737     }
738
739     return 0;
740 }
741
742 /*****************************************************************************
743  * Navigation callback: a bunch of navigation variables are used as an
744  *  alternative to the navigation API.
745  *****************************************************************************/
746 static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
747                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
748 {
749     input_thread_t *p_input = (input_thread_t *)p_this;
750
751     if( oldval.i_int == newval.i_int )
752        return VLC_SUCCESS;
753
754     vlc_mutex_lock( &p_input->stream.stream_lock );
755     if( ( newval.i_int > 0 ) )
756     {
757         vlc_mutex_unlock( &p_input->stream.stream_lock );
758         input_ChangeProgram( p_input, (uint16_t)newval.i_int );
759         input_SetStatus( p_input, INPUT_STATUS_PLAY );
760         vlc_mutex_lock( &p_input->stream.stream_lock );
761     }
762     vlc_mutex_unlock( &p_input->stream.stream_lock );
763
764     return VLC_SUCCESS;
765 }
766
767 static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
768                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
769 {
770     input_thread_t *p_input = (input_thread_t *)p_this;
771     input_area_t *p_area;
772
773     if( oldval.i_int == newval.i_int )
774        return VLC_SUCCESS;
775
776     /* Sanity check should have already be done by var_Set(). */
777     vlc_mutex_lock( &p_input->stream.stream_lock );
778     p_area = p_input->stream.pp_areas[newval.i_int];
779     p_area->i_part = 1;
780     vlc_mutex_unlock( &p_input->stream.stream_lock );
781     input_ChangeArea( p_input, p_area );
782     input_SetStatus( p_input, INPUT_STATUS_PLAY );
783
784     return VLC_SUCCESS;
785 }
786
787 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
788                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
789 {
790     input_thread_t *p_input = (input_thread_t *)p_this;
791     input_area_t *p_area;
792
793     if( oldval.i_int == newval.i_int )
794        return VLC_SUCCESS;
795
796     /* Sanity check will have already be done by var_Set(). */
797     vlc_mutex_lock( &p_input->stream.stream_lock );
798     p_area = p_input->stream.p_selected_area;
799     p_input->stream.p_selected_area->i_part = newval.i_int;
800     vlc_mutex_unlock( &p_input->stream.stream_lock );
801
802     input_ChangeArea( p_input, p_area );
803     input_SetStatus( p_input, INPUT_STATUS_PLAY );
804
805     return VLC_SUCCESS;
806 }
807
808 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
809                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
810 {
811     input_thread_t *p_input = (input_thread_t *)p_this;
812     uint16_t i_area_id = (int)p_data;
813
814     vlc_mutex_lock( &p_input->stream.stream_lock );
815
816     if( p_input->stream.p_selected_area->i_id == i_area_id &&
817         oldval.i_int == newval.i_int )
818     {
819         /* Nothing to do */
820         vlc_mutex_unlock( &p_input->stream.stream_lock );
821         return VLC_SUCCESS;
822     }
823
824     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
825         ( (uint16_t)newval.i_int <=
826           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
827     {
828         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
829         p_input->stream.p_selected_area->i_part = newval.i_int;
830         vlc_mutex_unlock( &p_input->stream.stream_lock );
831         input_ChangeArea( p_input, p_area );
832         input_SetStatus( p_input, INPUT_STATUS_PLAY );
833         vlc_mutex_lock( &p_input->stream.stream_lock );
834     }
835     vlc_mutex_unlock( &p_input->stream.stream_lock );
836
837     return VLC_SUCCESS;
838 }