From 6512b877572c84b142484efabb67a7273a989a76 Mon Sep 17 00:00:00 2001 From: Tellen Yu Date: Wed, 10 Jan 2018 07:26:31 +0000 Subject: Merge remote-tracking branch 'remotes/amlogic/ics-amlogic' into HEAD Change-Id: Ic06a6c357a1a958cfd5d76001b7f61eb47439e4b --- diff --git a/Android.mk b/Android.mk index 9caaf2d..0493a45 100644 --- a/Android.mk +++ b/Android.mk @@ -17,9 +17,11 @@ endif LOCAL_ARM_MODE := arm LOCAL_C_INCLUDES := external/icu4c/common +LOCAL_C_INCLUDES := vendor/amlogic/dvb/include/am_adp +#LOCAL_C_INCLUDES := external/dvb/include/am_adp LOCAL_C_INCLUDES += external/icu/icu4c/source/common -LOCAL_SHARED_LIBRARIES += libicuuc liblog +LOCAL_SHARED_LIBRARIES += libicuuc liblog libam_adp LOCAL_PRELINK_MODULE := false diff --git a/ntsc_decode/am_vbi.c b/ntsc_decode/am_vbi.c index 85d82fb..3bb8411 100755 --- a/ntsc_decode/am_vbi.c +++ b/ntsc_decode/am_vbi.c @@ -205,7 +205,7 @@ static void vbi_xds_handler (vbi_event * ev, void * user_data) * For details STFW for "v-chip" * If unknown rating_auth == VBI_RATING_NONE */ - if(!(prog_info->rating_auth == VBI_RATING_AUTH_NONE)) + if(!(prog_info->rating.auth == VBI_RATING_AUTH_NONE)) { xds_program = VBI_XDS_PROGRAM_RATING; AM_DEBUG("xds*********VBI_XDS_PROGRAM_RATING \n"); @@ -244,30 +244,30 @@ static void vbi_xds_handler (vbi_event * ev, void * user_data) } - if(prog_info->rating_auth != VBI_RATING_AUTH_NONE) + if(prog_info->rating.auth != VBI_RATING_AUTH_NONE) { - AM_DEBUG("xds**********prog_info->rating_auth = %d\n",prog_info->rating_auth ); + AM_DEBUG("xds**********prog_info->rating_auth = %d\n",prog_info->rating.auth ); - if(prog_info->rating_auth == VBI_RATING_AUTH_MPAA) + if(prog_info->rating.auth == VBI_RATING_AUTH_MPAA) AM_DEBUG("xds**********result*******************VBI_RATING_AUTH_MPAA\n"); - if(prog_info->rating_auth == VBI_RATING_AUTH_TV_US) + if(prog_info->rating.auth == VBI_RATING_AUTH_TV_US) AM_DEBUG("xds**********result*******************VBI_RATING_AUTH_TV_US\n"); - if(prog_info->rating_auth == VBI_RATING_AUTH_TV_CA_EN) + if(prog_info->rating.auth == VBI_RATING_AUTH_TV_CA_EN) AM_DEBUG("xds**********result*******************VBI_RATING_AUTH_TV_CA_EN\n"); - if(prog_info->rating_auth == VBI_RATING_AUTH_TV_CA_FR) + if(prog_info->rating.auth == VBI_RATING_AUTH_TV_CA_FR) AM_DEBUG("xds**********result*******************VBI_RATING_AUTH_TV_CA_FR\n"); - if(prog_info->rating_auth == VBI_RATING_AUTH_NONE) + if(prog_info->rating.auth == VBI_RATING_AUTH_NONE) AM_DEBUG("result*******************VBI_RATING_AUTH_NONE\n"); - if (prog_info->rating_dlsv == VBI_RATING_D) + if (prog_info->rating.dlsv == VBI_RATING_D) AM_DEBUG("xds**********result*******************VBI_RATING_D\n"); - if (prog_info->rating_dlsv == VBI_RATING_L) + if (prog_info->rating.dlsv == VBI_RATING_L) AM_DEBUG("xds**********result*******************VBI_RATING_L\n"); - if (prog_info->rating_dlsv == VBI_RATING_S) + if (prog_info->rating.dlsv == VBI_RATING_S) AM_DEBUG("xds**********result*******************VBI_RATING_S\n"); - if (prog_info->rating_dlsv == VBI_RATING_V) + if (prog_info->rating.dlsv == VBI_RATING_V) AM_DEBUG("xds**********result*******************VBI_RATING_V\n"); - AM_DEBUG("xds**********result*******************prog_info->rating_id = %d\n",prog_info->rating_id ); + AM_DEBUG("xds**********result*******************prog_info->rating_id = %d\n",prog_info->rating.id ); } diff --git a/src/caption.c b/src/caption.c index 90e7a67..82ca1e9 100644 --- a/src/caption.c +++ b/src/caption.c @@ -14,8 +14,8 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. */ @@ -35,6 +35,7 @@ #include "tables.h" #include "vbi.h" #include +#include "am_debug.h" #define elements(array) (sizeof(array) / sizeof(array[0])) @@ -79,8 +80,8 @@ static const char * language[8] = { "Unknown", "English", - "Español", - "Français", + "Español", + "Français", "Deutsch", "Italiano", "Other", @@ -132,7 +133,7 @@ xds_strfu(signed char *d, const uint8_t *s, int len) return neq; } -#define xds_intfu(d, val) (neq |= d ^ (val), d = (val)) +#define xds_intfu(d, val) (neq |= d ^ (val), d = (val)) static void flush_prog_info(vbi_decoder *vbi, vbi_program_info *pi, vbi_event *e) @@ -159,17 +160,19 @@ xds_decoder(vbi_decoder *vbi, int _class, int type, int neq, i; vbi_event e; - assert(length > 0 && length <= 32); + if ((length <= 0) || (length > 32)) + return; + //assert(length > 0 && length <= 32); // XXX we have no indication how long the program info applies. // It will be canceled on channel switch, but who knows // what the station transmits when the next program starts. -// (Nothing, possibly.) A timeout seems necessary. +// (Nothing, possibly.) A timeout seems necessary. switch (_class) { case XDS_CURRENT: /* 0 */ case XDS_FUTURE: /* 1 */ - if (!(vbi->event_mask & (VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO))){ + if (!(vbi->event_mask & (VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_RATING))){ XDS_SEP_DEBUG("vbi->event_mask & VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO"); return; } @@ -307,9 +310,9 @@ xds_decoder(vbi_decoder *vbi, int _class, int type, dlsv |= VBI_RATING_V; if ((buffer[0] & 0x08) == 0) { - if (r == 0) return; + //if (r == 0) return; auth = VBI_RATING_AUTH_MPAA; - pi->rating_dlsv = dlsv = 0; + pi->rating.dlsv = dlsv = 0; } else if ((buffer[0] & 0x10) == 0) { auth = VBI_RATING_AUTH_TV_US; r = g; @@ -321,24 +324,22 @@ xds_decoder(vbi_decoder *vbi, int _class, int type, if ((r = g) > 5) return; auth = VBI_RATING_AUTH_TV_CA_FR; } - pi->rating_dlsv = dlsv = 0; + pi->rating.dlsv = dlsv = 0; } else return; - if ((neq = (pi->rating_auth != auth - || pi->rating_id != r - || pi->rating_dlsv != dlsv))) { - pi->rating_auth = auth; - pi->rating_id = r; - pi->rating_dlsv = dlsv; + if ((neq = (pi->rating.auth != auth + || pi->rating.id != r + || pi->rating.dlsv != dlsv))) { + pi->rating.auth = auth; + pi->rating.id = r; + pi->rating.dlsv = dlsv; + } + if (vbi->event_mask & VBI_EVENT_RATING){ + e.type = VBI_EVENT_RATING; + e.ev.prog_info = pi; + caption_send_event(vbi, &e); } - - //***************************************zk - //callback - //e.type = VBI_EVENT_PROG_INFO; - //e.ev.prog_info = pi; - //caption_send_event(vbi, &e); - //*******************************************finish break; } @@ -490,7 +491,7 @@ xds_decoder(vbi_decoder *vbi, int _class, int type, } else if (vbi->cc.info_cycle[_class] & (1 << type)) { /* Second occurance of this type with same data */ - + e.type = VBI_EVENT_PROG_INFO; e.ev.prog_info = pi; @@ -502,6 +503,8 @@ xds_decoder(vbi_decoder *vbi, int _class, int type, break; case XDS_CHANNEL: + n->ts_id = -1; + switch (type) { case 1: /* network name */ if (xds_strfu(n->name, buffer, length)) { @@ -554,6 +557,20 @@ xds_decoder(vbi_decoder *vbi, int _class, int type, break; + case 4: /* Transmission Signal ID */ + if (length < 4) + return; + + n->ts_id = + ((buffer[3] & 15) << 0) + |((buffer[2] & 15) << 4) + |((buffer[1] & 15) << 8) + |((buffer[0] & 15) << 12); + + vbi->network.type = VBI_EVENT_NETWORK; + caption_send_event(vbi, &vbi->network); + break; + default: break; } @@ -733,70 +750,22 @@ itv_separator(vbi_decoder *vbi, struct caption *cc, char c) #define COLUMNS 34 static void -render(vbi_page *pg, int row) +render(cc_channel *ch) { vbi_event event; - if (row < 0 || pg->dirty.roll) { - /* no particular row or not fetched - since last roll/clear, redraw all */ - pg->dirty.y0 = 0; - pg->dirty.y1 = ROWS - 1; - pg->dirty.roll = 0; - } else { - pg->dirty.y0 = MIN(row, pg->dirty.y0); - pg->dirty.y1 = MAX(row, pg->dirty.y1); - } - - event.type = VBI_EVENT_CAPTION; - event.ev.caption.pgno = pg->pgno; - - caption_send_event(pg->vbi, &event); -} - -static void -clear(vbi_page *pg) -{ - vbi_event event; - - pg->dirty.y0 = 0; - pg->dirty.y1 = ROWS - 1; - pg->dirty.roll = -ROWS; - - event.type = VBI_EVENT_CAPTION; - event.ev.caption.pgno = pg->pgno; - - caption_send_event(pg->vbi, &event); -} - -static void -roll_up(vbi_page *pg, int first_row, int last_row) -{ - vbi_event event; - - if (pg->dirty.roll != 0 || pg->dirty.y0 <= pg->dirty.y1) { - /* not fetched since last update, redraw all */ - pg->dirty.roll = 0; - pg->dirty.y0 = MIN(first_row, pg->dirty.y0); - pg->dirty.y1 = MAX(last_row, pg->dirty.y1); - } else { - pg->dirty.roll = -1; - pg->dirty.y0 = first_row; - pg->dirty.y1 = last_row; - } + ch->pg[2] = ch->pg[ch->hidden ^ 1]; event.type = VBI_EVENT_CAPTION; - event.ev.caption.pgno = pg->pgno; + event.ev.caption.pgno = ch->pg[0].pgno; - caption_send_event(pg->vbi, &event); + caption_send_event(ch->pg[0].vbi, &event); } static inline void update(cc_channel *ch) { - vbi_char *acp = ch->line - ch->pg[0].text + ch->pg[1].text; - - memcpy(acp, ch->line, sizeof(*acp) * COLUMNS); + ch->update_flag = 1; } static void @@ -807,6 +776,7 @@ word_break(struct caption *cc, cc_channel *ch, int upd) /* * Add a leading and trailing space. */ +#if 0 if (ch->col > ch->col1) { vbi_char c = ch->line[ch->col1]; @@ -824,7 +794,7 @@ word_break(struct caption *cc, cc_channel *ch, int upd) ch->line[ch->col] = c; } } - +#endif if (!upd || ch->mode == MODE_POP_ON) return; @@ -835,20 +805,74 @@ word_break(struct caption *cc, cc_channel *ch, int upd) * for double buffering at word granularity. * * XXX should not render if space follows space, - * but force in long words. + * but force in long words. */ - update(ch); - render(ch->pg + 1, ch->row); +} + +static void +clear_roll(cc_channel *ch) +{ + int i; + + for (i = 0; i < ROWS; i ++) { + if ((i >= ch->row1) && (i < ch->row1 + ch->roll)) + continue; + + memset(ch->pg[ch->hidden ^ 1].text + i * COLUMNS, 0, sizeof(vbi_char) * COLUMNS); + } +} + +static void +roll_up (cc_channel *ch, int roll) +{ + vbi_char *acp = ch->pg[ch->hidden ^ 1].text; + + memmove(acp, acp + roll * COLUMNS, (ROWS - roll) * COLUMNS * sizeof(vbi_char)); + memset(acp + (ROWS - roll) * COLUMNS, 0, roll * COLUMNS * sizeof(vbi_char)); + + ch->row1 -= roll; + ch->row -= roll; + + if (ch->row1 < 0) + ch->row1 = 0; + if (ch->row < 0) + ch->row = 0; + clear_roll(ch); } static inline void set_cursor(cc_channel *ch, int col, int row) { + int p; + if (row < ch->row1) { + ch->row1 = row; + if (ch->mode == MODE_ROLL_UP) { + clear_roll(ch); + update(ch); + } + } else if (row >= ch->row1 + ch->roll) { + ch->row1 = row - ch->roll + 1; + if (ch->mode == MODE_ROLL_UP) { + clear_roll(ch); + update(ch); + } + } + ch->col = ch->col1 = col; ch->row = row; - ch->line = ch->pg[ch->hidden].text + row * COLUMNS; + p = (ch->mode == MODE_POP_ON) ? ch->hidden : (ch->hidden ^ 1); + ch->line = ch->pg[p].text + row * COLUMNS; +} + +static void +set_char(cc_channel *ch, int col, vbi_char c) +{ + if (!ch->pos_flag) + return; + + ch->line[col] = c; } static void @@ -857,22 +881,56 @@ put_char(struct caption *cc, cc_channel *ch, vbi_char c) /* c.foreground = rand() & 7; */ /* c.background = rand() & 7; */ - if (ch->col < COLUMNS - 1) - ch->line[ch->col++] = c; - else { + if (ch->col < COLUMNS - 1) { + set_char(ch, ch->col, c); + ch->col ++; + } else { /* line break here? */ - - ch->line[COLUMNS - 2] = c; + set_char(ch, COLUMNS - 2, c); } - if ((c.unicode & 0x7F) == 0x20) - word_break(cc, ch, 1); + if (ch->mode == MODE_POP_ON) + return; + + update(ch); } static inline cc_channel * -switch_channel(struct caption *cc, cc_channel *ch, int new_chan) +switch_channel(struct caption *cc, int new_chan) { - word_break(cc, ch, 1); // we leave for a number of frames + switch (new_chan) { + case 0: + case 4: + cc->curr_chan_f1_d1 = new_chan; + break; + case 1: + case 5: + cc->curr_chan_f1_d2 = new_chan; + break; + case 2: + case 6: + cc->curr_chan_f2_d1 = new_chan; + break; + case 3: + case 7: + cc->curr_chan_f2_d2 = new_chan; + break; + } + + switch (new_chan) { + case 0: + case 1: + case 4: + case 5: + cc->curr_chan_f1 = new_chan; + break; + case 2: + case 3: + case 6: + case 7: + cc->curr_chan_f2 = new_chan; + break; + } return &cc->channel[cc->curr_chan = new_chan]; } @@ -881,16 +939,8 @@ static void erase_memory(struct caption *cc, cc_channel *ch, int page) { vbi_page *pg = ch->pg + page; - vbi_char *acp = pg->text; - vbi_char c = cc->transp_space[ch >= &cc->channel[4]]; - int i; - - for (i = 0; i < COLUMNS * ROWS; acp++, i++) - *acp = c; - pg->dirty.y0 = 0; - pg->dirty.y1 = ROWS - 1; - pg->dirty.roll = ROWS; + memset(pg->text, 0, ROWS * COLUMNS * sizeof(vbi_char)); } static const vbi_color @@ -913,16 +963,26 @@ caption_command(vbi_decoder *vbi, struct caption *cc, { XDS_SEP_DEBUG("caption_command\n"); cc_channel *ch; + vbi_char *c; int chan, col, i; int last_row; + //chan = (cc->curr_chan & 4) + field2 * 2 + ((c1 >> 3) & 1); + + if (field2) { + chan = (c1 & 8) ? cc->curr_chan_f2_d2 : cc->curr_chan_f2_d1; + } else { + chan = (c1 & 8) ? cc->curr_chan_f1_d2 : cc->curr_chan_f1_d1; + } + + switch_channel(cc, chan); - chan = (cc->curr_chan & 4) + field2 * 2 + ((c1 >> 3) & 1); ch = &cc->channel[chan]; c1 &= 7; if (c2 >= 0x40) { /* Preamble Address Codes 001 crrr 1ri xxxu */ int row = row_mapping[(c1 << 1) + ((c2 >> 5) & 1)]; + int old_row = ch->row; if (row < 0 || !ch->mode) return; @@ -936,21 +996,22 @@ caption_command(vbi_decoder *vbi, struct caption *cc, if (ch->mode == MODE_ROLL_UP) { int row1 = row - ch->roll + 1; + int roll; if (row1 < 0) row1 = 0; - if (row1 != ch->row1) { - ch->row1 = row1; - erase_memory(cc, ch, ch->hidden); - erase_memory(cc, ch, ch->hidden ^ 1); + roll = ch->row1 - row1; + if (roll > 0) { + roll_up(ch, ch->row1 - row1); + update(ch); } + } - set_cursor(ch, 1, ch->row1 + ch->roll - 1); - } else - set_cursor(ch, 1, row); + set_cursor(ch, 1, row); if (c2 & 0x10) { + /* col = ch->col; for (i = (c2 & 14) * 2; i > 0 && col < COLUMNS - 1; i--) @@ -958,9 +1019,14 @@ caption_command(vbi_decoder *vbi, struct caption *cc, if (col > ch->col) ch->col = ch->col1 = col; + */ - ch->attr.italic = FALSE; - ch->attr.foreground = VBI_WHITE; + ch->col = (c2 & 14) * 2 + 1; + + if (old_row != ch->row) { + ch->attr.italic = FALSE; + ch->attr.foreground = VBI_WHITE; + } } else { // not verified c2 = (c2 >> 1) & 7; @@ -978,10 +1044,24 @@ caption_command(vbi_decoder *vbi, struct caption *cc, } switch (c1) { - case 0: /* Optional Attributes 001 c000 010 xxxt */ -// not verified + case 0: + /* Optional Attributes 001 c000 010 xxxt */ ch->attr.opacity = (c2 & 1) ? VBI_SEMI_TRANSPARENT : VBI_OPAQUE; ch->attr.background = palette_mapping[(c2 >> 1) & 7]; + + for (i = 0; i < 2; i ++) { + int col = ch->col - i; + + if ((col > 0) && (col < COLUMNS - 1)) { + vbi_char c = ch->line[col]; + + if (c.unicode == 0x20) { + c.background = ch->attr.background; + c.opacity = ch->attr.opacity; + set_char(ch, col, c); + } + } + } return; case 1: @@ -991,11 +1071,12 @@ caption_command(vbi_decoder *vbi, struct caption *cc, if (c2 == 9) { // "transparent space" if (ch->col < COLUMNS - 1) { - ch->line[ch->col++] = cc->transp_space[chan >> 2]; + set_char(ch, ch->col++, cc->transp_space[chan >> 2]); ch->col1 = ch->col; - } else - ch->line[COLUMNS - 2] = cc->transp_space[chan >> 2]; - // XXX boxed logic? + } else { + set_char(ch, COLUMNS - 2, cc->transp_space[chan >> 2]); + } + } else { vbi_char c = ch->attr; @@ -1006,6 +1087,14 @@ caption_command(vbi_decoder *vbi, struct caption *cc, } } else { /* Midrow Codes 001 c001 010 xxxu */ // not verified + vbi_char c = ch->attr; + + c.flash = FALSE; + c.underline = FALSE; + c.unicode = ' '; + + put_char(cc, ch, c); + ch->attr.flash = FALSE; ch->attr.underline = c2 & 1; @@ -1016,7 +1105,6 @@ caption_command(vbi_decoder *vbi, struct caption *cc, ch->attr.foreground = palette_mapping[c2]; } else { ch->attr.italic = TRUE; - ch->attr.foreground = VBI_WHITE; } } @@ -1025,6 +1113,19 @@ caption_command(vbi_decoder *vbi, struct caption *cc, case 2: /* Optional Extended Characters 001 c01f 01x xxxx */ case 3: /* Send specs to the maintainer of this code */ + { + vbi_char c; + + c = ch->attr; + c.unicode = vbi_caption_unicode((c1 << 8) | c2 | 0x1000, 0); + + if (c.unicode) { + if (ch->col > 1) + ch->col --; + + put_char(cc, ch, c); + } + } return; case 4: /* Misc Control Codes 001 c10f 010 xxxx */ @@ -1033,12 +1134,9 @@ caption_command(vbi_decoder *vbi, struct caption *cc, switch (c2 & 15) { case 0: /* Resume Caption Loading 001 c10f 010 0000 */ - ch = switch_channel(cc, ch, chan & 3); - + ch = switch_channel(cc, chan & 3); + ch->pos_flag = 1; ch->mode = MODE_POP_ON; - -// no? erase_memory(cc, ch); - return; /* case 4: reserved */ @@ -1049,51 +1147,56 @@ caption_command(vbi_decoder *vbi, struct caption *cc, { int roll = (c2 & 7) - 3; - ch = switch_channel(cc, ch, chan & 3); + ch = switch_channel(cc, chan & 3); + ch->pos_flag = 1; if (ch->mode == MODE_ROLL_UP && ch->roll == roll) return; - erase_memory(cc, ch, ch->hidden); - erase_memory(cc, ch, ch->hidden ^ 1); - ch->mode = MODE_ROLL_UP; ch->roll = roll; - set_cursor(ch, 1, 14); - - ch->row1 = 14 - roll + 1; - + ch->row1 = ch->row - roll + 1; + if (ch->row1 < 0) + ch->row1 = 0; return; } case 9: /* Resume Direct Captioning 001 c10f 010 1001 */ // not verified - ch = switch_channel(cc, ch, chan & 3); + ch = switch_channel(cc, chan & 3); ch->mode = MODE_PAINT_ON; + ch->pos_flag = 1; return; case 10: /* Text Restart 001 c10f 010 1010 */ // not verified - ch = switch_channel(cc, ch, chan | 4); + erase_memory(cc, ch, ch->hidden); + erase_memory(cc, ch, ch->hidden ^ 1); + ch = switch_channel(cc, chan | 4); + ch->pos_flag = 1; set_cursor(ch, 1, 0); + erase_memory(cc, ch, ch->hidden); + erase_memory(cc, ch, ch->hidden ^ 1); return; case 11: /* Resume Text Display 001 c10f 010 1011 */ - ch = switch_channel(cc, ch, chan | 4); + ch = switch_channel(cc, chan | 4); + ch->pos_flag = 1; return; case 15: /* End Of Caption 001 c10f 010 1111 */ - ch = switch_channel(cc, ch, chan & 3); + ch = switch_channel(cc, chan & 3); + ch->pos_flag = 1; + ch->mode = MODE_POP_ON; word_break(cc, ch, 1); ch->hidden ^= 1; - render(ch->pg + (ch->hidden ^ 1), -1 /* ! */); - - erase_memory(cc, ch, ch->hidden); // yes? + //erase_memory(cc, ch, ch->hidden); // yes? + update(ch); /* * A Preamble Address Code should follow, @@ -1112,11 +1215,38 @@ caption_command(vbi_decoder *vbi, struct caption *cc, case 1: /* Backspace 001 c10f 010 0001 */ // not verified - if (ch->mode && ch->col > 1) { - ch->line[--ch->col] = cc->transp_space[chan >> 2]; + if (ch->mode) { + if (ch->col > 1) { + if (ch->line[ch->col - 1].unicode == 0) + break; + ch->col --; + } else if (ch->row > 0) { + vbi_char *acp; + int p = (ch->mode == MODE_POP_ON) ? ch->hidden : (ch->hidden ^ 1); + + ch->row --; + ch->line = ch->pg[p].text + ch->row * COLUMNS; + ch->col1 = 1; + ch->col = COLUMNS - 1; + acp = ch->line + COLUMNS - 1; + + while (ch->col > 1) { + if (acp->unicode != 0) + break; + + acp --; + ch->col --; + } + } else { + break; + } + + memset(&ch->line[ch->col], 0, sizeof(vbi_char)); if (ch->col < ch->col1) ch->col1 = ch->col; + + update(ch); } return; @@ -1125,37 +1255,27 @@ caption_command(vbi_decoder *vbi, struct caption *cc, if (ch == cc->channel + 5) itv_separator(vbi, cc, 0); - if (!ch->mode) - return; - - last_row = ch->row1 + ch->roll - 1; - - if (last_row > ROWS - 1) - last_row = ROWS - 1; - - if (ch->row < last_row) { - word_break(cc, ch, 1); - set_cursor(ch, 1, ch->row + 1); - } else { - vbi_char *acp = &ch->pg[ch->hidden ^ (ch->mode != MODE_POP_ON)] - .text[ch->row1 * COLUMNS]; - - word_break(cc, ch, 1); + if ((ch->row >= ROWS - 1) && ((ch->mode == MODE_ROLL_UP) || (ch->mode == MODE_TEXT))) { + roll_up(ch, 1); + set_cursor(ch, 1, ROWS - 1); update(ch); - - memmove(acp, acp + COLUMNS, sizeof(*acp) * (ch->roll - 1) * COLUMNS); - - for (i = 0; i <= COLUMNS; i++) - ch->line[i] = cc->transp_space[chan >> 2]; - - if (ch->mode != MODE_POP_ON) { + } else if (ch->row >= ROWS - 1) { + set_cursor(ch, 1, ROWS - 1); + if (ch->mode != MODE_POP_ON) + update(ch); + } else { + set_cursor(ch, 1, ch->row + 1); + if (ch->mode != MODE_POP_ON) update(ch); - roll_up(ch->pg + (ch->hidden ^ 1), ch->row1, last_row); - } - - ch->col1 = ch->col = 1; } + ch->attr.underline = FALSE; + ch->attr.background = VBI_BLACK; + ch->attr.opacity = VBI_OPAQUE; + ch->attr.flash = FALSE; + ch->attr.italic = FALSE; + ch->attr.foreground = VBI_WHITE; + return; case 4: /* Delete To End Of Row 001 c10f 010 0100 */ @@ -1163,33 +1283,26 @@ caption_command(vbi_decoder *vbi, struct caption *cc, if (!ch->mode) return; - for (i = ch->col; i <= COLUMNS - 1; i++) - ch->line[i] = cc->transp_space[chan >> 2]; + for (i = ch->col; i <= COLUMNS - 1; i++) { + memset(&ch->line[i], 0, sizeof(vbi_char)); + } word_break(cc, ch, 0); if (ch->mode != MODE_POP_ON) { update(ch); - render(ch->pg + (ch->hidden ^ 1), ch->row); } return; case 12: /* Erase Displayed Memory 001 c10f 010 1100 */ -// s1, s4: EDM always before EOC - if (ch->mode != MODE_POP_ON) - erase_memory(cc, ch, ch->hidden); - - erase_memory(cc, ch, ch->hidden ^ 1); - clear(ch->pg + (ch->hidden ^ 1)); - + if (chan < 4) { + erase_memory(cc, ch, ch->hidden ^ 1); + update(ch); + } return; - case 14: /* Erase Non-Displayed Memory 001 c10f 010 1110 */ -// not verified - if (ch->mode == MODE_POP_ON) - erase_memory(cc, ch, ch->hidden); - + erase_memory(cc, ch, ch->hidden); return; } @@ -1204,13 +1317,18 @@ caption_command(vbi_decoder *vbi, struct caption *cc, switch (c2) { case 0x21 ... 0x23: /* Misc Control Codes, Tabs 001 c111 010 00xx */ // not verified + /* col = ch->col; for (i = c2 & 3; i > 0 && col < COLUMNS - 1; i--) ch->line[col++] = cc->transp_space[chan >> 2]; if (col > ch->col) - ch->col = ch->col1 = col; + ch->col = ch->col1 = col;*/ + + ch->col += (c2 & 3); + if (ch->col >= COLUMNS) + ch->col = COLUMNS - 1; return; @@ -1241,12 +1359,29 @@ caption_command(vbi_decoder *vbi, struct caption *cc, } } +static void +update_display (vbi_decoder *vbi) +{ + struct caption *cc = &vbi->cc; + int i; + + for (i=0; i<8; i++) + { + cc_channel *ch = &cc->channel[i]; + + if (ch->update_flag == 1) { + render(ch); + ch->update_flag = 0; + } + } +} + /** * @internal * @param vbi Initialized vbi decoding context. * @param line ITU-R line number this data originated from. * @param buf Two bytes. - * + * * Decode two bytes of Closed Caption data (Caption, XDS, ITV), * updating the decoder state accordingly. May send events. */ @@ -1257,8 +1392,19 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) struct caption *cc = &vbi->cc; char c1 = buf[0] & 0x7F; int field2 = 1, i; + int flash; + struct timespec now; pthread_mutex_lock(&cc->mutex); + //AM_DEBUG(1, "vbi_data: line: %d %x %x", line, buf[0], buf[1]); + + if (line == 21) { + cc->curr_chan = cc->curr_chan_f1; + memcpy(cc->last, cc->last_f1, sizeof(cc->last)); + } else { + cc->curr_chan = cc->curr_chan_f2; + memcpy(cc->last, cc->last_f2, sizeof(cc->last)); + } switch (line) { case 21: /* NTSC */ @@ -1284,6 +1430,13 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) cc->xds = (c1 != XDS_END); goto finish; } else if (c1 <= 0x1F) { + xds_sub_packet *sp = cc->curr_sp; + unsigned int class, type; + if (sp) { + class = (sp - cc->sub_packet[0]) / elements(cc->sub_packet[0]); + type = (sp - cc->sub_packet[0]) % elements(cc->sub_packet[0]); + xds_decoder(vbi, class, type, sp->buffer, sp->count - 2); + } cc->xds = FALSE; } else if (cc->xds) { xds_separator(vbi, buf); @@ -1293,7 +1446,7 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) xds_separator(vbi, buf); goto finish; } - + break; default: @@ -1302,9 +1455,13 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) //if ( (buf[0]) < 0) { //vbi_unpar8 (buf[0]) < 0 if ( vbi_unpar8 (buf[0]) < 0) { // +#if 0 c1 = 127; buf[0] = c1; /* traditional 'bad' glyph, ccfont has */ buf[1] = c1; /* room, design a special glyph? */ +#else + goto finish; +#endif } CC_DUMP( @@ -1318,15 +1475,15 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) vbi_char c; case 0x01 ... 0x0F: - if (!field2) + /*if (!field2)*/ cc->last[0] = 0; break; /* XDS field 1?? */ case 0x10 ... 0x1F: //if ( (buf[1]) >= 0) { // vbi_unpar8 (buf[1]) - if ( vbi_unpar8 (buf[1]) >= 0) { - if (!field2 - && buf[0] == cc->last[0] + if ( vbi_unpar8 (buf[1]) >= 0) { + if (/*!field2 + &&*/ buf[0] == cc->last[0] && buf[1] == cc->last[1]) { /* cmd repetition F1: already executed */ cc->last[0] = 0; /* one rep */ @@ -1335,11 +1492,11 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) caption_command(vbi, cc, c1, buf[1] & 0x7F, field2); - if (!field2) { + /*if (!field2)*/ { cc->last[0] = buf[0]; cc->last[1] = buf[1]; } - } else if (!field2) + } else /*if (!field2)*/ cc->last[0] = 0; break; @@ -1350,6 +1507,10 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) putchar(_vbi_to_ascii (buf[1])); fflush(stdout); ) +#if 0 + AM_DEBUG(1, "text value: %c %c %x %x", _vbi_to_ascii(buf[0]), + _vbi_to_ascii(buf[1]), buf[0], buf[1]); +#endif ch = &cc->channel[(cc->curr_chan & 5) + field2 * 2]; @@ -1363,7 +1524,7 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) break; } - if (!field2) + /*if (!field2)*/ cc->last[0] = 0; ch->nul_ct = 0; @@ -1377,7 +1538,7 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) for (i = 0; i < 2; i++) { //char ci = (buf[i]) & 0x7F; /* 127 if bad */ //vbi_unpar8 (buf[i]) - char ci = vbi_unpar8 (buf[i]) & 0x7F; /* 127 if bad */ + char ci = vbi_unpar8 (buf[i]) & 0x7F; /* 127 if bad */ if (ci <= 0x1F) /* 0x00 no char, 0x01 ... 0x1F invalid */ continue; @@ -1390,14 +1551,51 @@ vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf) } } + clock_gettime(CLOCK_REALTIME, &now); + + flash = (now.tv_nsec / 250000000) & 1; + + if (flash != vbi->cc.flash_state) { + int i, j; + + vbi->cc.flash_state = flash; + + for (i = 0; i < 8; i++) + { + cc_channel *ch; + vbi_page *spg; + + ch = &vbi->cc.channel[i]; + spg = ch->pg + 2; + for (j = 0; j < ROWS * COLUMNS; j++) + { + if (spg->text[j].flash) + { + update(ch); + break; + } + } + } + } + + update_display(vbi); + + if (line == 21) { + //cc->curr_chan_f1 = cc->curr_chan; + memcpy(cc->last_f1, cc->last, sizeof(cc->last)); + } else { + //cc->curr_chan_f2 = cc->curr_chan; + memcpy(cc->last_f2, cc->last, sizeof(cc->last)); + } + finish: pthread_mutex_unlock(&cc->mutex); } /** * @internal - * @param vbi Initialized vbi decoding context. - * + * @param vbi Initialized vbi decoding context. + * * This function must be called after desynchronisation * has been detected (i. e. vbi data has been lost) * to reset the Closed Caption decoder. @@ -1422,7 +1620,7 @@ vbi_caption_desync(vbi_decoder *vbi) /** * @internal * @param vbi Initialized vbi decoding context. - * + * * This function must be called after a channel switch, * to reset the Closed Caption decoder. */ @@ -1439,8 +1637,8 @@ vbi_caption_channel_switched(vbi_decoder *vbi) if (i < 4) { ch->mode = MODE_NONE; // MODE_ROLL_UP; ch->row = ROWS - 1; - ch->row1 = ROWS - 3; - ch->roll = 3; + ch->row1 = ROWS - 4; + ch->roll = 4; } else { ch->mode = MODE_TEXT; ch->row1 = ch->row = 0; @@ -1479,7 +1677,7 @@ vbi_caption_channel_switched(vbi_decoder *vbi) static vbi_rgba default_color_map[8] = { VBI_RGBA(0x00, 0x00, 0x00), VBI_RGBA(0xFF, 0x00, 0x00), - VBI_RGBA(0x00, 0xFF, 0x00), VBI_RGBA(0xFF, 0xFF, 0x00), + VBI_RGBA(0x00, 0xFF, 0x00), VBI_RGBA(0xFF, 0xFF, 0x00), VBI_RGBA(0x00, 0x00, 0xFF), VBI_RGBA(0xFF, 0x00, 0xFF), VBI_RGBA(0x00, 0xFF, 0xFF), VBI_RGBA(0xFF, 0xFF, 0xFF) }; @@ -1487,7 +1685,7 @@ default_color_map[8] = { /** * @internal * @param vbi Initialized vbi decoding context. - * + * * After the client changed text brightness and saturation * this function adjusts the Closed Caption color palette. */ @@ -1508,7 +1706,7 @@ vbi_caption_color_level(vbi_decoder *vbi) /** * @internal * @param vbi VBI decoding context. - * + * * This function is called during @a vbi destruction * to destroy Closed Caption subset of @a vbi. */ @@ -1521,7 +1719,7 @@ vbi_caption_destroy(vbi_decoder *vbi) /** * @internal * @param vbi VBI decoding context. - * + * * This function is called during @a vbi initialization * to initialize the Closed Caption subset of @a vbi. */ @@ -1534,6 +1732,13 @@ vbi_caption_init(vbi_decoder *vbi) memset(cc, 0, sizeof(struct caption)); + cc->curr_chan_f1 = 0; + cc->curr_chan_f2 = 2; + cc->curr_chan_f1_d1 = 0; + cc->curr_chan_f1_d2 = 1; + cc->curr_chan_f2_d1 = 2; + cc->curr_chan_f2_d2 = 3; + pthread_mutex_init(&cc->mutex, NULL); for (i = 0; i < 9; i++) { @@ -1554,6 +1759,7 @@ vbi_caption_init(vbi_decoder *vbi) ch->pg[0].font[1] = vbi_font_descriptors; memcpy(&ch->pg[1], &ch->pg[0], sizeof(ch->pg[1])); + memcpy(&ch->pg[2], &ch->pg[0], sizeof(ch->pg[2])); } for (i = 0; i < 2; i++) { @@ -1577,7 +1783,7 @@ vbi_caption_init(vbi_decoder *vbi) * @param reset @c TRUE resets the vbi_page dirty fields in cache after * fetching. Pass @c FALSE only if you plan to call this function again * to update other displays. - * + * * Fetches a Closed Caption page designated by @a pgno from the cache, * formats and stores it in @a pg. CC pages are transmitted basically in * two modes: at once and character by character ("roll-up" mode). @@ -1587,11 +1793,11 @@ vbi_caption_init(vbi_decoder *vbi) * (in case of "roll-up" mode that is with each new word received) * and the vbi_page->dirty fields will mark the lines actually in * need of updates, to speed up rendering. - * + * * Although safe to do, this function is not supposed to be * called from an event handler, since rendering may block decoding * for extended periods of time. - * + * * @return * @c FALSE if some error occured. */ @@ -1608,7 +1814,7 @@ vbi_fetch_cc_page(vbi_decoder *vbi, vbi_page *pg, vbi_pgno pgno, vbi_bool reset) pthread_mutex_lock(&vbi->cc.mutex); - spg = ch->pg + (ch->hidden ^ 1); + spg = ch->pg + 2; memcpy(pg, spg, sizeof(*pg)); /* shortcut? */ @@ -1621,6 +1827,44 @@ vbi_fetch_cc_page(vbi_decoder *vbi, vbi_page *pg, vbi_pgno pgno, vbi_bool reset) return 1; } +void vbi_refresh_cc(vbi_decoder *vbi) +{ + cc_channel *ch; + vbi_page *spg; + int i, j; + vbi_event event; + struct timespec now; + int flash; + + clock_gettime(CLOCK_REALTIME, &now); + + flash = (now.tv_nsec / 250000000) & 1; + + pthread_mutex_lock(&vbi->cc.mutex); + + if (flash != vbi->cc.flash_state) { + vbi->cc.flash_state = flash; + + for (i = 0; i < 8; i++) + { + ch = &vbi->cc.channel[i]; + spg = ch->pg + 2; + for (j = 0; j < ROWS * COLUMNS; j++) + { + if (spg->text[j].flash) + { + update(ch); + break; + } + } + } + + update_display(vbi); + } + + pthread_mutex_unlock(&vbi->cc.mutex); +} + /* Local variables: c-set-style: K&R diff --git a/src/cc.h b/src/cc.h index 2267b16..03dd8a3 100644 --- a/src/cc.h +++ b/src/cc.h @@ -54,6 +54,8 @@ typedef struct { int col, col1; int row, row1; int roll; + int update_flag; + int pos_flag; int nul_ct; // XXX should be 'silence count' double time; @@ -63,15 +65,25 @@ typedef struct { vbi_char * line; int hidden; - vbi_page pg[2]; + vbi_page pg[3]; + int update; } cc_channel; struct caption { pthread_mutex_t mutex; uint8_t last[2]; /* field 1, cc command repetition */ + uint8_t last_f1[2]; + uint8_t last_f2[2]; int curr_chan; + int curr_chan_f1; + int curr_chan_f2; + int curr_chan_f1_d1; + int curr_chan_f1_d2; + int curr_chan_f2_d1; + int curr_chan_f2_d2; + vbi_char transp_space[2]; /* caption, text mode */ cc_channel channel[9]; /* caption 1-4, text 1-4, garbage */ @@ -83,6 +95,7 @@ struct caption { int itv_count; int info_cycle[2]; + int flash_state; }; /* Public */ diff --git a/src/dtvcc.c b/src/dtvcc.c index bb12255..33c65b8 100644 --- a/src/dtvcc.c +++ b/src/dtvcc.c @@ -33,7 +33,7 @@ #include "libzvbi.h" #include "dtvcc.h" -#include +#include "am_debug.h" #define elements(array) (sizeof(array) / sizeof(array[0])) @@ -51,7 +51,6 @@ #define N_ELEMENTS(array) (sizeof (array) / sizeof ((array)[0])) #define CLEAR(var) memset (&(var), 0, sizeof (var)) - /* FIXME __typeof__ is a GCC extension. */ #undef SWAP #define SWAP(x, y) \ @@ -84,6 +83,10 @@ do { \ _member)) : (_type *) 0; \ }) +#define VBI_RGBA(r, g, b) \ + ((((r) & 0xFF) << 0) | (((g) & 0xFF) << 8) \ + | (((b) & 0xFF) << 16) | (0xFF << 24)) + /* These should be defined in inttypes.h. */ #ifndef PRId64 # define PRId64 "lld" @@ -99,8 +102,8 @@ extern void vbi_transp_colormap(vbi_decoder *vbi, vbi_rgba *d, vbi_rgba *s, int entries); extern void vbi_send_event(vbi_decoder *vbi, vbi_event *ev); -extern void -vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf); +static void +dtvcc_get_visible_windows(struct dtvcc_service *ds, int *cnt, struct dtvcc_window **windows); /* EIA 608-B decoder. */ @@ -2068,7 +2071,7 @@ static int webtv_check(struct caption_recorder *cr, char * buf,int len) } sprintf(temp,"%04X\n",(int)~sum&0xffff); buf++; - if(!strncmp(buf,temp,4)) + if (!strncmp(buf,temp,4)) { buf[5]=0; if (cr->cur_ch[cr->field] >= 0 && cr->cc_fp[cr->cur_ch[cr->field]]) { @@ -2143,6 +2146,7 @@ dtvcc_g2 [96] = { 0x2019, /* 0x1032 Right single quotation mark */ 0x201C, /* 0x1033 Left double quotation mark */ 0x201D, /* 0x1034 Right double quotation mark */ + 0x2022, 0, 0, 0, @@ -2228,7 +2232,7 @@ dtvcc_map_color(dtvcc_color c) return ret; } -static unsigned int +unsigned int dtvcc_unicode (unsigned int c) { if (unlikely (0 == (c & 0x60))) { @@ -2236,7 +2240,9 @@ dtvcc_unicode (unsigned int c) return 0; } else if (likely (c < 0x100)) { /* G0, G1 */ - if (unlikely (0x7F == c)) + if (unlikely (0xAD == c)) + return 0x2D; + else if (unlikely (0x7F == c)) return 0x266A; /* music note */ else return c; @@ -2249,7 +2255,7 @@ dtvcc_unicode (unsigned int c) /* We map all G2/G3 characters which are not representable in Unicode to private code U+E900 ... U+E9FF. */ - return 0xE9A0; /* caption icon */ + return 0xf101; /* caption icon */ } return 0; @@ -2258,8 +2264,68 @@ dtvcc_unicode (unsigned int c) static void dtvcc_render(struct dtvcc_decoder * dc, struct dtvcc_service * ds) { +#if 0 vbi_event event; struct tvcc_decoder *td = PARENT(dc, struct tvcc_decoder, dtvcc); + struct dtvcc_window *win[8]; + int i, cnt; + + //printf("render check\n"); + + cnt = 8; + dtvcc_get_visible_windows(ds, &cnt, win); + //if (!cnt) + // return; + + if (cnt != ds->old_win_cnt) { + //printf("cnt changed\n"); + goto changed; + } + + for (i = 0; i < cnt; i ++) { + struct dtvcc_window *w1 = win[i]; + struct dtvcc_window *w2 = &ds->old_window[i]; + + if (memcmp(w1->buffer, w2->buffer, sizeof(w1->buffer))) { + //printf("text changed\n"); + goto changed; + } + + if (memcmp(&w1->style, &w2->style, sizeof(w1->style))) { + //printf("style changed\n"); + goto changed; + } + + if (memcmp(&w1->curr_pen, &w2->curr_pen, sizeof(w1->curr_pen))) { + //printf("pen changed\n"); + goto changed; + } + + if (w1->row_count != w2->row_count) { + //printf("row changed\n"); + goto changed; + } + + if (w1->column_count != w2->column_count) { + //printf("col changed\n"); + goto changed; + } + + if (w1->visible != w2->visible) { + //printf("vis changed\n"); + goto changed; + } + } + + return; +changed: + for (i = 0; i < cnt; i ++) { + ds->old_window[i] = *win[i]; + } + ds->old_win_cnt = cnt; +#endif + ds->update = 1; +#if 0 event.type = VBI_EVENT_CAPTION; event.ev.caption.pgno = ds - dc->service + 1 + 8/*after 8 cc channels*/; @@ -2270,6 +2336,7 @@ dtvcc_render(struct dtvcc_decoder * dc, struct dtvcc_service * ds) vbi_send_event(td->vbi, &event); pthread_mutex_lock(&td->mutex); +#endif } static void @@ -2306,9 +2373,9 @@ dtvcc_caption_window (struct dtvcc_service * ds) continue; if (!ds->window[window_id].visible) continue; - if (DIR_BOTTOM_TOP + /*if (DIR_BOTTOM_TOP != ds->window[window_id].style.scroll_direction) - continue; + continue; */ if (ds->window[window_id].priority < max_priority) { dw = &ds->window[window_id]; max_priority = ds->window[window_id].priority; @@ -2382,62 +2449,124 @@ dtvcc_put_char (struct dtvcc_decoder * dc, { struct dtvcc_window *dw; unsigned int row; - unsigned int column; + unsigned int column,i; dc = dc; /* unused */ dw = ds->curr_window; + + //printf("putchar %c\n", c); + if (NULL == dw) { ds->error_line = __LINE__; + //AM_DEBUG(1, "================ window null !!!!!"); return FALSE; } - row = dw->curr_row; column = dw->curr_column; + row = dw->curr_row; /* FIXME how should we handle TEXT_TAG_NOT_DISPLAYABLE? */ + /* Add row column lock support */ + switch (dw->style.print_direction) { + case DIR_LEFT_RIGHT: + if (column >= dw->column_count) + { + if (dw->column_lock == 1) + { + if (dw->row_lock == 0) + { + column = 0; + row++; + if (row >= dw->row_count) + { + // row moves up + } + } + else + { + return TRUE; + } + } + else + { + if (column < 32) + { + if (dw->column_no_lock_length < column + 1) + dw->column_no_lock_length = column + 1; + } + else + { + if (dw->row_lock == 1) + return TRUE; + else + { + column = 0; + row++; + if (row >= dw->row_count) + { + // row moves up + } + } + } + } + } + break; + case DIR_RIGHT_LEFT: + case DIR_TOP_BOTTOM: + case DIR_BOTTOM_TOP: + break; + } + dw->buffer[row][column] = c; + dw->pen[row][column] = dw->curr_pen.style; + if (c == 0x1020 || c == 0x1021) + { + if (c == 0x1020) + dw->buffer[row][column] = 0x20; + else + dw->buffer[row][column] = 0xA0; + dw->pen[row][column].bg_opacity = OPACITY_TRANSPARENT; + dw->pen[row][column].fg_opacity = OPACITY_TRANSPARENT; + } + //AM_DEBUG(1, "========= putchar %x %c", c, c); + if (dw->visible) + dtvcc_render(dc, ds); switch (dw->style.print_direction) { case DIR_LEFT_RIGHT: dw->streamed &= ~(1 << row); if (!cc_timestamp_isset (&dw->timestamp_c0)) dw->timestamp_c0 = ds->timestamp; - if (++column >= dw->column_count) - return TRUE; + ++column; break; case DIR_RIGHT_LEFT: dw->streamed &= ~(1 << row); if (!cc_timestamp_isset (&dw->timestamp_c0)) dw->timestamp_c0 = ds->timestamp; - if (column-- <= 0) - return TRUE; + column--; break; case DIR_TOP_BOTTOM: dw->streamed &= ~(1 << column); if (!cc_timestamp_isset (&dw->timestamp_c0)) dw->timestamp_c0 = ds->timestamp; - if (++row >= dw->row_count) - return TRUE; + ++row; break; case DIR_BOTTOM_TOP: dw->streamed &= ~(1 << column); if (!cc_timestamp_isset (&dw->timestamp_c0)) dw->timestamp_c0 = ds->timestamp; - if (row-- <= 0) - return TRUE; + row--; break; } dw->curr_row = row; dw->curr_column = column; - dtvcc_render(dc, ds); - return TRUE; } @@ -2509,9 +2638,11 @@ dtvcc_set_pen_color (struct dtvcc_service * ds, c = buf[1]; dw->curr_pen.style.fg_opacity = c >> 6; dw->curr_pen.style.fg_color = c & 0x3F; + dw->curr_pen.style.fg_flash = ((c>>6)==1)?1:0; c = buf[2]; dw->curr_pen.style.bg_opacity = c >> 6; dw->curr_pen.style.bg_color = c & 0x3F; + dw->curr_pen.style.bg_flash = ((c>>6)==1)?1:0; return TRUE; } @@ -2535,10 +2666,12 @@ dtvcc_set_pen_attributes (struct dtvcc_service * ds, c = buf[1]; offset = (c >> 2) & 3; pen_size = c & 3; + //TODO: why not larger than 3 + /* if ((offset | pen_size) >= 3) { ds->error_line = __LINE__; return FALSE; - } + } */ c = buf[2]; edge_type = (c >> 3) & 7; @@ -2586,6 +2719,7 @@ dtvcc_set_window_attributes (struct dtvcc_service * ds, c = buf[1]; dw->style.fill_opacity = c >> 6; dw->style.fill_color = c & 0x3F; + dw->style.window_flash = ((c>>6)==1)?1:0; c = buf[2]; dw->style.border_type = border_type; dw->style.border_color = c & 0x3F; @@ -2622,8 +2756,16 @@ dtvcc_clear_windows (struct dtvcc_decoder * dc, dtvcc_stream_event (dc, ds, dw, dw->curr_row); memset (dw->buffer, 0, sizeof (dw->buffer)); + memset (dw->pen, 0, sizeof(dw->pen)); + + dw->curr_column = 0; + dw->curr_row = 0; dw->streamed = 0; + dw->style.display_effect = 0; + dw->effect_status = 0; + if (dw->visible) + dtvcc_render(dc, ds); /* FIXME CEA 708-C Section 7.1.4 (Form Feed) and 8.10.5.3 confuse me. */ @@ -2715,6 +2857,8 @@ dtvcc_define_window (struct dtvcc_decoder * dc, unsigned int pen_style_id; unsigned int c; + //printf("define window\n"); + if (0 != ((buf[1] | buf[6]) & 0xC0)) { ds->error_line = __LINE__; return FALSE; @@ -2745,12 +2889,15 @@ dtvcc_define_window (struct dtvcc_decoder * dc, return FALSE; } + //printf("define window %d\n", column_count_m1); + column_count_m1 = buf[5]; /* We also check the top two zero bits. */ - if (unlikely (column_count_m1 >= 41)) { + if (unlikely (column_count_m1 >= 42)) { ds->error_line = __LINE__; return FALSE; } + //printf("define windowa\n"); window_id = buf[0] & 7; dw = &ds->window[window_id]; @@ -2761,7 +2908,7 @@ dtvcc_define_window (struct dtvcc_decoder * dc, c = buf[1]; dw->visible = (c >> 5) & 1; dw->row_lock = (c >> 4) & 1; - dw->column_lock = (c >> 4) & 1; + dw->column_lock = (c >> 3) & 1; dw->priority = c & 7; dw->anchor_relative = anchor_relative; @@ -2772,13 +2919,14 @@ dtvcc_define_window (struct dtvcc_decoder * dc, c = buf[4]; dw->row_count = (c & 15) + 1; dw->column_count = column_count_m1 + 1; + dw->column_no_lock_length = 0; c = buf[6]; window_style_id = (c >> 3) & 7; pen_style_id = c & 7; if (window_style_id > 0) { - dw->style = window_styles[window_style_id]; + dw->style = window_styles[window_style_id-1]; } else if (0 == (ds->created & window_map)) { dw->style = window_styles[1]; } @@ -2804,6 +2952,8 @@ dtvcc_define_window (struct dtvcc_decoder * dc, ds->created |= window_map; + //printf("define %x %x\n", ds->curr_window, ds->created); + return dtvcc_clear_windows (dc, ds, window_map); } @@ -2817,6 +2967,12 @@ dtvcc_display_windows (struct dtvcc_decoder * dc, window_map &= ds->created; + //printf("display %02x %p %02x\n", c, ds->curr_window, ds->created); + if (ds->curr_window == NULL && ds->created == 0) { + return FALSE; + //return TRUE; + } + for (i = 0; i < 8; ++i) { struct dtvcc_window *dw; vbi_bool was_visible; @@ -2830,17 +2986,23 @@ dtvcc_display_windows (struct dtvcc_decoder * dc, switch (c) { case 0x89: /* DSW DisplayWindows */ dw->visible = TRUE; + dw->effect_status = CC_EFFECT_DISPLAY; break; case 0x8A: /* HDW HideWindows */ dw->visible = FALSE; + dw->effect_status = CC_EFFECT_HIDE; break; case 0x8B: /* TGW ToggleWindows */ dw->visible = was_visible ^ TRUE; + dw->effect_status = + (dw->visible == TRUE)?CC_EFFECT_DISPLAY:CC_EFFECT_HIDE; break; } + clock_gettime(CLOCK_REALTIME, &dw->effect_timer); + if (!was_visible) { unsigned int row; @@ -2851,6 +3013,8 @@ dtvcc_display_windows (struct dtvcc_decoder * dc, } } + dtvcc_render(dc, ds); + return TRUE; } @@ -2875,10 +3039,21 @@ dtvcc_carriage_return (struct dtvcc_decoder * dc, switch (dw->style.scroll_direction) { case DIR_LEFT_RIGHT: - dw->curr_row = 0; - if (column > 0) { - dw->curr_column = column - 1; - break; + if (dw->style.print_direction == DIR_BOTTOM_TOP) + { + dw->curr_row = dw->row_count - 1; + if (column > 0) { + dw->curr_column = column - 1; + break; + } + } + else + { + dw->curr_row = 0; + if (column > 0) { + dw->curr_column = column - 1; + break; + } } dw->streamed = (dw->streamed << 1) & ~(1 << dw->column_count); @@ -2887,16 +3062,30 @@ dtvcc_carriage_return (struct dtvcc_decoder * dc, column > 0; --column) { dw->buffer[row][column] = dw->buffer[row][column - 1]; + dw->pen[row][column] = + dw->pen[row][column - 1]; } dw->buffer[row][column] = 0; + memset(&dw->pen[row][column], 0, sizeof(dw->pen[0][0])); } break; case DIR_RIGHT_LEFT: - dw->curr_row = 0; - if (column + 1 < dw->row_count) { - dw->curr_column = column + 1; - break; + if (dw->style.print_direction == DIR_BOTTOM_TOP) + { + dw->curr_row = dw->row_count - 1; + if (column + 1 < dw->row_count) { + dw->curr_column = column + 1; + break; + } + } + else + { + dw->curr_row = 0; + if (column + 1 < dw->row_count) { + dw->curr_column = column + 1; + break; + } } dw->streamed >>= 1; for (row = 0; row < dw->row_count; ++row) { @@ -2904,34 +3093,66 @@ dtvcc_carriage_return (struct dtvcc_decoder * dc, column < dw->column_count - 1; ++column) { dw->buffer[row][column] = dw->buffer[row][column + 1]; + dw->pen[row][column] = + dw->pen[row][column + 1]; } dw->buffer[row][column] = 0; + memset(&dw->pen[row][column], 0, sizeof(dw->pen[0][0])); } break; case DIR_TOP_BOTTOM: - dw->curr_column = 0; - if (row > 0) { - dw->curr_row = row - 1; - break; + if (dw->style.print_direction == DIR_RIGHT_LEFT) + { + AM_DEBUG(0, "CR: print_r_l cur_col %d row %d", dw->curr_column, dw->curr_row); + dw->curr_column = dw->column_count - 1; + if (row > 0) { + dw->curr_row = row - 1; + break; + } + } + else + { + dw->curr_column = 0; + if (row > 0) { + dw->curr_row = row - 1; + break; + } } dw->streamed = (dw->streamed << 1) & ~(1 << dw->row_count); memmove (&dw->buffer[1], &dw->buffer[0], sizeof (dw->buffer[0]) * (dw->row_count - 1)); + memmove (&dw->pen[1], &dw->pen[0], + sizeof (dw->pen[0]) * (dw->row_count - 1)); memset (&dw->buffer[0], 0, sizeof (dw->buffer[0])); + memset (&dw->pen[0], 0, sizeof(dw->pen[0])); break; case DIR_BOTTOM_TOP: - dw->curr_column = 0; - if (row + 1 < dw->row_count) { - dw->curr_row = row + 1; - break; + if (dw->style.print_direction == DIR_RIGHT_LEFT) + { + dw->curr_column = dw->column_count - 1;; + if (row + 1 < dw->row_count) { + dw->curr_row = row + 1; + break; + } + } + else + { + dw->curr_column = 0; + if (row + 1 < dw->row_count) { + dw->curr_row = row + 1; + break; + } } dw->streamed >>= 1; memmove (&dw->buffer[0], &dw->buffer[1], sizeof (dw->buffer[0]) * (dw->row_count - 1)); + memmove (&dw->pen[0], &dw->pen[1], + sizeof (dw->pen[0]) * (dw->row_count - 1)); memset (&dw->buffer[row], 0, sizeof (dw->buffer[0])); + memset (&dw->pen[row], 0, sizeof (dw->pen[0])); break; } @@ -3011,6 +3232,7 @@ dtvcc_backspace (struct dtvcc_decoder * dc, if (0 != dw->buffer[row][column]) { dw->streamed &= ~mask; dw->buffer[row][column] = 0; + memset(&dw->pen[row][column], 0, sizeof(dw->pen[0][0])); } dw->curr_row = row; @@ -3045,6 +3267,8 @@ dtvcc_hor_carriage_return (struct dtvcc_decoder * dc, mask = 1 << row; memset (&dw->buffer[row][0], 0, sizeof (dw->buffer[0])); + memset (&dw->pen[row][0], 0, + sizeof (dw->pen[0])); if (DIR_LEFT_RIGHT == dw->style.print_direction) dw->curr_column = 0; else @@ -3054,8 +3278,10 @@ dtvcc_hor_carriage_return (struct dtvcc_decoder * dc, case DIR_TOP_BOTTOM: case DIR_BOTTOM_TOP: mask = 1 << column; - for (row = 0; row < dw->column_count; ++row) + for (row = 0; row < dw->column_count; ++row) { dw->buffer[row][column] = 0; + memset(&dw->pen[row][column], 0, sizeof(dw->pen[0][0])); + } if (DIR_TOP_BOTTOM == dw->style.print_direction) dw->curr_row = 0; else @@ -3074,15 +3300,36 @@ dtvcc_delete_windows (struct dtvcc_decoder * dc, dtvcc_window_map window_map) { struct dtvcc_window *dw; + int i; + int changed = 0; - dw = ds->curr_window; - if (NULL != dw) { - unsigned int window_id; - - window_id = dtvcc_window_id (ds, dw); - if (0 != (window_map & (1 << window_id))) { - dtvcc_stream_event (dc, ds, dw, dw->curr_row); - ds->curr_window = NULL; + for (i = 0; i < N_ELEMENTS(ds->window); i ++) { + dw = &ds->window[i]; + + if (NULL != dw) { + unsigned int window_id; + window_id = dtvcc_window_id (ds, dw); + if (ds->created & (1 << window_id)) { + if (window_map & (1 << window_id)) { + //printf("delete window %d\n", window_id); + dtvcc_stream_event (dc, ds, dw, dw->curr_row); + + if (dw == ds->curr_window) + ds->curr_window = NULL; + + if (dw->visible) + dtvcc_render(dc, ds); + + memset (dw->buffer, 0, sizeof (dw->buffer)); + memset (dw->pen, 0, sizeof(dw->pen)); + dw->visible = 0; + dw->effect_status = 0; + dw->effect_percent = 0; + dw->style.display_effect = 0; + + changed = 1; + } + } } } @@ -3092,6 +3339,45 @@ dtvcc_delete_windows (struct dtvcc_decoder * dc, } static vbi_bool +dtvcc_delay_cmd (struct dtvcc_decoder * dc, + struct dtvcc_service * ds, + uint8_t delay_cmd, + uint8_t delay_time) +{ + struct timespec now_ts; + int plus_one_second = 0; + clock_gettime(CLOCK_REALTIME, &now_ts); + if (delay_cmd == 0x8D) + { + /* Set trigger time + Until that time, sevice stop decoding. + */ + /* Delay time is tenths of second */ + /* We set the timer a little faster to avoid double delay conflict */ + if ((1000000000 - (delay_time%10) * 100000000) > now_ts.tv_nsec) + { + ds->delay_timer.tv_nsec = ((delay_time % 10) * 100000000 + now_ts.tv_nsec) % 1000000000; + ds->delay_timer.tv_sec = (1 + now_ts.tv_sec + delay_time/10) -1; + } + else + { + ds->delay_timer.tv_nsec = (delay_time % 10) * 100000000 + now_ts.tv_nsec; + ds->delay_timer.tv_sec = (now_ts.tv_sec + delay_time / 10) -1; + } + ds->delay_timer.tv_sec = now_ts.tv_sec + 1; + ds->delay = 1; + ds->delay_cancel = 0; + //AM_DEBUG(1, "Enter delay cmd, now %d until %d", now_ts.tv_sec, ds->delay_timer.tv_sec); + } + else if (delay_cmd == 0x8E) + { + ds->delay = 0; + ds->delay_cancel = 1; + } + return TRUE; +} + +static vbi_bool dtvcc_command (struct dtvcc_decoder * dc, struct dtvcc_service * ds, unsigned int * se_length, @@ -3110,9 +3396,8 @@ dtvcc_command (struct dtvcc_decoder * dc, if (*se_length > n_bytes) { ds->error_line = __LINE__; - return FALSE; + return TRUE; } - switch (c) { case 0x08: /* BS Backspace */ return dtvcc_backspace (dc, ds); @@ -3141,6 +3426,14 @@ dtvcc_command (struct dtvcc_decoder * dc, case 0x89: /* DSW DisplayWindows */ return dtvcc_display_windows (dc, ds, c, buf[1]); + case 0x8D: + dtvcc_delay_cmd(dc, ds, 0x8d, buf[1]); + return 0; + + case 0x8E: + dtvcc_delay_cmd(dc, ds, 0x8e, buf[1]); + return 0; + case 0x8A: /* HDW HideWindows */ return dtvcc_display_windows (dc, ds, c, buf[1]); @@ -3244,17 +3537,21 @@ dtvcc_decode_syntactic_elements (struct dtvcc_decoder * dc, unsigned int n_bytes) { ds->timestamp = dc->timestamp; + struct timespec ts_now; + +#if 0 + AM_DEBUG(1, "+++++++++++++++++++++ servie %d\n", n_bytes); + { + int i; + for (i = 0; i < n_bytes; i ++) + AM_DEBUG(1, "++++++++++++++ %02x ", buf[i]); + } +#endif while (n_bytes > 0) { unsigned int se_length; - - if (0x8D /* DLY */ == *buf - || 0x8E /* DLC */ == *buf) { - /* FIXME ignored for now. */ - ++buf; - --n_bytes; - continue; - } + + //printf("dec se %02x\n", buf[0]); if (!dtvcc_decode_se (dc, ds, &se_length, @@ -3269,6 +3566,40 @@ dtvcc_decode_syntactic_elements (struct dtvcc_decoder * dc, return TRUE; } +static void +dtvcc_try_decode_channels (struct dtvcc_decoder *dc) +{ + int i; + + for (i = 0; i < 6; ++i) { + struct dtvcc_service *ds; + struct program *pr; + vbi_bool success; + + ds = &dc->service[i]; + if (0 == ds->service_data_in) + continue; + if (!ds->delay || + (ds->delay && ds->service_data_in>=128)) + { + //AM_DEBUG(1, "service datain %d", ds->service_data_in); + success = dtvcc_decode_syntactic_elements + (dc, ds, ds->service_data, ds->service_data_in); + if (ds->service_data_in >= 128) + { + ds->delay = 0; + ds->delay_cancel = 0; + } + ds->service_data_in = 0; + + if (success) + continue; + } + //dtvcc_reset_service (ds); + //dc->next_sequence_number = -1; + } +} + void dtvcc_decode_packet (struct dtvcc_decoder * dc, const struct timeval * tv, @@ -3278,6 +3609,14 @@ dtvcc_decode_packet (struct dtvcc_decoder * dc, unsigned int packet_size; unsigned int i; +#if 0 + printf("%d dtvcc decode packet %d: ", get_input_offset(), dc->packet_size); + + for (i = 0; i < dc->packet_size; i ++) { + printf("%02x ", dc->packet[i]); + } + printf("\n"); +#endif dc->timestamp.sys = *tv; dc->timestamp.pts = pts; @@ -3285,13 +3624,13 @@ dtvcc_decode_packet (struct dtvcc_decoder * dc, /* sequence_number [2], packet_size_code [6], packet_data [n * 8] */ - +#if 1 if (dc->next_sequence_number >= 0 && 0 != ((dc->packet[0] ^ dc->next_sequence_number) & 0xC0)) { dtvcc_reset (dc); return; } - +#endif dc->next_sequence_number = dc->packet[0] + 0x40; packet_size_code = dc->packet[0] & 0x3F; @@ -3302,8 +3641,10 @@ dtvcc_decode_packet (struct dtvcc_decoder * dc, /* CEA 708-C Section 5: Apparently packet_size need not be equal to the actually transmitted amount of data. */ if (packet_size > dc->packet_size) { + /* dtvcc_reset (dc); - return; + return;*/ + packet_size = dc->packet_size; } /* Service Layer. */ @@ -3324,14 +3665,17 @@ dtvcc_decode_packet (struct dtvcc_decoder * dc, (null_fill [2], extended_service_number [6]), (Block_data [n * 8]) */ - c = dc->packet[i]; + c = dc->packet[i]; service_number = (c & 0xE0) >> 5; + //printf("srv %d\n", service_number); + /* CEA 708-C Section 6.3: Ignore block_size if service_number is zero. */ if (0 == service_number) { /* NULL Service Block Header, no more data in this Caption Channel Packet. */ + dc->next_sequence_number = -1; break; } @@ -3342,22 +3686,26 @@ dtvcc_decode_packet (struct dtvcc_decoder * dc, if (7 == service_number) { if (i + 1 > packet_size) - goto service_block_incomplete; + break; header_size = 2; c = dc->packet[i + 1]; /* We also check the null_fill bits. */ if (c < 7 || c > 63) - goto invalid_service_block; + break; service_number = c; } + //printf("srv %d %d %d %d\n", service_number, header_size, block_size, packet_size); + if (i + header_size + block_size > packet_size) - goto service_block_incomplete; + { + break; + } - if (service_number <= 2) { + if (service_number <= 6) { struct dtvcc_service *ds; unsigned int in; @@ -3372,48 +3720,215 @@ dtvcc_decode_packet (struct dtvcc_decoder * dc, i += header_size + block_size; } - for (i = 0; i < 2; ++i) { - struct dtvcc_service *ds; - struct program *pr; - vbi_bool success; + dtvcc_try_decode_channels(dc); + return; +} - ds = &dc->service[i]; - if (0 == ds->service_data_in) - continue; +static int +dtvcc_get_se_len (unsigned char *p, int left) +{ + unsigned char c; + int se_length; - success = dtvcc_decode_syntactic_elements - (dc, ds, ds->service_data, ds->service_data_in); + if (left < 1) + return 0; - ds->service_data_in = 0; + c = p[0]; - if (success) - continue; + if ((c == 0x8d) && (c == 0x8e)) + return 1; - dtvcc_reset_service (ds); + if (0 != (c & 0x60)) + return 1; + + if (0x10 != c) { + if ((int8_t) c < 0) { + se_length = dtvcc_c1_length[c - 0x80]; + } else { + se_length = dtvcc_c0_length[c >> 3]; + } + + if (left < se_length) + return 0; + + return se_length; } - return; + if (left < 2) + return 0; - invalid_service_block: - { - dtvcc_reset (dc); - return; + c = p[1]; + if (0 != (c & 0x60)) + return 2; + + if ((int8_t) c >= 0) { + se_length = (c >> 3) + 2; + } else if (c < 0x90) { + se_length = (c >> 3) - 10; + } else { + if (left < 3) + return 0; + + se_length = (p[2] & 0x1F) + 3; } - service_block_incomplete: - { - dtvcc_reset (dc); + if (left < se_length) + return 0; + + return se_length; +} + +void +dtvcc_try_decode_packet (struct dtvcc_decoder * dc, + const struct timeval * tv, + int64_t pts) +{ + unsigned int packet_size_code; + unsigned int packet_size; + unsigned char *p; + int left; + + if (dc->packet_size < 1) + return; + + packet_size_code = dc->packet[0] & 0x3F; + + packet_size = 128; + if (packet_size_code > 0) + packet_size = packet_size_code * 2; + + if (packet_size <= dc->packet_size) { + dtvcc_decode_packet(dc, tv, pts); + dc->packet_size = 0; return; } + p = dc->packet + 1; + left = dc->packet_size - 1; + while (left > 0) { + unsigned int service_number; + unsigned int block_size; + unsigned int header_size; + unsigned int c; + + header_size = 1; + + c = p[0]; + service_number = (c & 0xE0) >> 5; + if (0 == service_number) + break; + + block_size = c & 0x1F; + + if (7 == service_number) { + if (left < 2) + break; + + header_size = 2; + c = p[1]; + + if (c < 7 || c > 63) + break; + + service_number = c; + } + + if (left >= header_size + block_size) { + if (service_number <= 6) { + struct dtvcc_service *ds; + unsigned int in; + + ds = &dc->service[service_number - 1]; + in = ds->service_data_in; + memcpy (ds->service_data + in, + p + header_size, + block_size); + + ds->service_data_in = in + block_size; + } + } else { + unsigned char *s = p + header_size; + int sleft = left - header_size; + + while (sleft > 0) { + int se_len; + + se_len = dtvcc_get_se_len(s, sleft); + if (se_len <= 0) + break; + + s += se_len; + sleft -= se_len; + } + + if (sleft != left - header_size) { + int parsed = left - header_size - sleft; + + if (service_number <= 6) { + struct dtvcc_service *ds; + unsigned int in; + + ds = &dc->service[service_number - 1]; + in = ds->service_data_in; + memcpy (ds->service_data + in, + p + header_size, + parsed); + + ds->service_data_in = in + parsed; + } + + memmove(p + header_size, s, sleft); + block_size -= parsed; + left -= parsed; + + p[0] &= ~0x1f; + p[0] |= block_size; + } + break; + } + + p += header_size + block_size; + left -= header_size + block_size; + } + + if (left != dc->packet_size - 1) { + int parsed = dc->packet_size - 1 - left; + + memmove(dc->packet + 1, p, left); + + packet_size_code = ((dc->packet[0] & 0x3f) << 1) - parsed; + if (packet_size_code & 1) + packet_size_code ++; + packet_size_code >>= 1; + + dc->packet[0] &= ~0x3f; + dc->packet[0] |= packet_size_code; + dc->packet_size = left + 1; + + dtvcc_try_decode_channels(dc); + } } static void dtvcc_reset_service (struct dtvcc_service * ds) { + int i; ds->curr_window = NULL; ds->created = 0; + ds->delay = 0; + ds->delay_cancel = 0; + struct dtvcc_window *dw; + for (i=0;i<8;i++) + { + dw = &ds->window[i]; + ds->window[i].visible = 0; + memset (dw->buffer, 0, sizeof (dw->buffer)); + memset (dw->pen, 0, sizeof(dw->pen)); + dw->effect_status = 0; + dw->streamed = 0; + } + ds->update = 1; cc_timestamp_reset (&ds->timestamp); } @@ -3422,7 +3937,6 @@ dtvcc_reset (struct dtvcc_decoder * dc) { dtvcc_reset_service (&dc->service[0]); dtvcc_reset_service (&dc->service[1]); - dc->packet_size = 0; dc->next_sequence_number = -1; } @@ -3430,9 +3944,12 @@ dtvcc_reset (struct dtvcc_decoder * dc) void dtvcc_init (struct dtvcc_decoder * dc) { + int i; + memset(dc, 0, sizeof(struct dtvcc_decoder)); dtvcc_reset (dc); - cc_timestamp_reset (&dc->timestamp); + for (i=0;i<6;i++) + dc->service[i].id = i; } static void dtvcc_window_to_page(vbi_decoder *vbi, struct dtvcc_window *dw, struct vbi_page *pg) @@ -3440,21 +3957,22 @@ static void dtvcc_window_to_page(vbi_decoder *vbi, struct dtvcc_window *dw, stru int i, j, c; vbi_char ac; vbi_opacity fg_opacity, bg_opacity; - + static const vbi_opacity vbi_opacity_map[]={ VBI_OPAQUE, VBI_OPAQUE, VBI_SEMI_TRANSPARENT, VBI_TRANSPARENT_SPACE }; - + memset(pg, 0, sizeof(struct vbi_page)); #if 1 pg->rows = dw->row_count; pg->columns = dw->column_count; - + dtvcc_set_page_color_map(vbi, pg); - + memset(dw->row_start, 0, sizeof(dw->row_start)); + for (i=0; irows; i++) { for (j=0; jcolumns; j++) @@ -3472,22 +3990,23 @@ static void dtvcc_window_to_page(vbi_decoder *vbi, struct dtvcc_window *dw, stru }else{ bg_opacity = vbi_opacity_map[dw->curr_pen.style.bg_opacity]; } - + ac.opacity = (fg_opacity<<4) | bg_opacity; ac.foreground = dtvcc_map_color(dw->curr_pen.style.fg_color); ac.background = dtvcc_map_color(dw->curr_pen.style.bg_color); - + c = dw->buffer[i][j]; if (0 == c) { ac.unicode = 0x20; ac.opacity = VBI_TRANSPARENT_SPACE; + dw->row_start[i] ++; } else { ac.unicode = dtvcc_unicode (c); if (0 == ac.unicode) { ac.unicode = 0x20; + dw->row_start[i] ++; } } - pg->text[i*pg->columns + j] = ac; } } @@ -3545,7 +4064,7 @@ static void dtvcc_get_visible_windows(struct dtvcc_service *ds, int *cnt, struct void tvcc_fetch_page(struct tvcc_decoder *td, int pgno, int *sub_cnt, struct vbi_page *sub_pages) { int sub_pg = 0; - + if (pgno < 1 || pgno > 14 || *sub_cnt <= 0) goto fetch_done; @@ -3559,30 +4078,146 @@ void tvcc_fetch_page(struct tvcc_decoder *td, int pgno, int *sub_cnt, struct vbi struct dtvcc_service *ds = &td->dtvcc.service[pgno - 1 - 8]; struct dtvcc_window *dw; struct dtvcc_window *visible_windows[8]; - + sub_pg = *sub_cnt; if (sub_pg > 8) sub_pg = 8; - + dtvcc_get_visible_windows(ds, &sub_pg, visible_windows); - + for (i=0; ivbi, dw, &sub_pages[i]); - + + dtvcc_window_to_page(td->vbi, dw, &sub_pages[i]); sub_pages[i].vbi = td->vbi; sub_pages[i].pgno = pgno; sub_pages[i].subno = dw - ds->window; } } - + fetch_done: *sub_cnt = sub_pg; } /* ATSC A/53 Part 4:2007 Closed Caption Data decoder */ +/* Only handle effect */ +static void update_service_status_internal (struct tvcc_decoder *td) +{ + int i, j, k, l; + struct timespec ts_now; + struct dtvcc_decoder *decoder; + struct dtvcc_pen_style *target_pen; + int flash; + + decoder = &td->dtvcc; + clock_gettime(CLOCK_REALTIME, &ts_now); + + flash = (ts_now.tv_nsec / 250000000) & 1; + + /* CS1 - CS6 */ + for (i = 0; i < 6; ++i) + { + struct dtvcc_service *ds; + struct program *pr; + vbi_bool success; + ds = &decoder->service[i]; + /* Check every effect */ + if (ds->delay) + { + struct vbi_event event; + /* time is up */ + if ((ts_now.tv_sec > ds->delay_timer.tv_sec) || + ((ts_now.tv_sec == ds->delay_timer.tv_sec) &&(ts_now.tv_nsec > ds->delay_timer.tv_nsec)) || + ds->delay_cancel) + { + //AM_DEBUG(1, "delay timeup"); + ds->delay = 0; + ds->delay_cancel = 0; + dtvcc_decode_syntactic_elements + (decoder, ds, ds->service_data, ds->service_data_in); + + ds->service_data_in = 0; + } + } + + if (flash == decoder->flash_state) + continue; + + for (j = 0; j < 8; j++) + { + struct dtvcc_window *target_window; + target_window = &ds->window[j]; + /*window flash treatment */ + if (target_window->style.window_flash) + { + target_window->style.fill_opacity = flash?0:3; + ds->update = 1; + } + + /* Wipe and fade treatment */ + if (target_window->style.display_effect != 0 && + target_window->effect_status != 0) + { + target_window->effect_percent = + ((ts_now.tv_sec - target_window->effect_timer.tv_sec) * 1000 + + (ts_now.tv_nsec - target_window->effect_timer.tv_nsec) / 1000000) *100/ + (target_window->style.effect_speed * 500); + if (target_window->effect_percent > 100) + target_window->effect_percent = 100; + ds->update = 1; + } + + /* Pen flash treatment */ + for (k = 0; k < 16; k++) + { + for (l =0; l<42; l++) + { + target_pen = &target_window->pen[k][l]; + if (target_pen->bg_flash) + { + target_pen->bg_opacity = flash?0:3; + ds->update = 1; + } + if (target_pen->fg_flash) + { + target_pen->fg_opacity = flash?0:3; + ds->update = 1; + } + } + } + } + } + + decoder->flash_state = flash; +} + +static void +update_display (struct tvcc_decoder *td) +{ + int i; + + for (i = 0; i < N_ELEMENTS(td->dtvcc.service); i ++) { + struct dtvcc_service *ds = &td->dtvcc.service[i]; + + if (ds->update) { + struct vbi_event event; + + event.type = VBI_EVENT_CAPTION; + event.ev.caption.pgno = i + 1 + 8/*after 8 cc channels*/; + + /* Permits calling tvcc_fetch_page from handler */ + pthread_mutex_unlock(&td->mutex); + + vbi_send_event(td->vbi, &event); + pthread_mutex_lock(&td->mutex); + + ds->update = 0; + } + } +} + + /* Note pts may be < 0 if no PTS was received. */ void tvcc_decode_data (struct tvcc_decoder *td, @@ -3600,12 +4235,30 @@ tvcc_decode_data (struct tvcc_decoder *td, return; process_cc_data_flag = buf[1] & 0x40; if (!process_cc_data_flag) + { return; + } cc_count = buf[1] & 0x1F; dtvcc = FALSE; +#if 0 + printf("tvcc decode %d:\n", n_bytes); + { + int i; + + for (i = 0; i < n_bytes; i ++) { + printf("%02x ", buf[i]); + if ((i + 1) % 16 == 0) + printf("\n"); + } + printf("\n"); + } +#endif pthread_mutex_lock(&td->mutex); + + //printf("cc count %d\n", cc_count); + for (i = 0; i < cc_count; ++i) { unsigned int b0; unsigned int cc_valid; @@ -3620,9 +4273,12 @@ tvcc_decode_data (struct tvcc_decoder *td, cc_data_1 = buf[4 + i * 3]; cc_data_2 = buf[5 + i * 3]; + //printf("cc type %02x %02x %02x %02x\n", cc_type, cc_valid, cc_data_1, cc_data_2); + switch (cc_type) { case NTSC_F1: case NTSC_F2: + //printf("ntsc cc\n"); /* Note CEA 708-C Table 4: Only one NTSC pair will be present in field picture user_data or in progressive video pictures, and up to @@ -3647,10 +4303,7 @@ tvcc_decode_data (struct tvcc_decoder *td, break; } else if (!cc_valid) { /* End of DTVCC packet. */ - dtvcc_decode_packet (&td->dtvcc, - &now, pts); - td->dtvcc.packet_size = 0; - } else if (j >= 128) { + } else if (j + 2 > 128) { /* Packet buffer overflow. */ dtvcc_reset (&td->dtvcc); td->dtvcc.packet_size = 0; @@ -3658,6 +4311,8 @@ tvcc_decode_data (struct tvcc_decoder *td, td->dtvcc.packet[j] = cc_data_1; td->dtvcc.packet[j + 1] = cc_data_2; td->dtvcc.packet_size = j + 2; + + dtvcc_try_decode_packet (&td->dtvcc, &now, pts); } break; @@ -3676,10 +4331,26 @@ tvcc_decode_data (struct tvcc_decoder *td, td->dtvcc.packet[0] = cc_data_1; td->dtvcc.packet[1] = cc_data_2; td->dtvcc.packet_size = 2; + dtvcc_try_decode_packet(&td->dtvcc, &now, pts); } break; } } + + update_service_status_internal(td); + update_display(td); + + pthread_mutex_unlock(&td->mutex); +} + +/* Only handle effect */ +void update_service_status(struct tvcc_decoder *td) +{ + pthread_mutex_lock(&td->mutex); + + update_service_status_internal(td); + update_display(td); + pthread_mutex_unlock(&td->mutex); } diff --git a/src/dtvcc.h b/src/dtvcc.h index 24f4f58..32a7a7a 100644 --- a/src/dtvcc.h +++ b/src/dtvcc.h @@ -40,6 +40,12 @@ enum cc_mode { CC_MODE_TEXT }; +enum cc_effect_status { + CC_EFFECT_NONE, + CC_EFFECT_DISPLAY, + CC_EFFECT_HIDE +}; + /* EIA 608-B Section 4.1. */ #define VBI_CAPTION_CC1 1 /* primary synchronous caption service (F1) */ #define VBI_CAPTION_CC2 2 /* special non-synchronous use captions (F1) */ @@ -308,6 +314,8 @@ struct dtvcc_pen_style { enum opacity bg_opacity; dtvcc_color edge_color; + vbi_bool fg_flash; + vbi_bool bg_flash; }; struct dtvcc_pen { @@ -327,6 +335,7 @@ struct dtvcc_window_style { dtvcc_color fill_color; enum opacity fill_opacity; + vbi_bool window_flash; enum edge border_type; dtvcc_color border_color; @@ -336,6 +345,8 @@ struct dtvcc_window { /* EIA 708-C window state. */ uint16_t buffer[16][42]; + struct dtvcc_pen_style pen[16][42]; + int row_start[16]; vbi_bool visible; @@ -352,6 +363,7 @@ struct dtvcc_window { vbi_bool row_lock; vbi_bool column_lock; + unsigned int column_no_lock_length; unsigned int curr_row; unsigned int curr_column; @@ -360,6 +372,12 @@ struct dtvcc_window { struct dtvcc_window_style style; + /* Used for fade and swipe */ + struct timespec effect_timer; + /* 0~100 */ + int effect_percent; + enum cc_effect_status effect_status; + /* Our stuff. */ /** @@ -379,8 +397,13 @@ struct dtvcc_window { struct dtvcc_service { /* Interpretation Layer. */ + int id; struct dtvcc_window window[8]; + struct dtvcc_window old_window[8]; + + int old_win_cnt; + int update; struct dtvcc_window * curr_window; @@ -393,6 +416,14 @@ struct dtvcc_service { uint8_t service_data[128]; unsigned int service_data_in; + /* For 0x8D 0x8E delay command + * if delay flag is set, decoder stop decoding + */ + struct timespec delay_timer; + + + int delay; + int delay_cancel; /** The time when we last received data for this service. */ struct cc_timestamp timestamp; @@ -413,6 +444,8 @@ struct dtvcc_decoder { /** The time when we last received data. */ struct cc_timestamp timestamp; + + int flash_state; }; /* ATSC A/53 Part 4:2007 Closed Caption Data decoder. */ @@ -473,6 +506,7 @@ struct tvcc_decoder { struct cc_decoder cc; struct dtvcc_decoder dtvcc; }; +unsigned int dtvcc_unicode (unsigned int c); extern void tvcc_init(struct tvcc_decoder *td); @@ -482,7 +516,7 @@ extern void tvcc_reset(struct tvcc_decoder *td); extern void tvcc_destroy(struct tvcc_decoder *td); -extern void tvcc_fetch_page(struct tvcc_decoder *td, int pgno, int *sub_cnt, struct vbi_page *sub_pages); +extern void tvcc_fetch_page(struct tvcc_decoder *td, int pgno, int *cnt, struct vbi_page *sub_pages); static void dtvcc_init(struct dtvcc_decoder * dc); @@ -496,5 +530,14 @@ extern vbi_bool cc_feed(struct cc_decoder *cd, const uint8_t buffer[2], unsigned extern void cc_reset(struct cc_decoder *cd); +extern void vbi_decode_caption(vbi_decoder *vbi, int line, const uint8_t *buf); + +extern int get_input_offset(); + +extern void update_service_status(struct tvcc_decoder *td); +static vbi_bool dtvcc_carriage_return (struct dtvcc_decoder *dc, struct dtvcc_service * ds); + + + #endif diff --git a/src/event.h b/src/event.h index 7b699a3..1ba71ee 100644..100755 --- a/src/event.h +++ b/src/event.h @@ -128,6 +128,9 @@ typedef struct { /** Private. */ int cycle; + + /** Transmission Signal ID */ + int ts_id; } vbi_network; /* @@ -414,7 +417,16 @@ typedef enum { */ VBI_AUDIO_MODE_UNKNOWN } vbi_audio_mode; - +typedef struct vbi_rating{ + /* + * For details STFW for "v-chip" + * If unknown rating_auth == VBI_RATING_NONE + */ + vbi_rating_auth auth; + int id; + /* Only valid when auth == VBI_RATING_TV_US */ + int dlsv; +}vbi_rating; /** * @ingroup Event * @@ -470,16 +482,7 @@ typedef struct vbi_program_info { int type_id[33]; /* 05 Program rating */ - - /* - * For details STFW for "v-chip" - * If unknown rating_auth == VBI_RATING_NONE - */ - vbi_rating_auth rating_auth; - int rating_id; - - /* Only valid when auth == VBI_RATING_TV_US */ - int rating_dlsv; + vbi_rating rating; /* 06 Program Audio Services */ @@ -652,6 +655,7 @@ extern void vbi_reset_prog_info(vbi_program_info *pi); * @since 0.2.20 */ #define VBI_EVENT_NETWORK_ID 0x0100 +#define VBI_EVENT_RATING 0x0200 /** @} */ /** diff --git a/src/exp-gfx.c b/src/exp-gfx.c index c8ff3d5..4232dda 100644 --- a/src/exp-gfx.c +++ b/src/exp-gfx.c @@ -674,6 +674,8 @@ vbi_draw_vt_page_region(vbi_page *pg, ac = &pg->text[row * pg->columns + column]; for (count = width; count > 0; count--, ac++) { + int transparent; + if ((ac->conceal & conceal) || (ac->flash & off)) unicode = 0x0020; else @@ -682,6 +684,53 @@ vbi_draw_vt_page_region(vbi_page *pg, if (subtitle && (row == 0)) unicode = 0x0020; + if (!subtitle) { + transparent = 0; + } else if (subtitle == 1) { + if (vbi_is_drcs(unicode) || unicode==0x0020) + transparent = 1; + else + transparent = 0; + } else { + if (row == 0) { + transparent = 1; + } else if (vbi_is_drcs(unicode) || unicode==0x0020) { + vbi_char *tc; + int n, uc; + int left, right; + + left = right = 0; + + for (n = count + 1, tc = ac - 1; n <= width; tc --, n ++) { + if ((tc->conceal & conceal) || (tc->flash & off)) + uc = 0x0020; + else + uc = tc->unicode; + + if(!vbi_is_drcs(uc) && uc!=0x0020) { + left = 1; + break; + } + } + + for (n = count - 1, tc = ac + 1; n > 0; tc ++, n --) { + if ((tc->conceal & conceal) || (tc->flash & off)) + uc = 0x0020; + else + uc = tc->unicode; + + if(!vbi_is_drcs(uc) && uc!=0x0020) { + right = 1; + break; + } + } + + transparent = !(left & right); + } else { + transparent = 0; + } + } + if (canvas_type == 1) { pen.pal8[0] = ac->background; pen.pal8[1] = ac->foreground; @@ -689,15 +738,13 @@ vbi_draw_vt_page_region(vbi_page *pg, pen.rgba[0] = pg->color_map[ac->background]; pen.rgba[1] = pg->color_map[ac->foreground]; - if(subtitle){ - if(vbi_is_drcs(unicode) || unicode==0x0020) { - pen.rgba[0] &= 0x00FFFFFF; - pen.rgba[1] &= 0x00FFFFFF; - }else{ - pen.rgba[0] &= 0x00FFFFFF; - //pen.rgba[0] |= 0x80000000; - } - } + if (transparent) { + pen.rgba[0] = 0x00FFFFFF; + pen.rgba[1] = 0x00FFFFFF; + } + + if (subtitle == 1) + pen.rgba[0] = 0x00FFFFFF; } switch (ac->size) { diff --git a/src/libzvbi.h b/src/libzvbi.h index 4f562ee..2e251bf 100644..100755 --- a/src/libzvbi.h +++ b/src/libzvbi.h @@ -275,6 +275,7 @@ typedef struct { int cycle; + int ts_id; } vbi_network; /* @@ -384,6 +385,18 @@ typedef enum { VBI_AUDIO_MODE_UNKNOWN } vbi_audio_mode; +typedef struct vbi_rating{ + /* + * For details STFW for "v-chip" + * If unknown rating_auth == VBI_RATING_NONE + */ + vbi_rating_auth auth; + int id; + /* Only valid when auth == VBI_RATING_TV_US */ + int dlsv; +}vbi_rating; + + typedef struct vbi_program_info { /* * Refers to the current or next program. @@ -439,11 +452,7 @@ typedef struct vbi_program_info { * For details STFW for "v-chip" * If unknown rating_auth == VBI_RATING_NONE */ - vbi_rating_auth rating_auth; - int rating_id; - - /* Only valid when auth == VBI_RATING_TV_US */ - int rating_dlsv; + vbi_rating rating; /* 06 Program Audio Services */ @@ -508,6 +517,7 @@ extern void vbi_reset_prog_info(vbi_program_info *pi); #define VBI_EVENT_ASPECT 0x0040 #define VBI_EVENT_PROG_INFO 0x0080 #define VBI_EVENT_NETWORK_ID 0x0100 +#define VBI_EVENT_RATING 0x0200 diff --git a/src/vbi.c b/src/vbi.c index 687a02f..92cb363 100644..100755 --- a/src/vbi.c +++ b/src/vbi.c @@ -795,7 +795,7 @@ vbi_reset_prog_info(vbi_program_info *pi) /* PT */ pi->type_classf = VBI_PROG_CLASSF_NONE; /* PR */ - pi->rating_auth = VBI_RATING_AUTH_NONE; + pi->rating.auth = VBI_RATING_AUTH_NONE; /* PAS */ pi->audio[0].mode = VBI_AUDIO_MODE_UNKNOWN; pi->audio[0].language = NULL; -- cgit