mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 05:31:15 +00:00
713f74f4f9
Import the code from gst-plugins-rs (origin is https://github.com/szatmary/libcaption) Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4929>
375 lines
11 KiB
C
375 lines
11 KiB
C
/**********************************************************************************************/
|
|
/* The MIT License */
|
|
/* */
|
|
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
|
|
/* of this software and associated documentation files (the "Software"), to deal */
|
|
/* in the Software without restriction, including without limitation the rights */
|
|
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
|
|
/* copies of the Software, and to permit persons to whom the Software is */
|
|
/* furnished to do so, subject to the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be included in */
|
|
/* all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
|
|
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
|
|
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
|
|
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
|
|
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
|
|
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
|
|
/* THE SOFTWARE. */
|
|
/**********************************************************************************************/
|
|
#include "eia608.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int eia608_row_map[] = { 10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9 };
|
|
int eia608_reverse_row_map[] =
|
|
{ 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1 };
|
|
|
|
const char *eia608_style_map[] = {
|
|
"white",
|
|
"green",
|
|
"blue",
|
|
"cyan",
|
|
"red",
|
|
"yellow",
|
|
"magenta",
|
|
"italics",
|
|
};
|
|
|
|
static inline uint16_t
|
|
eia608_row_pramble (int row, int chan, int x, int underline)
|
|
{
|
|
row = eia608_reverse_row_map[row & 0x0F];
|
|
return eia608_parity (0x1040 | (chan ? 0x0800 : 0x0000) | ((row << 7) &
|
|
0x0700) | ((row << 5) & 0x0020) | ((x << 1) & 0x001E) | (underline ?
|
|
0x0001 : 0x0000));
|
|
}
|
|
|
|
uint16_t
|
|
eia608_row_column_pramble (int row, int col, int chan, int underline)
|
|
{
|
|
return eia608_row_pramble (row, chan, 0x8 | (col / 4), underline);
|
|
}
|
|
|
|
uint16_t
|
|
eia608_row_style_pramble (int row, int chan, eia608_style_t style,
|
|
int underline)
|
|
{
|
|
return eia608_row_pramble (row, chan, style, underline);
|
|
}
|
|
|
|
uint16_t
|
|
eia608_midrow_change (int chan, eia608_style_t style, int underline)
|
|
{
|
|
return eia608_parity (0x1120 | ((chan << 11) & 0x0800) | ((style << 1) &
|
|
0x000E) | (underline & 0x0001));
|
|
}
|
|
|
|
int
|
|
eia608_parse_preamble (uint16_t cc_data, int *row, int *col,
|
|
eia608_style_t * style, int *chan, int *underline)
|
|
{
|
|
(*row) =
|
|
eia608_row_map[((0x0700 & cc_data) >> 7) | ((0x0020 & cc_data) >> 5)];
|
|
(*chan) = ! !(0x0800 & cc_data);
|
|
(*underline) = 0x0001 & cc_data;
|
|
|
|
if (0x0010 & cc_data) {
|
|
(*style) = eia608_style_white;
|
|
(*col) = 4 * ((0x000E & cc_data) >> 1);
|
|
} else {
|
|
(*style) = (0x000E & cc_data) >> 1;
|
|
(*col) = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
eia608_parse_midrowchange (uint16_t cc_data, int *chan, eia608_style_t * style,
|
|
int *underline)
|
|
{
|
|
(*chan) = ! !(0x0800 & cc_data);
|
|
|
|
if (0x1120 == (0x7770 & cc_data)) {
|
|
(*style) = (0x000E & cc_data) >> 1;
|
|
(*underline) = 0x0001 & cc_data;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// control command
|
|
eia608_control_t
|
|
eia608_parse_control (uint16_t cc_data, int *cc)
|
|
{
|
|
if (0x0200 & cc_data) {
|
|
(*cc) = (cc_data & 0x0800 ? 0x01 : 0x00);
|
|
return (eia608_control_t) (0x177F & cc_data);
|
|
} else {
|
|
(*cc) = (cc_data & 0x0800 ? 0x01 : 0x00) | (cc_data & 0x0100 ? 0x02 : 0x00);
|
|
return (eia608_control_t) (0x167F & cc_data);
|
|
}
|
|
}
|
|
|
|
uint16_t
|
|
eia608_control_command (eia608_control_t cmd, int cc)
|
|
{
|
|
uint16_t c = (cc & 0x01) ? 0x0800 : 0x0000;
|
|
uint16_t f = (cc & 0x02) ? 0x0100 : 0x0000;
|
|
|
|
if (eia608_tab_offset_0 == (eia608_control_t) (cmd & 0xFFC0)) {
|
|
return (eia608_control_t) eia608_parity (cmd | c);
|
|
} else {
|
|
return (eia608_control_t) eia608_parity (cmd | c | f);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// text
|
|
static const char *
|
|
utf8_from_index (int idx)
|
|
{
|
|
return (0 <= idx && EIA608_CHAR_COUNT > idx) ? eia608_char_map[idx] : "";
|
|
}
|
|
|
|
static int
|
|
eia608_to_index (uint16_t cc_data, int *chan, int *c1, int *c2)
|
|
{
|
|
(*c1) = (*c2) = -1;
|
|
(*chan) = 0;
|
|
cc_data &= 0x7F7F; // strip off parity bits
|
|
|
|
// Handle Basic NA BEFORE we strip the channel bit
|
|
if (eia608_is_basicna (cc_data)) {
|
|
// we got first char, yes. But what about second char?
|
|
(*c1) = (cc_data >> 8) - 0x20;
|
|
cc_data &= 0x00FF;
|
|
|
|
if (0x0020 <= cc_data && 0x0080 > cc_data) {
|
|
(*c2) = cc_data - 0x20;
|
|
return 2;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
// Check then strip second channel toggle
|
|
(*chan) = cc_data & 0x0800;
|
|
cc_data = cc_data & 0xF7FF;
|
|
|
|
if (eia608_is_specialna (cc_data)) {
|
|
// Special North American character
|
|
(*c1) = cc_data - 0x1130 + 0x60;
|
|
return 1;
|
|
}
|
|
|
|
if (0x1220 <= cc_data && 0x1240 > cc_data) {
|
|
// Extended Western European character set, Spanish/Miscellaneous/French
|
|
(*c1) = cc_data - 0x1220 + 0x70;
|
|
return 1;
|
|
}
|
|
|
|
if (0x1320 <= cc_data && 0x1340 > cc_data) {
|
|
// Extended Western European character set, Portuguese/German/Danish
|
|
(*c1) = cc_data - 0x1320 + 0x90;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
eia608_to_utf8 (uint16_t c, int *chan, char *str1, char *str2)
|
|
{
|
|
int c1, c2;
|
|
int size = (int) eia608_to_index (c, chan, &c1, &c2);
|
|
utf8_char_copy (str1, utf8_from_index (c1));
|
|
utf8_char_copy (str2, utf8_from_index (c2));
|
|
return size;
|
|
}
|
|
|
|
uint16_t
|
|
eia608_from_basicna (uint16_t bna1, uint16_t bna2)
|
|
{
|
|
if (!eia608_is_basicna (bna1) || !eia608_is_basicna (bna2)) {
|
|
return 0;
|
|
}
|
|
|
|
return eia608_parity ((0xFF00 & bna1) | ((0xFF00 & bna2) >> 8));
|
|
}
|
|
|
|
// prototype for re2c generated function
|
|
uint16_t _eia608_from_utf8 (const utf8_char_t * s);
|
|
uint16_t
|
|
eia608_from_utf8_1 (const utf8_char_t * c, int chan)
|
|
{
|
|
uint16_t cc_data = _eia608_from_utf8 (c);
|
|
|
|
if (0 == cc_data) {
|
|
return cc_data;
|
|
}
|
|
|
|
if (chan && !eia608_is_basicna (cc_data)) {
|
|
cc_data |= 0x0800;
|
|
}
|
|
|
|
return eia608_parity (cc_data);
|
|
}
|
|
|
|
uint16_t
|
|
eia608_from_utf8_2 (const utf8_char_t * c1, const utf8_char_t * c2)
|
|
{
|
|
uint16_t cc1 = _eia608_from_utf8 (c1);
|
|
uint16_t cc2 = _eia608_from_utf8 (c2);
|
|
return eia608_from_basicna (cc1, cc2);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
eia608_to_text (char *buf, ssize_t size, uint16_t cc_data)
|
|
{
|
|
eia608_style_t style;
|
|
const char *text = 0;
|
|
char char1[5], char2[5];
|
|
char1[0] = char2[0] = 0;
|
|
int row, col, chan, underline;
|
|
int ret;
|
|
|
|
if (!eia608_parity_varify (cc_data)) {
|
|
text = "parity failed";
|
|
} else if (0 == eia608_parity_strip (cc_data)) {
|
|
text = "pad";
|
|
} else if (eia608_is_basicna (cc_data)) {
|
|
text = "basicna";
|
|
eia608_to_utf8 (cc_data, &chan, &char1[0], &char2[0]);
|
|
} else if (eia608_is_specialna (cc_data)) {
|
|
text = "specialna";
|
|
eia608_to_utf8 (cc_data, &chan, &char1[0], &char2[0]);
|
|
} else if (eia608_is_westeu (cc_data)) {
|
|
text = "westeu";
|
|
eia608_to_utf8 (cc_data, &chan, &char1[0], &char2[0]);
|
|
} else if (eia608_is_xds (cc_data)) {
|
|
text = "xds";
|
|
} else if (eia608_is_midrowchange (cc_data)) {
|
|
text = "midrowchange";
|
|
} else if (eia608_is_norpak (cc_data)) {
|
|
text = "norpak";
|
|
} else if (eia608_is_preamble (cc_data)) {
|
|
eia608_parse_preamble (cc_data, &row, &col, &style, &chan, &underline);
|
|
ret = snprintf(buf, size, "cc %04X (%04X) '%s' '%s' (preamble: row: %d col: %d style: %d chan: %d underline: %d)",
|
|
cc_data, eia608_parity_strip (cc_data), char1, char2, row, col, style, chan, underline);
|
|
} else if (eia608_is_control (cc_data)) {
|
|
switch (eia608_parse_control (cc_data, &chan)) {
|
|
|
|
default:
|
|
text = "unknown_control";
|
|
break;
|
|
|
|
case eia608_tab_offset_0:
|
|
text = "eia608_tab_offset_0";
|
|
break;
|
|
|
|
case eia608_tab_offset_1:
|
|
text = "eia608_tab_offset_1";
|
|
break;
|
|
|
|
case eia608_tab_offset_2:
|
|
text = "eia608_tab_offset_2";
|
|
break;
|
|
|
|
case eia608_tab_offset_3:
|
|
text = "eia608_tab_offset_3";
|
|
break;
|
|
|
|
case eia608_control_resume_caption_loading:
|
|
text = "eia608_control_resume_caption_loading";
|
|
break;
|
|
|
|
case eia608_control_backspace:
|
|
text = "eia608_control_backspace";
|
|
break;
|
|
|
|
case eia608_control_alarm_off:
|
|
text = "eia608_control_alarm_off";
|
|
break;
|
|
|
|
case eia608_control_alarm_on:
|
|
text = "eia608_control_alarm_on";
|
|
break;
|
|
|
|
case eia608_control_delete_to_end_of_row:
|
|
text = "eia608_control_delete_to_end_of_row";
|
|
break;
|
|
|
|
case eia608_control_roll_up_2:
|
|
text = "eia608_control_roll_up_2";
|
|
break;
|
|
|
|
case eia608_control_roll_up_3:
|
|
text = "eia608_control_roll_up_3";
|
|
break;
|
|
|
|
case eia608_control_roll_up_4:
|
|
text = "eia608_control_roll_up_4";
|
|
break;
|
|
|
|
case eia608_control_resume_direct_captioning:
|
|
text = "eia608_control_resume_direct_captioning";
|
|
break;
|
|
|
|
case eia608_control_text_restart:
|
|
text = "eia608_control_text_restart";
|
|
break;
|
|
|
|
case eia608_control_text_resume_text_display:
|
|
text = "eia608_control_text_resume_text_display";
|
|
break;
|
|
|
|
case eia608_control_erase_display_memory:
|
|
text = "eia608_control_erase_display_memory";
|
|
break;
|
|
|
|
case eia608_control_carriage_return:
|
|
text = "eia608_control_carriage_return";
|
|
break;
|
|
|
|
case eia608_control_erase_non_displayed_memory:
|
|
text = "eia608_control_erase_non_displayed_memory";
|
|
break;
|
|
|
|
case eia608_control_end_of_caption:
|
|
text = "eia608_control_end_of_caption";
|
|
break;
|
|
}
|
|
} else {
|
|
text = "unhandled";
|
|
}
|
|
|
|
if (text != 0) {
|
|
ret = snprintf (buf, size, "cc %04X (%04X) '%s' '%s' (%s)", cc_data, eia608_parity_strip (cc_data), char1, char2, text);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
eia608_dump (uint16_t cc_data)
|
|
{
|
|
char *text = NULL;
|
|
int bufsz;
|
|
|
|
bufsz = eia608_to_text (NULL, 0, cc_data);
|
|
text = malloc(bufsz + 1);
|
|
eia608_to_text (text, bufsz + 1, cc_data);
|
|
fprintf (stderr, "%s\n", text);
|
|
|
|
free (text);
|
|
}
|