diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index cfc7563bcd..1a2e94bbf4 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -1896,6 +1896,12 @@ Specify TV teletext display format (default: 0): .REss .IPs tpage=<100-899> Specify initial TV teletext page number (default: 100). +.IPs tlang=<\-1\-127> +Specify default teletext language code (default: 0). +Given value will be used as primary language until 28 packet received. +Useful when teletext system uses non-latin charset, but language codes +are not transmitted via 28 teletext packets for some reason. +To see list of supported language codes set this option to \-1. .RE . .TP diff --git a/cfg-common.h b/cfg-common.h index 7b5ff29cb8..cfaa4ee66f 100644 --- a/cfg-common.h +++ b/cfg-common.h @@ -464,6 +464,7 @@ m_option_t tvopts_conf[]={ {"tdevice", &stream_tv_defaults.tdevice, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"tpage", &stream_tv_defaults.tpage, CONF_TYPE_INT, CONF_RANGE, 100, 899, NULL}, {"tformat", &stream_tv_defaults.tformat, CONF_TYPE_INT, CONF_RANGE, 0, 3, NULL}, + {"tlang", &stream_tv_defaults.tlang, CONF_TYPE_INT, CONF_RANGE, -1, 0x7f, NULL}, #endif {"audioid", &stream_tv_defaults.audio_id, CONF_TYPE_INT, CONF_RANGE, 0, 9, NULL}, {NULL, NULL, 0, 0, 0, 0, NULL} diff --git a/help/help_mp-en.h b/help/help_mp-en.h index 5f70a615e4..a764263a83 100644 --- a/help/help_mp-en.h +++ b/help/help_mp-en.h @@ -2097,3 +2097,5 @@ static char help_text[]= #define MSGTR_TV_Bt848ErrorSettingWidth "tvi_bsdbt848: Error setting picture width. Error: %s\n" #define MSGTR_TV_Bt848ErrorSettingHeight "tvi_bsdbt848: Error setting picture height. Error: %s\n" #define MSGTR_TV_Bt848UnableToStopCapture "tvi_bsdbt848: Unable to stop capture. Error: %s\n" +#define MSGTR_TV_TTSupportedLanguages "Supported Teletext languages:\n" +#define MSGTR_TV_TTSelectedLanguage "Selected default teletext language: %s\n" diff --git a/stream/stream_tv.c b/stream/stream_tv.c index ccad96eb20..6d8967c069 100644 --- a/stream/stream_tv.c +++ b/stream/stream_tv.c @@ -75,6 +75,7 @@ tv_param_t stream_tv_defaults = { NULL, //tdevice 0, //tformat 100, //tpage + 0, //tlang 0, //scan_autostart 50, //scan_threshold diff --git a/stream/tv.h b/stream/tv.h index 98f9417f2e..d35b78e9bb 100644 --- a/stream/tv.h +++ b/stream/tv.h @@ -50,6 +50,7 @@ typedef struct tv_param_s { char *tdevice; ///< teletext device int tformat; ///< teletext display format int tpage; ///< start teletext page + int tlang; ///< primary language code int scan; int scan_threshold; @@ -276,7 +277,7 @@ typedef struct tt_char_s{ unsigned char bg; ///< background color unsigned char gfx; ///< 0-no gfx, 1-solid gfx, 2-separated gfx unsigned char ctl; ///< control character - unsigned char lng; ///< lang: 0-lating,1-national + unsigned char lng; ///< lang: 0-secondary language,1-primary language unsigned char raw; ///< raw character (as received from device) } tt_char; @@ -288,7 +289,8 @@ typedef struct tt_link_s{ typedef struct tt_page_s{ int pagenum; ///< page number int subpagenum; ///< subpage number - unsigned char lang; ///< language code + unsigned char primary_lang; ///< primary language code + unsigned char secondary_lang; ///< secondary language code unsigned char active; ///< page is complete and ready for rendering unsigned char flags; ///< page flags, not used unsigned char raw[VBI_ROWS*VBI_COLUMNS]; ///< page data diff --git a/stream/tvi_vbi.c b/stream/tvi_vbi.c index 50c5943174..f5513528d8 100644 --- a/stream/tvi_vbi.c +++ b/stream/tvi_vbi.c @@ -103,7 +103,6 @@ typedef struct mag_s{ tt_page* pt; int order; - int lang; } mag_t; typedef struct { @@ -116,6 +115,8 @@ typedef struct { teletext_format tformat; ///< see teletext_format enum teletext_zoom zoom; ///< see teletext_zoom enum mag_t* mag; ///< pages magazine (has 8 entities) + int primary_language; ///< primary character set + int secondary_language; ///< secondary character set /// Currently displayed page (with additional info, e.g current time) tt_char display_page[VBI_ROWS*VBI_COLUMNS]; /// number of raw bytes between two subsequent encoded bits @@ -320,25 +321,132 @@ static unsigned int latin_subchars[8][13]={ {0x23,0x24,0x40,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x7b,0x7c,0x7d,0x7e} }; -static int lang2id (int lang){ - return LATIN; +/** + * List of supported languages. + * + * lang_code bits for primary Language: + * bits 7-4 corresponds to bits 14-11 of 28 packet's first triplet + * bits 3-1 corresponds to bits C12-C14 of packet 0 (lang) + * + * lang_code bits for secondary Language: + * bits 7-5 corresponds to bits 3-1 of 28 packet's second triplet + * bits 4,2 corresponds to bits 18,16 of 28 packet's first triplet + * bits 3,1 corresponds to bits 15,17 of 28 packet's first triplet + * + * For details see Tables 32 and 33 of specification (subclause 15.2) + */ +struct { + unsigned char lang_code; + unsigned char charset; + char* lang_name; +} tt_languages[]= +{ + { 0x01, LATIN, "French"}, + { 0x02, LATIN, "Swedish/Finnish/Hungarian"}, + { 0x03, LATIN, "Czech/Slovak"}, + { 0x04, LATIN, "German"}, + { 0x05, LATIN, "Portuguese/Spanish"}, + { 0x06, LATIN, "Italian"}, + + { 0x08, LATIN, "Polish"}, + { 0x09, LATIN, "French"}, + { 0x0a, LATIN, "Swedish/Finnish/Hungarian"}, + { 0x0b, LATIN, "Czech/Slovak"}, + { 0x0c, LATIN, "German"}, + { 0x0e, LATIN, "Italian"}, + + { 0x10, LATIN, "English"}, + { 0x11, LATIN, "French"}, + { 0x12, LATIN, "Swedish/Finnish/Hungarian"}, + { 0x13, LATIN, "Turkish"}, + { 0x14, LATIN, "German"}, + { 0x15, LATIN, "Portuguese/Spanish"}, + { 0x16, LATIN, "Italian"}, + + { 0x1d, LATIN, "Serbian/Croatian/Slovenian (Latin)"}, + + { 0x20, CYRILLIC1, "Serbian/Croatian (Cyrillic)"}, + { 0x21, CYRILLIC2, "Russian, Bulgarian"}, + { 0x22, LATIN, "Estonian"}, + { 0x23, LATIN, "Czech/Slovak"}, + { 0x24, LATIN, "German"}, + { 0x25, CYRILLIC3, "Ukrainian"}, + { 0x26, LATIN, "Lettish/Lithuanian"}, + + { 0x33, LATIN, "Turkish"}, + { 0x37, GREEK, "Greek"}, + + { 0x40, LATIN, "English"}, + { 0x41, LATIN, "French"}, +// { 0x47, ARABIC, "Arabic"}, + +// { 0x55, HEBREW, "Hebrew"}, +// { 0x57, ARABIC, "Arabic"}, + + { 0x00, LATIN, "English"}, +}; + +/** + * \brief 24/18 Hamming code decoding + * \param data bytes with hamming code (array must be at least 3 bytes long) + * \return -1 if multiple bit error occured, D1-DI data bits - otherwise + * + * \note Bits must be correctly ordered, that is for 24/18 (lowest bit first) + * P1 P2 D1 P3 D2 D3 D4 P4 D5 D6 D7 D8 D9 DA DB P5 DC DD DE DF DG DH DI P6 + */ +int corrHamm24(unsigned char *data){ + unsigned char syndrom=0; + int cw=data[0] | (data[1]<<8) | (data[2]<<16); + int i; + + for(i=0;i<23;i++) + syndrom^=((cw>>i)&1)*(i+33); + + syndrom^=(cw>>11)&32; + + if(syndrom&31){ + if(syndrom < 32 || syndrom > 55) + return -1; + cw ^= 1<<((syndrom&31)-1); + } + + return (cw&4)>>2 | + (cw&0x70)>>3 | + (cw&0x3f00)>>4 | + (cw&0x3f0000)>>5; +} + +/** + * \brief converts language bits to charset index + * \param lang language bits + * \return charset index in lang_chars array + */ +static int lang2charset (int lang){ + int i; + for(i=0;tt_languages[i].lang_code;i++) + if(tt_languages[i].lang_code==lang) + break; + + return tt_languages[i].charset; } /** * \brief convert chars from curent teletext codepage into MPlayer charset * \param p raw teletext char to decode - * \param lang teletext internal language code (see lang2id) + * \param charset index on lang_chars + * \param lang index in substitution array (latin charset only) * \return UTF8 char * * \remarks * routine will analyze raw member of given tt_char structure and * fill unicode member of the same struct with appropriate utf8 code. */ -static unsigned int conv2uni(unsigned int p,int lang) +static unsigned int conv2uni(unsigned int p,int charset,int lang) { - int charset=lang2id(lang); + if(p<0x80 && p>=0x20){ if(charset==LATIN){ + lang&=7; if (p>=0x23 && p<=0x24){ return latin_subchars[lang][p-0x23]; }else if (p==0x40){ @@ -452,7 +560,8 @@ static void put_to_cache(priv_vbi_t* priv,tt_page* pg,int line){ } pgc->pagenum=pg->pagenum; pgc->subpagenum=pg->subpagenum; - pgc->lang=pg->lang; + pgc->primary_lang=pg->primary_lang; + pgc->secondary_lang=pg->secondary_lang; pgc->flags=pg->flags; for(j=0;j<6;++j) pgc->links[j]=pg->links[j]; @@ -559,17 +668,22 @@ static void destroy_cache(priv_vbi_t* priv){ /** * \brief converts raw teletext page into useful format (1st rendering stage) * \param pg page to decode - * + * \param raw raw data to decode page from + * \param primary_lang primary language code + * \param secondary_lang secondary language code +* * Routine fills tt_char structure of each teletext_page character with proper * info about foreground and background colors, character * type (graphics/control/text). */ -static void decode_page(tt_char* p,int lang,unsigned char* raw) +static void decode_page(tt_char* p,unsigned char* raw,int primary_lang,int secondary_lang) { int row,col; + int prim_charset=lang2charset(primary_lang); + int sec_charset=lang2charset(secondary_lang); for(row=0;row0x3f) p[i].unicode-=0x20; tt_held=p[i]; - }else - p[i].unicode=conv2uni(c,p[i].lng); - + }else{ + if(p[i].lng){ + p[i].unicode=conv2uni(c,prim_charset,primary_lang&7); + }else{ + p[i].unicode=conv2uni(c,sec_charset,secondary_lang&7); + } + } p[i].fg=fg_color; p[i].bg=bg_color; } @@ -684,7 +802,7 @@ static void prepare_visible_page(priv_vbi_t* priv){ priv->display_page[i]=tt_space; } }else{ - decode_page(priv->display_page,pg->lang,pg->raw); + decode_page(priv->display_page,pg->raw,pg->primary_lang,pg->secondary_lang); mp_msg(MSGT_TV,MSGL_DBG3,"page #%x was decoded!\n",pg->pagenum); } @@ -759,7 +877,7 @@ static void render2text(tt_page* pt,FILE* f,int colored){ 0); fprintf(f,"+----------------------------------------+\n"); - decode_page(dp,pt->lang,pt->raw); + decode_page(dp,pt->raw,pt->primary_lang,pt->secondary_lang); for(i=0;imag[magAddr].pt) priv->mag[magAddr].pt= malloc(sizeof(tt_page)); - priv->mag[magAddr].lang=(d[7]>>1)&0x7; - priv->mag[magAddr].pt->lang=priv->mag[magAddr].lang; + if(priv->primary_language) + priv->mag[magAddr].pt->primary_lang=priv->primary_language; + else + priv->mag[magAddr].pt->primary_lang= (d[7]&7)>>1; + priv->mag[magAddr].pt->secondary_lang=priv->secondary_language; priv->mag[magAddr].pt->subpagenum=(d[2]|(d[3]<<4)|(d[4]<<8)|(d[5]<<12))&0x3f7f; priv->mag[magAddr].pt->pagenum=(magAddr<<8) | d[0] | (d[1]<<4); priv->mag[magAddr].pt->flags=( d[6] | (d[7]<<4)); @@ -1118,6 +1239,46 @@ static int decode_pkt27(priv_vbi_t* priv,unsigned char* data,int magAddr){ return 1; } +/** + * \brief Decode teletext X/28/0 Format 1 packet + * \param priv private data structure + * \param data raw teletext data + * + * Primary G0 charset is transmitted in bits 14-8 of Triplet 1 + * See Table 32 of specification for details. + * + * Secondary G0 charset is transmitted in bits 3-1 of Triplet 2 and + * bits 18-15 of Triplet 1 + * See Table 33 of specification for details. + * + */ +static void decode_pkt28(priv_vbi_t* priv,unsigned char*data){ + int d; + int t1,t2; + d=corrHamm48[ data[0] ]; + if(d) return; //this is not X/28/0 Format 1 packet or error occured + + t1=corrHamm24(data+1); + t2=corrHamm24(data+4); + if (t1<0 || t2<0){ + pll_add(priv,1,4); + return; + } + + priv->primary_language=(t1>>7)&0x7f; + priv->secondary_language=((t2<<4) | (t1>>14))&0x7f; + if (priv->secondary_language==0x7f) + //No secondary language required + priv->secondary_language=priv->primary_language; + else // Swapping bits 1 and 3 + priv->secondary_language=(priv->secondary_language&0x7a) | + (priv->secondary_language&4)>>2 | + (priv->secondary_language&1)<<2; + + mp_msg(MSGT_TV,MSGL_DBG2,"pkt28: language: primary=%02x secondary=0x%02x\n", + priv->primary_language,priv->secondary_language); +} + /** * \brief decodes raw vbi data (signal amplitudes) into sequence of bytes * \param priv private data structure @@ -1311,6 +1472,8 @@ static void vbi_decode(priv_vbi_t* priv,unsigned char*buf){ decode_pkt_page(priv,data+2,magAddr,pkt);//skip MRGA }else if(pkt==27) { decode_pkt27(priv,data+2,magAddr); + }else if(pkt==28){ + decode_pkt28(priv,data+2); }else if(pkt==30){ decode_pkt30(priv,data+2,magAddr); } else { @@ -1455,6 +1618,7 @@ int teletext_control(void* p, int cmd, void *arg) switch (cmd) { case TV_VBI_CONTROL_RESET: { + int i; tv_param_t* tv_param=arg; pthread_mutex_lock(&(priv->buffer_mutex)); priv->pagenumdec=0; @@ -1463,6 +1627,25 @@ int teletext_control(void* p, int cmd, void *arg) priv->tformat=tv_param->tformat; priv->subpagenum=0; pll_reset(priv,fine_tune); + if(tv_param->tlang==-1){ + mp_msg(MSGT_TV,MSGL_INFO,MSGTR_TV_TTSupportedLanguages); + for(i=0; tt_languages[i].lang_code; i++){ + mp_msg(MSGT_TV,MSGL_INFO," %3d %s\n", + tt_languages[i].lang_code, tt_languages[i].lang_name); + } + mp_msg(MSGT_TV,MSGL_INFO," %3d %s\n", + tt_languages[i].lang_code, tt_languages[i].lang_name); + }else{ + for(i=0; tt_languages[i].lang_code; i++){ + if(tt_languages[i].lang_code==tv_param->tlang) + break; + } + if (priv->primary_language!=tt_languages[i].lang_code){ + mp_msg(MSGT_TV,MSGL_INFO,MSGTR_TV_TTSelectedLanguage, + tt_languages[i].lang_name); + priv->primary_language=tt_languages[i].lang_code; + } + } pthread_mutex_unlock(&(priv->buffer_mutex)); return TVI_CONTROL_TRUE; }