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];
403 }
else if (
DSW==code) {
405 }
else if (
HDW==code) {
407 }
else if (
TGW==code) {
409 }
else if (
DLW==code) {
411 }
else if (
DLY==code) {
412 cc->
Delay(service_num, param1);
417 else if (
SPA==code && ((i+2)<blk_size))
419 int pen_size = (blk_buf[i+1] ) & 0x3;
420 int offset = (blk_buf[i+1]>>2) & 0x3;
421 int text_tag = (blk_buf[i+1]>>4) & 0xf;
422 int font_tag = (blk_buf[i+2] ) & 0x7;
423 int edge_type = (blk_buf[i+2]>>3) & 0x7;
424 int underline = (blk_buf[i+2]>>6) & 0x1;
425 int italic = (blk_buf[i+2]>>7) & 0x1;
428 font_tag, edge_type, underline, italic);
431 else if (
SPC==code && ((i+3)<blk_size))
433 int fg_color = (blk_buf[i+1] ) & 0x3f;
434 int fg_opacity = (blk_buf[i+1]>>6) & 0x03;
435 int bg_color = (blk_buf[i+2] ) & 0x3f;
436 int bg_opacity = (blk_buf[i+2]>>6) & 0x03;
437 int edge_color = (blk_buf[i+3]>>6) & 0x3f;
440 bg_color, bg_opacity, edge_color);
443 else if (
SPL==code && ((i+2)<blk_size))
445 int row = blk_buf[i+1] & 0x0f;
446 int col = blk_buf[i+2] & 0x3f;
451 else if (
SWA==code && ((i+4)<blk_size))
453 int fill_color = (blk_buf[i+1] ) & 0x3f;
454 int fill_opacity = (blk_buf[i+1]>>6) & 0x03;
455 int border_color = (blk_buf[i+2] ) & 0x3f;
456 int border_type01 = (blk_buf[i+2]>>6) & 0x03;
457 int justify = (blk_buf[i+3] ) & 0x03;
458 int scroll_dir = (blk_buf[i+3]>>2) & 0x03;
459 int print_dir = (blk_buf[i+3]>>4) & 0x03;
460 int word_wrap = (blk_buf[i+3]>>6) & 0x01;
461 int border_type = (blk_buf[i+3]>>5) | border_type01;
462 int display_eff = (blk_buf[i+4] ) & 0x03;
463 int effect_dir = (blk_buf[i+4]>>2) & 0x03;
464 int effect_speed = (blk_buf[i+4]>>4) & 0x0f;
467 service_num, fill_color, fill_opacity, border_color, border_type,
468 scroll_dir, print_dir, effect_dir,
469 display_eff, effect_speed, justify, word_wrap);
472 else if ((code>=
DF0) && (code<=
DF7) && ((i+6)<blk_size))
475 int priority = ( blk_buf[i+1] ) & 0x7;
476 int col_lock = (blk_buf[i+1]>>3) & 0x1;
477 int row_lock = (blk_buf[i+1]>>4) & 0x1;
478 bool visible = ((blk_buf[i+1]>>5) & 0x1) != 0;
480 int anchor_vertical = blk_buf[i+2] & 0x7f;
481 int relative_pos = (blk_buf[i+2]>>7);
483 int anchor_horizontal = blk_buf[i+3];
485 int row_count = blk_buf[i+4] & 0xf;
486 int anchor_point = blk_buf[i+4]>>4;
488 int col_count = blk_buf[i+5] & 0x3f;
490 int pen_style = blk_buf[i+6] & 0x7;
491 int win_style = (blk_buf[i+6]>>3) & 0x7;
493 cc->
DefineWindow(service_num, code-0x98, priority, visible,
494 anchor_point, relative_pos,
495 anchor_vertical, anchor_horizontal,
496 row_count, col_count, row_lock, col_lock,
497 pen_style, win_style);
503 LOG(VB_VBI, LOG_ERR, QString(
"handle_cc_c1: (NOT HANDLED) "
504 "code(0x%1) i(%2) blk_size(%3)").arg(code, 2, 16, QLatin1Char(
'0'))
505 .arg(i).arg(blk_size));
514 const int blk_size = cc->
m_buf[service_num].size();
515 const int code = cc->
m_buf[service_num][i+1];
517 if ((code<=0x7) && ((i+1)<blk_size)){
521 else if ((code<=0xf) && ((i+2)<blk_size))
526 else if ((code<=0x17) && ((i+3)<blk_size))
531 else if ((code<=0x1f) && ((i+4)<blk_size))
541 const unsigned char* blk_buf = cc->
m_buf[service_num].data();
542 const int blk_size = cc->
m_buf[service_num].size();
543 const int code = cc->
m_buf[service_num][i+1];
545 if ((code<=0x87) && ((i+5)<blk_size))
550 else if ((code<=0x8f) && ((i+6)<blk_size))
555 else if ((i+2)<blk_size)
557 int length = blk_buf[i+2]&0x3f;
558 if ((i+length)<blk_size)
572 cc->
m_buf[service_num].insert(cc->
m_buf[service_num].end(),
575 catch (
const std::bad_alloc& e)
578 LOG(VB_VBI, LOG_DEBUG, QString(
"append_cc: srv %1 failed to add %2 bytes")
582#if DEBUG_CC_SERVICE_2
585 QString msg(
"append_cc:");
586 for (i = 0; i < cc->
m_buf[service_num].size(); i++)
587 msg += QString(
" 0x%1").arg(cc->
m_buf[service_num][i], 2, 16, QChar(
'0'));
588 LOG(VB_VBI, LOG_DEBUG, msg);
597 const unsigned char* pkt_buf = pkt->
data.data();
598 const int pkt_size = pkt->
size;
600 int len = ((((int)pkt_buf[0]) & 0x3f)<<1) - 1;
605#if DEBUG_CC_RAWPACKET
613 int srv = (pkt_buf[off]>>5) & 0x7;
614 int seq_num = (((int)pkt_buf[0])>>6)&0x3;
615 QString msg = QString(
"CC708 len %1 srv0 %2 seq %3").arg(len, 2)
616 .arg(srv) .arg(seq_num);
617 for (
int j = 0; j < pkt_size; j++)
618 msg += QString(
" 0x%1").arg(pkt_buf[j], 0, 16);
619 LOG(VB_VBI, LOG_DEBUG, msg);
624 QString(
"Unexpected pkt_size=%1").arg(pkt_size));
626 while (off < pkt_size && pkt_buf[off])
629 int service_number = (pkt_buf[off]>>5) & 0x7;
630 int block_data_offset = (0x7==service_number &&
block_size!=0) ?
632#if DEBUG_CC_SERVICE_BLOCK
633 LOG(VB_VBI, LOG_DEBUG,
634 QString(
"block_size(%1) service_number(%2) block_data_offset(%3)")
635 .arg(
block_size) .arg(service_number) .arg(block_data_offset));
637 if (off+2 == block_data_offset)
639 int extended_service_number = pkt_buf[off+2] & 0x3f;
640#if DEBUG_CC_SERVICE_BLOCK
641 LOG(VB_VBI, LOG_DEBUG, QString(
"extended_service_number(%1)")
642 .arg(extended_service_number));
644 service_number = extended_service_number;
651 0==pkt_buf[block_data_offset] &&
652 0==pkt_buf[block_data_offset+1]))
654 QString msg = QString(
"service %1:").arg(service_number);
656 msg += QString(
" 0x%1")
657 .arg(pkt_buf[block_data_offset+i], 2, 16, QChar(
'0'));
658 LOG(VB_VBI, LOG_DEBUG, msg);
664 last_seen[service_number] = std::chrono::system_clock::now();
670 if (pkt_buf[off] != 0)
673 QString(
"CEA-708 packet error: pkt_size=%1, pkt_buf[%2]=%3")
674 .arg(pkt_size).arg(off).arg(pkt_buf[off]));
681 std::u16string& s = cc->
m_tempStr[service_num];
682 if (s.size()+2 > s.capacity())
683 s.resize(s.capacity() * 2);
691 ' ',
'!',
'\"',
'#',
'$',
'%',
'&',
'\'',
692 '(',
')',
'*',
'+',
',',
'-',
'.',
'/',
693 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
694 '8',
'9',
':',
';',
'<',
'=',
'>',
'?',
696 '@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
697 'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
698 'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
699 'X',
'Y',
'Z',
'[',
'\\',
']',
'^',
'_',
701 '`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
702 'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
703 'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
704 'x',
'y',
'z',
'{',
'|',
'}',
'~', 0x266a,
715 0xA4 , 0xA5 , 0xA6 , 0xA7 ,
716 0xA8 , 0xA9 , 0xAA , 0xAB ,
717 0xAC , 0xAD , 0xAE , 0xAF ,
718 0xB0 , 0xB1 , 0xB2 , 0xB3 ,
719 0xB4 , 0xB5 , 0xB6 , 0xB7 ,
720 0xB8 , 0xB9 , 0xBA , 0xBB ,
721 0xBC , 0xBD , 0xBE , 0xBF ,
723 0xC0 , 0xC1 , 0xC2 , 0xC3 ,
724 0xC4 , 0xC5 , 0xC6 , 0xC7 ,
725 0xC8 , 0xC9 , 0xCA , 0xCB ,
726 0xCC , 0xCD , 0xCE , 0xCF ,
727 0xD0 , 0xD1 , 0xD2 , 0xD3 ,
728 0xD4 , 0xD5 , 0xD6 , 0xD7 ,
729 0xD8 , 0xD9 , 0xDA , 0xDB ,
730 0xDC , 0xDD , 0xDE , 0xDF ,
732 0xE0 , 0xE1 , 0xE2 , 0xE3 ,
733 0xE4 , 0xE5 , 0xE6 , 0xE7 ,
734 0xE8 , 0xE9 , 0xEA , 0xEB ,
735 0xEC , 0xED , 0xEE , 0xEF ,
736 0xF0 , 0xF1 , 0xF2 , 0xF3 ,
737 0xF4 , 0xF5 , 0xF6 , 0xF7 ,
738 0xF8 , 0xF9 , 0xFA , 0xFB ,
739 0xFC , 0xFD , 0xFE , 0xFF ,
795 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
796 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
798 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,
801 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,
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