1 #include <src/rds_text.h>
10 #include <src/enigma.h>
11 #include <lib/dvb/decoder.h>
12 #include <lib/driver/streamwd.h>
16 #define SWAP(x) ((x<<8)|(x>>8))
17 #define LO(x) (x&0xFF)
19 static inline unsigned short crc_ccitt_byte( unsigned short crc
, unsigned char c
)
22 crc
= crc
^ (LO(crc
) >> 4);
23 crc
= crc
^ (SWAP(LO(crc
)) << 4) ^ (LO(crc
) << 5);
27 RDSTextDecoder::RDSTextDecoder()
28 : m_interactive(0), wasVisible(0), bytesread(0), ptr(0), p1(-1), p2(-1), qdar_pos(0), rass_imode_active(0), leninfo(0), text_len(0), m_ptr(0), state(0)
29 , is_sync(0), paket_size(0), sync_try(0), sn(0), rass_logo( eZap::getInstance()->getDesktop( eZap::desktopFB
)), qdarmvi_show(-1)
31 int fd
=open("/dev/dvb/card0/ancillary0", O_RDONLY
|O_NONBLOCK
);
33 eDebug("open /dev/dvb/card0/ancillary0 failed(%m)");
36 sn
= new eSocketNotifier(eApp
, fd
, eSocketNotifier::Read
);
37 CONNECT(sn
->activated
, RDSTextDecoder::process_data
);
40 memset(rtp_item
, 0, sizeof(rtp_item
));
42 int x
= eSkin::getActive()->queryValue("rds.pos.x", 0);
43 int y
= eSkin::getActive()->queryValue("rds.pos.y", 0);
44 int width
= eSkin::getActive()->queryValue("rds.pos.width", 0);
45 int height
= eSkin::getActive()->queryValue("rds.pos.height", 0);
46 gFont rds_font
= eSkin::getActive()->queryFont("rds");
49 rds_text
= new eLabel( eZap::getInstance()->getDesktop( eZap::desktopFB
) );
50 rds_text
->move(ePoint(x
, y
));
51 rds_text
->resize(eSize(width
, height
));
52 rds_text
->setFont(rds_font
);
53 rds_text
->setProperty("align","left");
54 rds_text
->setProperty("backgroundColor","rds_bg");
55 rds_text
->setProperty("foregroundColor","rds_fg");
58 x
= eSkin::getActive()->queryValue("rds_plus.pos.x", 0);
59 y
= eSkin::getActive()->queryValue("rds_plus.pos.y", 0);
60 width
= eSkin::getActive()->queryValue("rds_plus.pos.width", 0);
61 height
= eSkin::getActive()->queryValue("rds_plus.pos.height", 0);
64 rtp_text
= new eLabel( eZap::getInstance()->getDesktop( eZap::desktopFB
) );
65 rtp_text
->move(ePoint(x
, y
));
66 rtp_text
->resize(eSize(width
, height
));
67 rtp_text
->setFont(rds_font
);
68 rtp_text
->setProperty("align","right");
69 rtp_text
->setProperty("backgroundColor","rds_bg");
70 rtp_text
->setProperty("foregroundColor","rds_fg");
73 x
= eSkin::getActive()->queryValue("rass_logo.pos.x", 0);
74 y
= eSkin::getActive()->queryValue("rass_logo.pos.y", 0);
77 gPixmap
*pm
= eSkin::getActive()->queryImage("rass_logo");
78 rass_logo
.setPixmap(pm
);
79 rass_logo
.move( ePoint(x
, y
) );
80 rass_logo
.resize( eSize(50, 21) );
81 rass_logo
.pixmap_position
= ePoint(0,0);
83 CONNECT(eWidget::globalFocusChanged
, RDSTextDecoder::globalFocusHasChanged
);
86 RDSTextDecoder::~RDSTextDecoder()
88 int fd
= sn
? sn
->getFD() : -1;
96 void RDSTextDecoder::clear_service()
98 is_sync
=0;paket_size
=0;sync_try
=0;
99 state
=bytesread
=ptr
=0;
101 message
[0]=lastmessage
[0]=0;
107 qdarmvi_show
=interactive_avail
=0;
111 wasVisible
&= ~(1|2|4);
113 // delete cached rass slides
115 struct dirent
*filename
;
116 char tmp_filename
[128];
118 tmp_directory
= opendir("/tmp");
119 while ((filename
= readdir(tmp_directory
)) != NULL
)
121 if (strstr(filename
->d_name
,"Rass") && strstr(filename
->d_name
,".mvi"))
123 sprintf(tmp_filename
,"/tmp/%s",filename
->d_name
);
124 remove(tmp_filename
);
127 closedir(tmp_directory
);
130 void RDSTextDecoder::process_qdar(unsigned char *buf
)
132 if (buf
[0] == 0x40 && buf
[1] == 0xDA)
134 int item
,cnt
,ctrl
,item_type
;
135 long item_length
,id
,item_no
,ptr
,tmp
;
136 unsigned short crc_qdar
,crc_read
;
139 item
=buf
[2]<<8; // Number of Items
142 while ( cnt
++ < item
) //read in items
144 id
=buf
[ptr
++]<<8; //QDarID
147 item_no
=buf
[ptr
++]<<8; // Item Number
150 ctrl
=buf
[ptr
++]; //controlbyte
151 item_type
=buf
[ptr
++]; //item type
153 item_length
=buf
[ptr
++]<<24; // Item length
154 item_length
|=buf
[ptr
++]<<16;
155 item_length
|=buf
[ptr
++]<<8;
156 item_length
|=buf
[ptr
++];
158 ptr
=ptr
+4; // rfu Bytes ... not used
161 while (tmp
< ptr
+item_length
)
162 crc_qdar
= crc_ccitt_byte(crc_qdar
, buf
[tmp
++]);
164 crc_read
=buf
[ptr
+item_length
]<<8;
165 crc_read
|=buf
[ptr
+item_length
+1];
166 //eDebug("[RDS/Rass] CRC read: %04X calculated: %04X",crc_read,crc_qdar^0xFFFF);
168 if (crc_read
== (crc_qdar
^0xFFFF)) // process item
172 case 0x01: //Stillframe
173 if (ctrl
&0x01) // display slide
176 eStreamWatchdog::getInstance()->reloadSettings(3); // set mpeg decoder to 16:9
178 if (rass_imode_active
!= 1)
179 Decoder::displayIFrame((const char*)buf
+ptr
,item_length
-2);
180 // save as last slide, used for leaving interactive mode
181 sprintf(fname
,"/tmp/RassLast.mvi");
182 FILE *fh
=fopen(fname
,"wb");
183 fwrite(buf
+ptr
,1,item_length
-2,fh
);
186 if (ctrl
&0x02) // save slide for interactive mode
188 sprintf(fname
,"/tmp/Rass%04d.mvi",(int)id
);
189 FILE *fh
=fopen(fname
,"wb");
190 fwrite(buf
+ptr
,1,item_length
-2,fh
);
192 if (id
== 0 && interactive_avail
== 0)
195 if (wasVisible
& 8) // another widget/window visible
200 if (m_interactive
&& id
!= 0)
201 m_interactive
->update_avail((int)(id
/1000));
203 if (ctrl
&0x04) // display slide if nothing had been displayed yet
205 if (qdarmvi_show
!= 1)
207 eStreamWatchdog::getInstance()->reloadSettings(3); // set mpeg decoder to 16:9
209 if (rass_imode_active
!= 1)
210 Decoder::displayIFrame((const char*)buf
+ptr
,item_length
-2);
211 // save as last slide, used for leaving interactive mode
212 sprintf(fname
,"/tmp/RassLast.mvi");
213 FILE *fh
=fopen(fname
,"wb");
214 fwrite(buf
+ptr
,1,item_length
-2,fh
);
218 if (ctrl
&0x08) // delete slide
220 sprintf(fname
,"/tmp/Rass%04d.mvi",(int)id
);
224 default: //nothing more yet defined
230 eDebug("[RDS/Rass] CRC error, skip Rass-Qdar-Item");
238 eDebug("[RDS/Rass] No Rass-QDAR archive (%02X %02X) so skipping !\n",buf
[0],buf
[1]);
242 void RDSTextDecoder::process_data(int what
)
244 int rd
=read(sn
->getFD(), buf
+bytesread
, 263-bytesread
);
248 if ( bytesread
== 263 )
252 // sync ancillary stream
253 if (sync_try
> 99) // sync failed after 100 tries ? give up and spend the cpu time for something useful ;)
255 eDebug("[RDS/Rass] ancillary sync not found (%d) ... give up",sync_try
);
259 if (is_sync
< 5) // try to get paket_length for sync ancillary stream
261 if ( buf
[ptr
] == 0xFD && ptr
> p1
)
263 if ( sync_try
< 15 || buf
[ptr
-1] > 0x02 )
271 if ( p1
!= -1 && p2
!= -1 )
274 if ((paket_size
== p2
-p1
) || (paket_size
== (p2
-p1
)+2) || (paket_size
== (p2
-p1
)-2))
287 eDebug("[RDS/Rass] ancillary sync found (%d/%d) ...",paket_size
,sync_try
);
291 // get endpoint if in sync to reverse paket, we need to handle some special cases here
292 // and search -2 to +4 from theoretical position
293 if (is_sync
== 5 && ptr
== (p1
+paket_size
+4))
301 while (tmp_step
<= 4)
303 if ( buf
[p1
+paket_size
+tmp_step
] == 0xFD &&
304 tmp_l
< buf
[p1
+paket_size
+tmp_step
-1] &&
305 buf
[p1
+paket_size
+tmp_step
-1] < paket_size
)
307 tmp_l
=buf
[p1
+paket_size
+tmp_step
-1];
312 if (tmp_pos
== 0xFF || tmp_l
==-1 )
314 eDebug("[RDS/Rass] ancillary sync lost ... try to resync");
322 p2
=p1
+paket_size
+tmp_pos
;
328 if ( p1
!= -1 && p2
!= -1 )
333 unsigned char c
= buf
[--p2
];
335 if (bsflag
== 1) // byte stuffing
340 case 0x00: c
=0xFD; break;
341 case 0x01: c
=0xFE; break;
342 case 0x02: c
=0xFF; break;
346 if (c
== 0xFD && bsflag
==0)
357 if (( state
>= 1 && state
< 11 ) || ( state
>=26 && state
< 36 ))
358 crc
= crc_ccitt_byte(crc
, c
);
363 if ( c
==0xFE ) // Start
366 case 1: // 10bit Site Address + 6bit Encoder Address
368 case 3: // Sequence Counter
378 case 0x0A: // Radiotext
381 case 0x46: // Radiotext Plus tags
387 default: // reset to state 0
393 case 6: // Data Set Number ... ignore
394 case 7: // Program Service Number ... ignore
397 case 8: // Message Element Length
399 if ( !text_len
|| text_len
> 65 || text_len
> leninfo
-4)
408 case 9: // Radio Text Status bit:
409 // 0 = AB-flagcontrol
410 // 1-4 = Transmission-Number
411 // 5-6 = Buffer-Config
412 ++state
; // ignore ...
415 // TODO build a complete radiotext charcode to UTF8 conversion table for all character > 0x80
418 case 0 ... 0x7f: break;
420 case 0x8d: c
='ß'; break;
421 case 0x91: c
='ä'; break;
422 case 0xd1: c
='Ä'; break;
423 case 0x97: c
='ö'; break;
424 case 0xd7: c
='Ö'; break;
425 case 0x99: c
='ü'; break;
426 case 0xd9: c
='Ü'; break;
427 default: c
=' '; break; // convert all unknown to space
442 while(message
[m_ptr
] == ' ' && m_ptr
> 0)
443 message
[m_ptr
--] = 0;
444 if ( crc16
== (crc
^0xFFFF) )
446 rds_text
->setText((const char*)message
);
447 /*emit*/ textReady((const char*)message
);
448 if (lastmessage
[0]==0 && rass_imode_active
!= 1)
455 memcpy(lastmessage
,message
,66);
458 eDebug("[RDS/Rass] invalid rdstext crc (%s)", message
);
471 case 27: // SID not used atm
474 case 28: // SID not used atm
478 case 29: // PNR packet number
482 case 30: // PNR packet number
486 case 31: // PNR packet number
490 case 32: // NOP number of packets
494 case 33: // NOP number of packets
498 case 34: // NOP number of packets
503 datamessage
[t_ptr
++]=c
;
515 //eDebug("[RDS/Rass] CRC read: %04X CRC calculated: %04X",crc16,crc^0xFFFF);
517 if ( crc16
== (crc
^0xFFFF) )
523 memcpy(qdar
+qdar_pos
,datamessage
,text_len2
+1);
524 qdar_pos
=qdar_pos
+text_len2
+1;
525 if (partcnt
== parts
)
527 process_qdar(qdar
); // decode qdar archive
542 eDebug("[RDS/Rass] CRC error, skip Rass-Qdar-Packet");
548 // process RT plus tags ...
549 case 38: // Message Element Length
553 case 39: // Application ID
554 case 40: // always 0x4BD7 so we ignore it ;)
555 case 41: // Applicationgroup Typecode/PTY ... ignore
574 case 46: // bit 10#4 = Item Togglebit
575 // bit 10#3 = Item Runningbit
576 // Tag1: bit 10#2..11#5 = Contenttype, 11#4..12#7 = Startmarker, 12#6..12#1 = Length
578 if (lastmessage
[0] == 0) // no rds message till now ? quit ...
580 int rtp_typ
[2],rtp_start
[2],rtp_len
[2];
581 rtp_typ
[0] = (0x38 & rtp_buf
[0]<<3) | rtp_buf
[1]>>5;
582 rtp_start
[0] = (0x3e & rtp_buf
[1]<<1) | rtp_buf
[2]>>7;
583 rtp_len
[0] = 0x3f & rtp_buf
[2]>>1;
584 // Tag2: bit 12#0..13#3 = Contenttype, 13#2..14#5 = Startmarker, 14#4..14#0 = Length(5bit)
585 rtp_typ
[1] = (0x20 & rtp_buf
[2]<<5) | rtp_buf
[3]>>3;
586 rtp_start
[1] = (0x38 & rtp_buf
[3]<<3) | rtp_buf
[4]>>5;
587 rtp_len
[1] = 0x1f & rtp_buf
[4];
589 unsigned char rtplus_osd_tmp
[64];
591 if (rtp_start
[0] < 66 && (rtp_len
[0]+rtp_start
[0]) < 66)
593 memcpy(rtp_item
[rtp_typ
[0]],lastmessage
+rtp_start
[0],rtp_len
[0]+1);
594 rtp_item
[rtp_typ
[0]][rtp_len
[0]+1]=0;
597 if (rtp_typ
[0] != rtp_typ
[1])
599 if (rtp_start
[1] < 66 && (rtp_len
[1]+rtp_start
[1]) < 66)
601 memcpy(rtp_item
[rtp_typ
[1]],lastmessage
+rtp_start
[1],rtp_len
[1]+1);
602 rtp_item
[rtp_typ
[1]][rtp_len
[1]+1]=0;
606 // main RTPlus item_types used by the radio stations:
615 // todo: make a window to display all saved items ...
617 //create RTPlus OSD for title/artist
620 if ( rtp_item
[4][0] != 0 )//artist
621 sprintf((char*)rtplus_osd_tmp
," (%s)",rtp_item
[4]);
623 if ( rtp_item
[1][0] != 0 )//title
624 sprintf((char*)rtplus_osd
,"%s%s",rtp_item
[1],rtplus_osd_tmp
);
626 if ( rtplus_osd
[0] != 0 )
628 rtp_text
->setText((const char*)rtplus_osd
);
629 /*emit*/ textReady((const char*)rtplus_osd
);
630 if (rass_imode_active
!= 1)
649 if (p1
!= -1 && (263-(p1
)) != 263)
651 bytesread
=ptr
=263-(p1
);
652 memcpy(buf
, buf
+p1
, ptr
);
661 // Rass InteractiveMode
662 void RDSTextDecoder::rass_interactive()
664 RassInteractivemode rass
;
670 eStreamWatchdog::getInstance()->reloadSettings(3); // set mpeg decoder to 16:9
671 Decoder::displayIFrameFromFile("/tmp/Rass0000.mvi");
674 rass_logo
.move( ePoint( 50, 180 ) );
675 rass_logo
.resize( eSize( 100, 21 ) );
683 int x
= eSkin::getActive()->queryValue("rass_logo.pos.x", 0);
684 int y
= eSkin::getActive()->queryValue("rass_logo.pos.y", 0);
686 rass_logo
.move( ePoint( x
, y
) );
687 rass_logo
.resize( eSize( 50, 21 ) );
691 Decoder::displayIFrameFromFile("/tmp/RassLast.mvi");
694 void RDSTextDecoder::globalFocusHasChanged(const eWidget
* newFocus
)
696 if ( !sn
) // not running
701 if (rds_text
->isVisible())
706 if (rtp_text
->isVisible())
711 if (!rass_imode_active
&& rass_logo
.isVisible())
739 RassInteractivemode::RassInteractivemode()
740 :eWidget(0,1), active_slide(0), active_slide_sub(-1)
742 gFont rds_font
= eSkin::getActive()->queryFont("rass");
744 cmove(ePoint(50,201)); cresize(eSize(100,254));
745 setProperty("backgroundColor","rass_bg");
746 setProperty("foregroundColor","rass_fg");
755 rass_page
[i
]=new eLabel(this);
756 rass_page
[i
]->setText(c
);
757 rass_page
[i
]->move(ePoint(5, (25*i
)+4));
758 rass_page
[i
]->resize(eSize(70, 25));
762 sprintf(c
,"%01d >",0); //set marker
763 rass_page
[0]->setText(c
);
765 rass_pm1
= eSkin::getActive()->queryImage("rass_page1");
766 rass_pm2
= eSkin::getActive()->queryImage("rass_page2");
767 rass_pm3
= eSkin::getActive()->queryImage("rass_page3");
768 rass_pm4
= eSkin::getActive()->queryImage("rass_page4");
770 rass_page_sub
[0]=new eLabel(this);
771 rass_page_sub
[0]->setText("Index");
772 rass_page_sub
[0]->move( ePoint(36, 4));
773 rass_page_sub
[0]->resize( eSize( 50, 25 ) );
778 rass_page_sub
[i
]=new eLabel(this);
780 rass_page_sub
[i
]->move( ePoint(35, (25*i
)+6));
781 rass_page_sub
[i
]->resize( eSize( 36, 20 ) );
782 rass_page_sub
[i
]->pixmap_position
= ePoint(0,0);
783 rass_page_sub
[i
]->setBlitFlags( BF_ALPHATEST
);
787 addActionMap(&i_shortcutActions
->map
);
788 addActionMap(&i_cursorActions
->map
);
791 RassInteractivemode::~RassInteractivemode()
798 delete rass_page_sub
[i
];
804 void RassInteractivemode::sel_entry(int val
)
811 while(i
< 10) // update availability
816 if (val
== 0xFF) // up
823 tmp
=check_avail(val
);
827 if (val
== 0xFE) // down
834 tmp
=check_avail(val
);
838 if (val
== 0xFD) // left
844 if (val
== 0xFC) // right
847 sprintf(fname
,"/tmp/Rass%04d.mvi",val
*1000);
848 file
[0]=check_file(fname
);
849 sprintf(fname
,"/tmp/Rass%04d.mvi",val
*1100);
850 file
[1]=check_file(fname
);
851 sprintf(fname
,"/tmp/Rass%04d.mvi",val
*1110);
852 file
[2]=check_file(fname
);
853 sprintf(fname
,"/tmp/Rass%04d.mvi",val
*1111);
854 file
[3]=check_file(fname
);
856 if ((file
[0] + file
[1] + file
[2] + file
[3]) == 0)
859 if (active_slide
!= val
)
864 sprintf(c
,"%01d",active_slide
);
865 rass_page
[active_slide
]->setText(c
);
866 sprintf(c
,"%01d >",val
);
867 rass_page
[val
]->setText(c
);
870 tmp
=active_slide_sub
;
873 if (i
== 0) //next subslide (right)
877 if (active_slide_sub
== -1 && file
[i
] == 1)
879 if (file
[i
] == 1 && i
> tmp
)
887 else //last subslide (left)
891 if (active_slide_sub
== -1 && file
[i
] == 1)
893 if (file
[i
] == 1 && i
< tmp
)
903 switch(active_slide_sub
)
906 val
=active_slide
*1000;
909 val
=active_slide
*1100;
912 val
=active_slide
*1110;
915 val
=active_slide
*1111;
921 sprintf(fname
,"/tmp/Rass%04d.mvi",val
);
922 Decoder::displayIFrameFromFile(fname
);
925 void RassInteractivemode::update_avail(int i
)
929 switch(check_avail(i
))
932 rass_page_sub
[i
]->setPixmap(rass_pm1
);
935 rass_page_sub
[i
]->setPixmap(rass_pm2
);
938 rass_page_sub
[i
]->setPixmap(rass_pm3
);
941 rass_page_sub
[i
]->setPixmap(rass_pm4
);
944 rass_page_sub
[i
]->setPixmap(0);
948 int RassInteractivemode::check_file(char* fname
)
950 FILE * f
= fopen (fname
,"r");
960 int RassInteractivemode::check_avail(int page
)
968 sprintf(fname
,"/tmp/Rass%04d.mvi",page
*1000);
969 tmp
+=check_file(fname
);
970 sprintf(fname
,"/tmp/Rass%04d.mvi",page
*1100);
971 tmp
+=check_file(fname
);
972 sprintf(fname
,"/tmp/Rass%04d.mvi",page
*1110);
973 tmp
+=check_file(fname
);
974 sprintf(fname
,"/tmp/Rass%04d.mvi",page
*1111);
975 tmp
+=check_file(fname
);
980 int RassInteractivemode::eventHandler( const eWidgetEvent
&e
)
984 case eWidgetEvent::evtAction
:
985 if ( e
.action
== &i_shortcutActions
->number0
)
987 else if ( e
.action
== &i_shortcutActions
->number1
)
989 else if ( e
.action
== &i_shortcutActions
->number2
)
991 else if ( e
.action
== &i_shortcutActions
->number3
)
993 else if ( e
.action
== &i_shortcutActions
->number4
)
995 else if ( e
.action
== &i_shortcutActions
->number5
)
997 else if ( e
.action
== &i_shortcutActions
->number6
)
999 else if ( e
.action
== &i_shortcutActions
->number7
)
1001 else if ( e
.action
== &i_shortcutActions
->number8
)
1003 else if ( e
.action
== &i_shortcutActions
->number9
)
1005 else if ( e
.action
== &i_cursorActions
->up
)
1007 else if ( e
.action
== &i_cursorActions
->down
)
1009 else if ( e
.action
== &i_cursorActions
->left
)
1011 else if ( e
.action
== &i_cursorActions
->right
)
1013 else if ( e
.action
== &i_cursorActions
->cancel
)
1021 return eWidget::eventHandler(e
);