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