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