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