add support for rds data on own pids (i.e. hitradio ffh, harmony fm)
[enigma2.git] / lib / dvb / radiotext.cpp
1 #include <lib/base/eerror.h>
2 #include <lib/dvb/radiotext.h>
3 #include <lib/dvb/idemux.h>
4 #include <lib/gdi/gpixmap.h>
5
6 DEFINE_REF(eDVBRdsDecoder);
7
8 eDVBRdsDecoder::eDVBRdsDecoder(iDVBDemux *demux, int type)
9         :msgPtr(0), bsflag(0), qdar_pos(0), t_ptr(0), qdarmvi_show(0), state(0)
10         ,m_type(type), m_pid(-1), m_abortTimer(eTimer::create(eApp))
11 {
12         setStreamID(0xC0, 0xC0);
13
14         memset(rass_picture_mask, 0, sizeof(rass_picture_mask));
15         memset(rtp_item, 0, sizeof(rtp_item));
16
17         if (demux->createPESReader(eApp, m_pes_reader))
18                 eDebug("failed to create PES reader!");
19         else if (type == 0)
20                 m_pes_reader->connectRead(slot(*this, &eDVBRdsDecoder::processData), m_read_connection);
21         else
22                 m_pes_reader->connectRead(slot(*this, &eDVBRdsDecoder::gotAncillaryData), m_read_connection);
23         CONNECT(m_abortTimer->timeout, eDVBRdsDecoder::abortNonAvail);
24 }
25
26 eDVBRdsDecoder::~eDVBRdsDecoder()
27 {
28         // delete cached rass slides
29         for (int page=0; page < 10; ++page)
30         {
31                 unsigned char mask = rass_picture_mask[(page*4)/8];
32                 if (page % 2)
33                         mask >>= 4;
34                 int subpage=0;
35                 while(mask)
36                 {
37                         if (mask & 1)
38                         {
39                                 std::string filename = getRassPicture(page, subpage);
40                                 if (filename.length())
41                                         remove(filename.c_str());
42                         }
43                         mask >>= 1;
44                         ++subpage;
45                 }
46         }
47         remove("/tmp/RassLast.mvi");
48 }
49
50 #define SWAP(x) ((x<<8)|(x>>8))
51 #define LO(x)   (x&0xFF)
52
53 static inline unsigned short crc_ccitt_byte( unsigned short crc, unsigned char c )
54 {
55         crc = SWAP(crc) ^ c;
56         crc = crc ^ (LO(crc) >> 4);
57         crc = crc ^ (SWAP(LO(crc)) << 4) ^ (LO(crc) << 5);
58         return crc;
59 }
60
61 static int bitrate[3][3][16] = {
62         {
63                 // MPEG-2, L3
64                 {-1,8000,16000,24000,32000,40000,48000,56000,64000,80000,96000,112000,128000,144000,160000,0}, 
65                 // MPEG-2, L2
66                 {-1,8000,16000,24000,32000,40000,48000,56000,64000,80000,96000,112000,128000,144000,160000,0},
67                 // MPEG-2, L1
68                 {-1,32000,48000,56000,64000,80000,96000,112000,128000,144000,160000,176000,192000,224000,256000,0}
69         },
70         {
71                 // MPEG-1, L3
72                 {-1,32000,40000,48000,56000,64000,80000,96000,112000,128000,160000,192000,224000,256000,320000,0}, 
73                 // MPEG-1, L2
74                 {-1,32000,48000,56000,64000,80000,96000,112000,128000,160000,192000,224000,256000,320000,384000,0},
75                 // MPEG-1, L1
76                 {-1,32000,64000,96000,128000,160000,192000,224000,256000,288000,320000,352000,384000,416000,448000,0}
77         },
78         {
79                 //MPEG-2.5, L3??
80                 {-1,6000,8000,10000,12000,16000,20000,24000,28000,320000,40000,48000,56000,64000,80000,0},
81                 //MPEG-2.5, L2
82                 {-1,6000,8000,10000,12000,16000,20000,24000,28000,320000,40000,48000,56000,64000,80000,0},
83                 //MPEG-2.5, L1
84                 {-1,8000,12000,16000,20000,24000,32000,40000,48000,560000,64000,80000,96000,112000,128000,0}
85         }
86 };
87
88 static int frequency[3][4] = {
89         // MPEG2 - 22.05, 24, 16khz
90         { 22050,24000,16000,0 },
91         // MPEG1 - 44.1, 48, 32khz
92         { 44100,48000,32000,0 },
93         // MPEG2.5 - 11.025, 12, 8khz
94         { 11025,12000,8000,0 }
95 };
96
97 void eDVBRdsDecoder::connectEvent(const Slot1<void, int> &slot, ePtr<eConnection> &connection)
98 {
99         connection = new eConnection(this, m_event.connect(slot));
100 }
101
102 void eDVBRdsDecoder::addToPictureMask(int id)
103 {
104         int page = id / 1000;
105         int tmp = page > 0 ? id / page : id;
106         int subpage = 0;
107         while(tmp > 1000)
108         {
109                 ++subpage;
110                 tmp -= 1000;
111                 tmp *= 10;
112         }
113         int index = (page*4+subpage)/8;
114         int val = (page%2) ? 16 * (1 << subpage) : (1 << subpage);
115         if (rass_picture_mask[index] & val) // already have this picture
116                 return;
117         rass_picture_mask[index] |= val;
118         /* emit */ m_event(RassInteractivePicMaskChanged);
119 }
120
121 void eDVBRdsDecoder::removeFromPictureMask(int id)
122 {
123         int page = id / 1000;
124         int tmp = page > 0 ? id / page : id;
125         int subpage = 0;
126         while(tmp > 1000)
127         {
128                 ++subpage;
129                 tmp -= 1000;
130                 tmp *= 10;
131         }
132         int index = (page*4)/8;
133         int val = (page%2) ? 16 * (1 << subpage) : (1 << subpage);
134         if (rass_picture_mask[index] & val) // have this picture
135         {
136                 rass_picture_mask[index] &= ~val;
137                 /* emit */ m_event(RassInteractivePicMaskChanged);
138         }
139 }
140
141 void eDVBRdsDecoder::processPESPacket(__u8 *data, int len)
142 {
143         int pos=9+data[8];// skip pes header
144
145         while (pos < len)
146         {
147                 if ((0xFF & data[pos]) != 0xFF || (0xF0 & data[pos + 1]) != 0xF0)
148                         return;
149
150                 int padding_bit = (data[pos + 2]>>1) & 1;
151                 int mode = (data[pos + 3]>>6) & 3;
152                 int channel = mode == 3 ? 1 : 2;
153                 int id = (data[pos + 1] >> 3) & 1;
154                 int emphasis_bit = data[pos + 3] & 3;
155                 //int protection_bit = data[pos + 1] & 1;
156                 int rate = -1;
157                 int sample_freq = -1;
158                 int layer = -1;
159
160                 if (emphasis_bit == 2 && id == 1 )
161                         id = 2;
162
163                 if ((layer = (data[pos + 1]>>1) & 3) < 1)
164                         return;
165
166                 if ((rate = bitrate[id][layer - 1][(data[pos + 2]>>4) & 0xf]) < 1)
167                         return;
168
169                 if ((sample_freq = frequency[id][(data[pos + 2]>>2) & 3]) == 0)
170                         return;
171
172                 if (id == 1 && layer == 2)
173                 {
174                         if (rate / channel < 32000)
175                                 return;
176                         if (rate / channel > 192000)
177                                 return;
178                 }
179
180                 int frame_size = layer < 3 ?
181                         (144 * rate / sample_freq) + padding_bit :
182                         ((12 * rate / sample_freq) * 4) + (4 * padding_bit);
183
184                 pos += frame_size;
185
186 #if 0
187 //              eDebug("protection_bit ? %d", protection_bit);
188 //              int offs = protection_bit ? pos - 1 : pos - 3;
189 //              if (data[offs] != 0xFD)
190 //                      offs += 2;
191 //              eDebug("%02x %02x %02x %02x %02x", data[offs-2], data[offs-1], data[offs], data[offs+1], data[offs+2]);
192 #else
193                 int offs = pos - 1;
194 #endif
195
196                 if (data[offs] == 0xFD)
197                 {
198                         m_abortTimer->stop();
199                         int ancillary_len = 1 + data[offs - 1];
200                         offs -= ancillary_len;
201                         gotAncillaryData(data+offs, ancillary_len-1);
202                 }
203         }
204 }
205
206 void eDVBRdsDecoder::process_qdar(unsigned char *buf)
207 {
208         if (buf[0] == 0x40 && buf[1] == 0xDA)
209         {
210                 unsigned int item,cnt,ctrl,item_type;
211                 unsigned long item_length,id,item_no,ptr,tmp;
212                 unsigned short crc_qdar,crc_read;
213                 char fname[50];
214                 ptr=4;cnt=0;
215                 item=buf[2]<<8; // Number of Items
216                 item|=buf[3];
217                 
218                 while ( cnt++ < item ) //read in items
219                 {
220                         id=buf[ptr++]<<8; //QDarID
221                         id|=buf[ptr++];
222                         
223                         item_no=buf[ptr++]<<8; // Item Number
224                         item_no|=buf[ptr++];
225                         
226                         ctrl=buf[ptr++]; //controlbyte
227                         item_type=buf[ptr++]; //item type
228                         
229                         item_length=buf[ptr++]<<24; // Item length
230                         item_length|=buf[ptr++]<<16;
231                         item_length|=buf[ptr++]<<8;
232                         item_length|=buf[ptr++];
233                         
234                         ptr=ptr+4; // rfu Bytes ... not used
235                         tmp=ptr; // calc crc
236                         crc_qdar=0xFFFF;
237                         while (tmp < ptr+item_length)
238                                 crc_qdar = crc_ccitt_byte(crc_qdar, buf[tmp++]);
239                 
240                         crc_read=buf[ptr+item_length]<<8;
241                         crc_read|=buf[ptr+item_length+1];
242                         //eDebug("[RDS/Rass] CRC read: %04X calculated: %04X",crc_read,crc_qdar^0xFFFF);
243
244                         if (crc_read == (crc_qdar^0xFFFF)) // process item
245                         {
246                                 switch(item_type)
247                                 {
248                                         case 0x01: //Stillframe
249                                                 if (ctrl&0x01) // display slide
250                                                 {
251                                                         sprintf(fname,"/tmp/RassLast.mvi");
252                                                         FILE *fh=fopen(fname,"wb");
253                                                         fwrite(buf+ptr,1,item_length-2,fh);
254                                                         fclose(fh);
255                                                         /*emit*/ m_event(RecvRassSlidePic);
256                                                         qdarmvi_show=1;
257                                                 }
258                                                 if (ctrl&0x02) // save slide for interactive mode
259                                                 {
260                                                         if (id == 0 || id >= 1000)
261                                                         {
262                                                                 sprintf(fname,"/tmp/Rass%04d.mvi",(int)id);
263                                                                 FILE *fh=fopen(fname,"wb");
264                                                                 fwrite(buf+ptr,1,item_length-2,fh);
265                                                                 fclose(fh);
266                                                                 addToPictureMask(id);
267                                                         }
268                                                         else
269                                                                 eDebug("ignore recv interactive picture id %lu", id);
270                                                 }
271                                                 if (ctrl&0x04) // display slide if nothing had been displayed yet
272                                                 {
273                                                         if (qdarmvi_show != 1)
274                                                         {
275                                                                 sprintf(fname,"/tmp/RassLast.mvi");
276                                                                 FILE *fh=fopen(fname,"wb");
277                                                                 fwrite(buf+ptr,1,item_length-2,fh);
278                                                                 fclose(fh);
279                                                                 /*emit*/ m_event(RecvRassSlidePic);
280                                                                 qdarmvi_show=1;
281                                                         }
282                                                 }
283                                                 if (ctrl&0x08) // delete slide
284                                                 {
285                                                         eDebug("delete slide id %lu, item_no %lu", id, item_no);
286                                                         if (id == 0 || id >= 1000)
287                                                         {
288                                                                 eDebug("delete %lu", id);
289                                                                 removeFromPictureMask(id);
290                                                                 sprintf(fname,"/tmp/Rass%04d.mvi",(int)id); // was item_no ? ! ?
291                                                                 remove(fname);
292                                                         }
293                                                         else
294                                                                 eDebug("ignore del interactive picture id %lu", id);
295                                                 }
296                                                 break;
297                                         default: //nothing more yet defined
298                                                 break;
299                                 }
300                         } 
301                         else
302                         {
303                                 eDebug("[RDS/Rass] CRC error, skip Rass-Qdar-Item");
304                         }
305                         
306                         ptr=+item_length;
307                 }
308         }
309         else
310         {
311                 eDebug("[RDS/Rass] No Rass-QDAR archive (%02X %02X) so skipping !\n",buf[0],buf[1]);
312         }
313 }
314
315 void eDVBRdsDecoder::gotAncillaryData(const __u8 *buf, int len)
316 {
317         if (len <= 0)
318                 return;
319         int pos = m_type ? 0 : len-1;
320         while ( len )
321         {
322                 unsigned char c = buf[pos];
323
324                 pos += m_type ? 1 : -1;
325
326                 --len;
327
328                 if (bsflag == 1) // byte stuffing
329                 {
330                         bsflag=2;
331                         switch (c)
332                         {
333                                 case 0x00: c=0xFD; break;
334                                 case 0x01: c=0xFE; break;
335                                 case 0x02: c=0xFF; break;
336                         }
337                 }
338
339                 if (c == 0xFD && bsflag ==0) 
340                         bsflag=1;
341                 else
342                         bsflag=0;
343                                         
344                 if (bsflag == 0) 
345                 {
346                         if ( state == 1 )
347                                 crc=0xFFFF;
348                         if (( state >= 1 && state < 11 ) || ( state >=26 && state < 36 ))
349                                 crc = crc_ccitt_byte(crc, c);
350
351                         switch (state)
352                         {
353                                 case 0:
354                                         if ( c==0xFE )  // Startkennung
355                                                 state=1;
356                                         break;
357                                 case 1: // 10bit Site Address + 6bit Encoder Address
358                                 case 2:
359                                 case 3: // Sequence Counter
360                                         ++state;
361                                         break;
362                                 case 4:
363                                         leninfo=c;
364                                         ++state;
365                                         break;
366                                 case 5:
367                                         switch (c)
368                                         {
369                                                 case 0x0A: // Radiotext
370                                                         ++state;
371                                                         break;
372                                                 case 0x46: // Radiotext Plus tags
373                                                         state=38;
374                                                         break;
375                                                 case 0xDA: // Rass
376                                                         state=26;
377                                                         break;
378                                                 default: // reset to state 0
379                                                         state=0;
380                                         }
381                                         break;
382
383                                         // process Radiotext
384                                 case 6: // Data Set Number ... ignore
385                                 case 7: // Program Service Number ... ignore
386                                         ++state;
387                                         break;
388                                 case 8: // Message Element Length
389                                         text_len=c;
390                                         if ( !text_len || text_len > 65 || text_len > leninfo-4)
391                                                 state=0;
392                                         else
393                                         {
394                                                 ++state;
395                                                 text_len-=2;
396                                                 msgPtr=0;
397                                         }
398                                         break;
399                                 case 9: // Radio Text Status bit:
400                                         // 0   = AB-flagcontrol
401                                         // 1-4 = Transmission-Number
402                                         // 5-6 = Buffer-Config
403                                         ++state; // ignore ...
404                                         break;
405                                 case 10:
406                                         // TODO build a complete radiotext charcode to UTF8 conversion table for all character > 0x80
407                                         switch (c)
408                                         {
409                                                 case 0 ... 0x7f: break;
410                                                 case 0x8d: c='ß'; break;
411                                                 case 0x91: c='ä'; break;
412                                                 case 0xd1: c='Ä'; break;
413                                                 case 0x97: c='ö'; break;
414                                                 case 0xd7: c='Ö'; break;
415                                                 case 0x99: c='ü'; break;
416                                                 case 0xd9: c='Ü'; break;
417                                                 default: c=' '; break;  // convert all unknown to space
418                                         }
419                                         message[msgPtr++]=c;
420                                         if(text_len)
421                                                 --text_len;
422                                         else
423                                                 ++state;
424                                         break;
425                                 case 11:
426                                         crc16=c<<8;
427                                         ++state;
428                                         break;
429                                 case 12:
430                                         crc16|=c;
431                                         message[msgPtr--]=0;
432                                         while(message[msgPtr] == ' ' && msgPtr > 0)
433                                                 message[msgPtr--] = 0;
434                                         if ( crc16 == (crc^0xFFFF) )
435                                         {
436                                                 eDebug("radiotext: (%s)", message);
437                                                 /*emit*/ m_event(RadioTextChanged);
438                                                 memcpy(lastmessage,message,66);
439                                         }
440                                         else
441                                                 eDebug("invalid radiotext crc (%s)", message);
442                                         state=0;
443                                         break;
444
445                                 // process Rass
446                                 case 26: //MEL
447                                         text_len = c;
448                                         text_len2 = c;
449                                         ++state;
450                                         text_len-=9;
451                                         text_len2-=9;
452                                         t_ptr=0;
453                                         break;
454                                 case 27: // SID not used atm
455                                         ++state;
456                                         break;
457                                 case 28: // SID not used atm
458                                         ++state;
459                                         break;
460                                 case 29: // PNR packet number
461                                         part=c<<16;
462                                         ++state;
463                                         break;
464                                 case 30: // PNR packet number
465                                         part|=c<<8;
466                                         ++state;
467                                         break;
468                                 case 31: // PNR packet number
469                                         part|=c;
470                                         ++state;
471                                         break;
472                                 case 32: // NOP number of packets
473                                         parts=c<<16;
474                                         ++state;
475                                         break;
476                                 case 33: // NOP number of packets
477                                         parts|=c<<8;
478                                         ++state;
479                                         break;
480                                 case 34: // NOP number of packets
481                                         parts|=c;
482                                         ++state;
483                                         break;
484                                 case 35:
485                                         datamessage[t_ptr++]=c;
486                                         if(text_len) 
487                                                 --text_len;
488                                         else
489                                                 ++state;
490                                         break;
491                                 case 36:
492                                         crc16=c<<8;
493                                         ++state;
494                                         break;
495                                 case 37:
496                                         crc16|=c;
497                                         //eDebug("[RDS/Rass] CRC read: %04X CRC calculated: %04X",crc16,crc^0xFFFF);
498                                         state=0;
499                                         if ( crc16 == (crc^0xFFFF) ) 
500                                         {
501                                                 if (partcnt == -1) 
502                                                         partcnt=1;
503                                                 if (partcnt == part)
504                                                 {
505                                                         memcpy(qdar+qdar_pos,datamessage,text_len2+1);
506                                                         qdar_pos=qdar_pos+text_len2+1;
507                                                         if (partcnt == parts)
508                                                         {
509                                                                 process_qdar(qdar); // decode qdar archive
510                                                                 qdar_pos=0;
511                                                                 partcnt=-1;
512                                                         }
513                                                         else
514                                                                 ++partcnt;
515                                                 }
516                                                 else
517                                                 {
518                                                         qdar_pos=0;
519                                                         partcnt=-1;
520                                                 }
521                                         }
522                                         else
523                                         {
524                                                 eDebug("[RDS/Rass] CRC error, skip Rass-Qdar-Packet");
525                                                 eDebug("[RDS/Rass] CRC read: %04X CRC calculated: %04X",crc16,crc^0xFFFF);
526                                                 partcnt=-1;
527                                         }
528                                         state=0;
529                                         break;
530
531                                 // process RT plus tags ... 
532                                 case 38: // Message Element Length
533                                         text_len=c;     
534                                         ++state;
535                                         break;
536                                 case 39: // Application ID 
537                                 case 40: // always 0x4BD7 so we ignore it ;)
538                                 case 41: // Applicationgroup Typecode/PTY ... ignore
539                                         ++state;
540                                         break;
541                                 case 42:
542                                         rtp_buf[0]=c;
543                                         ++state;
544                                         break;
545                                 case 43:
546                                         rtp_buf[1]=c;
547                                         ++state;
548                                         break;
549                                 case 44:
550                                         rtp_buf[2]=c;
551                                         ++state;
552                                         break;
553                                 case 45:
554                                         rtp_buf[3]=c;
555                                         ++state;
556                                         break;
557                                 case 46: // bit 10#4 = Item Togglebit
558                                         // bit 10#3 = Item Runningbit
559                                         // Tag1: bit 10#2..11#5 = Contenttype, 11#4..12#7 = Startmarker, 12#6..12#1 = Length
560                                         rtp_buf[4]=c;
561                                         if (lastmessage[0] == 0) // no rds message till now ? quit ...
562                                                 break;
563                                         int rtp_typ[2],rtp_start[2],rtp_len[2];
564                                         rtp_typ[0]   = (0x38 & rtp_buf[0]<<3) | rtp_buf[1]>>5;
565                                         rtp_start[0] = (0x3e & rtp_buf[1]<<1) | rtp_buf[2]>>7;
566                                         rtp_len[0]   = 0x3f & rtp_buf[2]>>1;
567                                         // Tag2: bit 12#0..13#3 = Contenttype, 13#2..14#5 = Startmarker, 14#4..14#0 = Length(5bit)
568                                         rtp_typ[1]   = (0x20 & rtp_buf[2]<<5) | rtp_buf[3]>>3;
569                                         rtp_start[1] = (0x38 & rtp_buf[3]<<3) | rtp_buf[4]>>5;
570                                         rtp_len[1]   = 0x1f & rtp_buf[4];
571                                                                         
572                                         unsigned char rtplus_osd_tmp[64];
573                                         
574                                         if (rtp_start[0] < 66 && (rtp_len[0]+rtp_start[0]) < 66)
575                                         {
576                                                 memcpy(rtp_item[rtp_typ[0]],lastmessage+rtp_start[0],rtp_len[0]+1);
577                                                 rtp_item[rtp_typ[0]][rtp_len[0]+1]=0;
578                                         }
579                                                                         
580                                         if (rtp_typ[0] != rtp_typ[1])
581                                         {
582                                                 if (rtp_start[1] < 66 && (rtp_len[1]+rtp_start[1]) < 66)
583                                                 {
584                                                         memcpy(rtp_item[rtp_typ[1]],lastmessage+rtp_start[1],rtp_len[1]+1);
585                                                         rtp_item[rtp_typ[1]][rtp_len[1]+1]=0;
586                                                 }
587                                         }
588
589                                         // main RTPlus item_types used by the radio stations:
590                                         // 1 title
591                                         // 4 artist
592                                         // 24 info.date_time
593                                         // 31 stationname
594                                         // 32 program.now
595                                         // 39 homepage
596                                         // 41 phone.hotline
597                                         // 46 email.hotline
598                                         // todo: make a window to display all saved items ...
599         
600                                         //create RTPlus OSD for title/artist
601                                         rtplus_osd[0]=0;
602                                                                 
603                                         if ( rtp_item[4][0] != 0 )//artist
604                                                 sprintf((char*)rtplus_osd_tmp," (%s)",rtp_item[4]);
605                                                                 
606                                         if ( rtp_item[1][0] != 0 )//title
607                                                 sprintf((char*)rtplus_osd,"%s%s",rtp_item[1],rtplus_osd_tmp);
608                                                                         
609                                         if ( rtplus_osd[0] != 0 )
610                                         {
611                                                 /*emit*/ m_event(RtpTextChanged);
612                                                 eDebug("RTPlus: %s",rtplus_osd);
613                                         }
614                                                 
615                                         state=0;
616                                         break;
617                         }
618                 }
619         }
620 }
621
622 std::string eDVBRdsDecoder::getRassPicture(int page, int subpage)
623 {
624         int val=0;
625         
626         switch(subpage)
627         {
628                 case 0:
629                         val=page*1000;
630                         break;
631                 case 1:
632                         val=page*1100;
633                         break;
634                 case 2:
635                         val=page*1110;
636                         break;
637                 case 3:
638                         val=page*1111;
639                         break;
640         }
641         char fname[50];
642         sprintf(fname,"/tmp/Rass%04d.mvi",val);
643         return fname;
644 }
645
646 int eDVBRdsDecoder::start(int pid)
647 {
648         int ret = -1;
649         if (m_pes_reader && !(ret = m_pes_reader->start(pid)) && m_type == 0)
650                 m_abortTimer->startLongTimer(20);
651         m_pid = pid;
652         return ret;
653 }
654
655 void eDVBRdsDecoder::abortNonAvail()
656 {
657         eDebug("no ancillary data in audio stream... abort radiotext pes parser");
658         if (m_pes_reader)
659                 m_pes_reader->stop();
660 }
661
662 ePyObject eDVBRdsDecoder::getRassPictureMask()
663 {
664         ePyObject ret = PyTuple_New(5);
665         PyTuple_SET_ITEM(ret, 0, PyInt_FromLong(rass_picture_mask[0]));
666         PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(rass_picture_mask[1]));
667         PyTuple_SET_ITEM(ret, 2, PyInt_FromLong(rass_picture_mask[2]));
668         PyTuple_SET_ITEM(ret, 3, PyInt_FromLong(rass_picture_mask[3]));
669         PyTuple_SET_ITEM(ret, 4, PyInt_FromLong(rass_picture_mask[4]));
670         return ret;
671 }