-rw-r--r-- | Android.mk | 4 | ||||
-rwxr-xr-x | ntsc_decode/am_vbi.c | 26 | ||||
-rw-r--r-- | src/caption.c | 656 | ||||
-rw-r--r-- | src/cc.h | 15 | ||||
-rw-r--r-- | src/dtvcc.c | 893 | ||||
-rw-r--r-- | src/dtvcc.h | 45 | ||||
-rwxr-xr-x[-rw-r--r--] | src/event.h | 26 | ||||
-rw-r--r-- | src/exp-gfx.c | 65 | ||||
-rwxr-xr-x[-rw-r--r--] | src/libzvbi.h | 20 | ||||
-rwxr-xr-x[-rw-r--r--] | src/vbi.c | 2 |
10 files changed, 1393 insertions, 359 deletions
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 <android/log.h> +#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 |