2 Copyright 2008 Marten Richter
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* Portions from vdr osdteletext plugin "txtrender.c": */
23 /***************************************************************************
25 * txtrender.c - Teletext display abstraction and teletext code *
28 * This program is free software; you can redistribute it and/or modify *
29 * it under the terms of the GNU General Public License as published by *
30 * the Free Software Foundation; either version 2 of the License, or *
31 * (at your option) any later version. *
34 * 2005-03 initial version (c) Udo Richter *
36 ***************************************************************************/
38 #include "teletextdecodervbiebu.h"
39 #include "teletxt/tables.h"
48 TeletextDecoderVBIEBU::TeletextDecoderVBIEBU()
61 char digits[]={'1','0','0'};
62 setKeyinDigits(digits,false);
64 for (int i=0;i<10;i++) record_pages[i]=-1;
69 TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU()
75 long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync)
80 void TeletextDecoderVBIEBU::ResetTimeOffsets()
87 void TeletextDecoderVBIEBU::ResetDecoder()
94 char digits[]={'1','0','0'};
95 setKeyinDigits(digits,false);
96 for (int i=0;i<10;i++) record_pages[i]=-1;
100 void TeletextDecoderVBIEBU::setPage(unsigned int newpage)
102 selectedpage=newpage;
106 for (int i=0;i<25;i++) {
107 memset(curpage[i],0,40);
111 void TeletextDecoderVBIEBU::CleanPage()
114 for (int i=0;i<25;i++) {
115 memset(curpage[i],0,40);
120 c.SetFGColor(ttcWhite);
121 c.SetBGColor(ttcBlack);
122 c.SetCharset(CHARSET_LATIN_G0);
132 void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying)
138 c.SetFGColor(ttcWhite);
139 c.SetBGColor(ttcBlack);
140 c.SetCharset(CHARSET_LATIN_G0);
141 c.SetChar(digits[x]);
146 keyindigits[x]=digits[x];
150 void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos)
152 mediapacket = mplist.front();
155 static ULLONG TxtPTSDifference(ULLONG pts1, ULLONG pts2)
157 // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
164 UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)
166 if (mediapacket.type != MPTYPE_TELETEXT)
171 unsigned int headerstrip;
172 unsigned char txtdata[43];
173 headerstrip=buffer[mediapacket.pos_buffer+8]+9;
174 //headerstrip+=4; //substream id
175 unsigned int datapos=0;
176 unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0];
177 //Chris should we use here the pts data from mediapacket ?
178 // in this case the data fields should also be added in mediamvp
179 if (mediapacket.synched)
180 { // An entry exists in the work list
181 ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
183 ULLONG ptsdifference=TxtPTSDifference(mediapacket.pts, nowPTS);
185 if (ptsdifference >= (120LL*90000LL)) {
187 return 1;//bad data skip it
189 if (nowPTS < (mediapacket.pts-4000) ) {
195 if (( datatype>=0x10 && datatype <=0x1F) ||
196 ( datatype>=0x99 && datatype <=0x9B)) {
199 while (datapos< (mediapacket.length-headerstrip)) {
200 unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos];
202 unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos];
206 case 0x03: {//Teletext with and without subtitles
207 //call teletext decoder
208 unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos];
210 if ((datapos+44)< (mediapacket.length-headerstrip)) {
211 for (int i=0;i<42;i++) {
212 txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]];
214 DecodeTeletext(txtdata,field);
217 case 0xC0: //inverted Teletext
218 //call teletext decoder
222 //what to do with this, in the moment we do not need it
225 //what to do with this, in the moment we do not need it
228 //what to do with this, in the moment we do not need it
230 case 0xC6: //monochrome 4:2:2 samples, what is that?
231 //what to do with this, in the moment we do not need it
234 // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard
238 datapos+=unit_length;
239 // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) {
240 // datapos++; //stuffing bytes
251 return 1; //Skip it! and discard data
257 // This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg
258 void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!!
261 for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4);
262 int header=hdrbuf[0];
263 int magazin=header & 0x7;
264 int line = (header>>3) & 0x1f;
265 if (magazin==0) magazin=8;
271 RenderTeletextCode(false);
273 RenderTeletextCode(true);
275 int pagenumber=hdrbuf[1];
276 int pagemagazin=magazin<<8 | pagenumber;
277 int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f);
279 if (pagemagazin == selectedpage) ourpage=true;
282 for (int i=0;i<10;i++) {
283 if (pagemagazin==record_pages[i]) break;
284 if (record_pages[i]==-1) {
285 record_pages[i]=pagemagazin;
290 if (hdrbuf[3] &0x80) { //This is a subtitle
291 for (int i=0;i<10;i++) {
292 if (pagemagazin==record_pages[i]) break;
293 if (record_pages[i]==-1) {
294 record_pages[i]=pagemagazin;
302 lang=((hdrbuf[4]>>5) & 0x07);
303 flags=hdrbuf[2] & 0x80;
304 flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //??????
305 flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01);
306 for (int i=0;i<25;i++) {
307 memset(curpage[i],0,40);
311 memcpy(curpage[line],buffer+2,40);
312 } else if (ourpage && (line<=25)) {
313 memcpy(curpage[line],buffer+2,40);
319 /* from osdteletext plugin: (slightly adapted for vomp)*/
322 // teletext uses 7-bit numbers to identify a font set.
323 // There are three font sets involved:
324 // Primary G0, Secondary G0, and G2 font set.
326 // Font tables are organized in blocks of 8 fonts:
328 enumCharsets FontBlockG0_0000[8] = {
331 CHARSET_LATIN_G0_SV_FI,
334 CHARSET_LATIN_G0_PT_ES,
335 CHARSET_LATIN_G0_CZ_SK,
339 enumCharsets FontBlockG2Latin[8]={
350 enumCharsets FontBlockG0_0001[8] = {
353 CHARSET_LATIN_G0_SV_FI,
357 CHARSET_LATIN_G0_CZ_SK,
361 enumCharsets FontBlockG0_0010[8] = {
364 CHARSET_LATIN_G0_SV_FI,
367 CHARSET_LATIN_G0_PT_ES,
373 enumCharsets FontBlockG0_0011[8] = {
379 CHARSET_LATIN_G0_SR_HR_SL,
384 enumCharsets FontBlockG0_0100[8] = {
385 CHARSET_CYRILLIC_G0_SR_HR,
388 CHARSET_LATIN_G0_LV_LT,
389 CHARSET_CYRILLIC_G0_RU_BG,
390 CHARSET_CYRILLIC_G0_UK,
391 CHARSET_LATIN_G0_CZ_SK,
395 enumCharsets FontBlockG2_0100[8] = {
406 enumCharsets FontBlockG0_0110[8] = {
417 enumCharsets FontBlockG2_0110[8] = {
428 enumCharsets FontBlockG0_1000[8] = {
439 enumCharsets FontBlockG2_1000[8] = {
450 enumCharsets FontBlockG0_1010[8] = {
461 enumCharsets FontBlockG2_1010[8] = {
472 enumCharsets FontBlockInvalid[8] = {
485 // The actual font table definition:
486 // Split the 7-bit number into upper 4 and lower 3 bits,
487 // use upper 4 bits for outer array,
488 // use lower 3 bits for inner array
490 struct structFontBlock {
491 enumCharsets *G0Block;
492 enumCharsets *G2Block;
495 structFontBlock FontTable[16] = {
496 { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block
497 { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block
498 { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block
499 { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block
500 { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block
501 { FontBlockInvalid, FontBlockInvalid }, // 0101 block
502 { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block
503 { FontBlockInvalid, FontBlockInvalid }, // 0111 block
504 { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block
505 { FontBlockInvalid, FontBlockInvalid }, // 1001 block
506 { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block
507 { FontBlockInvalid, FontBlockInvalid }, // 1011 block
508 { FontBlockInvalid, FontBlockInvalid }, // 1100 block
509 { FontBlockInvalid, FontBlockInvalid }, // 1101 block
510 { FontBlockInvalid, FontBlockInvalid }, // 1110 block
511 { FontBlockInvalid, FontBlockInvalid } // 1111 block
514 inline enumCharsets GetG0Charset(int codepage) {
515 return FontTable[codepage>>3].G0Block[codepage&7];
517 inline enumCharsets GetG2Charset(int codepage) {
518 return FontTable[codepage>>3].G2Block[codepage&7];
522 // Possible size modifications of characters
529 void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) {
531 bool EmptyNextLine=false;
532 // Skip one line, in case double height chars were/will be used
535 int LocalG0CodePage=(FirstG0CodePage & 0x78)
536 | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2);
537 enumCharsets FirstG0=GetG0Charset(LocalG0CodePage);
538 enumCharsets SecondG0=GetG0Charset(SecondG0CodePage);
539 // Reserved for later use:
540 // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage);
542 for (y=0;y<24;(EmptyNextLine?y+=2:y++)) {
543 // Start of line: Set start of line defaults
545 // Hold Mosaics mode: Remember last mosaic char/charset
546 // for next spacing code
547 bool HoldMosaics=false;
548 unsigned char HoldMosaicChar=' ';
549 enumCharsets HoldMosaicCharset=FirstG0;
551 enumSizeMode Size=sizeNormal;
552 // Font size modification
553 bool SecondCharset=false;
554 // Use primary or secondary G0 charset
555 bool GraphicCharset=false;
556 // Graphics charset used?
557 bool SeparateGraphics=false;
558 // Use separated vs. contiguous graphics charset
559 bool NoNextChar=false;
560 // Skip display of next char, for double-width
562 // Skip next line, for double-height
565 // auto.initialized to everything off
566 c.SetFGColor(ttcWhite);
567 c.SetBGColor(ttcBlack);
568 c.SetCharset(FirstG0);
570 if (y==0 && (flags&0x10)) {
571 if (!inkeying ) c.SetBoxedOut(true);
575 if (!(inkeying && y==0) ) c.SetBoxedOut(true);
584 c2.SetChar(keyindigits[x-3]);
593 // Pre-scan for double-height and double-size codes
595 if (y==0 && x<8) x=8;
596 if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F)
602 unsigned char ttc=curpage[y][x] & 0x7f;
605 if (y==0 && x<8) continue;
606 if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue;
607 // no displayable data here...
609 /* // Debug only: Output line data and spacing codes
612 printf("%s ",names[ttc]);
615 if (x==39) printf("\n");
619 // Handle all 'Set-At' spacing codes
624 case 0x0C: // Normal Size
625 if (Size!=sizeNormal) {
628 HoldMosaicCharset=FirstG0;
631 case 0x18: // Conceal
634 case 0x19: // Contiguous Mosaic Graphics
635 SeparateGraphics=false;
637 c.SetCharset(CHARSET_GRAPHICS_G1);
639 case 0x1A: // Separated Mosaic Graphics
640 SeparateGraphics=true;
642 c.SetCharset(CHARSET_GRAPHICS_G1_SEP);
644 case 0x1C: // Black Background
645 c.SetBGColor(ttcBlack);
647 case 0x1D: // New Background
648 c.SetBGColor(c.GetFGColor());
650 case 0x1E: // Hold Mosaic
655 // temporary copy of character data:
657 // c2 will be text character or space character or hold mosaic
658 // c2 may also have temporary flags or charsets
661 // Spacing code, display space or hold mosaic
663 c2.SetChar(HoldMosaicChar);
664 c2.SetCharset(HoldMosaicCharset);
671 if (GraphicCharset) {
673 // real graphics code, remember for HoldMosaics
675 HoldMosaicCharset=c.GetCharset();
677 // invalid code, pass-through to G0
678 c2.SetCharset(SecondCharset?SecondG0:FirstG0);
683 // Handle double-height and double-width extremes
685 if (Size==sizeDoubleHeight) Size=sizeNormal;
686 if (Size==sizeDoubleSize) Size=sizeDoubleWidth;
689 if (Size==sizeDoubleWidth) Size=sizeNormal;
690 if (Size==sizeDoubleSize) Size=sizeDoubleHeight;
693 // Now set character code
696 // Suppress this char due to double width last char
703 if (EmptyNextLine && y<23) {
704 // Clean up next line
705 setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0));
708 case sizeDoubleWidth:
710 setChar(x,y,c2.ToDblWidth(dblw_Left));
711 setChar(x+1,y,c2.ToDblWidth(dblw_Right));
712 if (EmptyNextLine && y<23) {
713 // Clean up next line
714 setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0));
715 setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0));
719 case sizeDoubleHeight:
721 setChar(x,y,c2.ToDblHeight(dblh_Top));
722 setChar(x,y+1,c2.ToDblHeight(dblh_Bottom));
726 setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left ));
727 setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right));
728 setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left ));
729 setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right));
735 // Handle all 'Set-After' spacing codes
736 if (ttc>=0x00 && ttc<=0x07) { // Set FG color
737 if (GraphicCharset) {
738 // Actual switch from graphics charset
740 HoldMosaicCharset=FirstG0;
742 c.SetFGColor((enumTeletextColor)ttc);
743 c.SetCharset(SecondCharset?SecondG0:FirstG0);
744 GraphicCharset=false;
746 } else if (ttc==0x08) {
748 } else if (ttc==0x0A) {
750 } else if (ttc==0x0B) {
752 c.SetBoxedOut(false);
753 } else if (ttc==0x0D) {
754 if (Size!=sizeDoubleHeight) {
755 Size=sizeDoubleHeight;
757 HoldMosaicCharset=FirstG0;
759 } else if (ttc==0x0E) {
760 if (Size!=sizeDoubleWidth) {
761 Size=sizeDoubleWidth;
763 HoldMosaicCharset=FirstG0;
765 } else if (ttc==0x0E) {
766 if (Size!=sizeDoubleSize) {
769 HoldMosaicCharset=FirstG0;
771 } else if (ttc>=0x10 && ttc<=0x17) {
772 if (!GraphicCharset) {
773 // Actual switch to graphics charset
775 HoldMosaicCharset=FirstG0;
777 c.SetFGColor((enumTeletextColor)(ttc-0x10));
778 c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1);
781 } else if (ttc==0x1B) {
782 SecondCharset=!SecondCharset;
783 if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0);
784 } else if (ttc==0x1F) {
789 if (renderfirstlineonly) break;
793 // Clean out last line
795 c.SetFGColor(ttcWhite);
796 c.SetBGColor(ttcBlack);
797 c.SetCharset(FirstG0);
815 if (dirty && txtview!=NULL ) {
817 if ( !renderfirstlineonly) {
818 Message* m= new Message();
819 m->message = Message::TELETEXTUPDATE;
823 Command::getInstance()->postMessageFromOuterSpace(m);
824 } else if (firstlineupdate==10) {
825 Message* m= new Message();
826 m->message = Message::TELETEXTUPDATEFIRSTLINE;
830 Command::getInstance()->postMessageFromOuterSpace(m);
832 } else firstlineupdate++;