11 #define LOC QString("CC708: ")
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
30 const std::array<const std::string, 4>
cc_types =
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"
46 LOG(VB_VBI, LOG_DEBUG,
LOC + QString(
"CC ST data(0x%1 0x%2)")
47 .arg(data1,0,16).arg(data2,0,16));
60 LOG(VB_VBI, LOG_DEBUG,
LOC + QString(
"CC Ex data(0x%1 0x%2)")
61 .arg(data1,0,16).arg(data2,0,16));
79 auto then = SystemClock::now() - seconds;
82 for (
uint i = 1; i < 64; i++)
86 enum C0 : std::uint8_t
98 enum C1 : std::uint8_t
121 if (cc->m_tempStrSize[service_num]) \
123 cc->TextWrite(service_num, \
124 cc->m_tempStr[service_num], \
125 cc->m_tempStrSize[service_num]); \
126 cc->m_tempStrSize[service_num] = 0; \
132 const int blk_size =
cc->m_bufSize[service_num];
139 for (i = 0; i < blk_size; i++)
141 switch (
cc->m_buf[service_num][i]) {
174 rst_loc = dlc_loc = i;
185 cc->Reset(service_num);
186 cc->m_delayed[service_num] =
false;
187 blk_start = rst_loc + 1;
191 if (dlc_loc &&
cc->m_delayed[service_num])
193 cc->DelayCancel(service_num);
194 cc->m_delayed[service_num] =
false;
198 if (
cc->m_delayed[service_num] && blk_size >= 126)
200 cc->DelayCancel(service_num);
201 cc->m_delayed[service_num] =
false;
202 dlc_loc = blk_size - 1;
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));
211 for (i = (
cc->m_delayed[service_num]) ? blk_size : blk_start;
215 const int code =
cc->m_buf[service_num][i];
220 else if (code <= 0x1f)
225 else if (code <= 0x7f)
233 else if (code <= 0x9f)
238 else if (code <= 0xff)
247 LOG(VB_VBI, LOG_DEBUG, QString(
"i %1, blk_size %2").arg(i)
255 LOG(VB_VBI, LOG_DEBUG, QString(
"old_i == i == %1").arg(i));
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);
261 if (blk_size - i > 10)
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];
271 if (
cc->m_delayed[service_num] && dlc_loc < i)
276 if (
cc->m_delayed[service_num])
279 cc->DelayCancel(service_num);
280 cc->m_delayed[service_num] =
false;
285 if ((blk_size - i) > 0)
287 memmove(
cc->m_buf[service_num],
cc->m_buf[service_num] + i,
289 cc->m_bufSize[service_num] -= i;
293 if (0 != (blk_size - i))
295 LOG(VB_VBI, LOG_ERR, QString(
"buffer error i(%1) buf_size(%2)")
296 .arg(i).arg(blk_size));
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);
302 cc->m_bufSize[service_num] = 0;
309 const int code =
cc->m_buf[service_num][i];
329 const int blk_size =
cc->m_bufSize[service_num];
330 if (
EXT1==code && ((i+1)<blk_size))
332 const int code2 =
cc->m_buf[service_num][i+1];
338 else if (code2<=0x7f)
344 else if (code2<=0x9f)
349 else if (code2<=0xff)
356 else if ((i+1)<blk_size)
364 const int blk_size =
cc->m_bufSize[service_num];
365 if (
P16==code && ((i+2)<blk_size))
377 const int blk_size =
cc->m_bufSize[service_num];
378 const int code =
cc->m_buf[service_num][i];
380 const unsigned char* blk_buf =
cc->m_buf[service_num];
384 cc->SetCurrentWindow(service_num, code-0x80);
387 else if (
DLC ==
cc->m_buf[service_num][i])
395 else if (code<=
DLY && ((i+1)<blk_size))
397 int param1 = blk_buf[i+1];
400 cc->ClearWindows(service_num, param1);
402 cc->DisplayWindows(service_num, param1);
404 cc->HideWindows(service_num, param1);
406 cc->ToggleWindows(service_num, param1);
408 cc->DeleteWindows(service_num, param1);
411 cc->Delay(service_num, param1);
412 cc->m_delayed[service_num] =
true;
416 else if (
SPA==code && ((i+2)<blk_size))
418 int pen_size = (blk_buf[i+1] ) & 0
x3;
419 int offset = (blk_buf[i+1]>>2) & 0
x3;
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) & 0
x1;
424 int italic = (blk_buf[i+2]>>7) & 0
x1;
426 cc->SetPenAttributes(service_num, pen_size, offset, text_tag,
427 font_tag, edge_type, underline, italic);
430 else if (
SPC==code && ((i+3)<blk_size))
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;
438 cc->SetPenColor(service_num, fg_color, fg_opacity,
439 bg_color, bg_opacity, edge_color);
442 else if (
SPL==code && ((i+2)<blk_size))
444 int row = blk_buf[i+1] & 0x0f;
445 int col = blk_buf[i+2] & 0x3f;
447 cc->SetPenLocation(service_num, row, col);
450 else if (
SWA==code && ((i+4)<blk_size))
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;
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);
471 else if ((code>=
DF0) && (code<=
DF7) && ((i+6)<blk_size))
474 int priority = ( blk_buf[i+1] ) & 0x7;
475 int col_lock = (blk_buf[i+1]>>3) & 0
x1;
476 int row_lock = (blk_buf[i+1]>>4) & 0
x1;
477 bool visible = ((blk_buf[i+1]>>5) & 0
x1) != 0;
479 int anchor_vertical = blk_buf[i+2] & 0x7f;
480 int relative_pos = (blk_buf[i+2]>>7);
482 int anchor_horizontal = blk_buf[i+3];
484 int row_count = blk_buf[i+4] & 0xf;
485 int anchor_point = blk_buf[i+4]>>4;
487 int col_count = blk_buf[i+5] & 0x3f;
489 int pen_style = blk_buf[i+6] & 0x7;
490 int win_style = (blk_buf[i+6]>>3) & 0x7;
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);
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));
513 const int blk_size =
cc->m_bufSize[service_num];
514 const int code =
cc->m_buf[service_num][i+1];
516 if ((code<=0x7) && ((i+1)<blk_size)){
520 else if ((code<=0xf) && ((i+2)<blk_size))
525 else if ((code<=0x17) && ((i+3)<blk_size))
530 else if ((code<=0x1f) && ((i+4)<blk_size))
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];
544 if ((code<=0x87) && ((i+5)<blk_size))
549 else if ((code<=0x8f) && ((i+6)<blk_size))
554 else if ((i+2)<blk_size)
556 int length = blk_buf[i+2]&0x3f;
557 if ((i+length)<blk_size)
568 size_t min_new_size =
block_size +
cc->m_bufSize[service_num];
570 if (min_new_size >=
cc->m_bufAlloc[service_num])
572 size_t new_alloc =
cc->m_bufAlloc[service_num];
573 for (
uint i = 0; (i < 32) && (new_alloc <= min_new_size); i++)
575 void *new_buf = realloc(
cc->m_buf[service_num], new_alloc);
578 cc->m_buf[service_num] = (uchar *)new_buf;
579 cc->m_bufAlloc[service_num] = new_alloc;
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]));
591 if (min_new_size >=
cc->m_bufAlloc[service_num])
594 QString(
"buffer resize error: min_new_size=%1, buf_alloc[%2]=%3")
597 .arg(
cc->m_bufAlloc[service_num]));
611 memcpy(
cc->m_buf[service_num] +
cc->m_bufSize[service_num],
615 #if DEBUG_CC_SERVICE_2
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);
630 const unsigned char* pkt_buf = pkt->
data.data();
631 const int pkt_size = pkt->
size;
633 int len = ((((int)pkt_buf[0]) & 0x3f)<<1) - 1;
638 #if DEBUG_CC_RAWPACKET
646 int srv = (pkt_buf[off]>>5) & 0x7;
647 int seq_num = (((int)pkt_buf[0])>>6)&0
x3;
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);
657 QString(
"Unexpected pkt_size=%1").arg(pkt_size));
659 while (off < pkt_size && pkt_buf[off])
662 int service_number = (pkt_buf[off]>>5) & 0x7;
663 int block_data_offset = (0x7==service_number &&
block_size!=0) ?
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));
670 if (off+2 == block_data_offset)
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));
677 service_number = extended_service_number;
684 0==pkt_buf[block_data_offset] &&
685 0==pkt_buf[block_data_offset+1]))
687 QString msg = QString(
"service %1: ").arg(service_number);
689 msg += QString(
"0x%1 ")
690 .arg(pkt_buf[block_data_offset+i], 0, 16);
691 LOG(VB_VBI, LOG_DEBUG, msg);
697 last_seen[service_number] = std::chrono::system_clock::now();
703 if (pkt_buf[off] != 0)
706 QString(
"CEA-708 packet error: pkt_size=%1, pkt_buf[%2]=%3")
707 .arg(pkt_size).arg(off).arg(pkt_buf[off]));
714 if (
cc->m_tempStrSize[service_num]+2 >
cc->m_tempStrAlloc[service_num])
716 int new_alloc = (
cc->m_tempStrAlloc[service_num]) ?
717 cc->m_tempStrAlloc[service_num] * 2 : 64;
719 cc->m_tempStr[service_num] = (
short*)
720 realloc(
cc->m_tempStr[service_num], new_alloc *
sizeof(
short));
722 cc->m_tempStrAlloc[service_num] = new_alloc;
725 if (
cc->m_tempStr[service_num])
727 int i =
cc->m_tempStrSize[service_num];
728 cc->m_tempStr[service_num][i] = ch;
729 cc->m_tempStrSize[service_num]++;
733 cc->m_tempStrSize[service_num] = 0;
734 cc->m_tempStrAlloc[service_num]=0;
742 ' ',
'!',
'\"',
'#',
'$',
'%',
'&',
'\'',
743 '(',
')',
'*',
'+',
',',
'-',
'.',
'/',
744 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
745 '8',
'9',
':',
';',
'<',
'=',
'>',
'?',
747 '@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
748 'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
749 'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
750 'X',
'Y',
'Z',
'[',
'\\',
']',
'^',
'_',
752 '`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
753 'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
754 'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
755 'x',
'y',
'z',
'{',
'|',
'}',
'~', 0x266a,
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 ,
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 ,
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 ,
846 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
847 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
849 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
850 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
852 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
853 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,