/*
* libzvbi - Extended Data Service demultiplexer
*
* Copyright (C) 2000-2005 Michael H. Schimek
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: xds_demux.c,v 1.7 2006/05/31 03:55:52 mschimek Exp $ */
#include "site_def.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "misc.h" /* vbi_log_printf() */
#include "hamm.h" /* vbi_ipar8() */
#include "tables.h" /* vbi_rating/prog_type_string() */
#include "xds_demux.h"
/**
* @addtogroup XDSDemux Extended Data Service (XDS) demultiplexer
* @ingroup LowDec
* @brief Separating XDS data from a Closed Caption stream
* (EIA 608).
*/
#ifndef XDS_DEMUX_LOG
#define XDS_DEMUX_LOG 0
#endif
/* LOG (level, format, args...) */
#define log(format, args...) \
do { \
if (XDS_DEMUX_LOG) \
fprintf (stderr, format , ##args); \
} while (0)
static void
xdump (const vbi_xds_packet * xp,
FILE * fp)
{
unsigned int i;
for (i = 0; i < xp->buffer_size; ++i)
fprintf (fp, " %02x", xp->buffer[i]);
fputs (" '", fp);
for (i = 0; i < xp->buffer_size; ++i)
fputc (_vbi_to_ascii (xp->buffer[i]), fp);
fputc ('\'', fp);
}
/** @internal */
void
_vbi_xds_packet_dump (const vbi_xds_packet * xp,
FILE * fp)
{
static const char *month_names [] = {
"0?", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec", "13?", "14?", "15?"
};
static const char *map_type [] = {
"unknown", "mono", "simulated stereo", "stereo",
"stereo surround", "data service", "unknown", "none"
};
static const char *sap_type [] = {
"unknown", "mono", "video descriptions", "non-program audio",
"special effects", "data service", "unknown", "none"
};
static const char *language [] = {
"unknown", "English", "Spanish", "French", "German",
"Italian", "Other", "none"
};
static const char *cgmsa [] = {
"copying permitted", "-", "one copy allowed",
"no copying permitted"
};
static const char *scrambling [] = {
"no pseudo-sync pulse",
"pseudo-sync pulse on; color striping off",
"pseudo-sync pulse on; 2-line color striping on",
"pseudo-sync pulse on; 4-line color striping on"
};
static const char *day_names [] = {
"0?", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
unsigned int i;
assert (NULL != xp);
assert (NULL != fp);
fprintf (fp, "XDS packet 0x%02x%02x ",
xp->xds_class * 2 + 1, xp->xds_subclass);
switch (xp->xds_class) {
case VBI_XDS_CLASS_CURRENT:
fputs ("(cur. program ", fp);
/* fall through */
case VBI_XDS_CLASS_FUTURE:
if (VBI_XDS_CLASS_FUTURE == xp->xds_class)
fputs ("(fut. program ", fp);
switch (xp->xds_subclass) {
case VBI_XDS_PROGRAM_ID:
{
unsigned int month, day, hour, min;
fputs ("id)", fp);
xdump (xp, fp);
if (4 != xp->buffer_size) {
invalid:
fputs (" (invalid)", fp);
break;
}
month = xp->buffer[3] & 15;
day = xp->buffer[2] & 31;
hour = xp->buffer[1] & 31;
min = xp->buffer[0] & 63;
if (month == 0 || month > 12
|| day == 0 || day > 31
|| hour > 23 || min > 59)
goto invalid;
fprintf (fp, " (%d %s %02d:%02d UTC,",
day, month_names[month], hour, min);
fprintf (fp, " D=%d L=%d Z=%d T=%d)",
!!(xp->buffer[1] & 0x20),
!!(xp->buffer[2] & 0x20),
!!(xp->buffer[3] & 0x20),
!!(xp->buffer[3] & 0x10));
break;
}
case VBI_XDS_PROGRAM_LENGTH:
{
unsigned int lhour, lmin;
fputs ("length)", fp);
xdump (xp, fp);
switch (xp->buffer_size) {
case 2:
case 4:
case 5:
break;
default:
goto invalid;
}
lhour = xp->buffer[1] & 63;
lmin = xp->buffer[0] & 63;
if (lmin > 59)
goto invalid;
fprintf (fp, " (%02d:%02d", lhour, lmin);
if (xp->buffer_size >= 4) {
unsigned int ehour, emin;
ehour = xp->buffer[3] & 63;
emin = xp->buffer[2] & 63;
if (emin > 59)
goto invalid;
fprintf (fp, " elapsed=%02d:%02d",
ehour, emin);
if (xp->buffer_size >= 5) {
unsigned int esec;
esec = xp->buffer[4] & 63;
if (esec > 59)
goto invalid;
fprintf (fp, ":%02d", esec);
}
}
fputc (')', fp);
break;
}
case VBI_XDS_PROGRAM_NAME:
fputs ("name)", fp);
xdump (xp, fp);
break;
case VBI_XDS_PROGRAM_TYPE:
{
unsigned int i;
fputs ("type)", fp);
xdump (xp, fp);
if (xp->buffer_size < 1)
goto invalid;
fputs (" (", fp);
for (i = 0; i < xp->buffer_size; ++i) {
fprintf (fp, (i > 0) ? ", %s" : "%s",
vbi_prog_type_string
(VBI_PROG_CLASSF_EIA_608,
xp->buffer[i]));
}
fputc (')', fp);
break;
}
case VBI_XDS_PROGRAM_RATING:
{
unsigned int r, g;
fputs ("rating)", fp);
xdump (xp, fp);
if (2 != xp->buffer_size)
goto invalid;
r = xp->buffer[0] & 7;
g = xp->buffer[1] & 7;
fprintf (fp, " (movie: %s, tv: ",
vbi_rating_string (VBI_RATING_AUTH_MPAA, r));
if (xp->buffer[0] & 0x10) {
const char *s;
if (xp->buffer[0] & 0x20)
s = vbi_rating_string
(VBI_RATING_AUTH_TV_CA_FR, g);
else
s = vbi_rating_string
(VBI_RATING_AUTH_TV_CA_EN, g);
fputs (s, fp);
} else {
fprintf (fp, "%s D=%d L=%d S=%d V=%d",
vbi_rating_string
(VBI_RATING_AUTH_TV_US, g),
!!(xp->buffer[0] & 0x20),
!!(xp->buffer[1] & 0x08),
!!(xp->buffer[1] & 0x10),
!!(xp->buffer[1] & 0x20));
}
fputc (')', fp);
break;
}
case VBI_XDS_PROGRAM_AUDIO_SERVICES:
fputs ("audio services)", fp);
xdump (xp, fp);
if (2 != xp->buffer_size)
goto invalid;
fprintf (fp, " (main: %s, %s; second: %s, %s)",
map_type[xp->buffer[0] & 7],
language[(xp->buffer[0] >> 3) & 7],
sap_type[xp->buffer[1] & 7],
language[(xp->buffer[1] >> 3) & 7]);
break;
case VBI_XDS_PROGRAM_CAPTION_SERVICES:
fputs ("caption services)", fp);
xdump (xp, fp);
if (xp->buffer_size < 1
|| xp->buffer_size > 8)
goto invalid;
fputc ('(', fp);
for (i = 0; i < xp->buffer_size; ++i) {
fprintf (fp, "%sline=%u channel=%u %s %s",
(0 == i) ? "" : ", ",
(xp->buffer[i] & 4) ? 284 : 21,
(xp->buffer[i] & 2) ? 2 : 1,
(xp->buffer[i] & 1) ?
"text" : "captioning",
language[(xp->buffer[i] >> 3) & 7]);
}
fputc (')', fp);
break;
case VBI_XDS_PROGRAM_CGMS:
fputs ("cgms)", fp);
xdump (xp, fp);
if (1 != xp->buffer_size)
goto invalid;
fprintf (fp, " (%s", cgmsa[(xp->buffer[0] >> 3) & 3]);
if (xp->buffer[0] & 0x18)
fprintf (fp, ", %s",
scrambling[(xp->buffer[0] >> 1) & 3]);
fprintf (fp, ", analog_source=%u)", xp->buffer[0] & 1);
break;
case VBI_XDS_PROGRAM_ASPECT_RATIO:
{
unsigned int first_line, last_line;
fputs ("aspect)", fp);
xdump (xp, fp);
if (2 != xp->buffer_size
&& 3 != xp->buffer_size)
goto invalid;
first_line = 22 + (xp->buffer[0] & 63);
last_line = 262 - (xp->buffer[1] & 63);
fprintf (fp, " (active picture %u ... %u%s)",
first_line, last_line,
(3 == xp->buffer_size
&& (xp->buffer[2] & 1)) ?
" anamorphic" : "");
break;
}
case VBI_XDS_PROGRAM_DATA:
{
unsigned int rating;
unsigned int lhour, lmin;
unsigned int ehour, emin;
fputs ("data)", fp);
xdump (xp, fp);
/* XXX ok? */
if (xp->buffer_size < 10)
goto invalid;
rating = xp->buffer[5] & 7;
lhour = xp->buffer[7] & 63;
lmin = xp->buffer[6] & 63;
if (lmin > 59)
goto invalid;
ehour = xp->buffer[9] & 63;
emin = xp->buffer[8] & 63;
if (emin > 59)
goto invalid;
fputs (" (type: ", fp);
for (i = 0; i < 5; ++i) {
fprintf (fp, (i > 0) ? ", %s" : "%s",
vbi_prog_type_string
(VBI_PROG_CLASSF_EIA_608,
xp->buffer[i]));
}
fprintf (fp, "; rating: %s; "
"length: %02d:%02d; "
"elapsed: %02d:%02d)",
vbi_rating_string (VBI_RATING_AUTH_MPAA,
rating),
lhour, lmin, ehour, emin);
/* program name: buffer[10 ... 31] (xdump'ed) */
break;
}
case VBI_XDS_PROGRAM_MISC_DATA:
{
unsigned int month, day, hour, min;
fputs ("misc data)", fp);
xdump (xp, fp);
/* XXX ok? */
if (14 != xp->buffer_size)
goto invalid;
month = xp->buffer[3] & 15;
day = xp->buffer[2] & 31;
hour = xp->buffer[1] & 31;
min = xp->buffer[0] & 63;
if (month == 0 || month > 12
|| day == 0 || day > 31
|| hour > 23 || min > 59)
goto invalid;
fprintf (fp, " (%d %s %02d:%02d UTC, ",
day, month_names[month], hour, min);
fprintf (fp, " D=%d L=%d Z=%d T=%d",
!!(xp->buffer[1] & 0x20),
!!(xp->buffer[2] & 0x20),
!!(xp->buffer[3] & 0x20),
!!(xp->buffer[3] & 0x10));
fprintf (fp, ", main audio: %s, %s; second: %s, %s;",
map_type[xp->buffer[4] & 7],
language[(xp->buffer[4] >> 3) & 7],
sap_type[xp->buffer[5] & 7],
language[(xp->buffer[5] >> 3) & 7]);
for (i = 6; i < 8; ++i) {
fprintf (fp, "%sline=%u channel=%u %s %s",
(6 == i) ? " caption: " : ", ",
(xp->buffer[i] & 4) ? 284 : 21,
(xp->buffer[i] & 2) ? 2 : 1,
(xp->buffer[i] & 1) ?
"text" : "captioning",
language[(xp->buffer[i] >> 3) & 7]);
}
fprintf (fp, ", call letters: ");
for (i = 8; i < 12; ++i) {
fputc (_vbi_to_ascii (xp->buffer[i]), fp);
}
/* 02 to 69 or 0x20 0x20 */
fprintf (fp, ", channel: ");
for (i = 12; i < 14; ++i) {
fputc (_vbi_to_ascii (xp->buffer[i]), fp);
}
fputc (')', fp);
break;
}
case VBI_XDS_PROGRAM_DESCRIPTION_BEGIN ...
VBI_XDS_PROGRAM_DESCRIPTION_END - 1:
fprintf (fp, "description %u)",
(unsigned int) xp->xds_subclass
- (unsigned int)
VBI_XDS_PROGRAM_DESCRIPTION_BEGIN);
xdump (xp, fp);
break;
default:
fputs ("?)", fp);
xdump (xp, fp);
break;
}
break;
case VBI_XDS_CLASS_CHANNEL:
fputs ("(channel ", fp);
switch (xp->xds_subclass) {
case VBI_XDS_CHANNEL_NAME:
fputs ("name)", fp);
xdump (xp, fp);
break;
case VBI_XDS_CHANNEL_CALL_LETTERS:
fputs ("call letters)", fp);
xdump (xp, fp);
break;
case VBI_XDS_CHANNEL_TAPE_DELAY:
{
unsigned int hour, min;
fputs ("tape delay)", fp);
xdump (xp, fp);
if (2 != xp->buffer_size)
goto invalid;
hour = xp->buffer[1] & 31;
min = xp->buffer[0] & 63;
if (min > 59)
goto invalid;
fprintf (fp, " (%02d:%02d)", hour, min);
break;
}
case VBI_XDS_CHANNEL_TSID:
{
unsigned int tsid;
fputs ("transmission signal identifier)", fp);
xdump (xp, fp);
if (4 != xp->buffer_size)
goto invalid;
tsid = (xp->buffer[3] & 15) << 0;
tsid += (xp->buffer[2] & 15) << 4;
tsid += (xp->buffer[1] & 15) << 8;
tsid += (xp->buffer[0] & 15) << 12;
if (0 == tsid)
goto invalid;
fprintf (fp, " (0x%04x)", tsid);
break;
}
default:
fputs ("?)", fp);
xdump (xp, fp);
break;
}
break;
case VBI_XDS_CLASS_MISC:
fputs ("(misc: ", fp);
switch (xp->xds_subclass) {
case VBI_XDS_TIME_OF_DAY:
fputs ("time of day)", fp);
xdump (xp, fp);
if (6 != xp->buffer_size)
goto invalid;
fprintf (fp, " (%s, %d %s %d",
day_names [xp->buffer[4] & 7],
xp->buffer[2] & 31,
month_names[xp->buffer[3] & 15],
1990 + (xp->buffer[5] & 63));
fprintf (fp, " %02d:%02d UTC",
xp->buffer[1] & 31,
xp->buffer[0] & 63);
fprintf (fp, " D=%u L=%u Z=%u T=%u)",
!!(xp->buffer[1] & 0x20),
!!(xp->buffer[2] & 0x20),
!!(xp->buffer[3] & 0x20),
!!(xp->buffer[3] & 0x10));
break;
case VBI_XDS_IMPULSE_CAPTURE_ID:
fputs ("capture id)", fp);
xdump (xp, fp);
if (6 != xp->buffer_size)
goto invalid;
fprintf (fp, " (%d %s",
xp->buffer[2] & 31,
month_names[xp->buffer[3] & 15]);
fprintf (fp, " %02d:%02d",
xp->buffer[1] & 31,
xp->buffer[0] & 63);
fprintf (fp, " length=%02d:%02d",
xp->buffer[5] & 63,
xp->buffer[4] & 63);
fprintf (fp, " D=%u L=%u Z=%u T=%u)",
!!(xp->buffer[1] & 0x20),
!!(xp->buffer[2] & 0x20),
!!(xp->buffer[3] & 0x20),
!!(xp->buffer[3] & 0x10));
break;
case VBI_XDS_SUPPLEMENTAL_DATA_LOCATION:
{
unsigned int i;
fputs ("supplemental data)", fp);
xdump (xp, fp);
if (xp->buffer_size < 1)
goto invalid;
fputc ('(', fp);
for (i = 0; i < xp->buffer_size; ++i) {
fprintf (fp, "%sfield=%u line=%u",
(0 == i) ? "" : ", ",
!!(xp->buffer[i] & 0x20),
xp->buffer[i] & 31);
}
fputc (')', fp);
break;
}
case VBI_XDS_LOCAL_TIME_ZONE:
fputs ("time zone)", fp);
xdump (xp, fp);
if (1 != xp->buffer_size)
goto invalid;
fprintf (fp, " (UTC%+05d ods=%u)",
(xp->buffer[0] & 31) * -100,
!!(xp->buffer[0] & 0x20));
break;
case VBI_XDS_OUT_OF_BAND_CHANNEL:
{
unsigned int channel;
fputs ("out of band channel number)", fp);
xdump (xp, fp);
if (2 != xp->buffer_size)
goto invalid;
channel = (xp->buffer[0] & 63)
+ ((xp->buffer[1] & 63) << 6);
fprintf (fp, " (%u)", channel);
break;
}
case VBI_XDS_CHANNEL_MAP_POINTER:
{
unsigned int channel;
fputs ("channel map pointer)", fp);
xdump (xp, fp);
if (2 != xp->buffer_size)
goto invalid;
channel = (xp->buffer[0] & 63)
+ ((xp->buffer[1] & 63) << 6);
fprintf (fp, " (%u)", channel);
break;
}
case VBI_XDS_CHANNEL_MAP_HEADER:
{
unsigned int n_channels;
unsigned int version;
fputs ("channel map header)", fp);
xdump (xp, fp);
if (4 != xp->buffer_size)
goto invalid;
n_channels = (xp->buffer[0] & 63)
+ ((xp->buffer[1] & 63) << 6);
version = xp->buffer[2] & 63;
fprintf (fp, " (n_channels: %u, version: %u)",
n_channels, version);
break;
}
case VBI_XDS_CHANNEL_MAP:
{
unsigned int channel;
vbi_bool remapped;
fputs ("channel map)", fp);
xdump (xp, fp);
channel = (xp->buffer[0] & 63)
+ ((xp->buffer[1] & 31) << 6);
fprintf (fp, " (channel: %u)", channel);
remapped = !!(xp->buffer[1] & 0x20);
if (remapped) {
unsigned int tune_channel;
tune_channel = (xp->buffer[2] & 63)
+ ((xp->buffer[3] & 63) << 6);
fprintf (fp, ", remapped to: %u)",
tune_channel);
}
/* channel id: buffer[2 or 4 ... 31] (xdump'ed) */
fputc (')', fp);
break;
}
default:
fputs ("?)", fp);
xdump (xp, fp);
break;
}
break;
case VBI_XDS_CLASS_PUBLIC_SERVICE:
fputs ("(pub. service ", fp);
switch (xp->xds_subclass) {
case VBI_XDS_WEATHER_BULLETIN:
{
unsigned int duration;
fputs ("weather bulletin)", fp);
xdump (xp, fp);
fprintf (fp, " (event category: ");
for (i = 0; i < 3; ++i) {
fputc (_vbi_to_ascii (xp->buffer[i]), fp);
}
/* 3 digit FIPS number. */
fprintf (fp, ", state: ");
for (i = 3; i < 6; ++i) {
fputc (_vbi_to_ascii (xp->buffer[i]), fp);
}
/* 3 digit FIPS number. */
fprintf (fp, ", county: ");
for (i = 6; i < 9; ++i) {
fputc (_vbi_to_ascii (xp->buffer[i]), fp);
}
/* 2 digit number of quarter hours */
duration = (xp->buffer[9] & 15) * 150
+ (xp->buffer[10] & 15) * 15;
fprintf (fp, ", duration: %02d:%02d)",
duration / 60, duration % 60);
break;
}
case VBI_XDS_WEATHER_MESSAGE:
fputs ("weather message)", fp);
xdump (xp, fp);
break;
}
break;
case VBI_XDS_CLASS_RESERVED:
fputs ("(reserved)", fp);
xdump (xp, fp);
break;
case VBI_XDS_CLASS_UNDEFINED:
fputs ("(undefined)", fp);
xdump (xp, fp);
break;
default:
fputs ("(?)", fp);
xdump (xp, fp);
break;
}
fputc ('\n', fp);
}
/**
* @param xd XDS demultiplexer context allocated with vbi_xds_demux_new().
*
* Resets the XDS demux, useful for example after a channel change.
*
* @since 0.2.16
*/
void
vbi_xds_demux_reset (vbi_xds_demux * xd)
{
unsigned int n;
unsigned int i;
assert (NULL != xd);
n = N_ELEMENTS (xd->subpacket) * N_ELEMENTS (xd->subpacket[0]);
for (i = 0; i < n; ++i)
xd->subpacket[0][i].count = 0;
xd->curr_sp = NULL;
}
/**
* @param xd XDS demultiplexer context allocated with vbi_xds_demux_new().
* @param buffer Closed Caption character pair, as in struct vbi_sliced.
*
* This function takes two successive bytes of a raw Closed Caption
* stream, filters out XDS data and calls the output function given to
* vbi_xds_demux_new() when a new packet is complete.
*
* You should feed only data from NTSC line 284.
*
* @returns
* FALSE if the buffer contained parity errors.
*
* @since 0.2.16
*/
vbi_bool
vbi_xds_demux_feed (vbi_xds_demux * xd,
const uint8_t buffer[2])
{
_vbi_xds_subpacket *sp;
vbi_bool r;
int c1, c2;
assert (NULL != xd);
assert (NULL != buffer);
r = TRUE;
sp = xd->curr_sp;
log ("XDS demux %02x %02x\n", buffer[0], buffer[1]);
c1 = vbi_unpar8 (buffer[0]);
c2 = vbi_unpar8 (buffer[1]);
if ((c1 | c2) < 0) {
log ("XDS tx error, discard current packet\n");
if (sp) {
sp->count = 0;
sp->checksum = 0;
}
xd->curr_sp = NULL;
return FALSE;
}
switch (c1) {
case 0x00:
/* Stuffing. */
break;
case 0x01 ... 0x0E:
{
vbi_xds_class xds_class;
vbi_xds_subclass xds_subclass;
unsigned int i;
/* Packet header. */
xds_class = (c1 - 1) >> 1;
xds_subclass = c2;
i = xds_subclass;
/* MISC subclass 0x4n */
if (i >= 0x40)
i += 0x10 - 0x40;
if (xds_class > VBI_XDS_CLASS_MISC
|| i > N_ELEMENTS (xd->subpacket[0])) {
log ("XDS ignore packet 0x%x/0x%02x, "
"unknown class or subclass\n",
xds_class, xds_subclass);
goto discard;
}
sp = &xd->subpacket[xds_class][i];
xd->curr_sp = sp;
xd->curr.xds_class = xds_class;
xd->curr.xds_subclass = xds_subclass;
if (c1 & 1) {
/* Start packet. */
sp->checksum = c1 + c2;
sp->count = 2;
} else {
/* Continue packet. */
if (0 == sp->count) {
log ("XDS can't continue packet "
"0x%x/0x%02x, missed start\n",
xd->curr.xds_class,
xd->curr.xds_subclass);
goto discard;
}
}
break;
}
case 0x0F:
/* Packet terminator. */
if (!sp) {
log ("XDS can't finish packet, missed start\n");
break;
}
sp->checksum += c1 + c2;
if (0 != (sp->checksum & 0x7F)) {
log ("XDS ignore packet 0x%x/0x%02x, "
"checksum error\n",
xd->curr.xds_class, xd->curr.xds_subclass);
} else if (sp->count <= 2) {
log ("XDS ignore empty packet 0x%x/0x%02x\n",
xd->curr.xds_class, xd->curr.xds_subclass);
} else {
memcpy (xd->curr.buffer, sp->buffer, 32);
xd->curr.buffer_size = sp->count - 2;
xd->curr.buffer[sp->count - 2] = 0;
if (XDS_DEMUX_LOG)
_vbi_xds_packet_dump (&xd->curr, stderr);
r = xd->callback (xd, &xd->curr, xd->user_data);
}
/* fall through */
discard:
if (sp) {
sp->count = 0;
sp->checksum = 0;
}
/* fall through */
case 0x10 ... 0x1F:
/* Closed Caption. */
xd->curr_sp = NULL;
break;
case 0x20 ... 0x7F:
/* Packet contents. */
if (!sp) {
log ("XDS can't store packet, missed start\n");
break;
}
if (sp->count >= sizeof (sp->buffer) + 2) {
log ("XDS discard packet 0x%x/0x%02x, "
"buffer overflow\n",
xd->curr.xds_class, xd->curr.xds_subclass);
goto discard;
}
sp->buffer[sp->count - 2] = c1;
sp->buffer[sp->count - 1] = c2;
sp->checksum += c1 + c2;
sp->count += 1 + (0 != c2);
break;
}
return r;
}
#if 0 /* ideas */
const vbi_xds_packet *
vbi_xds_demux_get_packet (vbi_xds_demux * xd,
vbi_xds_class xds_class,
vbi_xds_subclass xds_subclass)
{
/* most recently received packet of this class. */
}
const vbi_xds_packet *
vbi_xds_demux_cor (vbi_xds_demux * xd,
const uint8_t buffer[2])
{
...
}
void
vbi_xds_demux_set_log (vbi_xds_demux * xd,
vbi_log_fn * callback,
unsigned int pri_mask)
{
...
}
#endif
/** @internal */
void
_vbi_xds_demux_destroy (vbi_xds_demux * xd)
{
assert (NULL != xd);
CLEAR (*xd);
}
/** @internal */
vbi_bool
_vbi_xds_demux_init (vbi_xds_demux * xd,
vbi_xds_demux_cb * callback,
void * user_data)
{
assert (NULL != xd);
assert (NULL != callback);
vbi_xds_demux_reset (xd);
xd->callback = callback;
xd->user_data = user_data;
return TRUE;
}
/**
* @param xd XDS demultiplexer context allocated with
* vbi_xds_demux_new(), can be @c NULL.
*
* Frees all resources associated with @a xd.
*
* @since 0.2.16
*/
void
vbi_xds_demux_delete (vbi_xds_demux * xd)
{
if (NULL == xd)
return;
_vbi_xds_demux_destroy (xd);
free (xd);
}
/**
* @param callback Function to be called by vbi_xds_demux_feed() when
* a new packet is available.
* @param user_data User pointer passed through to @a callback function.
*
* Allocates a new Extended Data Service (EIA 608) demultiplexer.
*
* @returns
* Pointer to newly allocated XDS demux context which must be
* freed with vbi_xds_demux_delete() when done. @c NULL on failure
* (out of memory).
*
* @since 0.2.16
*/
vbi_xds_demux *
vbi_xds_demux_new (vbi_xds_demux_cb * callback,
void * user_data)
{
vbi_xds_demux *xd;
assert (NULL != callback);
if (!(xd = malloc (sizeof (*xd)))) {
return NULL;
}
_vbi_xds_demux_init (xd, callback, user_data);
return xd;
}
syntax highlighted by Code2HTML, v. 0.9.1