/* * 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 */