mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 02:30:35 +00:00
1201 lines
32 KiB
C
1201 lines
32 KiB
C
/*
|
|
* Copyright (c) 2002 Billy Biggs <vektor@dumbterm.net>.
|
|
* Copyright (c) 2002 Doug Bell <drbell@users.sourceforge.net>
|
|
*
|
|
* CC code from Nathan Laredo's ccdecode, used under the GPL.
|
|
* Lots of 'hey what does this mean?' code from
|
|
* Billy Biggs and Doug Bell, like all the crap with
|
|
* XDS and stuff. Some help from Zapping's vbi library by
|
|
* Michael H. Schimek and others, released under the GPL.
|
|
*
|
|
* Modified and adapted to GStreamer by
|
|
* David I. Lehn <dlehn@users.sourceforge.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program 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
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include "vbidata.h"
|
|
#include "vbiscreen.h"
|
|
/*#include "tvtimeosd.h"*/
|
|
|
|
#define DO_LINE 11
|
|
static int pll = 0;
|
|
|
|
struct vbidata_s
|
|
{
|
|
int fd;
|
|
vbiscreen_t *vs;
|
|
/*tvtime_osd_t *osd; */
|
|
char buf[65536];
|
|
int wanttop;
|
|
int wanttext;
|
|
|
|
unsigned int colour;
|
|
int row, ital;
|
|
int indent, ul;
|
|
int chan;
|
|
|
|
unsigned int current_colour;
|
|
int current_row, current_ital;
|
|
int current_indent, current_ul;
|
|
int current_chan;
|
|
int current_istext;
|
|
|
|
int initialised;
|
|
int enabled;
|
|
int lastcode;
|
|
int lastcount;
|
|
int verbose;
|
|
|
|
/* XDS data */
|
|
char xds_packet[2048];
|
|
int xds_cursor;
|
|
|
|
char *program_name;
|
|
char *network_name;
|
|
char *call_letters;
|
|
const char *rating;
|
|
const char *program_type;
|
|
int start_day;
|
|
int start_month;
|
|
int start_min;
|
|
int start_hour;
|
|
int length_hour;
|
|
int length_min;
|
|
int length_elapsed_hour;
|
|
int length_elapsed_min;
|
|
int length_elapsed_sec;
|
|
char *program_desc[8];
|
|
};
|
|
|
|
|
|
/* this is NOT exactly right */
|
|
//static char ccode[] = " !\"#$%&'()\0341+,-./0123456789:;<=>?@"
|
|
static char ccode[] = " !\"#$%&'()a+,-./0123456789:;<=>?@"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
// "abcdefghijklmnopqrstuvwxyz"
|
|
// "[\0351]\0355\0363\0372abcdefghijklmnopqr"
|
|
"[e]iouabcdefghijklmnopqr"
|
|
// "stuvwxyz\0347\0367\0245\0244\0240";
|
|
"stuvwxyzcoNn ";
|
|
static char wccode[] = "\0256\0260\0275\0277T\0242\0243#\0340 "
|
|
"\0350\0354\0362\0371";
|
|
|
|
static char extcode1[] = "\0301\0311\0323\0332\0334\0374"
|
|
"`\0241*'-\0251S*\"\"\0300\0302"
|
|
"\0307\0310\0312\0313\0353\0316\0317\0357" "\0324\0331\0371\0333\0253\0273";
|
|
|
|
static char extcode2[] = "\0303\0343\0315\0314\0354\0322\0362\0325"
|
|
"{}\\^_|~\0304\0344\0326\0366\0337\0245\0244|" "\0305\0345\0330\0370++++";
|
|
|
|
int
|
|
parityok (int n)
|
|
{ /* check parity for 2 bytes packed in n */
|
|
int j, k;
|
|
|
|
for (k = 0, j = 0; j < 7; j++)
|
|
if (n & (1 << j))
|
|
k++;
|
|
if ((k & 1) && (n & 0x80))
|
|
return 0;
|
|
for (k = 0, j = 8; j < 15; j++)
|
|
if (n & (1 << j))
|
|
k++;
|
|
if ((k & 1) && (n & 0x8000))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
decodebit (unsigned char *data, int threshold)
|
|
{
|
|
return ((data[0] + data[1] + data[2] + data[3] + data[4] + data[5] +
|
|
data[6] + data[7] + data[8] + data[9] + data[10] + data[11] +
|
|
data[12] + data[13] + data[14] + data[15] + data[16] + data[17] +
|
|
data[18] + data[19] + data[20] + data[21] + data[22] + data[23] +
|
|
data[24] + data[25] + data[26] + data[27] + data[28] + data[29] +
|
|
data[30] + data[31]) >> 5 > threshold);
|
|
}
|
|
|
|
|
|
int
|
|
ccdecode (unsigned char *vbiline)
|
|
{
|
|
int max = 0, maxval = 0, minval = 255, i = 0, clk = 0, tmp = 0;
|
|
int sample, packedbits = 0;
|
|
|
|
for (i = 0; i < 250; i++) {
|
|
sample = vbiline[i];
|
|
if (sample - maxval > 10)
|
|
(maxval = sample, max = i);
|
|
if (sample < minval)
|
|
minval = sample;
|
|
if (maxval - sample > 40)
|
|
break;
|
|
}
|
|
sample = ((maxval + minval) >> 1);
|
|
pll = max;
|
|
|
|
/* found clock lead-in, double-check start */
|
|
#ifndef PAL_DECODE
|
|
i = max + 478;
|
|
#else
|
|
i = max + 538;
|
|
#endif
|
|
if (!decodebit (&vbiline[i], sample))
|
|
return 0;
|
|
#ifndef PAL_DECODE
|
|
tmp = i + 57; /* tmp = data bit zero */
|
|
#else
|
|
tmp = i + 71;
|
|
#endif
|
|
for (i = 0; i < 16; i++) {
|
|
#ifndef PAL_DECODE
|
|
clk = tmp + i * 57;
|
|
#else
|
|
clk = tmp + i * 71;
|
|
#endif
|
|
if (decodebit (&vbiline[clk], sample)) {
|
|
packedbits |= 1 << i;
|
|
}
|
|
}
|
|
if (parityok (packedbits))
|
|
return packedbits;
|
|
return 0;
|
|
} /* ccdecode */
|
|
|
|
const char *movies[] = { "N/A", "G", "PG", "PG-13", "R",
|
|
"NC-17", "X", "Not Rated"
|
|
};
|
|
|
|
const char *usa_tv[] = { "Not Rated", "TV-Y", "TV-Y7", "TV-G",
|
|
"TV-PG", "TV-14", "TV-MA", "Not Rated"
|
|
};
|
|
|
|
const char *cane_tv[] = { "Exempt", "C", "C8+", "G", "PG",
|
|
"14+", "18+", "Reserved"
|
|
};
|
|
|
|
const char *canf_tv[] = { "Exempt", "G", "8 ans +", "13 ans +",
|
|
"16 ans +", "18 ans +", "Reserved",
|
|
"Reserved"
|
|
};
|
|
|
|
const char *months[] = { 0, "Jan", "Feb", "Mar", "Apr", "May",
|
|
"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
};
|
|
|
|
static const char *eia608_program_type[96] = {
|
|
"education", "entertainment", "movie", "news", "religious", "sports",
|
|
"other", "action", "advertisement", "animated", "anthology",
|
|
"automobile", "awards", "baseball", "basketball", "bulletin", "business",
|
|
"classical", "college", "combat", "comedy", "commentary", "concert",
|
|
"consumer", "contemporary", "crime", "dance", "documentary", "drama",
|
|
"elementary", "erotica", "exercise", "fantasy", "farm", "fashion",
|
|
"fiction", "food", "football", "foreign", "fund raiser", "game/quiz",
|
|
"garden", "golf", "government", "health", "high school", "history",
|
|
"hobby", "hockey", "home", "horror", "information", "instruction",
|
|
"international", "interview", "language", "legal", "live", "local",
|
|
"math", "medical", "meeting", "military", "miniseries", "music", "mystery",
|
|
"national", "nature", "police", "politics", "premiere", "prerecorded",
|
|
"product", "professional", "public", "racing", "reading", "repair", "repeat",
|
|
"review", "romance", "science", "series", "service", "shopping",
|
|
"soap opera", "special", "suspense", "talk", "technical", "tennis",
|
|
"travel", "variety", "video", "weather", "western"
|
|
};
|
|
|
|
|
|
static void
|
|
parse_xds_packet (vbidata_t * vbi, char *packet, int length)
|
|
{
|
|
int sum = 0;
|
|
int i;
|
|
|
|
if (!vbi)
|
|
return;
|
|
|
|
/* Check the checksum for validity of the packet. */
|
|
for (i = 0; i < length - 1; i++) {
|
|
sum += packet[i];
|
|
}
|
|
if ((((~sum) & 0x7f) + 1) != packet[length - 1]) {
|
|
return;
|
|
}
|
|
|
|
/* Stick a null at the end, and cut off the last two characters. */
|
|
packet[length - 2] = '\0';
|
|
length -= 2;
|
|
|
|
if (packet[0] == 0x01 && packet[1] == 0x03) {
|
|
if (vbi->program_name && !strcmp (vbi->program_name, packet + 2)) {
|
|
return;
|
|
}
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Current program name: '%s'\n", packet + 2);
|
|
if (vbi->program_name)
|
|
free (vbi->program_name);
|
|
vbi->program_name = strdup (packet + 2);
|
|
/*tvtime_osd_set_show_name( vbi->osd, vbi->program_name ); */
|
|
} else if (packet[0] == 0x03 && packet[1] == 0x03) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Future program name: '%s'\n", packet + 2);
|
|
} else if (packet[0] == 0x05 && packet[1] == 0x01) {
|
|
if (vbi->network_name && !strcmp (vbi->network_name, packet + 2)) {
|
|
return;
|
|
}
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Network name: '%s'\n", packet + 2);
|
|
if (vbi->network_name)
|
|
free (vbi->network_name);
|
|
vbi->network_name = strdup (packet + 2);
|
|
/*tvtime_osd_set_network_name( vbi->osd, vbi->network_name ); */
|
|
} else if (packet[0] == 0x01 && packet[1] == 0x05) {
|
|
int movie_rating = packet[2] & 7;
|
|
int scheme = (packet[2] & 56) >> 3;
|
|
int tv_rating = packet[3] & 7;
|
|
int VSL = packet[3] & 56;
|
|
const char *str;
|
|
|
|
switch (VSL | scheme) {
|
|
case 3: /* Canadian English TV */
|
|
str = cane_tv[tv_rating];
|
|
break;
|
|
case 7: /* Canadian French TV */
|
|
str = canf_tv[tv_rating];
|
|
break;
|
|
case 19: /* Reserved */
|
|
case 31:
|
|
str = "";
|
|
break;
|
|
default:
|
|
if (((VSL | scheme) & 3) == 1) {
|
|
/* USA TV */
|
|
str = usa_tv[tv_rating];
|
|
} else {
|
|
/* MPAA Movie Rating */
|
|
str = movies[movie_rating];
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (vbi->rating && !strcmp (vbi->rating, str)) {
|
|
return;
|
|
}
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Show rating: %s", str);
|
|
if (((VSL | scheme) & 3) == 1 || ((VSL | scheme) & 3) == 0) {
|
|
/* show VSLD for the americans */
|
|
if ((VSL | scheme) & 32) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, " V");
|
|
}
|
|
if ((VSL | scheme) & 16) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, " S");
|
|
}
|
|
if ((VSL | scheme) & 8) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, " L");
|
|
}
|
|
if ((VSL | scheme) & 4) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, " D");
|
|
}
|
|
}
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "\n");
|
|
vbi->rating = str;
|
|
/*tvtime_osd_set_show_rating( vbi->osd, vbi->rating ); */
|
|
} else if (packet[0] == 0x05 && packet[1] == 0x02) {
|
|
if (vbi->call_letters && !strcmp (vbi->call_letters, packet + 2)) {
|
|
return;
|
|
}
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Network call letters: '%s'\n", packet + 2);
|
|
if (vbi->call_letters)
|
|
free (vbi->call_letters);
|
|
vbi->call_letters = strdup (packet + 2);
|
|
/*tvtime_osd_set_network_call( vbi->osd, vbi->call_letters ); */
|
|
} else if (packet[0] == 0x01 && packet[1] == 0x01) {
|
|
int month = packet[5]; // & 15;
|
|
int day = packet[4]; // & 31;
|
|
int hour = packet[3]; // & 31;
|
|
int min = packet[2]; // & 63;
|
|
char str[33];
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Program Start: %02d %s, %02d:%02d\n",
|
|
day & 31, months[month & 15], hour & 31, min & 63);
|
|
// packet[ 3 ], packet[ 4 ], packet[ 5 ], packet[ 6 ] );
|
|
//packet[ 5 ] & 31, packet[ 6 ], packet[ 4 ] & 31, packet[ 3 ] & 63 );
|
|
vbi->start_month = month & 15;
|
|
vbi->start_day = day & 31;
|
|
vbi->start_hour = hour & 31;
|
|
vbi->start_min = hour & 63;
|
|
snprintf (str, 32, "%02d %s, %02d:%02d",
|
|
day & 31, months[month & 15], hour & 31, min & 63);
|
|
/*tvtime_osd_set_show_start( vbi->osd, str ); */
|
|
} else if (packet[0] == 0x01 && packet[1] == 0x04) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Program type: ");
|
|
for (i = 0; i < length - 2; i++) {
|
|
int cur = packet[2 + i] - 0x20;
|
|
|
|
if (cur >= 0 && cur < 96) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "%s%s", i ? ", " : "", eia608_program_type[cur]);
|
|
/* this will cause us to keep only the last type we check */
|
|
vbi->program_type = eia608_program_type[cur];
|
|
}
|
|
}
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "\n");
|
|
} else if (packet[0] < 0x03 && packet[1] >= 0x10 && packet[1] <= 0x17) {
|
|
|
|
if (vbi->program_desc[packet[1] & 0xf] &&
|
|
!strcmp (vbi->program_desc[packet[1] & 0xf], packet + 2)) {
|
|
return;
|
|
}
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Program Description: Line %d", packet[1] & 0xf);
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "%s\n", packet + 2);
|
|
if (vbi->program_desc[packet[1] & 0xf])
|
|
free (vbi->program_desc[packet[1] & 0xf]);
|
|
vbi->program_desc[packet[1] & 0xf] = strdup (packet + 2);
|
|
} else if (packet[0] == 0x01 && packet[1] == 0x02) {
|
|
char str[33];
|
|
|
|
str[0] = 0;
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Program Length: %02d:%02d",
|
|
packet[3] & 63, packet[2] & 63);
|
|
vbi->length_hour = packet[3] & 63;
|
|
vbi->length_min = packet[2] & 63;
|
|
snprintf (str, 32, "%02d:%02d", packet[3] & 63, packet[2] & 63);
|
|
if (length > 4) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, " Elapsed: %02d:%02d", packet[5] & 63, packet[4] & 63);
|
|
vbi->length_elapsed_hour = packet[5] & 63;
|
|
vbi->length_elapsed_min = packet[4] & 63;
|
|
snprintf (str, 32, "%02d:%02d/%02d:%02d",
|
|
packet[5] & 63, packet[4] & 63, packet[3] & 63, packet[2] & 63);
|
|
} else {
|
|
vbi->length_elapsed_hour = 0;
|
|
vbi->length_elapsed_min = 0;
|
|
}
|
|
|
|
if (length > 6) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, ".%02d", packet[6] & 63);
|
|
vbi->length_elapsed_hour = packet[6] & 63;
|
|
snprintf (str, 32, "%02d:%02d.%02d/%02d:%02d",
|
|
packet[5] & 63, packet[4] & 63, packet[6] & 63,
|
|
packet[3] & 63, packet[2] & 63);
|
|
} else {
|
|
vbi->length_elapsed_hour = 0;
|
|
}
|
|
/*tvtime_osd_set_show_length( vbi->osd, str ); */
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "\n");
|
|
} else if (packet[0] == 0x05 && packet[1] == 0x04) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Transmission Signal Identifier (TSID): 0x%04x\n",
|
|
packet[2] << 24 | packet[3] << 16 | packet[4] << 8 | packet[5]);
|
|
} else {
|
|
/* unknown */
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Unknown XDS packet, class ");
|
|
switch (packet[0]) {
|
|
case 0x1:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "CURRENT start\n");
|
|
break;
|
|
case 0x2:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "CURRENT continue\n");
|
|
break;
|
|
|
|
case 0x3:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "FUTURE start\n");
|
|
break;
|
|
case 0x4:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "FUTURE continue\n");
|
|
break;
|
|
|
|
case 0x5:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "CHANNEL start\n");
|
|
break;
|
|
case 0x6:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "CHANNEL continue\n");
|
|
break;
|
|
|
|
case 0x7:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "MISC start\n");
|
|
break;
|
|
case 0x8:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "MISC continue\n");
|
|
break;
|
|
|
|
case 0x9:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "PUB start\n");
|
|
break;
|
|
case 0xa:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "PUB continue\n");
|
|
break;
|
|
|
|
case 0xb:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "RES start\n");
|
|
break;
|
|
case 0xc:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "RES continue\n");
|
|
break;
|
|
|
|
case 0xd:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "UNDEF start\n");
|
|
break;
|
|
case 0xe:
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "UNDEF continue\n");
|
|
break;
|
|
}
|
|
for (i = 0; i < length; i++) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "0x%02x ", packet[i]);
|
|
}
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "\n");
|
|
}
|
|
}
|
|
|
|
static int
|
|
xds_decode (vbidata_t * vbi, int b1, int b2)
|
|
{
|
|
if (!vbi)
|
|
return 0;
|
|
if (vbi->xds_cursor > 2046) {
|
|
vbi->xds_cursor = 0;
|
|
}
|
|
|
|
if (!vbi->xds_cursor && b1 > 0xf) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (b1 < 0xf && (b1 & 0x2)) {
|
|
/* ignore the continue and thus 'support' continuation of
|
|
a single packet */
|
|
return 1;
|
|
} else if (b1 < 0xf) {
|
|
/* kill old packet cause we got a new one */
|
|
vbi->xds_cursor = 0;
|
|
}
|
|
|
|
vbi->xds_packet[vbi->xds_cursor] = b1;
|
|
vbi->xds_packet[vbi->xds_cursor + 1] = b2;
|
|
vbi->xds_cursor += 2;
|
|
|
|
if (b1 == 0xf) {
|
|
parse_xds_packet (vbi, vbi->xds_packet, vbi->xds_cursor);
|
|
vbi->xds_cursor = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define NOMODE 0
|
|
|
|
#define CC1 1
|
|
#define CC2 2
|
|
#define T1 3
|
|
#define T2 4
|
|
|
|
#define CC3 1
|
|
#define CC4 2
|
|
#define T3 3
|
|
#define T4 4
|
|
|
|
const unsigned int colours[] = {
|
|
0xFFFFFFFFU, /* white */
|
|
0xFF00FF00U, /* green */
|
|
0xFF0000FFU, /* blue */
|
|
0xFF00C7C7U, /* cyan */
|
|
0xFFFF0000U, /* red */
|
|
0xFFFFFF00U, /* yellow */
|
|
0xFFC700C7U /* magenta */
|
|
};
|
|
|
|
const int rows[] = {
|
|
11,
|
|
0, /* unused */
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
12,
|
|
13,
|
|
14,
|
|
15,
|
|
5,
|
|
6,
|
|
7,
|
|
8,
|
|
9,
|
|
10
|
|
};
|
|
|
|
#define ROLL_2 6
|
|
#define ROLL_3 7
|
|
#define ROLL_4 8
|
|
#define POP_UP 9
|
|
#define PAINT_ON 10
|
|
|
|
|
|
static int
|
|
Process16b (vbidata_t * vbi, int bottom, int w1)
|
|
{
|
|
int b1, b2;
|
|
|
|
b1 = w1 & 0x7f;
|
|
b2 = (w1 >> 8) & 0x7f;
|
|
|
|
if (!b1 && !b2) {
|
|
return 0;
|
|
}
|
|
|
|
if (vbi->enabled && b1 >= 0x10 && b1 <= 0x1F && b2 >= 0x20 && b2 <= 0x7F) {
|
|
int code;
|
|
|
|
if ((b2 & 64)) {
|
|
/* Preamble Code */
|
|
/* This sets up colors and indenting */
|
|
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
return 1;
|
|
}
|
|
|
|
vbi->current_chan = (b1 & 8) >> 3;
|
|
if (!bottom == vbi->wanttop) {
|
|
if (vbi->chan != vbi->current_chan)
|
|
return 0;
|
|
} else
|
|
return 0;
|
|
|
|
vbi->current_ital = (b2 & 1);
|
|
if (!(b2 & 16)) {
|
|
vbi->current_colour = colours[(b2 & 30) >> 1];
|
|
vbi->current_indent = 0;
|
|
} else {
|
|
vbi->current_colour = 0xFFFFFFFFU; /* white */
|
|
vbi->current_indent = 4 * ((b2 & 14) >> 1);
|
|
}
|
|
vbi->current_row = rows[((b1 & 7) << 1) | ((b2 & 32) >> 5)];
|
|
vbi->current_ul = b2 & 1;
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "field: %d chan %d, ital %d, ul %d, colour 0x%x, "
|
|
"indent %d, row %d\n", bottom, vbi->current_chan,
|
|
vbi->current_ital, vbi->current_ul, vbi->current_colour,
|
|
vbi->current_indent, vbi->current_row);
|
|
|
|
if (!bottom == vbi->wanttop &&
|
|
vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 0;
|
|
|
|
vbiscreen_new_caption (vbi->vs, vbi->indent, vbi->ital,
|
|
vbi->colour, vbi->row);
|
|
|
|
}
|
|
|
|
vbi->lastcode = (b1 << 8) | b2;
|
|
vbi->lastcount = 0;
|
|
return 1;
|
|
}
|
|
|
|
if ((b1 & 8) == 1) {
|
|
/* Midrow code */
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
return 1;
|
|
}
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Midrow TODO: Add me.\n");
|
|
|
|
vbi->lastcode = (b1 << 8) | b2;
|
|
return 1;
|
|
}
|
|
|
|
if ((b1 & 2) && !(b2 & 64)) {
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
return 1;
|
|
}
|
|
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Tab Offset: %d columns\n", b2 & 3);
|
|
if (vbi->wanttext && vbi->current_istext &&
|
|
vbi->current_chan == vbi->chan && !bottom == vbi->wanttop) {
|
|
vbiscreen_tab (vbi->vs, b2 & 3);
|
|
}
|
|
vbi->lastcode = (b1 << 8) | b2;
|
|
return 1;
|
|
}
|
|
|
|
switch ((code = b2 & 15)) {
|
|
case 0: /* POP-UP */
|
|
case 5: /* ROLL UP 2 */
|
|
case 6: /* ROLL UP 3 */
|
|
case 7: /* ROLL UP 4 */
|
|
case 9: /* PAINT-ON */
|
|
case 10: /* TEXT */
|
|
case 11: /* TEXT */
|
|
vbi->initialised = 1;
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
/* This is the repeated Control Code */
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
return 1;
|
|
}
|
|
switch (code) {
|
|
case 0: /* POP-UP */
|
|
if (!vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Pop-Up\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 0;
|
|
vbiscreen_set_mode (vbi->vs, 1, POP_UP);
|
|
}
|
|
break;
|
|
case 5: /* ROLL UP 2 */
|
|
if (!vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Roll-Up 2 (RU2)\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 0;
|
|
vbiscreen_set_mode (vbi->vs, 1, ROLL_2);
|
|
}
|
|
break;
|
|
case 6: /* ROLL UP 3 */
|
|
if (!vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Roll-Up 3 (RU3)\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 0;
|
|
vbiscreen_set_mode (vbi->vs, 1, ROLL_3);
|
|
}
|
|
break;
|
|
case 7: /* ROLL UP 4 */
|
|
if (!vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Roll-Up 4 (RU4)\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 0;
|
|
vbiscreen_set_mode (vbi->vs, 1, ROLL_4);
|
|
}
|
|
break;
|
|
case 9: /* PAINT-ON */
|
|
if (!vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Paint-On\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 0;
|
|
vbiscreen_set_mode (vbi->vs, 1, PAINT_ON);
|
|
}
|
|
break;
|
|
case 10: /* TEXT */
|
|
if (vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Text Restart\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 1;
|
|
vbiscreen_set_mode (vbi->vs, 0, 0);
|
|
}
|
|
break;
|
|
case 11: /* TEXT */
|
|
if (vbi->wanttext && vbi->current_chan == vbi->chan &&
|
|
!bottom == vbi->wanttop) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Resume Text Display\n");
|
|
vbi->indent = vbi->current_indent;
|
|
vbi->ital = vbi->current_ital;
|
|
vbi->colour = vbi->current_colour;
|
|
vbi->row = vbi->current_row;
|
|
vbi->current_istext = 1;
|
|
vbiscreen_set_mode (vbi->vs, 0, 0);
|
|
}
|
|
break;
|
|
default: /* impossible */
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
}
|
|
if (!bottom == vbi->wanttop && vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Backspace\n");
|
|
vbiscreen_backspace (vbi->vs);
|
|
}
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
fprintf (stderr, "Reserved\n");
|
|
break;
|
|
case 4:
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
}
|
|
if (!bottom == vbi->wanttop && vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Delete to End of Row\n");
|
|
vbiscreen_delete_to_end (vbi->vs);
|
|
}
|
|
break;
|
|
case 8:
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Flash On\n");
|
|
break;
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
if (!bottom && vbi->lastcode == ((b1 << 8) | b2)) {
|
|
vbi->lastcount = (vbi->lastcount + 1) % 2;
|
|
return 1;
|
|
}
|
|
|
|
switch (code) {
|
|
case 12:
|
|
/* Show buffer 1, Fill buffer 2 */
|
|
if (!bottom == vbi->wanttop &&
|
|
vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Erase Displayed Memory\n");
|
|
vbiscreen_erase_displayed (vbi->vs);
|
|
}
|
|
break;
|
|
case 13:
|
|
if (!bottom == vbi->wanttop &&
|
|
vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Carriage Return\n");
|
|
vbiscreen_carriage_return (vbi->vs);
|
|
}
|
|
break;
|
|
case 14:
|
|
if (!bottom == vbi->wanttop &&
|
|
vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "Erase Non-Displayed\n");
|
|
vbiscreen_erase_non_displayed (vbi->vs);
|
|
}
|
|
break;
|
|
case 15:
|
|
/* Show buffer 2, Fill Buffer 1 */
|
|
if (!bottom == vbi->wanttop &&
|
|
vbi->current_chan == vbi->chan &&
|
|
vbi->current_istext == vbi->wanttext) {
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "End Of Caption\n");
|
|
vbiscreen_end_of_caption (vbi->vs);
|
|
}
|
|
break;
|
|
default: /* impossible */
|
|
return 0;
|
|
break;
|
|
}
|
|
break;
|
|
default: /* Impossible */
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
if (vbi->lastcode != ((b1 << 8) | b2)) {
|
|
vbi->lastcount = 0;
|
|
}
|
|
|
|
vbi->lastcode = (b1 << 8) | b2;
|
|
return 1;
|
|
}
|
|
|
|
if (bottom && xds_decode (vbi, b1, b2)) {
|
|
return 1;
|
|
}
|
|
|
|
if (!vbi->enabled)
|
|
return 0;
|
|
|
|
vbi->lastcode = 0;
|
|
vbi->lastcount = 0;
|
|
|
|
if (!vbi->initialised)
|
|
return 0;
|
|
|
|
if (!bottom != vbi->wanttop || vbi->current_chan != vbi->chan ||
|
|
vbi->current_istext != vbi->wanttext) {
|
|
return 0;
|
|
}
|
|
|
|
if (b1 == 0x11 || b1 == 0x19 ||
|
|
b1 == 0x12 || b1 == 0x13 || b1 == 0x1A || b1 == 0x1B) {
|
|
switch (b1) {
|
|
case 0x1A:
|
|
case 0x12:
|
|
/* use extcode1 */
|
|
if (b1 > 31 && b2 > 31 && b1 <= 0x3F && b2 <= 0x3F)
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "char %d (%c), char %d (%c)\n", b1,
|
|
extcode1[b1 - 32], b2, extcode1[b2 - 32]);
|
|
|
|
break;
|
|
case 0x13:
|
|
case 0x1B:
|
|
/* use extcode2 */
|
|
if (b1 > 31 && b2 > 31 && b1 <= 0x3F && b2 <= 0x3F)
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "char %d (%c), char %d (%c)\n", b1,
|
|
extcode2[b1 - 32], b2, extcode2[b2 - 32]);
|
|
|
|
break;
|
|
case 0x11:
|
|
case 0x19:
|
|
/* use wcode */
|
|
if (b1 > 31 && b2 > 31 && b1 <= 0x3F && b2 <= 0x3F)
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "char %d (%c), char %d (%c)\n", b1,
|
|
wccode[b1 - 32], b2, wccode[b2 - 32]);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (b1) {
|
|
/* use ccode */
|
|
if (b1 < 32)
|
|
b1 = 32;
|
|
if (b2 < 32)
|
|
b2 = 32;
|
|
if (vbi->verbose)
|
|
fprintf (stderr, "vbidata: data: %c %c\n", ccode[b1 - 32],
|
|
ccode[b2 - 32]);
|
|
vbiscreen_print (vbi->vs, ccode[b1 - 32], ccode[b2 - 32]);
|
|
}
|
|
|
|
|
|
return 1;
|
|
} /* Process16b */
|
|
|
|
int
|
|
ProcessLine (vbidata_t * vbi, unsigned char *s, int bottom)
|
|
{
|
|
int w1;
|
|
|
|
/*char *outbuf = NULL; */
|
|
|
|
if (!vbi)
|
|
return 0;
|
|
|
|
w1 = ccdecode (s);
|
|
|
|
return Process16b (vbi, bottom, w1);
|
|
} /* ProcessLine */
|
|
|
|
|
|
|
|
vbidata_t *
|
|
vbidata_new_file (const char *filename, vbiscreen_t * vs,
|
|
/*tvtime_osd_t* osd, */ int verbose)
|
|
{
|
|
vbidata_t *vbi = (vbidata_t *) malloc (sizeof (vbidata_t));
|
|
|
|
if (!vbi) {
|
|
return 0;
|
|
}
|
|
|
|
vbi->fd = open (filename, O_RDONLY);
|
|
if (vbi->fd < 0) {
|
|
fprintf (stderr, "vbidata: Can't open %s: %s\n",
|
|
filename, strerror (errno));
|
|
free (vbi);
|
|
return 0;
|
|
}
|
|
|
|
vbi->vs = vs;
|
|
/*vbi->osd = osd; */
|
|
vbi->verbose = verbose;
|
|
|
|
vbidata_reset (vbi);
|
|
|
|
return vbi;
|
|
}
|
|
|
|
vbidata_t *
|
|
vbidata_new_line (vbiscreen_t * vs, int verbose)
|
|
{
|
|
vbidata_t *vbi = (vbidata_t *) malloc (sizeof (vbidata_t));
|
|
|
|
if (!vbi) {
|
|
return 0;
|
|
}
|
|
|
|
vbi->vs = vs;
|
|
/*vbi->osd = osd; */
|
|
vbi->verbose = verbose;
|
|
|
|
vbidata_reset (vbi);
|
|
|
|
return vbi;
|
|
}
|
|
|
|
void
|
|
vbidata_delete (vbidata_t * vbi)
|
|
{
|
|
if (!vbi)
|
|
return;
|
|
if (vbi->fd != 0) {
|
|
close (vbi->fd);
|
|
}
|
|
free (vbi);
|
|
}
|
|
|
|
void
|
|
vbidata_reset (vbidata_t * vbi)
|
|
{
|
|
if (!vbi)
|
|
return;
|
|
|
|
vbi->wanttop = 0;
|
|
vbi->wanttext = 0;
|
|
vbi->colour = 0xFFFFFFFFU;
|
|
vbi->row = 0;
|
|
|
|
vbi->ital = 0;
|
|
vbi->indent = 0;
|
|
vbi->ul = 0;
|
|
|
|
vbi->chan = 0;
|
|
|
|
vbi->initialised = 0;
|
|
vbi->enabled = 0;
|
|
|
|
memset (vbi->program_desc, 0, 8 * sizeof (char *));
|
|
vbi->program_name = NULL;
|
|
vbi->network_name = NULL;
|
|
vbi->call_letters = NULL;
|
|
vbi->rating = NULL;
|
|
vbi->program_type = NULL;
|
|
|
|
vbi->start_day = 0;
|
|
vbi->start_month = 0;
|
|
vbi->start_min = 0;
|
|
vbi->start_hour = 0;
|
|
vbi->length_hour = 0;
|
|
vbi->length_min = 0;
|
|
vbi->length_elapsed_hour = 0;
|
|
vbi->length_elapsed_min = 0;
|
|
vbi->length_elapsed_sec = 0;
|
|
|
|
/*
|
|
tvtime_osd_set_network_call( vbi->osd, "" );
|
|
tvtime_osd_set_network_name( vbi->osd, "" );
|
|
tvtime_osd_set_show_name( vbi->osd, "" );
|
|
tvtime_osd_set_show_rating( vbi->osd, "" );
|
|
tvtime_osd_set_show_start( vbi->osd, "" );
|
|
tvtime_osd_set_show_length( vbi->osd, "" );
|
|
*/
|
|
|
|
|
|
|
|
vbi->lastcode = 0;
|
|
vbi->lastcount = 0;
|
|
vbi->xds_packet[0] = 0;
|
|
vbi->xds_cursor = 0;
|
|
vbiscreen_reset (vbi->vs);
|
|
}
|
|
|
|
void
|
|
vbidata_set_verbose (vbidata_t * vbi, int verbose)
|
|
{
|
|
vbi->verbose = verbose;
|
|
}
|
|
|
|
void
|
|
vbidata_capture_mode (vbidata_t * vbi, int mode)
|
|
{
|
|
if (!vbi)
|
|
return;
|
|
switch (mode) {
|
|
case CAPTURE_OFF:
|
|
vbi->enabled = 0;
|
|
break;
|
|
case CAPTURE_CC1:
|
|
vbi->wanttop = 1;
|
|
vbi->wanttext = 0;
|
|
vbi->chan = 0;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_CC2:
|
|
vbi->wanttop = 1;
|
|
vbi->wanttext = 0;
|
|
vbi->chan = 1;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_CC3:
|
|
vbi->wanttop = 0;
|
|
vbi->wanttext = 0;
|
|
vbi->chan = 0;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_CC4:
|
|
vbi->wanttop = 0;
|
|
vbi->wanttext = 0;
|
|
vbi->chan = 1;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_T1:
|
|
vbi->wanttop = 1;
|
|
vbi->wanttext = 1;
|
|
vbi->chan = 0;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_T2:
|
|
vbi->wanttop = 1;
|
|
vbi->wanttext = 1;
|
|
vbi->chan = 1;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_T3:
|
|
vbi->wanttop = 0;
|
|
vbi->wanttext = 1;
|
|
vbi->chan = 0;
|
|
vbi->enabled = 1;
|
|
break;
|
|
case CAPTURE_T4:
|
|
vbi->wanttop = 0;
|
|
vbi->wanttext = 1;
|
|
vbi->chan = 1;
|
|
vbi->enabled = 1;
|
|
break;
|
|
default:
|
|
vbi->enabled = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
vbidata_process_frame (vbidata_t * vbi, int printdebug)
|
|
{
|
|
if (read (vbi->fd, vbi->buf, 65536) < 65536) {
|
|
fprintf (stderr, "error, can't read vbi data.\n");
|
|
return;
|
|
}
|
|
|
|
ProcessLine (vbi, &vbi->buf[DO_LINE * 2048], 0);
|
|
ProcessLine (vbi, &vbi->buf[(16 + DO_LINE) * 2048], 1);
|
|
}
|
|
|
|
void
|
|
vbidata_process_line (vbidata_t * vbi, unsigned char *s, int bottom)
|
|
{
|
|
ProcessLine (vbi, s, bottom);
|
|
}
|
|
|
|
void
|
|
vbidata_process_16b (vbidata_t * vbi, int bottom, int w)
|
|
{
|
|
Process16b (vbi, bottom, w);
|
|
}
|