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,2,16,QChar(
'0')).arg(data2,2,16,QChar(
'0')));
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')));
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 std::u16string& s = cc->
m_tempStr[service_num];
135 const int blk_size = cc->
m_buf[service_num].size();
142 for (i = 0; i < blk_size; i++)
144 switch (cc->
m_buf[service_num][i]) {
177 rst_loc = dlc_loc = i;
188 cc->
Reset(service_num);
190 blk_start = rst_loc + 1;
194 if (dlc_loc && cc->
m_delayed[service_num])
201 if (cc->
m_delayed[service_num] && blk_size >= 126)
205 dlc_loc = blk_size - 1;
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));
214 for (i = (cc->
m_delayed[service_num]) ? blk_size : blk_start;
218 const int code = cc->
m_buf[service_num][i];
223 else if (code <= 0x1f)
228 else if (code <= 0x7f)
236 else if (code <= 0x9f)
241 else if (code <= 0xff)
250 LOG(VB_VBI, LOG_DEBUG, QString(
"i %1, blk_size %2").arg(i)
258 LOG(VB_VBI, LOG_DEBUG, QString(
"old_i == i == %1").arg(i));
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);
264 if (blk_size - i > 10)
266 LOG(VB_VBI, LOG_INFO,
"eia-708 decoding error...");
267 cc->
Reset(service_num);
269 i = cc->
m_buf[service_num].size();
274 if (cc->
m_delayed[service_num] && dlc_loc < i)
288 if ((blk_size - i) > 0)
290 cc->
m_buf[service_num].erase(cc->
m_buf[service_num].begin(),
291 cc->
m_buf[service_num].begin() + i);
295 if (0 != (blk_size - i))
297 LOG(VB_VBI, LOG_ERR, QString(
"buffer error i(%1) buf_size(%2)")
298 .arg(i).arg(blk_size));
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);
304 cc->
m_buf[service_num].clear();
311 const int code = cc->
m_buf[service_num][i];
331 const int blk_size = cc->
m_buf[service_num].size();
332 if (
EXT1==code && ((i+1)<blk_size))
334 const int code2 = cc->
m_buf[service_num][i+1];
340 else if (code2<=0x7f)
346 else if (code2<=0x9f)
351 else if (code2<=0xff)
358 else if ((i+1)<blk_size)
366 const int blk_size = cc->
m_buf[service_num].size();
367 if (
P16==code && ((i+2)<blk_size))
379 const int blk_size = cc->
m_buf[service_num].size();
380 const int code = cc->
m_buf[service_num][i];
382 const unsigned char* blk_buf = cc->
m_buf[service_num].data();
389 else if (
DLC == cc->
m_buf[service_num][i])
397 else if (code<=
DLY && ((i+1)<blk_size))
399 int param1 = blk_buf[i+1];
413 cc->
Delay(service_num, param1);
418 else if (
SPA==code && ((i+2)<blk_size))
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;
429 font_tag, edge_type, underline, italic);
432 else if (
SPC==code && ((i+3)<blk_size))
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;
441 bg_color, bg_opacity, edge_color);
444 else if (
SPL==code && ((i+2)<blk_size))
446 int row = blk_buf[i+1] & 0x0f;
447 int col = blk_buf[i+2] & 0x3f;
452 else if (
SWA==code && ((i+4)<blk_size))
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;
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);
473 else if ((code>=
DF0) && (code<=
DF7) && ((i+6)<blk_size))
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;
481 int anchor_vertical = blk_buf[i+2] & 0x7f;
482 int relative_pos = (blk_buf[i+2]>>7);
484 int anchor_horizontal = blk_buf[i+3];
486 int row_count = blk_buf[i+4] & 0xf;
487 int anchor_point = blk_buf[i+4]>>4;
489 int col_count = blk_buf[i+5] & 0x3f;
491 int pen_style = blk_buf[i+6] & 0x7;
492 int win_style = (blk_buf[i+6]>>3) & 0x7;
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);
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));
515 const int blk_size = cc->
m_buf[service_num].size();
516 const int code = cc->
m_buf[service_num][i+1];
518 if ((code<=0x7) && ((i+1)<blk_size)){
522 else if ((code<=0xf) && ((i+2)<blk_size))
527 else if ((code<=0x17) && ((i+3)<blk_size))
532 else if ((code<=0x1f) && ((i+4)<blk_size))
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];
546 if ((code<=0x87) && ((i+5)<blk_size))
551 else if ((code<=0x8f) && ((i+6)<blk_size))
556 else if ((i+2)<blk_size)
558 int length = blk_buf[i+2]&0x3f;
559 if ((i+length)<blk_size)
573 cc->
m_buf[service_num].insert(cc->
m_buf[service_num].end(),
576 catch (
const std::bad_alloc& e)
579 LOG(VB_VBI, LOG_DEBUG, QString(
"append_cc: srv %1 failed to add %2 bytes")
583#if DEBUG_CC_SERVICE_2
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);
598 const unsigned char* pkt_buf = pkt->
data.data();
599 const int pkt_size = pkt->
size;
601 int len = ((((int)pkt_buf[0]) & 0x3f)<<1) - 1;
606#if DEBUG_CC_RAWPACKET
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);
625 QString(
"Unexpected pkt_size=%1").arg(pkt_size));
627 while (off < pkt_size && pkt_buf[off])
630 int service_number = (pkt_buf[off]>>5) & 0x7;
631 int block_data_offset = (0x7==service_number &&
block_size!=0) ?
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));
638 if (off+2 == block_data_offset)
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));
645 service_number = extended_service_number;
652 0==pkt_buf[block_data_offset] &&
653 0==pkt_buf[block_data_offset+1]))
655 QString msg = QString(
"service %1:").arg(service_number);
657 msg += QString(
" 0x%1")
658 .arg(pkt_buf[block_data_offset+i], 2, 16, QChar(
'0'));
659 LOG(VB_VBI, LOG_DEBUG, msg);
665 last_seen[service_number] = std::chrono::system_clock::now();
671 if (pkt_buf[off] != 0)
674 QString(
"CEA-708 packet error: pkt_size=%1, pkt_buf[%2]=%3")
675 .arg(pkt_size).arg(off).arg(pkt_buf[off]));
682 std::u16string& s = cc->
m_tempStr[service_num];
683 if (s.size()+2 > s.capacity())
684 s.resize(s.capacity() * 2);
692 ' ',
'!',
'\"',
'#',
'$',
'%',
'&',
'\'',
693 '(',
')',
'*',
'+',
',',
'-',
'.',
'/',
694 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
695 '8',
'9',
':',
';',
'<',
'=',
'>',
'?',
697 '@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
698 'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
699 'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
700 'X',
'Y',
'Z',
'[',
'\\',
']',
'^',
'_',
702 '`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
703 'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
704 'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
705 'x',
'y',
'z',
'{',
'|',
'}',
'~', 0x266a,
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 ,
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 ,
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 ,
796 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
797 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
799 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
800 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
802 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
803 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 int handle_cc_c2(CC708Reader *cc, uint service_num, int i)
std::array< const uint16_t, 0x60 > cc_table
static void send_str(CC708Reader *cc, uint service_num)
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)
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< std::vector< uint8_t >, k708MaxServices > m_buf
virtual void Delay(uint service_num, int tenths_of_seconds)
std::array< std::u16string, k708MaxServices > m_tempStr
virtual void TextWrite(uint service_num, std::u16string &unicode_string)
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)
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)
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