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