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 PTSDifference(ULLONG pts1, ULLONG pts2)
157 // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
161 return (1LL<<33) + 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();
182 if (PTSDifference(mediapacket.pts, nowPTS) >= 1200*90000) {
184 return 1;//bad data skip it
186 if (nowPTS < (mediapacket.pts-4000) ) {
192 if (( datatype>=0x10 && datatype <=0x1F) ||
193 ( datatype>=0x99 && datatype <=0x9B)) {
196 while (datapos< (mediapacket.length-headerstrip)) {
197 unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos];
199 unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos];
203 case 0x03: {//Teletext with and without subtitles
204 //call teletext decoder
205 unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos];
207 if ((datapos+44)< (mediapacket.length-headerstrip)) {
208 for (int i=0;i<42;i++) {
209 txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]];
211 DecodeTeletext(txtdata,field);
214 case 0xC0: //inverted Teletext
215 //call teletext decoder
219 //what to do with this, in the moment we do not need it
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
227 case 0xC6: //monochrome 4:2:2 samples, what is that?
228 //what to do with this, in the moment we do not need it
231 // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard
235 datapos+=unit_length;
236 // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) {
237 // datapos++; //stuffing bytes
248 return 1; //Skip it! and discard data
254 // This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg
255 void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!!
258 for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4);
259 int header=hdrbuf[0];
260 int magazin=header & 0x7;
261 int line = (header>>3) & 0x1f;
262 if (magazin==0) magazin=8;
268 RenderTeletextCode(false);
270 RenderTeletextCode(true);
272 int pagenumber=hdrbuf[1];
273 int pagemagazin=magazin<<8 | pagenumber;
274 int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f);
276 if (pagemagazin == selectedpage) ourpage=true;
279 for (int i=0;i<10;i++) {
280 if (pagemagazin==record_pages[i]) break;
281 if (record_pages[i]==-1) {
282 record_pages[i]=pagemagazin;
287 if (hdrbuf[3] &0x80) { //This is a subtitle
288 for (int i=0;i<10;i++) {
289 if (pagemagazin==record_pages[i]) break;
290 if (record_pages[i]==-1) {
291 record_pages[i]=pagemagazin;
299 lang=((hdrbuf[4]>>5) & 0x07);
300 flags=hdrbuf[2] & 0x80;
301 flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //??????
302 flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01);
303 for (int i=0;i<25;i++) {
304 memset(curpage[i],0,40);
308 memcpy(curpage[line],buffer+2,40);
309 } else if (ourpage && (line<=25)) {
310 memcpy(curpage[line],buffer+2,40);
316 /* from osdteletext plugin: (slightly adapted for vomp)*/
319 // teletext uses 7-bit numbers to identify a font set.
320 // There are three font sets involved:
321 // Primary G0, Secondary G0, and G2 font set.
323 // Font tables are organized in blocks of 8 fonts:
325 enumCharsets FontBlockG0_0000[8] = {
328 CHARSET_LATIN_G0_SV_FI,
331 CHARSET_LATIN_G0_PT_ES,
332 CHARSET_LATIN_G0_CZ_SK,
336 enumCharsets FontBlockG2Latin[8]={
347 enumCharsets FontBlockG0_0001[8] = {
350 CHARSET_LATIN_G0_SV_FI,
354 CHARSET_LATIN_G0_CZ_SK,
358 enumCharsets FontBlockG0_0010[8] = {
361 CHARSET_LATIN_G0_SV_FI,
364 CHARSET_LATIN_G0_PT_ES,
370 enumCharsets FontBlockG0_0011[8] = {
376 CHARSET_LATIN_G0_SR_HR_SL,
381 enumCharsets FontBlockG0_0100[8] = {
382 CHARSET_CYRILLIC_G0_SR_HR,
385 CHARSET_LATIN_G0_LV_LT,
386 CHARSET_CYRILLIC_G0_RU_BG,
387 CHARSET_CYRILLIC_G0_UK,
388 CHARSET_LATIN_G0_CZ_SK,
392 enumCharsets FontBlockG2_0100[8] = {
403 enumCharsets FontBlockG0_0110[8] = {
414 enumCharsets FontBlockG2_0110[8] = {
425 enumCharsets FontBlockG0_1000[8] = {
436 enumCharsets FontBlockG2_1000[8] = {
447 enumCharsets FontBlockG0_1010[8] = {
458 enumCharsets FontBlockG2_1010[8] = {
469 enumCharsets FontBlockInvalid[8] = {
482 // The actual font table definition:
483 // Split the 7-bit number into upper 4 and lower 3 bits,
484 // use upper 4 bits for outer array,
485 // use lower 3 bits for inner array
487 struct structFontBlock {
488 enumCharsets *G0Block;
489 enumCharsets *G2Block;
492 structFontBlock FontTable[16] = {
493 { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block
494 { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block
495 { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block
496 { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block
497 { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block
498 { FontBlockInvalid, FontBlockInvalid }, // 0101 block
499 { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block
500 { FontBlockInvalid, FontBlockInvalid }, // 0111 block
501 { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block
502 { FontBlockInvalid, FontBlockInvalid }, // 1001 block
503 { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block
504 { FontBlockInvalid, FontBlockInvalid }, // 1011 block
505 { FontBlockInvalid, FontBlockInvalid }, // 1100 block
506 { FontBlockInvalid, FontBlockInvalid }, // 1101 block
507 { FontBlockInvalid, FontBlockInvalid }, // 1110 block
508 { FontBlockInvalid, FontBlockInvalid } // 1111 block
511 inline enumCharsets GetG0Charset(int codepage) {
512 return FontTable[codepage>>3].G0Block[codepage&7];
514 inline enumCharsets GetG2Charset(int codepage) {
515 return FontTable[codepage>>3].G2Block[codepage&7];
519 // Possible size modifications of characters
526 void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) {
528 bool EmptyNextLine=false;
529 // Skip one line, in case double height chars were/will be used
532 int LocalG0CodePage=(FirstG0CodePage & 0x78)
533 | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2);
534 enumCharsets FirstG0=GetG0Charset(LocalG0CodePage);
535 enumCharsets SecondG0=GetG0Charset(SecondG0CodePage);
536 // Reserved for later use:
537 // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage);
539 for (y=0;y<24;(EmptyNextLine?y+=2:y++)) {
540 // Start of line: Set start of line defaults
542 // Hold Mosaics mode: Remember last mosaic char/charset
543 // for next spacing code
544 bool HoldMosaics=false;
545 unsigned char HoldMosaicChar=' ';
546 enumCharsets HoldMosaicCharset=FirstG0;
548 enumSizeMode Size=sizeNormal;
549 // Font size modification
550 bool SecondCharset=false;
551 // Use primary or secondary G0 charset
552 bool GraphicCharset=false;
553 // Graphics charset used?
554 bool SeparateGraphics=false;
555 // Use separated vs. contiguous graphics charset
556 bool NoNextChar=false;
557 // Skip display of next char, for double-width
559 // Skip next line, for double-height
562 // auto.initialized to everything off
563 c.SetFGColor(ttcWhite);
564 c.SetBGColor(ttcBlack);
565 c.SetCharset(FirstG0);
567 if (y==0 && (flags&0x10)) {
568 if (!inkeying ) c.SetBoxedOut(true);
572 if (!(inkeying && y==0) ) c.SetBoxedOut(true);
581 c2.SetChar(keyindigits[x-3]);
590 // Pre-scan for double-height and double-size codes
592 if (y==0 && x<8) x=8;
593 if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F)
599 unsigned char ttc=curpage[y][x] & 0x7f;
602 if (y==0 && x<8) continue;
603 if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue;
604 // no displayable data here...
606 /* // Debug only: Output line data and spacing codes
609 printf("%s ",names[ttc]);
612 if (x==39) printf("\n");
616 // Handle all 'Set-At' spacing codes
621 case 0x0C: // Normal Size
622 if (Size!=sizeNormal) {
625 HoldMosaicCharset=FirstG0;
628 case 0x18: // Conceal
631 case 0x19: // Contiguous Mosaic Graphics
632 SeparateGraphics=false;
634 c.SetCharset(CHARSET_GRAPHICS_G1);
636 case 0x1A: // Separated Mosaic Graphics
637 SeparateGraphics=true;
639 c.SetCharset(CHARSET_GRAPHICS_G1_SEP);
641 case 0x1C: // Black Background
642 c.SetBGColor(ttcBlack);
644 case 0x1D: // New Background
645 c.SetBGColor(c.GetFGColor());
647 case 0x1E: // Hold Mosaic
652 // temporary copy of character data:
654 // c2 will be text character or space character or hold mosaic
655 // c2 may also have temporary flags or charsets
658 // Spacing code, display space or hold mosaic
660 c2.SetChar(HoldMosaicChar);
661 c2.SetCharset(HoldMosaicCharset);
668 if (GraphicCharset) {
670 // real graphics code, remember for HoldMosaics
672 HoldMosaicCharset=c.GetCharset();
674 // invalid code, pass-through to G0
675 c2.SetCharset(SecondCharset?SecondG0:FirstG0);
680 // Handle double-height and double-width extremes
682 if (Size==sizeDoubleHeight) Size=sizeNormal;
683 if (Size==sizeDoubleSize) Size=sizeDoubleWidth;
686 if (Size==sizeDoubleWidth) Size=sizeNormal;
687 if (Size==sizeDoubleSize) Size=sizeDoubleHeight;
690 // Now set character code
693 // Suppress this char due to double width last char
700 if (EmptyNextLine && y<23) {
701 // Clean up next line
702 setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0));
705 case sizeDoubleWidth:
707 setChar(x,y,c2.ToDblWidth(dblw_Left));
708 setChar(x+1,y,c2.ToDblWidth(dblw_Right));
709 if (EmptyNextLine && y<23) {
710 // Clean up next line
711 setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0));
712 setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0));
716 case sizeDoubleHeight:
718 setChar(x,y,c2.ToDblHeight(dblh_Top));
719 setChar(x,y+1,c2.ToDblHeight(dblh_Bottom));
723 setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left ));
724 setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right));
725 setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left ));
726 setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right));
732 // Handle all 'Set-After' spacing codes
733 if (ttc>=0x00 && ttc<=0x07) { // Set FG color
734 if (GraphicCharset) {
735 // Actual switch from graphics charset
737 HoldMosaicCharset=FirstG0;
739 c.SetFGColor((enumTeletextColor)ttc);
740 c.SetCharset(SecondCharset?SecondG0:FirstG0);
741 GraphicCharset=false;
743 } else if (ttc==0x08) {
745 } else if (ttc==0x0A) {
747 } else if (ttc==0x0B) {
749 c.SetBoxedOut(false);
750 } else if (ttc==0x0D) {
751 if (Size!=sizeDoubleHeight) {
752 Size=sizeDoubleHeight;
754 HoldMosaicCharset=FirstG0;
756 } else if (ttc==0x0E) {
757 if (Size!=sizeDoubleWidth) {
758 Size=sizeDoubleWidth;
760 HoldMosaicCharset=FirstG0;
762 } else if (ttc==0x0E) {
763 if (Size!=sizeDoubleSize) {
766 HoldMosaicCharset=FirstG0;
768 } else if (ttc>=0x10 && ttc<=0x17) {
769 if (!GraphicCharset) {
770 // Actual switch to graphics charset
772 HoldMosaicCharset=FirstG0;
774 c.SetFGColor((enumTeletextColor)(ttc-0x10));
775 c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1);
778 } else if (ttc==0x1B) {
779 SecondCharset=!SecondCharset;
780 if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0);
781 } else if (ttc==0x1F) {
786 if (renderfirstlineonly) break;
790 // Clean out last line
792 c.SetFGColor(ttcWhite);
793 c.SetBGColor(ttcBlack);
794 c.SetCharset(FirstG0);
812 if (dirty && txtview!=NULL ) {
814 if ( !renderfirstlineonly) {
815 Message* m= new Message();
816 m->message = Message::TELETEXTUPDATE;
820 Command::getInstance()->postMessageFromOuterSpace(m);
821 } else if (firstlineupdate==10) {
822 Message* m= new Message();
823 m->message = Message::TELETEXTUPDATEFIRSTLINE;
827 Command::getInstance()->postMessageFromOuterSpace(m);
829 } else firstlineupdate++;