aboutsummaryrefslogtreecommitdiff
path: root/lib/dvb/radiotext.cpp
diff options
context:
space:
mode:
authorAndreas Monzner <andreas.monzner@multimedia-labs.de>2007-03-24 00:49:20 +0000
committerAndreas Monzner <andreas.monzner@multimedia-labs.de>2007-03-24 00:49:20 +0000
commitd1e450f95a9b79b144a00dcf0f9356ff6cb9bfb2 (patch)
tree85131a89500ccc60f343732e6727f00df907bc17 /lib/dvb/radiotext.cpp
parent058d38ca9bcfe94c5c551eac231f808330af3581 (diff)
downloadenigma2-d1e450f95a9b79b144a00dcf0f9356ff6cb9bfb2.tar.gz
enigma2-d1e450f95a9b79b144a00dcf0f9356ff6cb9bfb2.zip
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
Diffstat (limited to 'lib/dvb/radiotext.cpp')
-rw-r--r--lib/dvb/radiotext.cpp608
1 files changed, 494 insertions, 114 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;
+}