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