summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gst/closedcaption/Makefile.am11
-rw-r--r--gst/closedcaption/cc608_decoder.c2482
-rw-r--r--gst/closedcaption/cc608_decoder.h131
-rw-r--r--gst/closedcaption/event-priv.h104
-rw-r--r--gst/closedcaption/event.c270
-rw-r--r--gst/closedcaption/event.h794
-rw-r--r--gst/closedcaption/format.h408
-rw-r--r--gst/closedcaption/hamm-tables.h307
-rw-r--r--gst/closedcaption/hamm.c164
-rw-r--r--gst/closedcaption/hamm.h233
-rw-r--r--gst/closedcaption/lang.c907
-rw-r--r--gst/closedcaption/lang.h164
12 files changed, 5975 insertions, 0 deletions
diff --git a/gst/closedcaption/Makefile.am b/gst/closedcaption/Makefile.am
index a2a6cd1fb..625c54b18 100644
--- a/gst/closedcaption/Makefile.am
+++ b/gst/closedcaption/Makefile.am
@@ -2,14 +2,25 @@ plugin_LTLIBRARIES = libgstclosedcaption.la
zvbi_sources = \
bit_slicer.c \
+ cc608_decoder.c \
decoder.c \
+ event.c \
+ hamm.c \
+ lang.c \
raw_decoder.c \
sampling_par.c
zvbi_headers = \
bcd.h \
bit_slicer.h \
+ cc608_decoder.h \
decoder.h \
+ event-priv.h \
+ event.h \
+ format.h \
+ hamm.h \
+ hamm-tables.h \
+ lang.h \
macros.h \
misc.h \
raw_decoder.h \
diff --git a/gst/closedcaption/cc608_decoder.c b/gst/closedcaption/cc608_decoder.c
new file mode 100644
index 000000000..5ebec724a
--- /dev/null
+++ b/gst/closedcaption/cc608_decoder.c
@@ -0,0 +1,2482 @@
+/*
+ * libzvbi - EIA 608-B Closed Caption decoder
+ *
+ * Copyright (C) 2008 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: cc608_decoder.c,v 1.2 2009-12-14 23:43:23 mschimek Exp $ */
+
+/* This code is experimental and not yet part of the library.
+ Tests pending. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "hamm.h"
+#include "lang.h"
+#if 0
+/* Only used for vbi_fputs_iconv_ucs2() in _vbi_cc608_dump. Switch to glib
+ * method */
+#include "conv.h"
+#endif
+#include "format.h"
+#include "event-priv.h"
+#include "cc608_decoder.h"
+
+#ifndef CC608_DECODER_LOG_INPUT
+# define CC608_DECODER_LOG_INPUT 0
+#endif
+
+enum field_num
+{
+ FIELD_1 = 0,
+ FIELD_2,
+ MAX_FIELDS
+};
+
+#define UNKNOWN_CHANNEL 0
+#define MAX_CHANNELS 8
+
+/* 47 CFR 15.119 (d) Screen format. */
+
+#define FIRST_ROW 0
+#define LAST_ROW 14
+#define MAX_ROWS 15
+
+#define ALL_ROWS_MASK ((1 << MAX_ROWS) - 1)
+
+/* Note these are visible columns. We also buffer a zeroth column
+ which is implied by 47 CFR 15.119 and EIA 608-B to set the default
+ or PAC attributes for column one, and visible as a solid space if
+ padding for legibility is enabled. We do not reserve a 33rd column
+ for padding since format_row() can just append a space to the
+ output. */
+#define FIRST_COLUMN 1
+#define LAST_COLUMN 32
+#define MAX_COLUMNS 32
+
+struct timestamp
+{
+ /* System time when the event occured, zero if no event
+ occured yet. */
+ double sys;
+
+ /* ISO 13818-1 Presentation Time Stamp of the event. Unit is
+ 1/90000 second. Only the 33 least significant bits are
+ valid. < 0 if no event occured yet. */
+ int64_t pts;
+};
+
+struct channel
+{
+ /**
+ * [0] and [1] are the displayed and non-displayed buffer as
+ * defined in 47 CFR 15.119, and selected by @a
+ * displayed_buffer below. [2] is a snapshot of the displayed
+ * buffer at the last stream event.
+ *
+ * XXX Text channels don't need buffer[2] and buffer[3], we're
+ * wasting memory.
+ */
+ uint16_t buffer[3][MAX_ROWS][1 + MAX_COLUMNS];
+
+ /**
+ * For buffer[0 ... 2], if bit 1 << row is set this row
+ * contains displayable characters, spacing or non-spacing
+ * attributes. (Special character 0x1139 "transparent space"
+ * is not a displayable character.) This information is
+ * intended to speed up copying, erasing and formatting.
+ */
+ unsigned int dirty[3];
+
+ /** Index of the displayed buffer, 0 or 1. */
+ unsigned int displayed_buffer;
+
+ /**
+ * Cursor position: FIRST_ROW ... LAST_ROW and
+ * FIRST_COLUMN ... LAST_COLUMN.
+ */
+ unsigned int curr_row;
+ unsigned int curr_column;
+
+ /**
+ * Text window height in VBI_CC608_MODE_ROLL_UP. The top row
+ * of the window is curr_row - window_rows + 1, the bottom row
+ * is curr_row.
+ *
+ * Note: curr_row - window_rows + 1 may be < FIRST_ROW, this
+ * must be clipped before using window_rows:
+ *
+ * actual_rows = MIN (curr_row - FIRST_ROW + 1, window_rows);
+ *
+ * We won't do that at the RUx command because usually a PAC
+ * command follows which may change curr_row.
+ */
+ unsigned int window_rows;
+
+ /* Most recently received PAC command. */
+ unsigned int last_pac;
+
+ /**
+ * This variable counts successive transmissions of the
+ * letters A to Z. It is reset to zero upon reception of any
+ * letter a to z.
+ *
+ * Some stations do not transmit EIA 608-B extended characters
+ * and except for N with tilde the standard and special
+ * character sets contain only lower case accented
+ * characters. We force these characters to upper case if this
+ * variable indicates live caption, where the text is usually
+ * all upper case.
+ */
+ unsigned int uppercase_predictor;
+
+ /** Current caption mode or VBI_CC608_MODE_UNKNOWN. */
+ _vbi_cc608_mode mode;
+
+ /**
+ * The time when we last received data for this
+ * channel. Intended to detect if this caption channel is
+ * active.
+ */
+ struct timestamp timestamp;
+
+ /**
+ * The time when we received the first (but not necessarily
+ * leftmost) character in the current row. Unless the mode is
+ * VBI_CC608_MODE_POP_ON the next stream event will carry this
+ * timestamp.
+ */
+ struct timestamp timestamp_c0;
+};
+
+struct _vbi_cc608_decoder
+{
+ /**
+ * Decoder state. We decode all channels in parallel, this way
+ * clients can switch between channels without data loss or
+ * capture multiple channels with a single decoder instance.
+ *
+ * Also 47 CFR 15.119 and EIA 608-C require us to remember the
+ * cursor position on each channel.
+ */
+ struct channel channel[MAX_CHANNELS];
+
+ /**
+ * Current channel, switched by caption control codes. Can be
+ * one of @c VBI_CAPTION_CC1 (1) ... @c VBI_CAPTION_CC4 (4) or
+ * @c VBI_CAPTION_T1 (5) ... @c VBI_CAPTION_T4 (8) or @c
+ * UNKNOWN_CHANNEL (0) if no channel number was received yet.
+ */
+ vbi_pgno curr_ch_num[MAX_FIELDS];
+
+ /**
+ * Caption control codes (two bytes) may repeat once for error
+ * correction. -1 if no repeated control code can be expected.
+ */
+ int expect_ctrl[MAX_FIELDS][2];
+
+ /**
+ * Receiving XDS data, as opposed to caption / ITV data.
+ * There's no XDS data on the first field, we just use an
+ * array for convenience.
+ */
+ vbi_bool in_xds[MAX_FIELDS];
+
+ /**
+ * Pointer into the channel[] array if a display update event
+ * shall be sent at the end of this iteration, @c NULL
+ * otherwise. Purpose is to suppress an event for the first of
+ * two displayable characters in a caption byte pair.
+ */
+ struct channel *event_pending;
+
+ /**
+ * Remembers past parity errors: One bit for each call of
+ * vbi_cc608_decoder_feed(), most recent result in lsb. The
+ * idea is to disable the decoder if we detect too many
+ * errors.
+ */
+ unsigned int error_history;
+
+ /**
+ * The time when we last received data, including NUL bytes.
+ * Intended to detect if the station transmits any data on
+ * line 21 or 284.
+ */
+ struct timestamp timestamp;
+
+ _vbi_event_handler_list handlers;
+};
+
+/* 47 CFR 15.119 Mid-Row Codes, Preamble Address Codes.
+ EIA 608-B Table 3. */
+static const vbi_color color_map[8] = {
+ VBI_WHITE, VBI_GREEN, VBI_BLUE, VBI_CYAN,
+ VBI_RED, VBI_YELLOW, VBI_MAGENTA,
+
+ /* Note Mid-Row Codes interpret this value as "Italics"; PACs
+ as "White Italics"; Background Attributes as "Black". */
+ VBI_BLACK
+};
+
+/* *INDENT-OFF* */
+/* 47 CFR 15.119 Preamble Address Codes. */
+static const int8_t
+pac_row_map [16] = {
+ /* 0 */ 10, /* 0x1040 */
+ /* 1 */ -1, /* no function */
+ /* 2 */ 0, 1, 2, 3, /* 0x1140 ... 0x1260 */
+ /* 6 */ 11, 12, 13, 14, /* 0x1340 ... 0x1460 */
+ /* 10 */ 4, 5, 6, 7, 8, 9 /* 0x1540 ... 0x1760 */
+};
+
+/* *INDENT-ON* */
+
+#if 0
+/* Switch to glib iconv system and gst debugging */
+/** @internal */
+void
+_vbi_cc608_dump (FILE * fp, unsigned int c1, unsigned int c2)
+{
+ const vbi_bool to_upper = FALSE;
+ const int repl_char = '?';
+ uint16_t ucs2_str[2];
+ unsigned int c;
+ unsigned int f;
+ unsigned int u;
+
+ assert (NULL != fp);
+
+ fprintf (fp, "%02X%02X %02X%c%02X%c",
+ c1 & 0xFF, c2 & 0xFF,
+ c1 & 0x7F, vbi_unpar8 (c1) < 0 ? '*' : ' ',
+ c2 & 0x7F, vbi_unpar8 (c2) < 0 ? '*' : ' ');
+
+ /* Note we ignore wrong parity. */
+ c1 &= 0x7F;
+ c2 &= 0x7F;
+
+ if (0 == c1) {
+ fputs (" null\n", fp);
+ return;
+ } else if (c1 < 0x10) {
+ if (0x0F == c1)
+ fputs (" XDS packet end\n", fp);
+ else
+ fputs (" XDS packet start/continue\n", fp);
+ return;
+ } else if (c1 >= 0x20) {
+ unsigned int i = 0;
+
+ fputs (" '", fp);
+ ucs2_str[i++] = vbi_caption_unicode (c1, to_upper);
+ if (c2 >= 0x20) {
+ ucs2_str[i++] = vbi_caption_unicode (c2, to_upper);
+ }
+ vbi_fputs_iconv_ucs2 (fp, vbi_locale_codeset (), ucs2_str, i, repl_char);
+ fprintf (fp, "'%s\n", (c2 > 0 && c2 < 0x20) ? " invalid" : "");
+ return;
+ }
+
+ /* Some common bits. */
+ c = (c1 >> 3) & 1; /* channel */
+ f = c1 & 1; /* field */
+ u = c2 & 1; /* underline */
+
+ if (c2 < 0x20) {
+ fputs (" invalid\n", fp);
+ return;
+ } else if (c2 >= 0x40) {
+ unsigned int rrrr;
+ unsigned int xxx;
+ int row;
+
+ /* Preamble Address Codes -- 001 crrr 1ri xxxu */
+
+ rrrr = (c1 & 7) * 2 + ((c2 & 0x20) > 0);
+ xxx = (c2 >> 1) & 7;
+
+ row = pac_row_map[rrrr];
+ if (c2 & 0x10) {
+ fprintf (fp, " PAC ch=%u row=%d column=%u u=%u\n", c, row, xxx * 4, u);
+ } else {
+ fprintf (fp, " PAC ch=%u row=%d color=%u u=%u\n", c, row, xxx, u);
+ }
+
+ return;
+ }
+
+ /* Control codes -- 001 caaa 01x bbbu */
+
+ switch (c1 & 0x07) {
+ case 0:
+ if (c2 < 0x30) {
+ static const char mnemo[16 * 4] =
+ "BWO\0BWS\0BGO\0BGS\0"
+ "BBO\0BBS\0BCO\0BCS\0" "BRO\0BRS\0BYO\0BYS\0" "BMO\0BMS\0BAO\0BAS";
+
+ /* Backgr. Attr. Codes -- 001 c000 010 xxxt */
+
+ fprintf (fp, " %s ch=%u\n", mnemo + (c2 & 0xF) * 4, c);
+ return;
+ }
+ break;
+
+ case 1:
+ if (c2 < 0x30) {
+ unsigned int xxx;
+
+ /* Mid-Row Codes -- 001 c001 010 xxxu */
+
+ xxx = (c2 >> 1) & 7;
+ fprintf (fp, " mid-row ch=%u color=%u u=%u\n", c, xxx, u);
+ } else {
+ /* Special Characters -- 001 c001 011 xxxx */
+
+ fprintf (fp, " special character ch=%u '", c);
+ ucs2_str[0] = vbi_caption_unicode (0x1100 | c2, to_upper);
+ vbi_fputs_iconv_ucs2 (fp, vbi_locale_codeset (),
+ ucs2_str, 1, repl_char);
+ fputs ("'\n", fp);
+ }
+ return;
+
+ case 2: /* first group */
+ case 3: /* second group */
+ /* Extended Character Set -- 001 c01x 01x xxxx */
+
+ fprintf (fp, " extended character ch=%u '", c);
+ ucs2_str[0] = vbi_caption_unicode (c1 * 256 + c2, to_upper);
+ vbi_fputs_iconv_ucs2 (fp, vbi_locale_codeset (), ucs2_str, 1, repl_char);
+ fputs ("'\n", fp);
+ return;
+
+ case 4: /* f = 0 */
+ case 5: /* f = 1 */
+ if (c2 < 0x30) {
+ static const char mnemo[16 * 4] =
+ "RCL\0BS \0AOF\0AON\0"
+ "DER\0RU2\0RU3\0RU4\0" "FON\0RDC\0TR \0RTD\0" "EDM\0CR \0ENM\0EOC";
+
+ /* Misc. Control Codes -- 001 c10f 010 xxxx */
+
+ fprintf (fp, " %s ch=%u f=%u\n", mnemo + (c2 & 0xF) * 4, c, f);
+ return;
+ }
+ break;
+
+ case 6: /* reserved */
+ break;
+
+ case 7:
+ switch (c2) {
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ fprintf (fp, " TO%u ch=%u\n", c2 - 0x20, c);
+ return;
+
+ case 0x2D:
+ fprintf (fp, " BT ch=%u\n", c);
+ return;
+
+ case 0x2E:
+ fprintf (fp, " FA ch=%u\n", c);
+ return;
+
+ case 0x2F:
+ fprintf (fp, " FAU ch=%u\n", c);
+ return;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ fprintf (fp, " unknown\n");
+}
+#endif
+
+/* Future stuff. */
+enum
+{
+ VBI_UNDERLINE = (1 << 0),
+ VBI_ITALIC = (1 << 2),
+ VBI_FLASH = (1 << 3)
+};
+
+#if 0 /* UNUSED */
+_vbi_inline void
+vbi_char_copy_attr (struct vbi_char *cp1,
+ const struct vbi_char *cp2, unsigned int attr)
+{
+ if (attr & VBI_UNDERLINE)
+ cp1->underline = cp2->underline;
+ if (attr & VBI_ITALIC)
+ cp1->italic = cp2->italic;
+ if (attr & VBI_FLASH)
+ cp1->flash = cp2->flash;
+}
+#endif /* UNUSED */
+
+_vbi_inline void
+vbi_char_clear_attr (struct vbi_char *cp, unsigned int attr)
+{
+ if (attr & VBI_UNDERLINE)
+ cp->underline = 0;
+ if (attr & VBI_ITALIC)
+ cp->italic = 0;
+ if (attr & VBI_FLASH)
+ cp->flash = 0;
+}
+
+_vbi_inline void
+vbi_char_set_attr (struct vbi_char *cp, unsigned int attr)
+{
+ if (attr & VBI_UNDERLINE)
+ cp->underline = 1;
+ if (attr & VBI_ITALIC)
+ cp->italic = 1;
+ if (attr & VBI_FLASH)
+ cp->flash = 1;
+}
+
+#if 0 /* UNUSEd */
+_vbi_inline unsigned int
+vbi_char_has_attr (const struct vbi_char *cp, unsigned int attr)
+{
+ attr &= (VBI_UNDERLINE | VBI_ITALIC | VBI_FLASH);
+
+ if (0 == cp->underline)
+ attr &= ~VBI_UNDERLINE;
+ if (0 == cp->italic)
+ attr &= ~VBI_ITALIC;
+ if (0 == cp->flash)
+ attr &= ~VBI_FLASH;
+
+ return attr;
+}
+
+_vbi_inline unsigned int
+vbi_char_xor_attr (const struct vbi_char *cp1,
+ const struct vbi_char *cp2, unsigned int attr)
+{
+ attr &= (VBI_UNDERLINE | VBI_ITALIC | VBI_FLASH);
+
+ if (0 == (cp1->underline ^ cp2->underline))
+ attr &= ~VBI_UNDERLINE;
+ if (0 == (cp1->italic ^ cp2->italic))
+ attr &= ~VBI_ITALIC;
+ if (0 == (cp1->flash ^ cp2->flash))
+ attr &= ~VBI_FLASH;
+
+ return attr;
+}
+#endif /* UNUSED */
+
+static void
+timestamp_reset (struct timestamp *ts)
+{
+ ts->sys = 0.0;
+ ts->pts = -1;
+}
+
+static vbi_bool
+timestamp_is_set (const struct timestamp *ts)
+{
+ return (ts->pts >= 0 || ts->sys > 0.0);
+}
+
+static vbi_pgno
+channel_num (const _vbi_cc608_decoder * cd, const struct channel *ch)
+{
+ return (ch - cd->channel) + 1;
+}
+
+/* This implementation of character attributes is based on 47 CFR
+ 15.119 (h) and the following sections of EIA 608-B:
+
+ EIA 608-B Annex C.7 "Preamble Address Codes and Tab Offsets
+ (Regulatory/Preferred)": "In general, Preamble Address Codes (PACs)
+ have no immediate effect on the display. A major exception is the
+ receipt of a PAC during roll-up captioning. In that case, if the
+ base row designated in the PAC is not the same as the current base
+ row, the display shall be moved immediately to the new base
+ row. [...] An indenting PAC carries the attributes of white,
+ non-italicized, and it sets underlining on or off. Tab Offset
+ commands do not change these attributes. If an indenting PAC with
+ underline ON is received followed by a Tab Offset and by text, the
+ text shall be underlined (except as noted below). When a
+ displayable character is received, it is deposited at the current
+ cursor position. If there is already a displayable character in the
+ column immediately to the left, the new character assumes the
+ attributes of that character. The new character may be arriving as
+ the result of an indenting PAC (with or without a Tab Offset), and
+ that PAC may designate other attributes, but the new character is
+ forced to assume the attributes of the character immediately to its
+ left, and the PAC's attributes are ignored. If, when a displayable
+ character is received, it overwrites an existing PAC or mid-row
+ code, and there are already characters to the right of the new
+ character, these existing characters shall assume the same
+ attributes as the new character. This adoption can result in a
+ whole caption row suddenly changing color, underline, italics,
+ and/or flash attributes."
+
+ EIA 608-B Annex C.14 "Special Cases Regarding Attributes
+ (Normative)": "In most cases, Preamble Address Codes shall set
+ attributes for the caption elements they address. It is
+ theoretically possible for a service provider to use an indenting
+ PAC to start a row at Column 5 or greater, and then to use
+ Backspace to move the cursor to the left of the PAC into an area to
+ which no attributes have been assigned. It is also possible for a
+ roll-up row, having been created by a Carriage Return, to receive
+ characters with no PAC used to set attributes. In these cases, and
+ in any other case where no explicit attributes have been assigned,
+ the display shall be white, non-underlined, non-italicized, and
+ non-flashing. In case new displayable characters are received
+ immediately after a Delete to End of Row (DER), the display
+ attributes of the first deleted character shall remain in effect if
+ there is a displayable character to the left of the cursor;
+ otherwise, the most recently received PAC shall set the display
+ attributes."
+
+ 47 CFR 15.119 (n) clarifies that Special Character "transparent
+ space" is not a "displayable character". */
+
+/**
+ * @internal
+ * @param to_upper Convert the lower case Latin characters in the
+ * standard character set to upper case.
+ * @param padding Add spaces around words for improved legibility
+ * as defined in 47 CFR 15.119. If @c TRUE the resulting page will
+ * be 34 columns wide, otherwise 32 columns. The height is always 15
+ * rows.
+ * @param alpha Add an offset to the vbi_color of characters: +0 for
+ * opaque, +8 for translucent, +16 for transparent characters. Intended
+ * for formatting with an alpha color map.
+ */
+static void
+format_row (struct vbi_char *cp,
+ unsigned int max_columns,
+ const struct channel *ch,
+ unsigned int buffer,
+ unsigned int row, vbi_bool to_upper, vbi_bool padding, vbi_bool alpha)
+{
+ struct vbi_char ac;
+ struct vbi_char ac_ts;
+ vbi_char *end;
+ unsigned int i;
+
+ /* 47 CFR 15.119 (h)(1). EIA 608-B Section 6.4. */
+ CLEAR (ac);
+ ac.opacity = VBI_OPAQUE;
+ ac.foreground = VBI_WHITE;
+ ac.background = VBI_BLACK;
+
+ ac_ts = ac;
+ ac_ts.unicode = 0x20;
+ ac_ts.opacity = VBI_TRANSPARENT_SPACE;
+ if (alpha) {
+ ac_ts.foreground += 16;
+ ac_ts.background += 16;
+ }
+
+ end = cp + MAX_COLUMNS;
+ if (padding)
+ end += 2;
+
+ assert (end <= cp + max_columns);
+
+ /* Shortcut. */
+ if (0 == (ch->dirty[buffer] & (1 << row))) {
+ while (cp < end)
+ *cp++ = ac_ts;
+
+ return;
+ }
+
+ if (padding) {
+ *cp++ = ac_ts;
+ }
+
+ for (i = FIRST_COLUMN - 1; i <= LAST_COLUMN; ++i) {
+ unsigned int c;
+
+ ac.unicode = 0x20;
+
+ c = ch->buffer[buffer][row][i];
+ if (0 == c) {
+ if (padding
+ && VBI_TRANSPARENT_SPACE != cp[-1].opacity
+ && 0x20 != cp[-1].unicode) {
+ /* Append a space with the same colors
+ and opacity (opaque or transp.
+ backgr.) as the text to the left of
+ it. */
+ *cp++ = ac;
+ /* We don't underline spaces, see
+ below. */
+ vbi_char_clear_attr (cp - 1, -1);
+ } else if (i > 0) {
+ *cp++ = ac;
+ cp[-1].opacity = VBI_TRANSPARENT_SPACE;
+ if (alpha) {
+ cp[-1].foreground = 16 + (ac.foreground & 7);
+ cp[-1].background = 16 + (ac.background & 7);
+ }
+ }
+
+ continue;
+ } else if (c < 0x1020) {
+ if (padding && VBI_TRANSPARENT_SPACE == cp[-1].opacity) {
+ /* Prepend a space with the same
+ colors and opacity (opaque or
+ transp. backgr.) as the text to the
+ right of it. */
+ cp[-1] = ac;
+ /* We don't underline spaces, see
+ below. */
+ vbi_char_clear_attr (cp - 1, -1);
+ }
+
+ if ((c >= 'a' && c <= 'z')
+ || 0x7E == c /* n with tilde */ ) {
+ /* We do not force these characters to
+ upper case because the standard
+ character set includes upper case
+ versions of these characters and
+ lower case was probably
+ deliberately transmitted. */
+ ac.unicode = vbi_caption_unicode (c, /* to_upper */ FALSE);
+ } else {
+ ac.unicode = vbi_caption_unicode (c, to_upper);
+ }
+ } else if (c < 0x1040) {
+ unsigned int color;
+
+ /* Backgr. Attr. Codes -- 001 c000 010 xxxt */
+ /* EIA 608-B Section 6.2. */
+
+ /* This is a set-at spacing attribute. */
+
+ color = (c >> 1) & 7;
+ ac.background = color_map[color];
+
+ if (c & 0x0001) {
+ if (alpha)
+ ac.background += 8;
+ ac.opacity = VBI_SEMI_TRANSPARENT;
+ } else {
+ ac.opacity = VBI_OPAQUE;
+ }
+ } else if (c < 0x1120) {
+ /* Preamble Address Codes -- 001 crrr 1ri xxxu */
+
+ /* PAC is a non-spacing attribute and only
+ stored in the buffer at the addressed
+ column minus one if it replaces a
+ transparent space (EIA 608-B Annex C.7,
+ C.14). There's always a transparent space
+ to the left of the first column but we show
+ this zeroth column only if padding is
+ enabled. */
+ if (padding
+ && VBI_TRANSPARENT_SPACE != cp[-1].opacity
+ && 0x20 != cp[-1].unicode) {
+ /* See 0 == c. */
+ *cp++ = ac;
+ vbi_char_clear_attr (cp - 1, -1);
+ } else if (i > 0) {
+ *cp++ = ac;
+ cp[-1].opacity = VBI_TRANSPARENT_SPACE;
+ if (alpha) {
+ cp[-1].foreground = 16 + (ac.foreground & 7);
+ cp[-1].background = 16 + (ac.background & 7);
+ }
+ }
+
+ vbi_char_clear_attr (&ac, VBI_UNDERLINE | VBI_ITALIC);
+ if (c & 0x0001)
+ vbi_char_set_attr (&ac, VBI_UNDERLINE);
+ if (c & 0x0010) {
+ ac.foreground = VBI_WHITE;
+ } else {
+ unsigned int color;
+
+ color = (c >> 1) & 7;
+ if (7 == color) {
+ ac.foreground = VBI_WHITE;
+ vbi_char_set_attr (&ac, VBI_ITALIC);
+ } else {
+ ac.foreground = color_map[color];
+ }
+ }
+
+ continue;
+ } else if (c < 0x1130) {
+ unsigned int color;
+
+ /* Mid-Row Codes -- 001 c001 010 xxxu */
+ /* 47 CFR 15.119 Mid-Row Codes table,
+ (h)(1)(ii), (h)(1)(iii). */
+
+ /* 47 CFR 15.119 (h)(1)(i), EIA 608-B Section
+ 6.2: Mid-Row codes, FON, BT, FA and FAU are
+ set-at spacing attributes. */
+
+ vbi_char_clear_attr (&ac, -1);
+ if (c & 0x0001)
+ vbi_char_set_attr (&ac, VBI_UNDERLINE);
+ color = (c >> 1) & 7;
+ if (7 == color) {
+ vbi_char_set_attr (&ac, VBI_ITALIC);
+ } else {
+ ac.foreground = color_map[color];
+ }
+ } else if (c < 0x1220) {
+ /* Special Characters -- 001 c001 011 xxxx */
+ /* 47 CFR 15.119 Character Set Table. */
+
+ if (padding && VBI_TRANSPARENT_SPACE == cp[-1].opacity) {
+ cp[-1] = ac;
+ vbi_char_clear_attr (cp - 1, -1);
+ }
+
+ assert (0x1139 /* transparent space */ != c);
+ ac.unicode = vbi_caption_unicode (c, to_upper);
+ } else if (c < 0x1428) {
+ /* Extended Character Set -- 001 c01x 01x xxxx */
+ /* EIA 608-B Section 6.4.2 */
+
+ if (padding && VBI_TRANSPARENT_SPACE == cp[-1].opacity) {
+ cp[-1] = ac;
+ vbi_char_clear_attr (cp - 1, -1);
+ }
+
+ /* We do not force these characters to upper
+ case because the extended character set
+ includes upper case versions of all letters
+ and lower case was probably deliberately
+ transmitted. */
+ ac.unicode = vbi_caption_unicode (c, /* to_upper */ FALSE);
+
+ if (0x2500 == (ac.unicode & 0xFFE0)) {
+ /* Box drawing characters probably
+ shouldn't have these attributes. */
+ *cp++ = ac;
+ vbi_char_clear_attr (cp - 1, (VBI_ITALIC | VBI_UNDERLINE));
+ continue;
+ }
+ } else if (c < 0x172D) {
+ /* FON Flash On -- 001 c10f 010 1000 */
+ /* 47 CFR 15.119 (h)(1)(iii). */
+
+ vbi_char_set_attr (&ac, VBI_FLASH);
+ } else if (c < 0x172E) {
+ /* BT Background Transparent -- 001 c111 010 1101 */
+ /* EIA 608-B Section 6.4. */
+
+ ac.opacity = VBI_TRANSPARENT_FULL;
+ if (alpha) {
+ ac.background = 16 + (ac.background & 7);
+ }
+ } else if (c <= 0x172F) {
+ /* FA Foreground Black -- 001 c111 010 111u */
+ /* EIA 608-B Section 6.4. */
+
+ vbi_char_clear_attr (&ac, -1);
+ if (c & 0x0001)
+ vbi_char_set_attr (&ac, VBI_UNDERLINE);
+ ac.foreground = VBI_BLACK;
+ }
+
+ *cp++ = ac;
+
+ /* 47 CFR 15.119 and EIA 608-B are silent about
+ underlined spaces, but considering the example in
+ 47 CFR (h)(1)(iv) which would produce something
+ ugly like "__text" I suppose we should not
+ underline them. For good measure we also clear the
+ invisible italic and flash attribute. */
+ if (0x20 == ac.unicode)
+ vbi_char_clear_attr (cp - 1, -1);
+ }
+
+ if (padding) {
+ ac.unicode = 0x20;
+ vbi_char_clear_attr (&ac, -1);
+
+ if (VBI_TRANSPARENT_SPACE != cp[-1].opacity && 0x20 != cp[-1].unicode) {
+ *cp++ = ac;
+ } else {
+ ac.opacity = VBI_TRANSPARENT_SPACE;
+ ac.foreground = 16 + (ac.foreground & 7);
+ ac.background = 16 + (ac.background & 7);
+ *cp++ = ac;
+ }
+ }
+
+ assert (cp == end);
+}
+
+#ifndef VBI_RGBA
+# define VBI_RGBA(r, g, b) \
+ ((((r) & 0xFF) << 0) | (((g) & 0xFF) << 8) \
+ | (((b) & 0xFF) << 16) | (0xFF << 24))
+#endif
+
+/**
+ * @param cd Caption decoder allocated with vbi_cc608_decoder_new().
+ * @param pg The display state will be stored here.
+ * @param channel Caption channel @c VBI_CHANNEL_CC1 ...
+ * @c VBI_CHANNEL_CC4 or @c VBI_CHANNEL_T1 ... @c VBI_CHANNEL_T4.
+ * @param padding Add spaces around words for improved legibility
+ * as defined in 47 CFR 15.119. If @c TRUE the resulting page will
+ * be 34 columns wide, otherwise 32 columns. The height is always 15
+ * rows.
+ *
+ * This function stores the current display state of the given caption
+ * channel in the @a pg structure. (There is no channel switch
+ * function; all channels are decoded simultaneously.)
+ *
+ * All fields of @a pg will be initialized but the @a vbi, @a nuid, @a
+ * dirty, @a nav_link, @a nav_index, and @a font fields will not
+ * contain useful information.
+ *
+ * @returns
+ * @c FALSE on failure: The channel number is out of bounds.
+ */
+vbi_bool
+_vbi_cc608_decoder_get_page (_vbi_cc608_decoder * cd,
+ vbi_page * pg, vbi_pgno channel, vbi_bool padding)
+{
+ static const vbi_rgba default_color_map[3 * 8] = {
+ 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF,
+ 0xFFFF0000, 0xFFFF00FF, 0xFFFFFF00, 0xFFFFFFFF,
+
+ 0x80000000, 0x800000FF, 0x8000FF00, 0x8000FFFF,
+ 0x80FF0000, 0x80FF00FF, 0x80FFFF00, 0x80FFFFFF,
+
+ 0x00000000, 0x000000FF, 0x0000FF00, 0x0000FFFF,
+ 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00FFFFFF,
+ };
+ vbi_char *cp;
+ struct channel *ch;
+ unsigned int row;
+ vbi_bool to_upper;
+
+ assert (NULL != cd);
+ assert (NULL != pg);
+
+ if (channel < VBI_CAPTION_CC1 || channel > VBI_CAPTION_T4)
+ return FALSE;
+
+ ch = &cd->channel[channel - VBI_CAPTION_CC1];
+
+ CLEAR (*pg);
+
+ pg->pgno = channel;
+
+ pg->rows = MAX_ROWS;
+
+ if (padding)
+ pg->columns = MAX_COLUMNS + 2;
+ else
+ pg->columns = MAX_COLUMNS;
+
+ assert (N_ELEMENTS (pg->text) >= MAX_ROWS * (MAX_COLUMNS + 2));
+
+ pg->dirty.y1 = LAST_ROW;
+
+ pg->screen_opacity = VBI_TRANSPARENT_SPACE;
+
+ assert (sizeof (pg->color_map) >= sizeof (default_color_map));
+ memcpy (pg->color_map, default_color_map, sizeof (default_color_map));
+
+ cp = pg->text;
+
+ to_upper = (ch->uppercase_predictor > 3);
+
+ for (row = 0; row < MAX_ROWS; ++row) {
+ format_row (cp, pg->columns,
+ ch, ch->displayed_buffer, row, to_upper, padding,
+ /* alpha */ TRUE);
+
+ cp += pg->columns;
+ }
+
+ return TRUE;
+}
+
+static void
+display_event (_vbi_cc608_decoder * cd,
+ struct channel *ch, _vbi_cc608_event_flags flags)
+{
+ vbi_event ev;
+ struct _vbi_event_cc608_page cc608;
+
+ CLEAR (ev);
+
+ ev.type = _VBI_EVENT_CC608;
+ ev.ev._cc608 = &cc608;
+ cc608.channel = channel_num (cd, ch);
+ cc608.mode = ch->mode;
+ cc608.flags = flags;
+
+ _vbi_event_handler_list_send (&cd->handlers, &ev);
+}
+
+/* This decoder is mainly designed to overlay caption onto live video,
+ but to create transcripts we also offer an event every time a line
+ of caption is complete. The event occurs when certain control codes
+ are received:
+
+ In POP_ON mode we send the event upon reception of EOC, which swaps
+ the displayed and non-displayed memory.
+
+ In ROLL_UP and TEXT mode captioners are not expected to display new
+ text by erasing and overwriting a row with PAC, TOx, BS and DER so
+ we do not send an event on reception of these codes. In ROLL_UP
+ mode CR, EDM, EOC, RCL and RDC complete a line: CR moves the cursor
+ to a new row, EDM erases the displayed memory. The remaining codes
+ switch to POP_ON or PAINT_ON mode. In TEXT mode CR and TR are our
+ line completion indicators. CR works as above and TR erases the
+ displayed memory. EDM, EOC, RDC, RCL and RUx have no effect on Text
+ channels according to EIA 608.
+
+ In PAINT_ON mode RDC never erases the displayed memory and CR has
+ no function. Instead captioners can freely position the cursor and
+ erase or overwrite (parts of) rows with PAC, TOx, BS and DER, or
+ erase all rows with EDM. We send an event on PAC, EDM, EOC, RCL and
+ RUx, provided the characters (including spacing attributes) in the
+ current row changed since the last event: PAC is the only control
+ code which can move the cursor to the left and/or to a new row, and
+ likely to introduce a new line. EOC, RCL and RUx switch to POP_ON
+ or ROLL_UP mode. */
+
+static void
+stream_event (_vbi_cc608_decoder * cd,
+ struct channel *ch, unsigned int first_row, unsigned int last_row)
+{
+ vbi_event ev;
+ struct _vbi_event_cc608_stream cc608_stream;
+ unsigned int row;
+ vbi_bool to_upper;
+
+ CLEAR (ev);
+
+ ev.type = _VBI_EVENT_CC608_STREAM;
+ ev.ev._cc608_stream = &cc608_stream;
+ cc608_stream.capture_time = ch->timestamp_c0.sys;
+ cc608_stream.pts = ch->timestamp_c0.pts;
+ cc608_stream.channel = channel_num (cd, ch);
+ cc608_stream.mode = ch->mode;
+
+ to_upper = (ch->uppercase_predictor > 3);
+
+ for (row = first_row; row <= last_row; ++row) {
+ unsigned int end;
+
+ format_row (cc608_stream.text,
+ N_ELEMENTS (cc608_stream.text), ch, ch->displayed_buffer, row, to_upper,
+ /* padding */ FALSE,
+ /* alpha */ FALSE);
+
+ for (end = N_ELEMENTS (cc608_stream.text); end > 0; --end) {
+ if (VBI_TRANSPARENT_SPACE != cc608_stream.text[end - 1].opacity)
+ break;
+ }
+
+ if (0 == end)
+ continue;
+
+ _vbi_event_handler_list_send (&cd->handlers, &ev);
+ }
+
+ timestamp_reset (&ch->timestamp_c0);
+}
+
+static void
+put_char (_vbi_cc608_decoder * cd,
+ struct channel *ch, int c, vbi_bool displayable, vbi_bool backspace)
+{
+ uint16_t *text;
+ unsigned int curr_buffer;
+ unsigned int row;
+ unsigned int column;
+
+ /* 47 CFR Section 15.119 (f)(1), (f)(2), (f)(3). */
+ curr_buffer = ch->displayed_buffer ^ (_VBI_CC608_MODE_POP_ON == ch->mode);
+
+ row = ch->curr_row;
+ column = ch->curr_column;
+
+ if (unlikely (backspace)) {
+ /* 47 CFR 15.119 (f)(1)(vi), (f)(2)(ii),
+ (f)(3)(i). EIA 608-B Section 6.4.2, 7.4. */
+ if (column > FIRST_COLUMN)
+ --column;
+ } else {
+ /* 47 CFR 15.119 (f)(1)(v), (f)(1)(vi), (f)(2)(ii),
+ (f)(3)(i). EIA 608-B Section 7.4. */
+ if (column < LAST_COLUMN)
+ ch->curr_column = column + 1;
+ }
+
+ text = &ch->buffer[curr_buffer][row][0];
+ text[column] = c;
+
+ /* Send a display update event when the displayed buffer of
+ the current channel changed, but no more than once for each
+ pair of Closed Caption bytes. */
+ /* XXX This may not be a visible change, but such cases are
+ rare and we'd probably need a function almost as complex as
+ format_row() to find out. */
+ if (_VBI_CC608_MODE_POP_ON != ch->mode) {
+ cd->event_pending = ch;
+ }
+
+ if (likely (displayable)) {
+ /* EIA 608-B Annex C.7, C.14. */
+ if (FIRST_COLUMN == column || 0 == text[column - 1]) {
+ /* Note last_pac may be 0 as well. */
+ text[column - 1] = ch->last_pac;
+ }
+
+ if (c >= 'a' && c <= 'z') {
+ ch->uppercase_predictor = 0;
+ } else if (c >= 'A' && c <= 'Z') {
+ unsigned int up;
+
+ up = ch->uppercase_predictor + 1;
+ if (up > 0)
+ ch->uppercase_predictor = up;
+ }
+ } else if (unlikely (0 == c)) {
+ unsigned int i;
+
+ /* This is special character "transparent space". */
+
+ for (i = FIRST_COLUMN; i <= LAST_COLUMN; ++i)
+ c |= ch->buffer[curr_buffer][row][i];
+
+ ch->dirty[curr_buffer] &= ~((0 == c) << row);
+
+ return;
+ } else {
+ /* This is a spacing attribute. */
+
+ /* EIA 608-B Annex C.7, C.14. */
+ if (FIRST_COLUMN == column || 0 == text[column - 1]) {
+ /* Note last_pac may be 0 as well. */
+ text[column - 1] = ch->last_pac;
+ }
+ }
+
+ assert (sizeof (ch->dirty[0]) * 8 - 1 >= MAX_ROWS);
+ ch->dirty[curr_buffer] |= 1 << row;
+
+ if (!timestamp_is_set (&ch->timestamp_c0)) {
+ ch->timestamp_c0 = cd->timestamp;
+ }
+}
+
+static void
+ext_control_code (_vbi_cc608_decoder * cd, struct channel *ch, unsigned int c2)
+{
+ unsigned int column;
+
+ switch (c2) {
+ case 0x21: /* TO1 */
+ case 0x22: /* TO2 */
+ case 0x23: /* TO3 Tab Offset -- 001 c111 010 00xx */
+ /* 47 CFR 15.119 (e)(1)(ii). EIA 608-B Section 7.4,
+ Annex C.7. */
+ column = ch->curr_column + (c2 & 3);
+ ch->curr_column = MIN (column, (unsigned int) LAST_COLUMN);
+ break;
+
+ case 0x24: /* Select standard character set in normal size */
+ case 0x25: /* Select standard character set in double size */
+ case 0x26: /* Select first private character set */
+ case 0x27: /* Select second private character set */
+ case 0x28: /* Select character set GB 2312-80 (Chinese) */
+ case 0x29: /* Select character set KSC 5601-1987 (Korean) */
+ case 0x2A: /* Select first registered character set. */
+ /* EIA 608-B Section 6.3 Closed Group Extensions. */
+ break;
+
+ case 0x2D: /* BT Background Transparent -- 001 c111 010 1101 */
+ case 0x2E: /* FA Foreground Black -- 001 c111 010 1110 */
+ case 0x2F: /* FAU Foregr. Black Underl. -- 001 c111 010 1111 */
+ /* EIA 608-B Section 6.2. */
+ put_char (cd, ch, 0x1700 | c2,
+ /* displayable */ FALSE,
+ /* backspace */ TRUE);
+ break;
+
+ default:
+ /* 47 CFR Section 15.119 (j): Ignore. */
+ break;
+ }
+}
+
+/* Send a stream event if the current row has changed since the last
+ stream event. This is necessary in paint-on mode where CR has no
+ function and captioners can freely position the cursor to erase or
+ overwrite (parts of) rows. */
+static void
+stream_event_if_changed (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+ unsigned int i;
+
+ curr_buffer = ch->displayed_buffer;
+ row = ch->curr_row;
+
+ if (0 == (ch->dirty[curr_buffer] & (1 << row)))
+ return;
+
+ for (i = FIRST_COLUMN; i <= LAST_COLUMN; ++i) {
+ unsigned int c1;
+ unsigned int c2;
+
+ c1 = ch->buffer[curr_buffer][row][i];
+ if (c1 >= 0x1040) {
+ if (c1 < 0x1120) {
+ c1 = 0; /* PAC -- non-spacing */
+ } else if (c1 < 0x1130 || c1 >= 0x1428) {
+ /* MR, FON, BT, FA, FAU -- spacing */
+ c1 = 0x20;
+ }
+ }
+
+ c2 = ch->buffer[2][row][i];
+ if (c2 >= 0x1040) {
+ if (c2 < 0x1120) {
+ c2 = 0;
+ } else if (c2 < 0x1130 || c2 >= 0x1428) {
+ c1 = 0x20;
+ }
+ }
+
+ if (c1 != c2) {
+ stream_event (cd, ch, row, row);
+
+ memcpy (ch->buffer[2][row],
+ ch->buffer[curr_buffer][row], sizeof (ch->buffer[0][0]));
+
+ ch->dirty[2] = ch->dirty[curr_buffer];
+
+ return;
+ }
+ }
+}
+
+static void
+end_of_caption (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+
+ /* EOC End Of Caption -- 001 c10f 010 1111 */
+
+ curr_buffer = ch->displayed_buffer;
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_UNKNOWN:
+ case _VBI_CC608_MODE_POP_ON:
+ break;
+
+ case _VBI_CC608_MODE_ROLL_UP:
+ row = ch->curr_row;
+ if (0 != (ch->dirty[curr_buffer] & (1 << row)))
+ stream_event (cd, ch, row, row);
+ break;
+
+ case _VBI_CC608_MODE_PAINT_ON:
+ stream_event_if_changed (cd, ch);
+ break;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* Not reached. (ch is a caption channel.) */
+ return;
+ }
+
+ ch->displayed_buffer = curr_buffer ^= 1;
+
+ /* 47 CFR Section 15.119 (f)(2). */
+ ch->mode = _VBI_CC608_MODE_POP_ON;
+
+ if (0 != ch->dirty[curr_buffer]) {
+ ch->timestamp_c0 = cd->timestamp;
+
+ stream_event (cd, ch, FIRST_ROW, LAST_ROW);
+
+ display_event (cd, ch, /* flags */ 0);
+ }
+}
+
+static void
+carriage_return (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+ unsigned int window_rows;
+ unsigned int first_row;
+
+ /* CR Carriage Return -- 001 c10f 010 1101 */
+
+ curr_buffer = ch->displayed_buffer;
+ row = ch->curr_row;
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_UNKNOWN:
+ return;
+
+ case _VBI_CC608_MODE_ROLL_UP:
+ /* 47 CFR Section 15.119 (f)(1)(iii). */
+ ch->curr_column = FIRST_COLUMN;
+
+ /* 47 CFR 15.119 (f)(1): "The cursor always remains on
+ the base row." */
+
+ /* XXX Spec? */
+ ch->last_pac = 0;
+
+ /* No event if the buffer contains only
+ TRANSPARENT_SPACEs. */
+ if (0 == ch->dirty[curr_buffer])
+ return;
+
+ window_rows = MIN (row + 1 - FIRST_ROW, ch->window_rows);
+ break;
+
+ case _VBI_CC608_MODE_POP_ON:
+ case _VBI_CC608_MODE_PAINT_ON:
+ /* 47 CFR 15.119 (f)(2)(i), (f)(3)(i): No effect. */
+ return;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* 47 CFR Section 15.119 (f)(1)(iii). */
+ ch->curr_column = FIRST_COLUMN;
+
+ /* XXX Spec? */
+ ch->last_pac = 0;
+
+ /* EIA 608-B Section 7.4: "When Text Mode has
+ initially been selected and the specified Text
+ memory is empty, the cursor starts at the topmost
+ row, Column 1, and moves down to Column 1 on the
+ next row each time a Carriage Return is received
+ until the last available row is reached. A variety
+ of methods may be used to accomplish the scrolling,
+ provided that the text is legible while moving. For
+ example, as soon as all of the available rows of
+ text are on the screen, Text Mode switches to the
+ standard roll-up type of presentation." */
+
+ if (LAST_ROW != row) {
+ if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
+ stream_event (cd, ch, row, row);
+ }
+
+ ch->curr_row = row + 1;
+
+ return;
+ }
+
+ /* No event if the buffer contains all
+ TRANSPARENT_SPACEs. */
+ if (0 == ch->dirty[curr_buffer])
+ return;
+
+ window_rows = MAX_ROWS;
+
+ break;
+ }
+
+ /* 47 CFR Section 15.119 (f)(1)(iii). */
+
+ if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
+ stream_event (cd, ch, row, row);
+ }
+
+ first_row = row + 1 - window_rows;
+ memmove (ch->buffer[curr_buffer][first_row],
+ ch->buffer[curr_buffer][first_row + 1],
+ (window_rows - 1) * sizeof (ch->buffer[0][0]));
+
+ ch->dirty[curr_buffer] >>= 1;
+
+ memset (ch->buffer[curr_buffer][row], 0, sizeof (ch->buffer[0][0]));
+
+ /* See the description of VBI_CC608_START_ROLLING and
+ test/caption for the expected effect. */
+ display_event (cd, ch, _VBI_CC608_START_ROLLING);
+}
+
+static void
+erase_memory (_vbi_cc608_decoder * cd, struct channel *ch, unsigned int buffer)
+{
+ if (0 != ch->dirty[buffer]) {
+ CLEAR (ch->buffer[buffer]);
+
+ ch->dirty[buffer] = 0;
+
+ if (buffer == ch->displayed_buffer)
+ display_event (cd, ch, /* flags */ 0);
+ }
+}
+
+static void
+erase_displayed_memory (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int row;
+
+ /* EDM Erase Displayed Memory -- 001 c10f 010 1100 */
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_UNKNOWN:
+ /* We have not received EOC, RCL, RDC or RUx yet, but
+ ch is valid. */
+ break;
+
+ case _VBI_CC608_MODE_ROLL_UP:
+ row = ch->curr_row;
+ if (0 != (ch->dirty[ch->displayed_buffer] & (1 << row)))
+ stream_event (cd, ch, row, row);
+ break;
+
+ case _VBI_CC608_MODE_PAINT_ON:
+ stream_event_if_changed (cd, ch);
+ break;
+
+ case _VBI_CC608_MODE_POP_ON:
+ /* Nothing to do. */
+ break;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* Not reached. (ch is a caption channel.) */
+ return;
+ }
+
+ /* May send a display event. */
+ erase_memory (cd, ch, ch->displayed_buffer);
+}
+
+static void
+text_restart (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+
+ /* TR Text Restart -- 001 c10f 010 1010 */
+
+ curr_buffer = ch->displayed_buffer;
+ row = ch->curr_row;
+
+ /* ch->mode is invariably VBI_CC608_MODE_TEXT. */
+
+ if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
+ stream_event (cd, ch, row, row);
+ }
+
+ /* EIA 608-B Section 7.4. */
+ /* May send a display event. */
+ erase_memory (cd, ch, ch->displayed_buffer);
+
+ /* EIA 608-B Section 7.4. */
+ ch->curr_row = FIRST_ROW;
+ ch->curr_column = FIRST_COLUMN;
+}
+
+static void
+resume_direct_captioning (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+
+ /* RDC Resume Direct Captioning -- 001 c10f 010 1001 */
+
+ /* 47 CFR 15.119 (f)(1)(x), (f)(2)(vi) and EIA 608-B Annex
+ B.7: Does not erase memory, does not move the cursor when
+ resuming after a Text transmission.
+
+ XXX If ch->mode is unknown, roll-up or pop-on, what is
+ expected if no PAC is received between RDC and the text? */
+
+ curr_buffer = ch->displayed_buffer;
+ row = ch->curr_row;
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_ROLL_UP:
+ if (0 != (ch->dirty[curr_buffer] & (1 << row)))
+ stream_event (cd, ch, row, row);
+
+ /* fall through */
+
+ case _VBI_CC608_MODE_UNKNOWN:
+ case _VBI_CC608_MODE_POP_ON:
+ /* No change since last stream_event(). */
+ memcpy (ch->buffer[2], ch->buffer[curr_buffer], sizeof (ch->buffer[2]));
+ break;
+
+ case _VBI_CC608_MODE_PAINT_ON:
+ /* Mode continues. */
+ break;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* Not reached. (ch is a caption channel.) */
+ return;
+ }
+
+ ch->mode = _VBI_CC608_MODE_PAINT_ON;
+}
+
+static void
+resize_window (_vbi_cc608_decoder * cd,
+ struct channel *ch, unsigned int new_rows)
+{
+ unsigned int curr_buffer;
+ unsigned int max_rows;
+ unsigned int old_rows;
+ unsigned int row1;
+
+ curr_buffer = ch->displayed_buffer;
+
+ /* Shortcut. */
+ if (0 == ch->dirty[curr_buffer])
+ return;
+
+ row1 = ch->curr_row + 1;
+ max_rows = row1 - FIRST_ROW;
+ old_rows = MIN (ch->window_rows, max_rows);
+ new_rows = MIN (new_rows, max_rows);
+
+ /* Nothing to do unless the window shrinks. */
+ if (0 == new_rows || new_rows >= old_rows)
+ return;
+
+ memset (&ch->buffer[curr_buffer][row1 - old_rows][0], 0, (old_rows - new_rows)
+ * sizeof (ch->buffer[0][0]));
+
+ ch->dirty[curr_buffer] &= -1 << (row1 - new_rows);
+
+ display_event (cd, ch, /* flags */ 0);
+}
+
+static void
+roll_up_caption (_vbi_cc608_decoder * cd, struct channel *ch, unsigned int c2)
+{
+ unsigned int window_rows;
+
+ /* Roll-Up Captions -- 001 c10f 010 01xx */
+
+ window_rows = (c2 & 7) - 3; /* 2, 3, 4 */
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_ROLL_UP:
+ /* 47 CFR 15.119 (f)(1)(iv). */
+ /* May send a display event. */
+ resize_window (cd, ch, window_rows);
+
+ /* fall through */
+
+ case _VBI_CC608_MODE_UNKNOWN:
+ ch->mode = _VBI_CC608_MODE_ROLL_UP;
+ ch->window_rows = window_rows;
+
+ /* 47 CFR 15.119 (f)(1)(ix): No cursor movements,
+ no memory erasing. */
+
+ break;
+
+ case _VBI_CC608_MODE_PAINT_ON:
+ stream_event_if_changed (cd, ch);
+
+ /* fall through */
+
+ case _VBI_CC608_MODE_POP_ON:
+ ch->mode = _VBI_CC608_MODE_ROLL_UP;
+ ch->window_rows = window_rows;
+
+ /* 47 CFR 15.119 (f)(1)(ii). */
+ ch->curr_row = LAST_ROW;
+ ch->curr_column = FIRST_COLUMN;
+
+ /* 47 CFR 15.119 (f)(1)(x). */
+ /* May send a display event. */
+ erase_memory (cd, ch, ch->displayed_buffer);
+ erase_memory (cd, ch, ch->displayed_buffer ^ 1);
+
+ break;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* Not reached. (ch is a caption channel.) */
+ return;
+ }
+}
+
+static void
+delete_to_end_of_row (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+
+ /* DER Delete To End Of Row -- 001 c10f 010 0100 */
+
+ /* 47 CFR 15.119 (f)(1)(vii), (f)(2)(iii), (f)(3)(ii) and EIA
+ 608-B Section 7.4: In all caption modes and Text mode
+ "[the] Delete to End of Row command will erase from memory
+ any characters or control codes starting at the current
+ cursor location and in all columns to its right on the same
+ row." */
+
+ curr_buffer = ch->displayed_buffer ^ (_VBI_CC608_MODE_POP_ON == ch->mode);
+
+ row = ch->curr_row;
+
+ /* No event if the row contains only TRANSPARENT_SPACEs. */
+ if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
+ unsigned int column;
+ unsigned int i;
+ uint16_t c;
+
+ column = ch->curr_column;
+
+ memset (&ch->buffer[curr_buffer][row][column], 0, (LAST_COLUMN - column + 1)
+ * sizeof (ch->buffer[0][0][0]));
+
+ c = 0;
+ for (i = FIRST_COLUMN; i < column; ++i)
+ c |= ch->buffer[curr_buffer][row][i];
+
+ ch->dirty[curr_buffer] &= ~((0 == c) << row);
+
+ display_event (cd, ch, /* flags */ 0);
+ }
+}
+
+static void
+backspace (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int curr_buffer;
+ unsigned int row;
+ unsigned int column;
+
+ /* BS Backspace -- 001 c10f 010 0001 */
+
+ /* 47 CFR Section 15.119 (f)(1)(vi), (f)(2)(ii), (f)(3)(i) and
+ EIA 608-B Section 7.4. */
+ column = ch->curr_column;
+ if (column <= FIRST_COLUMN)
+ return;
+
+ ch->curr_column = --column;
+
+ curr_buffer = ch->displayed_buffer ^ (_VBI_CC608_MODE_POP_ON == ch->mode);
+
+ row = ch->curr_row;
+
+ /* No event if there's no visible effect. */
+ if (0 != ch->buffer[curr_buffer][row][column]) {
+ unsigned int i;
+ uint16_t c;
+
+ /* 47 CFR 15.119 (f), (f)(1)(vi), (f)(2)(ii) and EIA
+ 608-B Section 7.4. */
+ ch->buffer[curr_buffer][row][column] = 0;
+
+ c = 0;
+ for (i = FIRST_COLUMN; i <= LAST_COLUMN; ++i)
+ c |= ch->buffer[curr_buffer][row][i];
+
+ ch->dirty[curr_buffer] &= ~((0 == c) << row);
+
+ display_event (cd, ch, /* flags */ 0);
+ }
+}
+
+static void
+resume_caption_loading (_vbi_cc608_decoder * cd, struct channel *ch)
+{
+ unsigned int row;
+
+ /* RCL Resume Caption Loading -- 001 c10f 010 0000 */
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_UNKNOWN:
+ case _VBI_CC608_MODE_POP_ON:
+ break;
+
+ case _VBI_CC608_MODE_ROLL_UP:
+ row = ch->curr_row;
+ if (0 != (ch->dirty[ch->displayed_buffer] & (1 << row)))
+ stream_event (cd, ch, row, row);
+ break;
+
+ case _VBI_CC608_MODE_PAINT_ON:
+ stream_event_if_changed (cd, ch);
+ break;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* Not reached. (ch is a caption channel.) */
+ return;
+ }
+
+ /* 47 CFR 15.119 (f)(1)(x): Does not erase memory.
+ (f)(2)(iv): Cursor position remains unchanged. */
+
+ ch->mode = _VBI_CC608_MODE_POP_ON;
+}
+
+/* Note curr_ch is invalid if UNKNOWN_CHANNEL == cd->cc.curr_ch_num. */
+static struct channel *
+switch_channel (_vbi_cc608_decoder * cd,
+ struct channel *curr_ch, vbi_pgno new_ch_num, enum field_num f)
+{
+ struct channel *new_ch;
+
+ if (UNKNOWN_CHANNEL != cd->curr_ch_num[f]
+ && _VBI_CC608_MODE_UNKNOWN != curr_ch->mode) {
+ /* XXX Force a display update if we do not send events
+ on every display change. */
+ }
+
+ cd->curr_ch_num[f] = new_ch_num;
+ new_ch = &cd->channel[new_ch_num - VBI_CAPTION_CC1];
+
+ return new_ch;
+}
+
+/* Note ch is invalid if UNKNOWN_CHANNEL == cd->cc.curr_ch_num[f]. */
+static void
+misc_control_code (_vbi_cc608_decoder * cd,
+ struct channel *ch, unsigned int c2, unsigned int ch_num0, enum field_num f)
+{
+ unsigned int new_ch_num;
+
+ /* Misc Control Codes -- 001 c10f 010 xxxx */
+
+ /* c = channel (0 -> CC1/CC3/T1/T3, 1 -> CC2/CC4/T2/T4)
+ -- 47 CFR Section 15.119, EIA 608-B Section 7.7.
+ f = field (0 -> F1, 1 -> F2)
+ -- EIA 608-B Section 8.4, 8.5. */
+
+ /* XXX The f flag is intended to detect accidential field
+ swapping and we should use it for that purpose. */
+
+ switch (c2 & 15) {
+ case 0: /* RCL Resume Caption Loading -- 001 c10f 010 0000 */
+ /* 47 CFR 15.119 (f)(2) and EIA 608-B Section 7.7. */
+ new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
+ ch = switch_channel (cd, ch, new_ch_num, f);
+ resume_caption_loading (cd, ch);
+ break;
+
+ case 1: /* BS Backspace -- 001 c10f 010 0001 */
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+ backspace (cd, ch);
+ break;
+
+ case 2: /* reserved (formerly AOF Alarm Off) */
+ case 3: /* reserved (formerly AON Alarm On) */
+ break;
+
+ case 4: /* DER Delete To End Of Row -- 001 c10f 010 0100 */
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+ delete_to_end_of_row (cd, ch);
+ break;
+
+ case 5: /* RU2 */
+ case 6: /* RU3 */
+ case 7: /* RU4 Roll-Up Captions -- 001 c10f 010 01xx */
+ /* 47 CFR 15.119 (f)(1) and EIA 608-B Section 7.7. */
+ new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
+ ch = switch_channel (cd, ch, new_ch_num, f);
+ roll_up_caption (cd, ch, c2);
+ break;
+
+ case 8: /* FON Flash On -- 001 c10f 010 1000 */
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+
+ /* 47 CFR 15.119 (h)(1)(i): Spacing attribute. */
+ put_char (cd, ch, 0x1428,
+ /* displayable */ FALSE,
+ /* backspace */ FALSE);
+ break;
+
+ case 9: /* RDC Resume Direct Captioning -- 001 c10f 010 1001 */
+ /* 47 CFR 15.119 (f)(3) and EIA 608-B Section 7.7. */
+ new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
+ ch = switch_channel (cd, ch, new_ch_num, f);
+ resume_direct_captioning (cd, ch);
+ break;
+
+ case 10: /* TR Text Restart -- 001 c10f 010 1010 */
+ /* EIA 608-B Section 7.4. */
+ new_ch_num = VBI_CAPTION_T1 + (ch_num0 & 3);
+ ch = switch_channel (cd, ch, new_ch_num, f);
+ text_restart (cd, ch);
+ break;
+
+ case 11: /* RTD Resume Text Display -- 001 c10f 010 1011 */
+ /* EIA 608-B Section 7.4. */
+ new_ch_num = VBI_CAPTION_T1 + (ch_num0 & 3);
+ ch = switch_channel (cd, ch, new_ch_num, f);
+ /* ch->mode is invariably VBI_CC608_MODE_TEXT. */
+ break;
+
+ case 12: /* EDM Erase Displayed Memory -- 001 c10f 010 1100 */
+ /* 47 CFR 15.119 (f). EIA 608-B Section 7.7 and Annex
+ B.7: "[The] command shall be acted upon as
+ appropriate for caption processing without
+ terminating the Text Mode data stream." */
+
+ /* We need not check cd->curr_ch_num because bit 2 is
+ implied, bit 1 is the known field number and bit 0
+ is coded in the control code. */
+ ch = &cd->channel[ch_num0 & 3];
+
+ erase_displayed_memory (cd, ch);
+
+ break;
+
+ case 13: /* CR Carriage Return -- 001 c10f 010 1101 */
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f])
+ break;
+ carriage_return (cd, ch);
+ break;
+
+ case 14: /* ENM Erase Non-Displayed Memory -- 001 c10f 010 1110 */
+ /* 47 CFR 15.119 (f)(2)(v). EIA 608-B Section 7.7 and
+ Annex B.7: "[The] command shall be acted upon as
+ appropriate for caption processing without
+ terminating the Text Mode data stream." */
+
+ /* See EDM. */
+ ch = &cd->channel[ch_num0 & 3];
+
+ erase_memory (cd, ch, ch->displayed_buffer ^ 1);
+
+ break;
+
+ case 15: /* EOC End Of Caption -- 001 c10f 010 1111 */
+ /* 47 CFR 15.119 (f), (f)(2), (f)(3)(iv) and EIA 608-B
+ Section 7.7, Annex C.11. */
+ new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
+ ch = switch_channel (cd, ch, new_ch_num, f);
+ end_of_caption (cd, ch);
+ break;
+ }
+}
+
+static void
+move_window (_vbi_cc608_decoder * cd,
+ struct channel *ch, unsigned int new_base_row)
+{
+ uint8_t *base;
+ unsigned int curr_buffer;
+ unsigned int bytes_per_row;
+ unsigned int old_max_rows;
+ unsigned int new_max_rows;
+ unsigned int copy_bytes;
+ unsigned int erase_begin;
+ unsigned int erase_end;
+
+ curr_buffer = ch->displayed_buffer;
+
+ /* Shortcut and no event if we do not move the window or the
+ buffer contains only TRANSPARENT_SPACEs. */
+ if (new_base_row == ch->curr_row || 0 == ch->dirty[curr_buffer])
+ return;
+
+ base = (void *) &ch->buffer[curr_buffer][FIRST_ROW][0];
+ bytes_per_row = sizeof (ch->buffer[0][0]);
+
+ old_max_rows = ch->curr_row + 1 - FIRST_ROW;
+ new_max_rows = new_base_row + 1 - FIRST_ROW;
+ copy_bytes = MIN (MIN (old_max_rows, new_max_rows),
+ ch->window_rows) * bytes_per_row;
+
+ if (new_base_row < ch->curr_row) {
+ erase_begin = (new_base_row + 1) * bytes_per_row;
+ erase_end = (ch->curr_row + 1) * bytes_per_row;
+
+ memmove (base + erase_begin - copy_bytes,
+ base + erase_end - copy_bytes, copy_bytes);
+
+ ch->dirty[curr_buffer] >>= ch->curr_row - new_base_row;
+ } else {
+ erase_begin = (ch->curr_row + 1) * bytes_per_row - copy_bytes;
+ erase_end = (new_base_row + 1) * bytes_per_row - copy_bytes;
+
+ memmove (base + erase_end, base + erase_begin, copy_bytes);
+
+ ch->dirty[curr_buffer] <<= new_base_row - ch->curr_row;
+ ch->dirty[curr_buffer] &= ALL_ROWS_MASK;
+ }
+
+ memset (base + erase_begin, 0, erase_end - erase_begin);
+
+ display_event (cd, ch, /* flags */ 0);
+}
+
+static void
+preamble_address_code (_vbi_cc608_decoder * cd,
+ struct channel *ch, unsigned int c1, unsigned int c2)
+{
+ unsigned int row;
+
+ /* PAC Preamble Address Codes -- 001 crrr 1ri xxxu */
+
+ row = pac_row_map[(c1 & 7) * 2 + ((c2 >> 5) & 1)];
+ if ((int) row < 0)
+ return;
+
+ switch (ch->mode) {
+ case _VBI_CC608_MODE_UNKNOWN:
+ return;
+
+ case _VBI_CC608_MODE_ROLL_UP:
+ /* EIA 608-B Annex C.4. */
+ if (ch->window_rows > row + 1)
+ row = ch->window_rows - 1;
+
+ /* 47 CFR Section 15.119 (f)(1)(ii). */
+ /* May send a display event. */
+ move_window (cd, ch, row);
+
+ ch->curr_row = row;
+
+ break;
+
+ case _VBI_CC608_MODE_PAINT_ON:
+ stream_event_if_changed (cd, ch);
+
+ /* fall through */
+
+ case _VBI_CC608_MODE_POP_ON:
+ /* XXX 47 CFR 15.119 (f)(2)(i), (f)(3)(i): In Pop-on
+ and paint-on mode "Preamble Address Codes can be
+ used to move the cursor around the screen in random
+ order to place captions on Rows 1 to 15." We do not
+ have a limit on the number of displayable rows, but
+ as EIA 608-B Annex C.6 points out, if more than
+ four rows must be displayed they were probably
+ received in error and we should respond
+ accordingly. */
+
+ /* 47 CFR Section 15.119 (d)(1)(i) and EIA 608-B Annex
+ C.7. */
+ ch->curr_row = row;
+
+ break;
+
+ case _VBI_CC608_MODE_TEXT:
+ /* 47 CFR 15.119 (e)(1) and EIA 608-B Section 7.4:
+ Does not change the cursor row. */
+ break;
+ }
+
+ if (c2 & 0x10) {
+ /* 47 CFR 15.119 (e)(1)(i) and EIA 608-B Table 71. */
+ ch->curr_column = FIRST_COLUMN + (c2 & 0x0E) * 2;
+ }
+
+ /* PAC is a non-spacing attribute for the next character, see
+ put_char(). */
+ ch->last_pac = 0x1000 | c2;
+}
+
+static void
+control_code (_vbi_cc608_decoder * cd,
+ unsigned int c1, unsigned int c2, enum field_num f)
+{
+ struct channel *ch;
+ unsigned int ch_num0;
+
+ if (CC608_DECODER_LOG_INPUT) {
+ fprintf (stdout, "%s:%u: %s c1=%02x c2=%02x f=%d\n",
+ __FILE__, __LINE__, __FUNCTION__, c1, c2, f);
+ }
+
+ /* b2: Caption / text,
+ b1: field 1 / 2,
+ b0 (lsb): primary / secondary channel. */
+ ch_num0 = (((cd->curr_ch_num[f] - VBI_CAPTION_CC1) & 4)
+ + f * 2 + ((c1 >> 3) & 1));
+
+ /* Note ch is invalid if UNKNOWN_CHANNEL ==
+ cd->curr_ch_num[f]. */
+ ch = &cd->channel[ch_num0];
+
+ if (c2 >= 0x40) {
+ /* Preamble Address Codes -- 001 crrr 1ri xxxu */
+ if (UNKNOWN_CHANNEL != cd->curr_ch_num[f])
+ preamble_address_code (cd, ch, c1, c2);
+ return;
+ }
+
+ switch (c1 & 7) {
+ case 0:
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+
+ if (c2 < 0x30) {
+ /* Backgr. Attr. Codes -- 001 c000 010 xxxt */
+ /* EIA 608-B Section 6.2. */
+ put_char (cd, ch, 0x1000 | c2,
+ /* displayable */ FALSE,
+ /* backspace */ TRUE);
+ } else {
+ /* Undefined. */
+ }
+
+ break;
+
+ case 1:
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+
+ if (c2 < 0x30) {
+ /* Mid-Row Codes -- 001 c001 010 xxxu */
+ /* 47 CFR 15.119 (h)(1)(i): Spacing attribute. */
+ put_char (cd, ch, 0x1100 | c2,
+ /* displayable */ FALSE,
+ /* backspace */ FALSE);
+ } else {
+ /* Special Characters -- 001 c001 011 xxxx */
+ if (0x39 == c2) {
+ /* Transparent space. */
+ put_char (cd, ch, 0,
+ /* displayable */ FALSE,
+ /* backspace */ FALSE);
+ } else {
+ put_char (cd, ch, 0x1100 | c2,
+ /* displayable */ TRUE,
+ /* backspace */ FALSE);
+ }
+ }
+
+ break;
+
+ case 2:
+ case 3: /* Extended Character Set -- 001 c01x 01x xxxx */
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+
+ /* EIA 608-B Section 6.4.2. */
+ put_char (cd, ch, (c1 * 256 + c2) & 0x777F,
+ /* displayable */ TRUE,
+ /* backspace */ TRUE);
+ break;
+
+ case 4:
+ case 5:
+ if (c2 < 0x30) {
+ /* Misc. Control Codes -- 001 c10f 010 xxxx */
+ misc_control_code (cd, ch, c2, ch_num0, f);
+ } else {
+ /* Undefined. */
+ }
+
+ break;
+
+ case 6: /* reserved */
+ break;
+
+ case 7: /* Extended control codes -- 001 c111 01x xxxx */
+ if (UNKNOWN_CHANNEL == cd->curr_ch_num[f]
+ || _VBI_CC608_MODE_UNKNOWN == ch->mode)
+ break;
+
+ ext_control_code (cd, ch, c2);
+
+ break;
+ }
+}
+
+static vbi_bool
+characters (_vbi_cc608_decoder * cd, struct channel *ch, int c)
+{
+ if (CC608_DECODER_LOG_INPUT) {
+ fprintf (stdout, "%s:%u: %s c=0x%02x='%c'\n",
+ __FILE__, __LINE__, __FUNCTION__, c, _vbi_to_ascii (c));
+ }
+
+ if (0 == c) {
+ if (_VBI_CC608_MODE_UNKNOWN == ch->mode)
+ return TRUE;
+
+ /* XXX After x NUL characters (presumably a caption
+ pause), force a display update if we do not send
+ events on every display change. */
+
+ return TRUE;
+ }
+
+ if (c < 0x20) {
+ /* Parity error or invalid data. */
+
+ if (c < 0 && _VBI_CC608_MODE_UNKNOWN != ch->mode) {
+ /* 47 CFR Section 15.119 (j)(1). */
+ put_char (cd, ch, 0x7F,
+ /* displayable */ TRUE,
+ /* backspace */ FALSE);
+ }
+
+ return FALSE;
+ }
+
+ if (_VBI_CC608_MODE_UNKNOWN != ch->mode) {
+ put_char (cd, ch, c,
+ /* displayable */ TRUE,
+ /* backspace */ FALSE);
+ }
+
+ return TRUE;
+}
+
+/**
+ * @param cd Caption decoder allocated with _vbi_cc608_decoder_new().
+ * @param buffer A caption byte pair with parity bits.
+ * @param line ITU-R line number this data originated from,
+ * usually 21 or 284.
+ * @param capture_time System time in seconds when the sliced data was
+ * captured.
+ * @param pts ISO 13818-1 Presentation Time Stamp of the sliced
+ * data. @a pts counts 1/90000 seconds from an arbitrary point in the
+ * video stream. Only the 33 least significant bits have to be valid.
+ * If @a pts is negative the function converts @a capture_time to a
+ * PTS.
+ *
+ * This function decodes two bytes of Closed Caption data and updates
+ * the decoder state. It may send one VBI_EVENT_CC608 and one or more
+ * VBI_EVENT_CC608_STREAM.
+ *
+ * @returns
+ * @c FALSE if the caption byte pair contained errors.
+ */
+vbi_bool
+_vbi_cc608_decoder_feed (_vbi_cc608_decoder * cd,
+ const uint8_t buffer[2],
+ unsigned int line, double capture_time, int64_t pts)
+{
+ int c1, c2;
+ enum field_num f;
+ vbi_bool all_successful;
+
+ assert (NULL != cd);
+
+ if (0) {
+ fprintf (stdout, "%s:%u: %s "
+ "buffer={ 0x%02x 0x%02x '%c%c' } "
+ "line=%3d capture_time=%f "
+ "pts=%" PRId64 "\n",
+ __FILE__, __LINE__, __FUNCTION__,
+ buffer[0] & 0x7F,
+ buffer[1] & 0x7F,
+ _vbi_to_ascii (buffer[0]),
+ _vbi_to_ascii (buffer[1]), line, capture_time, pts);
+ }
+
+ f = FIELD_1;
+
+ switch (line) {
+ case 21: /* NTSC */
+ case 22: /* PAL/SECAM? */
+ break;
+
+ case 284: /* NTSC */
+ f = FIELD_2;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ cd->timestamp.sys = capture_time;
+
+ if (pts < 0)
+ pts = (int64_t) (capture_time * 90000);
+
+ /* Modulo 1 << 33 guaranteed in VBI_EVENT_CC608_STREAM dox. */
+ cd->timestamp.pts = pts & (((int64_t) 1 << 33) - 1);
+
+ /* XXX deferred reset here */
+
+ if (0 && FIELD_1 == f) {
+ _vbi_cc608_dump (stderr, buffer[0], buffer[1]);
+ }
+
+ c1 = vbi_unpar8 (buffer[0]);
+ c2 = vbi_unpar8 (buffer[1]);
+
+ all_successful = TRUE;
+
+ /* See 47 CFR 15.119 (2)(i)(4). EIA 608-B Section 8.3: Caption
+ control codes on field 2 may repeat as on field 1. Section
+ 8.6.2: XDS control codes shall not repeat. */
+
+ if (unlikely (c1 < 0)) {
+ goto parity_error;
+ } else if (c1 == cd->expect_ctrl[f][0]
+ && c2 == cd->expect_ctrl[f][1]) {
+ /* Already acted upon. */
+ cd->expect_ctrl[f][0] = -1;
+ goto finish;
+ }
+
+ if (c1 >= 0x10 && c1 < 0x20) {
+ /* Caption control code. */
+
+ /* There's no XDS on field 1, we just
+ use an array to save a branch. */
+ cd->in_xds[f] = FALSE;
+
+ /* 47 CFR Section 15.119 (i)(1), (i)(2). */
+ if (c2 < 0x20) {
+ /* Parity error or invalid control code.
+ Let's hope this code will repeat. */
+ goto parity_error;
+ }
+
+ control_code (cd, c1, c2, f);
+
+ if (cd->event_pending) {
+ display_event (cd, cd->event_pending,
+ /* flags */ 0);
+ cd->event_pending = NULL;
+ }
+
+ cd->expect_ctrl[f][0] = c1;
+ cd->expect_ctrl[f][1] = c2;
+ } else {
+ cd->expect_ctrl[f][0] = -1;
+
+ if (c1 < 0x10) {
+ if (FIELD_1 == f) {
+ /* 47 CFR Section 15.119 (i)(1): "If the
+ non-printing character in the pair is
+ in the range 00h to 0Fh, that character
+ alone will be ignored and the second
+ character will be treated normally." */
+ c1 = 0;
+ } else if (0x0F == c1) {
+ /* XDS packet terminator. */
+ cd->in_xds[FIELD_2] = FALSE;
+ goto finish;
+ } else if (c1 >= 0x01) {
+ /* XDS packet start or continuation.
+ EIA 608-B Section 7.7, 8.5: Also
+ interrupts a Text mode
+ transmission. */
+ cd->in_xds[FIELD_2] = TRUE;
+ goto finish;
+ }
+ }
+
+ {
+ struct channel *ch;
+ vbi_pgno ch_num;
+
+ ch_num = cd->curr_ch_num[f];
+ if (UNKNOWN_CHANNEL == ch_num)
+ goto finish;
+
+ ch_num = ((ch_num - VBI_CAPTION_CC1) & 5) + f * 2;
+ ch = &cd->channel[ch_num];
+
+ all_successful &= characters (cd, ch, c1);
+ all_successful &= characters (cd, ch, c2);
+
+ if (cd->event_pending) {
+ display_event (cd, cd->event_pending,
+ /* flags */ 0);
+ cd->event_pending = NULL;
+ }
+ }
+ }
+
+finish:
+ cd->error_history = cd->error_history * 2 + all_successful;
+
+ return all_successful;
+
+parity_error:
+ cd->expect_ctrl[f][0] = -1;
+
+ /* XXX Some networks stupidly transmit 0x0000 instead of
+ 0x8080 as filler. Perhaps we shouldn't take that as a
+ serious parity error. */
+ cd->error_history *= 2;
+
+ return FALSE;
+}
+
+/**
+ * @param cd Caption decoder allocated with _vbi_cc608_decoder_new().
+ * @param sliced Sliced VBI data.
+ * @param n_lines Number of lines in the @a sliced array.
+ * @param capture_time System time in seconds when the sliced data was
+ * captured.
+ * @param pts ISO 13818-1 Presentation Time Stamp of all elements
+ * in the sliced data array. @a pts counts 1/90000 seconds from an
+ * arbitrary point in the video stream. Only the 33 least significant
+ * bits have to be valid. If @a pts is negative the function
+ * converts @a capture_time to a PTS.
+ *
+ * This function works like _vbi_cc608_decoder_feed() but operates
+ * on sliced VBI data and filters out @c VBI_SLICED_CAPTION_525.
+ */
+vbi_bool
+_vbi_cc608_decoder_feed_frame (_vbi_cc608_decoder * cd,
+ const vbi_sliced * sliced,
+ unsigned int n_lines, double capture_time, int64_t pts)
+{
+ const vbi_sliced *end;
+
+ assert (NULL != cd);
+ assert (NULL != sliced);
+
+ for (end = sliced + n_lines; sliced < end; ++sliced) {
+ if (sliced->id & VBI_SLICED_CAPTION_525) {
+ if (!_vbi_cc608_decoder_feed (cd,
+ sliced->data, sliced->line, capture_time, pts))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * @param cd Caption decoder allocated with _vbi_cc608_decoder_new().
+ * @param callback Function to be called on events.
+ * @param user_data User pointer passed through to the @a callback
+ * function.
+ *
+ * Removes an event handler from the caption decoder, if a handler with
+ * this @a callback and @a user_data has been registered.
+ */
+void _vbi_cc608_decoder_remove_event_handler
+ (_vbi_cc608_decoder * cd, vbi_event_handler callback, void *user_data)
+{
+ _vbi_event_handler_list_remove_by_callback (&cd->handlers,
+ callback, user_data);
+}
+
+/**
+ * @param cd Caption decoder allocated with _vbi_cc608_decoder_new().
+ * @param event_mask Set of events the handler is waiting for,
+ * VBI_EVENT_CC608 or VBI_EVENT_CC608_STREAM.
+ * @param callback Function to be called on events by
+ * _vbi_cc608_decoder_feed().
+ * @param user_data User pointer passed through to the @a callback
+ * function.
+ *
+ * Adds a new event handler to the caption decoder. When the @a
+ * callback with this @a user_data is already registered the function
+ * changes the set of events the callback function will receive in the
+ * future.
+ *
+ * Any number of handlers can be added, also different handlers for the
+ * same event, which will be called in registration order.
+ *
+ * @returns
+ * @c FALSE on failure (out of memory).
+ */
+vbi_bool
+ _vbi_cc608_decoder_add_event_handler
+ (_vbi_cc608_decoder * cd,
+ unsigned int event_mask, vbi_event_handler callback, void *user_data)
+{
+ event_mask &= (_VBI_EVENT_CC608 | _VBI_EVENT_CC608_STREAM);
+
+ if (0 == event_mask) {
+ _vbi_event_handler_list_remove_by_callback (&cd->handlers,
+ callback, user_data);
+ return TRUE;
+ }
+
+ if (NULL != _vbi_event_handler_list_add (&cd->handlers,
+ event_mask, callback, user_data)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * @param cd Caption decoder allocated with _vbi_cc608_decoder_new().
+ *
+ * Resets the caption decoder, useful for example after a channel
+ * change.
+ */
+void
+_vbi_cc608_decoder_reset (_vbi_cc608_decoder * cd)
+{
+ unsigned int ch_num;
+
+ assert (NULL != cd);
+
+ if (CC608_DECODER_LOG_INPUT) {
+ fprintf (stderr, "%s:%u: %s\n", __FILE__, __LINE__, __FUNCTION__);
+
+ }
+
+ for (ch_num = 0; ch_num < MAX_CHANNELS; ++ch_num) {
+ struct channel *ch;
+
+ ch = &cd->channel[ch_num];
+
+ if (ch_num <= 3) {
+ ch->mode = _VBI_CC608_MODE_UNKNOWN;
+
+ /* Plausible for roll-up mode. We don't
+ display text while the caption mode is
+ unknown and may choose more suitable
+ defaults when we receive a mode changing
+ control code. */
+ ch->curr_row = LAST_ROW;
+ ch->curr_column = FIRST_COLUMN;
+ ch->window_rows = 4;
+ } else {
+ ch->mode = _VBI_CC608_MODE_TEXT; /* invariable */
+
+ /* EIA 608-B Section 7.4: "When Text Mode has
+ initially been selected and the specified
+ Text memory is empty, the cursor starts at
+ the topmost row, Column 1." */
+ ch->curr_row = FIRST_ROW;
+ ch->curr_column = FIRST_COLUMN;
+ ch->window_rows = 0; /* n/a */
+ }
+
+ ch->displayed_buffer = 0;
+
+ ch->last_pac = 0;
+
+ CLEAR (ch->buffer);
+ CLEAR (ch->dirty);
+
+ timestamp_reset (&ch->timestamp);
+ timestamp_reset (&ch->timestamp_c0);
+ }
+
+ cd->curr_ch_num[0] = UNKNOWN_CHANNEL;
+ cd->curr_ch_num[1] = UNKNOWN_CHANNEL;
+
+ memset (cd->expect_ctrl, -1, sizeof (cd->expect_ctrl));
+
+ CLEAR (cd->in_xds);
+
+ cd->event_pending = NULL;
+}
+
+static void
+_vbi_cc608_decoder_destroy (_vbi_cc608_decoder * cd)
+{
+ assert (NULL != cd);
+
+ _vbi_event_handler_list_destroy (&cd->handlers);
+
+ CLEAR (*cd);
+}
+
+static void
+_vbi_cc608_decoder_init (_vbi_cc608_decoder * cd)
+{
+ assert (NULL != cd);
+
+ CLEAR (*cd);
+
+ _vbi_event_handler_list_init (&cd->handlers);
+
+ _vbi_cc608_decoder_reset (cd);
+
+ timestamp_reset (&cd->timestamp);
+}
+
+/**
+ * @param cd Caption decoder allocated with _vbi_cc608_decoder_new(),
+ * can be @a NULL.
+ *
+ * Frees all resources associated with @a cd.
+ */
+void
+_vbi_cc608_decoder_delete (_vbi_cc608_decoder * cd)
+{
+ if (NULL == cd)
+ return;
+
+ _vbi_cc608_decoder_destroy (cd);
+
+ vbi_free (cd);
+}
+
+/**
+ * Allocates a new EIA 608-B Closed Caption decoder.
+ *
+ * To enter caption data call the _vbi_cc608_decoder_feed()
+ * function. Decoded data is available through VBI_EVENT_CC608_STREAM
+ * and the _vbi_cc608_decoder_get_page() function.
+ *
+ * To be notified when new data is available call
+ * _vbi_cc608_decoder_add_event_handler().
+ *
+ * @returns
+ * Pointer to a newly allocated caption decoder which must be freed
+ * with _vbi_cc608_decoder_delete() when no longer needed. @c NULL
+ * on failure (out of memory).
+ */
+_vbi_cc608_decoder *
+_vbi_cc608_decoder_new (void)
+{
+ _vbi_cc608_decoder *cd;
+
+ cd = vbi_malloc (sizeof (*cd));
+
+ if (NULL != cd) {
+ _vbi_cc608_decoder_init (cd);
+ }
+
+ return cd;
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/cc608_decoder.h b/gst/closedcaption/cc608_decoder.h
new file mode 100644
index 000000000..8f88d531d
--- /dev/null
+++ b/gst/closedcaption/cc608_decoder.h
@@ -0,0 +1,131 @@
+/*
+ * libzvbi - EIA 608-B Closed Caption decoder
+ *
+ * Copyright (C) 2008 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: cc608_decoder.h,v 1.2 2009-12-14 23:43:26 mschimek Exp $ */
+
+/* This code is experimental and not yet part of the library. */
+
+#ifndef __ZVBI_CC608_DECODER_H__
+#define __ZVBI_CC608_DECODER_H__
+
+#include <stdio.h>
+
+#include "format.h"
+#include "event.h"
+#include "sliced.h"
+
+VBI_BEGIN_DECLS
+
+/* Public */
+
+#define VBI_CAPTION_CC1 1 /* primary synchronous caption service (F1) */
+#define VBI_CAPTION_CC2 2 /* special non-synchronous use captions (F1) */
+#define VBI_CAPTION_CC3 3 /* secondary synchronous caption service (F2) */
+#define VBI_CAPTION_CC4 4 /* special non-synchronous use captions (F2) */
+
+#define VBI_CAPTION_T1 5 /* first text service (F1) */
+#define VBI_CAPTION_T2 6 /* second text service (F1) */
+#define VBI_CAPTION_T3 7 /* third text service (F2) */
+#define VBI_CAPTION_T4 8 /* fourth text service (F2) */
+
+/** @internal */
+typedef enum {
+ _VBI_CC608_MODE_UNKNOWN,
+ _VBI_CC608_MODE_ROLL_UP,
+ _VBI_CC608_MODE_POP_ON,
+ _VBI_CC608_MODE_PAINT_ON,
+ _VBI_CC608_MODE_TEXT
+} _vbi_cc608_mode;
+
+/** @internal */
+typedef enum {
+ _VBI_CC608_START_ROLLING = (1 << 0)
+} _vbi_cc608_event_flags;
+
+/** @internal */
+struct _vbi_event_cc608_page {
+ int channel;
+ _vbi_cc608_mode mode;
+ _vbi_cc608_event_flags flags;
+};
+
+/** @internal */
+struct _vbi_event_cc608_stream {
+ double capture_time;
+ int64_t pts;
+ int channel;
+ _vbi_cc608_mode mode;
+ vbi_char text[32];
+};
+
+typedef struct _vbi_cc608_decoder _vbi_cc608_decoder;
+
+extern void
+_vbi_cc608_dump (FILE * fp,
+ unsigned int c1,
+ unsigned int c2);
+extern vbi_bool
+_vbi_cc608_decoder_get_page (_vbi_cc608_decoder * cd,
+ vbi_page * pg,
+ vbi_pgno channel,
+ vbi_bool padding);
+extern vbi_bool
+_vbi_cc608_decoder_feed (_vbi_cc608_decoder * cd,
+ const uint8_t buffer[2],
+ unsigned int line,
+ double capture_time,
+ int64_t pts);
+extern vbi_bool
+_vbi_cc608_decoder_feed_frame (_vbi_cc608_decoder * cd,
+ const vbi_sliced * sliced,
+ unsigned int n_lines,
+ double capture_time,
+ int64_t pts);
+extern void
+_vbi_cc608_decoder_remove_event_handler
+ (_vbi_cc608_decoder * cd,
+ vbi_event_handler callback,
+ void * user_data);
+extern vbi_bool
+_vbi_cc608_decoder_add_event_handler
+ (_vbi_cc608_decoder * cd,
+ unsigned int event_mask,
+ vbi_event_handler callback,
+ void * user_data);
+extern void
+_vbi_cc608_decoder_reset (_vbi_cc608_decoder * cd);
+extern void
+_vbi_cc608_decoder_delete (_vbi_cc608_decoder * cd);
+extern _vbi_cc608_decoder *
+_vbi_cc608_decoder_new (void);
+
+/* Private */
+
+VBI_END_DECLS
+
+#endif /* __ZVBI_CC608_DECODER_H__ */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/event-priv.h b/gst/closedcaption/event-priv.h
new file mode 100644
index 000000000..a7c23829a
--- /dev/null
+++ b/gst/closedcaption/event-priv.h
@@ -0,0 +1,104 @@
+/*
+ * libzvbi - Events
+ *
+ * Copyright (C) 2004, 2008 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: event-priv.h,v 1.2 2009-12-14 23:43:38 mschimek Exp $ */
+
+#ifndef EVENT_PRIV_H
+#define EVENT_PRIV_H
+
+#include "event.h"
+
+#ifndef EVENT_PRIV_LOG
+#define EVENT_PRIV_LOG 0
+#endif
+
+/** @internal */
+typedef unsigned int vbi_event_mask;
+
+/** @internal */
+typedef struct _vbi_event_handler_rec vbi_event_handler_rec;
+
+/** @internal */
+struct _vbi_event_handler_rec {
+ vbi_event_handler_rec * next;
+ vbi_event_handler callback;
+ void * user_data;
+ vbi_event_mask event_mask;
+ vbi_bool remove;
+};
+
+/** @internal */
+typedef struct {
+ vbi_event_handler_rec * first;
+
+ /* Union of the event_mask of all handlers in the list. */
+ vbi_event_mask event_mask;
+
+ /* > 0 if _vbi_event_handler_list_send() currently traverses
+ this list. */
+ unsigned int ref_count;
+} _vbi_event_handler_list;
+
+#if EVENT_PRIV_LOG
+#define _vbi_event_handler_list_send(es, ev) \
+do { \
+ fprintf (stderr, "%s:%u event %s\n", \
+ __FILE__, __LINE__, _vbi_event_name ((ev)->type)); \
+ __vbi_event_handler_list_send (es, ev); \
+} while (0)
+#else
+#define _vbi_event_handler_list_send(es, ev) \
+ __vbi_event_handler_list_send (es, ev)
+#endif
+
+extern void
+__vbi_event_handler_list_send (_vbi_event_handler_list *es,
+ vbi_event * ev);
+extern void
+_vbi_event_handler_list_remove_by_event
+ (_vbi_event_handler_list *es,
+ vbi_event_mask event_mask);
+extern void
+_vbi_event_handler_list_remove_by_callback
+ (_vbi_event_handler_list *es,
+ vbi_event_handler callback,
+ void * user_data);
+extern void
+_vbi_event_handler_list_remove (_vbi_event_handler_list *es,
+ vbi_event_handler_rec *eh);
+extern vbi_event_handler_rec *
+_vbi_event_handler_list_add (_vbi_event_handler_list *es,
+ vbi_event_mask event_mask,
+ vbi_event_handler callback,
+ void * user_data);
+extern void
+_vbi_event_handler_list_destroy (_vbi_event_handler_list *es);
+extern vbi_bool
+_vbi_event_handler_list_init (_vbi_event_handler_list *es);
+
+#endif /* EVENT_PRIV_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/event.c b/gst/closedcaption/event.c
new file mode 100644
index 000000000..c192c00d1
--- /dev/null
+++ b/gst/closedcaption/event.c
@@ -0,0 +1,270 @@
+/*
+ * libzvbi - Events
+ *
+ * Copyright (C) 2004, 2008 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: event.c,v 1.2 2009-12-14 23:43:35 mschimek Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "misc.h" /* CLEAR() */
+#include "event-priv.h"
+
+/**
+ * @internal
+ * @param es Event handler list.
+ * @param ev The event to send.
+ *
+ * Traverses the list of event handlers and calls each handler waiting
+ * for the @a ev->type of event, passing @a ev as parameter.
+ */
+void
+__vbi_event_handler_list_send (_vbi_event_handler_list * el, vbi_event * ev)
+{
+ vbi_event_handler_rec *eh, **ehp;
+ unsigned int ref_count;
+
+ assert (NULL != el);
+ assert (NULL != ev);
+
+ if (0 == (el->event_mask & ev->type))
+ return;
+
+ ref_count = el->ref_count + 1;
+ if (unlikely (0 == ref_count))
+ return;
+ el->ref_count = ref_count;
+
+ for (eh = el->first; NULL != eh; eh = eh->next) {
+ if (0 != (eh->event_mask & ev->type) && !eh->remove) {
+ eh->callback (ev, eh->user_data);
+ }
+ }
+
+ el->ref_count = --ref_count;
+ if (ref_count > 0)
+ return;
+
+ ehp = &el->first;
+
+ while (NULL != (eh = *ehp)) {
+ if (eh->remove) {
+ *ehp = eh->next;
+ vbi_free (eh);
+ } else {
+ ehp = &eh->next;
+ }
+ }
+}
+
+/**
+ * @internal
+ * @param el Event handler list.
+ * @param event_mask Event mask.
+ *
+ * Removes all handlers from the list which handle
+ * only events given in the @a event_mask.
+ */
+void _vbi_event_handler_list_remove_by_event
+ (_vbi_event_handler_list * el, vbi_event_mask event_mask)
+{
+ vbi_event_handler_rec *eh, **ehp;
+ vbi_event_mask clear_mask;
+
+ assert (NULL != el);
+
+ clear_mask = ~event_mask;
+
+ ehp = &el->first;
+
+ while (NULL != (eh = *ehp)) {
+ if (0 == (eh->event_mask &= clear_mask)) {
+ if (0 == el->ref_count) {
+ *ehp = eh->next;
+ vbi_free (eh);
+ } else {
+ eh->remove = TRUE;
+ ehp = &eh->next;
+ }
+ } else {
+ ehp = &eh->next;
+ }
+ }
+
+ el->event_mask &= clear_mask;
+}
+
+/**
+ * @param el Event handler list.
+ * @param callback Function to be called on events.
+ * @param user_data User pointer passed through to the @a callback function.
+ *
+ * Removes all event handlers from the list with this @a callback and
+ * @a user_data. You can safely call this function from a handler removing
+ * itself or another handler.
+ */
+void _vbi_event_handler_list_remove_by_callback
+ (_vbi_event_handler_list * el, vbi_event_handler callback, void *user_data)
+{
+ _vbi_event_handler_list_add (el, 0, callback, user_data);
+}
+
+/**
+ * @param el Event handler list.
+ * @param eh Event handler.
+ *
+ * Removes event handler @a eh if member of the list @a el. You can
+ * safely call this function from a handler removing itself or another
+ * handler.
+ */
+void
+_vbi_event_handler_list_remove (_vbi_event_handler_list * el,
+ vbi_event_handler_rec * eh)
+{
+ vbi_event_handler_rec *eh1, **ehp;
+ vbi_event_mask event_union;
+
+ assert (NULL != el);
+ assert (NULL != eh);
+
+ ehp = &el->first;
+ event_union = 0;
+
+ while (NULL != (eh1 = *ehp)) {
+ if (eh1 == eh) {
+ if (0 == el->ref_count) {
+ *ehp = eh1->next;
+ vbi_free (eh1);
+ } else {
+ eh1->remove = TRUE;
+ ehp = &eh1->next;
+ }
+ } else {
+ event_union |= eh1->event_mask;
+ ehp = &eh1->next;
+ }
+ }
+
+ el->event_mask = event_union;
+}
+
+/**
+ * @param el Event handler list.
+ * @param event_mask Set of events (@c VBI_EVENT_) the handler is waiting
+ * for, can be -1 for all and 0 for none.
+ * @param callback Function to be called on events.
+ * @param user_data User pointer passed through to the @a callback
+ * function.
+ *
+ * Adds a new event handler to the list. When the @a callback with @a
+ * user_data is already registered the function merely changes the set
+ * of events it will receive in the future. When the @a event_mask is
+ * zero the function does nothing or removes an already registered event
+ * handler. You can safely call this function from an event handler.
+ *
+ * Any number of handlers can be added, also different handlers for the
+ * same event which will be called in registration order.
+ *
+ * @return
+ * Pointer to opaque vbi_event_handler object, @c NULL on failure or if
+ * no handler has been added.
+ */
+vbi_event_handler_rec *
+_vbi_event_handler_list_add (_vbi_event_handler_list * el,
+ vbi_event_mask event_mask, vbi_event_handler callback, void *user_data)
+{
+ vbi_event_handler_rec *eh, **ehp, *found;
+ vbi_event_mask event_union;
+
+ assert (NULL != el);
+
+ ehp = &el->first;
+ event_union = 0;
+ found = NULL;
+
+ while (NULL != (eh = *ehp)) {
+ if (eh->callback == callback && eh->user_data == user_data) {
+ if (0 == event_mask) {
+ if (0 == el->ref_count) {
+ *ehp = eh->next;
+ vbi_free (eh);
+ } else {
+ eh->remove = TRUE;
+ ehp = &eh->next;
+ }
+
+ continue;
+ } else {
+ found = eh;
+ eh->event_mask = event_mask;
+ }
+ }
+
+ event_union |= eh->event_mask;
+ ehp = &eh->next;
+ }
+
+ if (NULL == found && 0 != event_mask) {
+ found = vbi_malloc (sizeof (*found));
+ if (NULL != found) {
+ CLEAR (*found);
+
+ found->event_mask = event_mask;
+ found->callback = callback;
+ found->user_data = user_data;
+
+ event_union |= event_mask;
+
+ *ehp = found;
+ }
+ }
+
+ el->event_mask = event_union;
+
+ return found;
+}
+
+void
+_vbi_event_handler_list_destroy (_vbi_event_handler_list * el)
+{
+ assert (NULL != el);
+
+ _vbi_event_handler_list_remove_by_event (el, (vbi_event_mask) - 1);
+
+ CLEAR (*el);
+}
+
+vbi_bool
+_vbi_event_handler_list_init (_vbi_event_handler_list * el)
+{
+ assert (NULL != el);
+
+ CLEAR (*el);
+
+ return TRUE;
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/event.h b/gst/closedcaption/event.h
new file mode 100644
index 000000000..9782629cb
--- /dev/null
+++ b/gst/closedcaption/event.h
@@ -0,0 +1,794 @@
+/*
+ * libzvbi -- Events
+ *
+ * Copyright (C) 2000, 2001, 2002 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: event.h,v 1.16 2009-12-14 23:43:29 mschimek Exp $ */
+
+#ifndef EVENT_H
+#define EVENT_H
+
+#include "bcd.h"
+/* #include "pdc.h" */
+
+#ifndef VBI_DECODER
+#define VBI_DECODER
+typedef struct vbi_decoder vbi_decoder;
+#endif
+
+/* Public */
+
+#include <inttypes.h>
+
+/**
+ * @addtogroup Event Events
+ * @ingroup HiDec
+ *
+ * Typically the transmission of VBI data like a Teletext or Closed
+ * Caption page spans several VBI lines or even video frames. So internally
+ * the data service decoder maintains caches accumulating data. When a page
+ * or other object is complete it calls the respective event handler to
+ * notify the application.
+ *
+ * Clients can register any number of handlers needed, also different
+ * handlers for the same event. They will be called in the order registered
+ * from the vbi_decode() function. Since they block decoding, they should
+ * return as soon as possible. The event structure and all data
+ * pointed to from there must be read only. The data is only valid until
+ * the handler returns.
+ */
+
+/**
+ * @ingroup Event
+ * @brief Unique network id (a libzvbi thing).
+ *
+ * 0 = unknown network, bit 31 reserved for preliminary nuids.
+ * Other network codes are arbitrary.
+ */
+typedef unsigned int vbi_nuid;
+
+/**
+ * @ingroup Event
+ * @brief Network description.
+ *
+ * All strings are ISO 8859-1 encoded (yes that's stupid, sorry)
+ * and @c NUL terminated. Prepare for empty strings. Read only.
+ */
+typedef struct {
+ vbi_nuid nuid;
+
+ /**
+ * Name of the network from XDS or from a table lookup of
+ * CNIs in Teletext packet 8/30 or VPS.
+ */
+ signed char name[64];
+
+ /**
+ * Network call letters, from XDS. Empty if unknown or
+ * not applicable.
+ */
+ signed char call[40];
+
+ /**
+ * Tape delay in minutes, from XDS. Zero if unknown or
+ * not applicable.
+ **/
+ int tape_delay;
+
+ /**
+ * The European Broadcasting Union (EBU) maintains several tables
+ * of Country and Network Identification (CNI) codes. CNIs of type
+ * VPS, 8/30/1 and 8/30/2 can be used to identify networks during
+ * a channel scan.
+ *
+ * This field contains the CNI of the network found in a VPS
+ * packet. It can be zero if unknown or CNI's are not applicable.
+ * Note VPS has room for only 4 lsb of the country code (0xCNN).
+ *
+ * For example ZDF: 0xDC2.
+ */
+ int cni_vps;
+
+ /**
+ * CNI of the network from Teletext packet 8/30 format 1,
+ * zero if unknown or not applicable. The country code is
+ * stored in the MSB, the network code in the LSB (0xCCNN).
+ * Note these CNIs may use different country and network codes
+ * than the PDC (VPS, 8/30/2) CNIs.
+ *
+ * For example BBC1: 0x447F, ZDF: 0x4902.
+ */
+ int cni_8301;
+
+ /**
+ * CNI of the network from Teletext packet 8/30 format 2,
+ * zero if unknown or not applicable. The country code is
+ * stored in the MSB, the network code in the LSB (0xCCNN).
+ *
+ * For example BBC1: 0x2C7F, ZDF: 0x1DC2.
+ */
+ int cni_8302;
+
+ int reserved;
+
+ /** Private. */
+ int cycle;
+} vbi_network;
+
+/*
+ * Link
+ */
+
+/**
+ * @ingroup Event
+ * @brief Link type.
+ */
+typedef enum {
+ /**
+ * vbi_resolve_link() may return a link of this type on failure.
+ */
+ VBI_LINK_NONE = 0,
+ /**
+ * Not really a link, only vbi_link->name will be set. (Probably
+ * something like "Help! Help! The station is on fire!")
+ */
+ VBI_LINK_MESSAGE,
+ /**
+ * Points to a Teletext page, vbi_link->pgno and vbi_link->subno,
+ * eventually vbi_link->nuid and a descriptive text in vbi_link->name.
+ */
+ VBI_LINK_PAGE,
+ /**
+ * Also a Teletext page link, but this one is used exclusively
+ * to link subpages of the page containing the link.
+ */
+ VBI_LINK_SUBPAGE,
+ /**
+ * vbi_link->url is a HTTP URL (like "http://zapping.sf.net"),
+ * eventually accompanied by a descriptive text vbi_link->name.
+ */
+ VBI_LINK_HTTP,
+ /**
+ * vbi_link->url is a FTP URL (like "ftp://foo.bar.com/baz"),
+ * eventually accompanied by a descriptive text vbi_link->name.
+ */
+ VBI_LINK_FTP,
+ /**
+ * vbi_link->url is an e-mail address (like "mailto:foo@bar"),
+ * eventually accompanied by a descriptive text vbi_link->name.
+ */
+ VBI_LINK_EMAIL,
+ /** Is a trigger link id. Not useful, just ignore. */
+ VBI_LINK_LID,
+ /** Is a SuperTeletext link, ignore. */
+ VBI_LINK_TELEWEB
+} vbi_link_type;
+
+/**
+ * @ingroup Event
+ * @brief ITV link type.
+ *
+ * Some ITV (WebTV, ATVEF) triggers include a type id intended
+ * to filter relevant information. The names should speak for
+ * themselves. EACEM triggers always have type @c VBI_WEBLINK_UNKNOWN.
+ **/
+typedef enum {
+ VBI_WEBLINK_UNKNOWN = 0,
+ VBI_WEBLINK_PROGRAM_RELATED,
+ VBI_WEBLINK_NETWORK_RELATED,
+ VBI_WEBLINK_STATION_RELATED,
+ VBI_WEBLINK_SPONSOR_MESSAGE,
+ VBI_WEBLINK_OPERATOR
+} vbi_itv_type;
+
+/**
+ * @ingroup Event
+ *
+ * General purpose link description for ATVEF (ITV, WebTV in the
+ * United States) and EACEM (SuperTeletext et al in Europe) triggers,
+ * Teletext TOP and FLOF navigation, and for links "guessed" by
+ * libzvbi from the text (e. g. page numbers and URLs). Usually
+ * not all fields will be used.
+ */
+typedef struct vbi_link {
+ /**
+ * See vbi_link_type.
+ */
+ vbi_link_type type;
+ /**
+ * Links can be obtained two ways, via @ref VBI_EVENT_TRIGGER,
+ * then it arrived either through the EACEM or ATVEF transport
+ * method as flagged by this field. Or it is a navigational link
+ * returned by vbi_resolve_link(), then this field does not apply.
+ */
+ vbi_bool eacem;
+ /**
+ * Some descriptive text, Latin-1, possibly blank.
+ */
+ signed char name[80];
+ signed char url[256];
+ /**
+ * A piece of ECMA script (Javascript), this may be
+ * used on WebTV or SuperTeletext pages to trigger some action.
+ * Usually blank.
+ */
+ signed char script[256];
+ /**
+ * Teletext page links (no Closed Caption counterpart) can
+ * can actually reach across networks. That happens for example
+ * when vbi_resolve_link() picked up a link on a page after we
+ * switch away from that channel, or with EACEM triggers
+ * deliberately pointing to a page on another network (sic!).
+ * So the network id (if known, otherwise 0) is part of the
+ * page number. See vbi_nuid.
+ */
+ vbi_nuid nuid;
+ /**
+ * @a pgno and @a subno Teletext page number, see vbi_pgno, vbi_subno.
+ */
+ vbi_pgno pgno;
+ vbi_subno subno;
+ /**
+ * The time in seconds and fractions since
+ * 1970-01-01 00:00 when the link should no longer be offered
+ * to the user, similar to a HTTP cache expiration date.
+ */
+ double expires;
+ /**
+ * See vbi_itv_type. This field applies only to
+ * ATVEF triggers, is otherwise @c VBI_WEBLINK_UNKNOWN.
+ */
+ vbi_itv_type itv_type;
+ /**
+ * Trigger priority. 0 = emergency, should never be
+ * blocked. 1 or 2 = "high", 3 ... 5 = "medium", 6 ... 9 =
+ * "low". The default is 9. Apart of filtering triggers, this
+ * is also used to determine at which priority multiple links
+ * should be presented to the user. This field applies only to
+ * EACEM triggers, is otherwise 9.
+ */
+ int priority;
+ /**
+ * Open the target without user confirmation. (Supposedly
+ * this flag will be used to trigger scripts, not to open pages,
+ * but I have yet to see such a trigger.)
+ */
+ vbi_bool autoload;
+} vbi_link;
+
+/*
+ * Aspect ratio information.
+ */
+
+/**
+ * @ingroup Event
+ * @brief Open subtitle information.
+ *
+ * Open because they have been inserted into the picture, as
+ * opposed to closed subtitles (closed caption) encoded in the vbi.
+ */
+typedef enum {
+ VBI_SUBT_NONE, /**< No open subtitles. */
+ VBI_SUBT_ACTIVE, /**< Inserted in active picture. */
+ VBI_SUBT_MATTE, /**< Inserted in upper or lower letterbox bar. */
+ VBI_SUBT_UNKNOWN /**< Presence of open subtitles unknown. */
+} vbi_subt;
+
+/**
+ * @ingroup Event
+ * @brief Information about the picture aspect ratio and open subtitles.
+ *
+ * This is available via @ref VBI_EVENT_ASPECT.
+ */
+typedef struct {
+ /**
+ * @a first_line and @a last_line, inclusive, describe the bounds of active
+ * video, i. e. without the black bars in letterbox mode. These are
+ * <em>first field</em> line numbers according to the ITU-R line
+ * numbering scheme, see vbi_sliced. For example PAL 23 ... 310 (288 lines),
+ * NTSC 22 ... 262 (240 lines).
+ */
+ int first_line;
+ int last_line;
+ /**
+ * The picture aspect ratio in <em>anamorphic</em> mode,
+ * 16/9 for example. Normal or letterboxed video has aspect ratio 1/1.
+ */
+ double ratio;
+ /**
+ * @c TRUE when the source is known to be film transferred to
+ * video, as opposed to interlaced video from a video camera. (This is
+ * actually a helper flag for PALPlus decoders, but it may assist
+ * deinterlacers too.)
+ */
+ vbi_bool film_mode;
+ /**
+ * Describes how subtitles are inserted into the picture,
+ * see vbi_subt for details.
+ */
+ vbi_subt open_subtitles;
+} vbi_aspect_ratio;
+
+/*
+ * Program Info
+ *
+ * ATTN this is new stuff and subject to change
+ */
+
+/**
+ * @ingroup Event
+ * @brief Program rating source.
+ *
+ * If program rating information is available (also known in the
+ * U. S. as V-Chip data), this describes which rating scheme is
+ * being used: U. S. film, U. S. TV, Canadian English or French TV.
+ * You can convert the rating code to a string with
+ * vbi_rating_string().
+ *
+ * When the scheme is @c VBI_RATING_TV_US, additionally the
+ * DLSV rating flags will be set.
+ */
+typedef enum {
+ VBI_RATING_AUTH_NONE = 0,
+ VBI_RATING_AUTH_MPAA,
+ VBI_RATING_AUTH_TV_US,
+ VBI_RATING_AUTH_TV_CA_EN,
+ VBI_RATING_AUTH_TV_CA_FR
+} vbi_rating_auth;
+
+/**
+ * @ingroup Event
+ * @name US TV rating flags
+ * @{
+ */
+#define VBI_RATING_D 0x08 /**< "sexually suggestive dialog" */
+#define VBI_RATING_L 0x04 /**< "indecent language" */
+#define VBI_RATING_S 0x02 /**< "sexual situations" */
+#define VBI_RATING_V 0x01 /**< "violence" */
+/** @} */
+
+extern const char * vbi_rating_string(vbi_rating_auth auth, int id);
+
+/**
+ * @ingroup Event
+ * @brief Program classification schemes.
+ *
+ * libzvbi understands two different program classification schemes,
+ * the EIA-608 based in the United States and the ETS 300 231 based
+ * one in Europe. You can convert the program type code into a
+ * string with vbi_prog_type_string().
+ **/
+typedef enum {
+ VBI_PROG_CLASSF_NONE = 0,
+ VBI_PROG_CLASSF_EIA_608,
+ VBI_PROG_CLASSF_ETS_300231
+} vbi_prog_classf;
+
+/**
+ * @addtogroup Event
+ * @{
+ */
+extern const char * vbi_prog_type_string(vbi_prog_classf classf, int id);
+/** @} */
+
+/**
+ * @ingroup Event
+ * @brief Type of audio transmitted on one (mono or stereo)
+ * audio track.
+ */
+/* code depends on order, don't change */
+typedef enum {
+ VBI_AUDIO_MODE_NONE = 0, /**< No sound. */
+ VBI_AUDIO_MODE_MONO, /**< Mono audio. */
+ VBI_AUDIO_MODE_STEREO, /**< Stereo audio. */
+ VBI_AUDIO_MODE_STEREO_SURROUND, /**< Surround. */
+ VBI_AUDIO_MODE_SIMULATED_STEREO, /**< ? */
+ /**
+ * Spoken descriptions of the program for the blind, on a secondary audio track.
+ */
+ VBI_AUDIO_MODE_VIDEO_DESCRIPTIONS,
+ /**
+ * Unrelated to the current program.
+ */
+ VBI_AUDIO_MODE_NON_PROGRAM_AUDIO,
+
+ VBI_AUDIO_MODE_SPECIAL_EFFECTS, /**< ? */
+ VBI_AUDIO_MODE_DATA_SERVICE, /**< ? */
+ /**
+ * We have no information what is transmitted.
+ */
+ VBI_AUDIO_MODE_UNKNOWN
+} vbi_audio_mode;
+
+/**
+ * @ingroup Event
+ *
+ * Information about the current program, preliminary.
+ */
+typedef struct vbi_program_info {
+ /*
+ * Refers to the current or next program.
+ * (No [2] to allow clients filtering current data more easily.)
+ */
+ unsigned int future : 1;
+
+ /* 01 Program Identification Number */
+
+ /* If unknown all these fields are -1 */
+ signed char month; /* 0 ... 11 */
+ signed char day; /* 0 ... 30 */
+ signed char hour; /* 0 ... 23 */
+ signed char min; /* 0 ... 59 */
+
+ /*
+ * VD: "T indicates if a program is routinely tape delayed for the
+ * Mountain and Pacific time zones."
+ */
+ signed char tape_delayed;
+
+ /* 02 Program Length */
+
+ /* If unknown all these fields are -1 */
+ signed char length_hour; /* 0 ... 63 */
+ signed char length_min; /* 0 ... 59 */
+
+ signed char elapsed_hour; /* 0 ... 63 */
+ signed char elapsed_min; /* 0 ... 59 */
+ signed char elapsed_sec; /* 0 ... 59 */
+
+ /* 03 Program name */
+
+ /* If unknown title[0] == 0 */
+ signed char title[64]; /* ASCII + '\0' */
+
+ /* 04 Program type */
+
+ /*
+ * If unknown type_classf == VBI_PROG_CLASSF_NONE.
+ * VBI_PROG_CLASSF_EIA_608 can have up to 32 tags
+ * identifying 96 keywords. Their numerical value
+ * is given here instead of composing a string for
+ * easier filtering. Use vbi_prog_type_str_by_id to
+ * get the keywords. A zero marks the end.
+ */
+ vbi_prog_classf type_classf;
+ int type_id[33];
+
+ /* 05 Program rating */
+
+ /*
+ * For details STFW for "v-chip"
+ * If unknown rating_auth == VBI_RATING_NONE
+ */
+ vbi_rating_auth rating_auth;
+ int rating_id;
+
+ /* Only valid when auth == VBI_RATING_TV_US */
+ int rating_dlsv;
+
+ /* 06 Program Audio Services */
+
+ /*
+ * BTSC audio (two independent tracks) is flagged according to XDS,
+ * Zweiton/NICAM/EIA-J audio is flagged mono/none, stereo/none or
+ * mono/mono for bilingual transmissions.
+ */
+ struct {
+ /* If unknown mode == VBI_AUDIO_MODE_UNKNOWN */
+ vbi_audio_mode mode;
+ /* If unknown language == NULL */
+ unsigned char * language; /* Latin-1 */
+ } audio[2]; /* primary and secondary */
+
+ /* 07 Program Caption Services */
+
+ /*
+ * Bits 0...7 corresponding to Caption page 1...8.
+ * Note for the current program this information is also
+ * available via vbi_classify_page().
+ *
+ * If unknown caption_services == -1, _language[] = NULL
+ */
+ int caption_services;
+ unsigned char * caption_language[8]; /* Latin-1 */
+
+ /* 08 Copy Generation Management System */
+
+ /* If unknown cgms_a == -1 */
+ int cgms_a; /* XXX */
+
+ /* 09 Aspect Ratio */
+
+ /*
+ * Note for the current program this information is also
+ * available via VBI_EVENT_ASPECT.
+ *
+ * If unknown first_line == last_line == -1, ratio == 0.0
+ */
+ vbi_aspect_ratio aspect;
+
+ /* 10 - 17 Program Description */
+
+ /*
+ * 8 rows of 0...32 ASCII chars + '\0',
+ * if unknown description[0...7][0] == 0
+ */
+ signed char description[8][33];
+} vbi_program_info;
+
+/**
+ * @addtogroup Event
+ * @{
+ */
+extern void vbi_reset_prog_info(vbi_program_info *pi);
+/** @} */
+
+/**
+ * @ingroup Event
+ * @name Event types.
+ * @{
+ */
+
+/**
+ * @anchor VBI_EVENT_
+ * No event.
+ */
+#define VBI_EVENT_NONE 0x0000
+/**
+ * The vbi decoding context is about to be closed. This event is
+ * sent by vbi_decoder_delete() and can be used to clean up
+ * event handlers.
+ */
+#define VBI_EVENT_CLOSE 0x0001
+/**
+ * The vbi decoder received and cached another Teletext page
+ * designated by ev.ttx_page.pgno and ev.ttx_page.subno.
+ *
+ * ev.ttx_page.roll_header flags the page header as suitable for
+ * rolling page numbers, e. g. excluding pages transmitted out
+ * of order.
+ *
+ * The ev.ttx_page.header_update flag is set when the header,
+ * excluding the page number and real time clock, changed since the
+ * last @c VBI_EVENT_TTX_PAGE. Note this may happen at midnight when the
+ * date string changes. The ev.ttx_page.clock_update flag is set when
+ * the real time clock changed since the last @c VBI_EVENT_TTX_PAGE (that is
+ * at most once per second). They are both set at the first
+ * @c VBI_EVENT_TTX_PAGE sent and unset while the received header
+ * or clock field is corrupted.
+ *
+ * If any of the roll_header, header_update or clock_update flags
+ * are set ev.ttx_page.raw_header is a pointer to the raw header data
+ * (40 bytes), which remains valid until the event handler returns.
+ * ev.ttx_page.pn_offset will be the offset (0 ... 37) of the three
+ * digit page number in the raw or formatted header. Allways call
+ * vbi_fetch_vt_page() for proper translation of national characters
+ * and character attributes, the raw header is only provided here
+ * as a means to quickly detect changes.
+ */
+#define VBI_EVENT_TTX_PAGE 0x0002
+/**
+ * A Closed Caption page has changed and needs visual update.
+ * The page or "CC channel" is designated by ev.caption.pgno,
+ * see vbi_pgno for details.
+ *
+ * When the client is monitoring this page, the expected action is
+ * to call vbi_fetch_cc_page(). To speed up rendering more detailed
+ * update information is provided in vbi_page.dirty, see #vbi_page.
+ * The vbi_page will be a snapshot of the status at fetch time
+ * and not event time, vbi_page.dirty accumulates all changes since
+ * the last fetch.
+ */
+#define VBI_EVENT_CAPTION 0x0004
+/**
+ * Some station/network identifier has been received or is no longer
+ * transmitted (vbi_network all zero, eg. after a channel switch).
+ * ev.network is a vbi_network object, read only. The event will not
+ * repeat*) unless a different identifier has been received and confirmed.
+ *
+ * Minimum time to identify network, when data service is transmitted:
+ * <table>
+ * <tr><td>VPS (DE/AT/CH only):</td><td>0.08 s</td></tr>
+ * <tr><td>Teletext PDC, 8/30:</td><td>2 s</td></tr>
+ * <tr><td>XDS (US only):</td><td>unknown, between 0.1x to 10x seconds</td></tr>
+ * </table>
+ *
+ * *) VPS/TTX and XDS will not combine in real life, feeding the decoder
+ * with artificial data can confuse the logic.
+ */
+#define VBI_EVENT_NETWORK 0x0008
+/**
+ * @anchor VBI_EVENT_TRIGGER
+ *
+ * Triggers are sent by broadcasters to start some action on the
+ * user interface of modern TVs. Until libzvbi implements all ;-) of
+ * WebTV and SuperTeletext the information available are program
+ * related (or unrelated) URLs, short messages and Teletext
+ * page links.
+ *
+ * This event is sent when a trigger has fired, ev.trigger
+ * points to a vbi_link structure describing the link in detail.
+ * The structure must be read only.
+ */
+#define VBI_EVENT_TRIGGER 0x0010
+/**
+ * @anchor VBI_EVENT_ASPECT
+ *
+ * The vbi decoder received new information (potentially from
+ * PAL WSS, NTSC XDS or EIA-J CPR-1204) about the program
+ * aspect ratio. ev.ratio is a pointer to a vbi_ratio structure.
+ * The structure must be read only.
+ */
+#define VBI_EVENT_ASPECT 0x0040
+/**
+ * We have new information about the current or next program.
+ * ev.prog_info is a vbi_program_info pointer (due to size), read only.
+ *
+ * Preliminary.
+ *
+ * XXX Info from Teletext not implemented yet.
+ * XXX Change to get_prog_info. network ditto?
+ */
+#define VBI_EVENT_PROG_INFO 0x0080
+/**
+ * Like @a VBI_EVENT_NETWORK, but this event will also be sent
+ * when the decoder cannot determine a network name.
+ *
+ * @since 0.2.20
+ */
+#define VBI_EVENT_NETWORK_ID 0x0100
+/**
+ * A new local time has been received. ev.local_time points to a
+ * vbi_local_time structure with details.
+ */
+#define VBI_EVENT_LOCAL_TIME 0x0400
+/**
+ * A new Program ID (VPS or PDC) has been received. ev.prog_id points
+ * to a vbi_program_id structure with details.
+ */
+#define VBI_EVENT_PROG_ID 0x0800
+/** @} */
+
+/**
+ * Specifies if daylight-saving time is in effect in the time zone of
+ * the intended audience of the network.
+ */
+typedef enum {
+ /** The network does not provide any DST information. */
+ VBI_DST_UNKNOWN = 0,
+
+ /**
+ * A DST offset (+0 or +1 hour) has been added to the time
+ * zone offset.
+ */
+ VBI_DST_INCLUDED,
+
+ /** Daylight-saving time is not in effect. */
+ VBI_DST_INACTIVE,
+
+ /**
+ * Daylight-saving time is in effect, and +1 hour has been
+ * added to the time zone offset.
+ */
+ VBI_DST_ACTIVE
+} vbi_dst_state;
+
+typedef struct {
+ /** The current time in the UTC zone. */
+ time_t time;
+
+ /**
+ * The offset of the time zone of the intended audience of the
+ * network in seconds east of UTC. For example the EST zone is
+ * offset by -18000, GMT by 0, and CET by +3600 seconds. An
+ * additional +3600 second daylight-saving time offset may
+ * have been added as specified by @a dst_state, giving for
+ * example an EDT offset of -14400 seconds.
+ */
+ int seconds_east;
+
+ /** If FALSE, the network does not provide a time zone offset. */
+ vbi_bool seconds_east_valid;
+
+ /**
+ * Whether daylight-saving time is currently in effect in the
+ * time zone of the intended audience of the network.
+ */
+ vbi_dst_state dst_state;
+} vbi_local_time;
+
+/* Experimental CC608 decoder. */
+#define _VBI_EVENT_CC608 0x1000
+#define _VBI_EVENT_CC608_STREAM 0x2000
+struct _vbi_event_cc608_page;
+struct _vbi_event_cc608_stream;
+
+/**
+ * @example examples/network.c
+ * Network identification example.
+ */
+
+#include <inttypes.h>
+
+/**
+ * @ingroup Event
+ * @brief Event union.
+ */
+/* XXX network, aspect, prog_info: should only notify about
+ * changes and provide functions to query current value.
+ */
+typedef struct vbi_event {
+ int type;
+ union {
+ struct {
+ int pgno;
+ int subno;
+ uint8_t * raw_header;
+ int pn_offset;
+ unsigned int roll_header : 1;
+ unsigned int header_update : 1;
+ unsigned int clock_update : 1;
+ } ttx_page;
+ struct {
+ int pgno;
+ } caption;
+ vbi_network network;
+ vbi_link * trigger;
+ vbi_aspect_ratio aspect;
+ vbi_program_info * prog_info;
+ vbi_local_time * local_time;
+ /* vbi_program_id * prog_id; */
+
+ /* Experimental. */
+ struct _vbi_event_cc608_page * _cc608;
+ struct _vbi_event_cc608_stream * _cc608_stream;
+ } ev;
+} vbi_event;
+
+/**
+ * @addtogroup Event
+ * @{
+ */
+typedef void (* vbi_event_handler)(vbi_event *event, void *user_data);
+
+extern vbi_bool vbi_event_handler_add(vbi_decoder *vbi, int event_mask,
+ vbi_event_handler handler,
+ void *user_data);
+extern void vbi_event_handler_remove(vbi_decoder *vbi,
+ vbi_event_handler handler);
+extern vbi_bool vbi_event_handler_register(vbi_decoder *vbi, int event_mask,
+ vbi_event_handler handler,
+ void *user_data);
+extern void vbi_event_handler_unregister(vbi_decoder *vbi,
+ vbi_event_handler handler,
+ void *user_data);
+/** @} */
+
+/* Private */
+
+extern void vbi_send_event(vbi_decoder *vbi, vbi_event *ev);
+
+#endif /* EVENT_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/format.h b/gst/closedcaption/format.h
new file mode 100644
index 000000000..7049649ba
--- /dev/null
+++ b/gst/closedcaption/format.h
@@ -0,0 +1,408 @@
+/*
+ * libzvbi -- Formatted Teletext and Closed Caption page
+ *
+ * Copyright (C) 2000, 2001 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: format.h,v 1.11 2008-02-19 00:35:19 mschimek Exp $ */
+
+#ifndef FORMAT_H
+#define FORMAT_H
+
+#include "event.h" /* vbi_nuid */
+
+#ifndef VBI_DECODER
+#define VBI_DECODER
+typedef struct vbi_decoder vbi_decoder;
+#endif
+
+/* Public */
+
+#include <inttypes.h>
+
+/**
+ * @addtogroup Page Formatted text page
+ * @ingroup HiDec
+ */
+
+/**
+ * @ingroup Page
+ * @brief Index into the vbi_page->color_map.
+ *
+ * The enumerated color names
+ * refer to the Teletext and Closed Caption base palette of eight
+ * colors. Note however the color_map really has 40 entries for
+ * Teletext Level 2.5+, 32 of which are redefinable, the remaining
+ * eight are private colors of libzvbi e. g. for
+ * navigational information. So these symbols may not necessarily
+ * correspond to the respective color.
+ */
+/* Code depends on order, don't change. */
+typedef enum {
+ VBI_BLACK,
+ VBI_RED,
+ VBI_GREEN,
+ VBI_YELLOW,
+ VBI_BLUE,
+ VBI_MAGENTA,
+ VBI_CYAN,
+ VBI_WHITE
+} vbi_color;
+
+/**
+ * @ingroup Page
+ * @brief Colormap entry: 0xAABBGGRR. libzvbi sets the alpha channel
+ * always to 0xFF.
+ */
+typedef uint32_t vbi_rgba;
+
+/* Private */
+
+#define VBI_RGBA(r, g, b) \
+ ((((r) & 0xFF) << 0) | (((g) & 0xFF) << 8) \
+ | (((b) & 0xFF) << 16) | (0xFF << 24))
+#define VBI_R(rgba) (((rgba) >> 0) & 0xFF)
+#define VBI_G(rgba) (((rgba) >> 8) & 0xFF)
+#define VBI_B(rgba) (((rgba) >> 16) & 0xFF)
+#define VBI_A(rgba) (((rgba) >> 24) & 0xFF)
+
+/* Public */
+
+/**
+ * @ingroup Page
+ * @brief Defines the opacity of a vbi_char and vbi_page border.
+ *
+ * Teletext Level 2.5 defines a special transparent color which
+ * permits unusual characters with transparent foreground, opaque
+ * background. For simplicity this type of opacity has been omitted. Also
+ * renderers shall rely on the opacity attribute and not attempt to
+ * interpret the color value as transparency indicator.
+ */
+typedef enum {
+ /**
+ * This page is supposed to be overlayed onto
+ * video, with video displayed in place of this character (or the page
+ * border). In other words the character is a space (vbi_char->unicode =
+ * U+0020) and the glyph background is transparent. If desired the
+ * renderer may also fall back to VBI_SEMI_TRANSPARENT or VBI_OPAQUE
+ * mode. For this case vbi_char->background names the color to use as
+ * the semi-transparent or opaque background.
+ *
+ * VBI_TRANSPARENT_SPACE is the opacity of subtitle pages (both border and
+ * characters, while the 'boxed' words are marked as VBI_SEMI_TRANSPARENT),
+ * but can also occur on a mainly VBI_OPAQUE page to create a 'window'
+ * effect.
+ */
+ VBI_TRANSPARENT_SPACE,
+ /**
+ * Display video instead of the background color.
+ * Here the character is <em>not</em> a space and shall be displayed
+ * in vbi_char->foreground color. Only in the background of the character
+ * video shall look through. Again the renderer may fall back to
+ * VBI_SEMI_TRANSPARENT or VBI_OPAQUE.
+ */
+ VBI_TRANSPARENT_FULL,
+ /**
+ * Alpha blend video into background color, the
+ * character background becomes translucent. This is the opacity used
+ * for 'boxed' text on an otherwise VBI_TRANSPARENT_SPACE page, typically
+ * a subtitle or Teletext newsflash page. The renderer may fall back
+ * to VBI_OPAQUE.
+ */
+ VBI_SEMI_TRANSPARENT,
+ /**
+ * Display foreground and background color. Showing
+ * foreground or background transparent instead is not recommended because
+ * the editor may have swapped foreground and background color, then
+ * replaced a glyph by its inverse image, so one cannot really know if
+ * the character foreground or background will appear transparent.
+ */
+ VBI_OPAQUE
+} vbi_opacity;
+
+/**
+ * @ingroup Page
+ * @brief Defines the size of a vbi_char in a vbi_page.
+
+ * Double width or height characters expand into the next
+ * column right and/or next row below.
+ *
+ * Scanning two rows left to right, you will find<br>
+ * <pre>
+ * VBI_NORMAL_SIZE | VBI_DOUBLE_WIDTH VBI_OVER_TOP | VBI_DOUBLE_HEIGHT | VBI_DOUBLE_SIZE VBI_OVER_TOP
+ * x | x x | VBI_DOUBLE_HEIGHT2 | VBI_DOUBLE_SIZE2 VBI_OVER_BOTTOM
+ * </pre>
+ *
+ * A VBI_DOUBLE_HEIGHT2, VBI_DOUBLE_SIZE2, VBI_OVER_TOP, VBI_OVER_BOTTOM
+ * vbi_char has the same character unicode and attributes as the top/left anchor.
+ * Partial characters (like a single VBI_DOUBLE_HEIGHT2) will not appear, so
+ * VBI_DOUBLE_HEIGHT2, VBI_DOUBLE_SIZE2, VBI_OVER_TOP, VBI_OVER_BOTTOM
+ * can be safely ignored when scanning the page.
+ */
+/* Code depends on order, don't change. */
+typedef enum {
+ VBI_NORMAL_SIZE, VBI_DOUBLE_WIDTH, VBI_DOUBLE_HEIGHT, VBI_DOUBLE_SIZE,
+ VBI_OVER_TOP, VBI_OVER_BOTTOM, VBI_DOUBLE_HEIGHT2, VBI_DOUBLE_SIZE2
+} vbi_size;
+
+/**
+ * @ingroup Page
+ * @brief Attributed character.
+ */
+typedef struct vbi_char {
+ unsigned underline : 1; /**< Display character underlined. */
+ unsigned bold : 1; /**< Display character bold. */
+ unsigned italic : 1; /**< Display character slanted right. */
+ /**
+ * Display character or space (U+0020), one second cycle time.
+ */
+ unsigned flash : 1;
+ /**
+ * Replace character by space (U+0020) if not revealed.
+ * This is used for example to hide text on question & answer pages.
+ */
+ unsigned conceal : 1;
+ /**
+ * No function yet, default is fixed spacing.
+ */
+ unsigned proportional : 1;
+ /**
+ * This character is part of a hyperlink. Call vbi_resolve_link()
+ * to get more information.
+ */
+ unsigned link : 1;
+ /**
+ * Reserved for VPT link flag.
+ */
+ unsigned reserved : 1;
+ /**
+ * Character size, see vbi_size.
+ */
+ unsigned size : 8;
+ /**
+ * Character opacity, see vbi_opacity. Both @a foreground
+ * and @a background color are valid independent of @a opacity.
+ */
+ unsigned opacity : 8;
+ /**
+ * Character foreground color, a vbi_color index
+ * into the vbi_page->color_map.
+ */
+ unsigned foreground : 8;
+ /**
+ * Character background color, a vbi_color index
+ * into the vbi_page->color_map.
+ */
+ unsigned background : 8;
+ /**
+ * DRCS color look-up table offset, see vbi_page for details.
+ */
+ unsigned drcs_clut_offs : 8;
+ /**
+ * Character code according to ISO 10646 UCS-2 (not UTF-16).
+ *
+ * All Closed Caption characters can be represented in Unicode,
+ * but unfortunately not all Teletext characters.
+ *
+ * <a href="http://www.etsi.org">ETS 300 706
+ * </a> Table 36 Latin National Subset Turkish, character
+ * 0x23 "Turkish currency symbol" is not representable in Unicode,
+ * thus translated to private code U+E800. I was unable to identify
+ * all Arabic glyphs in Table 44 and 45 Arabic G0 and G2, so for now
+ * these are mapped to private code U+E620 ... U+E67F and U+E720 ...
+ * U+E77F respectively. Table 47 G1 Block Mosaic is not representable
+ * in Unicode, translated to private code U+EE00 ... U+EE7F. That is,
+ * the contiguous form has bit 5 (0x20) set, the separate form cleared.
+ * Table 48 G3 "Smooth Mosaics and Line Drawing Set" is not
+ * representable in Unicode, translated to private code
+ * U+EF20 ... U+EF7F.
+ *
+ * Teletext Level 2.5+ DRCS are represented by private code
+ * U+F000 ... U+F7FF. The 6 lsb select character 0x00 ... 0x3F
+ * from a DRCS plane, the 5 msb select DRCS plane 0 ... 31, see
+ * vbi_page for details.
+ *
+ * @bug
+ * Some Teletext character sets contain complementary
+ * Latin characters. For example the Greek capital letters Alpha
+ * and Beta are re-used as Latin capital letter A and B, while a
+ * separate code exists for Latin capital letter C. libzvbi will
+ * not analyse the page contents, so Greek A and B are always
+ * translated to Greek Alpha and Beta, C to Latin C, even if they
+ * appear in a pure Latin character word.
+ */
+ unsigned unicode : 16;
+} vbi_char;
+
+struct vbi_font_descr;
+
+/**
+ * @ingroup Page
+ * @brief Formatted Teletext or Closed Caption page.
+ *
+ * Clients can fetch pages
+ * from the respective cache using vbi_fetch_vt_page() or
+ * vbi_fetch_cc_page() for evaluation, display or output. Since
+ * the page may reference other objects in cache which are locked
+ * by the fetch functions, vbi_unref_page() must be called when done.
+ * Note this structure is large, some 10 KB.
+ */
+typedef struct vbi_page {
+ /**
+ * Points back to the source context.
+ */
+ vbi_decoder * vbi;
+
+ /**
+ * Identifies the network broadcasting this page.
+ */
+ vbi_nuid nuid;
+ /**
+ * Page number, see vbi_pgno.
+ */
+ /* FIXME this shouldn't be int */
+ int pgno;
+ /**
+ * Subpage number, see vbi_subno.
+ */
+ /* FIXME this shouldn't be int */
+ int subno;
+ /**
+ * Number of character rows in the page.
+ */
+ int rows;
+ /**
+ * Number of character columns in the page.
+ */
+ int columns;
+ /**
+ * The page contents, these are @a rows x @a columns without
+ * padding between the rows. See vbi_char for details.
+ */
+ vbi_char text[1056];
+
+ /**
+ * To speed up rendering these variables mark the rows
+ * which actually changed since the page has been last fetched
+ * from cache. @a y0 ... @a y1 are the first to last row changed,
+ * inclusive, in range 0 ... @a rows - 1. @a roll indicates the
+ * page has been vertically scrolled this number of rows,
+ * negative numbers up (towards lower row numbers), positive
+ * numbers down. For example -1 means row @a y0 + 1 ... @a y1
+ * moved to @a y0 ... @a y1 - 1, erasing row @a y1 to all spaces.
+ *
+ * Practically this is only used in Closed Caption roll-up
+ * mode, otherwise all rows are always marked dirty. Clients
+ * are free to ignore this information.
+ */
+ struct {
+ /* int x0, x1; */
+ int y0, y1;
+ int roll;
+ } dirty;
+
+ /**
+ * When a TV displays Teletext or Closed Caption
+ * pages, only a section in the center of the screen is
+ * actually covered by characters. The remaining space is
+ * referred to here as 'border', which can have a color different
+ * from the typical black. (In the Teletext specs this is referred
+ * to as the screen color, hence the field name.) This is a
+ * vbi_color index into the @a color_map.
+ */
+ vbi_color screen_color;
+ /**
+ * The 'border' can also have a distinguished
+ * opacity. Typically this will be VBI_OPAQUE, but pages intended
+ * for overlay onto video (Teletext subtitles, newsflash, Caption
+ * pages) will have a @a screen_opacity of VBI_TRANSPARENT_SPACE.
+ * See vbi_opacity for details.
+ */
+ vbi_opacity screen_opacity;
+ /**
+ * This is the color palette indexed by vbi_color in
+ * vbi_char and elsewhere, colors defined as vbi_rgba. Note this
+ * palette may not correspond to the vbi_color enumeration since
+ * Teletext allows editors to redefine the entire palette.
+ * Closed Caption and Teletext Level 1.0/1.5 pages use
+ * entries 0 ... 7. Teletext Level 2.5/3.5 pages use entries
+ * 0 ... 31. Navigation related text (TOP, FLOF) added by libzvbi
+ * uses entries 32 ... 39 which are not subject to redefinition.
+ */
+ vbi_rgba color_map[40];
+
+ /**
+ * DRCS (dynamically redefinable characters) can have
+ * two, four or sixteen different colors. Without further details,
+ * the effective color of each pixel is given by
+ * @code
+ * vbi_page->color_map[drcs_clut[drcs pixel color + vbi_char->drcs_clut_offs]],
+ * @endcode
+ * whereby drcs_clut[0] shall be replaced by vbi_char->foreground,
+ * drcs_clut[1] by vbi_char->background. (Renderers are supposed to convert the
+ * drcs_clut into a private color map of the desired pixel format.)
+ *
+ * Practically vbi_char->drcs_clut_offs encodes the DRCS color depth
+ * and selects between the vbi_char colors and one of two 4- or
+ * 16-entry Color Look-Up Tables. Also the different resolution of DRCS and
+ * the bitplane color coding is hidden to speed up rendering.
+ */
+ uint8_t * drcs_clut; /* 64 entries */
+ /**
+ * Pointer to DRCS data. Per definition the maximum number of DRCS
+ * usable at the same time, i. e. on one page, is limited to 96. However the
+ * number of DRCS defined in a Teletext data stream can be much larger. The
+ * 32 pointers here correspond to the 32 DRCS character planes mentioned
+ * in the vbi_char description. Each of them points to an array of character
+ * definitions, a DRCS font. One character occupies 60 bytes or 12 x 10 pixels,
+ * stored left to right and top to bottom. The color of each pixel (index
+ * into @a drcs_clut) is coded in four bits stored in little endian order,
+ * first pixel 0x0F, second pixel 0xF0 and so on. For example the first,
+ * top/leftmost pixel can be found at
+ * @code
+ * vbi_page->drcs[(unicode >> 6) & 0x1F][(unicode & 0x3F) * 60]
+ * @endcode
+ *
+ * Do not access DRCS data unless referenced by a vbi_char in @a text, a
+ * segfault may result. Do not access DRCS data after calling
+ * vbi_unref_page(), it may not be cached anymore.
+ */
+ uint8_t * drcs[32];
+
+ struct {
+ int pgno, subno;
+ } nav_link[6];
+ char nav_index[64];
+
+ struct vbi_font_descr * font[2];
+ unsigned int double_height_lower; /* legacy */
+
+ vbi_opacity page_opacity[2];
+ vbi_opacity boxed_opacity[2];
+} vbi_page;
+
+/* Private */
+
+#endif /* FORMAT_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/hamm-tables.h b/gst/closedcaption/hamm-tables.h
new file mode 100644
index 000000000..d51318621
--- /dev/null
+++ b/gst/closedcaption/hamm-tables.h
@@ -0,0 +1,307 @@
+/* Generated file, do not edit! */
+
+/* This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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,
+ Boston, MA 02110-1301 USA. */
+
+const uint8_t
+_vbi_bit_reverse [256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+const uint8_t
+_vbi_hamm8_fwd [16] = {
+ 0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
+ 0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea
+};
+
+const int8_t
+_vbi_hamm8_inv [256] = {
+ 0x01, 0xff, 0x01, 0x01, 0xff, 0x00, 0x01, 0xff,
+ 0xff, 0x02, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x07,
+ 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0x00,
+ 0x06, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x03, 0xff,
+ 0xff, 0x0c, 0x01, 0xff, 0x04, 0xff, 0xff, 0x07,
+ 0x06, 0xff, 0xff, 0x07, 0xff, 0x07, 0x07, 0x07,
+ 0x06, 0xff, 0xff, 0x05, 0xff, 0x00, 0x0d, 0xff,
+ 0x06, 0x06, 0x06, 0xff, 0x06, 0xff, 0xff, 0x07,
+ 0xff, 0x02, 0x01, 0xff, 0x04, 0xff, 0xff, 0x09,
+ 0x02, 0x02, 0xff, 0x02, 0xff, 0x02, 0x03, 0xff,
+ 0x08, 0xff, 0xff, 0x05, 0xff, 0x00, 0x03, 0xff,
+ 0xff, 0x02, 0x03, 0xff, 0x03, 0xff, 0x03, 0x03,
+ 0x04, 0xff, 0xff, 0x05, 0x04, 0x04, 0x04, 0xff,
+ 0xff, 0x02, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x07,
+ 0xff, 0x05, 0x05, 0x05, 0x04, 0xff, 0xff, 0x05,
+ 0x06, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x03, 0xff,
+ 0xff, 0x0c, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x09,
+ 0x0a, 0xff, 0xff, 0x0b, 0x0a, 0x0a, 0x0a, 0xff,
+ 0x08, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x0d, 0xff,
+ 0xff, 0x0b, 0x0b, 0x0b, 0x0a, 0xff, 0xff, 0x0b,
+ 0x0c, 0x0c, 0xff, 0x0c, 0xff, 0x0c, 0x0d, 0xff,
+ 0xff, 0x0c, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x07,
+ 0xff, 0x0c, 0x0d, 0xff, 0x0d, 0xff, 0x0d, 0x0d,
+ 0x06, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x0d, 0xff,
+ 0x08, 0xff, 0xff, 0x09, 0xff, 0x09, 0x09, 0x09,
+ 0xff, 0x02, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x09,
+ 0x08, 0x08, 0x08, 0xff, 0x08, 0xff, 0xff, 0x09,
+ 0x08, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x03, 0xff,
+ 0xff, 0x0c, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x09,
+ 0x0f, 0xff, 0x0f, 0x0f, 0xff, 0x0e, 0x0f, 0xff,
+ 0x08, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x0d, 0xff,
+ 0xff, 0x0e, 0x0f, 0xff, 0x0e, 0x0e, 0xff, 0x0e
+};
+
+static const uint8_t
+_vbi_hamm24_fwd_0 [256] = {
+ 0x8b, 0x8c, 0x92, 0x95, 0xa1, 0xa6, 0xb8, 0xbf,
+ 0xc0, 0xc7, 0xd9, 0xde, 0xea, 0xed, 0xf3, 0xf4,
+ 0x0a, 0x0d, 0x13, 0x14, 0x20, 0x27, 0x39, 0x3e,
+ 0x41, 0x46, 0x58, 0x5f, 0x6b, 0x6c, 0x72, 0x75,
+ 0x09, 0x0e, 0x10, 0x17, 0x23, 0x24, 0x3a, 0x3d,
+ 0x42, 0x45, 0x5b, 0x5c, 0x68, 0x6f, 0x71, 0x76,
+ 0x88, 0x8f, 0x91, 0x96, 0xa2, 0xa5, 0xbb, 0xbc,
+ 0xc3, 0xc4, 0xda, 0xdd, 0xe9, 0xee, 0xf0, 0xf7,
+ 0x08, 0x0f, 0x11, 0x16, 0x22, 0x25, 0x3b, 0x3c,
+ 0x43, 0x44, 0x5a, 0x5d, 0x69, 0x6e, 0x70, 0x77,
+ 0x89, 0x8e, 0x90, 0x97, 0xa3, 0xa4, 0xba, 0xbd,
+ 0xc2, 0xc5, 0xdb, 0xdc, 0xe8, 0xef, 0xf1, 0xf6,
+ 0x8a, 0x8d, 0x93, 0x94, 0xa0, 0xa7, 0xb9, 0xbe,
+ 0xc1, 0xc6, 0xd8, 0xdf, 0xeb, 0xec, 0xf2, 0xf5,
+ 0x0b, 0x0c, 0x12, 0x15, 0x21, 0x26, 0x38, 0x3f,
+ 0x40, 0x47, 0x59, 0x5e, 0x6a, 0x6d, 0x73, 0x74,
+ 0x03, 0x04, 0x1a, 0x1d, 0x29, 0x2e, 0x30, 0x37,
+ 0x48, 0x4f, 0x51, 0x56, 0x62, 0x65, 0x7b, 0x7c,
+ 0x82, 0x85, 0x9b, 0x9c, 0xa8, 0xaf, 0xb1, 0xb6,
+ 0xc9, 0xce, 0xd0, 0xd7, 0xe3, 0xe4, 0xfa, 0xfd,
+ 0x81, 0x86, 0x98, 0x9f, 0xab, 0xac, 0xb2, 0xb5,
+ 0xca, 0xcd, 0xd3, 0xd4, 0xe0, 0xe7, 0xf9, 0xfe,
+ 0x00, 0x07, 0x19, 0x1e, 0x2a, 0x2d, 0x33, 0x34,
+ 0x4b, 0x4c, 0x52, 0x55, 0x61, 0x66, 0x78, 0x7f,
+ 0x80, 0x87, 0x99, 0x9e, 0xaa, 0xad, 0xb3, 0xb4,
+ 0xcb, 0xcc, 0xd2, 0xd5, 0xe1, 0xe6, 0xf8, 0xff,
+ 0x01, 0x06, 0x18, 0x1f, 0x2b, 0x2c, 0x32, 0x35,
+ 0x4a, 0x4d, 0x53, 0x54, 0x60, 0x67, 0x79, 0x7e,
+ 0x02, 0x05, 0x1b, 0x1c, 0x28, 0x2f, 0x31, 0x36,
+ 0x49, 0x4e, 0x50, 0x57, 0x63, 0x64, 0x7a, 0x7d,
+ 0x83, 0x84, 0x9a, 0x9d, 0xa9, 0xae, 0xb0, 0xb7,
+ 0xc8, 0xcf, 0xd1, 0xd6, 0xe2, 0xe5, 0xfb, 0xfc
+};
+
+static const uint8_t
+_vbi_hamm24_fwd_1 [256] = {
+ 0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
+ 0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89,
+ 0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
+ 0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
+ 0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
+ 0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
+ 0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89,
+ 0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
+ 0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
+ 0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
+ 0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
+ 0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
+ 0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
+ 0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
+ 0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
+ 0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
+ 0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
+ 0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
+ 0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
+ 0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
+ 0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
+ 0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
+ 0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
+ 0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
+ 0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89,
+ 0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
+ 0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
+ 0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
+ 0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
+ 0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
+ 0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
+ 0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89
+};
+
+static const uint8_t
+_vbi_hamm24_fwd_2 [4] = {
+ 0x00, 0x0a, 0x0b, 0x01
+};
+
+const int8_t
+_vbi_hamm24_inv_par [3][256] = {
+ {
+ 0x00, 0x21, 0x22, 0x03, 0x23, 0x02, 0x01, 0x20,
+ 0x24, 0x05, 0x06, 0x27, 0x07, 0x26, 0x25, 0x04,
+ 0x25, 0x04, 0x07, 0x26, 0x06, 0x27, 0x24, 0x05,
+ 0x01, 0x20, 0x23, 0x02, 0x22, 0x03, 0x00, 0x21,
+ 0x26, 0x07, 0x04, 0x25, 0x05, 0x24, 0x27, 0x06,
+ 0x02, 0x23, 0x20, 0x01, 0x21, 0x00, 0x03, 0x22,
+ 0x03, 0x22, 0x21, 0x00, 0x20, 0x01, 0x02, 0x23,
+ 0x27, 0x06, 0x05, 0x24, 0x04, 0x25, 0x26, 0x07,
+ 0x27, 0x06, 0x05, 0x24, 0x04, 0x25, 0x26, 0x07,
+ 0x03, 0x22, 0x21, 0x00, 0x20, 0x01, 0x02, 0x23,
+ 0x02, 0x23, 0x20, 0x01, 0x21, 0x00, 0x03, 0x22,
+ 0x26, 0x07, 0x04, 0x25, 0x05, 0x24, 0x27, 0x06,
+ 0x01, 0x20, 0x23, 0x02, 0x22, 0x03, 0x00, 0x21,
+ 0x25, 0x04, 0x07, 0x26, 0x06, 0x27, 0x24, 0x05,
+ 0x24, 0x05, 0x06, 0x27, 0x07, 0x26, 0x25, 0x04,
+ 0x00, 0x21, 0x22, 0x03, 0x23, 0x02, 0x01, 0x20,
+ 0x28, 0x09, 0x0a, 0x2b, 0x0b, 0x2a, 0x29, 0x08,
+ 0x0c, 0x2d, 0x2e, 0x0f, 0x2f, 0x0e, 0x0d, 0x2c,
+ 0x0d, 0x2c, 0x2f, 0x0e, 0x2e, 0x0f, 0x0c, 0x2d,
+ 0x29, 0x08, 0x0b, 0x2a, 0x0a, 0x2b, 0x28, 0x09,
+ 0x0e, 0x2f, 0x2c, 0x0d, 0x2d, 0x0c, 0x0f, 0x2e,
+ 0x2a, 0x0b, 0x08, 0x29, 0x09, 0x28, 0x2b, 0x0a,
+ 0x2b, 0x0a, 0x09, 0x28, 0x08, 0x29, 0x2a, 0x0b,
+ 0x0f, 0x2e, 0x2d, 0x0c, 0x2c, 0x0d, 0x0e, 0x2f,
+ 0x0f, 0x2e, 0x2d, 0x0c, 0x2c, 0x0d, 0x0e, 0x2f,
+ 0x2b, 0x0a, 0x09, 0x28, 0x08, 0x29, 0x2a, 0x0b,
+ 0x2a, 0x0b, 0x08, 0x29, 0x09, 0x28, 0x2b, 0x0a,
+ 0x0e, 0x2f, 0x2c, 0x0d, 0x2d, 0x0c, 0x0f, 0x2e,
+ 0x29, 0x08, 0x0b, 0x2a, 0x0a, 0x2b, 0x28, 0x09,
+ 0x0d, 0x2c, 0x2f, 0x0e, 0x2e, 0x0f, 0x0c, 0x2d,
+ 0x0c, 0x2d, 0x2e, 0x0f, 0x2f, 0x0e, 0x0d, 0x2c,
+ 0x28, 0x09, 0x0a, 0x2b, 0x0b, 0x2a, 0x29, 0x08
+ }, {
+ 0x00, 0x29, 0x2a, 0x03, 0x2b, 0x02, 0x01, 0x28,
+ 0x2c, 0x05, 0x06, 0x2f, 0x07, 0x2e, 0x2d, 0x04,
+ 0x2d, 0x04, 0x07, 0x2e, 0x06, 0x2f, 0x2c, 0x05,
+ 0x01, 0x28, 0x2b, 0x02, 0x2a, 0x03, 0x00, 0x29,
+ 0x2e, 0x07, 0x04, 0x2d, 0x05, 0x2c, 0x2f, 0x06,
+ 0x02, 0x2b, 0x28, 0x01, 0x29, 0x00, 0x03, 0x2a,
+ 0x03, 0x2a, 0x29, 0x00, 0x28, 0x01, 0x02, 0x2b,
+ 0x2f, 0x06, 0x05, 0x2c, 0x04, 0x2d, 0x2e, 0x07,
+ 0x2f, 0x06, 0x05, 0x2c, 0x04, 0x2d, 0x2e, 0x07,
+ 0x03, 0x2a, 0x29, 0x00, 0x28, 0x01, 0x02, 0x2b,
+ 0x02, 0x2b, 0x28, 0x01, 0x29, 0x00, 0x03, 0x2a,
+ 0x2e, 0x07, 0x04, 0x2d, 0x05, 0x2c, 0x2f, 0x06,
+ 0x01, 0x28, 0x2b, 0x02, 0x2a, 0x03, 0x00, 0x29,
+ 0x2d, 0x04, 0x07, 0x2e, 0x06, 0x2f, 0x2c, 0x05,
+ 0x2c, 0x05, 0x06, 0x2f, 0x07, 0x2e, 0x2d, 0x04,
+ 0x00, 0x29, 0x2a, 0x03, 0x2b, 0x02, 0x01, 0x28,
+ 0x30, 0x19, 0x1a, 0x33, 0x1b, 0x32, 0x31, 0x18,
+ 0x1c, 0x35, 0x36, 0x1f, 0x37, 0x1e, 0x1d, 0x34,
+ 0x1d, 0x34, 0x37, 0x1e, 0x36, 0x1f, 0x1c, 0x35,
+ 0x31, 0x18, 0x1b, 0x32, 0x1a, 0x33, 0x30, 0x19,
+ 0x1e, 0x37, 0x34, 0x1d, 0x35, 0x1c, 0x1f, 0x36,
+ 0x32, 0x1b, 0x18, 0x31, 0x19, 0x30, 0x33, 0x1a,
+ 0x33, 0x1a, 0x19, 0x30, 0x18, 0x31, 0x32, 0x1b,
+ 0x1f, 0x36, 0x35, 0x1c, 0x34, 0x1d, 0x1e, 0x37,
+ 0x1f, 0x36, 0x35, 0x1c, 0x34, 0x1d, 0x1e, 0x37,
+ 0x33, 0x1a, 0x19, 0x30, 0x18, 0x31, 0x32, 0x1b,
+ 0x32, 0x1b, 0x18, 0x31, 0x19, 0x30, 0x33, 0x1a,
+ 0x1e, 0x37, 0x34, 0x1d, 0x35, 0x1c, 0x1f, 0x36,
+ 0x31, 0x18, 0x1b, 0x32, 0x1a, 0x33, 0x30, 0x19,
+ 0x1d, 0x34, 0x37, 0x1e, 0x36, 0x1f, 0x1c, 0x35,
+ 0x1c, 0x35, 0x36, 0x1f, 0x37, 0x1e, 0x1d, 0x34,
+ 0x30, 0x19, 0x1a, 0x33, 0x1b, 0x32, 0x31, 0x18
+ }, {
+ 0x3f, 0x0e, 0x0d, 0x3c, 0x0c, 0x3d, 0x3e, 0x0f,
+ 0x0b, 0x3a, 0x39, 0x08, 0x38, 0x09, 0x0a, 0x3b,
+ 0x0a, 0x3b, 0x38, 0x09, 0x39, 0x08, 0x0b, 0x3a,
+ 0x3e, 0x0f, 0x0c, 0x3d, 0x0d, 0x3c, 0x3f, 0x0e,
+ 0x09, 0x38, 0x3b, 0x0a, 0x3a, 0x0b, 0x08, 0x39,
+ 0x3d, 0x0c, 0x0f, 0x3e, 0x0e, 0x3f, 0x3c, 0x0d,
+ 0x3c, 0x0d, 0x0e, 0x3f, 0x0f, 0x3e, 0x3d, 0x0c,
+ 0x08, 0x39, 0x3a, 0x0b, 0x3b, 0x0a, 0x09, 0x38,
+ 0x08, 0x39, 0x3a, 0x0b, 0x3b, 0x0a, 0x09, 0x38,
+ 0x3c, 0x0d, 0x0e, 0x3f, 0x0f, 0x3e, 0x3d, 0x0c,
+ 0x3d, 0x0c, 0x0f, 0x3e, 0x0e, 0x3f, 0x3c, 0x0d,
+ 0x09, 0x38, 0x3b, 0x0a, 0x3a, 0x0b, 0x08, 0x39,
+ 0x3e, 0x0f, 0x0c, 0x3d, 0x0d, 0x3c, 0x3f, 0x0e,
+ 0x0a, 0x3b, 0x38, 0x09, 0x39, 0x08, 0x0b, 0x3a,
+ 0x0b, 0x3a, 0x39, 0x08, 0x38, 0x09, 0x0a, 0x3b,
+ 0x3f, 0x0e, 0x0d, 0x3c, 0x0c, 0x3d, 0x3e, 0x0f,
+ 0x1f, 0x2e, 0x2d, 0x1c, 0x2c, 0x1d, 0x1e, 0x2f,
+ 0x2b, 0x1a, 0x19, 0x28, 0x18, 0x29, 0x2a, 0x1b,
+ 0x2a, 0x1b, 0x18, 0x29, 0x19, 0x28, 0x2b, 0x1a,
+ 0x1e, 0x2f, 0x2c, 0x1d, 0x2d, 0x1c, 0x1f, 0x2e,
+ 0x29, 0x18, 0x1b, 0x2a, 0x1a, 0x2b, 0x28, 0x19,
+ 0x1d, 0x2c, 0x2f, 0x1e, 0x2e, 0x1f, 0x1c, 0x2d,
+ 0x1c, 0x2d, 0x2e, 0x1f, 0x2f, 0x1e, 0x1d, 0x2c,
+ 0x28, 0x19, 0x1a, 0x2b, 0x1b, 0x2a, 0x29, 0x18,
+ 0x28, 0x19, 0x1a, 0x2b, 0x1b, 0x2a, 0x29, 0x18,
+ 0x1c, 0x2d, 0x2e, 0x1f, 0x2f, 0x1e, 0x1d, 0x2c,
+ 0x1d, 0x2c, 0x2f, 0x1e, 0x2e, 0x1f, 0x1c, 0x2d,
+ 0x29, 0x18, 0x1b, 0x2a, 0x1a, 0x2b, 0x28, 0x19,
+ 0x1e, 0x2f, 0x2c, 0x1d, 0x2d, 0x1c, 0x1f, 0x2e,
+ 0x2a, 0x1b, 0x18, 0x29, 0x19, 0x28, 0x2b, 0x1a,
+ 0x2b, 0x1a, 0x19, 0x28, 0x18, 0x29, 0x2a, 0x1b,
+ 0x1f, 0x2e, 0x2d, 0x1c, 0x2c, 0x1d, 0x1e, 0x2f
+ }
+};
+
+static const uint8_t
+_vbi_hamm24_inv_d1_d4 [64] = {
+ 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
+ 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
+ 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f,
+ 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
+ 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
+ 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f
+};
+
+static const int32_t
+_vbi_hamm24_inv_err [64] = {
+ 0x00000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000000, 0x00000002, 0x00000004, 0x00000008,
+ 0x00000000, 0x00000010, 0x00000020, 0x00000040,
+ 0x00000080, 0x00000100, 0x00000200, 0x00000400,
+ 0x00000000, 0x00000800, 0x00001000, 0x00002000,
+ 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000, 0x80000000, 0x80000000
+};
diff --git a/gst/closedcaption/hamm.c b/gst/closedcaption/hamm.c
new file mode 100644
index 000000000..0cec95b42
--- /dev/null
+++ b/gst/closedcaption/hamm.c
@@ -0,0 +1,164 @@
+/*
+ * libzvbi -- Error correction functions
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004, 2007 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: hamm.c,v 1.11 2013-07-10 11:37:08 mschimek Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h> /* CHAR_BIT */
+
+#include "hamm.h"
+#include "hamm-tables.h"
+
+/**
+ * @ingroup Error
+ *
+ * @param p Array of unsigned bytes.
+ * @param n Size of array.
+ *
+ * Of each byte of the array, changes the most significant
+ * bit to make the number of set bits odd.
+ *
+ * @since 0.2.12
+ */
+void
+vbi_par (uint8_t * p, unsigned int n)
+{
+ while (n-- > 0) {
+ uint8_t c = *p;
+
+ /* if 0 == (inv_par[] & 32) change msb of *p. */
+ *p++ = c ^ (128 & ~(_vbi_hamm24_inv_par[0][c] << 2));
+ }
+}
+
+/**
+ * @ingroup Error
+ * @param p Array of unsigned bytes.
+ * @param n Size of array.
+ *
+ * Tests the parity and clears the most significant bit of
+ * each byte of the array.
+ *
+ * @return
+ * A negative value if any byte of the array had even
+ * parity (sum of bits modulo 2 is 0).
+ *
+ * @since 0.2.12
+ */
+int
+vbi_unpar (uint8_t * p, unsigned int n)
+{
+ int r = 0;
+
+ while (n-- > 0) {
+ uint8_t c = *p;
+
+ /* if 0 == (inv_par[] & 32) set msb of r. */
+ r |= ~_vbi_hamm24_inv_par[0][c]
+ << (sizeof (int) * CHAR_BIT - 1 - 5);
+
+ *p++ = c & 127;
+ }
+
+ return r;
+}
+
+/**
+ * @ingroup Error
+ * @param p A Hamming 24/18 protected 24 bit word will be stored here,
+ * last significant byte first, lsb first transmitted.
+ * @param c Integer between 0 ... 1 << 18 - 1.
+ *
+ * Encodes an 18 bit word with Hamming 24/18 protection
+ * as specified in ETS 300 706, Section 8.3.
+ *
+ * @since 0.2.27
+ */
+void
+vbi_ham24p (uint8_t * p, unsigned int c)
+{
+ unsigned int D5_D11;
+ unsigned int D12_D18;
+ unsigned int P5, P6;
+ unsigned int Byte_0;
+
+ Byte_0 = (_vbi_hamm24_fwd_0[(c >> 0) & 0xFF]
+ ^ _vbi_hamm24_fwd_1[(c >> 8) & 0xFF]
+ ^ _vbi_hamm24_fwd_2[(c >> 16) & 0x03]);
+ p[0] = Byte_0;
+
+ D5_D11 = (c >> 4) & 0x7F;
+ D12_D18 = (c >> 11) & 0x7F;
+
+ P5 = 0x80 & ~(_vbi_hamm24_inv_par[0][D12_D18] << 2);
+ p[1] = D5_D11 | P5;
+
+ P6 = 0x80 & ((_vbi_hamm24_inv_par[0][Byte_0]
+ ^ _vbi_hamm24_inv_par[0][D5_D11]) << 2);
+ p[2] = D12_D18 | P6;
+}
+
+/**
+ * @ingroup Error
+ * @param p Pointer to a Hamming 24/18 protected 24 bit word,
+ * last significant byte first, lsb first transmitted.
+ *
+ * Decodes a Hamming 24/18 protected byte triplet
+ * as specified in ETS 300 706, Section 8.3.
+ *
+ * @return
+ * Triplet data bits D18 [msb] ... D1 [lsb] or a negative value
+ * if the triplet contained uncorrectable errors.
+ *
+ * @since 0.2.12
+ */
+int
+vbi_unham24p (const uint8_t * p)
+{
+ unsigned int D1_D4;
+ unsigned int D5_D11;
+ unsigned int D12_D18;
+ unsigned int ABCDEF;
+ int32_t d;
+
+ D1_D4 = _vbi_hamm24_inv_d1_d4[p[0] >> 2];
+ D5_D11 = p[1] & 0x7F;
+ D12_D18 = p[2] & 0x7F;
+
+ d = D1_D4 | (D5_D11 << 4) | (D12_D18 << 11);
+
+ ABCDEF = (_vbi_hamm24_inv_par[0][p[0]]
+ ^ _vbi_hamm24_inv_par[1][p[1]]
+ ^ _vbi_hamm24_inv_par[2][p[2]]);
+
+ /* Correct single bit error, set MSB on double bit error. */
+ return d ^ (int) _vbi_hamm24_inv_err[ABCDEF];
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/hamm.h b/gst/closedcaption/hamm.h
new file mode 100644
index 000000000..2fd8da700
--- /dev/null
+++ b/gst/closedcaption/hamm.h
@@ -0,0 +1,233 @@
+/*
+ * libzvbi -- Error correction functions
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004, 2007 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: hamm.h,v 1.16 2013-07-10 11:37:13 mschimek Exp $ */
+
+#ifndef __ZVBI_HAMM_H__
+#define __ZVBI_HAMM_H__
+
+#include <inttypes.h> /* uintN_t */
+#include "macros.h"
+
+VBI_BEGIN_DECLS
+
+/* Public */
+
+extern const uint8_t _vbi_bit_reverse [256];
+extern const uint8_t _vbi_hamm8_fwd [16];
+extern const int8_t _vbi_hamm8_inv [256];
+extern const int8_t _vbi_hamm24_inv_par [3][256];
+
+/**
+ * @addtogroup Error Error correction functions
+ * @ingroup Raw
+ * @brief Helper functions to decode sliced VBI data.
+ * @{
+ */
+
+/**
+ * @param c Unsigned byte.
+ *
+ * Reverses the bits of the argument.
+ *
+ * @returns
+ * Data bits 0 [msb] ... 7 [lsb].
+ *
+ * @since 0.2.12
+ */
+_vbi_inline unsigned int
+vbi_rev8 (unsigned int c)
+{
+ return _vbi_bit_reverse[(uint8_t) c];
+}
+
+/**
+ * @param c Unsigned 16 bit word.
+ *
+ * Reverses (or "reflects") the bits of the argument.
+ *
+ * @returns
+ * Data bits 0 [msb] ... 15 [lsb].
+ *
+ * @since 0.2.12
+ */
+_vbi_inline unsigned int
+vbi_rev16 (unsigned int c)
+{
+ return _vbi_bit_reverse[(uint8_t) c] * 256
+ + _vbi_bit_reverse[(uint8_t)(c >> 8)];
+}
+
+/**
+ * @param p Pointer to a 16 bit word, last significant
+ * byte first.
+ *
+ * Reverses (or "reflects") the bits of the argument.
+ *
+ * @returns
+ * Data bits 0 [msb] ... 15 [lsb].
+ *
+ * @since 0.2.12
+ */
+_vbi_inline unsigned int
+vbi_rev16p (const uint8_t * p)
+{
+ return _vbi_bit_reverse[p[0]] * 256
+ + _vbi_bit_reverse[p[1]];
+}
+
+/**
+ * @param c Unsigned byte.
+ *
+ * @returns
+ * Changes the most significant bit of the byte
+ * to make the number of set bits odd.
+ *
+ * @since 0.2.12
+ */
+_vbi_inline unsigned int
+vbi_par8 (unsigned int c)
+{
+ c &= 255;
+
+ /* if 0 == (inv_par[] & 32) change bit 7 of c. */
+ c ^= 128 & ~(_vbi_hamm24_inv_par[0][c] << 2);
+
+ return c;
+}
+
+/**
+ * @param c Unsigned byte.
+ *
+ * @returns
+ * If the byte has odd parity (sum of bits modulo 2 is 1) the
+ * byte AND 127, otherwise a negative value.
+ *
+ * @since 0.2.12
+ */
+_vbi_inline int
+vbi_unpar8 (unsigned int c)
+{
+/* Disabled until someone finds a reliable way
+ to test for cmov support at compile time. */
+#if 0
+ int r = c & 127;
+
+ /* This saves cache flushes and an explicit branch. */
+ __asm__ (" testb %1,%1\n"
+ " cmovp %2,%0\n"
+ : "+&a" (r) : "c" (c), "rm" (-1));
+ return r;
+#endif
+ if (_vbi_hamm24_inv_par[0][(uint8_t) c] & 32) {
+ return c & 127;
+ } else {
+ /* The idea is to OR results together to find a parity
+ error in a sequence, rather than a test and branch on
+ each byte. */
+ return -1;
+ }
+}
+
+extern void
+vbi_par (uint8_t * p,
+ unsigned int n);
+extern int
+vbi_unpar (uint8_t * p,
+ unsigned int n);
+
+/**
+ * @param c Integer between 0 ... 15.
+ *
+ * Encodes a nibble with Hamming 8/4 protection
+ * as specified in EN 300 706, Section 8.2.
+ *
+ * @returns
+ * Hamming encoded unsigned byte, lsb first transmitted.
+ *
+ * @since 0.2.12
+ */
+_vbi_inline unsigned int
+vbi_ham8 (unsigned int c)
+{
+ return _vbi_hamm8_fwd[c & 15];
+}
+
+/**
+ * @param c Hamming 8/4 protected byte, lsb first transmitted.
+ *
+ * Decodes a Hamming 8/4 protected byte
+ * as specified in EN 300 706, Section 8.2.
+ *
+ * @returns
+ * Data bits (D4 [msb] ... D1 [lsb]) or a negative
+ * value if the byte contained uncorrectable errors.
+ *
+ * @since 0.2.12
+ */
+_vbi_inline int
+vbi_unham8 (unsigned int c)
+{
+ return _vbi_hamm8_inv[(uint8_t) c];
+}
+
+/**
+ * @param p Pointer to a Hamming 8/4 protected 16 bit word,
+ * last significant byte first, lsb first transmitted.
+ *
+ * Decodes a Hamming 8/4 protected byte pair
+ * as specified in EN 300 706, Section 8.2.
+ *
+ * @returns
+ * Data bits D4 [msb] ... D1 of first byte and D4 ... D1 [lsb]
+ * of second byte, or a negative value if any of the bytes
+ * contained uncorrectable errors.
+ *
+ * @since 0.2.12
+ */
+_vbi_inline int
+vbi_unham16p (const uint8_t * p)
+{
+ return ((int) _vbi_hamm8_inv[p[0]])
+ | (((int) _vbi_hamm8_inv[p[1]]) << 4);
+}
+
+extern void
+vbi_ham24p (uint8_t * p,
+ unsigned int c);
+extern int
+vbi_unham24p (const uint8_t * p)
+ _vbi_pure;
+
+/** @} */
+
+/* Private */
+
+VBI_END_DECLS
+
+#endif /* __ZVBI_HAMM_H__ */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/lang.c b/gst/closedcaption/lang.c
new file mode 100644
index 000000000..805f2dc89
--- /dev/null
+++ b/gst/closedcaption/lang.c
@@ -0,0 +1,907 @@
+/*
+ * libzvbi - Teletext and Closed Caption character set
+ *
+ * Copyright (C) 2000, 2001 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: lang.c,v 1.18 2016-02-08 07:30:48 mschimek Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "lang.h"
+
+/* *INDENT-OFF* */
+
+/*
+ * teletext font descriptors
+ *
+ * ETS 300 706 Table 32, 33, 34
+ */
+struct vbi_font_descr
+vbi_font_descriptors[88] = {
+ /* 0 - Western and Central Europe */
+ { LATIN_G0, LATIN_G2, ENGLISH, "English" },
+ { LATIN_G0, LATIN_G2, GERMAN, "Deutsch" },
+ { LATIN_G0, LATIN_G2, SWE_FIN_HUN, "Svenska / Suomi / Magyar" },
+ { LATIN_G0, LATIN_G2, ITALIAN, "Italiano" },
+ { LATIN_G0, LATIN_G2, FRENCH, "Français" },
+ { LATIN_G0, LATIN_G2, PORTUG_SPANISH, "Português / Español" },
+ { LATIN_G0, LATIN_G2, CZECH_SLOVAK, "Cesky / Slovencina" },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+
+ /* 8 - Eastern Europe */
+ { LATIN_G0, LATIN_G2, POLISH, "Polski" },
+ { LATIN_G0, LATIN_G2, GERMAN, "Deutsch" },
+ { LATIN_G0, LATIN_G2, SWE_FIN_HUN, "Svenska / Suomi / Magyar" },
+ { LATIN_G0, LATIN_G2, ITALIAN, "Italiano" },
+ { LATIN_G0, LATIN_G2, FRENCH, "Français" },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, CZECH_SLOVAK, "Cesky / Slovencina" },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+
+ /* 16 - Western Europe and Turkey */
+ { LATIN_G0, LATIN_G2, ENGLISH, "English" },
+ { LATIN_G0, LATIN_G2, GERMAN, "Deutsch" },
+ { LATIN_G0, LATIN_G2, SWE_FIN_HUN, "Svenska / Suomi / Magyar" },
+ { LATIN_G0, LATIN_G2, ITALIAN, "Italiano" },
+ { LATIN_G0, LATIN_G2, FRENCH, "Français" },
+ { LATIN_G0, LATIN_G2, PORTUG_SPANISH, "Português / Español" },
+ { LATIN_G0, LATIN_G2, TURKISH, "Türkçe" },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+
+ /* 24 - Central and Southeast Europe */
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, SERB_CRO_SLO, "Srbski / Hrvatski / Slovenscina" },
+ { LATIN_G0, LATIN_G2, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, RUMANIAN, "Româna" },
+
+ /* 32 - Cyrillic */
+ { CYRILLIC_1_G0, CYRILLIC_G2, NO_SUBSET, "Srpski / Hrvatski" },
+ { LATIN_G0, LATIN_G2, GERMAN, "Deutsch" },
+ { LATIN_G0, LATIN_G2, ESTONIAN, "Eesti" },
+ { LATIN_G0, LATIN_G2, LETT_LITH, "Lettish / Lietuviskai" },
+ { CYRILLIC_2_G0, CYRILLIC_G2, NO_SUBSET, "Russky / Balgarski " },
+ { CYRILLIC_3_G0, CYRILLIC_G2, NO_SUBSET, "Ukrayins'ka" },
+ { LATIN_G0, LATIN_G2, CZECH_SLOVAK, "Cesky / Slovencina" },
+ { 0, 0, NO_SUBSET, 0 },
+
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+
+ /* 48 */
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { LATIN_G0, LATIN_G2, TURKISH, "Türkçe" },
+ { GREEK_G0, GREEK_G2, NO_SUBSET, "Ellinika'" },
+
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+
+ /* 64 - Arabic */
+ { LATIN_G0, ARABIC_G2, ENGLISH, "Alarabia / English" },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { LATIN_G0, ARABIC_G2, FRENCH, "Alarabia / Français" },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { ARABIC_G0, ARABIC_G2, NO_SUBSET, "Alarabia" },
+
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+
+ /* 80 */
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { 0, 0, NO_SUBSET, 0 },
+ { HEBREW_G0, ARABIC_G2, NO_SUBSET, "Ivrit" },
+ { 0, 0, NO_SUBSET, 0 },
+ { ARABIC_G0, ARABIC_G2, NO_SUBSET, "Alarabia" },
+};
+
+#if 0
+
+/*
+ * These characters mimic a few common block mosaic, smooth mosaic
+ * or line drawing character patterns, eg. horizontal bars
+ *
+ * Not currently used (anymore), maybe later.
+ */
+static const unsigned char
+gfx_transcript[] = {
+/* 2 */ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
+/* 3 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00,
+/* 2 */ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
+/* 3 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00,
+/* 6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00,
+/* 7 */ 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+/* 6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00,
+/* 7 */ 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+/* 2 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 3 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 4 */ 0x2B, 0x2B, 0x2B, 0x2B, 0x3C, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x2A, 0x2A, 0x6F,
+/* 5 */ 0x7C, 0x2D, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x3E, 0x3C, 0x00, 0x00, 0x00,
+/* 6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#endif
+
+/*
+ * Teletext character set
+ *
+ * (Similar to ISO 6937 -
+ * ftp://dkuug.dk/i18n/charmaps/ISO_6937)
+ */
+
+/*
+ * ETS 300 706 Table 36: Latin National Option Sub-sets
+ *
+ * Latin G0 character code to Unicode mapping per national subset,
+ * unmodified codes (NO_SUBSET) in row zero.
+ *
+ * [13][0] Turkish currency symbol not in Unicode, use private code U+E800
+ */
+static const unsigned short
+national_subset[14][13] = {
+ { 0x0023u, 0x0024u, 0x0040u, 0x005Bu, 0x005Cu, 0x005Du, 0x005Eu, 0x005Fu, 0x0060u, 0x007Bu, 0x007Cu, 0x007Du, 0x007Eu },
+ { 0x0023u, 0x016Fu, 0x010Du, 0x0165u, 0x017Eu, 0x00FDu, 0x00EDu, 0x0159u, 0x00E9u, 0x00E1u, 0x011Bu, 0x00FAu, 0x0161u },
+ { 0x00A3u, 0x0024u, 0x0040u, 0x2190u, 0x00BDu, 0x2192u, 0x2191u, 0x0023u, 0x2014u, 0x00BCu, 0x2016u, 0x00BEu, 0x00F7u },
+ { 0x0023u, 0x00F5u, 0x0160u, 0x00C4u, 0x00D6u, 0x017Du, 0x00DCu, 0x00D5u, 0x0161u, 0x00E4u, 0x00F6u, 0x017Eu, 0x00FCu },
+ { 0x00E9u, 0x00EFu, 0x00E0u, 0x00EBu, 0x00EAu, 0x00F9u, 0x00EEu, 0x0023u, 0x00E8u, 0x00E2u, 0x00F4u, 0x00FBu, 0x00E7u },
+ { 0x0023u, 0x0024u, 0x00A7u, 0x00C4u, 0x00D6u, 0x00DCu, 0x005Eu, 0x005Fu, 0x00B0u, 0x00E4u, 0x00F6u, 0x00FCu, 0x00DFu },
+ { 0x00A3u, 0x0024u, 0x00E9u, 0x00B0u, 0x00E7u, 0x2192u, 0x2191u, 0x0023u, 0x00F9u, 0x00E0u, 0x00F2u, 0x00E8u, 0x00ECu },
+ { 0x0023u, 0x0024u, 0x0160u, 0x0117u, 0x0229u, 0x017Du, 0x010Du, 0x016Bu, 0x0161u, 0x0105u, 0x0173u, 0x017Eu, 0x012Fu },
+ { 0x0023u, 0x0144u, 0x0105u, 0x01B5u, 0x015Au, 0x0141u, 0x0107u, 0x00F3u, 0x0119u, 0x017Cu, 0x015Bu, 0x0142u, 0x017Au },
+ { 0x00E7u, 0x0024u, 0x00A1u, 0x00E1u, 0x00E9u, 0x00EDu, 0x00F3u, 0x00FAu, 0x00BFu, 0x00FCu, 0x00F1u, 0x00E8u, 0x00E0u },
+ { 0x0023u, 0x00A4u, 0x0162u, 0x00C2u, 0x015Eu, 0x01CDu, 0x00CDu, 0x0131u, 0x0163u, 0x00E2u, 0x015Fu, 0X01CEu, 0x00EEu },
+ { 0x0023u, 0x00CBu, 0x010Cu, 0x0106u, 0x017Du, 0x00D0u, 0x0160u, 0x00EBu, 0x010Du, 0x0107u, 0x017Eu, 0x00F0u, 0x0161u },
+ { 0x0023u, 0x00A4u, 0x00C9u, 0x00C4u, 0x00D6u, 0x00C5u, 0x00DCu, 0x005Fu, 0x00E9u, 0x00E4u, 0x00F6u, 0x00E5u, 0x00FCu },
+ { 0xE800u, 0x011Fu, 0x0130u, 0x015Eu, 0x00D6u, 0x00C7u, 0x00DCu, 0x011Eu, 0x0131u, 0x015Fu, 0x00F6u, 0x00E7u, 0x00FCu }
+};
+
+/*
+ * ETS 300 706 Table 37: Latin G2 Supplementary Set
+ *
+ * 0x49 seems to be dot below; not in Unicode (except combining), use U+002E.
+ */
+static const unsigned short
+latin_g2[96] = {
+ 0x00A0u, 0x00A1u, 0x00A2u, 0x00A3u, 0x0024u, 0x00A5u, 0x0023u, 0x00A7u, 0x00A4u, 0x2018u, 0x201Cu, 0x00ABu, 0x2190u, 0x2191u, 0x2192u, 0x2193u,
+ 0x00B0u, 0x00B1u, 0x00B2u, 0x00B3u, 0x00D7u, 0x00B5u, 0x00B6u, 0x00B7u, 0x00F7u, 0x2019u, 0x201Du, 0x00BBu, 0x00BCu, 0x00BDu, 0x00BEu, 0x00BFu,
+ 0x0020u, 0x02CBu, 0x02CAu, 0x02C6u, 0x02DCu, 0x02C9u, 0x02D8u, 0x02D9u, 0x00A8u, 0x002Eu, 0x02DAu, 0x02CFu, 0x02CDu, 0x02DDu, 0x02DBu, 0x02C7u,
+ 0x2014u, 0x00B9u, 0x00AEu, 0x00A9u, 0x2122u, 0x266Au, 0x20A0u, 0x2030u, 0x0251u, 0x0020u, 0x0020u, 0x0020u, 0x215Bu, 0x215Cu, 0x215Du, 0x215Eu,
+ 0x2126u, 0x00C6u, 0x00D0u, 0x00AAu, 0x0126u, 0x0020u, 0x0132u, 0x013Fu, 0x0141u, 0x00D8u, 0x0152u, 0x00BAu, 0x00DEu, 0x0166u, 0x014Au, 0x0149u,
+ 0x0138u, 0x00E6u, 0x0111u, 0x00F0u, 0x0127u, 0x0131u, 0x0133u, 0x0140u, 0x0142u, 0x00F8u, 0x0153u, 0x00DFu, 0x00FEu, 0x0167u, 0x014Bu, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 38: Cyrillic G0 Primary Set - Option 1 - Serbian/Croatian
+ */
+static const unsigned short
+cyrillic_1_g0[64] = {
+ 0x0427u, 0x0410u, 0x0411u, 0x0426u, 0x0414u, 0x0415u, 0x0424u, 0x0413u, 0x0425u, 0x0418u, 0x0408u, 0x041Au, 0x041Bu, 0x041Cu, 0x041Du, 0x041Eu,
+ 0x041Fu, 0x040Cu, 0x0420u, 0x0421u, 0x0422u, 0x0423u, 0x0412u, 0x0403u, 0x0409u, 0x040Au, 0x0417u, 0x040Bu, 0x0416u, 0x0402u, 0x0428u, 0x040Fu,
+ 0x0447u, 0x0430u, 0x0431u, 0x0446u, 0x0434u, 0x0435u, 0x0444u, 0x0433u, 0x0445u, 0x0438u, 0x0458u, 0x043Au, 0x043Bu, 0x043Cu, 0x043Du, 0x043Eu,
+ 0x043Fu, 0x045Cu, 0x0440u, 0x0441u, 0x0442u, 0x0443u, 0x0432u, 0x0453u, 0x0459u, 0x045Au, 0x0437u, 0x045Bu, 0x0436u, 0x0452u, 0x0448u, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 39: Cyrillic G0 Primary Set - Option 2 - Russian/Bulgarian
+ */
+static const unsigned short
+cyrillic_2_g0[64] = {
+ 0x042Eu, 0x0410u, 0x0411u, 0x0426u, 0x0414u, 0x0415u, 0x0424u, 0x0413u, 0x0425u, 0x0418u, 0x040Du, 0x041Au, 0x041Bu, 0x041Cu, 0x041Du, 0x041Eu,
+ 0x041Fu, 0x042Fu, 0x0420u, 0x0421u, 0x0422u, 0x0423u, 0x0416u, 0x0412u, 0x042Cu, 0x042Au, 0x0417u, 0x0428u, 0x042Du, 0x0429u, 0x0427u, 0x042Bu,
+ 0x044Eu, 0x0430u, 0x0431u, 0x0446u, 0x0434u, 0x0435u, 0x0444u, 0x0433u, 0x0445u, 0x0438u, 0x045Du, 0x043Au, 0x043Bu, 0x043Cu, 0x043Du, 0x043Eu,
+ 0x043Fu, 0x044Fu, 0x0440u, 0x0441u, 0x0442u, 0x0443u, 0x0436u, 0x0432u, 0x044Cu, 0x044Au, 0x0437u, 0x0448u, 0x044Du, 0x0449u, 0x0447u, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 40: Cyrillic G0 Primary Set - Option 3 - Ukrainian
+ */
+static const unsigned short
+cyrillic_3_g0[64] = {
+ 0x042Eu, 0x0410u, 0x0411u, 0x0426u, 0x0414u, 0x0415u, 0x0424u, 0x0413u, 0x0425u, 0x0418u, 0x040Du, 0x041Au, 0x041Bu, 0x041Cu, 0x041Du, 0x041Eu,
+ 0x041Fu, 0x042Fu, 0x0420u, 0x0421u, 0x0422u, 0x0423u, 0x0416u, 0x0412u, 0x042Cu, 0x0406u, 0x0417u, 0x0428u, 0x0404u, 0x0429u, 0x0427u, 0x0407u,
+ 0x044Eu, 0x0430u, 0x0431u, 0x0446u, 0x0434u, 0x0435u, 0x0444u, 0x0433u, 0x0445u, 0x0438u, 0x045Du, 0x043Au, 0x043Bu, 0x043Cu, 0x043Du, 0x043Eu,
+ 0x043Fu, 0x044Fu, 0x0440u, 0x0441u, 0x0442u, 0x0443u, 0x0436u, 0x0432u, 0x044Cu, 0x0456u, 0x0437u, 0x0448u, 0x0454u, 0x0449u, 0x0447u, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 41: Cyrillic G2 Supplementary Set
+ */
+static const unsigned short
+cyrillic_g2[96] = {
+ 0x00A0u, 0x00A1u, 0x00A2u, 0x00A3u, 0x0020u, 0x00A5u, 0x0023u, 0x00A7u, 0x0020u, 0x2018u, 0x201Cu, 0x00ABu, 0x2190u, 0x2191u, 0x2192u, 0x2193u,
+ 0x00B0u, 0x00B1u, 0x00B2u, 0x00B3u, 0x00D7u, 0x00B5u, 0x00B6u, 0x00B7u, 0x00F7u, 0x2019u, 0x201Du, 0x00BBu, 0x00BCu, 0x00BDu, 0x00BEu, 0x00BFu,
+ 0x0020u, 0x02CBu, 0x02CAu, 0x02C6u, 0x02DCu, 0x02C9u, 0x02D8u, 0x02D9u, 0x00A8u, 0x002Eu, 0x02DAu, 0x02CFu, 0x02CDu, 0x02DDu, 0x02DBu, 0x02C7u,
+ 0x2014u, 0x00B9u, 0x00AEu, 0x00A9u, 0x2122u, 0x266Au, 0x20A0u, 0x2030u, 0x0251u, 0x0141u, 0x0142u, 0x00DFu, 0x215Bu, 0x215Cu, 0x215Du, 0x215Eu,
+ 0x0044u, 0x0045u, 0x0046u, 0x0047u, 0x0049u, 0x004Au, 0x004Bu, 0x004Cu, 0x004Eu, 0x0051u, 0x0052u, 0x0053u, 0x0055u, 0x0056u, 0x0057u, 0x005Au,
+ 0x0064u, 0x0065u, 0x0066u, 0x0067u, 0x0069u, 0x006Au, 0x006Bu, 0x006Cu, 0x006Eu, 0x0071u, 0x0072u, 0x0073u, 0x0075u, 0x0076u, 0x0077u, 0x007Au
+};
+
+/*
+ * ETS 300 706 Table 42: Greek G0 Primary Set
+ */
+static const unsigned short
+greek_g0[64] = {
+ 0x0390u, 0x0391u, 0x0392u, 0x0393u, 0x0394u, 0x0395u, 0x0396u, 0x0397u, 0x0398u, 0x0399u, 0x039Au, 0x039Bu, 0x039Cu, 0x039Du, 0x039Eu, 0x039Fu,
+ 0x03A0u, 0x03A1u, 0x0374u, 0x03A3u, 0x03A4u, 0x03A5u, 0x03A6u, 0x03A7u, 0x03A8u, 0x03A9u, 0x03AAu, 0x03ABu, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu,
+ 0x03B0u, 0x03B1u, 0x03B2u, 0x03B3u, 0x03B4u, 0x03B5u, 0x03B6u, 0x03B7u, 0x03B8u, 0x03B9u, 0x03BAu, 0x03BBu, 0x03BCu, 0x03BDu, 0x03BEu, 0x03BFu,
+ 0x03C0u, 0x03C1u, 0x03C2u, 0x03C3u, 0x03C4u, 0x03C5u, 0x03C6u, 0x03C7u, 0x03C8u, 0x03C9u, 0x03CAu, 0x03CBu, 0x03CCu, 0x03CDu, 0x03CEu, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 43: Greek G2 Supplementary Set
+ */
+static const unsigned short
+greek_g2[96] = {
+ 0x00A0u, 0x0061u, 0x0062u, 0x00A3u, 0x0065u, 0x0068u, 0x0069u, 0x00A7u, 0x003Au, 0x2018u, 0x201Cu, 0x006Bu, 0x2190u, 0x2191u, 0x2192u, 0x2193u,
+ 0x00B0u, 0x00B1u, 0x00B2u, 0x00B3u, 0x0078u, 0x006Du, 0x006Eu, 0x0070u, 0x00F7u, 0x2019u, 0x201Du, 0x0074u, 0x00BCu, 0x00BDu, 0x00BEu, 0x0078u,
+ 0x0020u, 0x02CBu, 0x02CAu, 0x02C6u, 0x02DCu, 0x02C9u, 0x02D8u, 0x02D9u, 0x00A8u, 0x002Eu, 0x02DAu, 0x02CFu, 0x02CDu, 0x02DDu, 0x02DBu, 0x02C7u,
+ 0x003Fu, 0x00B9u, 0x00AEu, 0x00A9u, 0x2122u, 0x266Au, 0x20A0u, 0x2030u, 0x0251u, 0x038Au, 0x038Eu, 0x038Fu, 0x215Bu, 0x215Cu, 0x215Du, 0x215Eu,
+ 0x0043u, 0x0044u, 0x0046u, 0x0047u, 0x004Au, 0x004Cu, 0x0051u, 0x0052u, 0x0053u, 0x0055u, 0x0056u, 0x0057u, 0x0059u, 0x005Au, 0x0386u, 0x0389u,
+ 0x0063u, 0x0064u, 0x0066u, 0x0067u, 0x006Au, 0x006Cu, 0x0071u, 0x0072u, 0x0073u, 0x0075u, 0x0076u, 0x0077u, 0x0079u, 0x007Au, 0x0388u, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 44: Arabic G0 Primary Set
+ *
+ * XXX 0X0000 is what?
+ * Until these tables are finished use private codes U+E6xx.
+ */
+static const unsigned short
+arabic_g0[96] = {
+/*
+ 0x0020, 0x0021, 0x0022, 0x00A3, 0x0024, 0x0025, 0x0000, 0x0000, 0x0029, 0x0028, 0x002A, 0x002B, 0x060C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x061B, 0x003E, 0x003D, 0x003C, 0x061F,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0023,
+ 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x062B, 0x062D, 0x062C, 0x062E, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x25A0
+*/
+ 0x0020u, 0x0021u, 0x0022u, 0x00A3u, 0x0024u, 0x0025u, 0xE606u, 0xE607u, 0x0029u, 0x0028u, 0x002Au, 0x002Bu, 0x060Cu, 0x002Du, 0x002Eu, 0x002Fu,
+ 0x0030u, 0x0031u, 0x0032u, 0x0033u, 0x0034u, 0x0035u, 0x0036u, 0x0037u, 0x0038u, 0x0039u, 0x003Au, 0x061Bu, 0x003Eu, 0x003Du, 0x003Cu, 0x061Fu,
+ 0xE620u, 0xE621u, 0xE622u, 0xE623u, 0xE624u, 0xE625u, 0xE626u, 0xE627u, 0xE628u, 0xE629u, 0xE62Au, 0xE62Bu, 0xE62Cu, 0xE62Du, 0xE62Eu, 0xE62Fu,
+ 0xE630u, 0xE631u, 0xE632u, 0xE633u, 0xE634u, 0xE635u, 0xE636u, 0xE637u, 0xE638u, 0xE639u, 0xE63Au, 0xE63Bu, 0xE63Cu, 0xE63Du, 0xE63Eu, 0x0023u,
+ 0xE640u, 0xE641u, 0xE642u, 0xE643u, 0xE644u, 0xE645u, 0xE646u, 0xE647u, 0xE648u, 0xE649u, 0xE64Au, 0xE64Bu, 0xE64Cu, 0xE64Du, 0xE64Eu, 0xE64Fu,
+ 0xE650u, 0xE651u, 0xE652u, 0xE653u, 0xE654u, 0xE655u, 0xE656u, 0xE657u, 0xE658u, 0xE659u, 0xE65Au, 0xE65Bu, 0xE65Cu, 0xE65Du, 0xE65Eu, 0x25A0u
+};
+
+/*
+ * ETS 300 706 Table 45: Arabic G2 Supplementary Set
+ *
+ * XXX 0X0000 is what?
+ * Until these tables are finished use private codes U+E7xx.
+ */
+static const unsigned short
+arabic_g2[96] = {
+/*
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+*/
+ 0xE660u, 0xE661u, 0xE662u, 0xE663u, 0xE664u, 0xE665u, 0xE666u, 0xE667u, 0xE668u, 0xE669u, 0xE66Au, 0xE66Bu, 0xE66Cu, 0xE66Du, 0xE66Eu, 0xE66Fu,
+ 0xE670u, 0xE671u, 0xE672u, 0xE673u, 0xE674u, 0xE675u, 0xE676u, 0xE677u, 0xE678u, 0xE679u, 0xE67Au, 0xE67Bu, 0xE67Cu, 0xE67Du, 0xE67Eu, 0xE67Fu,
+ 0x00E0u, 0x0041u, 0x0042u, 0x0043u, 0x0044u, 0x0045u, 0x0046u, 0x0047u, 0x0048u, 0x0049u, 0x004Au, 0x004Bu, 0x004Cu, 0x004Du, 0x004Eu, 0x004Fu,
+ 0x0050u, 0x0051u, 0x0052u, 0x0053u, 0x0054u, 0x0055u, 0x0056u, 0x0057u, 0x0058u, 0x0059u, 0x005Au, 0x00EBu, 0x00EAu, 0x00F9u, 0x00EEu, 0xE75Fu,
+ 0x00E9u, 0x0061u, 0x0062u, 0x0063u, 0x0064u, 0x0065u, 0x0066u, 0x0067u, 0x0068u, 0x0069u, 0x006Au, 0x006Bu, 0x006Cu, 0x006Du, 0x006Eu, 0x006Fu,
+ 0x0070u, 0x0071u, 0x0072u, 0x0073u, 0x0074u, 0x0075u, 0x0076u, 0x0077u, 0x0078u, 0x0079u, 0x007Au, 0x00E2u, 0x00F4u, 0x00FBu, 0x00E7u, 0x0020u
+};
+
+/*
+ * ETS 300 706 Table 46: Hebrew G0 Primary Set
+ */
+static const unsigned short
+hebrew_g0[37] = {
+ 0x2190u, 0x00BDu, 0x2192u, 0x2191u, 0x0023u,
+ 0x05D0u, 0x05D1u, 0x05D2u, 0x05D3u, 0x05D4u, 0x05D5u, 0x05D6u, 0x05D7u, 0x05D8u, 0x05D9u, 0x05DAu, 0x05DBu, 0x05DCu, 0x05DDu, 0x05DEu, 0x05DFu,
+ 0x05E0u, 0x05E1u, 0x05E2u, 0x05E3u, 0x05E4u, 0x05E5u, 0x05E6u, 0x05E7u, 0x05E8u, 0x05E9u, 0x05EAu, 0x20AAu, 0x2016u, 0x00BEu, 0x00F7u, 0x25A0u
+};
+
+/* *INDENT-ON* */
+
+/**
+ * @internal
+ * @param s Teletext character set as listed in ETS 300 706 section 15.
+ * @param n National character subset as listed in section 15, only
+ * applicable to character set LATIN_G0, ignored otherwise.
+ * @param c Character code in range 0x20 ... 0x7F.
+ *
+ * Translate Teletext character code to Unicode.
+ *
+ * Exceptions:
+ * ETS 300 706 Table 36 Latin National Subset Turkish character
+ * 0x23 Turkish currency symbol is not representable in Unicode,
+ * translated to private code U+E800. Was unable to identify all
+ * Arabic glyphs in Table 44 and 45 Arabic G0 and G2, these are
+ * mapped to private code U+E620 ... U+E67F and U+E720 ... U+E77F
+ * respectively. Table 47 G1 Block Mosaic is not representable
+ * in Unicode, translated to private code U+EE00 ... U+EE7F.
+ * (contiguous form has bit 5 set, separate form cleared).
+ * Table 48 G3 Smooth Mosaics and Line Drawing Set is not
+ * representable in Unicode, translated to private code U+EF20
+ * ... U+EF7F.
+ *
+ * Note that some Teletext character sets contain complementary
+ * Latin characters. For example the Greek capital letters Alpha
+ * and Beta are reused as Latin capital letter A and B, while a
+ * separate code exists for Latin capital letter C. This function
+ * is unable to distinguish between uses, so it will always translate
+ * Greek A and B to Alpha and Beta, C to Latin C.
+ *
+ * Private codes U+F000 ... U+F7FF are reserved for DRCS.
+ *
+ * @return
+ * Unicode value.
+ */
+unsigned int
+vbi_teletext_unicode (vbi_character_set s, vbi_national_subset n,
+ unsigned int c)
+{
+ int i;
+
+ assert (c >= 0x20 && c <= 0x7F);
+
+ switch (s) {
+ case LATIN_G0:
+ /* shortcut */
+ if (0xF8000019UL & (1 << (c & 31))) {
+ if (n > 0) {
+ assert (n < 14);
+
+ for (i = 0; i < 13; i++)
+ if (c == national_subset[0][i])
+ return national_subset[n][i];
+ }
+
+ if (c == 0x24)
+ return 0x00A4u;
+ else if (c == 0x7C)
+ return 0x00A6u;
+ else if (c == 0x7F)
+ return 0x25A0u;
+ }
+
+ return c;
+
+ case LATIN_G2:
+ return latin_g2[c - 0x20];
+
+ case CYRILLIC_1_G0:
+ if (c < 0x40)
+ return c;
+ else
+ return cyrillic_1_g0[c - 0x40];
+
+ case CYRILLIC_2_G0:
+ if (c == 0x26)
+ return 0x044Bu;
+ else if (c < 0x40)
+ return c;
+ else
+ return cyrillic_2_g0[c - 0x40];
+
+ case CYRILLIC_3_G0:
+ if (c == 0x26)
+ return 0x00EFu;
+ else if (c < 0x40)
+ return c;
+ else
+ return cyrillic_3_g0[c - 0x40];
+
+ case CYRILLIC_G2:
+ return cyrillic_g2[c - 0x20];
+
+ case GREEK_G0:
+ if (c == 0x3C)
+ return 0x00ABu;
+ else if (c == 0x3E)
+ return 0x00BBu;
+ else if (c < 0x40)
+ return c;
+ else
+ return greek_g0[c - 0x40];
+
+ case GREEK_G2:
+ return greek_g2[c - 0x20];
+
+ case ARABIC_G0:
+ return arabic_g0[c - 0x20];
+
+ case ARABIC_G2:
+ return arabic_g2[c - 0x20];
+
+ case HEBREW_G0:
+ if (c < 0x5B)
+ return c;
+ else
+ return hebrew_g0[c - 0x5B];
+
+ case BLOCK_MOSAIC_G1:
+ /* 0x20 ... 0x3F -> 0xEE00 ... 0xEE1F separated */
+ /* 0xEE20 ... 0xEE3F contiguous */
+ /* 0x60 ... 0x7F -> 0xEE40 ... 0xEE5F separated */
+ /* 0xEE60 ... 0xEE7F contiguous */
+ assert (c < 0x40 || c >= 0x60);
+ return 0xEE00u + c;
+
+ case SMOOTH_MOSAIC_G3:
+ return 0xEF00u + c;
+
+ default:
+ fprintf (stderr, "%s: unknown char set %d\n", __FUNCTION__, s);
+ exit (EXIT_FAILURE);
+ }
+}
+
+/* *INDENT-OFF* */
+
+/*
+ * Unicode U+00C0 ... U+017F to
+ * Teletext accent ((Latin G2 0x40 ... 0x4F) - 0x40) << 12 + Latin G0
+ *
+ * 40 SPACE 48 COMBINING DIAERESIS (U+0308)
+ * 41 COMBINING GRAVE ACCENT (U+0300) 49 COMBINING DOT BELOW (U+0323) ?
+ * 42 COMBINING ACUTE ACCENT (U+0301) 4A COMBINING RING ABOVE (U+030A)
+ * 43 COMBINING CIRCUMFLEX ACCENT (U+0302) 4B COMBINING CEDILLA (U+0327)
+ * 44 COMBINING TILDE (U+0303) 4C COMBINING MACRON BELOW (U+0331) ?
+ * 45 COMBINING MACRON (U+0304) 4D COMBINING DOUBLE ACUTE ACCENT (U+030B)
+ * 46 COMBINING BREVE (U+0306) 4E COMBINING OGONEK (U+0328)
+ * 47 COMBINING DOT ABOVE (U+0307) 4F COMBINING CARON (U+030C)
+ */
+static const unsigned short
+composed[12 * 16] = {
+ /* U+00C0 */
+ 0x1041, 0x2041, 0x3041, 0x4041, 0x8041, 0xA041, 0x0000, 0xB043, 0x1045, 0x2045, 0x3045, 0x8045, 0x1049, 0x2049, 0x3049, 0x8049,
+ 0x0000, 0x404E, 0x104F, 0x204F, 0x304F, 0x404F, 0x804F, 0x0000, 0x0000, 0x1055, 0x2055, 0x3055, 0x8055, 0x2059, 0x0000, 0x0000,
+ 0x1061, 0x2061, 0x3061, 0x4061, 0x8061, 0xA061, 0x0000, 0xB063, 0x1065, 0x2065, 0x3065, 0x8065, 0x1069, 0x2069, 0x3069, 0x8069,
+ 0x0000, 0x406E, 0x106F, 0x206F, 0x306F, 0x406F, 0x806F, 0x0000, 0x0000, 0x1075, 0x2075, 0x3075, 0x8075, 0x2079, 0x0000, 0x8079,
+ /* U+0100 */
+ 0x5041, 0x5061, 0x6041, 0x6061, 0xE041, 0xE061, 0x2043, 0x2063, 0x3043, 0x3063, 0x7043, 0x7063, 0xF043, 0xF063, 0xF044, 0xF064,
+ 0x0000, 0x0000, 0x5045, 0x5065, 0x6045, 0x6065, 0x7045, 0x7065, 0xE045, 0xE065, 0xF045, 0xF065, 0x3047, 0x3067, 0x6047, 0x6067,
+ 0x7047, 0x7067, 0xB047, 0xB067, 0x3048, 0x3068, 0x0000, 0x0000, 0x4049, 0x4069, 0x5049, 0x5069, 0x6049, 0x6069, 0xE049, 0xE069,
+ 0x7049, 0x0000, 0x0000, 0x0000, 0x304A, 0x306A, 0xB04B, 0xB06B, 0x0000, 0x204C, 0x206C, 0xB04C, 0xB06C, 0xF04C, 0xF06C, 0x0000,
+ /* U+0140 */
+ 0x0000, 0x0000, 0x0000, 0x204E, 0x206E, 0xB04E, 0xB06E, 0xF04E, 0xF06E, 0x0000, 0x0000, 0x0000, 0x504F, 0x506F, 0x604F, 0x606F,
+ 0xD04F, 0xD06F, 0x0000, 0x0000, 0x2052, 0x2072, 0xB052, 0xB072, 0xF052, 0xF072, 0x2053, 0x2073, 0x3053, 0x3073, 0xB053, 0xB073,
+ 0xF053, 0xF073, 0xB054, 0xB074, 0xF054, 0xF074, 0x0000, 0x0000, 0x4055, 0x4075, 0x5055, 0x5075, 0x6055, 0x6075, 0xA055, 0xA075,
+ 0xD055, 0xD075, 0xE055, 0xE075, 0x3057, 0x3077, 0x3059, 0x3079, 0x8059, 0x205A, 0x207A, 0x705A, 0x707A, 0xF05A, 0xF07A, 0x0000
+};
+
+/* *INDENT-ON* */
+
+/**
+ * @internal
+ * @param a Accent 0 ... 15.
+ * @param c Character code in range 0x20 ... 0x7F.
+ *
+ * Translate Teletext Latin G0 character 0x20 ... 0x7F combined
+ * with accent code from Latin G2 0x40 ... 0x4F to Unicode. Not all
+ * combinations are representable in Unicode.
+ *
+ * @bug
+ * Since version 0.2.36 if @a a equals zero (no diacritical mark) this
+ * function replaces character U+002A in the Latin G0 set by U+0040
+ * in accordance with EN 300 706 V1.2.1 Table 35 Note 3.
+ *
+ * @return
+ * Unicode value or 0.
+ */
+unsigned int
+vbi_teletext_composed_unicode (unsigned int a, unsigned int c)
+{
+ unsigned int i;
+
+ assert (a <= 15);
+ assert (c >= 0x20 && c <= 0x7F);
+
+ if (a == 0) {
+ if (c == 0x2A)
+ return 0x0040u;
+
+ return vbi_teletext_unicode (LATIN_G0, NO_SUBSET, c);
+ }
+
+ c += a << 12;
+
+ for (i = 0; i < sizeof (composed) / sizeof (composed[0]); i++)
+ if (composed[i] == c)
+ return 0x00C0u + i;
+ return 0;
+}
+
+static inline int
+is_blank (vbi_char c)
+{
+ /* flash/conceal: undecided; underline: nope. */
+ if (c.flash || c.conceal || c.underline)
+ return 0;
+ else
+ return c.unicode <= 0x0020 || c.unicode == 0x00A0 || c.unicode == 0xEE00 /* blank, separated */
+ || c.unicode == 0xEE20; /* blank, contiguous */
+}
+
+static inline int
+is_full (vbi_char c)
+{
+ /* flash/conceal: undecided. */
+ if (c.flash || c.conceal)
+ return 0;
+ else
+ return c.unicode == 0xEE7F /* G1 block, contiguous form */
+ || c.unicode == 0xFF3F; /* G3 block */
+}
+
+/**
+ * @internal
+ * @param pg Formatted vbi_page to be optimized.
+ * @param column First column, 0 ... pg->columns - 1.
+ * @param row First row, 0 ... pg->rows - 1.
+ * @param width Number of columns to optimize, 1 ... pg->columns.
+ * @param height Number of rows to optimize, 1 ... pg->rows.
+ *
+ * Experimental.
+ */
+void
+vbi_optimize_page (vbi_page * pg, int column, int row, int width, int height)
+{
+ vbi_char c, l, *cp;
+ int column0, row0;
+ int column1, row1;
+
+ column0 = column;
+ row0 = row;
+ column1 = column + width;
+ row1 = row + height;
+
+ l = pg->text[pg->columns * row + column];
+
+ for (row = row0; row < row1; row++)
+ for (column = column0; column < column1; column++) {
+ cp = pg->text + pg->columns * row + column;
+ c = *cp;
+
+ if (is_blank (c)) {
+ c.bold = l.bold;
+ c.italic = l.italic;
+ c.foreground = l.foreground;
+ } else if (is_full (c)) {
+ c.bold = l.bold;
+ c.italic = l.italic;
+ c.background = l.background;
+ }
+
+ *cp = l = c;
+ }
+
+ for (row = row1 - 1; row >= row0; row--)
+ for (column = column1 - 1; column >= column0; column--) {
+ cp = pg->text + pg->columns * row + column;
+ c = *cp;
+
+ if (is_blank (c)) {
+ c.bold = l.bold;
+ c.italic = l.italic;
+ c.foreground = l.foreground;
+ } else if (is_full (c)) {
+ c.bold = l.bold;
+ c.italic = l.italic;
+ c.background = l.background;
+ }
+
+ *cp = l = c;
+ }
+}
+
+/*
+ Closed Caption character set
+*/
+
+/* Closed Caption Basic Character Set.
+ 47 CFR Section 15.119 (g) */
+static const uint16_t caption[96][2] = {
+ {0x0020, 0x0020},
+ {0x0021, 0x0021},
+ {0x0022, 0x0022},
+ {0x0023, 0x0023},
+ {0x0024, 0x0024},
+ {0x0025, 0x0025},
+ {0x0026, 0x0026},
+ {0x0027, 0x0027},
+ {0x0028, 0x0028},
+ {0x0029, 0x0029},
+ {0x00E1, 0x00C1}, /* 0x2A a with acute accent */
+ {0x002B, 0x002B},
+ {0x002C, 0x002C},
+ {0x002D, 0x002D},
+ {0x002E, 0x002E},
+ {0x002F, 0x002F},
+ {0x0030, 0x0030},
+ {0x0031, 0x0031},
+ {0x0032, 0x0032},
+ {0x0033, 0x0033},
+ {0x0034, 0x0034},
+ {0x0035, 0x0035},
+ {0x0036, 0x0036},
+ {0x0037, 0x0037},
+ {0x0038, 0x0038},
+ {0x0039, 0x0039},
+ {0x003A, 0x003A},
+ {0x003B, 0x003B},
+ {0x003C, 0x003C},
+ {0x003D, 0x003D},
+ {0x003E, 0x003E},
+ {0x003F, 0x003F},
+ {0x0040, 0x0040},
+ {0x0041, 0x0041},
+ {0x0042, 0x0042},
+ {0x0043, 0x0043},
+ {0x0044, 0x0044},
+ {0x0045, 0x0045},
+ {0x0046, 0x0046},
+ {0x0047, 0x0047},
+ {0x0048, 0x0048},
+ {0x0049, 0x0049},
+ {0x004A, 0x004A},
+ {0x004B, 0x004B},
+ {0x004C, 0x004C},
+ {0x004D, 0x004D},
+ {0x004E, 0x004E},
+ {0x004F, 0x004F},
+ {0x0050, 0x0050},
+ {0x0051, 0x0051},
+ {0x0052, 0x0052},
+ {0x0053, 0x0053},
+ {0x0054, 0x0054},
+ {0x0055, 0x0055},
+ {0x0056, 0x0056},
+ {0x0057, 0x0057},
+ {0x0058, 0x0058},
+ {0x0059, 0x0059},
+ {0x005A, 0x005A},
+ {0x005B, 0x005B},
+ {0x00E9, 0x00C9}, /* 0x5C e with acute accent */
+ {0x005D, 0x005D},
+ {0x00ED, 0x00CD}, /* 0x5E i with acute accent */
+ {0x00F3, 0x00D3}, /* 0x5F o with acute accent */
+ {0x00FA, 0x00DA}, /* 0x60 u with acute accent */
+ {0x0061, 0x0041},
+ {0x0062, 0x0042},
+ {0x0063, 0x0043},
+ {0x0064, 0x0044},
+ {0x0065, 0x0045},
+ {0x0066, 0x0046},
+ {0x0067, 0x0047},
+ {0x0068, 0x0048},
+ {0x0069, 0x0049},
+ {0x006A, 0x004A},
+ {0x006B, 0x004B},
+ {0x006C, 0x004C},
+ {0x006D, 0x004D},
+ {0x006E, 0x004E},
+ {0x006F, 0x004F},
+ {0x0070, 0x0050},
+ {0x0071, 0x0051},
+ {0x0072, 0x0052},
+ {0x0073, 0x0053},
+ {0x0074, 0x0054},
+ {0x0075, 0x0055},
+ {0x0076, 0x0056},
+ {0x0077, 0x0057},
+ {0x0078, 0x0058},
+ {0x0079, 0x0059},
+ {0x007A, 0x005A},
+ {0x00E7, 0x00C7}, /* 0x7B c with cedilla */
+ {0x00F7, 0x00F7}, /* 0x7C Division sign */
+ {0x00D1, 0x00D1}, /* 0x7D N with tilde */
+ {0x00F1, 0x00D1}, /* 0x7E n with tilde */
+ {0x25A0, 0x25A0} /* 0x7F Black square */
+};
+
+/* Closed Caption Special Characters.
+ 47 CFR Section 15.119 (g) */
+static const uint16_t caption_special[16][2] = {
+ {0x00AE, 0x00AE}, /* 0x1130 Registered symbol */
+ {0x00B0, 0x00B0}, /* 0x1131 Degree sign */
+ {0x00BD, 0x00BD}, /* 0x1132 1/2 */
+ {0x00BF, 0x00BF}, /* 0x1133 Inverse question mark */
+ {0x2122, 0x2122}, /* 0x1134 Trademark symbol */
+ {0x00A2, 0x00A2}, /* 0x1135 Cents sign */
+ {0x00A3, 0x00A3}, /* 0x1136 Pounds sign */
+ {0x266A, 0x266A}, /* 0x1137 Music note */
+ {0x00E0, 0x00C0}, /* 0x1138 a with grave accent */
+ {0x0020, 0x0020}, /* 0x1139 Transparent space */
+ {0x00E8, 0x00C8}, /* 0x113A e with grave accent */
+ {0x00E2, 0x00C2}, /* 0x113B a with circumflex */
+ {0x00EA, 0x00CA}, /* 0x113C e with circumflex */
+ {0x00EE, 0x00CE}, /* 0x113D i with circumflex */
+ {0x00F4, 0x00D4}, /* 0x113E o with circumflex */
+ {0x00FB, 0x00DB} /* 0x113F u with circumflex */
+};
+
+/* Closed Caption Extended Character Set.
+ EIA 608-B Section 6.4.2 */
+static const uint16_t caption_extended2[32][2] = {
+ {0x00C1, 0x00C1}, /* 0x1220 A with acute accent */
+ {0x00C9, 0x00C9}, /* 0x1221 E with acute accent */
+ {0x00D3, 0x00D3}, /* 0x1222 O with acute accent */
+ {0x00DA, 0x00DA}, /* 0x1223 U with acute accent */
+ {0x00DC, 0x00DC}, /* 0x1224 U with diaeresis */
+ {0x00FC, 0x00DC}, /* 0x1225 u with diaeresis */
+ {0x2018, 0x2018}, /* 0x1226 Opening single quote */
+ {0x00A1, 0x00A1}, /* 0x1227 Inverted exclamation mark */
+ {0x002A, 0x002A}, /* 0x1228 Asterisk */
+ {0x0027, 0x0027}, /* 0x1229 Plain single quote */
+ {0x2500, 0x2500}, /* 0x122A Em dash (for box drawing) */
+ {0x00A9, 0x00A9}, /* 0x122B Copyright */
+ {0x2120, 0x2120}, /* 0x122C Service mark */
+ {0x2022, 0x2022}, /* 0x122D Round bullet */
+ {0x201C, 0x201C}, /* 0x122E Opening double quotes */
+ {0x201D, 0x201D}, /* 0x122F Closing double quotes */
+ {0x00C0, 0x00C0}, /* 0x1230 A with grave accent */
+ {0x00C2, 0x00C2}, /* 0x1231 A with circumflex accent */
+ {0x00C7, 0x00C7}, /* 0x1232 C with cedilla */
+ {0x00C8, 0x00C8}, /* 0x1233 E with grave accent */
+ {0x00CA, 0x00CA}, /* 0x1234 E with circumflex accent */
+ {0x00CB, 0x00CB}, /* 0x1235 E with diaeresis */
+ {0x00EB, 0x00CB}, /* 0x1236 e with diaeresis */
+ {0x00CE, 0x00CE}, /* 0x1237 I with circumflex */
+ {0x00CF, 0x00CF}, /* 0x1238 I with diaeresis */
+ {0x00EF, 0x00CF}, /* 0x1239 i with diaeresis */
+ {0x00D4, 0x00D4}, /* 0x123A O with circumflex */
+ {0x00D9, 0x00D9}, /* 0x123B U with grave accent */
+ {0x00F9, 0x00D9}, /* 0x123C u with grave accent */
+ {0x00DB, 0x00DB}, /* 0x123D U with circumflex accent */
+ {0x00AB, 0x00AB}, /* 0x123E Opening guillemets */
+ {0x00BB, 0x00BB} /* 0x123F Closing guillemets */
+};
+
+/* Closed Caption Extended Character Set.
+ EIA 608-B Section 6.4.2 */
+static const uint16_t caption_extended3[32][2] = {
+ {0x00C3, 0x00C3}, /* 0x1320 A with tilde */
+ {0x00E3, 0x00C3}, /* 0x1321 a with tilde */
+ {0x00CD, 0x00CD}, /* 0x1322 I with acute accent */
+ {0x00CC, 0x00CC}, /* 0x1323 I with grave accent */
+ {0x00EC, 0x00CC}, /* 0x1324 i with grave accent */
+ {0x00D2, 0x00D2}, /* 0x1325 O with grave accent */
+ {0x00F2, 0x00D2}, /* 0x1326 o with grave accent */
+ {0x00D5, 0x00D5}, /* 0x1327 O with tilde */
+ {0x00F5, 0x00D5}, /* 0x1328 o with tilde */
+ {0x007B, 0x007B}, /* 0x1329 Opening curly brace */
+ {0x007D, 0x007D}, /* 0x132A Closing curly brace */
+ {0x005C, 0x005C}, /* 0x132B Backslash */
+ {0x005E, 0x005E}, /* 0x132C Caret */
+ {0x005F, 0x005F}, /* 0x132D Underscore */
+ {0x007C, 0x007C}, /* 0x132E Vertical bar */
+ {0x007E, 0x007E}, /* 0x132F Tilde */
+ {0x00C4, 0x00C4}, /* 0x1330 A with diaeresis */
+ {0x00E4, 0x00C4}, /* 0x1331 a with diaeresis */
+ {0x00D6, 0x00D6}, /* 0x1332 O with diaeresis */
+ {0x00F6, 0x00D6}, /* 0x1333 o with diaeresis */
+ {0x00DF, 0x00DF}, /* 0x1334 Sharp s */
+ {0x00A5, 0x00A5}, /* 0x1335 Yen sign */
+ {0x00A4, 0x00A4}, /* 0x1336 Currency sign */
+ {0x2502, 0x2502}, /* 0x1337 Vertical bar (for box drawing) */
+ {0x00C5, 0x00C5}, /* 0x1338 A with ring above */
+ {0x00E5, 0x00C5}, /* 0x1339 a with ring above */
+ {0x00D8, 0x00D8}, /* 0x133A O with slash */
+ {0x00F8, 0x00D8}, /* 0x133B o with slash */
+ {0x250C, 0x250C}, /* 0x133C Upper left corner */
+ {0x2510, 0x2510}, /* 0x133D Upper right corner */
+ {0x2514, 0x2514}, /* 0x133E Lower left corner */
+ {0x2518, 0x2518} /* 0x133F Lower right corner */
+};
+
+/**
+ * @ingroup Conv
+ * @param c Character code in range 0x20 ... 0x7F,
+ * 0x1130 ... 0x113F, 0x1930 ... 0x193F, 0x1220 ... 0x123F,
+ * 0x1A20 ... 0x1A3F, 0x1320 ... 0x133F, 0x1B20 ... 0x1B3F.
+ * @param to_upper Convert the character to upper case. (In general
+ * real time caption is capitalized, but for a few accented characters
+ * older versions of the standard defined only lower case character
+ * codes. This option is available to conveniently capitalize all
+ * characters received.)
+ *
+ * Converts a Closed Caption character code to Unicode. Codes
+ * in range 0x1130 to 0x1B3F are "special characters" and "extended
+ * characters" (e.g. caption command 0x11 0x37).
+ *
+ * @see vbi_strndup_iconv_caption()
+ *
+ * @return
+ * Unicode value or 0 if @a c is outside the valid ranges.
+ *
+ * @since 0.2.23
+ */
+unsigned int
+vbi_caption_unicode (unsigned int c, vbi_bool to_upper)
+{
+ to_upper = ! !to_upper;
+
+ /* Note the comparisons are sorted for shortest path. */
+ if (likely (c < 0x80)) {
+ if (likely (c >= 0x20)) {
+ return caption[c - 0x20][to_upper];
+ }
+ } else {
+ c &= ~0x0800; /* ignore channel bit */
+
+ if (c < 0x1240) {
+ if (c < 0x1140 && c >= 0x1130) {
+ /* 001 c001 011 xxxx */
+ return caption_special[c - 0x1130][to_upper];
+ } else if (c >= 0x1220) {
+ /* 001 c010 01x xxxx */
+ return caption_extended2[c - 0x1220][to_upper];
+ }
+ } else if (c < 0x1340 && c >= 0x1320) {
+ /* 001 c011 01x xxxx */
+ return caption_extended3[c - 0x1320][to_upper];
+ }
+ }
+
+ return 0;
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/gst/closedcaption/lang.h b/gst/closedcaption/lang.h
new file mode 100644
index 000000000..79cbbaf5b
--- /dev/null
+++ b/gst/closedcaption/lang.h
@@ -0,0 +1,164 @@
+/*
+ * libzvbi -- Teletext and Closed Caption character set
+ *
+ * Copyright (C) 2000, 2001 Michael H. Schimek
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02110-1301 USA.
+ */
+
+/* $Id: lang.h,v 1.9 2008-02-19 00:35:20 mschimek Exp $ */
+
+#ifndef LANG_H
+#define LANG_H
+
+#include "bcd.h" /* vbi_bool */
+#include "format.h" /* vbi_page */
+
+/**
+ * @internal
+ *
+ * Teletext character set according to ETS 300 706, Section 15.
+ */
+typedef enum {
+ LATIN_G0 = 1,
+ LATIN_G2,
+ CYRILLIC_1_G0,
+ CYRILLIC_2_G0,
+ CYRILLIC_3_G0,
+ CYRILLIC_G2,
+ GREEK_G0,
+ GREEK_G2,
+ ARABIC_G0,
+ ARABIC_G2,
+ HEBREW_G0,
+ BLOCK_MOSAIC_G1,
+ SMOOTH_MOSAIC_G3
+} vbi_character_set;
+
+/**
+ * @internal
+ *
+ * Teletext Latin G0 national option subsets according to
+ * ETS 300 706, Section 15.2; Section 15.6.2 Table 36.
+ */
+typedef enum {
+ NO_SUBSET,
+ CZECH_SLOVAK,
+ ENGLISH,
+ ESTONIAN,
+ FRENCH,
+ GERMAN,
+ ITALIAN,
+ LETT_LITH,
+ POLISH,
+ PORTUG_SPANISH,
+ RUMANIAN,
+ SERB_CRO_SLO,
+ SWE_FIN_HUN,
+ TURKISH
+} vbi_national_subset;
+
+/**
+ * @internal
+ *
+ * vbi_font_descriptors[], array of vbi_font_descr implements
+ * the Teletext character set designation tables in ETS 300 706,
+ * Section 15: Table 32, 33 and 34.
+ */
+struct vbi_font_descr {
+ vbi_character_set G0;
+ vbi_character_set G2;
+ vbi_national_subset subset; /* applies only to LATIN_G0 */
+ const char * label; /* Latin-1 */
+};
+
+extern struct vbi_font_descr vbi_font_descriptors[88];
+
+#define VALID_CHARACTER_SET(n) ((n) < 88 && vbi_font_descriptors[n].G0)
+
+/* Public */
+
+/**
+ * @ingroup Page
+ * @brief Opaque font descriptor.
+ */
+typedef struct vbi_font_descr vbi_font_descr;
+
+/**
+ * @ingroup Page
+ * @param unicode Unicode as in vbi_char.
+ *
+ * @return
+ * @c TRUE if @a unicode represents a Teletext or Closed Caption
+ * printable character. This excludes Teletext Arabic characters (which
+ * are represented by private codes U+E600 ... U+E7FF until the conversion
+ * table is ready), the Teletext Turkish currency sign U+E800 which is not
+ * representable in Unicode, the Teletext G1 Block Mosaic and G3 Smooth
+ * Mosaics and Line Drawing Set, with codes U+EE00 ... U+EFFF, and
+ * Teletext DRCS coded U+F000 ... U+F7FF.
+ */
+_vbi_inline vbi_bool
+vbi_is_print(unsigned int unicode)
+{
+ return unicode < 0xE600;
+}
+
+/**
+ * @ingroup Page
+ * @param unicode Unicode as in vbi_char.
+ *
+ * @return
+ * @c TRUE if @a unicode represents a Teletext G1 Block Mosaic or G3 Smooth
+ * Mosaics and Line Drawing Set, that is a code in range U+EE00 ... U+EFFF.
+ */
+_vbi_inline vbi_bool
+vbi_is_gfx(unsigned int unicode)
+{
+ return unicode >= 0xEE00 && unicode <= 0xEFFF;
+}
+
+/**
+ * @ingroup Page
+ * @param unicode Unicode as in vbi_char.
+ *
+ * @return
+ * @c TRUE if @a unicode represents a Teletext DRCS (Dynamically
+ * Redefinable Character), that is a code in range U+F000 ... U+F7FF.
+ **/
+_vbi_inline vbi_bool
+vbi_is_drcs(unsigned int unicode)
+{
+ return unicode >= 0xF000;
+}
+
+extern unsigned int
+vbi_caption_unicode (unsigned int c,
+ vbi_bool to_upper);
+
+/* Private */
+
+extern unsigned int vbi_teletext_unicode(vbi_character_set s, vbi_national_subset n, unsigned int c);
+extern unsigned int vbi_teletext_composed_unicode(unsigned int a, unsigned int c);
+extern void vbi_optimize_page(vbi_page *pg, int column, int row, int width, int height);
+
+#endif /* LANG_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/