MythTV  master
cc708decoder.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 // Copyright (c) 2003-2005, Daniel Kristjansson
3 
4 #include <cstdio>
5 #include <cstdlib>
6 
8 #include "captions/cc708reader.h"
10 
11 #define LOC QString("CC708: ")
12 
13 #define DEBUG_CAPTIONS 0
14 #define DEBUG_CC_SERVICE 0
15 #define DEBUG_CC_SERVICE_2 0
16 #define DEBUG_CC_SERVICE_BLOCK 0
17 #define DEBUG_CC_RAWPACKET 0
18 #define DEBUG_CC_VALIDPACKET 0
19 #define DEBUG_CC_DECODE 0
20 #define DEBUG_CC_PARSE 0
21 
22 enum kCCTypes : std::uint8_t
23 {
28 };
29 
30 const std::array<const std::string, 4> cc_types =
31 {
32  "NTSC line 21 field 1 closed captions"
33  "NTSC line 21 field 2 closed captions"
34  "DTVCC Channel Packet Data"
35  "DTVCC Channel Packet Start"
36 };
37 
38 static void parse_cc_packet(CC708Reader *cb_cbs, CaptionPacket *pkt,
39  cc708_seen_times& last_seen);
40 
41 void CC708Decoder::decode_cc_data(uint cc_type, uint data1, uint data2)
42 {
43  if (DTVCC_PACKET_START == cc_type)
44  {
45 #if DEBUG_CC_DECODE
46  LOG(VB_VBI, LOG_DEBUG, LOC + QString("CC ST data(0x%1 0x%2)")
47  .arg(data1,0,16).arg(data2,0,16));
48 #endif
49 
52 
53  m_partialPacket.data[0] = data1;
54  m_partialPacket.data[1] = data2;
56  }
57  else if (DTVCC_PACKET_DATA == cc_type)
58  {
59 #if DEBUG_CC_DECODE
60  LOG(VB_VBI, LOG_DEBUG, LOC + QString("CC Ex data(0x%1 0x%2)")
61  .arg(data1,0,16).arg(data2,0,16));
62 #endif
63 
66  m_partialPacket.size += 2;
67  }
68 }
69 
71 {
75 }
76 
77 void CC708Decoder::services(std::chrono::seconds seconds, cc708_seen_flags & seen) const
78 {
79  auto then = SystemClock::now() - seconds;
80 
81  seen[0] = false; // service zero is not allowed in CEA-708-D
82  for (uint i = 1; i < 64; i++)
83  seen[i] = (m_lastSeen[i] >= then);
84 }
85 
86 enum C0 : std::uint8_t
87 {
88  NUL = 0x00,
89  ETX = 0x03,
90  BS = 0x08,
91  FF = 0x0C,
92  CR = 0x0D,
93  HCR = 0x0E,
94  EXT1 = 0x10,
95  P16 = 0x18,
96 };
97 
98 enum C1 : std::uint8_t
99 {
100  CW0=0x80, CW1=0x81, CW2=0x82, CW3=0x83, CW4=0x84, CW5=0x85, CW6=0x86, CW7=0x87,
101  CLW=0x88, DSW=0x89, HDW=0x8A, TGW=0x8B, DLW=0x8C, DLY=0x8D, DLC=0x8E, RST=0x8F,
102  SPA=0x90, SPC=0x91, SPL=0x92, SWA=0x97,
103  DF0=0x98, DF1=0x99, DF2=0x9A, DF3=0x9B, DF4=0x9C, DF5=0x9D, DF6=0x9E, DF7=0x9F,
104 };
105 
106 using cc_table = std::array<const uint16_t, 0x60>;
107 extern const cc_table CCtableG0;
108 extern const cc_table CCtableG1;
109 extern const cc_table CCtableG2;
110 extern const cc_table CCtableG3;
111 
112 static void append_character(CC708Reader *cc, uint service_num, short ch);
113 static void parse_cc_service_stream(CC708Reader *cc, uint service_num);
114 static int handle_cc_c0_ext1_p16(CC708Reader *cc, uint service_num, int i);
115 static int handle_cc_c1(CC708Reader *cc, uint service_num, int i);
116 static int handle_cc_c2(CC708Reader *cc, uint service_num, int i);
117 static int handle_cc_c3(CC708Reader *cc, uint service_num, int i);
118 
119 #define SEND_STR \
120 do { \
121  if (cc->m_tempStrSize[service_num]) \
122  { \
123  cc->TextWrite(service_num, \
124  cc->m_tempStr[service_num], \
125  cc->m_tempStrSize[service_num]); \
126  cc->m_tempStrSize[service_num] = 0; \
127  } \
128 } while (false)
129 
130 static void parse_cc_service_stream(CC708Reader* cc, uint service_num)
131 {
132  const int blk_size = cc->m_bufSize[service_num];
133  int blk_start = 0;
134  int dlc_loc = 0;
135  int rst_loc = 0;
136  int i = 0;
137 
138  // find last reset or delay cancel in buffer
139  for (i = 0; i < blk_size; i++)
140  {
141  switch (cc->m_buf[service_num][i]) {
142  // Skip over parameters, since their bytes may coincide
143  // with RST or DLC
144  case CLW:
145  case DLW:
146  case DSW:
147  case HDW:
148  case TGW:
149  case DLY:
150  i += 1;
151  break;
152  case SPA:
153  case SPL:
154  i += 2;
155  break;
156  case SPC:
157  i += 3;
158  break;
159  case SWA:
160  i += 4;
161  break;
162  case DF0:
163  case DF1:
164  case DF2:
165  case DF3:
166  case DF4:
167  case DF5:
168  case DF6:
169  case DF7:
170  i += 6;
171  break;
172  // Detect RST or DLC bytes
173  case RST:
174  rst_loc = dlc_loc = i;
175  break;
176  case DLC:
177  dlc_loc = i;
178  break;
179  }
180  }
181 
182  // reset, process only data after reset
183  if (rst_loc)
184  {
185  cc->Reset(service_num);
186  cc->m_delayed[service_num] = false; // Reset implicitly cancels delay
187  blk_start = rst_loc + 1;
188  }
189 
190  // if we have a delay cancel, cancel any delay
191  if (dlc_loc && cc->m_delayed[service_num])
192  {
193  cc->DelayCancel(service_num);
194  cc->m_delayed[service_num] = false;
195  }
196 
197  // cancel delay if the buffer is full
198  if (cc->m_delayed[service_num] && blk_size >= 126)
199  {
200  cc->DelayCancel(service_num);
201  cc->m_delayed[service_num] = false;
202  dlc_loc = blk_size - 1;
203  }
204 
205 #if DEBUG_CC_PARSE
206  LOG(VB_VBI, LOG_ERR,
207  QString("cc_ss delayed(%1) blk_start(%2) blk_size(%3)")
208  .arg(cc->m_delayed[service_num]) .arg(blk_start) .arg(blk_size));
209 #endif
210 
211  for (i = (cc->m_delayed[service_num]) ? blk_size : blk_start;
212  i < blk_size; )
213  {
214  const int old_i = i;
215  const int code = cc->m_buf[service_num][i];
216  if (0x0 == code)
217  {
218  i++;
219  }
220  else if (code <= 0x1f)
221  {
222  // C0 code -- ASCII commands + ext1: C2,C3,G2,G3 + p16: 16 chars
223  i = handle_cc_c0_ext1_p16(cc, service_num, i);
224  }
225  else if (code <= 0x7f)
226  {
227  // G0 code -- mostly ASCII printables
228  short character = CCtableG0[code-0x20];
229  append_character(cc, service_num, character);
230  i++;
231  SEND_STR;
232  }
233  else if (code <= 0x9f)
234  {
235  // C1 code -- caption control codes
236  i = handle_cc_c1(cc, service_num, i);
237  }
238  else if (code <= 0xff)
239  {
240  // G1 code -- ISO 8859-1 Latin 1 characters
241  short character = CCtableG1[code-0xA0];
242  append_character(cc, service_num, character);
243  i++;
244  }
245 
246 #if DEBUG_CC_SERVICE
247  LOG(VB_VBI, LOG_DEBUG, QString("i %1, blk_size %2").arg(i)
248  .arg(blk_size));
249 #endif
250 
251  // loop continuation check
252  if (old_i == i)
253  {
254 #if DEBUG_CC_SERVICE
255  LOG(VB_VBI, LOG_DEBUG, QString("old_i == i == %1").arg(i));
256  QString msg;
257  for (int j = 0; j < blk_size; j++)
258  msg += QString("0x%1 ").arg(cc->m_buf[service_num][j], 0, 16);
259  LOG(VB_VBI, LOG_DEBUG, msg);
260 #endif
261  if (blk_size - i > 10)
262  {
263  LOG(VB_VBI, LOG_INFO, "eia-708 decoding error...");
264  cc->Reset(service_num);
265  cc->m_delayed[service_num] = false;
266  i = cc->m_bufSize[service_num];
267  }
268  // There must be an incomplete code in buffer...
269  break;
270  }
271  if (cc->m_delayed[service_num] && dlc_loc < i)
272  {
273  // delay in effect
274  break;
275  }
276  if (cc->m_delayed[service_num])
277  {
278  // this delay has already been canceled..
279  cc->DelayCancel(service_num);
280  cc->m_delayed[service_num] = false;
281  }
282  }
283 
284  // get rid of remaining bytes...
285  if ((blk_size - i) > 0)
286  {
287  memmove(cc->m_buf[service_num], cc->m_buf[service_num] + i,
288  blk_size - i);
289  cc->m_bufSize[service_num] -= i;
290  }
291  else
292  {
293  if (0 != (blk_size - i))
294  {
295  LOG(VB_VBI, LOG_ERR, QString("buffer error i(%1) buf_size(%2)")
296  .arg(i).arg(blk_size));
297  QString msg;
298  for (i=0; i < blk_size; i++)
299  msg += QString("0x%1 ").arg(cc->m_buf[service_num][i], 0, 16);
300  LOG(VB_VBI, LOG_ERR, msg);
301  }
302  cc->m_bufSize[service_num] = 0;
303  }
304 }
305 
306 static int handle_cc_c0_ext1_p16(CC708Reader* cc, uint service_num, int i)
307 {
308  // C0 code -- subset of ASCII misc. control codes
309  const int code = cc->m_buf[service_num][i];
310 
311  if (code <= 0xf)
312  {
313  // single byte code
314  if (C0::ETX == code)
315  SEND_STR;
316  else if (C0::BS == code)
317  append_character(cc, service_num, 0x8); // Backspace
318  else if (C0::FF==code)
319  append_character(cc, service_num, 0xc); // Form Feed
320  else if (C0::CR==code)
321  append_character(cc, service_num, 0xd); // Carriage Return
322  else if (C0::HCR==code)
323  append_character(cc, service_num, 0xe); // Horizontal Carriage Return
324  i++;
325  }
326  else if (code<=0x17)
327  {
328  // double byte code
329  const int blk_size = cc->m_bufSize[service_num];
330  if (EXT1==code && ((i+1)<blk_size))
331  {
332  const int code2 = cc->m_buf[service_num][i+1];
333  if (code2<=0x1f)
334  {
335  // C2 code -- nothing in EIA-708-A
336  i = handle_cc_c2(cc, service_num, i);
337  }
338  else if (code2<=0x7f)
339  {
340  // G2 code -- fractions, drawing, symbols
341  append_character(cc, service_num, CCtableG2[code2-0x20]);
342  i+=2;
343  }
344  else if (code2<=0x9f)
345  {
346  // C3 code -- nothing in EIA-708-A
347  i = handle_cc_c3(cc, service_num, i);
348  }
349  else if (code2<=0xff)
350  {
351  // G3 code -- one symbol in EIA-708-A "[cc]"
352  append_character(cc, service_num, CCtableG3[code2-0xA0]);
353  i+=2;
354  }
355  }
356  else if ((i+1)<blk_size)
357  {
358  i+=2;
359  }
360  }
361  else if (code<=0x1f)
362  {
363  // triple byte code
364  const int blk_size = cc->m_bufSize[service_num];
365  if (P16==code && ((i+2)<blk_size))
366  {
367  // reserved for large alphabets, but not yet defined
368  }
369  if ((i+2)<blk_size)
370  i+=3;
371  }
372  return i;
373 }
374 
375 static int handle_cc_c1(CC708Reader* cc, uint service_num, int i)
376 {
377  const int blk_size = cc->m_bufSize[service_num];
378  const int code = cc->m_buf[service_num][i];
379 
380  const unsigned char* blk_buf = cc->m_buf[service_num];
381  if (code<=CW7)
382  { // no paramaters
383  SEND_STR;
384  cc->SetCurrentWindow(service_num, code-0x80);
385  i+=1;
386  }
387  else if (DLC == cc->m_buf[service_num][i])
388  {
389 /* processed out-of-band
390  cc->DelayCancel(service_num);
391  cc->m_delayed[service_num] = 0;
392 */
393  i+=1;
394  }
395  else if (code<=DLY && ((i+1)<blk_size))
396  { // 1 byte of paramaters
397  int param1 = blk_buf[i+1];
398  SEND_STR;
399  if (CLW==code)
400  cc->ClearWindows(service_num, param1);
401  else if (DSW==code)
402  cc->DisplayWindows(service_num, param1);
403  else if (HDW==code)
404  cc->HideWindows(service_num, param1);
405  else if (TGW==code)
406  cc->ToggleWindows(service_num, param1);
407  else if (DLW==code)
408  cc->DeleteWindows(service_num, param1);
409  else if (DLY==code)
410  {
411  cc->Delay(service_num, param1);
412  cc->m_delayed[service_num] = true;
413  }
414  i+=2;
415  }
416  else if (SPA==code && ((i+2)<blk_size))
417  {
418  int pen_size = (blk_buf[i+1] ) & 0x3;
419  int offset = (blk_buf[i+1]>>2) & 0x3;
420  int text_tag = (blk_buf[i+1]>>4) & 0xf;
421  int font_tag = (blk_buf[i+2] ) & 0x7;
422  int edge_type = (blk_buf[i+2]>>3) & 0x7;
423  int underline = (blk_buf[i+2]>>6) & 0x1;
424  int italic = (blk_buf[i+2]>>7) & 0x1;
425  SEND_STR;
426  cc->SetPenAttributes(service_num, pen_size, offset, text_tag,
427  font_tag, edge_type, underline, italic);
428  i+=3;
429  }
430  else if (SPC==code && ((i+3)<blk_size))
431  {
432  int fg_color = (blk_buf[i+1] ) & 0x3f;
433  int fg_opacity = (blk_buf[i+1]>>6) & 0x03;
434  int bg_color = (blk_buf[i+2] ) & 0x3f;
435  int bg_opacity = (blk_buf[i+2]>>6) & 0x03;
436  int edge_color = (blk_buf[i+3]>>6) & 0x3f;
437  SEND_STR;
438  cc->SetPenColor(service_num, fg_color, fg_opacity,
439  bg_color, bg_opacity, edge_color);
440  i+=4;
441  }
442  else if (SPL==code && ((i+2)<blk_size))
443  {
444  int row = blk_buf[i+1] & 0x0f;
445  int col = blk_buf[i+2] & 0x3f;
446  SEND_STR;
447  cc->SetPenLocation(service_num, row, col);
448  i+=3;
449  }
450  else if (SWA==code && ((i+4)<blk_size))
451  {
452  int fill_color = (blk_buf[i+1] ) & 0x3f;
453  int fill_opacity = (blk_buf[i+1]>>6) & 0x03;
454  int border_color = (blk_buf[i+2] ) & 0x3f;
455  int border_type01 = (blk_buf[i+2]>>6) & 0x03;
456  int justify = (blk_buf[i+3] ) & 0x03;
457  int scroll_dir = (blk_buf[i+3]>>2) & 0x03;
458  int print_dir = (blk_buf[i+3]>>4) & 0x03;
459  int word_wrap = (blk_buf[i+3]>>6) & 0x01;
460  int border_type = (blk_buf[i+3]>>5) | border_type01;
461  int display_eff = (blk_buf[i+4] ) & 0x03;
462  int effect_dir = (blk_buf[i+4]>>2) & 0x03;
463  int effect_speed = (blk_buf[i+4]>>4) & 0x0f;
464  SEND_STR;
465  cc->SetWindowAttributes(
466  service_num, fill_color, fill_opacity, border_color, border_type,
467  scroll_dir, print_dir, effect_dir,
468  display_eff, effect_speed, justify, word_wrap);
469  i+=5;
470  }
471  else if ((code>=DF0) && (code<=DF7) && ((i+6)<blk_size))
472  {
473  // param1
474  int priority = ( blk_buf[i+1] ) & 0x7;
475  int col_lock = (blk_buf[i+1]>>3) & 0x1;
476  int row_lock = (blk_buf[i+1]>>4) & 0x1;
477  bool visible = ((blk_buf[i+1]>>5) & 0x1) != 0;
478  // param2
479  int anchor_vertical = blk_buf[i+2] & 0x7f;
480  int relative_pos = (blk_buf[i+2]>>7);
481  // param3
482  int anchor_horizontal = blk_buf[i+3];
483  // param4
484  int row_count = blk_buf[i+4] & 0xf;
485  int anchor_point = blk_buf[i+4]>>4;
486  // param5
487  int col_count = blk_buf[i+5] & 0x3f;
488  // param6
489  int pen_style = blk_buf[i+6] & 0x7;
490  int win_style = (blk_buf[i+6]>>3) & 0x7;
491  SEND_STR;
492  cc->DefineWindow(service_num, code-0x98, priority, visible,
493  anchor_point, relative_pos,
494  anchor_vertical, anchor_horizontal,
495  row_count, col_count, row_lock, col_lock,
496  pen_style, win_style);
497  i+=7;
498  }
499 #if DEBUG_CC_SERVICE
500  else
501  {
502  LOG(VB_VBI, LOG_ERR, QString("handle_cc_c1: (NOT HANDLED) "
503  "code(0x%1) i(%2) blk_size(%3)").arg(code, 2, 16, QLatin1Char('0'))
504  .arg(i).arg(blk_size));
505  }
506 #endif
507 
508  return i;
509 }
510 
511 static int handle_cc_c2(CC708Reader* cc, uint service_num, int i)
512 {
513  const int blk_size = cc->m_bufSize[service_num];
514  const int code = cc->m_buf[service_num][i+1];
515 
516  if ((code<=0x7) && ((i+1)<blk_size)){
517  i+=2;
518  SEND_STR;
519  }
520  else if ((code<=0xf) && ((i+2)<blk_size))
521  {
522  i+=3;
523  SEND_STR;
524  }
525  else if ((code<=0x17) && ((i+3)<blk_size))
526  {
527  i+=4;
528  SEND_STR;
529  }
530  else if ((code<=0x1f) && ((i+4)<blk_size))
531  {
532  i+=5;
533  SEND_STR;
534  }
535  return i;
536 }
537 
538 static int handle_cc_c3(CC708Reader* cc, uint service_num, int i)
539 {
540  const unsigned char* blk_buf = cc->m_buf[service_num];
541  const int blk_size = cc->m_bufSize[service_num];
542  const int code = cc->m_buf[service_num][i+1];
543 
544  if ((code<=0x87) && ((i+5)<blk_size))
545  {
546  i+=6;
547  SEND_STR;
548  }
549  else if ((code<=0x8f) && ((i+6)<blk_size))
550  {
551  i+=7;
552  SEND_STR;
553  }
554  else if ((i+2)<blk_size)
555  { // varible length commands
556  int length = blk_buf[i+2]&0x3f;
557  if ((i+length)<blk_size)
558  {
559  i+=1+length;
560  SEND_STR;
561  }
562  }
563  return i;
564 }
565 
566 static bool rightsize_buf(CC708Reader* cc, uint service_num, uint block_size)
567 {
568  size_t min_new_size = block_size + cc->m_bufSize[service_num];
569  bool ret = true;
570  if (min_new_size >= cc->m_bufAlloc[service_num])
571  {
572  size_t new_alloc = cc->m_bufAlloc[service_num];
573  for (uint i = 0; (i < 32) && (new_alloc <= min_new_size); i++)
574  new_alloc *= 2;
575  void *new_buf = realloc(cc->m_buf[service_num], new_alloc);
576  if (new_buf)
577  {
578  cc->m_buf[service_num] = (uchar *)new_buf;
579  cc->m_bufAlloc[service_num] = new_alloc;
580  }
581  else
582  {
583  ret = false;
584  }
585 
586 #if DEBUG_CC_SERVICE_2
587  LOG(VB_VBI, LOG_DEBUG, QString("rightsize_buf: srv %1 to %1 bytes")
588  .arg(service_num) .arg(cc->m_bufAlloc[service_num]));
589 #endif
590  }
591  if (min_new_size >= cc->m_bufAlloc[service_num])
592  {
593  LOG(VB_VBI, LOG_ERR,
594  QString("buffer resize error: min_new_size=%1, buf_alloc[%2]=%3")
595  .arg(min_new_size)
596  .arg(service_num)
597  .arg(cc->m_bufAlloc[service_num]));
598  }
599  return ret;
600 }
601 
602 static void append_cc(CC708Reader* cc, uint service_num,
603  const unsigned char* blk_buf, int block_size)
604 {
605  if (!rightsize_buf(cc, service_num, block_size))
606  {
607  // The buffer resize failed. Drop the new data.
608  return;
609  }
610 
611  memcpy(cc->m_buf[service_num] + cc->m_bufSize[service_num],
612  blk_buf, block_size);
613 
614  cc->m_bufSize[service_num] += block_size;
615 #if DEBUG_CC_SERVICE_2
616  {
617  uint i;
618  QString msg("append_cc: ");
619  for (i = 0; i < cc->m_bufSize[service_num]; i++)
620  msg += QString("0x%1").arg(cc->m_buf[service_num][i], 0, 16);
621  LOG(VB_VBI, LOG_DEBUG, msg);
622  }
623 #endif
624  parse_cc_service_stream(cc, service_num);
625 }
626 
627 static void parse_cc_packet(CC708Reader* cb_cbs, CaptionPacket* pkt,
628  cc708_seen_times& last_seen)
629 {
630  const unsigned char* pkt_buf = pkt->data.data();
631  const int pkt_size = pkt->size;
632  int off = 1;
633  int len = ((((int)pkt_buf[0]) & 0x3f)<<1) - 1;
634 
635  if (len < 0)
636  return;
637 
638 #if DEBUG_CC_RAWPACKET
639  if (true)
640 #elif DEBUG_CAPTIONS
641  if (len > pkt_size)
642 #else
643  if (false) // NOLINT(readability-simplify-boolean-expr)
644 #endif
645  {
646  int srv = (pkt_buf[off]>>5) & 0x7;
647  int seq_num = (((int)pkt_buf[0])>>6)&0x3;
648  QString msg = QString("CC708 len %1 srv0 %2 seq %3 ").arg(len, 2)
649  .arg(srv) .arg(seq_num);
650  for (int j = 0; j < pkt_size; j++)
651  msg += QString("0x%1").arg(pkt_buf[j], 0, 16);
652  LOG(VB_VBI, LOG_DEBUG, msg);
653  }
654 
655  if (pkt_size >= 127)
656  LOG(VB_VBI, LOG_ERR,
657  QString("Unexpected pkt_size=%1").arg(pkt_size));
658 
659  while (off < pkt_size && pkt_buf[off])
660  { // service_block
661  int block_size = pkt_buf[off] & 0x1f;
662  int service_number = (pkt_buf[off]>>5) & 0x7;
663  int block_data_offset = (0x7==service_number && block_size!=0) ?
664  off+2 : off+1;
665 #if DEBUG_CC_SERVICE_BLOCK
666  LOG(VB_VBI, LOG_DEBUG,
667  QString("service_block size(%1) num(%2) off(%3)")
668  .arg(block_size) .arg(service_number) .arg(block_data_offset));
669 #endif
670  if (off+2 == block_data_offset)
671  {
672  int extended_service_number = pkt_buf[off+2] & 0x3f;
673 #if DEBUG_CC_SERVICE_BLOCK
674  LOG(VB_VBI, LOG_DEBUG, QString("ext_svc_num(%1)")
675  .arg(extended_service_number));
676 #endif
677  service_number = extended_service_number;
678  }
679  if (service_number)
680  {
681 #if DEBUG_CC_SERVICE
682  int i;
683  if (!(2==block_size &&
684  0==pkt_buf[block_data_offset] &&
685  0==pkt_buf[block_data_offset+1]))
686  {
687  QString msg = QString("service %1: ").arg(service_number);
688  for (i=0; i<block_size; i++)
689  msg += QString("0x%1 ")
690  .arg(pkt_buf[block_data_offset+i], 0, 16);
691  LOG(VB_VBI, LOG_DEBUG, msg);
692  }
693 #endif
694  append_cc(cb_cbs, service_number,
695  &pkt_buf[block_data_offset], block_size);
696 
697  last_seen[service_number] = std::chrono::system_clock::now();
698  }
699  off+=block_size+1;
700  }
701  if (off<pkt_size) // must end in null service block, if packet is not full.
702  {
703  if (pkt_buf[off] != 0)
704  {
705  LOG(VB_VBI, LOG_ERR,
706  QString("CEA-708 packet error: pkt_size=%1, pkt_buf[%2]=%3")
707  .arg(pkt_size).arg(off).arg(pkt_buf[off]));
708  }
709  }
710 }
711 
712 static void append_character(CC708Reader *cc, uint service_num, short ch)
713 {
714  if (cc->m_tempStrSize[service_num]+2 > cc->m_tempStrAlloc[service_num])
715  {
716  int new_alloc = (cc->m_tempStrAlloc[service_num]) ?
717  cc->m_tempStrAlloc[service_num] * 2 : 64;
718 
719  cc->m_tempStr[service_num] = (short*)
720  realloc(cc->m_tempStr[service_num], new_alloc * sizeof(short));
721 
722  cc->m_tempStrAlloc[service_num] = new_alloc; // shorts allocated
723  }
724 
725  if (cc->m_tempStr[service_num])
726  {
727  int i = cc->m_tempStrSize[service_num];
728  cc->m_tempStr[service_num][i] = ch;
729  cc->m_tempStrSize[service_num]++;
730  }
731  else
732  {
733  cc->m_tempStrSize[service_num] = 0;
734  cc->m_tempStrAlloc[service_num]=0;
735  }
736 }
737 
739 {
740 // 0 1 2 3 4 5 6 7
741 // 8 9 a b c d e f
742  ' ', '!','\"', '#', '$', '%', '&', '\'', /* 0x20-0x27 */
743  '(', ')', '*', '+', ',', '-', '.', '/', /* 0x28-0x2f */
744  '0', '1', '2', '3', '4', '5', '6', '7', /* 0x30-0x37 */
745  '8', '9', ':', ';', '<', '=', '>', '?', /* 0x38-0x3f */
746 
747  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40-0x47 */
748  'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 0x48-0x4f */
749  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50-0x57 */
750  'X', 'Y', 'Z', '[', '\\',']', '^', '_', /* 0x58-0x5f */
751 
752  '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60-0x67 */
753  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 0x68-0x6f */
754  'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70-0x77 */
755  'x', 'y', 'z', '{', '|', '}', '~', 0x266a, // music note/* 0x78-0x7f */
756 };
757 
759 {
760 // 0 1 2 3
761 // 4 5 6 7
762 // 8 9 a b
763 // c d e f
764  0xA0, // unicode non-breaking space
765  0xA1 /* ¡ */, 0xA2 /* ¢ */, 0xA3 /* £ */,
766  0xA4 /* ¤ */, 0xA5 /* ¥ */, 0xA6 /* ¦ */, 0xA7 /* § */,
767  0xA8 /* ¨ */, 0xA9 /* © */, 0xAA /* ª */, 0xAB /* « */,
768  0xAC /* ¬ */, 0xAD /* ­ */, 0xAE /* ® */, 0xAF /* ¯ */,
769  0xB0 /* ° */, 0xB1 /* ± */, 0xB2 /* ² */, 0xB3 /* ³ */,
770  0xB4 /* ´ */, 0xB5 /* µ */, 0xB6 /* ¶ */, 0xB7 /* · */,
771  0xB8 /* ¸ */, 0xB9 /* ¹ */, 0xBA /* º */, 0xBB /* » */,
772  0xBC /* ¼ */, 0xBD /* ½ */, 0xBE /* ¾ */, 0xBF /* ¿ */,
773 
774  0xC0 /* À */, 0xC1 /* Á */, 0xC2 /* Â */, 0xC3 /* Ã */,
775  0xC4 /* Ä */, 0xC5 /* Å */, 0xC6 /* Æ */, 0xC7 /* Ç */,
776  0xC8 /* È */, 0xC9 /* É */, 0xCA /* Ê */, 0xCB /* Ë */,
777  0xCC /* Ì */, 0xCD /* Í */, 0xCE /* Î */, 0xCF /* Ï */,
778  0xD0 /* Ð */, 0xD1 /* Ñ */, 0xD2 /* Ò */, 0xD3 /* Ó */,
779  0xD4 /* Ô */, 0xD5 /* Õ */, 0xD6 /* Ö */, 0xD7 /* × */,
780  0xD8 /* Ø */, 0xD9 /* Ù */, 0xDA /* Ú */, 0xDB /* Û */,
781  0xDC /* Ü */, 0xDD /* Ý */, 0xDE /* Þ */, 0xDF /* ß */,
782 
783  0xE0 /* à */, 0xE1 /* á */, 0xE2 /* â */, 0xE3 /* ã */,
784  0xE4 /* ä */, 0xE5 /* å */, 0xE6 /* æ */, 0xE7 /* ç */,
785  0xE8 /* è */, 0xE9 /* é */, 0xEA /* ê */, 0xEB /* ë */,
786  0xEC /* ì */, 0xED /* í */, 0xEE /* î */, 0xEF /* ï */,
787  0xF0 /* ð */, 0xF1 /* ñ */, 0xF2 /* ò */, 0xF3 /* ó */,
788  0xF4 /* ô */, 0xF5 /* õ */, 0xF6 /* ö */, 0xF7 /* ÷ */,
789  0xF8 /* ø */, 0xF9 /* ù */, 0xFA /* ú */, 0xFB /* û */,
790  0xFC /* ü */, 0xFD /* ý */, 0xFE /* þ */, 0xFF /* ÿ */,
791 };
792 
794 {
795  ' ', /* transparent space */
796  0xA0, /* non-breaking transparent space */
797  0, 0, /* 0x20-0x23 */
798  0, 0x2026,/* elipsis */
799  0, 0, /* 0x24-0x27 */
800  0, 0,
801  0x160,/*S under \/ */0, /* 0x28-0x2b */
802  0x152, /* CE */ 0,
803  0, 0, /* 0x2c-0x2f */
804  0x2588,/*block*/ 0x2018,/* open ' */
805  0x2019,/*close ' */ 0x201c,/* open " */ /* 0x30-0x33 */
806  0x201d,/*close " */ 0xB7,/* dot */
807  0, 0, /* 0x34-0x37 */
808  0, 0x2122,/* super TM */
809  0x161,/*s under \/ */0, /* 0x38-0x3b */
810  0x153, /* ce */ 0x2120,/* super SM */
811  0, 0x178,/*Y w/umlout*/ /* 0x3c-0x3f */
812 
813 // 0 1 2 3
814 // 4 5 6 7
815 // 8 9 a b
816 // c d e f
817  0, 0, 0, 0,
818  0, 0, 0, 0, /* 0x40-0x47 */
819  0, 0, 0, 0,
820  0, 0, 0, 0, /* 0x48-0x4f */
821 
822  0, 0, 0, 0,
823  0, 0, 0, 0, /* 0x50-0x57 */
824  0, 0, 0, 0,
825  0, 0, 0, 0, /* 0x58-0x5f */
826 
827  0, 0, 0, 0,
828  0, 0, 0, 0, /* 0x60-0x67 */
829  0, 0, 0, 0,
830  0, 0, 0, 0, /* 0x68-0x6f */
831 
832  0, 0,
833  0, 0, /* 0x70-0x73 */
834  0, 0,
835  0x215b, /* 1/8 */ 0x215c, /* 3/8 */ /* 0x74-0x77 */
836  0x215d, /* 5/8 */ 0x215e, /* 7/8 */
837  0x2502, /*line | */ 0x2510, /*line ~| */ /* 0x78-0x7b */
838  0x2514, /*line |_*/ 0x2500, /*line -*/
839  0x2518, /*line _|*/ 0x250c, /*line |~ */ /* 0x7c-0x7f */
840 };
841 
843 {
844 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
845  '#', /* [CC] closed captioning logo */
846  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */
847  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */
848 
849  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0-0xcf */
850  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0-0xdf */
851 
852  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0-0xff */
853  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0-0xff */
854 };
BS
@ BS
Definition: cc708decoder.cpp:90
CW0
@ CW0
Definition: cc708decoder.cpp:100
NTSC_CC_f1
@ NTSC_CC_f1
Definition: cc708decoder.cpp:24
CCtableG3
const cc_table CCtableG3
Definition: cc708decoder.cpp:842
CLW
@ CLW
Definition: cc708decoder.cpp:101
rightsize_buf
static bool rightsize_buf(CC708Reader *cc, uint service_num, uint block_size)
Definition: cc708decoder.cpp:566
cc708_seen_flags
std::array< bool, 64 > cc708_seen_flags
Definition: cc708decoder.h:15
parse_cc_packet
static void parse_cc_packet(CC708Reader *cb_cbs, CaptionPacket *pkt, cc708_seen_times &last_seen)
Definition: cc708decoder.cpp:627
CaptionPacket::data
std::array< unsigned char, 128+16 > data
Definition: cc708decoder.h:22
ETX
@ ETX
Definition: cc708decoder.cpp:89
handle_cc_c2
static int handle_cc_c2(CC708Reader *cc, uint service_num, int i)
Definition: cc708decoder.cpp:511
cc708decoder.h
DF4
@ DF4
Definition: cc708decoder.cpp:103
P16
@ P16
Definition: cc708decoder.cpp:95
CW7
@ CW7
Definition: cc708decoder.cpp:100
cc_types
const std::array< const std::string, 4 > cc_types
Definition: cc708decoder.cpp:30
cc
Definition: cc.h:9
CR
@ CR
Definition: cc708decoder.cpp:92
CW4
@ CW4
Definition: cc708decoder.cpp:100
x0
static int x0
Definition: mythsocket.cpp:49
CW6
@ CW6
Definition: cc708decoder.cpp:100
CC708Decoder::decode_cc_data
void decode_cc_data(uint cc_type, uint data1, uint data2)
Definition: cc708decoder.cpp:41
x3
static int x3
Definition: mythsocket.cpp:52
DTVCC_PACKET_START
@ DTVCC_PACKET_START
Definition: cc708decoder.cpp:27
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
CW2
@ CW2
Definition: cc708decoder.cpp:100
DSW
@ DSW
Definition: cc708decoder.cpp:101
SPA
@ SPA
Definition: cc708decoder.cpp:102
CCtableG0
const cc_table CCtableG0
Definition: cc708decoder.cpp:738
cc_table
std::array< const uint16_t, 0x60 > cc_table
Definition: cc708decoder.cpp:106
C1
C1
Definition: cc708decoder.cpp:98
DLY
@ DLY
Definition: cc708decoder.cpp:101
CC708Decoder::m_reader
CC708Reader * m_reader
Definition: cc708decoder.h:43
append_cc
static void append_cc(CC708Reader *cc, uint service_num, const unsigned char *blk_buf, int block_size)
Definition: cc708decoder.cpp:602
mythlogging.h
CaptionPacket::size
int size
Definition: cc708decoder.h:23
HCR
@ HCR
Definition: cc708decoder.cpp:93
DTVCC_PACKET_DATA
@ DTVCC_PACKET_DATA
Definition: cc708decoder.cpp:26
DLW
@ DLW
Definition: cc708decoder.cpp:101
x1
static int x1
Definition: mythsocket.cpp:50
DF7
@ DF7
Definition: cc708decoder.cpp:103
handle_cc_c0_ext1_p16
static int handle_cc_c0_ext1_p16(CC708Reader *cc, uint service_num, int i)
Definition: cc708decoder.cpp:306
CC708Decoder::m_lastSeen
cc708_seen_times m_lastSeen
Definition: cc708decoder.h:44
FF
@ FF
Definition: cc708decoder.cpp:91
SWA
@ SWA
Definition: cc708decoder.cpp:102
NUL
@ NUL
Definition: cc708decoder.cpp:88
CC708Decoder::services
void services(std::chrono::seconds seconds, cc708_seen_flags &seen) const
Definition: cc708decoder.cpp:77
CC708Decoder::m_partialPacket
CaptionPacket m_partialPacket
Definition: cc708decoder.h:42
RST
@ RST
Definition: cc708decoder.cpp:101
CCtableG2
const cc_table CCtableG2
Definition: cc708decoder.cpp:793
DF0
@ DF0
Definition: cc708decoder.cpp:103
uint
unsigned int uint
Definition: compat.h:81
DF5
@ DF5
Definition: cc708decoder.cpp:103
DF3
@ DF3
Definition: cc708decoder.cpp:103
CW1
@ CW1
Definition: cc708decoder.cpp:100
LOC
#define LOC
Definition: cc708decoder.cpp:11
block_size
unsigned int block_size
Definition: freesurround.cpp:49
DF1
@ DF1
Definition: cc708decoder.cpp:103
DF2
@ DF2
Definition: cc708decoder.cpp:103
SPL
@ SPL
Definition: cc708decoder.cpp:102
NTSC_CC_f2
@ NTSC_CC_f2
Definition: cc708decoder.cpp:25
cc708reader.h
SPC
@ SPC
Definition: cc708decoder.cpp:102
HDW
@ HDW
Definition: cc708decoder.cpp:101
handle_cc_c3
static int handle_cc_c3(CC708Reader *cc, uint service_num, int i)
Definition: cc708decoder.cpp:538
SEND_STR
#define SEND_STR
Definition: cc708decoder.cpp:119
kCCTypes
kCCTypes
Definition: cc708decoder.cpp:22
append_character
static void append_character(CC708Reader *cc, uint service_num, short ch)
Definition: cc708decoder.cpp:712
CC708Decoder::decode_cc_null
void decode_cc_null(void)
Definition: cc708decoder.cpp:70
CCtableG1
const cc_table CCtableG1
Definition: cc708decoder.cpp:758
EXT1
@ EXT1
Definition: cc708decoder.cpp:94
C0
C0
Definition: cc708decoder.cpp:86
cc708_seen_times
std::array< SystemTime, 64 > cc708_seen_times
Definition: cc708decoder.h:16
handle_cc_c1
static int handle_cc_c1(CC708Reader *cc, uint service_num, int i)
Definition: cc708decoder.cpp:375
DF6
@ DF6
Definition: cc708decoder.cpp:103
CaptionPacket
EIA-708-A closed caption packet.
Definition: cc708decoder.h:20
CC708Reader
Definition: cc708reader.h:16
DLC
@ DLC
Definition: cc708decoder.cpp:101
CW3
@ CW3
Definition: cc708decoder.cpp:100
parse_cc_service_stream
static void parse_cc_service_stream(CC708Reader *cc, uint service_num)
Definition: cc708decoder.cpp:130
TGW
@ TGW
Definition: cc708decoder.cpp:101
CW5
@ CW5
Definition: cc708decoder.cpp:100