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, see <https://www.gnu.org/licenses/>.
21 /* Portions from vdr osdteletext plugin "txtrender.c": */
22 /***************************************************************************
24 * txtrender.c - Teletext display abstraction and teletext code *
27 * This program is free software; you can redistribute it and/or modify *
28 * it under the terms of the GNU General Public License as published by *
29 * the Free Software Foundation; either version 2 of the License, or *
30 * (at your option) any later version. *
33 * 2005-03 initial version (c) Udo Richter *
35 ***************************************************************************/
37 #include "teletextdecodervbiebu.h"
38 #include "teletxt/tables.h"
40 #include "messagequeue.h"
47 TeletextDecoderVBIEBU::TeletextDecoderVBIEBU()
60 char digits[]={'1','0','0'};
61 setKeyinDigits(digits,false);
63 for (int i=0;i<10;i++) record_pages[i]=-1;
68 TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU()
74 long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync)
79 void TeletextDecoderVBIEBU::ResetTimeOffsets()
86 void TeletextDecoderVBIEBU::ResetDecoder()
93 char digits[]={'1','0','0'};
94 setKeyinDigits(digits,false);
95 for (int i=0;i<10;i++) record_pages[i]=-1;
99 void TeletextDecoderVBIEBU::setPage(unsigned int newpage)
101 selectedpage=newpage;
105 for (int i=0;i<25;i++) {
106 memset(curpage[i],0,40);
110 void TeletextDecoderVBIEBU::CleanPage()
113 for (int i=0;i<25;i++) {
114 memset(curpage[i],0,40);
119 c.SetFGColor(ttcWhite);
120 c.SetBGColor(ttcBlack);
121 c.SetCharset(CHARSET_LATIN_G0);
131 void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying)
137 c.SetFGColor(ttcWhite);
138 c.SetBGColor(ttcBlack);
139 c.SetCharset(CHARSET_LATIN_G0);
140 c.SetChar(digits[x]);
145 keyindigits[x]=digits[x];
149 void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos)
151 mediapacket = mplist.front();
156 #pragma warning(disable : 4146)
159 static ULLONG TxtPTSDifference(ULLONG pts1, ULLONG pts2)
161 // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
168 UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)
170 if (mediapacket.type != MPTYPE_TELETEXT)
175 unsigned int headerstrip;
176 unsigned char txtdata[43];
177 headerstrip=buffer[mediapacket.pos_buffer+8]+9;
178 //headerstrip+=4; //substream id
179 unsigned int datapos=0;
180 unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0];
181 //Chris should we use here the pts data from mediapacket ?
182 // in this case the data fields should also be added in mediamvp
183 if (mediapacket.synched)
184 { // An entry exists in the work list
185 ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
187 ULLONG ptsdifference=TxtPTSDifference(mediapacket.pts, nowPTS);
189 if (ptsdifference >= (120LL*90000LL)) {
191 return 1;//bad data skip it
193 if (nowPTS < (mediapacket.pts-4000) ) {
199 if (( datatype>=0x10 && datatype <=0x1F) ||
200 ( datatype>=0x99 && datatype <=0x9B)) {
203 while (datapos< (mediapacket.length-headerstrip)) {
204 unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos];
206 unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos];
210 case 0x03: {//Teletext with and without subtitles
211 //call teletext decoder
212 unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos];
214 if ((datapos+44)< (mediapacket.length-headerstrip)) {
215 for (int i=0;i<42;i++) {
216 txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]];
218 DecodeTeletext(txtdata,field);
221 case 0xC0: //inverted Teletext
222 //call teletext decoder
226 //what to do with this, in the moment we do not need it
229 //what to do with this, in the moment we do not need it
232 //what to do with this, in the moment we do not need it
234 case 0xC6: //monochrome 4:2:2 samples, what is that?
235 //what to do with this, in the moment we do not need it
238 // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard
242 datapos+=unit_length;
243 // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) {
244 // datapos++; //stuffing bytes
255 return 1; //Skip it! and discard data
261 // This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg
262 void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!!
265 for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4);
266 int header=hdrbuf[0];
267 int magazin=header & 0x7;
268 int line = (header>>3) & 0x1f;
269 if (magazin==0) magazin=8;
275 RenderTeletextCode(false);
277 RenderTeletextCode(true);
279 int pagenumber=hdrbuf[1];
280 int pagemagazin=magazin<<8 | pagenumber;
281 // int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f);
283 if (pagemagazin == selectedpage) ourpage=true;
286 for (int i=0;i<10;i++) {
287 if (pagemagazin==record_pages[i]) break;
288 if (record_pages[i]==-1) {
289 record_pages[i]=pagemagazin;
294 if (hdrbuf[3] &0x80) { //This is a subtitle
295 for (int i=0;i<10;i++) {
296 if (pagemagazin==record_pages[i]) break;
297 if (record_pages[i]==-1) {
298 record_pages[i]=pagemagazin;
306 lang=((hdrbuf[4]>>5) & 0x07);
307 flags=hdrbuf[2] & 0x80;
308 flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //??????
309 flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01);
310 for (int i=0;i<25;i++) {
311 memset(curpage[i],0,40);
315 memcpy(curpage[line],buffer+2,40);
316 } else if (ourpage && (line<=25)) {
317 memcpy(curpage[line],buffer+2,40);
323 /* from osdteletext plugin: (slightly adapted for vomp)*/
326 // teletext uses 7-bit numbers to identify a font set.
327 // There are three font sets involved:
328 // Primary G0, Secondary G0, and G2 font set.
330 // Font tables are organized in blocks of 8 fonts:
332 enumCharsets FontBlockG0_0000[8] = {
335 CHARSET_LATIN_G0_SV_FI,
338 CHARSET_LATIN_G0_PT_ES,
339 CHARSET_LATIN_G0_CZ_SK,
343 enumCharsets FontBlockG2Latin[8]={
354 enumCharsets FontBlockG0_0001[8] = {
357 CHARSET_LATIN_G0_SV_FI,
361 CHARSET_LATIN_G0_CZ_SK,
365 enumCharsets FontBlockG0_0010[8] = {
368 CHARSET_LATIN_G0_SV_FI,
371 CHARSET_LATIN_G0_PT_ES,
377 enumCharsets FontBlockG0_0011[8] = {
383 CHARSET_LATIN_G0_SR_HR_SL,
388 enumCharsets FontBlockG0_0100[8] = {
389 CHARSET_CYRILLIC_G0_SR_HR,
392 CHARSET_LATIN_G0_LV_LT,
393 CHARSET_CYRILLIC_G0_RU_BG,
394 CHARSET_CYRILLIC_G0_UK,
395 CHARSET_LATIN_G0_CZ_SK,
399 enumCharsets FontBlockG2_0100[8] = {
410 enumCharsets FontBlockG0_0110[8] = {
421 enumCharsets FontBlockG2_0110[8] = {
432 enumCharsets FontBlockG0_1000[8] = {
443 enumCharsets FontBlockG2_1000[8] = {
454 enumCharsets FontBlockG0_1010[8] = {
465 enumCharsets FontBlockG2_1010[8] = {
476 enumCharsets FontBlockInvalid[8] = {
489 // The actual font table definition:
490 // Split the 7-bit number into upper 4 and lower 3 bits,
491 // use upper 4 bits for outer array,
492 // use lower 3 bits for inner array
494 struct structFontBlock {
495 enumCharsets *G0Block;
496 enumCharsets *G2Block;
499 structFontBlock FontTable[16] = {
500 { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block
501 { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block
502 { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block
503 { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block
504 { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block
505 { FontBlockInvalid, FontBlockInvalid }, // 0101 block
506 { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block
507 { FontBlockInvalid, FontBlockInvalid }, // 0111 block
508 { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block
509 { FontBlockInvalid, FontBlockInvalid }, // 1001 block
510 { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block
511 { FontBlockInvalid, FontBlockInvalid }, // 1011 block
512 { FontBlockInvalid, FontBlockInvalid }, // 1100 block
513 { FontBlockInvalid, FontBlockInvalid }, // 1101 block
514 { FontBlockInvalid, FontBlockInvalid }, // 1110 block
515 { FontBlockInvalid, FontBlockInvalid } // 1111 block
518 inline enumCharsets GetG0Charset(int codepage) {
519 return FontTable[codepage>>3].G0Block[codepage&7];
521 inline enumCharsets GetG2Charset(int codepage) {
522 return FontTable[codepage>>3].G2Block[codepage&7];
526 // Possible size modifications of characters
533 void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) {
535 bool EmptyNextLine=false;
536 // Skip one line, in case double height chars were/will be used
539 int LocalG0CodePage=(FirstG0CodePage & 0x78)
540 | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2);
541 enumCharsets FirstG0=GetG0Charset(LocalG0CodePage);
542 enumCharsets SecondG0=GetG0Charset(SecondG0CodePage);
543 // Reserved for later use:
544 // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage);
546 for (y=0;y<24;(EmptyNextLine?y+=2:y++)) {
547 // Start of line: Set start of line defaults
549 // Hold Mosaics mode: Remember last mosaic char/charset
550 // for next spacing code
551 bool HoldMosaics=false;
552 unsigned char HoldMosaicChar=' ';
553 enumCharsets HoldMosaicCharset=FirstG0;
555 enumSizeMode Size=sizeNormal;
556 // Font size modification
557 bool SecondCharset=false;
558 // Use primary or secondary G0 charset
559 bool GraphicCharset=false;
560 // Graphics charset used?
561 bool SeparateGraphics=false;
562 // Use separated vs. contiguous graphics charset
563 bool NoNextChar=false;
564 // Skip display of next char, for double-width
566 // Skip next line, for double-height
569 // auto.initialized to everything off
570 c.SetFGColor(ttcWhite);
571 c.SetBGColor(ttcBlack);
572 c.SetCharset(FirstG0);
574 if (y==0 && (flags&0x10)) {
575 if (!inkeying ) c.SetBoxedOut(true);
579 if (!(inkeying && y==0) ) c.SetBoxedOut(true);
588 c2.SetChar(keyindigits[x-3]);
597 // Pre-scan for double-height and double-size codes
599 if (y==0 && x<8) x=8;
600 if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F)
606 unsigned char ttc=curpage[y][x] & 0x7f;
609 if (y==0 && x<8) continue;
610 if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue;
611 // no displayable data here...
613 /* // Debug only: Output line data and spacing codes
616 printf("%s ",names[ttc]);
619 if (x==39) printf("\n");
623 // Handle all 'Set-At' spacing codes
628 case 0x0C: // Normal Size
629 if (Size!=sizeNormal) {
632 HoldMosaicCharset=FirstG0;
635 case 0x18: // Conceal
638 case 0x19: // Contiguous Mosaic Graphics
639 SeparateGraphics=false;
641 c.SetCharset(CHARSET_GRAPHICS_G1);
643 case 0x1A: // Separated Mosaic Graphics
644 SeparateGraphics=true;
646 c.SetCharset(CHARSET_GRAPHICS_G1_SEP);
648 case 0x1C: // Black Background
649 c.SetBGColor(ttcBlack);
651 case 0x1D: // New Background
652 c.SetBGColor(c.GetFGColor());
654 case 0x1E: // Hold Mosaic
659 // temporary copy of character data:
661 // c2 will be text character or space character or hold mosaic
662 // c2 may also have temporary flags or charsets
665 // Spacing code, display space or hold mosaic
667 c2.SetChar(HoldMosaicChar);
668 c2.SetCharset(HoldMosaicCharset);
675 if (GraphicCharset) {
677 // real graphics code, remember for HoldMosaics
679 HoldMosaicCharset=c.GetCharset();
681 // invalid code, pass-through to G0
682 c2.SetCharset(SecondCharset?SecondG0:FirstG0);
687 // Handle double-height and double-width extremes
689 if (Size==sizeDoubleHeight) Size=sizeNormal;
690 if (Size==sizeDoubleSize) Size=sizeDoubleWidth;
693 if (Size==sizeDoubleWidth) Size=sizeNormal;
694 if (Size==sizeDoubleSize) Size=sizeDoubleHeight;
697 // Now set character code
700 // Suppress this char due to double width last char
707 if (EmptyNextLine && y<23) {
708 // Clean up next line
709 setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0));
712 case sizeDoubleWidth:
714 setChar(x,y,c2.ToDblWidth(dblw_Left));
715 setChar(x+1,y,c2.ToDblWidth(dblw_Right));
716 if (EmptyNextLine && y<23) {
717 // Clean up next line
718 setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0));
719 setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0));
723 case sizeDoubleHeight:
725 setChar(x,y,c2.ToDblHeight(dblh_Top));
726 setChar(x,y+1,c2.ToDblHeight(dblh_Bottom));
730 setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left ));
731 setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right));
732 setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left ));
733 setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right));
739 // Handle all 'Set-After' spacing codes
740 if (ttc>=0x00 && ttc<=0x07) { // Set FG color
741 if (GraphicCharset) {
742 // Actual switch from graphics charset
744 HoldMosaicCharset=FirstG0;
746 c.SetFGColor((enumTeletextColor)ttc);
747 c.SetCharset(SecondCharset?SecondG0:FirstG0);
748 GraphicCharset=false;
750 } else if (ttc==0x08) {
752 } else if (ttc==0x0A) {
754 } else if (ttc==0x0B) {
756 c.SetBoxedOut(false);
757 } else if (ttc==0x0D) {
758 if (Size!=sizeDoubleHeight) {
759 Size=sizeDoubleHeight;
761 HoldMosaicCharset=FirstG0;
763 } else if (ttc==0x0E) {
764 if (Size!=sizeDoubleWidth) {
765 Size=sizeDoubleWidth;
767 HoldMosaicCharset=FirstG0;
769 } else if (ttc==0x0E) {
770 if (Size!=sizeDoubleSize) {
773 HoldMosaicCharset=FirstG0;
775 } else if (ttc>=0x10 && ttc<=0x17) {
776 if (!GraphicCharset) {
777 // Actual switch to graphics charset
779 HoldMosaicCharset=FirstG0;
781 c.SetFGColor((enumTeletextColor)(ttc-0x10));
782 c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1);
785 } else if (ttc==0x1B) {
786 SecondCharset=!SecondCharset;
787 if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0);
788 } else if (ttc==0x1F) {
793 if (renderfirstlineonly) break;
797 // Clean out last line
799 c.SetFGColor(ttcWhite);
800 c.SetBGColor(ttcBlack);
801 c.SetCharset(FirstG0);
819 if (dirty && txtview!=NULL ) {
821 if ( !renderfirstlineonly) {
822 Message* m= new Message();
823 m->message = Message::TELETEXTUPDATE;
827 MessageQueue::getInstance()->postMessage(m);
828 } else if (firstlineupdate==10) {
829 Message* m= new Message();
830 m->message = Message::TELETEXTUPDATEFIRSTLINE;
834 MessageQueue::getInstance()->postMessage(m);
836 } else firstlineupdate++;