summaryrefslogtreecommitdiff
Diffstat
-rw-r--r--Android.mk4
-rwxr-xr-xntsc_decode/am_vbi.c26
-rw-r--r--src/caption.c656
-rw-r--r--src/cc.h15
-rw-r--r--src/dtvcc.c893
-rw-r--r--src/dtvcc.h45
-rwxr-xr-x[-rw-r--r--]src/event.h26
-rw-r--r--src/exp-gfx.c65
-rwxr-xr-x[-rw-r--r--]src/libzvbi.h20
-rwxr-xr-x[-rw-r--r--]src/vbi.c2
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);
}