diff options
-rw-r--r-- | gst/closedcaption/Makefile.am | 11 | ||||
-rw-r--r-- | gst/closedcaption/cc608_decoder.c | 2482 | ||||
-rw-r--r-- | gst/closedcaption/cc608_decoder.h | 131 | ||||
-rw-r--r-- | gst/closedcaption/event-priv.h | 104 | ||||
-rw-r--r-- | gst/closedcaption/event.c | 270 | ||||
-rw-r--r-- | gst/closedcaption/event.h | 794 | ||||
-rw-r--r-- | gst/closedcaption/format.h | 408 | ||||
-rw-r--r-- | gst/closedcaption/hamm-tables.h | 307 | ||||
-rw-r--r-- | gst/closedcaption/hamm.c | 164 | ||||
-rw-r--r-- | gst/closedcaption/hamm.h | 233 | ||||
-rw-r--r-- | gst/closedcaption/lang.c | 907 | ||||
-rw-r--r-- | gst/closedcaption/lang.h | 164 |
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: +*/ |