Commit 236ca7ae authored by Laurent Aimar's avatar Laurent Aimar

* all: added a --audio-language and --spu-language to select tracks based

 on language code.
 Both options accepts a list (comma separated) of country code(2/3 letters)
or name (english/native).
 For demuxers that doesn't provide language info, they are ignored.

 Support for dvdnav isn't complete (only first language is tried) and untested.

 Ex: --spu-language en,fr will try to select english track and if none
 then french one (and if none, no subtitle).
 (you can also use --spu-language english,french or english,francais)
 
parent ed0b82ee
...@@ -45,6 +45,10 @@ ...@@ -45,6 +45,10 @@
#include "vlc_keys.h" #include "vlc_keys.h"
#include "iso_lang.h" #include "iso_lang.h"
/* FIXME we should find a better way than including that */
#include "../../src/misc/iso-639_def.h"
#include <dvdnav/dvdnav.h> #include <dvdnav/dvdnav.h>
#include "../demux/ps.h" #include "../demux/ps.h"
...@@ -65,6 +69,8 @@ ...@@ -65,6 +69,8 @@
"Allows you to start the DVD directly in the main menu. This "\ "Allows you to start the DVD directly in the main menu. This "\
"will try to skip all the useless warnings introductions." ) "will try to skip all the useless warnings introductions." )
#define LANGUAGE_DEFAULT ("en")
static int Open ( vlc_object_t * ); static int Open ( vlc_object_t * );
static void Close( vlc_object_t * ); static void Close( vlc_object_t * );
...@@ -145,6 +151,8 @@ static void ButtonUpdate( demux_t * ); ...@@ -145,6 +151,8 @@ static void ButtonUpdate( demux_t * );
static void ESNew( demux_t *, int ); static void ESNew( demux_t *, int );
static int ProbeDVD( demux_t *, char * ); static int ProbeDVD( demux_t *, char * );
static char *DemuxGetLanguageCode( demux_t *p_demux, char *psz_var );
/***************************************************************************** /*****************************************************************************
* DemuxOpen: * DemuxOpen:
*****************************************************************************/ *****************************************************************************/
...@@ -155,6 +163,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -155,6 +163,7 @@ static int Open( vlc_object_t *p_this )
dvdnav_t *p_dvdnav; dvdnav_t *p_dvdnav;
int i_angle; int i_angle;
char *psz_name; char *psz_name;
char *psz_code;
vlc_value_t val; vlc_value_t val;
if( !p_demux->psz_path || !*p_demux->psz_path ) if( !p_demux->psz_path || !*p_demux->psz_path )
...@@ -233,13 +242,40 @@ static int Open( vlc_object_t *p_this ) ...@@ -233,13 +242,40 @@ static int Open( vlc_object_t *p_this )
msg_Warn( p_demux, "cannot set PGC positioning flag" ); msg_Warn( p_demux, "cannot set PGC positioning flag" );
} }
if( dvdnav_menu_language_select (p_sys->dvdnav,"en") != DVDNAV_STATUS_OK || /* Set menu language ("en")
dvdnav_audio_language_select(p_sys->dvdnav,"en") != DVDNAV_STATUS_OK || * XXX: maybe it would be better to set it like audio/spu or to create a --menu-language option */
dvdnav_spu_language_select (p_sys->dvdnav,"en") != DVDNAV_STATUS_OK ) if( dvdnav_menu_language_select( p_sys->dvdnav,LANGUAGE_DEFAULT ) != DVDNAV_STATUS_OK )
{
msg_Warn( p_demux, "something failed while setting menu '%s' language (%s)",
LANGUAGE_DEFAULT,
dvdnav_err_to_string( p_sys->dvdnav ) );
}
/* Set audio language */
psz_code = DemuxGetLanguageCode( p_demux, "audio-language" );
if( dvdnav_audio_language_select(p_sys->dvdnav, psz_code ) != DVDNAV_STATUS_OK )
{
msg_Warn( p_demux, "something failed while setting audio '%s' language (%s)",
psz_code,
dvdnav_err_to_string( p_sys->dvdnav ) );
/* We try to fall back to 'en' */
if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
dvdnav_audio_language_select(p_sys->dvdnav, LANGUAGE_DEFAULT );
}
free( psz_code );
/* Set spu language */
psz_code = DemuxGetLanguageCode( p_demux, "spu-language" );
if( dvdnav_spu_language_select( p_sys->dvdnav,psz_code ) != DVDNAV_STATUS_OK )
{ {
msg_Warn( p_demux, "something failed while setting en language (%s)", msg_Warn( p_demux, "something failed while setting spu '%s' language (%s)",
psz_code,
dvdnav_err_to_string( p_sys->dvdnav ) ); dvdnav_err_to_string( p_sys->dvdnav ) );
/* We try to fall back to 'en' */
if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
dvdnav_spu_language_select(p_sys->dvdnav, LANGUAGE_DEFAULT );
} }
free( psz_code );
DemuxTitles( p_demux ); DemuxTitles( p_demux );
...@@ -732,6 +768,38 @@ static int Demux( demux_t *p_demux ) ...@@ -732,6 +768,38 @@ static int Demux( demux_t *p_demux )
return 1; return 1;
} }
/* Get a 2 char code
* FIXME: partiallyy duplicated from src/input/es_out.c
*/
static char *DemuxGetLanguageCode( demux_t *p_demux, char *psz_var )
{
const iso639_lang_t *pl;
char *psz_lang;
char *p;
psz_lang = var_CreateGetString( p_demux, psz_var );
/* XXX: we will use only the first value (and ignore other ones in case of a list) */
if( ( p = strchr( psz_lang, "," ) ) )
*p = '\0';
for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
{
if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
!strcasecmp( pl->psz_native_name, psz_lang ) ||
!strcasecmp( pl->psz_iso639_1, psz_lang ) ||
!strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
!strcasecmp( pl->psz_iso639_2B, psz_lang ) )
break;
}
free( psz_lang );
if( pl->psz_iso639_1 != NULL )
return strdup( pl->psz_iso639_1 );
return strdup(LANGUAGE_DEFAULT);
}
static void DemuxTitles( demux_t *p_demux ) static void DemuxTitles( demux_t *p_demux )
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
......
/***************************************************************************** /*****************************************************************************
* es_out.c: Es Out handler for input. * es_out.c: Es Out handler for input.
***************************************************************************** *****************************************************************************
...@@ -35,6 +34,8 @@ ...@@ -35,6 +34,8 @@
#include "vlc_playlist.h" #include "vlc_playlist.h"
#include "iso_lang.h" #include "iso_lang.h"
/* FIXME we should find a better way than including that */
#include "../misc/iso-639_def.h"
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
...@@ -64,6 +65,7 @@ struct es_out_id_t ...@@ -64,6 +65,7 @@ struct es_out_id_t
int i_channel; int i_channel;
es_format_t fmt; es_format_t fmt;
char *psz_language; char *psz_language;
char *psz_language_code;
decoder_t *p_dec; decoder_t *p_dec;
}; };
...@@ -94,6 +96,8 @@ struct es_out_sys_t ...@@ -94,6 +96,8 @@ struct es_out_sys_t
/* es to select */ /* es to select */
int i_audio_last; int i_audio_last;
int i_sub_last; int i_sub_last;
char **ppsz_audio_language;
char **ppsz_sub_language;
/* current main es */ /* current main es */
es_out_id_t *p_es_audio; es_out_id_t *p_es_audio;
...@@ -116,6 +120,9 @@ static void EsOutAddInfo( es_out_t *, es_out_id_t *es ); ...@@ -116,6 +120,9 @@ static void EsOutAddInfo( es_out_t *, es_out_id_t *es );
static void EsSelect( es_out_t *out, es_out_id_t *es ); static void EsSelect( es_out_t *out, es_out_id_t *es );
static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update ); static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );
static char *LanguageGetName( const char *psz_code ); static char *LanguageGetName( const char *psz_code );
static char *LanguageGetCode( const char *psz_lang );
static char **LanguageSplit( const char *psz_langs );
static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
/***************************************************************************** /*****************************************************************************
* input_EsOutNew: * input_EsOutNew:
...@@ -125,6 +132,7 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) ...@@ -125,6 +132,7 @@ es_out_t *input_EsOutNew( input_thread_t *p_input )
es_out_t *out = malloc( sizeof( es_out_t ) ); es_out_t *out = malloc( sizeof( es_out_t ) );
es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) ); es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
vlc_value_t val; vlc_value_t val;
int i;
out->pf_add = EsOutAdd; out->pf_add = EsOutAdd;
out->pf_send = EsOutSend; out->pf_send = EsOutSend;
...@@ -150,12 +158,32 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) ...@@ -150,12 +158,32 @@ es_out_t *input_EsOutNew( input_thread_t *p_input )
p_sys->i_video = 0; p_sys->i_video = 0;
p_sys->i_sub = 0; p_sys->i_sub = 0;
/* */
var_Get( p_input, "audio-channel", &val ); var_Get( p_input, "audio-channel", &val );
p_sys->i_audio_last = val.i_int; p_sys->i_audio_last = val.i_int;
var_Get( p_input, "spu-channel", &val ); var_Get( p_input, "spu-channel", &val );
p_sys->i_sub_last = val.i_int; p_sys->i_sub_last = val.i_int;
var_Get( p_input, "audio-language", &val );
p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
if( p_sys->ppsz_audio_language )
{
for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
msg_Dbg( p_input, "Select audio in language[%d] %s",
i, p_sys->ppsz_audio_language[i] );
}
var_Get( p_input, "spu-language", &val );
p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
if( p_sys->ppsz_sub_language )
{
for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
msg_Dbg( p_input, "Select subtitle in language[%d] %s",
i, p_sys->ppsz_sub_language[i] );
}
/* */
p_sys->p_es_audio = NULL; p_sys->p_es_audio = NULL;
p_sys->p_es_video = NULL; p_sys->p_es_video = NULL;
p_sys->p_es_sub = NULL; p_sys->p_es_sub = NULL;
...@@ -182,10 +210,25 @@ void input_EsOutDelete( es_out_t *out ) ...@@ -182,10 +210,25 @@ void input_EsOutDelete( es_out_t *out )
} }
if( p_sys->es[i]->psz_language ) if( p_sys->es[i]->psz_language )
free( p_sys->es[i]->psz_language ); free( p_sys->es[i]->psz_language );
if( p_sys->es[i]->psz_language_code )
free( p_sys->es[i]->psz_language_code );
es_format_Clean( &p_sys->es[i]->fmt ); es_format_Clean( &p_sys->es[i]->fmt );
free( p_sys->es[i] ); free( p_sys->es[i] );
} }
if( p_sys->ppsz_audio_language )
{
for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
free( p_sys->ppsz_audio_language[i] );
free( p_sys->ppsz_audio_language );
}
if( p_sys->ppsz_sub_language )
{
for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
free( p_sys->ppsz_sub_language[i] );
free( p_sys->ppsz_sub_language );
}
if( p_sys->es ) if( p_sys->es )
free( p_sys->es ); free( p_sys->es );
...@@ -305,11 +348,11 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, ...@@ -305,11 +348,11 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
if( es->fmt.psz_description && *es->fmt.psz_description ) if( es->fmt.psz_description && *es->fmt.psz_description )
{ {
if( es->psz_language && *es->psz_language ) if( es->psz_language && *es->psz_language )
{ {
text.psz_string = malloc( strlen( es->fmt.psz_description) + strlen( es->psz_language ) + 10 ); text.psz_string = malloc( strlen( es->fmt.psz_description) + strlen( es->psz_language ) + 10 );
sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, es->psz_language ); sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, es->psz_language );
} }
else text.psz_string = strdup( es->fmt.psz_description ); else text.psz_string = strdup( es->fmt.psz_description );
} }
else else
{ {
...@@ -493,6 +536,7 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) ...@@ -493,6 +536,7 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
break; break;
} }
es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */ es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */
es->psz_language_code = LanguageGetCode( fmt->psz_language );
es->p_dec = NULL; es->p_dec = NULL;
if( es->p_pgrm == p_sys->p_pgrm ) if( es->p_pgrm == p_sys->p_pgrm )
...@@ -667,23 +711,63 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) ...@@ -667,23 +711,63 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )
if( i_cat == AUDIO_ES ) if( i_cat == AUDIO_ES )
{ {
int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,
es->psz_language_code );
if( p_sys->p_es_audio && if( p_sys->p_es_audio &&
p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority ) p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
{ {
return; int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,
p_sys->p_es_audio->psz_language_code );
if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
return;
i_wanted = es->i_channel;
} }
i_wanted = p_sys->i_audio_last >= 0 ? else
p_sys->i_audio_last : es->i_channel; {
/* Select audio if (no audio selected yet)
* - no audio-language
* - no audio code for the ES
* - audio code in the requested list */
if( idx1 >= 0 ||
!strcmp( es->psz_language_code, "??" ) ||
!p_sys->ppsz_audio_language )
i_wanted = es->i_channel;
}
if( p_sys->i_audio_last >= 0 )
i_wanted = p_sys->i_audio_last;
} }
else if( i_cat == SPU_ES ) else if( i_cat == SPU_ES )
{ {
int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,
es->psz_language_code );
if( p_sys->p_es_sub && if( p_sys->p_es_sub &&
p_sys->p_es_sub->fmt.i_priority >= p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )
es->fmt.i_priority ) {
int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,
p_sys->p_es_sub->psz_language_code );
msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",
idx1, es->psz_language_code, idx2,
p_sys->p_es_sub->psz_language_code );
if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
return;
/* We found a SPU that matches our language request */
i_wanted = es->i_channel;
}
else if( idx1 >= 0 )
{ {
return; msg_Dbg( p_sys->p_input, "idx1=%d(%s)",
idx1, es->psz_language_code );
i_wanted = es->i_channel;
} }
i_wanted = p_sys->i_sub_last; if( p_sys->i_sub_last >= 0 )
i_wanted = p_sys->i_sub_last;
} }
else if( i_cat == VIDEO_ES ) else if( i_cat == VIDEO_ES )
{ {
...@@ -832,6 +916,8 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) ...@@ -832,6 +916,8 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
if( es->psz_language ) if( es->psz_language )
free( es->psz_language ); free( es->psz_language );
if( es->psz_language_code )
free( es->psz_language_code );
es_format_Clean( &es->fmt ); es_format_Clean( &es->fmt );
...@@ -1132,6 +1218,84 @@ static char *LanguageGetName( const char *psz_code ) ...@@ -1132,6 +1218,84 @@ static char *LanguageGetName( const char *psz_code )
} }
} }
/* Get a 2 char code */
static char *LanguageGetCode( const char *psz_lang )
{
const iso639_lang_t *pl;
if( psz_lang == NULL || *psz_lang == '\0' )
return strdup("??");
for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
{
if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
!strcasecmp( pl->psz_native_name, psz_lang ) ||
!strcasecmp( pl->psz_iso639_1, psz_lang ) ||
!strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
!strcasecmp( pl->psz_iso639_2B, psz_lang ) )
break;
}
if( pl->psz_iso639_1 != NULL )
return strdup( pl->psz_iso639_1 );
return strdup("??");
}
static char **LanguageSplit( const char *psz_langs )
{
char *psz_dup;
char *psz_parser;
char **ppsz = NULL;
int i_psz = 0;
if( psz_langs == NULL )
return NULL;
psz_parser = psz_dup = strdup(psz_langs);
while( psz_parser && *psz_parser )
{
char *psz;
char *psz_code;
psz = strchr(psz_parser, ',' );
if( psz )
{
*psz++ = '\0';
}
psz_code = LanguageGetCode( psz_parser );
if( strcmp( psz_code, "??" ) )
{
TAB_APPEND( i_psz, ppsz, psz_code );
}
psz_parser = psz;
}
if( i_psz )
{
TAB_APPEND( i_psz, ppsz, NULL );
}
return ppsz;
}
static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )
{
int i;
if( !ppsz_langs || !psz_lang )
return -1;
for( i = 0; ppsz_langs[i]; i++ )
if( !strcasecmp( ppsz_langs[i], psz_lang ) )
return i;
return -1;
}
/**************************************************************************** /****************************************************************************
* EsOutAddInfo: * EsOutAddInfo:
* - add meta info to the playlist item * - add meta info to the playlist item
......
...@@ -400,6 +400,9 @@ void input_ConfigVarInit ( input_thread_t *p_input ) ...@@ -400,6 +400,9 @@ void input_ConfigVarInit ( input_thread_t *p_input )
var_Create( p_input, "audio-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "audio-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
var_Create( p_input, "spu-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "spu-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
var_Create( p_input, "audio-language", VLC_VAR_STRING|VLC_VAR_DOINHERIT );
var_Create( p_input, "spu-language", VLC_VAR_STRING|VLC_VAR_DOINHERIT );
var_Create( p_input, "sub-file", VLC_VAR_FILE | VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-file", VLC_VAR_FILE | VLC_VAR_DOINHERIT );
var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL | var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL |
VLC_VAR_DOINHERIT ); VLC_VAR_DOINHERIT );
......
...@@ -327,6 +327,16 @@ static char *ppsz_align_descriptions[] = ...@@ -327,6 +327,16 @@ static char *ppsz_align_descriptions[] =
"(from 0 to n).") "(from 0 to n).")
#define INPUT_SUB_TEXT N_("Choose subtitles track") #define INPUT_SUB_TEXT N_("Choose subtitles track")
#define INPUT_CHAN_LANG_TEXT N_("Choose audio language")
#define INPUT_CHAN_LANG_LONGTEXT N_( \
"Give the language of the audio channel you want to use " \
"(comma separted, two or tree letter country code).")
#define INPUT_SUB_LANG_TEXT N_("Choose subtitle track")
#define INPUT_SUB_LANG_LONGTEXT N_( \
"Give the language of the subtitle channel you want to use " \
"(comma separted, two or tree letter country code).")
#define INPUT_SUB_LONGTEXT N_( \ #define INPUT_SUB_LONGTEXT N_( \
"Give the stream number of the subtitle channel you want to use " \ "Give the stream number of the subtitle channel you want to use " \
"(from 0 to n).") "(from 0 to n).")
...@@ -979,6 +989,11 @@ vlc_module_begin(); ...@@ -979,6 +989,11 @@ vlc_module_begin();
INPUT_CHAN_TEXT, INPUT_CHAN_LONGTEXT, VLC_FALSE ); INPUT_CHAN_TEXT, INPUT_CHAN_LONGTEXT, VLC_FALSE );
add_integer( "spu-channel", -1, NULL, add_integer( "spu-channel", -1, NULL,
INPUT_SUB_TEXT, INPUT_SUB_LONGTEXT, VLC_FALSE ); INPUT_SUB_TEXT, INPUT_SUB_LONGTEXT, VLC_FALSE );
add_string( "audio-language", "", NULL,
INPUT_CHAN_LANG_TEXT, INPUT_CHAN_LANG_LONGTEXT, VLC_FALSE );
add_string( "spu-language", "", NULL,
INPUT_SUB_LANG_TEXT, INPUT_SUB_LANG_LONGTEXT, VLC_FALSE );
set_section( N_( "Playback control" ) , NULL); set_section( N_( "Playback control" ) , NULL);
add_integer( "input-repeat", 0, NULL, add_integer( "input-repeat", 0, NULL,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment