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