changeset 24290:ca7ee5c1b88d

Support for selecting language via packet 28. Also allows to select default teletext language. It will be used if language is not specified by network provider via packet 28.
author voroshil
date Fri, 31 Aug 2007 16:53:27 +0000
parents 53f2436d68d7
children 1c14b18d6249
files DOCS/man/en/mplayer.1 cfg-common.h help/help_mp-en.h stream/stream_tv.c stream/tv.h stream/tvi_vbi.c
diffstat 6 files changed, 216 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/DOCS/man/en/mplayer.1	Fri Aug 31 14:57:50 2007 +0000
+++ b/DOCS/man/en/mplayer.1	Fri Aug 31 16:53:27 2007 +0000
@@ -1896,6 +1896,12 @@
 .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
--- a/cfg-common.h	Fri Aug 31 14:57:50 2007 +0000
+++ b/cfg-common.h	Fri Aug 31 16:53:27 2007 +0000
@@ -464,6 +464,7 @@
 	{"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}
--- a/help/help_mp-en.h	Fri Aug 31 14:57:50 2007 +0000
+++ b/help/help_mp-en.h	Fri Aug 31 16:53:27 2007 +0000
@@ -2097,3 +2097,5 @@
 #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"
--- a/stream/stream_tv.c	Fri Aug 31 14:57:50 2007 +0000
+++ b/stream/stream_tv.c	Fri Aug 31 16:53:27 2007 +0000
@@ -75,6 +75,7 @@
     NULL,          //tdevice
     0,             //tformat
     100,           //tpage
+    0,             //tlang
 
     0,             //scan_autostart
     50,            //scan_threshold
--- a/stream/tv.h	Fri Aug 31 14:57:50 2007 +0000
+++ b/stream/tv.h	Fri Aug 31 16:53:27 2007 +0000
@@ -50,6 +50,7 @@
     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 @@
     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_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
--- a/stream/tvi_vbi.c	Fri Aug 31 14:57:50 2007 +0000
+++ b/stream/tvi_vbi.c	Fri Aug 31 16:53:27 2007 +0000
@@ -103,7 +103,6 @@
 typedef struct mag_s{
     tt_page* pt;
     int      order;
-    int      lang;
 } mag_t;
 
 typedef struct {
@@ -116,6 +115,8 @@
     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 @@
   {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 @@
     }
     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 @@
 /**
  * \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;row<VBI_ROWS;row++)   {
-        int lat=(lang==0);
+        int prim_lang=1;
         int gfx=0;
         int fg_color=7;
         int bg_color=0;
@@ -586,7 +700,7 @@
                 continue;
             }
             p[i].gfx=gfx?(separated?2:1):0;
-            p[i].lng=lat?0:lang;
+            p[i].lng=prim_lang;
             p[i].ctl=(c&0x60)==0?1:0;
             p[i].fg=fg_color;
             p[i].bg=bg_color;
@@ -609,7 +723,7 @@
                 }else if (c<=0x1a){ //Contiguous/Separated gfx
                     separated=!(c&1);
                 }else if (c<=0x1b){
-                    lat=!lat;
+                    prim_lang=!prim_lang;
                 }else if (c<=0x1d){
                     bg_color=(c&1)?fg_color:0;
                     p[i].bg=bg_color;
@@ -633,9 +747,13 @@
                 p[i].unicode=c-0x20;
                 if (p[i].unicode>0x3f) 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 @@
             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 @@
     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;i<VBI_ROWS;i++){
         fprintf(f,"|");
         if(colored) fprintf(f,"\033[40m");
@@ -959,8 +1077,11 @@
     if (!priv->mag[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));
@@ -1119,6 +1240,46 @@
 }
 
 /**
+ * \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
  * \param buf raw vbi data (one line of frame)
@@ -1311,6 +1472,8 @@
             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 @@
     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 @@
         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;
     }