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;
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, selected = 0;
196  const TeletextPage *page = FindPage(m_curpage);
197  if (page)
198  {
199  m_magazines[mag - 1].lock.lock();
200  int_to_subpage_t::const_iterator subpageIter;
201  subpageIter = page->subpages.begin();
202  while (subpageIter != page->subpages.end())
203  {
204  const TeletextSubPage *subpage = &subpageIter->second;
205 
206  if (subpage->subpagenum == m_cursubpage)
207  {
208  selected = count;
209  str += "*";
210  }
211  else
212  str += " ";
213 
214  str += QString().sprintf("%02X", subpage->subpagenum);
215 
216  ++subpageIter;
217  ++count;
218  }
219  m_magazines[mag - 1].lock.unlock();
220  }
221 
222  if (str.isEmpty())
223  return str;
224 
225  // if there are less than 9 subpages fill the empty slots with spaces
226  if (count < 10)
227  {
228  QString spaces;
229  spaces.fill(' ', 27 - str.length());
230  str = " <" + str + spaces + " > ";
231  }
232  else
233  {
234  // try to centralize the selected sub page in the list
235  int startPos = selected - 5;
236  if (startPos < 0)
237  startPos = 0;
238  if (startPos + 9 >= count)
239  startPos = count - 10;
240 
241  str = " <" + str.mid(startPos * 3, 27) + " > ";
242  }
243  return str;
244 }
245 
246 void TeletextReader::SetPage(int page, int subpage)
247 {
248  if (page < 0x100 || page > 0x899)
249  return;
250 
251  m_pageinput[0] = (page / 256) + '0';
252  m_pageinput[1] = ((page % 256) / 16) + '0';
253  m_pageinput[2] = (page % 16) + '0';
254 
255  m_curpage = page;
256  m_cursubpage = subpage;
258 }
259 
261 {
262  for (uint mag = 0; mag < 8; mag++)
263  {
264  QMutexLocker lock(&m_magazines[mag].lock);
265 
266  // clear all sub pages in page
267  int_to_page_t::iterator iter;
268  iter = m_magazines[mag].pages.begin();
269  while (iter != m_magazines[mag].pages.end())
270  {
271  TeletextPage *page = &iter->second;
272  page->subpages.clear();
273  ++iter;
274  }
275 
276  // clear pages
277  m_magazines[mag].pages.clear();
278  m_magazines[mag].current_page = 0;
279  m_magazines[mag].current_subpage = 0;
280  m_magazines[mag].loadingpage.active = false;
281  }
282  memset(m_header, ' ', 40);
283 
284  m_curpage = 0x100;
285  m_cursubpage = -1;
286  m_curpage_showheader = true;
287 
288  m_pageinput[0] = '1';
289  m_pageinput[1] = '0';
290  m_pageinput[2] = '0';
291 }
292 
293 void TeletextReader::AddPageHeader(int page, int subpage, const uint8_t *buf,
294  int vbimode, int lang, int flags)
295 {
296  int magazine = MAGAZINE(page);
297  if (magazine < 1 || magazine > 8)
298  return;
299 
300  for(int m = 1; m <= 8; m++)
301  {
302  // ETS 300 706, chapter 7.2.1:
303  // The transmission of a given page begins with, and includes, its page
304  // header packet. It is terminated by and excludes the next page header
305  // packet having the same magazine address in parallel transmission
306  // mode, or any magazine address in serial transmission mode.
307  // ETS 300 706, chapter 9.3.1.3:
308  // When set to '1' the service is designated to be in Serial mode and
309  // the transmission of a page is terminated by the next page header with
310  // a different page number.
311  // When set to '0' the service is designated to be in Parallel mode and
312  // the transmission of a page is terminated by the next page header with
313  // a different page number but the same magazine number. The same
314  // setting shall be used for all page headers in the service.
315 
316  bool isMagazineSerialMode = (flags & TP_MAGAZINE_SERIAL) != 0;
317  if (!(isMagazineSerialMode) && m != magazine)
318  {
319  continue; // in parallel mode only process magazine
320  }
321 
322  int lastPage = m_magazines[m - 1].current_page;
323  int lastSubPage = m_magazines[m - 1].current_subpage;
324 
325  LOG(VB_VBI, LOG_DEBUG,
326  QString("AddPageHeader(p %1, sp %2, lang %3, mag %4, lp %5, lsp %6"
327  " sm %7)")
328  .arg(page).arg(subpage).arg(lang).arg(m).arg(lastPage)
329  .arg(lastSubPage).arg(isMagazineSerialMode));
330 
331  if ((page != lastPage || subpage != lastSubPage) &&
332  m_magazines[m - 1].loadingpage.active)
333  {
334  TeletextSubPage *ttpage = FindSubPage(lastPage, lastSubPage);
335  if (!ttpage)
336  {
337  ttpage = &(m_magazines[m - 1]
338  .pages[lastPage].subpages[lastSubPage]);
339  m_magazines[m - 1].pages[lastPage].pagenum = lastPage;
340  ttpage->subpagenum = lastSubPage;
341  }
342 
343  memcpy(ttpage, &m_magazines[m - 1].loadingpage,
344  sizeof(TeletextSubPage));
345 
346  m_magazines[m - 1].loadingpage.active = false;
347 
348  PageUpdated(lastPage, lastSubPage);
349  }
350  }
351 
352  m_fetchpage = page;
353  m_fetchsubpage = subpage;
354 
355  TeletextSubPage *ttpage = &m_magazines[magazine - 1].loadingpage;
356 
357  m_magazines[magazine - 1].current_page = page;
358  m_magazines[magazine - 1].current_subpage = subpage;
359 
360  memset(ttpage->data, ' ', sizeof(ttpage->data));
361 
362  ttpage->active = true;
363  ttpage->subpagenum = subpage;
364 
365  for (uint i = 0; i < 6; i++)
366  ttpage->floflink[i] = 0;
367 
368  ttpage->lang = lang;
369  ttpage->flags = flags;
370  ttpage->flof = 0;
371 
372  ttpage->subtitle = (vbimode == VBI_DVB_SUBTITLE);
373 
374  memset(ttpage->data[0], ' ', 8 * sizeof(uint8_t));
375 
377  {
378  for (uint j = 8; j < 40; j++)
379  ttpage->data[0][j] = m_bitswap[buf[j]];
380  }
381  else
382  {
383  memcpy(ttpage->data[0]+0, buf, 40);
384  }
385 
386  if ( !(ttpage->flags & TP_INTERRUPTED_SEQ))
387  {
388  memcpy(m_header, ttpage->data[0], 40);
389  HeaderUpdated(page, subpage, ttpage->data[0],ttpage->lang);
390  }
391 }
392 
393 void TeletextReader::AddTeletextData(int magazine, int row,
394  const uint8_t* buf, int vbimode)
395 {
396  //LOG(VB_GENERAL, LOG_ERR, QString("AddTeletextData(%1, %2)")
397  // .arg(magazine).arg(row));
398 
399  int b1, b2, b3, err = 0;
400 
401  if (magazine < 1 || magazine > 8)
402  return;
403 
404  int currentpage = m_magazines[magazine - 1].current_page;
405  if (!currentpage)
406  return;
407 
408  TeletextSubPage *ttpage = &m_magazines[magazine - 1].loadingpage;
409 
410  switch (row)
411  {
412  case 26:
413  /* XXX TODO: Level 1.5, 2.5, 3.5
414  * Character location & override
415  * Level 2.5, 3.5
416  * Modifying display attributes
417  * All levels
418  * VCR Programming
419  * See 12.3
420  */
421  break;
422  case 27: // FLOF data (FastText)
423  switch (vbimode)
424  {
425  case VBI_IVTV:
426  b1 = hamm8(buf, &err);
427  b2 = hamm8(buf + 37, &err);
428  if (err & 0xF000)
429  return;
430  break;
431  case VBI_DVB:
432  case VBI_DVB_SUBTITLE:
433  b1 = hamm84(buf, &err);
434  b2 = hamm84(buf + 37, &err);
435  if (err == 1)
436  return;
437  break;
438  default:
439  return;
440  }
441  if (b1 != 0 || !(b2 & 8))
442  return;
443 
444  for (int i = 0; i < 6; ++i)
445  {
446  err = 0;
447  switch (vbimode)
448  {
449  case VBI_IVTV:
450  b1 = hamm16(buf+1+6*i, &err);
451  b2 = hamm16(buf+3+6*i, &err);
452  b3 = hamm16(buf+5+6*i, &err);
453  if (err & 0xF000)
454  return;
455  break;
456  case VBI_DVB:
457  case VBI_DVB_SUBTITLE:
458  b1 = hamm84(buf+2+6*i, &err) * 16 +
459  hamm84(buf+1+6*i, &err);
460  b2 = hamm84(buf+4+6*i, &err) * 16 +
461  hamm84(buf+3+6*i, &err);
462  b3 = hamm84(buf+6+6*i, &err) * 16 +
463  hamm84(buf+5+6*i, &err);
464  if (err == 1)
465  return;
466  break;
467  default:
468  return;
469  }
470 
471  int x = (b2 >> 7) | ((b3 >> 5) & 0x06);
472  int nTmp = (magazine ^ x);
473  ttpage->floflink[i] = ( nTmp ? nTmp : 8) * 256 + b1;
474  ttpage->flof = 1;
475  }
476  break;
477 
478  case 31: // private streams
479  break;
480 
481  default:
482 
483  if (( row >= 1 ) && ( row <= 24 )) // Page Data
484  {
486  {
487  for (uint j = 0; j < 40; j++)
488  ttpage->data[row][j] = m_bitswap[buf[j]];
489  }
490  else
491  {
492  memcpy(ttpage->data[row], buf, 40);
493  }
494  }
495 
496  break;
497  }
498 }
499 
500 void TeletextReader::PageUpdated(int page, int subpage)
501 {
502  if (page != m_curpage)
503  return;
504  if (subpage != m_cursubpage && m_cursubpage != -1)
505  return;
506  m_page_changed = true;
507 }
508 
510  int page, int subpage, uint8_t *page_ptr, int lang)
511 {
512  (void)page;
513  (void)subpage;
514  (void)lang;
515 
516  if (page_ptr == nullptr)
517  return;
518 
520  return;
521 
522  m_header_changed = true;
523 }
524 
526  int page, int direction) const
527 {
528  int mag = MAGAZINE(page);
529 
530  if (mag > 8 || mag < 1)
531  return nullptr;
532 
533  QMutexLocker lock(&m_magazines[mag - 1].lock);
534 
535  int_to_page_t::const_iterator pageIter;
536  pageIter = m_magazines[mag - 1].pages.find(page);
537  if (pageIter == m_magazines[mag - 1].pages.end())
538  return nullptr;
539 
540  const TeletextPage *res = &pageIter->second;
541  if (direction == -1)
542  {
543  --pageIter;
544  if (pageIter == m_magazines[mag - 1].pages.end())
545  {
546  int_to_page_t::const_reverse_iterator iter;
547  iter = m_magazines[mag - 1].pages.rbegin();
548  res = &iter->second;
549  }
550  else
551  res = &pageIter->second;
552  }
553 
554  if (direction == 1)
555  {
556  ++pageIter;
557  if (pageIter == m_magazines[mag - 1].pages.end())
558  {
559  pageIter = m_magazines[mag - 1].pages.begin();
560  res = &pageIter->second;
561  }
562  else
563  res = &pageIter->second;
564  }
565 
566  return res;
567 }
568 
570  int page, int subpage, int direction) const
571 {
572  int mag = MAGAZINE(page);
573 
574  if (mag > 8 || mag < 1)
575  return nullptr;
576 
577  QMutexLocker lock(&m_magazines[mag - 1].lock);
578 
579  int_to_page_t::const_iterator pageIter;
580  pageIter = m_magazines[mag - 1].pages.find(page);
581  if (pageIter == m_magazines[mag - 1].pages.end())
582  return nullptr;
583 
584  const TeletextPage *ttpage = &(pageIter->second);
585  int_to_subpage_t::const_iterator subpageIter =
586  ttpage->subpages.begin();
587 
588  // try to find the subpage given, or first if subpage == -1
589  if (subpage != -1)
590  subpageIter = ttpage->subpages.find(subpage);
591 
592  if (subpageIter == ttpage->subpages.end())
593  return nullptr;
594 
595  if (subpage == -1)
596  return &(subpageIter->second);
597 
598  const TeletextSubPage *res = &(subpageIter->second);
599  if (direction == -1)
600  {
601  --subpageIter;
602  if (subpageIter == ttpage->subpages.end())
603  {
604  int_to_subpage_t::const_reverse_iterator iter =
605  ttpage->subpages.rbegin();
606  res = &(iter->second);
607  }
608  else
609  {
610  res = &(subpageIter->second);
611  }
612  }
613 
614  if (direction == 1)
615  {
616  ++subpageIter;
617  if (subpageIter == ttpage->subpages.end())
618  subpageIter = ttpage->subpages.begin();
619 
620  res = &(subpageIter->second);
621  }
622 
623  return res;
624 }
#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:99
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:81
unsigned int uint
Definition: compat.h:140
uint8_t data[25][40]
page data
#define ACTION_MENUGREEN
Definition: tv_actions.h:79
const TeletextPage * FindPageInternal(int, int) const
int floflink[6]
FastText links (FLOF)
int flof
page has FastText links
void AddTeletextData(int magazine, int row, const uint8_t *buf, int vbimode)
#define ACTION_REVEAL
Definition: tv_actions.h:104
TeletextSubPage * FindSubPage(void)
#define ACTION_PREVPAGE
Definition: tv_actions.h:98
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, int, int) const
QString GetPage(void)
#define ACTION_MENUYELLOW
Definition: tv_actions.h:80
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:102
bool subtitle
page is subtitle page
#define ACTION_7
Definition: mythuiactions.h:11
< IVTV packet
Definition: vbilut.h:23
#define ACTION_MENURED
Definition: tv_actions.h:78
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:97
#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:103
int lang
language code
#define ACTION_PREVSUBPAGE
Definition: tv_actions.h:100