/*
* libzvbi
*
* Copyright (C) 2004, 2006 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: dvb_demux.c,v 1.13 2006/05/26 00:45:53 mschimek Exp $ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "misc.h" /* CLEAR() */
#include "hamm.h" /* vbi_rev8() */
#include "dvb.h"
#include "dvb_demux.h"
/**
* @addtogroup DVBDemux DVB VBI demultiplexer
* @ingroup Raw
* @brief Separating VBI data from a DVB PES stream
* (EN 301 472, EN 301 775).
*/
struct wrap {
/* Size must be >= maximum consume + maximum lookahead. */
uint8_t * buffer;
/* End of data in buffer. */
uint8_t * bp;
/* See below. */
unsigned int skip;
/* unsigned int consume; */
unsigned int lookahead;
/* Unconsumed data in buffer, starting at bp[-leftover]. */
unsigned int leftover;
};
/**
* @internal
* @param w Wrap-around context.
* @param dst Wrapped data pointer.
* @param scan_end End of lookahead range.
* @param src Source buffer pointer, will be incremented.
* @param src_left Bytes left in source buffer, will be decremented.
* @param src_size Size of source buffer.
*
* A buffer is assumed in memory at *src + *src_left - src_size, with
* src_size. This function reads at most *src_left bytes from this
* buffer starting at *src, incrementing *src and decrementing *src_left
* by the number of bytes read. NOTE *src_left must be equal to src_size
* when you change buffers.
*
* It removes (reads) w->skip bytes from the buffer and sets w->skip to
* zero, then removes w->consume bytes (not implemented at this time,
* assumed to be zero), copying the data AND the following w->lookahead
* bytes to an output buffer. In other words, *src is incremented by
* at most w->skip + w->consume bytes.
*
* On success TRUE is returned, *dst will point to the begin of the
* copied data (w->consume + w->lookahead), *scan_end to the end.
* However *scan_end - *dst can be greater than w->consume + w->lookahead
* if *src_left permits this. NOTE if copying can be avoided *dst and
* *scan_end may point into the source buffer, so don't free /
* overwrite it prematurely. *src_left will be >= 0.
*
* w->skip, w->consume and w->lookahead can change between successful
* calls.
*
* If more data is needed the function returns FALSE, and *src_left
* will be 0.
*/
vbi_inline vbi_bool
wrap_around (struct wrap * w,
const uint8_t ** dst,
const uint8_t ** scan_end,
const uint8_t ** src,
unsigned int * src_left,
unsigned int src_size)
{
unsigned int available;
unsigned int required;
if (w->skip > 0) {
/* w->skip is not w->consume to save copying. */
if (w->skip > w->leftover) {
w->skip -= w->leftover;
w->leftover = 0;
if (w->skip > *src_left) {
w->skip -= *src_left;
*src += *src_left;
*src_left = 0;
return FALSE;
}
*src += w->skip;
*src_left -= w->skip;
} else {
w->leftover -= w->skip;
}
w->skip = 0;
}
available = w->leftover + *src_left;
required = /* w->consume + */ w->lookahead;
if (required > available || available > src_size) {
/* Not enough data at s, or we have bytes left
over from the previous buffer, must wrap. */
if (required > w->leftover) {
/* Need more data in the wrap_buffer. */
memmove (w->buffer, w->bp - w->leftover, w->leftover);
w->bp = w->buffer + w->leftover;
required -= w->leftover;
if (required > *src_left) {
memcpy (w->bp, *src, *src_left);
w->bp += *src_left;
w->leftover += *src_left;
*src += *src_left;
*src_left = 0;
return FALSE;
}
memcpy (w->bp, *src, required);
w->bp += required;
w->leftover = w->lookahead;
*src += required;
*src_left -= required;
*dst = w->buffer;
*scan_end = w->bp - w->lookahead;
} else {
*dst = w->bp - w->leftover;
*scan_end = w->bp - w->lookahead;
/* w->leftover -= w->consume; */
}
} else {
/* All the required bytes are in this frame and
we have a complete copy of the w->buffer
leftover bytes before s. */
*dst = *src - w->leftover;
*scan_end = *src + *src_left - w->lookahead;
/* if (w->consume > w->leftover) {
unsigned int advance;
advance = w->consume - w->leftover;
*src += advance;
*src_left -= advance;
w->leftover = 0;
} else {
w->leftover -= w->consume;
} */
}
return TRUE;
}
/** @internal */
struct frame {
/* Buffers for sliced and raw data of one frame. */
vbi_sliced * sliced_begin;
vbi_sliced * sliced_end;
uint8_t * raw;
/* XXX replace by vbi_sampling_par. */
unsigned int raw_start[2];
unsigned int raw_count[2];
/* Current position. */
vbi_sliced * sp;
unsigned int last_line;
/* Number of lines extracted from current packet. */
unsigned int sliced_count;
uint8_t * rp;
vbi_sliced * raw_sp;
unsigned int raw_offset;
};
/**
* @internal
* Minimum lookahead to identify the packet header.
*/
#define HEADER_LOOKAHEAD 48
/** @internal */
struct _vbi_dvb_demux {
/** Wrap-around buffer. Must hold one PES packet,
at most 6 + 65535 bytes. */
uint8_t buffer[65536 + 16];
/** Output buffer for vbi_dvb_demux_demux(). */
vbi_sliced sliced[64];
/** Wrap-around state. */
struct wrap wrap;
/** Data unit demux state. */
struct frame frame;
/** PTS of current frame. */
int64_t frame_pts;
/** PTS of current packet. */
int64_t packet_pts;
/** New frame commences in this packet. (We cannot reset
immediately due to the coroutine design.) */
vbi_bool new_frame;
/** vbi_dvb_demux_demux() data. */
vbi_dvb_demux_cb * callback;
void * user_data;
_vbi_log_hook log;
};
/* Converts the line_offset and field_parity byte. */
vbi_inline unsigned int
lofp_to_line (unsigned int lofp,
unsigned int system)
{
static const unsigned int start [2][2] = {
{ 0, 263 },
{ 0, 313 },
};
unsigned int line_offset;
line_offset = lofp & 31;
if (line_offset > 0) {
unsigned int field_parity;
field_parity = !(lofp & (1 << 5));
return line_offset + start[system][field_parity];
} else {
/* Unknown line. */
return 0;
}
}
static vbi_sliced *
line_address (vbi_dvb_demux * dx,
unsigned int lofp,
unsigned int system,
vbi_bool raw)
{
struct frame *f = &dx->frame;
unsigned int line;
vbi_sliced *s;
if (f->sp >= f->sliced_end) {
error (&dx->log,
"Out of buffer space (%d lines).",
(int)(f->sliced_end - f->sliced_begin));
return NULL;
}
line = lofp_to_line (lofp, system);
debug2 (&dx->log, "Line %u.", line);
if (line > 0) {
if (raw) {
unsigned int field;
field = (line >= f->raw_start[1]);
if (line < f->raw_start[0]
|| line >= (f->raw_start[field]
+ f->raw_count[field])) {
notice (&dx->log,
"Raw line %u outside sampling range "
"%u ... %u, %u ... %u.",
line,
f->raw_start[0],
f->raw_start[0] + f->raw_count[0],
f->raw_start[1],
f->raw_start[1] + f->raw_count[1]);
return NULL;
} else if (0 != field) {
f->rp = f->raw
+ (line + f->raw_count[0]) * 720;
} else {
f->rp = f->raw + line * 720;
}
}
if (line > f->last_line) {
f->last_line = line;
s = f->sp++;
s->line = line;
} else {
/* EN 301 775 section 4.1: ascending line
order, no line twice. */
/* When the first line number in a packet is
smaller than the last line number in the
previous packet the frame is complete. */
if (0 == f->sliced_count)
return NULL;
notice (&dx->log,
"Illegal line order %u >= %u.",
line, f->last_line);
return NULL;
for (s = f->sliced_begin; s < f->sp; ++s)
if (line <= s->line)
break;
if (line < s->line) {
memmove (s, s + 1, (f->sp - s) * sizeof (*s));
++f->sp;
s->line = line;
}
if (raw) {
memset (f->rp, 0, 720);
}
}
} else {
/* Unknown line. */
if (0 == f->sliced_count
&& f->last_line > 0)
return NULL;
++f->last_line;
s = f->sp++;
s->line = 0;
f->rp += raw;
}
++f->sliced_count;
return s;
}
#if 0 /* not used yet */
static void
discard_raw (struct frame * f)
{
debug2 (&dx->log, "Discard raw VBI packet.");
memset (f->rp, 0, 720);
memmove (f->raw_sp + 1, f->raw_sp,
(f->sp - f->raw_sp - 1) * sizeof (vbi_sliced));
--f->sp;
f->raw_sp = NULL;
f->raw_offset = 0;
}
static int
demux_samples (struct frame * f,
uint8_t * p,
unsigned int system)
{
vbi_sliced *s;
unsigned int offset;
unsigned int n_pixels;
assert (0);
s = NULL; /* FIXME */
offset = p[3] * 256 + p[4];
n_pixels = p[5];
debug2 (&dx->log,
"Raw VBI packet offset=%u n_pixels=%u "
"first_segment=%u last_segment=%u.",
offset, n_pixels,
!!(p[2] & (1 << 7)),
!!(p[2] & (1 << 6)));
/* n_pixels <= 251 has been checked by caller. */
if (0 == n_pixels || (offset + n_pixels) > 720) {
notice (&dx->log,
"Illegal segment size %u ... %u (%u pixels).",
offset, offset + n_pixels, n_pixels);
discard_raw (f);
return 0;
}
if (p[2] & (1 << 7)) {
/* First segment. */
if (f->raw_offset > 0) {
debug2 (&dx->log,
"Last segment missing, line %u, offset %u.",
f->raw_sp->line, f->raw_offset);
discard_raw (f);
/* Recoverable error. */
}
if (!(f->raw_sp = line_address (f, p[2], system, TRUE))) {
if (0 == f->sliced_count)
return -1; /* is a new frame */
else
return 0; /* bad packet */
}
s->id = VBI_SLICED_NONE;
} else {
unsigned int line;
line = lofp_to_line (p[2], system);
if (0 == f->raw_offset) {
debug2 (&dx->log,
"First segment missing of line %u, "
"offset %u.",
line, offset);
/* Recoverable error. */
return 1;
} else if (line != f->raw_sp->line
|| offset != f->raw_offset) {
debug2 (&dx->log,
"Segment(s) missing or out of order, "
"expected line=%u offset=%u, "
"got line=%u offset=%u.",
f->raw_sp->line, f->raw_offset,
line, offset);
discard_raw (f);
/* Recoverable error. */
return 1;
}
}
memcpy (f->rp + offset, p + 6, n_pixels);
if (p[2] & (1 << 6)) {
/* Last segment. */
f->raw_offset = 0;
} else {
f->raw_offset = offset + n_pixels;
}
return TRUE;
}
#endif /* 0 */
static void
log_du_ttx (vbi_dvb_demux * dx,
const vbi_sliced * s)
{
uint8_t buffer[43];
unsigned int i;
for (i = 0; i < 42; ++i)
buffer[i] = _vbi_to_ascii (s->data[i]);
buffer[i] = 0;
debug2 (&dx->log, "DU-TTX %u >%s<", s->line, buffer);
}
static int
demux_data_units (vbi_dvb_demux * dx,
const uint8_t ** src,
unsigned int * src_left)
{
struct frame *f = &dx->frame;
const uint8_t *p;
const uint8_t *end2;
int r;
assert (*src_left >= 2);
r = 0; /* bad packet */
p = *src;
end2 = p + *src_left
- 2 /* data_unit_id,
data_unit_length */;
while (p < end2) {
unsigned int data_unit_id;
unsigned int data_unit_length;
vbi_sliced *s;
unsigned int i;
data_unit_id = p[0];
data_unit_length = p[1];
debug2 (&dx->log,
"data_unit_id=0x%02x data_unit_length=%u.",
data_unit_id, data_unit_length);
/* EN 301 775 section 4.3.1: Data units
must not cross PES packet boundaries. */
if (p + data_unit_length > end2)
goto failure;
switch (data_unit_id) {
case DATA_UNIT_STUFFING:
break;
case DATA_UNIT_EBU_TELETEXT_NON_SUBTITLE:
case DATA_UNIT_EBU_TELETEXT_SUBTITLE:
if (data_unit_length < 1 + 1 + 42) {
bad_length:
notice (&dx->log,
"data_unit_length=%u too small "
"for data_unit_id=%u.",
data_unit_length, data_unit_id);
goto failure;
}
/* We cannot transcode custom framing codes. */
if (0xE4 != p[3]) /* vbi_rev8 (0x27) */
break;
if (!(s = line_address (dx, p[2], 1, FALSE)))
goto no_line;
s->id = VBI_SLICED_TELETEXT_B;
for (i = 0; i < 42; ++i)
s->data[i] = vbi_rev8 (p[4 + i]);
if (dx->log.mask & VBI_LOG_DEBUG2)
log_du_ttx (dx, s);
break;
case DATA_UNIT_VPS:
if (data_unit_length < 1 + 13)
goto bad_length;
if (!(s = line_address (dx, p[2], 1, FALSE)))
goto no_line;
s->id = (s->line >= 313) ?
VBI_SLICED_VPS : VBI_SLICED_VPS;
for (i = 0; i < 13; ++i)
s->data[i] = p[3 + i];
break;
case DATA_UNIT_WSS:
if (data_unit_length < 1 + 2)
goto bad_length;
if (!(s = line_address (dx, p[2], 1, FALSE)))
goto no_line;
s->id = VBI_SLICED_WSS_625;
s->data[0] = vbi_rev8 (p[3]);
s->data[1] = vbi_rev8 (p[4]);
break;
case DATA_UNIT_ZVBI_WSS_CPR1204:
if (data_unit_length < 1 + 3)
goto bad_length;
if (!(s = line_address (dx, p[2], 0, FALSE)))
goto no_line;
s->id = VBI_SLICED_WSS_CPR1204;
s->data[0] = p[3];
s->data[1] = p[4];
s->data[2] = p[5];
break;
case DATA_UNIT_ZVBI_CLOSED_CAPTION_525:
if (data_unit_length < 1 + 2)
goto bad_length;
if (!(s = line_address (dx, p[2], 0, FALSE)))
goto no_line;
s->id = VBI_SLICED_CAPTION_525;
s->data[0] = vbi_rev8 (p[3]);
s->data[1] = vbi_rev8 (p[4]);
break;
case DATA_UNIT_CLOSED_CAPTION:
if (data_unit_length < 1 + 2)
goto bad_length;
if (!(s = line_address (dx, p[2], 1, FALSE)))
goto no_line;
s->id = VBI_SLICED_CAPTION_625;
s->data[0] = vbi_rev8 (p[3]);
s->data[1] = vbi_rev8 (p[4]);
break;
#if 0 /* later */
case DATA_UNIT_MONOCHROME_SAMPLES:
if (data_unit_length < 1 + 2 + 1 + p[5]) {
bad_sample_length:
notice (&dx->log,
"data_unit_length=%u too small "
"for data_unit_id=%u with %u "
"samples.",
data_unit_length,
data_unit_id, p[5]);
goto failure;
}
if ((r = demux_samples (f, p, 1)) < 1)
goto failure;
break;
case DATA_UNIT_ZVBI_MONOCHROME_SAMPLES_525:
if (data_unit_length < 1 + 2 + 1 + p[5])
goto bad_sample_length;
if ((r = demux_samples (f, p, 0)) < 1)
goto failure;
break;
#endif
default:
notice (&dx->log,
"Unknown data_unit_id=%u.",
data_unit_id);
break;
}
p += data_unit_length + 2;
}
*src = p;
*src_left = 0;
return 1; /* success */
no_line:
if (0 == f->sliced_count)
r = -1; /* is a new frame */
failure:
*src_left = end2 + 2 - p;
*src = p;
return r;
}
vbi_inline void
reset_frame (struct frame * f)
{
f->sp = f->sliced_begin;
f->last_line = 0;
f->sliced_count = 0;
if (f->rp > f->raw) {
unsigned int lines;
lines = f->raw_count[0] + f->raw_count[1];
memset (f->raw, 0, lines * 720);
}
f->rp = f->raw;
f->raw_sp = NULL;
f->raw_offset = 0;
}
/*
Add _vbi_dvb_demultiplex() here.
*/
static vbi_bool
timestamp (vbi_dvb_demux * dx,
int64_t * pts,
unsigned int mark,
const uint8_t * p)
{
unsigned int t;
if (mark != (p[0] & 0xF1u))
return FALSE;
t = p[1] << 22;
t |= (p[2] & ~1) << 14;
t |= p[3] << 7;
t |= p[4] >> 1;
if (dx->log.mask & VBI_LOG_DEBUG) {
int64_t old_pts;
int64_t new_pts;
old_pts = *pts;
new_pts = t | (((int64_t) p[0] & 0x0E) << 29);
debug1 (&dx->log,
"TS%x 0x%" PRIx64 " (%+" PRId64 ").",
mark, new_pts, new_pts - old_pts);
}
*pts = t | (((int64_t) p[0] & 0x0E) << 29);
return TRUE;
}
static vbi_bool
demux_packet (vbi_dvb_demux * dx,
const uint8_t ** src,
unsigned int * src_left)
{
const uint8_t *s;
unsigned int s_left;
s = *src;
s_left = *src_left;
for (;;) {
const uint8_t *p;
const uint8_t *scan_begin;
const uint8_t *scan_end;
unsigned int packet_length;
unsigned int header_length;
if (!wrap_around (&dx->wrap,
&p, &scan_end,
&s, &s_left, *src_left))
break; /* out of data */
/* Data units */
if (dx->wrap.lookahead > HEADER_LOOKAHEAD) {
unsigned int left;
dx->frame.sliced_count = 0;
left = dx->wrap.lookahead;
for (;;) {
unsigned int lines;
int r;
if (dx->new_frame) {
/* New frame commences
in this packet. */
reset_frame (&dx->frame);
dx->frame_pts = dx->packet_pts;
dx->new_frame = FALSE;
}
r = demux_data_units (dx, &p, &left);
if (0 == r) {
debug1 (&dx->log,
"Bad packet, discard.");
dx->new_frame = TRUE;
break;
}
if (r > 0) {
/* Data unit extraction successful.
Packet continues previous frame. */
break;
}
debug1 (&dx->log, "New frame.");
/* A new frame commences in this packet.
We must flush dx->frame before we extract
data units from this packet. */
/* Must not change dx->frame or dx->frame_pts
here to permit "pass by return". */
dx->new_frame = TRUE;
if (!dx->callback)
goto failure;
lines = dx->frame.sp - dx->frame.sliced_begin;
if (!dx->callback (dx,
dx->user_data,
dx->frame.sliced_begin,
lines,
dx->frame_pts))
goto failure;
}
dx->wrap.skip = dx->wrap.lookahead;
dx->wrap.lookahead = HEADER_LOOKAHEAD;
continue;
}
/* Start code scan */
scan_begin = p;
for (;;) {
/* packet_start_code_prefix [24] == 0x000001,
stream_id [8] == PRIVATE_STREAM_1 */
debug1 (&dx->log,
"packet_start_code=%02x%02x%02x%02x.",
p[0], p[1], p[2], p[3]);
if (likely (p[2] & ~1)) {
/* Not 000001 or xx0000 or xxxx00. */
p += 3;
} else if (0 != (p[0] | p[1]) || 1 != p[2]) {
++p;
} else if (PRIVATE_STREAM_1 == p[3]) {
break;
} else if (p[3] >= 0xBC) {
packet_length = p[4] * 256 + p[5];
dx->wrap.skip = (p - scan_begin)
+ 6 + packet_length;
goto outer_continue;
}
if (unlikely (p >= scan_end)) {
dx->wrap.skip = p - scan_begin;
goto outer_continue;
}
}
/* Packet header */
packet_length = p[4] * 256 + p[5];
debug1 (&dx->log,
"packet_length=%u.",
packet_length);
dx->wrap.skip = (p - scan_begin) + 6 + packet_length;
/* EN 300 472 section 4.2: N x 184 - 6. (We'll read
46 bytes without further checks and need at least
one data unit to function properly.) */
if (packet_length < 178)
continue;
/* PES_header_data_length [8] */
header_length = p[8];
debug1 (&dx->log,
"header_length=%u.",
header_length);
/* EN 300 472 section 4.2: 0x24. */
if (36 != header_length)
continue;
debug1 (&dx->log,
"data_identifier=%u.",
p[9 + 36]);
/* data_identifier (EN 301 775 section 4.3.2) */
switch (p[9 + 36]) {
case 0x10 ... 0x1F:
case 0x99 ... 0x9B:
break;
default:
continue;
}
/* '10', PES_scrambling_control [2] == 0 (not scrambled),
PES_priority, data_alignment_indicator == 1 (data unit
starts immediately after header),
copyright, original_or_copy */
if (0x84 != (p[6] & 0xF4))
continue;
/* PTS_DTS_flags [2], ESCR_flag, ES_rate_flag,
DSM_trick_mode_flag, additional_copy_info_flag,
PES_CRC_flag, PES_extension_flag */
switch (p[7] >> 6) {
case 2: /* PTS 0010 xxx 1 ... */
if (!timestamp (dx, &dx->packet_pts, 0x21, p + 9))
continue;
break;
case 3: /* PTS 0011 xxx 1 ... DTS ... */
if (!timestamp (dx, &dx->packet_pts, 0x31, p + 9))
continue;
break;
default:
/* EN 300 472 section 4.2: a VBI PES packet [...]
always carries a PTS. */
/* But it doesn't matter if this packet continues
the previous frame. */
if (dx->new_frame)
continue;
break;
}
/* Habemus packet. */
dx->wrap.skip = (p - scan_begin) + 9 + 36 + 1;
dx->wrap.lookahead = packet_length - 3 - 36 - 1;
outer_continue:
;
}
*src = s;
*src_left = s_left;
return TRUE;
failure:
*src = s;
*src_left = s_left;
return FALSE;
}
/**
* @brief DVB VBI demux coroutine.
* @param dx DVB demultiplexer context allocated with vbi_dvb_pes_demux_new().
* @param sliced Demultiplexed sliced data will be stored here. You must
* not change @a sliced and @a sliced_lines in successive calls until
* a frame is complete (i.e. the function returns a value > 0).
* @param sliced_lines At most this number of sliced lines will be stored
* at @a sliced.
* @param pts If not @c NULL the Presentation Time Stamp associated with the
* first line of the demultiplexed frame will be stored here.
* @param buffer *buffer points to DVB PES data, will be incremented by the
* number of bytes read from the buffer. This pointer need not align with
* packet boundaries.
* @param buffer_left *buffer_left is the number of bytes left in @a buffer,
* will be decremented by the number of bytes read. *buffer_left need not
* align with packet size. The packet filter works faster with larger
* buffers. When you read from an MPEG file, mapping the file into memory
* and passing pointers to the mapped data will be fastest.
*
* This function takes an arbitrary number of DVB PES data bytes, filters
* out PRIVATE_STREAM_1 packets, filters out valid VBI data units, converts
* them to vbi_sliced format and stores the sliced data at @a sliced.
*
* @returns
* The number of sliced lines stored at @a sliced when a frame is complete,
* @c 0 if more data is needed (@a *buffer_left is @c 0) or the data
* contained errors.
*
* @bug
* Demultiplexing of raw VBI data is not supported yet,
* raw data will be discarded.
*
* @since 0.2.10
*/
unsigned int
vbi_dvb_demux_cor (vbi_dvb_demux * dx,
vbi_sliced * sliced,
unsigned int sliced_lines,
int64_t * pts,
const uint8_t ** buffer,
unsigned int * buffer_left)
{
assert (NULL != dx);
assert (NULL != sliced);
assert (NULL != buffer);
assert (NULL != buffer_left);
dx->frame.sliced_begin = sliced;
dx->frame.sliced_end = sliced + sliced_lines;
if (!demux_packet (dx, buffer, buffer_left)) {
if (pts)
*pts = dx->frame_pts;
return dx->frame.sp - dx->frame.sliced_begin;
}
return 0;
}
/**
* @brief Feeds DVB VBI demux with data.
* @param dx DVB demultiplexer context allocated with vbi_dvb_pes_demux_new().
* @param buffer DVB PES data, need not align with packet boundaries.
* @param buffer_size Number of bytes in @a buffer, need not align with
* packet size. The packet filter works faster with larger buffers.
*
* This function takes an arbitrary number of DVB PES data bytes, filters
* out PRIVATE_STREAM_1 packets, filters out valid VBI data units, converts
* them to vbi_sliced format and calls the vbi_dvb_demux_cb function given
* to vbi_dvb_pes_demux_new() when a new frame is complete.
*
* @returns
* @c FALSE if the data contained errors.
*
* @bug
* Demultiplexing of raw VBI data is not supported yet,
* raw data will be discarded.
*
* @since 0.2.10
*/
vbi_bool
vbi_dvb_demux_feed (vbi_dvb_demux * dx,
const uint8_t * buffer,
unsigned int buffer_size)
{
vbi_bool success;
assert (NULL != dx);
assert (NULL != buffer);
assert (NULL != dx->callback);
success = demux_packet (dx, &buffer, &buffer_size);
return success;
}
/**
* @brief Resets DVB VBI demux.
* @param dx DVB demultiplexer context allocated with vbi_dvb_pes_demux_new().
*
* Resets the DVB demux to the initial state as after vbi_dvb_pes_demux_new(),
* useful for example after a channel change.
*
* @since 0.2.10
*/
void
vbi_dvb_demux_reset (vbi_dvb_demux * dx)
{
assert (NULL != dx);
CLEAR (dx->wrap);
dx->wrap.buffer = dx->buffer;
dx->wrap.bp = dx->buffer;
dx->wrap.lookahead = HEADER_LOOKAHEAD;
CLEAR (dx->frame);
dx->frame.sliced_begin = dx->sliced;
dx->frame.sliced_end = dx->sliced + N_ELEMENTS (dx->sliced);
/* Raw data ignored for now. */
dx->frame_pts = 0;
dx->packet_pts = 0;
dx->new_frame = TRUE;
}
/**
* @param dx DVB demultiplexer context allocated with vbi_dvb_pes_demux_new().
* @param mask Which kind of information to log. Can be @c 0.
* @param log_fn This function is called with log messages. Consider
* vbi_log_on_stderr(). Can be @c NULL to disable logging.
* @param user_data User pointer passed through to the @a log_fn function.
*
* The DVB demultiplexer supports the logging of errors in the PES stream and
* information useful to debug the demultiplexer.
*
* With this function you can redirect log messages generated by this module
* from the global log function ( see vbi_set_log_fn() ) to a different function,
* or enable logging only in the DVB demultiplexer @a dx.
*
* @note
* The kind and contents of log messages may change in the future.
*
* @since 0.2.22
*/
void
vbi_dvb_demux_set_log_fn (vbi_dvb_demux * dx,
vbi_log_mask mask,
vbi_log_fn * log_fn,
void * user_data)
{
assert (NULL != dx);
if (NULL == log_fn)
mask = 0;
dx->log.mask = mask;
dx->log.fn = log_fn;
dx->log.user_data = user_data;
}
/**
* @brief Deletes DVB VBI demux.
* @param dx DVB demultiplexer context allocated with
* vbi_dvb_pes_demux_new(), can be @c NULL.
*
* Frees all resources associated with @a dx.
*
* @since 0.2.10
*/
void
vbi_dvb_demux_delete (vbi_dvb_demux * dx)
{
if (NULL == dx)
return;
CLEAR (*dx);
free (dx);
}
/**
* @brief Allocates DVB VBI demux.
* @param callback Function to be called by vbi_dvb_demux_demux() when
* a new frame is available.
* @param user_data User pointer passed through to @a callback function.
*
* Allocates a new DVB VBI (EN 301 472, EN 301 775) demultiplexer taking
* a PES stream as input.
*
* @returns
* Pointer to newly allocated DVB demux context which must be
* freed with vbi_dvb_demux_delete() when done. @c NULL on failure
* (out of memory).
*
* @since 0.2.10
*/
vbi_dvb_demux *
vbi_dvb_pes_demux_new (vbi_dvb_demux_cb * callback,
void * user_data)
{
vbi_dvb_demux *dx;
if (!(dx = malloc (sizeof (*dx)))) {
return NULL;
}
CLEAR (*dx);
vbi_dvb_demux_reset (dx);
dx->callback = callback;
dx->user_data = user_data;
return dx;
}
/* For compatibility with Zapping 0.8 */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
extern unsigned int
_vbi_dvb_demux_cor (vbi_dvb_demux * dx,
vbi_sliced * sliced,
unsigned int sliced_lines,
int64_t * pts,
const uint8_t ** buffer,
unsigned int * buffer_left);
extern void
_vbi_dvb_demux_delete (vbi_dvb_demux * dx);
extern vbi_dvb_demux *
_vbi_dvb_demux_pes_new (vbi_dvb_demux_cb * callback,
void * user_data);
unsigned int
_vbi_dvb_demux_cor (vbi_dvb_demux * dx,
vbi_sliced * sliced,
unsigned int sliced_lines,
int64_t * pts,
const uint8_t ** buffer,
unsigned int * buffer_left)
{
return vbi_dvb_demux_cor (dx, sliced, sliced_lines,
pts, buffer, buffer_left);
}
void
_vbi_dvb_demux_delete (vbi_dvb_demux * dx)
{
vbi_dvb_demux_delete (dx);
}
vbi_dvb_demux *
_vbi_dvb_demux_pes_new (vbi_dvb_demux_cb * callback,
void * user_data)
{
return vbi_dvb_pes_demux_new (callback, user_data);
}
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
syntax highlighted by Code2HTML, v. 0.9.1