]> git.sesse.net Git - vlc/blob - src/input/es_out.c
video.c: don't display pre-rolled pictures
[vlc] / src / input / es_out.c
1 /*****************************************************************************\r
2  * es_out.c: Es Out handler for input.\r
3  *****************************************************************************\r
4  * Copyright (C) 2003-2004 VideoLAN\r
5  * $Id$\r
6  *\r
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>\r
8  *\r
9  * This program is free software; you can redistribute it and/or modify\r
10  * it under the terms of the GNU General Public License as published by\r
11  * the Free Software Foundation; either version 2 of the License, or\r
12  * (at your option) any later version.\r
13  *\r
14  * This program is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU General Public License for more details.\r
18  *\r
19  * You should have received a copy of the GNU General Public License\r
20  * along with this program; if not, write to the Free Software\r
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
22  *****************************************************************************/\r
23 \r
24 /*****************************************************************************\r
25  * Preamble\r
26  *****************************************************************************/\r
27 #include <stdlib.h>\r
28 \r
29 #include <vlc/vlc.h>\r
30 #include <vlc/input.h>\r
31 #include <vlc/decoder.h>\r
32 \r
33 #include "input_internal.h"\r
34 \r
35 #include "vlc_playlist.h"\r
36 #include "iso_lang.h"\r
37 /* FIXME we should find a better way than including that */\r
38 #include "../misc/iso-639_def.h"\r
39 \r
40 /*****************************************************************************\r
41  * Local prototypes\r
42  *****************************************************************************/\r
43 typedef struct\r
44 {\r
45     /* Program ID */\r
46     int i_id;\r
47 \r
48     /* Number of es for this pgrm */\r
49     int i_es;\r
50 \r
51     vlc_bool_t b_selected;\r
52 \r
53     /* Clock for this program */\r
54     input_clock_t clock;\r
55 \r
56 } es_out_pgrm_t;\r
57 \r
58 struct es_out_id_t\r
59 {\r
60     /* ES ID */\r
61     int       i_id;\r
62     es_out_pgrm_t *p_pgrm;\r
63 \r
64     /* */\r
65     int64_t i_preroll_end;\r
66 \r
67     /* Channel in the track type */\r
68     int         i_channel;\r
69     es_format_t fmt;\r
70     char        *psz_language;\r
71     char        *psz_language_code;\r
72     decoder_t   *p_dec;\r
73 };\r
74 \r
75 struct es_out_sys_t\r
76 {\r
77     input_thread_t *p_input;\r
78 \r
79     /* all programs */\r
80     int           i_pgrm;\r
81     es_out_pgrm_t **pgrm;\r
82     es_out_pgrm_t **pp_selected_pgrm; /* --programs */\r
83     es_out_pgrm_t *p_pgrm;  /* Master program */\r
84 \r
85     /* all es */\r
86     int         i_id;\r
87     int         i_es;\r
88     es_out_id_t **es;\r
89 \r
90     /* mode gestion */\r
91     vlc_bool_t  b_active;\r
92     int         i_mode;\r
93 \r
94     /* es count */\r
95     int         i_audio;\r
96     int         i_video;\r
97     int         i_sub;\r
98 \r
99     /* es to select */\r
100     int         i_audio_last;\r
101     int         i_sub_last;\r
102     char        **ppsz_audio_language;\r
103     char        **ppsz_sub_language;\r
104 \r
105     /* current main es */\r
106     es_out_id_t *p_es_audio;\r
107     es_out_id_t *p_es_video;\r
108     es_out_id_t *p_es_sub;\r
109 \r
110     /* delay */\r
111     int64_t i_audio_delay;\r
112     int64_t i_spu_delay;\r
113 };\r
114 \r
115 static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );\r
116 static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );\r
117 static void         EsOutDel    ( es_out_t *, es_out_id_t * );\r
118 static void         EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force );\r
119 static int          EsOutControl( es_out_t *, int i_query, va_list );\r
120 \r
121 static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );\r
122 \r
123 static void EsSelect( es_out_t *out, es_out_id_t *es );\r
124 static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );\r
125 static char *LanguageGetName( const char *psz_code );\r
126 static char *LanguageGetCode( const char *psz_lang );\r
127 static char **LanguageSplit( const char *psz_langs );\r
128 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );\r
129 \r
130 /*****************************************************************************\r
131  * input_EsOutNew:\r
132  *****************************************************************************/\r
133 es_out_t *input_EsOutNew( input_thread_t *p_input )\r
134 {\r
135     es_out_t     *out = malloc( sizeof( es_out_t ) );\r
136     es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );\r
137     vlc_value_t  val;\r
138     int i;\r
139 \r
140     out->pf_add     = EsOutAdd;\r
141     out->pf_send    = EsOutSend;\r
142     out->pf_del     = EsOutDel;\r
143     out->pf_control = EsOutControl;\r
144     out->p_sys      = p_sys;\r
145 \r
146     p_sys->p_input = p_input;\r
147 \r
148     p_sys->b_active = VLC_FALSE;\r
149     p_sys->i_mode   = ES_OUT_MODE_AUTO;\r
150 \r
151 \r
152     p_sys->i_pgrm   = 0;\r
153     p_sys->pgrm     = NULL;\r
154     p_sys->p_pgrm   = NULL;\r
155 \r
156     p_sys->i_id    = 0;\r
157     p_sys->i_es    = 0;\r
158     p_sys->es      = NULL;\r
159 \r
160     p_sys->i_audio = 0;\r
161     p_sys->i_video = 0;\r
162     p_sys->i_sub   = 0;\r
163 \r
164     /* */\r
165     var_Get( p_input, "audio-track", &val );\r
166     p_sys->i_audio_last = val.i_int;\r
167 \r
168     var_Get( p_input, "sub-track", &val );\r
169     p_sys->i_sub_last = val.i_int;\r
170 \r
171     var_Get( p_input, "audio-language", &val );\r
172     p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);\r
173     if( p_sys->ppsz_audio_language )\r
174     {\r
175         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )\r
176             msg_Dbg( p_input, "Select audio in language[%d] %s",\r
177                      i, p_sys->ppsz_audio_language[i] );\r
178     }\r
179 \r
180     var_Get( p_input, "sub-language", &val );\r
181     p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);\r
182     if( p_sys->ppsz_sub_language )\r
183     {\r
184         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )\r
185             msg_Dbg( p_input, "Select subtitle in language[%d] %s",\r
186                      i, p_sys->ppsz_sub_language[i] );\r
187     }\r
188 \r
189     /* */\r
190     p_sys->p_es_audio = NULL;\r
191     p_sys->p_es_video = NULL;\r
192     p_sys->p_es_sub   = NULL;\r
193 \r
194     p_sys->i_audio_delay= 0;\r
195     p_sys->i_spu_delay  = 0;\r
196 \r
197     return out;\r
198 }\r
199 \r
200 /*****************************************************************************\r
201  * input_EsOutDelete:\r
202  *****************************************************************************/\r
203 void input_EsOutDelete( es_out_t *out )\r
204 {\r
205     es_out_sys_t *p_sys = out->p_sys;\r
206     int i;\r
207 \r
208     for( i = 0; i < p_sys->i_es; i++ )\r
209     {\r
210         if( p_sys->es[i]->p_dec )\r
211         {\r
212             input_DecoderDelete( p_sys->es[i]->p_dec );\r
213         }\r
214         if( p_sys->es[i]->psz_language )\r
215             free( p_sys->es[i]->psz_language );\r
216         if( p_sys->es[i]->psz_language_code )\r
217             free( p_sys->es[i]->psz_language_code );\r
218         es_format_Clean( &p_sys->es[i]->fmt );\r
219 \r
220         free( p_sys->es[i] );\r
221     }\r
222     if( p_sys->ppsz_audio_language )\r
223     {\r
224         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )\r
225             free( p_sys->ppsz_audio_language[i] );\r
226         free( p_sys->ppsz_audio_language );\r
227     }\r
228     if( p_sys->ppsz_sub_language )\r
229     {\r
230         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )\r
231             free( p_sys->ppsz_sub_language[i] );\r
232         free( p_sys->ppsz_sub_language );\r
233     }\r
234 \r
235     if( p_sys->es )\r
236         free( p_sys->es );\r
237 \r
238     for( i = 0; i < p_sys->i_pgrm; i++ )\r
239     {\r
240         free( p_sys->pgrm[i] );\r
241     }\r
242     if( p_sys->pgrm )\r
243         free( p_sys->pgrm );\r
244 \r
245     free( p_sys );\r
246     free( out );\r
247 }\r
248 \r
249 es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )\r
250 {\r
251     int i;\r
252     if( i_id < 0 )\r
253     {\r
254         /* Special HACK, -i_id is tha cat of the stream */\r
255         return (es_out_id_t*)((uint8_t*)NULL-i_id);\r
256     }\r
257 \r
258     for( i = 0; i < out->p_sys->i_es; i++ )\r
259     {\r
260         if( out->p_sys->es[i]->i_id == i_id )\r
261             return out->p_sys->es[i];\r
262     }\r
263     return NULL;\r
264 }\r
265 \r
266 void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio )\r
267 {\r
268     es_out_sys_t      *p_sys = out->p_sys;\r
269     int i;\r
270 \r
271     for( i = 0; i < p_sys->i_es; i++ )\r
272     {\r
273         es_out_id_t *es = p_sys->es[i];\r
274 \r
275         /* Send a dummy block to let decoder know that\r
276          * there is a discontinuity */\r
277         if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )\r
278         {\r
279             input_DecoderDiscontinuity( es->p_dec );\r
280         }\r
281     }\r
282 }\r
283 \r
284 void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )\r
285 {\r
286     es_out_sys_t *p_sys = out->p_sys;\r
287 \r
288     if( i_cat == AUDIO_ES )\r
289         p_sys->i_audio_delay = i_delay;\r
290     else if( i_cat == SPU_ES )\r
291         p_sys->i_spu_delay = i_delay;\r
292 }\r
293 \r
294 vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out )\r
295 {\r
296     es_out_sys_t      *p_sys = out->p_sys;\r
297     int i;\r
298 \r
299     for( i = 0; i < p_sys->i_es; i++ )\r
300     {\r
301         es_out_id_t *es = p_sys->es[i];\r
302 \r
303         if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )\r
304             return VLC_FALSE;\r
305     }\r
306     return VLC_TRUE;\r
307 }\r
308 \r
309 /*****************************************************************************\r
310  *\r
311  *****************************************************************************/\r
312 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,\r
313                               vlc_bool_t b_delete )\r
314 {\r
315     es_out_sys_t      *p_sys = out->p_sys;\r
316     input_thread_t    *p_input = p_sys->p_input;\r
317     vlc_value_t       val, text;\r
318 \r
319     char *psz_var;\r
320 \r
321     if( es->fmt.i_cat == AUDIO_ES )\r
322         psz_var = "audio-es";\r
323     else if( es->fmt.i_cat == VIDEO_ES )\r
324         psz_var = "video-es";\r
325     else if( es->fmt.i_cat == SPU_ES )\r
326         psz_var = "spu-es";\r
327     else\r
328         return;\r
329 \r
330     if( b_delete )\r
331     {\r
332         val.i_int = es->i_id;\r
333         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );\r
334         var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
335         return;\r
336     }\r
337 \r
338     /* Get the number of ES already added */\r
339     var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );\r
340     if( val.i_int == 0 )\r
341     {\r
342         vlc_value_t val2;\r
343 \r
344         /* First one, we need to add the "Disable" choice */\r
345         val2.i_int = -1; text.psz_string = _("Disable");\r
346         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );\r
347         val.i_int++;\r
348     }\r
349 \r
350     /* Take care of the ES description */\r
351     if( es->fmt.psz_description && *es->fmt.psz_description )\r
352     {\r
353         if( es->psz_language && *es->psz_language )\r
354         {\r
355             text.psz_string = malloc( strlen( es->fmt.psz_description) + strlen( es->psz_language ) + 10 );\r
356             sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, es->psz_language );\r
357         }\r
358         else text.psz_string = strdup( es->fmt.psz_description );\r
359     }\r
360     else\r
361     {\r
362         if( es->psz_language && *es->psz_language )\r
363         {\r
364             char *temp;\r
365             text.psz_string = malloc( strlen( _("Track %i") )+ strlen( es->psz_language ) + 30 );\r
366             asprintf( &temp,  _("Track %i"), val.i_int );\r
367             sprintf( text.psz_string, "%s - [%s]", temp, es->psz_language );\r
368             free( temp );\r
369         }\r
370         else\r
371         {\r
372             text.psz_string = malloc( strlen( _("Track %i") ) + 20 );\r
373             sprintf( text.psz_string, _("Track %i"), val.i_int );\r
374         }\r
375     }\r
376 \r
377     val.i_int = es->i_id;\r
378     var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );\r
379 \r
380     free( text.psz_string );\r
381 \r
382     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
383 }\r
384 \r
385 /* EsOutProgramSelect:\r
386  *  Select a program and update the object variable\r
387  */\r
388 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )\r
389 {\r
390     es_out_sys_t      *p_sys = out->p_sys;\r
391     input_thread_t    *p_input = p_sys->p_input;\r
392     vlc_value_t       val;\r
393     int               i;\r
394 \r
395     if( p_sys->p_pgrm == p_pgrm )\r
396         return; /* Nothing to do */\r
397 \r
398     if( p_sys->p_pgrm )\r
399     {\r
400         es_out_pgrm_t *old = p_sys->p_pgrm;\r
401         msg_Dbg( p_input, "Unselecting program id=%d", old->i_id );\r
402 \r
403         for( i = 0; i < p_sys->i_es; i++ )\r
404         {\r
405             if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec &&\r
406                 p_sys->i_mode != ES_OUT_MODE_ALL )\r
407                 EsUnselect( out, p_sys->es[i], VLC_TRUE );\r
408         }\r
409 \r
410         p_sys->p_es_audio = NULL;\r
411         p_sys->p_es_sub = NULL;\r
412         p_sys->p_es_video = NULL;\r
413     }\r
414 \r
415     msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id );\r
416 \r
417     /* Mark it selected */\r
418     p_pgrm->b_selected = VLC_TRUE;\r
419 \r
420     /* Switch master stream */\r
421     if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )\r
422     {\r
423         p_sys->p_pgrm->clock.b_master = VLC_FALSE;\r
424     }\r
425     p_pgrm->clock.b_master = VLC_TRUE;\r
426     p_sys->p_pgrm = p_pgrm;\r
427 \r
428     /* Update "program" */\r
429     val.i_int = p_pgrm->i_id;\r
430     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );\r
431 \r
432     /* Update "es-*" */\r
433     var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );\r
434     var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );\r
435     var_Change( p_input, "spu-es",   VLC_VAR_CLEARCHOICES, NULL, NULL );\r
436     for( i = 0; i < p_sys->i_es; i++ )\r
437     {\r
438         if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )\r
439             EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE );\r
440         EsOutSelect( out, p_sys->es[i], VLC_FALSE );\r
441     }\r
442 \r
443     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
444 }\r
445 \r
446 /* EsOutAddProgram:\r
447  *  Add a program\r
448  */\r
449 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )\r
450 {\r
451     es_out_sys_t      *p_sys = out->p_sys;\r
452     input_thread_t    *p_input = p_sys->p_input;\r
453     vlc_value_t       val;\r
454 \r
455     es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );\r
456 \r
457     /* Init */\r
458     p_pgrm->i_id = i_group;\r
459     p_pgrm->i_es = 0;\r
460     p_pgrm->b_selected = VLC_FALSE;\r
461     input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average );\r
462 \r
463     /* Append it */\r
464     TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );\r
465 \r
466     /* Update "program" variable */\r
467     val.i_int = i_group;\r
468     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );\r
469 \r
470     if( i_group == var_GetInteger( p_input, "program" ) )\r
471     {\r
472         EsOutProgramSelect( out, p_pgrm );\r
473     }\r
474     else\r
475     {\r
476         var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
477     }\r
478     return p_pgrm;\r
479 }\r
480 \r
481 /* EsOutAdd:\r
482  *  Add an es_out\r
483  */\r
484 static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )\r
485 {\r
486     es_out_sys_t      *p_sys = out->p_sys;\r
487     input_thread_t    *p_input = p_sys->p_input;\r
488 \r
489     es_out_id_t       *es = malloc( sizeof( es_out_id_t ) );\r
490     es_out_pgrm_t     *p_pgrm = NULL;\r
491     int i;\r
492 \r
493     if( fmt->i_group < 0 )\r
494     {\r
495         msg_Err( p_input, "invalid group number" );\r
496         return NULL;\r
497     }\r
498 \r
499     /* Search the program */\r
500     for( i = 0; i < p_sys->i_pgrm; i++ )\r
501     {\r
502         if( fmt->i_group == p_sys->pgrm[i]->i_id )\r
503         {\r
504             p_pgrm = p_sys->pgrm[i];\r
505             break;\r
506         }\r
507     }\r
508     if( p_pgrm == NULL )\r
509     {\r
510         /* Create a new one */\r
511         p_pgrm = EsOutProgramAdd( out, fmt->i_group );\r
512     }\r
513 \r
514     /* Increase ref count for program */\r
515     p_pgrm->i_es++;\r
516 \r
517     /* Set up ES */\r
518     if( fmt->i_id < 0 )\r
519         fmt->i_id = out->p_sys->i_id;\r
520     es->i_id = fmt->i_id;\r
521     es->p_pgrm = p_pgrm;\r
522     es_format_Copy( &es->fmt, fmt );\r
523     es->i_preroll_end = -1;\r
524 \r
525     switch( fmt->i_cat )\r
526     {\r
527     case AUDIO_ES:\r
528         es->i_channel = p_sys->i_audio;\r
529         break;\r
530 \r
531     case VIDEO_ES:\r
532         es->i_channel = p_sys->i_video;\r
533         break;\r
534 \r
535     case SPU_ES:\r
536         es->i_channel = p_sys->i_sub;\r
537         break;\r
538 \r
539     default:\r
540         es->i_channel = 0;\r
541         break;\r
542     }\r
543     es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */\r
544     es->psz_language_code = LanguageGetCode( fmt->psz_language );\r
545     es->p_dec = NULL;\r
546 \r
547     if( es->p_pgrm == p_sys->p_pgrm )\r
548         EsOutESVarUpdate( out, es, VLC_FALSE );\r
549 \r
550     /* Select it if needed */\r
551     EsOutSelect( out, es, VLC_FALSE );\r
552 \r
553 \r
554     TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );\r
555     p_sys->i_id++;  /* always incremented */\r
556     switch( fmt->i_cat )\r
557     {\r
558         case AUDIO_ES:\r
559             p_sys->i_audio++;\r
560             break;\r
561         case SPU_ES:\r
562             p_sys->i_sub++;\r
563             break;\r
564         case VIDEO_ES:\r
565             p_sys->i_video++;\r
566             break;\r
567     }\r
568 \r
569     EsOutAddInfo( out, es );\r
570 \r
571     return es;\r
572 }\r
573 \r
574 static void EsSelect( es_out_t *out, es_out_id_t *es )\r
575 {\r
576     es_out_sys_t   *p_sys = out->p_sys;\r
577     input_thread_t *p_input = p_sys->p_input;\r
578     vlc_value_t    val;\r
579     char           *psz_var;\r
580 \r
581     if( es->p_dec )\r
582     {\r
583         msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );\r
584         return;\r
585     }\r
586 \r
587     if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )\r
588     {\r
589         if( !var_GetBool( p_input, "video" ) ||\r
590             ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) )\r
591         {\r
592             msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",\r
593                      es->i_id );\r
594             return;\r
595         }\r
596     }\r
597     else if( es->fmt.i_cat == AUDIO_ES )\r
598     {\r
599         var_Get( p_input, "audio", &val );\r
600         if( !var_GetBool( p_input, "audio" ) ||\r
601             ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) )\r
602         {\r
603             msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",\r
604                      es->i_id );\r
605             return;\r
606         }\r
607     }\r
608 \r
609     es->i_preroll_end = -1;\r
610     es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE );\r
611     if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )\r
612         return;\r
613 \r
614     if( es->fmt.i_cat == VIDEO_ES )\r
615         psz_var = "video-es";\r
616     else if( es->fmt.i_cat == AUDIO_ES )\r
617         psz_var = "audio-es";\r
618     else if( es->fmt.i_cat == SPU_ES )\r
619         psz_var = "spu-es";\r
620     else\r
621         return;\r
622 \r
623     /* Mark it as selected */\r
624     val.i_int = es->i_id;\r
625     var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );\r
626 \r
627 \r
628     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
629 }\r
630 \r
631 static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update )\r
632 {\r
633     es_out_sys_t   *p_sys = out->p_sys;\r
634     input_thread_t *p_input = p_sys->p_input;\r
635     vlc_value_t    val;\r
636     char           *psz_var;\r
637 \r
638     if( es->p_dec == NULL )\r
639     {\r
640         msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );\r
641         return;\r
642     }\r
643 \r
644     input_DecoderDelete( es->p_dec );\r
645     es->p_dec = NULL;\r
646 \r
647     if( !b_update )\r
648         return;\r
649 \r
650     /* Update var */\r
651     if( es->p_dec == NULL )\r
652         return;\r
653     if( es->fmt.i_cat == VIDEO_ES )\r
654         psz_var = "video-es";\r
655     else if( es->fmt.i_cat == AUDIO_ES )\r
656         psz_var = "audio-es";\r
657     else if( es->fmt.i_cat == SPU_ES )\r
658         psz_var = "spu-es";\r
659     else\r
660         return;\r
661 \r
662     /* Mark it as selected */\r
663     val.i_int = -1;\r
664     var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );\r
665 \r
666     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
667 }\r
668 \r
669 /**\r
670  * Select an ES given the current mode\r
671  * XXX: you need to take a the lock before (stream.stream_lock)\r
672  *\r
673  * \param out The es_out structure\r
674  * \param es es_out_id structure\r
675  * \param b_force ...\r
676  * \return nothing\r
677  */\r
678 static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )\r
679 {\r
680     es_out_sys_t      *p_sys = out->p_sys;\r
681 \r
682     int i_cat = es->fmt.i_cat;\r
683 \r
684     if( !p_sys->b_active ||\r
685         ( !b_force && es->fmt.i_priority < 0 ) )\r
686     {\r
687         return;\r
688     }\r
689 \r
690     if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )\r
691     {\r
692         if( !es->p_dec )\r
693             EsSelect( out, es );\r
694     }\r
695     else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )\r
696     {\r
697         vlc_value_t val;\r
698         int i;\r
699         var_Get( p_sys->p_input, "programs", &val );\r
700         for ( i = 0; i < val.p_list->i_count; i++ )\r
701         {\r
702             if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )\r
703             {\r
704                 if( !es->p_dec )\r
705                     EsSelect( out, es );\r
706                 break;\r
707             }\r
708         }\r
709         var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );\r
710     }\r
711     else if( p_sys->i_mode == ES_OUT_MODE_AUTO )\r
712     {\r
713         int i_wanted  = -1;\r
714 \r
715         if( es->p_pgrm != p_sys->p_pgrm )\r
716             return;\r
717 \r
718         if( i_cat == AUDIO_ES )\r
719         {\r
720             int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,\r
721                                      es->psz_language_code );\r
722 \r
723             if( p_sys->p_es_audio &&\r
724                 p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )\r
725             {\r
726                 int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,\r
727                                          p_sys->p_es_audio->psz_language_code );\r
728 \r
729                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )\r
730                     return;\r
731                 i_wanted = es->i_channel;\r
732             }\r
733             else\r
734             {\r
735                 /* Select audio if (no audio selected yet)\r
736                  * - no audio-language\r
737                  * - no audio code for the ES\r
738                  * - audio code in the requested list */\r
739                 if( idx1 >= 0 ||\r
740                     !strcmp( es->psz_language_code, "??" ) ||\r
741                     !p_sys->ppsz_audio_language )\r
742                     i_wanted = es->i_channel;\r
743             }\r
744 \r
745             if( p_sys->i_audio_last >= 0 )\r
746                 i_wanted = p_sys->i_audio_last;\r
747         }\r
748         else if( i_cat == SPU_ES )\r
749         {\r
750             int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,\r
751                                      es->psz_language_code );\r
752 \r
753             if( p_sys->p_es_sub &&\r
754                 p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )\r
755             {\r
756                 int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,\r
757                                          p_sys->p_es_sub->psz_language_code );\r
758 \r
759                 msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",\r
760                         idx1, es->psz_language_code, idx2,\r
761                         p_sys->p_es_sub->psz_language_code );\r
762 \r
763                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )\r
764                     return;\r
765                 /* We found a SPU that matches our language request */\r
766                 i_wanted  = es->i_channel;\r
767             }\r
768             else if( idx1 >= 0 )\r
769             {\r
770                 msg_Dbg( p_sys->p_input, "idx1=%d(%s)",\r
771                         idx1, es->psz_language_code );\r
772 \r
773                 i_wanted  = es->i_channel;\r
774             }\r
775             if( p_sys->i_sub_last >= 0 )\r
776                 i_wanted  = p_sys->i_sub_last;\r
777         }\r
778         else if( i_cat == VIDEO_ES )\r
779         {\r
780             i_wanted  = es->i_channel;\r
781         }\r
782 \r
783         if( i_wanted == es->i_channel && es->p_dec == NULL )\r
784             EsSelect( out, es );\r
785     }\r
786 \r
787     /* FIXME TODO handle priority here */\r
788     if( es->p_dec )\r
789     {\r
790         if( i_cat == AUDIO_ES )\r
791         {\r
792             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&\r
793                 p_sys->p_es_audio &&\r
794                 p_sys->p_es_audio != es &&\r
795                 p_sys->p_es_audio->p_dec )\r
796             {\r
797                 EsUnselect( out, p_sys->p_es_audio, VLC_FALSE );\r
798             }\r
799             p_sys->p_es_audio = es;\r
800         }\r
801         else if( i_cat == SPU_ES )\r
802         {\r
803             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&\r
804                 p_sys->p_es_sub &&\r
805                 p_sys->p_es_sub != es &&\r
806                 p_sys->p_es_sub->p_dec )\r
807             {\r
808                 EsUnselect( out, p_sys->p_es_sub, VLC_FALSE );\r
809             }\r
810             p_sys->p_es_sub = es;\r
811         }\r
812         else if( i_cat == VIDEO_ES )\r
813         {\r
814             p_sys->p_es_video = es;\r
815         }\r
816     }\r
817 }\r
818 \r
819 /**\r
820  * Send a block for the given es_out\r
821  *\r
822  * \param out the es_out to send from\r
823  * \param es the es_out_id\r
824  * \param p_block the data block to send\r
825  */\r
826 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )\r
827 {\r
828     es_out_sys_t *p_sys = out->p_sys;\r
829     input_thread_t    *p_input = p_sys->p_input;\r
830     es_out_pgrm_t *p_pgrm = es->p_pgrm;\r
831     int64_t i_delay;\r
832 \r
833     if( es->fmt.i_cat == AUDIO_ES )\r
834         i_delay = p_sys->i_audio_delay;\r
835     else if( es->fmt.i_cat == SPU_ES )\r
836         i_delay = p_sys->i_spu_delay;\r
837     else\r
838         i_delay = 0;\r
839 \r
840     /* Mark preroll blocks */\r
841     if( es->i_preroll_end >= 0 )\r
842     {\r
843         int64_t i_date = p_block->i_pts;\r
844         if( i_date <= 0 )\r
845             i_date = p_block->i_dts;\r
846 \r
847         if( i_date < es->i_preroll_end )\r
848             p_block->i_flags |= BLOCK_FLAG_PREROLL;\r
849         else\r
850             es->i_preroll_end = -1;\r
851     }\r
852 \r
853     /* +11 -> avoid null value with non null dts/pts */\r
854     if( p_block->i_dts > 0 )\r
855     {\r
856         p_block->i_dts =\r
857             input_ClockGetTS( p_input, &p_pgrm->clock,\r
858                               ( p_block->i_dts + 11 ) * 9 / 100 ) + i_delay;\r
859     }\r
860     if( p_block->i_pts > 0 )\r
861     {\r
862         p_block->i_pts =\r
863             input_ClockGetTS( p_input, &p_pgrm->clock,\r
864                               ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay;\r
865     }\r
866     if ( es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) )\r
867     {\r
868         mtime_t current_date = mdate();\r
869         if( !p_block->i_pts\r
870                || p_block->i_pts > current_date + 10000000\r
871                || current_date > p_block->i_pts )\r
872         {\r
873             /* ETSI EN 300 472 Annex A : do not take into account the PTS\r
874              * for teletext streams. */\r
875             p_block->i_pts = current_date + 400000\r
876                                + p_input->i_pts_delay + i_delay;\r
877         }\r
878     }\r
879 \r
880     p_block->i_rate = p_input->i_rate;\r
881 \r
882     /* TODO handle mute */\r
883     if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES ||\r
884         p_input->i_rate == INPUT_RATE_DEFAULT ) )\r
885     {\r
886         input_DecoderDecode( es->p_dec, p_block );\r
887     }\r
888     else\r
889     {\r
890         block_Release( p_block );\r
891     }\r
892 \r
893     return VLC_SUCCESS;\r
894 }\r
895 \r
896 /*****************************************************************************\r
897  * EsOutDel:\r
898  *****************************************************************************/\r
899 static void EsOutDel( es_out_t *out, es_out_id_t *es )\r
900 {\r
901     es_out_sys_t *p_sys = out->p_sys;\r
902 \r
903     /* We don't try to reselect */\r
904     if( es->p_dec )\r
905         EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );\r
906 \r
907     if( es->p_pgrm == p_sys->p_pgrm )\r
908         EsOutESVarUpdate( out, es, VLC_TRUE );\r
909 \r
910     TAB_REMOVE( p_sys->i_es, p_sys->es, es );\r
911 \r
912     es->p_pgrm->i_es--;\r
913     if( es->p_pgrm->i_es == 0 )\r
914     {\r
915         msg_Warn( p_sys->p_input, "Program doesn't contain anymore ES, "\r
916                   "TODO cleaning ?" );\r
917     }\r
918 \r
919     if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;\r
920     if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;\r
921     if( p_sys->p_es_sub   == es ) p_sys->p_es_sub   = NULL;\r
922 \r
923     switch( es->fmt.i_cat )\r
924     {\r
925         case AUDIO_ES:\r
926             p_sys->i_audio--;\r
927             break;\r
928         case SPU_ES:\r
929             p_sys->i_sub--;\r
930             break;\r
931         case VIDEO_ES:\r
932             p_sys->i_video--;\r
933             break;\r
934     }\r
935 \r
936     if( es->psz_language )\r
937         free( es->psz_language );\r
938     if( es->psz_language_code )\r
939         free( es->psz_language_code );\r
940 \r
941     es_format_Clean( &es->fmt );\r
942 \r
943     free( es );\r
944 }\r
945 \r
946 /**\r
947  * Control query handler\r
948  *\r
949  * \param out the es_out to control\r
950  * \param i_query A es_out query as defined in include/ninput.h\r
951  * \param args a variable list of arguments for the query\r
952  * \return VLC_SUCCESS or an error code\r
953  */\r
954 static int EsOutControl( es_out_t *out, int i_query, va_list args )\r
955 {\r
956     es_out_sys_t *p_sys = out->p_sys;\r
957     vlc_bool_t  b, *pb;\r
958     int         i, *pi;\r
959 \r
960     es_out_id_t *es;\r
961 \r
962     switch( i_query )\r
963     {\r
964         case ES_OUT_SET_ES_STATE:\r
965             es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
966             b = (vlc_bool_t) va_arg( args, vlc_bool_t );\r
967             if( b && es->p_dec == NULL )\r
968             {\r
969                 EsSelect( out, es );\r
970                 return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;\r
971             }\r
972             else if( !b && es->p_dec )\r
973             {\r
974                 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );\r
975                 return VLC_SUCCESS;\r
976             }\r
977             return VLC_SUCCESS;\r
978 \r
979         case ES_OUT_GET_ES_STATE:\r
980             es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
981             pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );\r
982 \r
983             *pb = es->p_dec ? VLC_TRUE : VLC_FALSE;\r
984             return VLC_SUCCESS;\r
985 \r
986         case ES_OUT_SET_ACTIVE:\r
987         {\r
988             b = (vlc_bool_t) va_arg( args, vlc_bool_t );\r
989             p_sys->b_active = b;\r
990             /* Needed ? */\r
991             if( b )\r
992                 var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
993             return VLC_SUCCESS;\r
994         }\r
995 \r
996         case ES_OUT_GET_ACTIVE:\r
997             pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );\r
998             *pb = p_sys->b_active;\r
999             return VLC_SUCCESS;\r
1000 \r
1001         case ES_OUT_SET_MODE:\r
1002             i = (int) va_arg( args, int );\r
1003             if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||\r
1004                 i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )\r
1005             {\r
1006                 p_sys->i_mode = i;\r
1007 \r
1008                 /* Reapply policy mode */\r
1009                 for( i = 0; i < p_sys->i_es; i++ )\r
1010                 {\r
1011                     if( p_sys->es[i]->p_dec )\r
1012                     {\r
1013                         EsUnselect( out, p_sys->es[i],\r
1014                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
1015                     }\r
1016                 }\r
1017                 for( i = 0; i < p_sys->i_es; i++ )\r
1018                 {\r
1019                     EsOutSelect( out, p_sys->es[i], VLC_FALSE );\r
1020                 }\r
1021                 return VLC_SUCCESS;\r
1022             }\r
1023             return VLC_EGENERIC;\r
1024 \r
1025         case ES_OUT_GET_MODE:\r
1026             pi = (int*) va_arg( args, int* );\r
1027             *pi = p_sys->i_mode;\r
1028             return VLC_SUCCESS;\r
1029 \r
1030         case ES_OUT_SET_ES:\r
1031             es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
1032             /* Special case NULL, NULL+i_cat */\r
1033             if( es == NULL )\r
1034             {\r
1035                 for( i = 0; i < p_sys->i_es; i++ )\r
1036                 {\r
1037                     if( p_sys->es[i]->p_dec )\r
1038                         EsUnselect( out, p_sys->es[i],\r
1039                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
1040                 }\r
1041             }\r
1042             else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )\r
1043             {\r
1044                 for( i = 0; i < p_sys->i_es; i++ )\r
1045                 {\r
1046                     if( p_sys->es[i]->p_dec &&\r
1047                         p_sys->es[i]->fmt.i_cat == AUDIO_ES )\r
1048                         EsUnselect( out, p_sys->es[i],\r
1049                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
1050                 }\r
1051             }\r
1052             else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )\r
1053             {\r
1054                 for( i = 0; i < p_sys->i_es; i++ )\r
1055                 {\r
1056                     if( p_sys->es[i]->p_dec &&\r
1057                         p_sys->es[i]->fmt.i_cat == VIDEO_ES )\r
1058                         EsUnselect( out, p_sys->es[i],\r
1059                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
1060                 }\r
1061             }\r
1062             else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )\r
1063             {\r
1064                 for( i = 0; i < p_sys->i_es; i++ )\r
1065                 {\r
1066                     if( p_sys->es[i]->p_dec &&\r
1067                         p_sys->es[i]->fmt.i_cat == SPU_ES )\r
1068                         EsUnselect( out, p_sys->es[i],\r
1069                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
1070                 }\r
1071             }\r
1072             else\r
1073             {\r
1074                 for( i = 0; i < p_sys->i_es; i++ )\r
1075                 {\r
1076                     if( es == p_sys->es[i] )\r
1077                     {\r
1078                         EsOutSelect( out, es, VLC_TRUE );\r
1079                         break;\r
1080                     }\r
1081                 }\r
1082             }\r
1083             return VLC_SUCCESS;\r
1084 \r
1085         case ES_OUT_SET_PCR:\r
1086         case ES_OUT_SET_GROUP_PCR:\r
1087         {\r
1088             es_out_pgrm_t *p_pgrm = NULL;\r
1089             int            i_group = 0;\r
1090             int64_t        i_pcr;\r
1091 \r
1092             if( i_query == ES_OUT_SET_PCR )\r
1093             {\r
1094                 p_pgrm = p_sys->p_pgrm;\r
1095             }\r
1096             else\r
1097             {\r
1098                 int i;\r
1099                 i_group = (int)va_arg( args, int );\r
1100                 for( i = 0; i < p_sys->i_pgrm; i++ )\r
1101                 {\r
1102                     if( p_sys->pgrm[i]->i_id == i_group )\r
1103                     {\r
1104                         p_pgrm = p_sys->pgrm[i];\r
1105                         break;\r
1106                     }\r
1107                 }\r
1108             }\r
1109             if( p_pgrm == NULL )\r
1110                 p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */\r
1111 \r
1112             i_pcr = (int64_t)va_arg( args, int64_t );\r
1113             /* search program */\r
1114             /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */\r
1115             input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock,\r
1116                                (i_pcr + 11 ) * 9 / 100);\r
1117             return VLC_SUCCESS;\r
1118         }\r
1119 \r
1120         case ES_OUT_RESET_PCR:\r
1121             for( i = 0; i < p_sys->i_pgrm; i++ )\r
1122             {\r
1123                 p_sys->pgrm[i]->clock.i_synchro_state =  SYNCHRO_REINIT;\r
1124                 p_sys->pgrm[i]->clock.last_pts = 0;\r
1125             }\r
1126             return VLC_SUCCESS;\r
1127 \r
1128         case ES_OUT_GET_TS:\r
1129             if( p_sys->p_pgrm )\r
1130             {\r
1131                 int64_t i_ts = (int64_t)va_arg( args, int64_t );\r
1132                 int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );\r
1133                 *pi_ts = input_ClockGetTS( p_sys->p_input,\r
1134                                            &p_sys->p_pgrm->clock,\r
1135                                            ( i_ts + 11 ) * 9 / 100 );\r
1136                 return VLC_SUCCESS;\r
1137             }\r
1138             return VLC_EGENERIC;\r
1139 \r
1140         case ES_OUT_GET_GROUP:\r
1141             pi = (int*) va_arg( args, int* );\r
1142             if( p_sys->p_pgrm )\r
1143                 *pi = p_sys->p_pgrm->i_id;\r
1144             else\r
1145                 *pi = -1;    /* FIXME */\r
1146             return VLC_SUCCESS;\r
1147 \r
1148         case ES_OUT_SET_GROUP:\r
1149         {\r
1150             int j;\r
1151             i = (int) va_arg( args, int );\r
1152             for( j = 0; j < p_sys->i_pgrm; j++ )\r
1153             {\r
1154                 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];\r
1155                 if( p_pgrm->i_id == i )\r
1156                 {\r
1157                     EsOutProgramSelect( out, p_pgrm );\r
1158                     return VLC_SUCCESS;\r
1159                 }\r
1160             }\r
1161             return VLC_EGENERIC;\r
1162         }\r
1163 \r
1164         case ES_OUT_SET_FMT:\r
1165         {\r
1166             /* This ain't pretty but is need by some demuxers (eg. Ogg )\r
1167              * to update the p_extra data */\r
1168             es_format_t *p_fmt;\r
1169             es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
1170             p_fmt = (es_format_t*) va_arg( args, es_format_t * );\r
1171             if( es == NULL ) return VLC_EGENERIC;\r
1172 \r
1173             if( p_fmt->i_extra )\r
1174             {\r
1175                 es->fmt.i_extra = p_fmt->i_extra;\r
1176                 es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );\r
1177                 memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );\r
1178 \r
1179                 if( !es->p_dec ) return VLC_SUCCESS;\r
1180 \r
1181                 es->p_dec->fmt_in.i_extra = p_fmt->i_extra;\r
1182                 es->p_dec->fmt_in.p_extra =\r
1183                     realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );\r
1184                 memcpy( es->p_dec->fmt_in.p_extra,\r
1185                         p_fmt->p_extra, p_fmt->i_extra );\r
1186             }\r
1187 \r
1188             return VLC_SUCCESS;\r
1189         }\r
1190 \r
1191         case ES_OUT_SET_NEXT_DISPLAY_TIME:\r
1192         {\r
1193             int64_t i_date;\r
1194 \r
1195             es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
1196             i_date = (int64_t)va_arg( args, int64_t );\r
1197 \r
1198             if( !es || !es->p_dec )\r
1199                 return VLC_EGENERIC;\r
1200 \r
1201             es->i_preroll_end = i_date;\r
1202             input_DecoderPreroll( es->p_dec, i_date );\r
1203 \r
1204             return VLC_SUCCESS;\r
1205         }\r
1206 \r
1207         default:\r
1208             msg_Err( p_sys->p_input, "unknown query in es_out_Control" );\r
1209             return VLC_EGENERIC;\r
1210     }\r
1211 }\r
1212 \r
1213 /****************************************************************************\r
1214  * LanguageGetName: try to expend iso639 into plain name\r
1215  ****************************************************************************/\r
1216 static char *LanguageGetName( const char *psz_code )\r
1217 {\r
1218     const iso639_lang_t *pl;\r
1219 \r
1220     if( psz_code == NULL )\r
1221     {\r
1222         return strdup( "" );\r
1223     }\r
1224 \r
1225     if( strlen( psz_code ) == 2 )\r
1226     {\r
1227         pl = GetLang_1( psz_code );\r
1228     }\r
1229     else if( strlen( psz_code ) == 3 )\r
1230     {\r
1231         pl = GetLang_2B( psz_code );\r
1232         if( !strcmp( pl->psz_iso639_1, "??" ) )\r
1233         {\r
1234             pl = GetLang_2T( psz_code );\r
1235         }\r
1236     }\r
1237     else\r
1238     {\r
1239         return strdup( psz_code );\r
1240     }\r
1241 \r
1242     if( !strcmp( pl->psz_iso639_1, "??" ) )\r
1243     {\r
1244        return strdup( psz_code );\r
1245     }\r
1246     else\r
1247     {\r
1248         if( *pl->psz_native_name )\r
1249         {\r
1250             return strdup( pl->psz_native_name );\r
1251         }\r
1252         return strdup( pl->psz_eng_name );\r
1253     }\r
1254 }\r
1255 \r
1256 /* Get a 2 char code */\r
1257 static char *LanguageGetCode( const char *psz_lang )\r
1258 {\r
1259     const iso639_lang_t *pl;\r
1260 \r
1261     if( psz_lang == NULL || *psz_lang == '\0' )\r
1262         return strdup("??");\r
1263 \r
1264     for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )\r
1265     {\r
1266         if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||\r
1267             !strcasecmp( pl->psz_native_name, psz_lang ) ||\r
1268             !strcasecmp( pl->psz_iso639_1, psz_lang ) ||\r
1269             !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||\r
1270             !strcasecmp( pl->psz_iso639_2B, psz_lang ) )\r
1271             break;\r
1272     }\r
1273 \r
1274     if( pl->psz_iso639_1 != NULL )\r
1275         return strdup( pl->psz_iso639_1 );\r
1276 \r
1277     return strdup("??");\r
1278 }\r
1279 \r
1280 static char **LanguageSplit( const char *psz_langs )\r
1281 {\r
1282     char *psz_dup;\r
1283     char *psz_parser;\r
1284     char **ppsz = NULL;\r
1285     int i_psz = 0;\r
1286 \r
1287     if( psz_langs == NULL )\r
1288         return NULL;\r
1289 \r
1290     psz_parser = psz_dup = strdup(psz_langs);\r
1291 \r
1292     while( psz_parser && *psz_parser )\r
1293     {\r
1294         char *psz;\r
1295         char *psz_code;\r
1296 \r
1297         psz = strchr(psz_parser, ',' );\r
1298         if( psz )\r
1299         {\r
1300             *psz++ = '\0';\r
1301         }\r
1302 \r
1303         psz_code = LanguageGetCode( psz_parser );\r
1304         if( strcmp( psz_code, "??" ) )\r
1305         {\r
1306             TAB_APPEND( i_psz, ppsz, psz_code );\r
1307         }\r
1308 \r
1309         psz_parser = psz;\r
1310     }\r
1311 \r
1312     if( i_psz )\r
1313     {\r
1314         TAB_APPEND( i_psz, ppsz, NULL );\r
1315     }\r
1316 \r
1317     return ppsz;\r
1318 }\r
1319 \r
1320 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )\r
1321 {\r
1322     int i;\r
1323 \r
1324     if( !ppsz_langs || !psz_lang )\r
1325         return -1;\r
1326 \r
1327     for( i = 0; ppsz_langs[i]; i++ )\r
1328         if( !strcasecmp( ppsz_langs[i], psz_lang ) )\r
1329             return i;\r
1330 \r
1331     return -1;\r
1332 }\r
1333 \r
1334 /****************************************************************************\r
1335  * EsOutAddInfo:\r
1336  * - add meta info to the playlist item\r
1337  ****************************************************************************/\r
1338 static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )\r
1339 {\r
1340     es_out_sys_t   *p_sys = out->p_sys;\r
1341     input_thread_t *p_input = p_sys->p_input;\r
1342     es_format_t    *fmt = &es->fmt;\r
1343     char           *psz_cat;\r
1344 \r
1345     /* Add stream info */\r
1346     asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 );\r
1347 \r
1348     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),\r
1349                    "%.4s", (char*)&fmt->i_codec );\r
1350 \r
1351     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),\r
1352                    "%s", es->psz_language );\r
1353 \r
1354     /* Add information */\r
1355     switch( fmt->i_cat )\r
1356     {\r
1357     case AUDIO_ES:\r
1358         input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
1359                        _("Type"), _("Audio") );\r
1360 \r
1361         if( fmt->audio.i_channels > 0 )\r
1362             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),\r
1363                            "%d", fmt->audio.i_channels );\r
1364 \r
1365         if( fmt->audio.i_rate > 0 )\r
1366             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),\r
1367                            _("%d Hz"), fmt->audio.i_rate );\r
1368 \r
1369         if( fmt->audio.i_bitspersample > 0 )\r
1370             input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
1371                            _("Bits per sample"), "%d",\r
1372                            fmt->audio.i_bitspersample );\r
1373 \r
1374         if( fmt->i_bitrate > 0 )\r
1375             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),\r
1376                            _("%d kb/s"), fmt->i_bitrate / 1000 );\r
1377         break;\r
1378 \r
1379     case VIDEO_ES:\r
1380         input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
1381                        _("Type"), _("Video") );\r
1382 \r
1383         if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )\r
1384             input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
1385                            _("Resolution"), "%dx%d",\r
1386                            fmt->video.i_width, fmt->video.i_height );\r
1387 \r
1388         if( fmt->video.i_visible_width > 0 &&\r
1389             fmt->video.i_visible_height > 0 )\r
1390             input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
1391                            _("Display resolution"), "%dx%d",\r
1392                            fmt->video.i_visible_width,\r
1393                            fmt->video.i_visible_height);\r
1394         break;\r
1395 \r
1396     case SPU_ES:\r
1397         input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
1398                        _("Type"), _("Subtitle") );\r
1399         break;\r
1400 \r
1401     default:\r
1402         break;\r
1403     }\r
1404 \r
1405     free( psz_cat );\r
1406 }\r