* ALL: experimental code for stream (dvd) navigation through object variables.
[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.102 2003/03/11 23:56:54 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 );
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
528     if( i_data_len )
529     {
530         p_es->p_demux_data = malloc( i_data_len );
531         if( p_es->p_demux_data == NULL )
532         {
533             msg_Err( p_input, "out of memory" );
534             return( NULL );
535         }
536         memset( p_es->p_demux_data, 0, i_data_len );
537     }
538     else
539     {
540         p_es->p_demux_data = NULL;
541     }
542     p_es->p_waveformatex     = NULL;
543     p_es->p_bitmapinfoheader = NULL;
544
545     /* Add this ES to the program definition if one is given */
546     if( p_pgrm )
547     {
548         INSERT_ELEM( p_pgrm->pp_es,
549                      p_pgrm->i_es_number,
550                      p_pgrm->i_es_number,
551                      p_es );
552         p_es->p_pgrm = p_pgrm;
553     }
554     else
555     {
556         p_es->p_pgrm = NULL;
557     }
558
559     return p_es;
560 }
561
562 /*****************************************************************************
563  * input_DelES:
564  *****************************************************************************/
565 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
566 {
567     unsigned int            i_index, i_es_index;
568     pgrm_descriptor_t *     p_pgrm;
569
570     /* Find the ES in the ES table */
571     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
572          i_es_index++ )
573     {
574         if( p_input->stream.pp_es[i_es_index] == p_es )
575             break;
576     }
577
578     /* If the ES wasn't found, do nothing */
579     if( i_es_index == p_input->stream.i_es_number )
580     {
581         msg_Err( p_input, "ES does not belong to this input" );
582         return;
583     }
584
585     p_pgrm = p_es->p_pgrm;
586
587     /* Kill associated decoder, if any. */
588     if( p_es->p_decoder_fifo != NULL )
589     {
590         input_EndDecoder( p_input, p_es );
591     }
592
593     /* Remove this ES from the description of the program if it is associated to
594      * one */
595     if( p_pgrm )
596     {
597         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
598         {
599             if( p_pgrm->pp_es[i_index] == p_es )
600             {
601                 REMOVE_ELEM( p_pgrm->pp_es,
602                              p_pgrm->i_es_number,
603                              i_index );
604                 break;
605             }
606         }
607     }
608
609     /* Free the demux data */
610     if( p_es->p_demux_data != NULL )
611     {
612         free( p_es->p_demux_data );
613     }
614     if( p_es->p_waveformatex )
615     {
616         free( p_es->p_waveformatex );
617     }
618     if( p_es->p_bitmapinfoheader )
619     {
620         free( p_es->p_bitmapinfoheader );
621     }
622
623     /* Find the ES in the ES table */
624     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
625          i_es_index++ )
626     {
627         if( p_input->stream.pp_es[i_es_index] == p_es )
628             break;
629     }
630
631     /* Remove this ES from the stream's list of ES */
632     REMOVE_ELEM( p_input->stream.pp_es,
633                  p_input->stream.i_es_number,
634                  i_es_index );
635
636     /* Free the ES */
637     free( p_es );
638 }
639
640 /*****************************************************************************
641  * input_SelectES: selects an ES and spawns the associated decoder
642  *****************************************************************************
643  * Remember we are still supposed to have stream_lock when entering this
644  * function ?
645  *****************************************************************************/
646 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
647 {
648     if( p_es == NULL )
649     {
650         msg_Err( p_input, "nothing to do in input_SelectES" );
651         return -1;
652     }
653
654     if( ((p_es->i_cat == VIDEO_ES) || (p_es->i_cat == SPU_ES))
655         && !config_GetInt( p_input, "video" ) )
656     {
657         msg_Dbg( p_input,
658                  "video is disabled, not selecting ES 0x%x", p_es->i_id );
659         return -1;
660     }
661
662     if( (p_es->i_cat == AUDIO_ES) && !config_GetInt( p_input, "audio" ) )
663     {
664         msg_Dbg( p_input,
665                  "audio is disabled, not selecting ES 0x%x", p_es->i_id );
666         return -1;
667     }
668
669     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
670
671     if( p_es->p_decoder_fifo != NULL )
672     {
673         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
674         return -1;
675     }
676
677     /* Release the lock, not to block the input thread during
678      * the creation of the thread. */
679     vlc_mutex_unlock( &p_input->stream.stream_lock );
680     p_es->p_decoder_fifo = input_RunDecoder( p_input, p_es );
681     vlc_mutex_lock( &p_input->stream.stream_lock );
682
683     if( p_es->p_decoder_fifo == NULL )
684     {
685         return -1;
686     }
687
688     return 0;
689 }
690
691 /*****************************************************************************
692  * input_UnselectES: removes an ES from the list of selected ES
693  *****************************************************************************/
694 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
695 {
696
697     unsigned int i_index = 0;
698
699     if( p_es == NULL )
700     {
701         msg_Err( p_input, "nothing to do in input_UnselectES" );
702         return -1;
703     }
704
705     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
706
707     if( p_es->p_decoder_fifo == NULL )
708     {
709         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
710         return( -1 );
711     }
712
713     input_EndDecoder( p_input, p_es );
714     p_es->p_pes = NULL;
715
716     if( ( p_es->p_decoder_fifo == NULL ) &&
717         ( p_input->stream.i_selected_es_number > 0 ) )
718     {
719         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
720                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
721         {
722             i_index++;
723         }
724
725         /* XXX: no need to memmove, we have unsorted data */
726         REMOVE_ELEM( p_input->stream.pp_selected_es,
727                      p_input->stream.i_selected_es_number,
728                      i_index );
729
730         if( p_input->stream.i_selected_es_number == 0 )
731         {
732             msg_Dbg( p_input, "no more selected ES" );
733             return 1;
734         }
735     }
736
737     return 0;
738 }
739
740 /*****************************************************************************
741  * Navigation callback: a bunch of navigation variables are used as an
742  *  alternative to the navigation API.
743  *****************************************************************************/
744 static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
745                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
746 {
747     input_thread_t *p_input = (input_thread_t *)p_this;
748
749     if( oldval.i_int == newval.i_int )
750        return VLC_SUCCESS;
751
752     vlc_mutex_lock( &p_input->stream.stream_lock );
753     if( ( newval.i_int > 0 ) )
754     {
755         vlc_mutex_unlock( &p_input->stream.stream_lock );
756         input_ChangeProgram( p_input, newval.i_int );
757         input_SetStatus( p_input, INPUT_STATUS_PLAY );
758         vlc_mutex_lock( &p_input->stream.stream_lock );
759     }
760     vlc_mutex_unlock( &p_input->stream.stream_lock );
761
762     return VLC_SUCCESS;
763 }
764
765 static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
766                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
767 {
768     input_thread_t *p_input = (input_thread_t *)p_this;
769     input_area_t *p_area;
770
771     if( oldval.i_int == newval.i_int )
772        return VLC_SUCCESS;
773
774     /* Sanity check should have already be done by var_Set(). */
775     vlc_mutex_lock( &p_input->stream.stream_lock );
776     p_area = p_input->stream.pp_areas[newval.i_int];
777     p_area->i_part = 1;
778     vlc_mutex_unlock( &p_input->stream.stream_lock );
779     input_ChangeArea( p_input, p_area );
780     input_SetStatus( p_input, INPUT_STATUS_PLAY );
781
782     return VLC_SUCCESS;
783 }
784
785 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
786                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
787 {
788     input_thread_t *p_input = (input_thread_t *)p_this;
789     input_area_t *p_area;
790
791     if( oldval.i_int == newval.i_int )
792        return VLC_SUCCESS;
793
794     /* Sanity check will have already be done by var_Set(). */
795     vlc_mutex_lock( &p_input->stream.stream_lock );
796     p_area = p_input->stream.p_selected_area;
797     p_input->stream.p_selected_area->i_part = newval.i_int;
798     vlc_mutex_unlock( &p_input->stream.stream_lock );
799
800     input_ChangeArea( p_input, p_area );
801     input_SetStatus( p_input, INPUT_STATUS_PLAY );
802
803     return VLC_SUCCESS;
804 }
805
806 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
807                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
808 {
809     input_thread_t *p_input = (input_thread_t *)p_this;
810     uint16_t i_area_id = (int)p_data;
811
812     vlc_mutex_lock( &p_input->stream.stream_lock );
813
814     if( p_input->stream.p_selected_area->i_id == i_area_id &&
815         oldval.i_int == newval.i_int )
816     {
817         /* Nothing to do */
818         vlc_mutex_unlock( &p_input->stream.stream_lock );
819         return VLC_SUCCESS;
820     }
821
822     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
823         ( (uint16_t)newval.i_int <=
824           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
825     {
826         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
827         p_input->stream.p_selected_area->i_part = newval.i_int;
828         vlc_mutex_unlock( &p_input->stream.stream_lock );
829         input_ChangeArea( p_input, p_area );
830         input_SetStatus( p_input, INPUT_STATUS_PLAY );
831         vlc_mutex_lock( &p_input->stream.stream_lock );
832     }
833     vlc_mutex_unlock( &p_input->stream.stream_lock );
834
835     return VLC_SUCCESS;
836 }