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();
157 #pragma warning(disable : 4146)
160 static ULLONG TxtPTSDifference(ULLONG pts1, ULLONG pts2)
162 // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
169 UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)
171 if (mediapacket.type != MPTYPE_TELETEXT)
176 unsigned int headerstrip;
177 unsigned char txtdata[43];
178 headerstrip=buffer[mediapacket.pos_buffer+8]+9;
179 //headerstrip+=4; //substream id
180 unsigned int datapos=0;
181 unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0];
182 //Chris should we use here the pts data from mediapacket ?
183 // in this case the data fields should also be added in mediamvp
184 if (mediapacket.synched)
185 { // An entry exists in the work list
186 ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
188 ULLONG ptsdifference=TxtPTSDifference(mediapacket.pts, nowPTS);
190 if (ptsdifference >= (120LL*90000LL)) {
192 return 1;//bad data skip it
194 if (nowPTS < (mediapacket.pts-4000) ) {
200 if (( datatype>=0x10 && datatype <=0x1F) ||
201 ( datatype>=0x99 && datatype <=0x9B)) {
204 while (datapos< (mediapacket.length-headerstrip)) {
205 unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos];
207 unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos];
211 case 0x03: {//Teletext with and without subtitles
212 //call teletext decoder
213 unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos];
215 if ((datapos+44)< (mediapacket.length-headerstrip)) {
216 for (int i=0;i<42;i++) {
217 txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]];
219 DecodeTeletext(txtdata,field);
222 case 0xC0: //inverted Teletext
223 //call teletext decoder
227 //what to do with this, in the moment we do not need it
230 //what to do with this, in the moment we do not need it
233 //what to do with this, in the moment we do not need it
235 case 0xC6: //monochrome 4:2:2 samples, what is that?
236 //what to do with this, in the moment we do not need it
239 // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard
243 datapos+=unit_length;
244 // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) {
245 // datapos++; //stuffing bytes
256 return 1; //Skip it! and discard data
262 // This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg
263 void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!!
266 for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4);
267 int header=hdrbuf[0];
268 int magazin=header & 0x7;
269 int line = (header>>3) & 0x1f;
270 if (magazin==0) magazin=8;
276 RenderTeletextCode(false);
278 RenderTeletextCode(true);
280 int pagenumber=hdrbuf[1];
281 int pagemagazin=magazin<<8 | pagenumber;
282 // int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f);
284 if (pagemagazin == selectedpage) ourpage=true;
287 for (int i=0;i<10;i++) {
288 if (pagemagazin==record_pages[i]) break;
289 if (record_pages[i]==-1) {
290 record_pages[i]=pagemagazin;
295 if (hdrbuf[3] &0x80) { //This is a subtitle
296 for (int i=0;i<10;i++) {
297 if (pagemagazin==record_pages[i]) break;
298 if (record_pages[i]==-1) {
299 record_pages[i]=pagemagazin;
307 lang=((hdrbuf[4]>>5) & 0x07);
308 flags=hdrbuf[2] & 0x80;
309 flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //??????
310 flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01);
311 for (int i=0;i<25;i++) {
312 memset(curpage[i],0,40);
316 memcpy(curpage[line],buffer+2,40);
317 } else if (ourpage && (line<=25)) {
318 memcpy(curpage[line],buffer+2,40);
324 /* from osdteletext plugin: (slightly adapted for vomp)*/
327 // teletext uses 7-bit numbers to identify a font set.
328 // There are three font sets involved:
329 // Primary G0, Secondary G0, and G2 font set.
331 // Font tables are organized in blocks of 8 fonts:
333 enumCharsets FontBlockG0_0000[8] = {
336 CHARSET_LATIN_G0_SV_FI,
339 CHARSET_LATIN_G0_PT_ES,
340 CHARSET_LATIN_G0_CZ_SK,
344 enumCharsets FontBlockG2Latin[8]={
355 enumCharsets FontBlockG0_0001[8] = {
358 CHARSET_LATIN_G0_SV_FI,
362 CHARSET_LATIN_G0_CZ_SK,
366 enumCharsets FontBlockG0_0010[8] = {
369 CHARSET_LATIN_G0_SV_FI,
372 CHARSET_LATIN_G0_PT_ES,
378 enumCharsets FontBlockG0_0011[8] = {
384 CHARSET_LATIN_G0_SR_HR_SL,
389 enumCharsets FontBlockG0_0100[8] = {
390 CHARSET_CYRILLIC_G0_SR_HR,
393 CHARSET_LATIN_G0_LV_LT,
394 CHARSET_CYRILLIC_G0_RU_BG,
395 CHARSET_CYRILLIC_G0_UK,
396 CHARSET_LATIN_G0_CZ_SK,
400 enumCharsets FontBlockG2_0100[8] = {
411 enumCharsets FontBlockG0_0110[8] = {
422 enumCharsets FontBlockG2_0110[8] = {
433 enumCharsets FontBlockG0_1000[8] = {
444 enumCharsets FontBlockG2_1000[8] = {
455 enumCharsets FontBlockG0_1010[8] = {
466 enumCharsets FontBlockG2_1010[8] = {
477 enumCharsets FontBlockInvalid[8] = {
490 // The actual font table definition:
491 // Split the 7-bit number into upper 4 and lower 3 bits,
492 // use upper 4 bits for outer array,
493 // use lower 3 bits for inner array
495 struct structFontBlock {
496 enumCharsets *G0Block;
497 enumCharsets *G2Block;
500 structFontBlock FontTable[16] = {
501 { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block
502 { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block
503 { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block
504 { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block
505 { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block
506 { FontBlockInvalid, FontBlockInvalid }, // 0101 block
507 { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block
508 { FontBlockInvalid, FontBlockInvalid }, // 0111 block
509 { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block
510 { FontBlockInvalid, FontBlockInvalid }, // 1001 block
511 { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block
512 { FontBlockInvalid, FontBlockInvalid }, // 1011 block
513 { FontBlockInvalid, FontBlockInvalid }, // 1100 block
514 { FontBlockInvalid, FontBlockInvalid }, // 1101 block
515 { FontBlockInvalid, FontBlockInvalid }, // 1110 block
516 { FontBlockInvalid, FontBlockInvalid } // 1111 block
519 inline enumCharsets GetG0Charset(int codepage) {
520 return FontTable[codepage>>3].G0Block[codepage&7];
522 inline enumCharsets GetG2Charset(int codepage) {
523 return FontTable[codepage>>3].G2Block[codepage&7];
527 // Possible size modifications of characters
534 void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) {
536 bool EmptyNextLine=false;
537 // Skip one line, in case double height chars were/will be used
540 int LocalG0CodePage=(FirstG0CodePage & 0x78)
541 | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2);
542 enumCharsets FirstG0=GetG0Charset(LocalG0CodePage);
543 enumCharsets SecondG0=GetG0Charset(SecondG0CodePage);
544 // Reserved for later use:
545 // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage);
547 for (y=0;y<24;(EmptyNextLine?y+=2:y++)) {
548 // Start of line: Set start of line defaults
550 // Hold Mosaics mode: Remember last mosaic char/charset
551 // for next spacing code
552 bool HoldMosaics=false;
553 unsigned char HoldMosaicChar=' ';
554 enumCharsets HoldMosaicCharset=FirstG0;
556 enumSizeMode Size=sizeNormal;
557 // Font size modification
558 bool SecondCharset=false;
559 // Use primary or secondary G0 charset
560 bool GraphicCharset=false;
561 // Graphics charset used?
562 bool SeparateGraphics=false;
563 // Use separated vs. contiguous graphics charset
564 bool NoNextChar=false;
565 // Skip display of next char, for double-width
567 // Skip next line, for double-height
570 // auto.initialized to everything off
571 c.SetFGColor(ttcWhite);
572 c.SetBGColor(ttcBlack);
573 c.SetCharset(FirstG0);
575 if (y==0 && (flags&0x10)) {
576 if (!inkeying ) c.SetBoxedOut(true);
580 if (!(inkeying && y==0) ) c.SetBoxedOut(true);
589 c2.SetChar(keyindigits[x-3]);
598 // Pre-scan for double-height and double-size codes
600 if (y==0 && x<8) x=8;
601 if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F)
607 unsigned char ttc=curpage[y][x] & 0x7f;
610 if (y==0 && x<8) continue;
611 if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue;
612 // no displayable data here...
614 /* // Debug only: Output line data and spacing codes
617 printf("%s ",names[ttc]);
620 if (x==39) printf("\n");
624 // Handle all 'Set-At' spacing codes
629 case 0x0C: // Normal Size
630 if (Size!=sizeNormal) {
633 HoldMosaicCharset=FirstG0;
636 case 0x18: // Conceal
639 case 0x19: // Contiguous Mosaic Graphics
640 SeparateGraphics=false;
642 c.SetCharset(CHARSET_GRAPHICS_G1);
644 case 0x1A: // Separated Mosaic Graphics
645 SeparateGraphics=true;
647 c.SetCharset(CHARSET_GRAPHICS_G1_SEP);
649 case 0x1C: // Black Background
650 c.SetBGColor(ttcBlack);
652 case 0x1D: // New Background
653 c.SetBGColor(c.GetFGColor());
655 case 0x1E: // Hold Mosaic
660 // temporary copy of character data:
662 // c2 will be text character or space character or hold mosaic
663 // c2 may also have temporary flags or charsets
666 // Spacing code, display space or hold mosaic
668 c2.SetChar(HoldMosaicChar);
669 c2.SetCharset(HoldMosaicCharset);
676 if (GraphicCharset) {
678 // real graphics code, remember for HoldMosaics
680 HoldMosaicCharset=c.GetCharset();
682 // invalid code, pass-through to G0
683 c2.SetCharset(SecondCharset?SecondG0:FirstG0);
688 // Handle double-height and double-width extremes
690 if (Size==sizeDoubleHeight) Size=sizeNormal;
691 if (Size==sizeDoubleSize) Size=sizeDoubleWidth;
694 if (Size==sizeDoubleWidth) Size=sizeNormal;
695 if (Size==sizeDoubleSize) Size=sizeDoubleHeight;
698 // Now set character code
701 // Suppress this char due to double width last char
708 if (EmptyNextLine && y<23) {
709 // Clean up next line
710 setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0));
713 case sizeDoubleWidth:
715 setChar(x,y,c2.ToDblWidth(dblw_Left));
716 setChar(x+1,y,c2.ToDblWidth(dblw_Right));
717 if (EmptyNextLine && y<23) {
718 // Clean up next line
719 setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0));
720 setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0));
724 case sizeDoubleHeight:
726 setChar(x,y,c2.ToDblHeight(dblh_Top));
727 setChar(x,y+1,c2.ToDblHeight(dblh_Bottom));
731 setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left ));
732 setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right));
733 setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left ));
734 setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right));
740 // Handle all 'Set-After' spacing codes
741 if (ttc>=0x00 && ttc<=0x07) { // Set FG color
742 if (GraphicCharset) {
743 // Actual switch from graphics charset
745 HoldMosaicCharset=FirstG0;
747 c.SetFGColor((enumTeletextColor)ttc);
748 c.SetCharset(SecondCharset?SecondG0:FirstG0);
749 GraphicCharset=false;
751 } else if (ttc==0x08) {
753 } else if (ttc==0x0A) {
755 } else if (ttc==0x0B) {
757 c.SetBoxedOut(false);
758 } else if (ttc==0x0D) {
759 if (Size!=sizeDoubleHeight) {
760 Size=sizeDoubleHeight;
762 HoldMosaicCharset=FirstG0;
764 } else if (ttc==0x0E) {
765 if (Size!=sizeDoubleWidth) {
766 Size=sizeDoubleWidth;
768 HoldMosaicCharset=FirstG0;
770 } else if (ttc==0x0E) {
771 if (Size!=sizeDoubleSize) {
774 HoldMosaicCharset=FirstG0;
776 } else if (ttc>=0x10 && ttc<=0x17) {
777 if (!GraphicCharset) {
778 // Actual switch to graphics charset
780 HoldMosaicCharset=FirstG0;
782 c.SetFGColor((enumTeletextColor)(ttc-0x10));
783 c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1);
786 } else if (ttc==0x1B) {
787 SecondCharset=!SecondCharset;
788 if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0);
789 } else if (ttc==0x1F) {
794 if (renderfirstlineonly) break;
798 // Clean out last line
800 c.SetFGColor(ttcWhite);
801 c.SetBGColor(ttcBlack);
802 c.SetCharset(FirstG0);
820 if (dirty && txtview!=NULL ) {
822 if ( !renderfirstlineonly) {
823 Message* m= new Message();
824 m->message = Message::TELETEXTUPDATE;
827 m->parameter.num = 0;
828 Command::getInstance()->postMessageFromOuterSpace(m);
829 } else if (firstlineupdate==10) {
830 Message* m= new Message();
831 m->message = Message::TELETEXTUPDATEFIRSTLINE;
834 m->parameter.num = 0;
835 Command::getInstance()->postMessageFromOuterSpace(m);
837 } else firstlineupdate++;