diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/dvb/radiotext.cpp | 608 | ||||
| -rw-r--r-- | lib/dvb/radiotext.h | 31 | ||||
| -rw-r--r-- | lib/python/Components/Converter/Makefile.am | 2 | ||||
| -rw-r--r-- | lib/python/Components/Converter/RadioText.py | 0 | ||||
| -rw-r--r-- | lib/python/Components/Converter/RdsInfo.py | 53 | ||||
| -rw-r--r-- | lib/python/Components/Sources/Makefile.am | 2 | ||||
| -rw-r--r-- | lib/python/Components/Sources/RadioText.py | 0 | ||||
| -rw-r--r-- | lib/python/Components/Sources/RdsDecoder.py | 29 | ||||
| -rw-r--r-- | lib/python/Screens/ChannelSelection.py | 58 | ||||
| -rw-r--r-- | lib/python/Screens/InfoBar.py | 12 | ||||
| -rw-r--r-- | lib/python/Screens/InfoBarGenerics.py | 51 | ||||
| -rw-r--r-- | lib/python/Screens/Makefile.am | 2 | ||||
| -rw-r--r-- | lib/python/Screens/RdsDisplay.py | 272 | ||||
| -rw-r--r-- | lib/service/iservice.h | 34 | ||||
| -rw-r--r-- | lib/service/servicedvb.cpp | 93 | ||||
| -rw-r--r-- | lib/service/servicedvb.h | 17 | ||||
| -rw-r--r-- | lib/service/servicemp3.h | 2 | ||||
| -rw-r--r-- | lib/service/servicexine.h | 2 |
18 files changed, 1084 insertions, 184 deletions
diff --git a/lib/dvb/radiotext.cpp b/lib/dvb/radiotext.cpp index 87498da9..eff40e92 100644 --- a/lib/dvb/radiotext.cpp +++ b/lib/dvb/radiotext.cpp @@ -3,19 +3,45 @@ #include <lib/dvb/idemux.h> #include <lib/gdi/gpixmap.h> -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<void> &slot, ePtr<eConnection> &connection) +void eDVBRdsDecoder::connectEvent(const Slot1<void, int> &slot, ePtr<eConnection> &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 b4ae0e26..8c354ff1 100644 --- a/lib/dvb/radiotext.h +++ b/lib/dvb/radiotext.h @@ -6,24 +6,37 @@ #include <lib/dvb/pesparse.h> #include <lib/gdi/gpixmap.h> -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<void> &slot, ePtr<eConnection> &connection); - const char *getCurrentText() { return msgPtr ? (const char*)message : ""; } + void connectEvent(const Slot1<void, int> &slot, ePtr<eConnection> &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<iDVBPESReader> m_pes_reader; ePtr<eConnection> m_read_connection; - Signal0<void> m_updated_radiotext; + Signal1<void, int> m_event; eTimer m_abortTimer; }; diff --git a/lib/python/Components/Converter/Makefile.am b/lib/python/Components/Converter/Makefile.am index b25a32d8..a5d95f53 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 e69de29b..00000000 --- a/lib/python/Components/Converter/RadioText.py +++ /dev/null diff --git a/lib/python/Components/Converter/RdsInfo.py b/lib/python/Components/Converter/RdsInfo.py new file mode 100644 index 00000000..3a7b2be3 --- /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 5e6a30a5..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 e69de29b..00000000 --- a/lib/python/Components/Sources/RadioText.py +++ /dev/null diff --git a/lib/python/Components/Sources/RdsDecoder.py b/lib/python/Components/Sources/RdsDecoder.py new file mode 100644 index 00000000..886f81f6 --- /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 5a0f1177..7f9f797e 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 24c2b40b..ab40838d 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 2ccd52de..78a67b7c 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 d5512e7e..920a97fb 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 00000000..4190b945 --- /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 feaa771e..df4e302f 100644 --- a/lib/service/iservice.h +++ b/lib/service/iservice.h @@ -483,17 +483,30 @@ public: }; SWIG_TEMPLATE_TYPEDEF(ePtr<iAudioDelay>, 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<iRadioText>, 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<iRdsDecoder>, 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<iCueSheet> &SWIG_OUTPUT)=0; virtual SWIG_VOID(RESULT) subtitle(ePtr<iSubtitleOutput> &SWIG_OUTPUT)=0; virtual SWIG_VOID(RESULT) audioDelay(ePtr<iAudioDelay> &SWIG_OUTPUT)=0; - virtual SWIG_VOID(RESULT) radioText(ePtr<iRadioText> &SWIG_OUTPUT)=0; + virtual SWIG_VOID(RESULT) rdsDecoder(ePtr<iRdsDecoder> &SWIG_OUTPUT)=0; }; SWIG_TEMPLATE_TYPEDEF(ePtr<iPlayableService>, iPlayableServicePtr); diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 9d9f2d1f..e47ae52c 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -1475,7 +1475,7 @@ RESULT eDVBServicePlay::audioDelay(ePtr<iAudioDelay> &ptr) return 0; } -RESULT eDVBServicePlay::radioText(ePtr<iRadioText> &ptr) +RESULT eDVBServicePlay::rdsDecoder(ePtr<iRdsDecoder> &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<iDVBDemux> 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 34ed9720..7c785c4e 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<iCueSheet> &ptr); RESULT subtitle(ePtr<iSubtitleOutput> &ptr); RESULT audioDelay(ePtr<iAudioDelay> &ptr); - RESULT radioText(ePtr<iRadioText> &ptr); + RESULT rdsDecoder(ePtr<iRdsDecoder> &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<eDVBRadioTextParser> m_radiotext_parser; - ePtr<eConnection> m_radiotext_updated_connection; - void radioTextUpdated(); + ePtr<eDVBRdsDecoder> m_rds_decoder; + ePtr<eConnection> m_rds_decoder_event_connection; + void rdsDecoderEvent(int); ePtr<eConnection> m_video_event_connection; void video_event(struct iTSMPEGDecoder::videoEvent); diff --git a/lib/service/servicemp3.h b/lib/service/servicemp3.h index 4245c816..49964883 100644 --- a/lib/service/servicemp3.h +++ b/lib/service/servicemp3.h @@ -66,7 +66,7 @@ public: RESULT cueSheet(ePtr<iCueSheet> &ptr) { ptr = 0; return -1; } RESULT subtitle(ePtr<iSubtitleOutput> &ptr) { ptr = 0; return -1; } RESULT audioDelay(ePtr<iAudioDelay> &ptr) { ptr = 0; return -1; } - RESULT radioText(ePtr<iRadioText> &ptr) { ptr = 0; return -1; } + RESULT rdsDecoder(ePtr<iRdsDecoder> &ptr) { ptr = 0; return -1; } // iPausableService RESULT pause(); diff --git a/lib/service/servicexine.h b/lib/service/servicexine.h index 4a50b11f..b56a4858 100644 --- a/lib/service/servicexine.h +++ b/lib/service/servicexine.h @@ -69,7 +69,7 @@ public: RESULT cueSheet(ePtr<iCueSheet> &ptr) { ptr = 0; return -1; } RESULT subtitle(ePtr<iSubtitleOutput> &ptr) { ptr = 0; return -1; } RESULT audioDelay(ePtr<iAudioDelay> &ptr) { ptr = 0; return -1; } - RESULT radioText(ePtr<iRadioText> &ptr) { ptr = 0; return -1; } + RESULT rdsDecoder(ePtr<iRdsDecoder> &ptr) { ptr = 0; return -1; } // iPausableService RESULT pause(); |
