MythTV master
teletextreader.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstring>
5
6#include "captions/vbilut.h"
7#include "tv.h"
9#include "tv_actions.h"
11
12static inline int MAGAZINE(int page) { return page / 256; };
13
15{
16 m_bitswap.fill(0);
17 for (int i = 0; i < 256; i++)
18 {
19 for (int bit = 0; bit < 8; bit++)
20 if (i & (1 << bit))
21 m_bitswap[i] |= (1 << (7-bit));
22 }
23 Reset();
24}
25
26bool TeletextReader::KeyPress(const QString &Key, bool& Exit)
27{
28 int newPage = m_curpage;
29 int newSubPage = m_cursubpage;
30 bool numeric_input = false;
31
32 if (Key == "MENU" || Key == ACTION_TOGGLETT || Key == "ESCAPE")
33 {
34 Exit = true;
35 return true;
36 }
37
39
40 if (Key == ACTION_0 || Key == ACTION_1 || Key == ACTION_2 ||
41 Key == ACTION_3 || Key == ACTION_4 || Key == ACTION_5 ||
42 Key == ACTION_6 || Key == ACTION_7 || Key == ACTION_8 ||
43 Key == ACTION_9)
44 {
45 numeric_input = true;
47 if (m_pageinput[0] == ' ')
48 m_pageinput[0] = '0' + Key.toInt();
49 else if (m_pageinput[1] == ' ')
50 m_pageinput[1] = '0' + Key.toInt();
51 else if (m_pageinput[2] == ' ')
52 {
53 m_pageinput[2] = '0' + Key.toInt();
54 newPage = ((m_pageinput[0] - '0') * 256) +
55 ((m_pageinput[1] - '0') * 16) +
56 (m_pageinput[2] - '0');
57 newSubPage = -1;
58 }
59 else
60 {
61 m_pageinput[0] = '0' + Key.toInt();
62 m_pageinput[1] = ' ';
63 m_pageinput[2] = ' ';
64 }
65
67 }
68 else if (Key == ACTION_NEXTPAGE)
69 {
70 TeletextPage *ttpage = FindPage(m_curpage, 1);
71 if (ttpage)
72 newPage = ttpage->pagenum;
73 newSubPage = -1;
75 }
76 else if (Key == ACTION_PREVPAGE)
77 {
78 TeletextPage *ttpage = FindPage(m_curpage, -1);
79 if (ttpage)
80 newPage = ttpage->pagenum;
81 newSubPage = -1;
83 }
84 else if (Key == ACTION_NEXTSUBPAGE)
85 {
87 if (ttpage)
88 newSubPage = ttpage->subpagenum;
90 }
91 else if (Key == ACTION_PREVSUBPAGE)
92 {
94 if (ttpage)
95 newSubPage = ttpage->subpagenum;
97 }
98 else if (Key == ACTION_TOGGLEBACKGROUND)
99 {
102 }
103 else if (Key == ACTION_REVEAL)
104 {
107 }
108 else if (Key == ACTION_MENURED)
109 {
110 if (!curpage)
111 return true;
112
113 TeletextPage *page = FindPage(curpage->floflink[0]);
114 if (page != nullptr)
115 {
116 newPage = page->pagenum;
117 newSubPage = -1;
118 m_curpageShowHeader = true;
119 }
120 }
121 else if (Key == ACTION_MENUGREEN)
122 {
123 if (!curpage)
124 return true;
125
126 TeletextPage *page = FindPage(curpage->floflink[1]);
127 if (page != nullptr)
128 {
129 newPage = page->pagenum;
130 newSubPage = -1;
131 m_curpageShowHeader = true;
132 }
133 }
134 else if (Key == ACTION_MENUYELLOW)
135 {
136 if (!curpage)
137 return true;
138
139 TeletextPage *page = FindPage(curpage->floflink[2]);
140 if (page != nullptr)
141 {
142 newPage = page->pagenum;
143 newSubPage = -1;
144 m_curpageShowHeader = true;
145 }
146 }
147 else if (Key == ACTION_MENUBLUE)
148 {
149 if (!curpage)
150 return true;
151
152 TeletextPage *page = FindPage(curpage->floflink[3]);
153 if (page != nullptr)
154 {
155 newPage = page->pagenum;
156 newSubPage = -1;
157 m_curpageShowHeader = true;
158 }
159 }
160 else if (Key == ACTION_MENUWHITE)
161 {
162 if (!curpage)
163 return true;
164
165 TeletextPage *page = FindPage(curpage->floflink[4]);
166 if (page != nullptr)
167 {
168 newPage = page->pagenum;
169 newSubPage = -1;
170 m_curpageShowHeader = true;
171 }
172 }
173 else
174 {
175 return false;
176 }
177
178 newPage = std::clamp(newPage, 0x100, 0x899);
179
180 if (!numeric_input)
181 {
182 m_pageinput[0] = (newPage / 256) + '0';
183 m_pageinput[1] = ((newPage % 256) / 16) + '0';
184 m_pageinput[2] = (newPage % 16) + '0';
185 }
186
187 if (newPage != m_curpage || newSubPage != m_cursubpage)
188 {
189 m_curpage = newPage;
190 m_cursubpage = newSubPage;
191 m_revealHidden = false;
193 }
194
195 return true;
196}
197
199{
200 QString str = "";
201 int mag = MAGAZINE(m_curpage);
202 if (mag > 8 || mag < 1)
203 return str;
204
205 int count = 1;
206 int selected = 0;
207 const TeletextPage *page = FindPage(m_curpage);
208 if (page)
209 {
210 m_magazines[mag - 1].lock->lock();
211 int_to_subpage_t::const_iterator subpageIter;
212 subpageIter = page->subpages.begin();
213 while (subpageIter != page->subpages.end())
214 {
215 const TeletextSubPage *subpage = &subpageIter->second;
216
217 if (subpage->subpagenum == m_cursubpage)
218 {
219 selected = count;
220 str += "*";
221 }
222 else
223 {
224 str += " ";
225 }
226
227 str += QString("%1").arg(subpage->subpagenum,2,16,QChar('0'));
228
229 ++subpageIter;
230 ++count;
231 }
232 m_magazines[mag - 1].lock->unlock();
233 }
234
235 if (str.isEmpty())
236 return str;
237
238 // if there are less than 9 subpages fill the empty slots with spaces
239 if (count < 10)
240 {
241 QString spaces;
242 spaces.fill(' ', 27 - str.length());
243 str = " <" + str + spaces + " > ";
244 }
245 else
246 {
247 // try to centralize the selected sub page in the list
248 int startPos = selected - 5;
249 startPos = std::max(startPos, 0);
250 if (startPos + 9 >= count)
251 startPos = count - 10;
252
253 str = " <" + str.mid(startPos * 3, 27) + " > ";
254 }
255 return str;
256}
257
258void TeletextReader::SetPage(int page, int subpage)
259{
260 if (page < 0x100 || page > 0x899)
261 return;
262
263 m_pageinput[0] = (page / 256) + '0';
264 m_pageinput[1] = ((page % 256) / 16) + '0';
265 m_pageinput[2] = (page % 16) + '0';
266
267 m_curpage = page;
268 m_cursubpage = subpage;
270}
271
273{
274 for (auto & mag : m_magazines)
275 {
276 QMutexLocker lock(mag.lock);
277
278 // clear all sub pages in page
279 int_to_page_t::iterator iter;
280 iter = mag.pages.begin();
281 while (iter != mag.pages.end())
282 {
283 TeletextPage *page = &iter->second;
284 page->subpages.clear();
285 ++iter;
286 }
287
288 // clear pages
289 mag.pages.clear();
290 mag.current_page = 0;
291 mag.current_subpage = 0;
292 mag.loadingpage.active = false;
293 }
294 m_header.fill(' ');
295
296 m_curpage = 0x100;
297 m_cursubpage = -1;
298 m_curpageShowHeader = true;
299
300 m_pageinput[0] = '1';
301 m_pageinput[1] = '0';
302 m_pageinput[2] = '0';
303}
304
305void TeletextReader::AddPageHeader(int page, int subpage, const uint8_t *buf,
306 int vbimode, int lang, int flags)
307{
308 int magazine = MAGAZINE(page);
309 if (magazine < 1 || magazine > 8)
310 return;
311
312 for(int m = 1; m <= 8; m++)
313 {
314 // ETS 300 706, chapter 7.2.1:
315 // The transmission of a given page begins with, and includes, its page
316 // header packet. It is terminated by and excludes the next page header
317 // packet having the same magazine address in parallel transmission
318 // mode, or any magazine address in serial transmission mode.
319 // ETS 300 706, chapter 9.3.1.3:
320 // When set to '1' the service is designated to be in Serial mode and
321 // the transmission of a page is terminated by the next page header with
322 // a different page number.
323 // When set to '0' the service is designated to be in Parallel mode and
324 // the transmission of a page is terminated by the next page header with
325 // a different page number but the same magazine number. The same
326 // setting shall be used for all page headers in the service.
327
328 bool isMagazineSerialMode = (flags & TP_MAGAZINE_SERIAL) != 0;
329 if (!(isMagazineSerialMode) && m != magazine)
330 {
331 continue; // in parallel mode only process magazine
332 }
333
334 int lastPage = m_magazines[m - 1].current_page;
335 int lastSubPage = m_magazines[m - 1].current_subpage;
336
337 LOG(VB_VBI, LOG_DEBUG,
338 QString("AddPageHeader(p %1, sp %2, lang %3, mag %4, lp %5, lsp %6"
339 " sm %7)")
340 .arg(page).arg(subpage).arg(lang).arg(m).arg(lastPage)
341 .arg(lastSubPage).arg(isMagazineSerialMode));
342
343 if ((page != lastPage || subpage != lastSubPage) &&
344 m_magazines[m - 1].loadingpage.active)
345 {
346 TeletextSubPage *ttpage = FindSubPage(lastPage, lastSubPage);
347 if (!ttpage)
348 {
349 ttpage = &(m_magazines[m - 1]
350 .pages[lastPage].subpages[lastSubPage]);
351 m_magazines[m - 1].pages[lastPage].pagenum = lastPage;
352 ttpage->subpagenum = lastSubPage;
353 }
354
355 memcpy(ttpage, &m_magazines[m - 1].loadingpage,
356 sizeof(TeletextSubPage));
357
358 m_magazines[m - 1].loadingpage.active = false;
359
360 PageUpdated(lastPage, lastSubPage);
361 }
362 }
363
364 m_fetchpage = page;
365 m_fetchsubpage = subpage;
366
367 TeletextSubPage *ttpage = &m_magazines[magazine - 1].loadingpage;
368
369 m_magazines[magazine - 1].current_page = page;
370 m_magazines[magazine - 1].current_subpage = subpage;
371
372 for (auto & line : ttpage->data)
373 line.fill(' ');
374
375 ttpage->active = true;
376 ttpage->subpagenum = subpage;
377 ttpage->floflink.fill(0);
378 ttpage->lang = lang;
379 ttpage->flags = flags;
380 ttpage->flof = 0;
381
382 ttpage->subtitle = (vbimode == VBI_DVB_SUBTITLE);
383
384 std::fill_n(ttpage->data[0].data(), 8, ' ');
385
387 {
388 for (uint j = 8; j < 40; j++)
389 ttpage->data[0][j] = m_bitswap[buf[j]];
390 }
391 else
392 {
393 std::copy(buf, buf + 40, ttpage->data[0].data());
394 }
395
396 if ( !(ttpage->flags & TP_INTERRUPTED_SEQ))
397 {
398 std::copy(ttpage->data[0].cbegin(), ttpage->data[0].cend(), m_header.data());
399 HeaderUpdated(page, subpage, ttpage->data[0],ttpage->lang);
400 }
401}
402
403void TeletextReader::AddTeletextData(int magazine, int row,
404 const uint8_t* buf, int vbimode)
405{
406 //LOG(VB_GENERAL, LOG_ERR, QString("AddTeletextData(%1, %2)")
407 // .arg(magazine).arg(row));
408
409 int b1 = 0;
410 int b2 = 0;
411 int b3 = 0;
412 int err = 0;
413
414 if (magazine < 1 || magazine > 8)
415 return;
416
417 int currentpage = m_magazines[magazine - 1].current_page;
418 if (!currentpage)
419 return;
420
421 TeletextSubPage *ttpage = &m_magazines[magazine - 1].loadingpage;
422
423 switch (row)
424 {
425 case 26:
426 /* XXX TODO: Level 1.5, 2.5, 3.5
427 * Character location & override
428 * Level 2.5, 3.5
429 * Modifying display attributes
430 * All levels
431 * VCR Programming
432 * See 12.3
433 */
434 break;
435 case 27: // FLOF data (FastText)
436 switch (vbimode)
437 {
438 case VBI_IVTV:
439 b1 = hamm8(buf, &err);
440 b2 = hamm8(buf + 37, &err);
441 if (err & 0xF000)
442 return;
443 break;
444 case VBI_DVB:
445 case VBI_DVB_SUBTITLE:
446 b1 = hamm84(buf, &err);
447 b2 = hamm84(buf + 37, &err);
448 if (err == 1)
449 return;
450 break;
451 default:
452 return;
453 }
454 if (b1 != 0 || !(b2 & 8))
455 return;
456
457 for (ptrdiff_t i = 0; i < 6; ++i)
458 {
459 err = 0;
460 switch (vbimode)
461 {
462 case VBI_IVTV:
463 b1 = hamm16(buf+1+(6*i), &err);
464 b2 = hamm16(buf+3+(6*i), &err);
465 b3 = hamm16(buf+5+(6*i), &err);
466 if (err & 0xF000)
467 return;
468 break;
469 case VBI_DVB:
470 case VBI_DVB_SUBTITLE:
471 b1 = (hamm84(buf+2+(6*i), &err) * 16) +
472 hamm84(buf+1+(6*i), &err);
473 b2 = (hamm84(buf+4+(6*i), &err) * 16) +
474 hamm84(buf+3+(6*i), &err);
475 b3 = (hamm84(buf+6+(6*i), &err) * 16) +
476 hamm84(buf+5+(6*i), &err);
477 if (err == 1)
478 return;
479 break;
480 default:
481 return;
482 }
483
484 int x = (b2 >> 7) | ((b3 >> 5) & 0x06);
485 int nTmp = (magazine ^ x);
486 ttpage->floflink[i] = (( nTmp ? nTmp : 8) * 256) + b1;
487 ttpage->flof = 1;
488 }
489 break;
490
491 case 31: // private streams
492 break;
493
494 default:
495
496 if (( row >= 1 ) && ( row <= 24 )) // Page Data
497 {
499 {
500 for (uint j = 0; j < 40; j++)
501 ttpage->data[row][j] = m_bitswap[buf[j]];
502 }
503 else
504 {
505 std::copy(buf, buf + 40, ttpage->data[row].data());
506 }
507 }
508
509 break;
510 }
511}
512
513void TeletextReader::PageUpdated(int page, int subpage)
514{
515 if (page != m_curpage)
516 return;
517 if (subpage != m_cursubpage && m_cursubpage != -1)
518 return;
519 m_pageChanged = true;
520}
521
523 [[maybe_unused]] int page,
524 [[maybe_unused]] int subpage,
525 [[maybe_unused]] tt_line_array& page_ptr,
526 [[maybe_unused]] int lang)
527{
529 return;
530
531 m_headerChanged = true;
532}
533
535 int page, int direction) const
536{
537 int mag = MAGAZINE(page);
538
539 if (mag > 8 || mag < 1)
540 return nullptr;
541
542 QMutexLocker lock(m_magazines[mag - 1].lock);
543
544 int_to_page_t::const_iterator pageIter;
545 pageIter = m_magazines[mag - 1].pages.find(page);
546 if (pageIter == m_magazines[mag - 1].pages.end())
547 return nullptr;
548
549 const TeletextPage *res = &pageIter->second;
550 if (direction == -1)
551 {
552 --pageIter;
553 if (pageIter == m_magazines[mag - 1].pages.end())
554 {
555 int_to_page_t::const_reverse_iterator iter;
556 iter = m_magazines[mag - 1].pages.rbegin();
557 res = &iter->second;
558 }
559 else
560 {
561 res = &pageIter->second;
562 }
563 }
564
565 if (direction == 1)
566 {
567 ++pageIter;
568 if (pageIter == m_magazines[mag - 1].pages.end())
569 {
570 pageIter = m_magazines[mag - 1].pages.begin();
571 res = &pageIter->second;
572 }
573 else
574 {
575 res = &pageIter->second;
576 }
577 }
578
579 return res;
580}
581
583 int page, int subpage, int direction) const
584{
585 int mag = MAGAZINE(page);
586
587 if (mag > 8 || mag < 1)
588 return nullptr;
589
590 QMutexLocker lock(m_magazines[mag - 1].lock);
591
592 int_to_page_t::const_iterator pageIter;
593 pageIter = m_magazines[mag - 1].pages.find(page);
594 if (pageIter == m_magazines[mag - 1].pages.end())
595 return nullptr;
596
597 const TeletextPage *ttpage = &(pageIter->second);
598 auto subpageIter = ttpage->subpages.cbegin();
599
600 // try to find the subpage given, or first if subpage == -1
601 if (subpage != -1)
602 subpageIter = ttpage->subpages.find(subpage);
603
604 if (subpageIter == ttpage->subpages.cend())
605 return nullptr;
606
607 if (subpage == -1)
608 return &(subpageIter->second);
609
610 const TeletextSubPage *res = &(subpageIter->second);
611 if (direction == -1)
612 {
613 --subpageIter;
614 if (subpageIter == ttpage->subpages.cend())
615 {
616 auto iter = ttpage->subpages.crbegin();
617 res = &(iter->second);
618 }
619 else
620 {
621 res = &(subpageIter->second);
622 }
623 }
624
625 if (direction == 1)
626 {
627 ++subpageIter;
628 if (subpageIter == ttpage->subpages.cend())
629 subpageIter = ttpage->subpages.cbegin();
630
631 res = &(subpageIter->second);
632 }
633
634 return res;
635}
int_to_subpage_t subpages
std::array< uint8_t, 256 > m_bitswap
void AddTeletextData(int magazine, int row, const uint8_t *buf, int vbimode)
virtual void HeaderUpdated(int page, int subpage, tt_line_array &page_ptr, int lang)
void AddPageHeader(int page, int subpage, const uint8_t *buf, int vbimode, int lang, int flags)
const TeletextPage * FindPage(int page, int dir=0) const
virtual void PageUpdated(int page, int subpage)
std::array< TeletextMagazine, 8 > m_magazines
TeletextSubPage * FindSubPage(void)
void SetPage(int page, int subpage)
bool KeyPress(const QString &Key, bool &Exit)
tt_line_array m_header
QString GetPage(void)
const TeletextSubPage * FindSubPageInternal(int page, int subpage, int direction) const
const TeletextPage * FindPageInternal(int page, int direction) const
std::array< int, 3 > m_pageinput
int subpagenum
the wanted subpage
int flags
misc flags
int lang
language code
int flof
page has FastText links
std::array< int, 6 > floflink
FastText links (FLOF)
bool active
data has arrived since page last cleared
bool subtitle
page is subtitle page
std::array< tt_line_array, 25 > data
page data
unsigned int uint
Definition: freesurround.h:24
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static constexpr const char * ACTION_7
Definition: mythuiactions.h:11
static constexpr const char * ACTION_5
Definition: mythuiactions.h:9
static constexpr const char * ACTION_0
Definition: mythuiactions.h:4
static constexpr const char * ACTION_3
Definition: mythuiactions.h:7
static constexpr const char * ACTION_1
Definition: mythuiactions.h:5
static constexpr const char * ACTION_4
Definition: mythuiactions.h:8
static constexpr const char * ACTION_6
Definition: mythuiactions.h:10
static constexpr const char * ACTION_8
Definition: mythuiactions.h:12
static constexpr const char * ACTION_2
Definition: mythuiactions.h:6
static constexpr const char * ACTION_9
Definition: mythuiactions.h:13
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
static int MAGAZINE(int page)
std::array< uint8_t, 40 > tt_line_array
static constexpr uint8_t TP_INTERRUPTED_SEQ
static constexpr uint8_t TP_MAGAZINE_SERIAL
#define ACTION_MENUBLUE
Definition: tv_actions.h:80
#define ACTION_NEXTSUBPAGE
Definition: tv_actions.h:98
#define ACTION_MENURED
Definition: tv_actions.h:77
#define ACTION_TOGGLEBACKGROUND
Definition: tv_actions.h:102
#define ACTION_PREVSUBPAGE
Definition: tv_actions.h:99
#define ACTION_TOGGLETT
Definition: tv_actions.h:100
#define ACTION_REVEAL
Definition: tv_actions.h:103
#define ACTION_PREVPAGE
Definition: tv_actions.h:97
#define ACTION_MENUWHITE
Definition: tv_actions.h:101
#define ACTION_MENUYELLOW
Definition: tv_actions.h:79
#define ACTION_NEXTPAGE
Definition: tv_actions.h:96
#define ACTION_MENUGREEN
Definition: tv_actions.h:78
int hamm8(const uint8_t *p, int *err)
Definition: vbilut.cpp:326
int hamm84(const uint8_t *p, int *err)
Definition: vbilut.cpp:333
int hamm16(const uint8_t *p, int *err)
Definition: vbilut.cpp:343
vbimode
Definition: vbilut.h:22
@ VBI_DVB_SUBTITLE
< DVB packet
Definition: vbilut.h:25
@ VBI_DVB
< IVTV packet
Definition: vbilut.h:24
@ VBI_IVTV
Definition: vbilut.h:23