-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/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 <android/log.h> +#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; i<pg->rows; i++) { for (j=0; j<pg->columns; 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; i<sub_pg; i++){ dw = visible_windows[i]; - - dtvcc_window_to_page(td->vbi, 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); } |