From: Andreas Monzner Date: Sat, 24 Mar 2007 00:49:20 +0000 (+0000) Subject: add a radiomode background picture (mvi file.. changable in X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=commitdiff_plain;h=d1e450f95a9b79b144a00dcf0f9356ff6cb9bfb2 add a radiomode background picture (mvi file.. changable in /etc/enigma2/config ... config.misc.radiopic = /bla/blubber.mvi add support for radio text plus, add rass (radio screen show) support (yes used by SWR3, cont.ra and DASDING) thanks to seddi for some piece of code for better single iframe support its recommend to update in dreambox-dvb-modules.bb CVSDATE for dm7025 to 20070323 --- diff --git a/data/Makefile.am b/data/Makefile.am index c866eac..ed26ec5 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -5,4 +5,4 @@ SUBDIRS = countries fonts defaults extensions installdir = $(DATADIR)/enigma2 install_DATA = \ - *.xml *.png encoding.conf + *.xml *.png encoding.conf radio.mvi diff --git a/data/keymap.xml b/data/keymap.xml index 835c521..5c0cb54 100644 --- a/data/keymap.xml +++ b/data/keymap.xml @@ -300,6 +300,18 @@ + + + + + + + + + + + + diff --git a/data/radio.mvi b/data/radio.mvi new file mode 100755 index 0000000..bbd8016 Binary files /dev/null and b/data/radio.mvi differ diff --git a/data/rass_logo.png b/data/rass_logo.png new file mode 100644 index 0000000..e69de29 diff --git a/data/rass_page1.png b/data/rass_page1.png new file mode 100644 index 0000000..e69de29 diff --git a/data/rass_page2.png b/data/rass_page2.png new file mode 100644 index 0000000..e69de29 diff --git a/data/rass_page3.png b/data/rass_page3.png new file mode 100644 index 0000000..e69de29 diff --git a/data/rass_page4.png b/data/rass_page4.png new file mode 100644 index 0000000..e69de29 diff --git a/data/skin.xml b/data/skin.xml index 36f7325..cf2d2c7 100644 --- a/data/skin.xml +++ b/data/skin.xml @@ -45,18 +45,13 @@ - - - - RadioText - - - + + - - - - + + + + - - - + + + - + SNR - + AGC - + BER - + SNR - + AGC - + BER - + - + IsCrypted - + IsMultichannel - + IsWidescreen - + Name - + WithSeconds - + StartTime Default - + StartTime Default - + Name - + Name - + Remaining InMinutes - + Duration InMinutes - + Progress - + - + - + SubservicesAvailable - + SubservicesAvailable - + - + - + - + diff --git a/data/skin_default.xml b/data/skin_default.xml index 4bdc5c1..65e810d 100644 --- a/data/skin_default.xml +++ b/data/skin_default.xml @@ -3,8 +3,7 @@ - - + @@ -18,8 +17,7 @@ - - + @@ -75,7 +73,7 @@ - + @@ -248,41 +246,43 @@ - - RadioText + + RadioText Name - WithSeconds Progress - - StartTime - Default + + RasInteractiveAvailable + - + StartTime Default - - Name - - + Name - - + Remaining InMinutes - + + Name + + + StartTime + Default + + Duration InMinutes @@ -331,11 +331,50 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/dvb/radiotext.cpp b/lib/dvb/radiotext.cpp index 87498da..eff40e9 100644 --- a/lib/dvb/radiotext.cpp +++ b/lib/dvb/radiotext.cpp @@ -3,19 +3,45 @@ #include #include -DEFINE_REF(eDVBRadioTextParser); +DEFINE_REF(eDVBRdsDecoder); -eDVBRadioTextParser::eDVBRadioTextParser(iDVBDemux *demux) - :bytesread(0), ptr(0), p1(-1), p2(-1), msgPtr(0), state(0) +eDVBRdsDecoder::eDVBRdsDecoder(iDVBDemux *demux) + :msgPtr(0), bsflag(0), qdar_pos(0), t_ptr(0), qdarmvi_show(0), state(0) ,m_abortTimer(eApp) { setStreamID(0xC0, 0xC0); + memset(rass_picture_mask, 0, sizeof(rass_picture_mask)); + if (demux->createPESReader(eApp, m_pes_reader)) eDebug("failed to create PES reader!"); else - m_pes_reader->connectRead(slot(*this, &eDVBRadioTextParser::processData), m_read_connection); - CONNECT(m_abortTimer.timeout, eDVBRadioTextParser::abortNonAvail); + m_pes_reader->connectRead(slot(*this, &eDVBRdsDecoder::processData), m_read_connection); + CONNECT(m_abortTimer.timeout, eDVBRdsDecoder::abortNonAvail); +} + +eDVBRdsDecoder::~eDVBRdsDecoder() +{ + // delete cached rass slides + for (int page=0; page < 10; ++page) + { + unsigned char mask = rass_picture_mask[(page*4)/8]; + if (page % 2) + mask >>= 4; + int subpage=0; + while(mask) + { + if (mask & 1) + { + std::string filename = getRassPicture(page, subpage); + if (filename.length()) + remove(filename.c_str()); + } + mask >>= 1; + ++subpage; + } + } + remove("/tmp/RassLast.mvi"); } #define SWAP(x) ((x<<8)|(x>>8)) @@ -65,14 +91,54 @@ static int frequency[3][4] = { { 11025,12000,8000,0 } }; -void eDVBRadioTextParser::connectUpdatedRadiotext(const Slot0 &slot, ePtr &connection) +void eDVBRdsDecoder::connectEvent(const Slot1 &slot, ePtr &connection) { - connection = new eConnection(this, m_updated_radiotext.connect(slot)); + connection = new eConnection(this, m_event.connect(slot)); } -void eDVBRadioTextParser::processPESPacket(__u8 *data, int len) +void eDVBRdsDecoder::addToPictureMask(int id) +{ + int page = id / 1000; + int tmp = page > 0 ? id / page : id; + int subpage = 0; + while(tmp > 1000) + { + ++subpage; + tmp -= 1000; + tmp *= 10; + } + int index = (page*4+subpage)/8; + int val = (page%2) ? 16 * (1 << subpage) : (1 << subpage); + if (rass_picture_mask[index] & val) // already have this picture + return; + rass_picture_mask[index] |= val; + /* emit */ m_event(RassInteractivePicMaskChanged); +} + +void eDVBRdsDecoder::removeFromPictureMask(int id) +{ + int page = id / 1000; + int tmp = page > 0 ? id / page : id; + int subpage = 0; + while(tmp > 1000) + { + ++subpage; + tmp -= 1000; + tmp *= 10; + } + int index = (page*4)/8; + int val = (page%2) ? 16 * (1 << subpage) : (1 << subpage); + if (rass_picture_mask[index] & val) // have this picture + { + rass_picture_mask[index] &= ~val; + /* emit */ m_event(RassInteractivePicMaskChanged); + } +} + +void eDVBRdsDecoder::processPESPacket(__u8 *data, int len) { int pos=9+data[8];// skip pes header + int cnt=0; while (pos < len) { @@ -130,137 +196,440 @@ void eDVBRadioTextParser::processPESPacket(__u8 *data, int len) m_abortTimer.stop(); int ancillary_len = 1 + data[offs - 1]; offs -= ancillary_len; - while(offs < pos) - gotAncillaryByte(data[offs++]); + gotAncillaryData(data+offs, ancillary_len); } } } -inline void eDVBRadioTextParser::gotAncillaryByte(__u8 data) +void eDVBRdsDecoder::process_qdar(unsigned char *buf) { - buf[bytesread]=data; - bytesread+=1; - if ( bytesread == 128 ) + if (buf[0] == 0x40 && buf[1] == 0xDA) { - while(ptr<128) + unsigned int item,cnt,ctrl,item_type; + unsigned long item_length,id,item_no,ptr,tmp; + unsigned short crc_qdar,crc_read; + char fname[50]; + ptr=4;cnt=0; + item=buf[2]<<8; // Number of Items + item|=buf[3]; + + while ( cnt++ < item ) //read in items { - if ( buf[ptr] == 0xFD ) - { - if (p1 == -1) - p1 = ptr; - else - p2 = ptr; - } - if ( p1 != -1 && p2 != -1 ) + id=buf[ptr++]<<8; //QDarID + id|=buf[ptr++]; + + item_no=buf[ptr++]<<8; // Item Number + item_no|=buf[ptr++]; + + ctrl=buf[ptr++]; //controlbyte + item_type=buf[ptr++]; //item type + + item_length=buf[ptr++]<<24; // Item length + item_length|=buf[ptr++]<<16; + item_length|=buf[ptr++]<<8; + item_length|=buf[ptr++]; + + ptr=ptr+4; // rfu Bytes ... not used + tmp=ptr; // calc crc + crc_qdar=0xFFFF; + while (tmp < ptr+item_length) + crc_qdar = crc_ccitt_byte(crc_qdar, buf[tmp++]); + + crc_read=buf[ptr+item_length]<<8; + crc_read|=buf[ptr+item_length+1]; + //eDebug("[RDS/Rass] CRC read: %04X calculated: %04X",crc_read,crc_qdar^0xFFFF); + + if (crc_read == (crc_qdar^0xFFFF)) // process item { - int cnt=buf[--p2]; - while ( cnt-- > 0 ) + switch(item_type) { - unsigned char c = buf[--p2]; - if ( state == 1 ) - crc=0xFFFF; - if ( state >= 1 && state < 11 ) - crc = crc_ccitt_byte(crc, c); - - switch (state) - { - case 0: - if ( c==0xFE ) // Startkennung - state=1; - break; - case 1: // 10bit Site Address + 6bit Encoder Address - case 2: - case 3: // Sequence Counter - ++state; - break; - case 4: - leninfo=c; - ++state; - break; - case 5: - if ( c==0x0A ) // message element code 0x0A Radio Text - ++state; - else - state=0; - break; - case 6: // Data Set Number ... ignore - case 7: // Program Service Number ... ignore - ++state; - break; - case 8: // Message Element Length - todo=c; - if ( !todo || todo > 65 || todo > leninfo-4) - state=0; + case 0x01: //Stillframe + if (ctrl&0x01) // display slide + { + sprintf(fname,"/tmp/RassLast.mvi"); + FILE *fh=fopen(fname,"wb"); + fwrite(buf+ptr,1,item_length-2,fh); + fclose(fh); + /*emit*/ m_event(RecvRassSlidePic); + qdarmvi_show=1; + } + if (ctrl&0x02) // save slide for interactive mode + { + if (id == 0 || id >= 1000) + { + sprintf(fname,"/tmp/Rass%04d.mvi",(int)id); + FILE *fh=fopen(fname,"wb"); + fwrite(buf+ptr,1,item_length-2,fh); + fclose(fh); + addToPictureMask(id); + } else + eDebug("ignore recv interactive picture id %lu", id); + } + if (ctrl&0x04) // display slide if nothing had been displayed yet + { + if (qdarmvi_show != 1) { - ++state; - todo-=2; - msgPtr=0; + sprintf(fname,"/tmp/RassLast.mvi"); + FILE *fh=fopen(fname,"wb"); + fwrite(buf+ptr,1,item_length-2,fh); + fclose(fh); + /*emit*/ m_event(RecvRassSlidePic); + qdarmvi_show=1; } - break; - case 9: // Radio Text Status bit: - // 0 = AB-flagcontrol - // 1-4 = Transmission-Number - // 5-6 = Buffer-Config - ++state; // ignore ... - break; - case 10: - // TODO build a complete radiotext charcode to UTF8 conversion table for all character > 0x80 - switch (c) + } + if (ctrl&0x08) // delete slide + { + eDebug("delete slide id %lu, item_no %lu", id, item_no); + if (id == 0 || id >= 1000) { - case 0 ... 0x7f: break; - case 0x8d: c='ß'; break; - case 0x91: c='ä'; break; - case 0xd1: c='Ä'; break; - case 0x97: c='ö'; break; - case 0xd7: c='Ö'; break; - case 0x99: c='ü'; break; - case 0xd9: c='Ü'; break; - default: c=' '; break; // convert all unknown to space + eDebug("delete %lu", id); + removeFromPictureMask(id); + sprintf(fname,"/tmp/Rass%04d.mvi",(int)id); // was item_no ? ! ? + remove(fname); } - message[msgPtr++]=c; - if(todo) - --todo; else - ++state; - break; - case 11: - crc16=c<<8; + eDebug("ignore del interactive picture id %lu", id); + } + break; + default: //nothing more yet defined + break; + } + } + else + { + eDebug("[RDS/Rass] CRC error, skip Rass-Qdar-Item"); + } + + ptr=+item_length; + } + } + else + { + eDebug("[RDS/Rass] No Rass-QDAR archive (%02X %02X) so skipping !\n",buf[0],buf[1]); + } +} + +inline void eDVBRdsDecoder::gotAncillaryData(__u8 *buf, int len) +{ + int cnt=buf[--len]; + while ( cnt-- > 0 ) + { + unsigned char c = buf[--len]; + + if (bsflag == 1) // byte stuffing + { + bsflag=2; + switch (c) + { + case 0x00: c=0xFD; break; + case 0x01: c=0xFE; break; + case 0x02: c=0xFF; break; + } + } + + if (c == 0xFD && bsflag ==0) + bsflag=1; + else + bsflag=0; + + if (bsflag == 0) + { + if ( state == 1 ) + crc=0xFFFF; + if (( state >= 1 && state < 11 ) || ( state >=26 && state < 36 )) + crc = crc_ccitt_byte(crc, c); + + switch (state) + { + case 0: + if ( c==0xFE ) // Startkennung + state=1; + break; + case 1: // 10bit Site Address + 6bit Encoder Address + case 2: + case 3: // Sequence Counter + ++state; + break; + case 4: + leninfo=c; + ++state; + break; + case 5: + switch (c) + { + case 0x0A: // Radiotext ++state; break; - case 12: - crc16|=c; - message[msgPtr--]=0; - while(message[msgPtr] == ' ' && msgPtr > 0) - message[msgPtr--] = 0; - if ( crc16 == (crc^0xFFFF) ) + case 0x46: // Radiotext Plus tags + state=38; + break; + case 0xDA: // Rass + state=26; + break; + default: // reset to state 0 + state=0; + } + break; + + // process Radiotext + case 6: // Data Set Number ... ignore + case 7: // Program Service Number ... ignore + ++state; + break; + case 8: // Message Element Length + text_len=c; + if ( !text_len || text_len > 65 || text_len > leninfo-4) + state=0; + else + { + ++state; + text_len-=2; + msgPtr=0; + } + break; + case 9: // Radio Text Status bit: + // 0 = AB-flagcontrol + // 1-4 = Transmission-Number + // 5-6 = Buffer-Config + ++state; // ignore ... + break; + case 10: + // TODO build a complete radiotext charcode to UTF8 conversion table for all character > 0x80 + switch (c) + { + case 0 ... 0x7f: break; + case 0x8d: c='ß'; break; + case 0x91: c='ä'; break; + case 0xd1: c='Ä'; break; + case 0x97: c='ö'; break; + case 0xd7: c='Ö'; break; + case 0x99: c='ü'; break; + case 0xd9: c='Ü'; break; + default: c=' '; break; // convert all unknown to space + } + message[msgPtr++]=c; + if(text_len) + --text_len; + else + ++state; + break; + case 11: + crc16=c<<8; + ++state; + break; + case 12: + crc16|=c; + message[msgPtr--]=0; + while(message[msgPtr] == ' ' && msgPtr > 0) + message[msgPtr--] = 0; + if ( crc16 == (crc^0xFFFF) ) + { + eDebug("radiotext: (%s)", message); + /*emit*/ m_event(RadioTextChanged); + memcpy(lastmessage,message,66); + } + else + eDebug("invalid radiotext crc (%s)", message); + state=0; + break; + + // process Rass + case 26: //MEL + text_len = c; + text_len2 = c; + ++state; + text_len-=9; + text_len2-=9; + t_ptr=0; + break; + case 27: // SID not used atm + ++state; + break; + case 28: // SID not used atm + ++state; + break; + case 29: // PNR packet number + part=c<<16; + ++state; + break; + case 30: // PNR packet number + part|=c<<8; + ++state; + break; + case 31: // PNR packet number + part|=c; + ++state; + break; + case 32: // NOP number of packets + parts=c<<16; + ++state; + break; + case 33: // NOP number of packets + parts|=c<<8; + ++state; + break; + case 34: // NOP number of packets + parts|=c; + ++state; + break; + case 35: + datamessage[t_ptr++]=c; + if(text_len) + --text_len; + else + ++state; + break; + case 36: + crc16=c<<8; + ++state; + break; + case 37: + crc16|=c; + //eDebug("[RDS/Rass] CRC read: %04X CRC calculated: %04X",crc16,crc^0xFFFF); + state=0; + if ( crc16 == (crc^0xFFFF) ) + { + if (partcnt == -1) + partcnt=1; + if (partcnt == part) + { + memcpy(qdar+qdar_pos,datamessage,text_len2+1); + qdar_pos=qdar_pos+text_len2+1; + if (partcnt == parts) { - eDebug("radiotext: (%s)", message); - /*emit*/ m_updated_radiotext(); + process_qdar(qdar); // decode qdar archive + qdar_pos=0; + partcnt=-1; } else - eDebug("invalid radiotext crc (%s)", message); - state=0; - break; + ++partcnt; + } + else + { + qdar_pos=0; + partcnt=-1; + } } - } - p1=ptr; - p2=-1; + else + { + eDebug("[RDS/Rass] CRC error, skip Rass-Qdar-Packet"); + eDebug("[RDS/Rass] CRC read: %04X CRC calculated: %04X",crc16,crc^0xFFFF); + partcnt=-1; + } + state=0; + break; + + // process RT plus tags ... + case 38: // Message Element Length + text_len=c; + ++state; + break; + case 39: // Application ID + case 40: // always 0x4BD7 so we ignore it ;) + case 41: // Applicationgroup Typecode/PTY ... ignore + ++state; + break; + case 42: + rtp_buf[0]=c; + ++state; + break; + case 43: + rtp_buf[1]=c; + ++state; + break; + case 44: + rtp_buf[2]=c; + ++state; + break; + case 45: + rtp_buf[3]=c; + ++state; + break; + case 46: // bit 10#4 = Item Togglebit + // bit 10#3 = Item Runningbit + // Tag1: bit 10#2..11#5 = Contenttype, 11#4..12#7 = Startmarker, 12#6..12#1 = Length + rtp_buf[4]=c; + if (lastmessage[0] == 0) // no rds message till now ? quit ... + break; + int rtp_typ[2],rtp_start[2],rtp_len[2]; + rtp_typ[0] = (0x38 & rtp_buf[0]<<3) | rtp_buf[1]>>5; + rtp_start[0] = (0x3e & rtp_buf[1]<<1) | rtp_buf[2]>>7; + rtp_len[0] = 0x3f & rtp_buf[2]>>1; + // Tag2: bit 12#0..13#3 = Contenttype, 13#2..14#5 = Startmarker, 14#4..14#0 = Length(5bit) + rtp_typ[1] = (0x20 & rtp_buf[2]<<5) | rtp_buf[3]>>3; + rtp_start[1] = (0x38 & rtp_buf[3]<<3) | rtp_buf[4]>>5; + rtp_len[1] = 0x1f & rtp_buf[4]; + + unsigned char rtplus_osd_tmp[64]; + + memcpy(rtp_item[rtp_typ[0]],lastmessage+rtp_start[0],rtp_len[0]+1); + rtp_item[rtp_typ[0]][rtp_len[0]+1]=0; + + if (rtp_typ[0] != rtp_typ[1]) + { + memcpy(rtp_item[rtp_typ[1]],lastmessage+rtp_start[1],rtp_len[1]+1); + rtp_item[rtp_typ[1]][rtp_len[1]+1]=0; + } + + // main RTPlus item_types used by the radio stations: + // 1 title + // 4 artist + // 24 info.date_time + // 31 stationname + // 32 program.now + // 39 homepage + // 41 phone.hotline + // 46 email.hotline + // todo: make a window to display all saved items ... + + //create RTPlus OSD for title/artist + rtplus_osd[0]=0; + + if ( rtp_item[4][0] != 0 )//artist + sprintf((char*)rtplus_osd_tmp," (%s)",rtp_item[4]); + + if ( rtp_item[1][0] != 0 )//title + sprintf((char*)rtplus_osd,"%s%s",rtp_item[1],rtplus_osd_tmp); + + if ( rtplus_osd[0] != 0 ) + { + /*emit*/ m_event(RtpTextChanged); + eDebug("RTPlus: %s",rtplus_osd); + } + + state=0; + break; } - ++ptr; } - if (p1 != -1 && (128-p1) != 128) - { - bytesread=ptr=128-p1; - memcpy(buf, buf+p1, ptr); - p1=0; - } - else - bytesread=ptr=0; } } -int eDVBRadioTextParser::start(int pid) +std::string eDVBRdsDecoder::getRassPicture(int page, int subpage) +{ + int val=0; + + switch(subpage) + { + case 0: + val=page*1000; + break; + case 1: + val=page*1100; + break; + case 2: + val=page*1110; + break; + case 3: + val=page*1111; + break; + } + char fname[50]; + sprintf(fname,"/tmp/Rass%04d.mvi",val); + return fname; +} + +int eDVBRdsDecoder::start(int pid) { int ret = -1; if (m_pes_reader && !(ret = m_pes_reader->start(pid))) @@ -268,9 +637,20 @@ int eDVBRadioTextParser::start(int pid) return ret; } -void eDVBRadioTextParser::abortNonAvail() +void eDVBRdsDecoder::abortNonAvail() { eDebug("no ancillary data in audio stream... abort radiotext pes parser"); if (m_pes_reader) m_pes_reader->stop(); } + +ePyObject eDVBRdsDecoder::getRassPictureMask() +{ + ePyObject ret = PyTuple_New(5); + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong(rass_picture_mask[0])); + PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(rass_picture_mask[1])); + PyTuple_SET_ITEM(ret, 2, PyInt_FromLong(rass_picture_mask[2])); + PyTuple_SET_ITEM(ret, 3, PyInt_FromLong(rass_picture_mask[3])); + PyTuple_SET_ITEM(ret, 4, PyInt_FromLong(rass_picture_mask[4])); + return ret; +} diff --git a/lib/dvb/radiotext.h b/lib/dvb/radiotext.h index b4ae0e2..8c354ff 100644 --- a/lib/dvb/radiotext.h +++ b/lib/dvb/radiotext.h @@ -6,24 +6,37 @@ #include #include -class eDVBRadioTextParser: public iObject, public ePESParser, public Object +class eDVBRdsDecoder: public iObject, public ePESParser, public Object { - DECLARE_REF(eDVBRadioTextParser); - int bytesread, ptr, p1, p2, msgPtr; - unsigned char buf[128], message[66], leninfo, todo, state; + DECLARE_REF(eDVBRdsDecoder); + int msgPtr, bsflag, qdar_pos, t_ptr, qdarmvi_show; + unsigned char message[66], lastmessage[66], datamessage[256], rtp_buf[5], leninfo, text_len, text_len2, state; + unsigned char rtp_item[64][64], rtplus_osd[64]; //rtp + unsigned char qdar[60*1024]; //60 kB for holding Rass qdar archive unsigned short crc16, crc; + long part, parts, partcnt; + enum { RadioTextChanged, RtpTextChanged, RassInteractivePicMaskChanged, RecvRassSlidePic }; + unsigned char rass_picture_mask[5]; // 40 bits... (10 * 4 pictures) + void addToPictureMask(int id); + void removeFromPictureMask(int id); public: - eDVBRadioTextParser(iDVBDemux *demux); + eDVBRdsDecoder(iDVBDemux *demux); + ~eDVBRdsDecoder(); int start(int pid); - void connectUpdatedRadiotext(const Slot0 &slot, ePtr &connection); - const char *getCurrentText() { return msgPtr ? (const char*)message : ""; } + void connectEvent(const Slot1 &slot, ePtr &connection); + const char *getRadioText() { return (const char*)message; } + const char *getRtpText() { return (const char*)rtplus_osd; } + ePyObject getRassPictureMask(); + std::string getRassPicture(int page, int subpage); + std::string getRassSlideshowPicture() { return "/tmp/RassLast.mvi"; } private: void abortNonAvail(); void processPESPacket(__u8 *pkt, int len); - inline void gotAncillaryByte(__u8 data); + inline void gotAncillaryData(__u8 *data, int len); + void process_qdar(unsigned char*); ePtr m_pes_reader; ePtr m_read_connection; - Signal0 m_updated_radiotext; + Signal1 m_event; eTimer m_abortTimer; }; diff --git a/lib/python/Components/Converter/Makefile.am b/lib/python/Components/Converter/Makefile.am index b25a32d..a5d95f5 100644 --- a/lib/python/Components/Converter/Makefile.am +++ b/lib/python/Components/Converter/Makefile.am @@ -3,5 +3,5 @@ installdir = $(LIBDIR)/enigma2/python/Components/Converter install_PYTHON = \ __init__.py ClockToText.py Converter.py EventName.py StaticText.py EventTime.py \ Poll.py RemainingToText.py StringList.py ServiceName.py FrontendInfo.py ServiceInfo.py \ - ConditionalShowHide.py ServicePosition.py ValueRange.py RadioText.py Streaming.py + ConditionalShowHide.py ServicePosition.py ValueRange.py RdsInfo.py Streaming.py diff --git a/lib/python/Components/Converter/RadioText.py b/lib/python/Components/Converter/RadioText.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/python/Components/Converter/RdsInfo.py b/lib/python/Components/Converter/RdsInfo.py new file mode 100644 index 0000000..3a7b2be --- /dev/null +++ b/lib/python/Components/Converter/RdsInfo.py @@ -0,0 +1,53 @@ +from enigma import iRdsDecoder, iPlayableService +from Components.Converter.Converter import Converter +from Components.Element import cached + +class RdsInfo(Converter, object): + RASS_INTERACTIVE_AVAILABLE = 0 + RTP_TEXT_CHANGED = 1 + RADIO_TEXT_CHANGED = 2 + + def __init__(self, type): + Converter.__init__(self, type) + self.type = { + "RadioText": self.RADIO_TEXT_CHANGED, + "RtpText": self.RTP_TEXT_CHANGED, + "RasInteractiveAvailable": self.RASS_INTERACTIVE_AVAILABLE + }[type] + + self.interesting_events = { + self.RADIO_TEXT_CHANGED: [iPlayableService.evUpdatedRadioText], + self.RTP_TEXT_CHANGED: [iPlayableService.evUpdatedRtpText], + self.RASS_INTERACTIVE_AVAILABLE: [iPlayableService.evUpdatedRassInteractivePicMask] + }[self.type] + + @cached + def getText(self): + decoder = self.source.decoder + text = "" + if decoder: + if self.type == self.RADIO_TEXT_CHANGED: + text = decoder.getText(iRdsDecoder.RadioText) + elif self.type == self.RTP_TEXT_CHANGED: + text = decoder.getText(iRdsDecoder.RtpText) + else: + print "unknown RdsInfo Converter type", self.type + return text + + text = property(getText) + + @cached + def getBoolean(self): + decoder = self.source.decoder + if self.type == self.RASS_INTERACTIVE_AVAILABLE: + mask = decoder and decoder.getRassInteractiveMask() + return (mask and mask[0] & 1 and True) or False + elif self.type == self.RADIO_TEXT_CHANGED: + return (len(decoder.getText(iRdsDecoder.RadioText)) and True) or False + elif self.type == self.RTP_TEXT_CHANGED: + return (len(decoder.getText(iRdsDecoder.RtpText)) and True) or False + boolean = property(getBoolean) + + def changed(self, what): + if what[0] != self.CHANGED_SPECIFIC or what[1] in self.interesting_events: + Converter.changed(self, what) diff --git a/lib/python/Components/Sources/Makefile.am b/lib/python/Components/Sources/Makefile.am index 5e6a30a..b5e16d98 100644 --- a/lib/python/Components/Sources/Makefile.am +++ b/lib/python/Components/Sources/Makefile.am @@ -2,5 +2,5 @@ installdir = $(LIBDIR)/enigma2/python/Components/Sources install_PYTHON = \ __init__.py Clock.py EventInfo.py Source.py MenuList.py CurrentService.py \ - FrontendStatus.py Boolean.py Config.py ServiceList.py RadioText.py StreamService.py \ + FrontendStatus.py Boolean.py Config.py ServiceList.py RdsDecoder.py StreamService.py \ StaticText.py diff --git a/lib/python/Components/Sources/RadioText.py b/lib/python/Components/Sources/RadioText.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/python/Components/Sources/RdsDecoder.py b/lib/python/Components/Sources/RdsDecoder.py new file mode 100644 index 0000000..886f81f --- /dev/null +++ b/lib/python/Components/Sources/RdsDecoder.py @@ -0,0 +1,29 @@ +from Components.PerServiceDisplay import PerServiceBase +from Components.Element import cached +from enigma import iPlayableService +from Source import Source + +class RdsDecoder(PerServiceBase, Source, object): + def __init__(self, navcore): + Source.__init__(self) + PerServiceBase.__init__(self, navcore, + { + iPlayableService.evStart: self.gotEvent, + iPlayableService.evUpdatedRadioText: self.gotEvent, + iPlayableService.evUpdatedRtpText: self.gotEvent, + iPlayableService.evUpdatedRassInteractivePicMask: self.gotEvent, + iPlayableService.evEnd: self.gotEvent + }, with_event=True) + + @cached + def getDecoder(self): + service = self.navcore.getCurrentService() + return service and service.rdsDecoder() + + decoder = property(getDecoder) + + def gotEvent(self, what): + if what in [iPlayableService.evStart, iPlayableService.evEnd]: + self.changed((self.CHANGED_CLEAR,)) + else: + self.changed((self.CHANGED_SPECIFIC, what)) diff --git a/lib/python/Screens/ChannelSelection.py b/lib/python/Screens/ChannelSelection.py index 5a0f117..7f9f797 100644 --- a/lib/python/Screens/ChannelSelection.py +++ b/lib/python/Screens/ChannelSelection.py @@ -1,7 +1,7 @@ from Screen import Screen from Components.Button import Button from Components.ServiceList import ServiceList -from Components.ActionMap import NumberActionMap, ActionMap +from Components.ActionMap import NumberActionMap, ActionMap, HelpableActionMap from Components.MenuList import MenuList from Components.ServiceEventTracker import ServiceEventTracker from EpgSelection import EPGSelection @@ -11,11 +11,13 @@ from Screens.FixedMenu import FixedMenu from Tools.NumericalTextInput import NumericalTextInput from Components.NimManager import nimmanager from Components.Sources.Clock import Clock +from Components.Sources.RdsDecoder import RdsDecoder from Components.Input import Input from Components.ParentalControl import parentalControl from Screens.InputBox import InputBox, PinInput from Screens.MessageBox import MessageBox from Screens.ServiceInfo import ServiceInfo +from Screens.RdsDisplay import RassInteractive from ServiceReference import ServiceReference from Tools.BoundFunction import boundFunction from re import * @@ -1256,32 +1258,30 @@ class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelect self.revertMode = None self.close(None) -from Screens.InfoBarGenerics import InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord, InfoBarRadioText +from Screens.InfoBarGenerics import InfoBarEvent, InfoBarServiceName -class RadioInfoBar(Screen, InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord): +class RadioInfoBar(Screen, InfoBarEvent, InfoBarServiceName): def __init__(self, session): Screen.__init__(self, session) InfoBarEvent.__init__(self) InfoBarServiceName.__init__(self) - InfoBarInstantRecord.__init__(self) self["CurrentTime"] = Clock() + self["RdsDecoder"] = RdsDecoder(self.session.nav) -class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, InfoBarRadioText): - +class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG): ALLOW_SUSPEND = True - def __init__(self, session): + def __init__(self, session, infobar): ChannelSelectionBase.__init__(self, session) ChannelSelectionEdit.__init__(self) ChannelSelectionEPG.__init__(self) - InfoBarRadioText.__init__(self) - + self.infobar = infobar config.radio = ConfigSubsection(); config.radio.lastservice = ConfigText() config.radio.lastroot = ConfigText() self.onLayoutFinish.append(self.onCreate) - self.info = session.instantiateDialog(RadioInfoBar) + self.info = session.instantiateDialog(RadioInfoBar) # our simple infobar self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"], { @@ -1297,6 +1297,37 @@ class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelS iPlayableService.evEnd: self.__evServiceEnd }) +########## RDS Radiotext / Rass Support BEGIN + self.infobar = infobar # reference to real infobar (the one and only) + self["RdsDecoder"] = self.info["RdsDecoder"] + self["RdsActions"] = HelpableActionMap(self, "InfobarRdsActions", + { + "startRassInteractive": (self.startRassInteractive, _("View Rass interactive...")) + },-1) + self["RdsActions"].setEnabled(False) + infobar.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged) + + def startRassInteractive(self): + self.info.hide(); + self.infobar.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive) + + def RassInteractiveClosed(self): + self.info.show() + self.infobar.rass_interactive = None + self.infobar.RassSlidePicChanged() + + def RassInteractivePossibilityChanged(self, state): + self["RdsActions"].setEnabled(state) +########## RDS Radiotext / Rass Support END + + def closeRadio(self): + self.infobar.rds_display.onRassInteractivePossibilityChanged.remove(self.RassInteractivePossibilityChanged) + self.info.hide() + #set previous tv service + lastservice=eServiceReference(config.tv.lastservice.value) + self.session.nav.playService(lastservice) + self.close(None) + def __evServiceStart(self): service = self.session.nav.getCurrentService() if service: @@ -1371,13 +1402,6 @@ class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelS config.radio.lastservice.save() self.saveRoot() - def closeRadio(self): - self.info.hide() - #set previous tv service - lastservice=eServiceReference(config.tv.lastservice.value) - self.session.nav.playService(lastservice) - self.close(None) - class SimpleChannelSelection(ChannelSelectionBase): def __init__(self, session, title): ChannelSelectionBase.__init__(self, session) diff --git a/lib/python/Screens/InfoBar.py b/lib/python/Screens/InfoBar.py index 24c2b40..ab40838 100644 --- a/lib/python/Screens/InfoBar.py +++ b/lib/python/Screens/InfoBar.py @@ -12,7 +12,7 @@ from Components.config import config from Tools.Notifications import AddNotificationWithCallback from Screens.InfoBarGenerics import InfoBarShowHide, \ - InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarRadioText, \ + InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarRdsDecoder, \ InfoBarEPG, InfoBarEvent, InfoBarServiceName, InfoBarSeek, InfoBarInstantRecord, \ InfoBarAudioSelection, InfoBarAdditionalInfo, InfoBarNotifications, InfoBarDish, \ InfoBarSubserviceSelection, InfoBarTuner, InfoBarShowMovies, InfoBarTimeshift, \ @@ -23,7 +23,7 @@ from Screens.InfoBarGenerics import InfoBarShowHide, \ from Screens.HelpMenu import HelpableScreen, HelpMenu class InfoBar(InfoBarShowHide, - InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarEPG, InfoBarRadioText, + InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarEPG, InfoBarRdsDecoder, InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord, InfoBarAudioSelection, HelpableScreen, InfoBarAdditionalInfo, InfoBarNotifications, InfoBarDish, InfoBarSubserviceSelection, InfoBarTuner, InfoBarTimeshift, InfoBarSeek, @@ -47,7 +47,7 @@ class InfoBar(InfoBarShowHide, for x in HelpableScreen, \ InfoBarShowHide, \ - InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarEPG, InfoBarRadioText, \ + InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarEPG, InfoBarRdsDecoder, \ InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord, InfoBarAudioSelection, \ InfoBarAdditionalInfo, InfoBarNotifications, InfoBarDish, InfoBarSubserviceSelection, \ InfoBarTuner, InfoBarTimeshift, InfoBarSeek, InfoBarSummarySupport, InfoBarTimeshiftState, \ @@ -67,7 +67,11 @@ class InfoBar(InfoBarShowHide, if config.usage.e1like_radio_mode.value: self.showRadioChannelList(True) else: - self.session.open(ChannelSelectionRadio) + self.rds_display.hide() # in InfoBarRdsDecoder + self.session.openWithCallback(self.ChannelSelectionRadioClosed, ChannelSelectionRadio, self) + + def ChannelSelectionRadioClosed(self, *arg): + self.rds_display.show() # in InfoBarRdsDecoder def showMovies(self): self.session.openWithCallback(self.movieSelected, MovieSelection) diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py index 2ccd52d..78a67b7 100644 --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -12,7 +12,6 @@ from Components.ProgressBar import * from Components.ServiceEventTracker import ServiceEventTracker from Components.Sources.CurrentService import CurrentService from Components.Sources.EventInfo import EventInfo -from Components.Sources.RadioText import RadioText from Components.Sources.FrontendStatus import FrontendStatus from Components.Sources.Boolean import Boolean from Components.Sources.Clock import Clock @@ -32,6 +31,7 @@ from Screens.MinuteInput import MinuteInput from Screens.TimerSelection import TimerSelection from Screens.PictureInPicture import PictureInPicture from Screens.SubtitleDisplay import SubtitleDisplay +from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive from Screens.SleepTimerEdit import SleepTimerEdit from ServiceReference import ServiceReference @@ -538,10 +538,53 @@ class InfoBarEvent: self["Event_Now"] = EventInfo(self.session.nav, EventInfo.NOW) self["Event_Next"] = EventInfo(self.session.nav, EventInfo.NEXT) -class InfoBarRadioText: - """provides radio (RDS) text info display""" +class InfoBarRdsDecoder: + """provides RDS and Rass support/display""" def __init__(self): - self["RadioText"] = RadioText(self.session.nav) + self.rds_display = self.session.instantiateDialog(RdsInfoDisplay) + self.rass_interactive = None + + self.__event_tracker = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evEnd: self.__serviceStopped, + iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged + }) + + self["RdsActions"] = HelpableActionMap(self, "InfobarRdsActions", + { + "startRassInteractive": (self.startRassInteractive, _("View Rass interactive...")) + },-1) + + self["RdsActions"].setEnabled(False) + + self.onLayoutFinish.append(self.rds_display.show) + self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged) + + def RassInteractivePossibilityChanged(self, state): + self["RdsActions"].setEnabled(state) + + def RassSlidePicChanged(self): + if not self.rass_interactive: + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + if decoder: + decoder.showRassSlidePicture() + + def __serviceStopped(self): + if self.rass_interactive is not None: + rass_interactive = self.rass_interactive + self.rass_interactive = None + rass_interactive.close() + + def startRassInteractive(self): + self.rds_display.hide() + self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive) + + def RassInteractiveClosed(self, *val): + if self.rass_interactive is not None: + self.rass_interactive = None + self.RassSlidePicChanged() + self.rds_display.show() class InfoBarServiceName: def __init__(self): diff --git a/lib/python/Screens/Makefile.am b/lib/python/Screens/Makefile.am index d5512e7..920a97f 100644 --- a/lib/python/Screens/Makefile.am +++ b/lib/python/Screens/Makefile.am @@ -12,4 +12,4 @@ install_PYTHON = \ Console.py InputBox.py ChoiceBox.py SimpleSummary.py ImageWizard.py \ MediaPlayer.py TimerSelection.py PictureInPicture.py TimeDateInput.py \ SubtitleDisplay.py SubservicesQuickzap.py ParentalControlSetup.py NumericalTextInputHelpDialog.py \ - SleepTimerEdit.py Ipkg.py + SleepTimerEdit.py Ipkg.py RdsDisplay.py diff --git a/lib/python/Screens/RdsDisplay.py b/lib/python/Screens/RdsDisplay.py new file mode 100644 index 0000000..4190b94 --- /dev/null +++ b/lib/python/Screens/RdsDisplay.py @@ -0,0 +1,272 @@ +from enigma import iPlayableService, loadPNG, iRdsDecoder, ePoint, gRGB +from Screens.Screen import Screen +from Components.Sources.RdsDecoder import RdsDecoder +from Components.ActionMap import NumberActionMap +from Components.ServiceEventTracker import ServiceEventTracker +from Components.Pixmap import Pixmap +from Components.Label import Label +from Tools.Directories import resolveFilename, SCOPE_SKIN_IMAGE + +class RdsInfoDisplay(Screen): + ALLOW_SUSPEND = True + + def __init__(self, session): + Screen.__init__(self, session) + + self.__event_tracker = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evEnd: self.__serviceStopped, + iPlayableService.evUpdatedRadioText: self.RadioTextChanged, + iPlayableService.evUpdatedRtpText: self.RtpTextChanged, + iPlayableService.evUpdatedRassInteractivePicMask: self.RassInteractivePicMaskChanged, + }) + + self["RadioText"] = Label() + self["RtpText"] = Label() + self["RassLogo"] = Pixmap() + + self.onLayoutFinish.append(self.hideWidgets) + self.rassInteractivePossible=False + self.onRassInteractivePossibilityChanged = [ ] + + def hideWidgets(self): + for x in (self["RadioText"],self["RtpText"],self["RassLogo"]): + x.hide() + + def RadioTextChanged(self): + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + rdsText = decoder and decoder.getText(iRdsDecoder.RadioText) + if rdsText and len(rdsText): + self["RadioText"].setText(rdsText) + self["RadioText"].show() + else: + self["RadioText"].hide() + + def RtpTextChanged(self): + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + rtpText = decoder and decoder.getText(iRdsDecoder.RtpText) + if rtpText and len(rtpText): + self["RtpText"].setText(rtpText) + self["RtpText"].show() + else: + self["RtpText"].hide() + + def RassInteractivePicMaskChanged(self): + if not self.rassInteractivePossible: + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + mask = decoder and decoder.getRassInteractiveMask() + if mask[0] & 1: #rass interactive index page available + self["RassLogo"].show() + self.rassInteractivePossible = True + for x in self.onRassInteractivePossibilityChanged: + x(True) + + def __serviceStopped(self): + self.hideWidgets() + if self.rassInteractivePossible: + self.rassInteractivePossible = False + for x in self.onRassInteractivePossibilityChanged: + x(False) + +class RassInteractive(Screen): + def __init__(self, session): + Screen.__init__(self, session) + + self["actions"] = NumberActionMap( [ "NumberActions", "RassInteractiveActions" ], + { + "exit": self.close, + "0": lambda x : self.numPressed(0), + "1": lambda x : self.numPressed(1), + "2": lambda x : self.numPressed(2), + "3": lambda x : self.numPressed(3), + "4": lambda x : self.numPressed(4), + "5": lambda x : self.numPressed(5), + "6": lambda x : self.numPressed(6), + "7": lambda x : self.numPressed(7), + "8": lambda x : self.numPressed(8), + "9": lambda x : self.numPressed(9), + "nextPage": self.nextPage, + "prevPage": self.prevPage, + "nextSubPage": self.nextSubPage, + "prevSubPage": self.prevSubPage + }) + + self.__event_tracker = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evUpdatedRassInteractivePicMask: self.recvRassInteractivePicMaskChanged + }) + + self["subpages_1"] = Pixmap() + self["subpages_2"] = Pixmap() + self["subpages_3"] = Pixmap() + self["subpages_4"] = Pixmap() + self["subpages_5"] = Pixmap() + self["subpages_6"] = Pixmap() + self["subpages_7"] = Pixmap() + self["subpages_8"] = Pixmap() + self["subpages_9"] = Pixmap() + self["Marker"] = Label(">") + + self.subpage = { + 1 : self["subpages_1"], + 2 : self["subpages_2"], + 3 : self["subpages_3"], + 4 : self["subpages_4"], + 5 : self["subpages_5"], + 6 : self["subpages_6"], + 7 : self["subpages_7"], + 8 : self["subpages_8"], + 9 : self["subpages_9"] } + + self.subpage_png = { + 1 : loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "rass_page1.png")), + 2 : loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "rass_page2.png")), + 3 : loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "rass_page3.png")), + 4 : loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "rass_page4.png")) } + + self.current_page=0; + self.current_subpage=0; + self.showRassPage(0,0) + self.onLayoutFinish.append(self.updateSubPagePixmaps) + + def updateSubPagePixmaps(self): + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + if not decoder: # this should never happen + print "NO RDS DECODER in showRassPage" + else: + mask = decoder.getRassInteractiveMask() + page = 1 + while page < 10: + subpage_cnt = self.countAvailSubpages(page, mask) + subpage = self.subpage[page] + if subpage_cnt > 0: + if subpage.instance: + png = self.subpage_png[subpage_cnt] + if png: + subpage.instance.setPixmap(png) + subpage.show() + else: + print "rass png missing" + else: + subpage.hide() + page += 1 + + def recvRassInteractivePicMaskChanged(self): + self.updateSubPagePixmaps() + + def showRassPage(self, page, subpage): + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + if not decoder: # this should never happen + print "NO RDS DECODER in showRassPage" + else: + decoder.showRassInteractivePic(page, subpage) + page_diff = page - self.current_page + self.current_page = page + if page_diff: + current_pos = self["Marker"].getPosition() + y = current_pos[1] + y += page_diff * 25 + self["Marker"].setPosition(current_pos[0],y) + + def getMaskForPage(self, page, masks=None): + if not masks: + service = self.session.nav.getCurrentService() + decoder = service and service.rdsDecoder() + if not decoder: # this should never happen + print "NO RDS DECODER in getMaskForPage" + masks = decoder.getRassInteractiveMask() + if masks: + mask = masks[(page*4)/8] + if page % 2: + mask >>= 4 + else: + mask &= 0xF + return mask + + def countAvailSubpages(self, page, masks): + mask = self.getMaskForPage(page, masks) + cnt = 0 + while mask: + if mask & 1: + cnt += 1 + mask >>= 1 + return cnt + + def nextPage(self): + mask = 0 + page = self.current_page + while mask == 0: + page += 1 + if page > 9: + page = 0 + mask = self.getMaskForPage(page) + self.numPressed(page) + + def prevPage(self): + mask = 0 + page = self.current_page + while mask == 0: + if page > 0: + page -= 1 + else: + page = 9 + mask = self.getMaskForPage(page) + self.numPressed(page) + + def nextSubPage(self): + self.numPressed(self.current_page) + + def prevSubPage(self): + num = self.current_page + mask = self.getMaskForPage(num) + cur_bit = 1 << self.current_subpage + tmp = cur_bit + while True: + if tmp == 1: + tmp = 8 + else: + tmp >>= 1 + if tmp == cur_bit: # no other subpage avail + return + if mask & tmp: # next subpage found + subpage = 0 + while tmp > 1: # convert bit to subpage + subpage += 1 + tmp >>= 1 + self.current_subpage = subpage + self.showRassPage(num, subpage) + return + + def numPressed(self, num): + mask = self.getMaskForPage(num) + if self.current_page == num: + self.skip = 0 + cur_bit = 1 << self.current_subpage + tmp = cur_bit + else: + self.skip = 1 + cur_bit = 16 + tmp = 1 + while True: + if not self.skip: + if tmp == 8 and cur_bit < 16: + tmp = 1 + else: + tmp <<= 1 + else: + self.skip = 0 + if tmp == cur_bit: # no other subpage avail + return + if mask & tmp: # next subpage found + subpage = 0 + while tmp > 1: # convert bit to subpage + subpage += 1 + tmp >>= 1 + self.current_subpage = subpage + self.showRassPage(num, subpage) + return diff --git a/lib/service/iservice.h b/lib/service/iservice.h index feaa771..df4e302 100644 --- a/lib/service/iservice.h +++ b/lib/service/iservice.h @@ -483,17 +483,30 @@ public: }; SWIG_TEMPLATE_TYPEDEF(ePtr, iAudioDelayPtr); -SWIG_IGNORE(iRadioText); -class iRadioText: public iObject +class iRdsDecoder_ENUMS { #ifdef SWIG - iRadioText(); - ~iRadioText(); + iRdsDecoder_ENUMS(); + ~iRdsDecoder_ENUMS(); #endif public: - virtual std::string getRadioText(int x=0)=0; + enum { RadioText, RtpText }; }; -SWIG_TEMPLATE_TYPEDEF(ePtr, iRadioTextPtr); + +SWIG_IGNORE(iRdsDecoder); +class iRdsDecoder: public iObject, public iRdsDecoder_ENUMS +{ +#ifdef SWIG + iRdsDecoder(); + ~iRdsDecoder(); +#endif +public: + virtual std::string getText(int x=RadioText)=0; + virtual void showRassSlidePicture()=0; + virtual void showRassInteractivePic(int page, int subpage)=0; + virtual SWIG_PYOBJECT(ePyObject) getRassInteractiveMask()=0; +}; +SWIG_TEMPLATE_TYPEDEF(ePtr, iRdsDecoderPtr); SWIG_IGNORE(iSubserviceList); class iSubserviceList: public iObject @@ -676,8 +689,13 @@ public: /* when cueSheet is implemented */ evCuesheetChanged, - /* when radioText is implemented */ + /* when rdsDecoder is implemented */ evUpdatedRadioText, + evUpdatedRtpText, + + /* Radio Screenshow Support */ + evUpdatedRassSlidePic, + evUpdatedRassInteractivePicMask, evVideoSizeChanged, @@ -712,7 +730,7 @@ public: virtual SWIG_VOID(RESULT) cueSheet(ePtr &SWIG_OUTPUT)=0; virtual SWIG_VOID(RESULT) subtitle(ePtr &SWIG_OUTPUT)=0; virtual SWIG_VOID(RESULT) audioDelay(ePtr &SWIG_OUTPUT)=0; - virtual SWIG_VOID(RESULT) radioText(ePtr &SWIG_OUTPUT)=0; + virtual SWIG_VOID(RESULT) rdsDecoder(ePtr &SWIG_OUTPUT)=0; }; SWIG_TEMPLATE_TYPEDEF(ePtr, iPlayableServicePtr); diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 9d9f2d1..e47ae52 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -1475,7 +1475,7 @@ RESULT eDVBServicePlay::audioDelay(ePtr &ptr) return 0; } -RESULT eDVBServicePlay::radioText(ePtr &ptr) +RESULT eDVBServicePlay::rdsDecoder(ePtr &ptr) { ptr = this; return 0; @@ -1705,8 +1705,8 @@ int eDVBServicePlay::selectAudioStream(int i) if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type)) return -4; - if (m_radiotext_parser) - m_radiotext_parser->start(program.audioStreams[i].pid); + if (m_rds_decoder) + m_rds_decoder->start(program.audioStreams[i].pid); if (m_dvb_service && !m_is_pvr) { @@ -1743,20 +1743,81 @@ RESULT eDVBServicePlay::selectChannel(int i) return 0; } -std::string eDVBServicePlay::getRadioText(int x) +std::string eDVBServicePlay::getText(int x) { - if (m_radiotext_parser) + if (m_rds_decoder) switch(x) { - case 0: - return convertLatin1UTF8(m_radiotext_parser->getCurrentText()); + case RadioText: + return convertLatin1UTF8(m_rds_decoder->getRadioText()); + case RtpText: + return convertLatin1UTF8(m_rds_decoder->getRtpText()); } return ""; } -void eDVBServicePlay::radioTextUpdated() +void eDVBServicePlay::rdsDecoderEvent(int what) { - m_event((iPlayableService*)this, evUpdatedRadioText); + switch(what) + { + case eDVBRdsDecoder::RadioTextChanged: + m_event((iPlayableService*)this, evUpdatedRadioText); + break; + case eDVBRdsDecoder::RtpTextChanged: + m_event((iPlayableService*)this, evUpdatedRtpText); + break; + case eDVBRdsDecoder::RassInteractivePicMaskChanged: + m_event((iPlayableService*)this, evUpdatedRassInteractivePicMask); + break; + case eDVBRdsDecoder::RecvRassSlidePic: + m_event((iPlayableService*)this, evUpdatedRassSlidePic); + break; + } +} + +void eDVBServicePlay::showRassSlidePicture() +{ + if (m_rds_decoder) + { + if (m_decoder) + { + std::string rass_slide_pic = m_rds_decoder->getRassSlideshowPicture(); + if (rass_slide_pic.length()) + m_decoder->showSinglePic(rass_slide_pic.c_str()); + else + eDebug("empty filename for rass slide picture received!!"); + } + else + eDebug("no MPEG Decoder to show iframes avail"); + } + else + eDebug("showRassSlidePicture called.. but not decoder"); +} + +void eDVBServicePlay::showRassInteractivePic(int page, int subpage) +{ + if (m_rds_decoder) + { + if (m_decoder) + { + std::string rass_interactive_pic = m_rds_decoder->getRassPicture(page, subpage); + if (rass_interactive_pic.length()) + m_decoder->showSinglePic(rass_interactive_pic.c_str()); + else + eDebug("empty filename for rass interactive picture %d/%d received!!", page, subpage); + } + else + eDebug("no MPEG Decoder to show iframes avail"); + } + else + eDebug("showRassInteractivePic called.. but not decoder"); +} + +ePyObject eDVBServicePlay::getRassInteractiveMask() +{ + if (m_rds_decoder) + return m_rds_decoder->getRassPictureMask(); + Py_RETURN_NONE; } int eDVBServiceBase::getFrontendInfo(int w) @@ -2089,11 +2150,11 @@ void eDVBServicePlay::switchToLive() m_decoder = 0; m_decode_demux = 0; m_teletext_parser = 0; - m_radiotext_parser = 0; + m_rds_decoder = 0; m_subtitle_parser = 0; m_new_dvb_subtitle_page_connection = 0; m_new_subtitle_page_connection = 0; - m_radiotext_updated_connection = 0; + m_rds_decoder_event_connection = 0; m_video_event_connection = 0; /* free the timeshift service handler, we need the resources */ @@ -2113,11 +2174,11 @@ void eDVBServicePlay::switchToTimeshift() m_decode_demux = 0; m_decoder = 0; m_teletext_parser = 0; - m_radiotext_parser = 0; + m_rds_decoder = 0; m_subtitle_parser = 0; m_new_subtitle_page_connection = 0; m_new_dvb_subtitle_page_connection = 0; - m_radiotext_updated_connection = 0; + m_rds_decoder_event_connection = 0; m_video_event_connection = 0; m_timeshift_active = 1; @@ -2259,9 +2320,9 @@ void eDVBServicePlay::updateDecoder() ePtr data_demux; if (!h.getDataDemux(data_demux)) { - m_radiotext_parser = new eDVBRadioTextParser(data_demux); - m_radiotext_parser->connectUpdatedRadiotext(slot(*this, &eDVBServicePlay::radioTextUpdated), m_radiotext_updated_connection); - m_radiotext_parser->start(apid); + m_rds_decoder = new eDVBRdsDecoder(data_demux); + m_rds_decoder->connectEvent(slot(*this, &eDVBServicePlay::rdsDecoderEvent), m_rds_decoder_event_connection); + m_rds_decoder->start(apid); } } } diff --git a/lib/service/servicedvb.h b/lib/service/servicedvb.h index 34ed972..7c785c4 100644 --- a/lib/service/servicedvb.h +++ b/lib/service/servicedvb.h @@ -89,7 +89,7 @@ class eDVBServicePlay: public eDVBServiceBase, public iAudioTrackSelection, public iAudioChannelSelection, public iSubserviceList, public iTimeshiftService, public iCueSheet, public iSubtitleOutput, public iAudioDelay, - public iRadioText + public iRdsDecoder { DECLARE_REF(eDVBServicePlay); public: @@ -112,7 +112,7 @@ public: RESULT cueSheet(ePtr &ptr); RESULT subtitle(ePtr &ptr); RESULT audioDelay(ePtr &ptr); - RESULT radioText(ePtr &ptr); + RESULT rdsDecoder(ePtr &ptr); // iPauseableService RESULT pause(); @@ -144,8 +144,11 @@ public: int getCurrentChannel(); RESULT selectChannel(int i); - // iRadioText - std::string getRadioText(int i=0); + // iRdsDecoder + std::string getText(int i=0); + void showRassSlidePicture(); + void showRassInteractivePic(int page, int subpage); + ePyObject getRassInteractiveMask(); // iSubserviceList int getNumberOfSubservices(); @@ -267,9 +270,9 @@ private: void checkSubtitleTiming(); /* radiotext */ - ePtr m_radiotext_parser; - ePtr m_radiotext_updated_connection; - void radioTextUpdated(); + ePtr m_rds_decoder; + ePtr m_rds_decoder_event_connection; + void rdsDecoderEvent(int); ePtr m_video_event_connection; void video_event(struct iTSMPEGDecoder::videoEvent); diff --git a/lib/service/servicemp3.h b/lib/service/servicemp3.h index 4245c81..4996488 100644 --- a/lib/service/servicemp3.h +++ b/lib/service/servicemp3.h @@ -66,7 +66,7 @@ public: RESULT cueSheet(ePtr &ptr) { ptr = 0; return -1; } RESULT subtitle(ePtr &ptr) { ptr = 0; return -1; } RESULT audioDelay(ePtr &ptr) { ptr = 0; return -1; } - RESULT radioText(ePtr &ptr) { ptr = 0; return -1; } + RESULT rdsDecoder(ePtr &ptr) { ptr = 0; return -1; } // iPausableService RESULT pause(); diff --git a/lib/service/servicexine.h b/lib/service/servicexine.h index 4a50b11..b56a485 100644 --- a/lib/service/servicexine.h +++ b/lib/service/servicexine.h @@ -69,7 +69,7 @@ public: RESULT cueSheet(ePtr &ptr) { ptr = 0; return -1; } RESULT subtitle(ePtr &ptr) { ptr = 0; return -1; } RESULT audioDelay(ePtr &ptr) { ptr = 0; return -1; } - RESULT radioText(ePtr &ptr) { ptr = 0; return -1; } + RESULT rdsDecoder(ePtr &ptr) { ptr = 0; return -1; } // iPausableService RESULT pause();