/*
* libzvbi - Teletext packet decoder, page format clear
*
* Copyright (C) 2003-2004 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 version 2 as
* published by the Free Software Foundation.
*
* 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: pfc_demux.c,v 1.4 2006/05/18 16:53:38 mschimek Exp $ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "misc.h"
#include "hamm.h" /* vbi_iham8(), vbi_iham16p() */
#include "pfc_demux.h"
#define BLOCK_SEPARATOR 0x0C
#define FILLER_BYTE 0x03
/**
* @addtogroup PFCDemux Teletext Page Function Clear demultiplexer
* @ingroup LowDec
* @brief Separating data transmitted in Page Function Clear
* Teletext packets (ETS 300 708 section 4).
*/
/** @internal */
void
_vbi_pfc_block_dump (const vbi_pfc_block * pb,
FILE * fp,
vbi_bool binary)
{
assert (NULL != pb);
assert (NULL != fp);
fprintf (fp, "PFC pgno=%x stream=%u id=%u size=%u\n",
pb->pgno, pb->stream,
pb->application_id,
pb->block_size);
if (binary) {
fwrite (pb->block, sizeof (pb->block[0]), pb->block_size, fp);
} else {
unsigned int i;
for (i = 0; i < pb->block_size; ++i) {
fputc (_vbi_to_ascii (pb->block[i]), fp);
if ((i % 75) == 75)
fputc ('\n', fp);
}
if ((i % 75) != 75)
fputc ('\n', fp);
}
}
/**
* @param dx PFC demultiplexer context allocated with vbi_pfc_demux_new().
*
* Resets the PFC demux context, useful for example after a channel
* change.
*/
void
vbi_pfc_demux_reset (vbi_pfc_demux * dx)
{
assert (NULL != dx);
dx->ci = 256; /* normally 0 ... 15 */
dx->packet = 256; /* normally 1 ... 25 */
dx->n_packets = 0; /* discard all */
dx->bi = 0; /* empty buffer */
dx->left = 0;
dx->block.application_id = (unsigned int) -1; /* expect SH next */
}
/** @internal */
vbi_bool
_vbi_pfc_demux_decode (vbi_pfc_demux * dx,
const uint8_t buffer[42])
{
unsigned int col;
int bp;
bp = vbi_unham8 (buffer[2]) * 3;
if (bp < 0 || bp > 39) {
/* Invalid pointer or hamming error (-1). */
goto desynced;
}
col = 3;
while (col < 42) {
int bs;
if (dx->left > 0) {
unsigned int size;
size = MIN (dx->left, 42 - col);
memcpy (dx->block.block + dx->bi, buffer + col, size);
dx->bi += size;
dx->left -= size;
if (dx->left > 0) {
/* Packet done, block unfinished. */
return TRUE;
}
col += size;
if ((int) dx->block.application_id < 0) {
int sh; /* structure header */
sh = vbi_unham16p (dx->block.block)
+ vbi_unham16p (dx->block.block + 2)
* 256;
if (sh < 0) {
/* Hamming error. */
goto desynced;
}
dx->block.application_id = sh & 0x1F;
dx->block.block_size = sh >> 5;
dx->bi = 0;
dx->left = dx->block.block_size;
continue;
} else {
if (!dx->callback (dx, dx->user_data,
&dx->block)) {
goto desynced;
}
}
}
if (col <= 3) {
if (bp >= 39) {
/* No new block starts in this packet. */
return TRUE;
}
col = bp + 4; /* 2 pmag, 1 bp, 1 bs */
bs = vbi_unham8 (buffer[col - 1]);
} else {
while (FILLER_BYTE ==
(bs = vbi_unham8 (buffer[col++]))) {
if (col >= 42) {
/* No more data in this packet. */
return TRUE;
}
}
}
if (BLOCK_SEPARATOR != bs) {
/* BP must point to a block separator. */
goto desynced;
}
/* First with application_id == -1 we read 4 bytes structure
header into block[], then with application_id >= 0
block_size data bytes. */
dx->bi = 0;
dx->left = 4;
dx->block.application_id = (unsigned int) -1;
}
return TRUE;
desynced:
/* Incorrectable error, discard current block. */
vbi_pfc_demux_reset (dx);
return FALSE;
}
/**
* @param dx PFC demultiplexer context allocated with vbi_pfc_demux_new().
* @param buffer Teletext packet (last 42 bytes, i. e. without clock
* run-in and framing code), as in struct vbi_sliced.
*
* This function takes a raw stream of Teletext packets, filters out the page
* and stream requested with vbi_pfc_demux_new() and assembles the
* data transmitted in this page in a buffer. When a data block is complete
* it calls the output function given to vbi_pfc_demux_new().
*
* @returns
* FALSE if the packet contained incorrectable errors.
*/
vbi_bool
vbi_pfc_demux_feed (vbi_pfc_demux * dx,
const uint8_t buffer[42])
{
int pmag;
vbi_pgno pgno;
vbi_subno subno;
unsigned int packet;
assert (NULL != dx);
assert (NULL != buffer);
/* Packet filter. */
if ((pmag = vbi_unham16p (buffer)) < 0)
goto desynced;
pgno = pmag & 7;
if (0 == pgno)
pgno = 0x800;
else
pgno <<= 8;
packet = pmag >> 3;
if (0 == packet) {
unsigned int stream;
unsigned int ci;
pgno |= vbi_unham16p (buffer + 2);
if (pgno < 0)
goto desynced;
if (pgno != dx->block.pgno) {
dx->n_packets = 0;
return TRUE;
}
subno = vbi_unham16p (buffer + 4)
+ vbi_unham16p (buffer + 6) * 256;
if (subno < 0)
goto desynced;
stream = (subno >> 8) & 15;
if (stream != dx->block.stream) {
dx->n_packets = 0;
return TRUE;
}
ci = subno & 15;
if (ci != dx->ci) {
/* Page continuity lost, wait for new block. */
vbi_pfc_demux_reset (dx);
}
dx->ci = (ci + 1) & 15; /* next ci expected */
dx->packet = 1;
dx->n_packets = ((subno >> 4) & 7) + ((subno >> 9) & 0x18);
return TRUE;
} else {
/* In case 0 == C11 parallel page transmission. */
if ((pgno ^ dx->block.pgno) & 0xF00) {
/* Not dx->block.pgno. */
return TRUE;
}
}
if (0 == dx->n_packets) {
/* Not dx->block.pgno. */
return TRUE;
}
if (packet > 25) {
/* Stuffing packets, whatever. */
return TRUE;
}
if (packet != dx->packet
|| packet > dx->n_packets) {
/* Packet continuity lost, wait for new
block and page header. */
vbi_pfc_demux_reset (dx);
return TRUE;
}
dx->packet = packet + 1; /* next packet expected */
/* Now the actual decoding. */
return _vbi_pfc_demux_decode (dx, buffer);
desynced:
/* Incorrectable error, discard current block. */
vbi_pfc_demux_reset (dx);
return FALSE;
}
/**
* @internal
*/
void
_vbi_pfc_demux_destroy (vbi_pfc_demux * dx)
{
assert (NULL != dx);
CLEAR (*dx);
}
/**
* @internal
*/
vbi_bool
_vbi_pfc_demux_init (vbi_pfc_demux * dx,
vbi_pgno pgno,
unsigned int stream,
vbi_pfc_demux_cb * callback,
void * user_data)
{
assert (NULL != dx);
assert (NULL != callback);
vbi_pfc_demux_reset (dx);
dx->callback = callback;
dx->user_data = user_data;
dx->block.pgno = pgno;
dx->block.stream = stream;
return TRUE;
}
/**
* @param dx PFC demultiplexer context allocated with
* vbi_pfc_demux_new(), can be @c NULL.
*
* Frees all resources associated with @a dx.
*/
void
vbi_pfc_demux_delete (vbi_pfc_demux * dx)
{
if (NULL == dx)
return;
_vbi_pfc_demux_destroy (dx);
free (dx);
}
/**
* @param pgno Page to take PFC data from.
* @param stream PFC stream to be demultiplexed.
* @param cb Function to be called by vbi_pfc_demux_demux() when
* a new data block is available.
* @param user_data User pointer passed through to @a cb function.
*
* Allocates a new Page Function Clear (ETS 300 708 section 4)
* demultiplexer.
*
* @returns
* Pointer to newly allocated PFC demux context which must be
* freed with vbi_pfc_demux_delete() when done. @c NULL on failure
* (out of memory).
*/
vbi_pfc_demux *
vbi_pfc_demux_new (vbi_pgno pgno,
unsigned int stream,
vbi_pfc_demux_cb * callback,
void * user_data)
{
vbi_pfc_demux *dx;
if (!(dx = malloc (sizeof (*dx)))) {
return NULL;
}
if (!_vbi_pfc_demux_init (dx, pgno, stream,
callback, user_data)) {
free (dx);
dx = NULL;
}
return dx;
}
syntax highlighted by Code2HTML, v. 0.9.1