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++)
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)
362 const int blk_size =
cc->m_bufSize[service_num];
363 if (
P16==code && ((i+2)<blk_size))
375 const int blk_size =
cc->m_bufSize[service_num];
376 const int code =
cc->m_buf[service_num][i];
378 const unsigned char* blk_buf =
cc->m_buf[service_num];
382 cc->SetCurrentWindow(service_num, code-0x80);
385 else if (
DLC ==
cc->m_buf[service_num][i])
393 else if (code<=
DLY && ((i+1)<blk_size))
395 int param1 = blk_buf[i+1];
398 cc->ClearWindows(service_num, param1);
400 cc->DisplayWindows(service_num, param1);
402 cc->HideWindows(service_num, param1);
404 cc->ToggleWindows(service_num, param1);
406 cc->DeleteWindows(service_num, param1);
409 cc->Delay(service_num, param1);
410 cc->m_delayed[service_num] =
true;
414 else if (
SPA==code && ((i+2)<blk_size))
416 int pen_size = (blk_buf[i+1] ) & 0
x3;
417 int offset = (blk_buf[i+1]>>2) & 0
x3;
418 int text_tag = (blk_buf[i+1]>>4) & 0xf;
419 int font_tag = (blk_buf[i+2] ) & 0x7;
420 int edge_type = (blk_buf[i+2]>>3) & 0x7;
421 int underline = (blk_buf[i+2]>>6) & 0
x1;
422 int italic = (blk_buf[i+2]>>7) & 0
x1;
424 cc->SetPenAttributes(service_num, pen_size, offset, text_tag,
425 font_tag, edge_type, underline, italic);
428 else if (
SPC==code && ((i+3)<blk_size))
430 int fg_color = (blk_buf[i+1] ) & 0x3f;
431 int fg_opacity = (blk_buf[i+1]>>6) & 0x03;
432 int bg_color = (blk_buf[i+2] ) & 0x3f;
433 int bg_opacity = (blk_buf[i+2]>>6) & 0x03;
434 int edge_color = (blk_buf[i+3]>>6) & 0x3f;
436 cc->SetPenColor(service_num, fg_color, fg_opacity,
437 bg_color, bg_opacity, edge_color);
440 else if (
SPL==code && ((i+2)<blk_size))
442 int row = blk_buf[i+1] & 0x0f;
443 int col = blk_buf[i+2] & 0x3f;
445 cc->SetPenLocation(service_num, row, col);
448 else if (
SWA==code && ((i+4)<blk_size))
450 int fill_color = (blk_buf[i+1] ) & 0x3f;
451 int fill_opacity = (blk_buf[i+1]>>6) & 0x03;
452 int border_color = (blk_buf[i+2] ) & 0x3f;
453 int border_type01 = (blk_buf[i+2]>>6) & 0x03;
454 int justify = (blk_buf[i+3] ) & 0x03;
455 int scroll_dir = (blk_buf[i+3]>>2) & 0x03;
456 int print_dir = (blk_buf[i+3]>>4) & 0x03;
457 int word_wrap = (blk_buf[i+3]>>6) & 0x01;
458 int border_type = (blk_buf[i+3]>>5) | border_type01;
459 int display_eff = (blk_buf[i+4] ) & 0x03;
460 int effect_dir = (blk_buf[i+4]>>2) & 0x03;
461 int effect_speed = (blk_buf[i+4]>>4) & 0x0f;
463 cc->SetWindowAttributes(
464 service_num, fill_color, fill_opacity, border_color, border_type,
465 scroll_dir, print_dir, effect_dir,
466 display_eff, effect_speed, justify, word_wrap);
469 else if ((code>=
DF0) && (code<=
DF7) && ((i+6)<blk_size))
472 int priority = ( blk_buf[i+1] ) & 0x7;
473 int col_lock = (blk_buf[i+1]>>3) & 0
x1;
474 int row_lock = (blk_buf[i+1]>>4) & 0
x1;
475 bool visible = ((blk_buf[i+1]>>5) & 0
x1) != 0;
477 int anchor_vertical = blk_buf[i+2] & 0x7f;
478 int relative_pos = (blk_buf[i+2]>>7);
480 int anchor_horizontal = blk_buf[i+3];
482 int row_count = blk_buf[i+4] & 0xf;
483 int anchor_point = blk_buf[i+4]>>4;
485 int col_count = blk_buf[i+5] & 0x3f;
487 int pen_style = blk_buf[i+6] & 0x7;
488 int win_style = (blk_buf[i+6]>>3) & 0x7;
490 cc->DefineWindow(service_num, code-0x98, priority, visible,
491 anchor_point, relative_pos,
492 anchor_vertical, anchor_horizontal,
493 row_count, col_count, row_lock, col_lock,
494 pen_style, win_style);
500 LOG(VB_VBI, LOG_ERR, QString(
"handle_cc_c1: (NOT HANDLED) "
501 "code(0x%1) i(%2) blk_size(%3)").arg(code, 2, 16, QLatin1Char(
'0'))
502 .arg(i).arg(blk_size));
511 const int blk_size =
cc->m_bufSize[service_num];
512 const int code =
cc->m_buf[service_num][i+1];
514 if ((code<=0x7) && ((i+1)<blk_size)){
518 else if ((code<=0xf) && ((i+2)<blk_size))
523 else if ((code<=0x17) && ((i+3)<blk_size))
528 else if ((code<=0x1f) && ((i+4)<blk_size))
538 const unsigned char* blk_buf =
cc->m_buf[service_num];
539 const int blk_size =
cc->m_bufSize[service_num];
540 const int code =
cc->m_buf[service_num][i+1];
542 if ((code<=0x87) && ((i+5)<blk_size))
547 else if ((code<=0x8f) && ((i+6)<blk_size))
552 else if ((i+2)<blk_size)
554 int length = blk_buf[i+2]&0x3f;
555 if ((i+length)<blk_size)
566 size_t min_new_size =
block_size +
cc->m_bufSize[service_num];
568 if (min_new_size >=
cc->m_bufAlloc[service_num])
570 size_t new_alloc =
cc->m_bufAlloc[service_num];
571 for (
uint i = 0; (i < 32) && (new_alloc <= min_new_size); i++)
573 void *new_buf = realloc(
cc->m_buf[service_num], new_alloc);
576 cc->m_buf[service_num] = (uchar *)new_buf;
577 cc->m_bufAlloc[service_num] = new_alloc;
584 #if DEBUG_CC_SERVICE_2
585 LOG(VB_VBI, LOG_DEBUG, QString(
"rightsize_buf: srv %1 to %1 bytes")
586 .arg(service_num) .arg(
cc->m_bufAlloc[service_num]));
589 if (min_new_size >=
cc->m_bufAlloc[service_num])
592 QString(
"buffer resize error: min_new_size=%1, buf_alloc[%2]=%3")
595 .arg(
cc->m_bufAlloc[service_num]));
609 memcpy(
cc->m_buf[service_num] +
cc->m_bufSize[service_num],
613 #if DEBUG_CC_SERVICE_2
616 QString msg(
"append_cc: ");
617 for (i = 0; i <
cc->m_bufSize[service_num]; i++)
618 msg += QString(
"0x%1").arg(
cc->m_buf[service_num][i], 0, 16);
619 LOG(VB_VBI, LOG_DEBUG, msg);
628 const unsigned char* pkt_buf = pkt->
data.data();
629 const int pkt_size = pkt->
size;
631 int len = ((((int)pkt_buf[0]) & 0x3f)<<1) - 1;
636 #if DEBUG_CC_RAWPACKET
644 int srv = (pkt_buf[off]>>5) & 0x7;
645 int seq_num = (((int)pkt_buf[0])>>6)&0
x3;
646 QString msg = QString(
"CC708 len %1 srv0 %2 seq %3 ").arg(len, 2)
647 .arg(srv) .arg(seq_num);
648 for (
int j = 0; j < pkt_size; j++)
649 msg += QString(
"0x%1").arg(pkt_buf[j], 0, 16);
650 LOG(VB_VBI, LOG_DEBUG, msg);
655 QString(
"Unexpected pkt_size=%1").arg(pkt_size));
657 while (off < pkt_size && pkt_buf[off])
660 int service_number = (pkt_buf[off]>>5) & 0x7;
661 int block_data_offset = (0x7==service_number &&
block_size!=0) ?
663 #if DEBUG_CC_SERVICE_BLOCK
664 LOG(VB_VBI, LOG_DEBUG,
665 QString(
"service_block size(%1) num(%2) off(%3)")
666 .arg(
block_size) .arg(service_number) .arg(block_data_offset));
668 if (off+2 == block_data_offset)
670 int extended_service_number = pkt_buf[off+2] & 0x3f;
671 #if DEBUG_CC_SERVICE_BLOCK
672 LOG(VB_VBI, LOG_DEBUG, QString(
"ext_svc_num(%1)")
673 .arg(extended_service_number));
675 service_number = extended_service_number;
682 0==pkt_buf[block_data_offset] &&
683 0==pkt_buf[block_data_offset+1]))
685 QString msg = QString(
"service %1: ").arg(service_number);
687 msg += QString(
"0x%1 ")
688 .arg(pkt_buf[block_data_offset+i], 0, 16);
689 LOG(VB_VBI, LOG_DEBUG, msg);
695 last_seen[service_number] = std::chrono::system_clock::now();
701 if (pkt_buf[off] != 0)
704 QString(
"CEA-708 packet error: pkt_size=%1, pkt_buf[%2]=%3")
705 .arg(pkt_size).arg(off).arg(pkt_buf[off]));
712 if (
cc->m_tempStrSize[service_num]+2 >
cc->m_tempStrAlloc[service_num])
714 int new_alloc = (
cc->m_tempStrAlloc[service_num]) ?
715 cc->m_tempStrAlloc[service_num] * 2 : 64;
717 cc->m_tempStr[service_num] = (
short*)
718 realloc(
cc->m_tempStr[service_num], new_alloc *
sizeof(
short));
720 cc->m_tempStrAlloc[service_num] = new_alloc;
723 if (
cc->m_tempStr[service_num])
725 int i =
cc->m_tempStrSize[service_num];
726 cc->m_tempStr[service_num][i] = ch;
727 cc->m_tempStrSize[service_num]++;
731 cc->m_tempStrSize[service_num] = 0;
732 cc->m_tempStrAlloc[service_num]=0;
740 ' ',
'!',
'\"',
'#',
'$',
'%',
'&',
'\'',
741 '(',
')',
'*',
'+',
',',
'-',
'.',
'/',
742 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
743 '8',
'9',
':',
';',
'<',
'=',
'>',
'?',
745 '@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
746 'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
747 'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
748 'X',
'Y',
'Z',
'[',
'\\',
']',
'^',
'_',
750 '`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
751 'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
752 'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
753 'x',
'y',
'z',
'{',
'|',
'}',
'~', 0x266a,
764 0xA4 , 0xA5 , 0xA6 , 0xA7 ,
765 0xA8 , 0xA9 , 0xAA , 0xAB ,
766 0xAC , 0xAD , 0xAE , 0xAF ,
767 0xB0 , 0xB1 , 0xB2 , 0xB3 ,
768 0xB4 , 0xB5 , 0xB6 , 0xB7 ,
769 0xB8 , 0xB9 , 0xBA , 0xBB ,
770 0xBC , 0xBD , 0xBE , 0xBF ,
772 0xC0 , 0xC1 , 0xC2 , 0xC3 ,
773 0xC4 , 0xC5 , 0xC6 , 0xC7 ,
774 0xC8 , 0xC9 , 0xCA , 0xCB ,
775 0xCC , 0xCD , 0xCE , 0xCF ,
776 0xD0 , 0xD1 , 0xD2 , 0xD3 ,
777 0xD4 , 0xD5 , 0xD6 , 0xD7 ,
778 0xD8 , 0xD9 , 0xDA , 0xDB ,
779 0xDC , 0xDD , 0xDE , 0xDF ,
781 0xE0 , 0xE1 , 0xE2 , 0xE3 ,
782 0xE4 , 0xE5 , 0xE6 , 0xE7 ,
783 0xE8 , 0xE9 , 0xEA , 0xEB ,
784 0xEC , 0xED , 0xEE , 0xEF ,
785 0xF0 , 0xF1 , 0xF2 , 0xF3 ,
786 0xF4 , 0xF5 , 0xF6 , 0xF7 ,
787 0xF8 , 0xF9 , 0xFA , 0xFB ,
788 0xFC , 0xFD , 0xFE , 0xFF ,
844 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
845 0, 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,
848 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,
851 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,