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
30const 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));
70 uint8_t total_packet_size = packet_size_code * 2;
71 if (packet_size_code == 0)
73 total_packet_size = 128;
84 auto then = SystemClock::now() - seconds;
87 for (
uint i = 1; i < 64; i++)
103enum C1 : std::uint8_t
126 if (cc->m_tempStrSize[service_num]) \
128 cc->TextWrite(service_num, \
129 cc->m_tempStr[service_num], \
130 cc->m_tempStrSize[service_num]); \
131 cc->m_tempStrSize[service_num] = 0; \
137 const int blk_size = cc->
m_bufSize[service_num];
144 for (i = 0; i < blk_size; i++)
146 switch (cc->
m_buf[service_num][i]) {
179 rst_loc = dlc_loc = i;
190 cc->
Reset(service_num);
192 blk_start = rst_loc + 1;
196 if (dlc_loc && cc->
m_delayed[service_num])
203 if (cc->
m_delayed[service_num] && blk_size >= 126)
207 dlc_loc = blk_size - 1;
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));
216 for (i = (cc->
m_delayed[service_num]) ? blk_size : blk_start;
220 const int code = cc->
m_buf[service_num][i];
225 else if (code <= 0x1f)
230 else if (code <= 0x7f)
238 else if (code <= 0x9f)
243 else if (code <= 0xff)
252 LOG(VB_VBI, LOG_DEBUG, QString(
"i %1, blk_size %2").arg(i)
260 LOG(VB_VBI, LOG_DEBUG, QString(
"old_i == i == %1").arg(i));
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);
266 if (blk_size - i > 10)
268 LOG(VB_VBI, LOG_INFO,
"eia-708 decoding error...");
269 cc->
Reset(service_num);
276 if (cc->
m_delayed[service_num] && dlc_loc < i)
290 if ((blk_size - i) > 0)
292 memmove(cc->
m_buf[service_num], cc->
m_buf[service_num] + i,
298 if (0 != (blk_size - i))
300 LOG(VB_VBI, LOG_ERR, QString(
"buffer error i(%1) buf_size(%2)")
301 .arg(i).arg(blk_size));
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);
314 const int code = cc->
m_buf[service_num][i];
334 const int blk_size = cc->
m_bufSize[service_num];
335 if (
EXT1==code && ((i+1)<blk_size))
337 const int code2 = cc->
m_buf[service_num][i+1];
343 else if (code2<=0x7f)
349 else if (code2<=0x9f)
354 else if (code2<=0xff)
361 else if ((i+1)<blk_size)
369 const int blk_size = cc->
m_bufSize[service_num];
370 if (
P16==code && ((i+2)<blk_size))
382 const int blk_size = cc->
m_bufSize[service_num];
383 const int code = cc->
m_buf[service_num][i];
385 const unsigned char* blk_buf = cc->
m_buf[service_num];
392 else if (
DLC == cc->
m_buf[service_num][i])
400 else if (code<=
DLY && ((i+1)<blk_size))
402 int param1 = blk_buf[i+1];
416 cc->
Delay(service_num, param1);
421 else if (
SPA==code && ((i+2)<blk_size))
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;
432 font_tag, edge_type, underline, italic);
435 else if (
SPC==code && ((i+3)<blk_size))
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;
444 bg_color, bg_opacity, edge_color);
447 else if (
SPL==code && ((i+2)<blk_size))
449 int row = blk_buf[i+1] & 0x0f;
450 int col = blk_buf[i+2] & 0x3f;
455 else if (
SWA==code && ((i+4)<blk_size))
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;
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);
476 else if ((code>=
DF0) && (code<=
DF7) && ((i+6)<blk_size))
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;
484 int anchor_vertical = blk_buf[i+2] & 0x7f;
485 int relative_pos = (blk_buf[i+2]>>7);
487 int anchor_horizontal = blk_buf[i+3];
489 int row_count = blk_buf[i+4] & 0xf;
490 int anchor_point = blk_buf[i+4]>>4;
492 int col_count = blk_buf[i+5] & 0x3f;
494 int pen_style = blk_buf[i+6] & 0x7;
495 int win_style = (blk_buf[i+6]>>3) & 0x7;
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);
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));
518 const int blk_size = cc->
m_bufSize[service_num];
519 const int code = cc->
m_buf[service_num][i+1];
521 if ((code<=0x7) && ((i+1)<blk_size)){
525 else if ((code<=0xf) && ((i+2)<blk_size))
530 else if ((code<=0x17) && ((i+3)<blk_size))
535 else if ((code<=0x1f) && ((i+4)<blk_size))
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];
549 if ((code<=0x87) && ((i+5)<blk_size))
554 else if ((code<=0x8f) && ((i+6)<blk_size))
559 else if ((i+2)<blk_size)
561 int length = blk_buf[i+2]&0x3f;
562 if ((i+length)<blk_size)
575 if (min_new_size >= cc->
m_bufAlloc[service_num])
577 size_t new_alloc = cc->
m_bufAlloc[service_num];
578 for (
uint i = 0; (i < 32) && (new_alloc <= min_new_size); i++)
580 void *new_buf = realloc(cc->
m_buf[service_num], new_alloc);
583 cc->
m_buf[service_num] = (uchar *)new_buf;
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]));
596 if (min_new_size >= cc->
m_bufAlloc[service_num])
599 QString(
"buffer resize error: min_new_size=%1, buf_alloc[%2]=%3")
620#if DEBUG_CC_SERVICE_2
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);
635 const unsigned char* pkt_buf = pkt->
data.data();
636 const int pkt_size = pkt->
size;
638 int len = ((((int)pkt_buf[0]) & 0x3f)<<1) - 1;
643#if DEBUG_CC_RAWPACKET
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);
662 QString(
"Unexpected pkt_size=%1").arg(pkt_size));
664 while (off < pkt_size && pkt_buf[off])
667 int service_number = (pkt_buf[off]>>5) & 0x7;
668 int block_data_offset = (0x7==service_number &&
block_size!=0) ?
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));
675 if (off+2 == block_data_offset)
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));
682 service_number = extended_service_number;
689 0==pkt_buf[block_data_offset] &&
690 0==pkt_buf[block_data_offset+1]))
692 QString msg = QString(
"service %1: ").arg(service_number);
694 msg += QString(
"0x%1 ")
695 .arg(pkt_buf[block_data_offset+i], 0, 16);
696 LOG(VB_VBI, LOG_DEBUG, msg);
702 last_seen[service_number] = std::chrono::system_clock::now();
708 if (pkt_buf[off] != 0)
711 QString(
"CEA-708 packet error: pkt_size=%1, pkt_buf[%2]=%3")
712 .arg(pkt_size).arg(off).arg(pkt_buf[off]));
725 realloc(cc->
m_tempStr[service_num], new_alloc *
sizeof(
short));
747 ' ',
'!',
'\"',
'#',
'$',
'%',
'&',
'\'',
748 '(',
')',
'*',
'+',
',',
'-',
'.',
'/',
749 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
750 '8',
'9',
':',
';',
'<',
'=',
'>',
'?',
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',
'[',
'\\',
']',
'^',
'_',
757 '`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
758 'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
759 'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
760 'x',
'y',
'z',
'{',
'|',
'}',
'~', 0x266a,
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 ,
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 ,
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 ,
851 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,
854 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
855 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
857 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
858 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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)
static bool rightsize_buf(CC708Reader *cc, uint service_num, uint block_size)
static int handle_cc_c2(CC708Reader *cc, uint service_num, int i)
std::array< const uint16_t, 0x60 > cc_table
static void parse_cc_service_stream(CC708Reader *cc, uint service_num)
std::array< bool, 64 > cc708_seen_flags
std::array< SystemTime, 64 > cc708_seen_times
cc708_seen_times m_lastSeen
void decode_cc_data(uint cc_type, uint data1, uint data2)
CaptionPacket m_partialPacket
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
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)
std::array< int16_t *, k708MaxServices > m_tempStr
virtual void Delay(uint service_num, int tenths_of_seconds)
std::array< uint, k708MaxServices > m_bufAlloc
std::array< uint, k708MaxServices > m_bufSize
virtual void DeleteWindows(uint service_num, int window_map)
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
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)
std::array< unsigned char *, k708MaxServices > m_buf
virtual void DelayCancel(uint service_num)
virtual void SetPenLocation(uint service_num, int row, int column)
std::array< bool, k708MaxServices > m_delayed
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
EIA-708-A closed caption packet.
std::array< uint8_t, 128+16 > data