/*
* libzvbi
*
* Copyright (C) 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: dvb_mux.c,v 1.7 2006/05/31 03:52:18 mschimek Exp $ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "misc.h"
#include "dvb.h"
#include "dvb_mux.h"
#include "hamm.h" /* vbi_rev8() */
#ifndef DVB_MUX_LOG
# define DVB_MUX_LOG 0
#endif
static vbi_bool
stuffing (uint8_t * p,
unsigned int p_left,
vbi_bool fixed_length)
{
/* data_unit_id: DATA_UNIT_STUFFING (0xFF),
stuffing byte: 0xFF */
memset (p, 0xFF, p_left);
while (p_left >= 2 + 1 + 1 + 42) {
p[1] = 1 + 1 + 42; /* fixed length */
p += 46;
p_left -= 46;
}
if (p_left > 0) {
if (p_left < 2)
return FALSE;
p[1] = p_left - 2;
if (fixed_length)
return FALSE;
}
return TRUE;
}
/**
* @param packet *packet is the output buffer pointer and will be
* advanced by the number of bytes stored.
* @param packet_left *packet_left is the number of bytes left
* in the output buffer, and will be decremented by the number of
* bytes stored.
* @param sliced *sliced is the vbi_sliced data to be multiplexed,
* and will be advanced by the number of sliced lines read.
* @param sliced_left *sliced_left is the number of sliced lines
* left in the *sliced array, and will be decremented by the
* number of lines read.
* @param data_identifier Compliant to EN 301 775 section
* 4.3.2, when the data_indentifier lies in range 0x10 ... 0x1F
* data units will be split or padded to data_unit_length 0x2C.
* @param service_set Only data services in this set will be
* encoded. Create a set by or-ing @c VBI_SLICED_ values.
*
* Stores an array of vbi_sliced data in a MPEG-2 PES packet
* as defined in EN 301 775. When *sliced_left is zero, or when
* *packet_left becomes too small the function fills the remaining
* space with stuffing units.
*
* The following data services can be encoded per EN 300 472:
* @c VBI_SLICED_TELETEXT_B_625_L10 with data_unit_id 0x02. Additionally
* EN 301 775 permits @c VBI_SLICED_VPS, @c _VPS_F2, @c _WSS_625
* and @c _CAPTION_625 (any field).
*
* For completeness the function also encodes
* @c VBI_SLICED_TELETEXT_B_625_L25, @c _CAPTION_525 (any field) and
* @c _WSS_CPR1204 with data_unit_id 0x02, 0xB5 and 0xB4 respectively.
* You can modify this behaviour with the service_set parameter.
*
* The lines in the sliced array must be sorted by ascending line
* number and belong to the same frame. If unknown all lines can
* have line number zero. Sliced data outside lines 1 ... 31 and 313 ... 344
* (1 ... 31 and 263 ... 294 for NTSC data services) and data services
* not covered will be ignored.
*/
void
_vbi_dvb_multiplex_sliced (uint8_t ** packet,
unsigned int * packet_left,
const vbi_sliced ** sliced,
unsigned int * sliced_left,
unsigned int data_identifier,
vbi_service_set service_set)
{
uint8_t *p;
const vbi_sliced *s;
unsigned int p_left;
unsigned int s_left;
unsigned int last_line;
vbi_bool fixed_length;
assert (NULL != packet);
assert (NULL != sliced);
assert (NULL != packet_left);
assert (NULL != sliced_left);
p = *packet;
p_left = *packet_left;
if (NULL == p || 0 == p_left) {
return;
}
s = *sliced;
s_left = *sliced_left;
fixed_length = (data_identifier >= DATA_ID_EBU_TELETEXT_BEGIN
&& data_identifier < DATA_ID_EBU_TELETEXT_END);
if (NULL == s || 0 == s_left) {
if (!stuffing (p, p_left, fixed_length)) {
fprintf (stderr,
"%s: packet_left=%u too small for "
"stuffing.\n",
__FUNCTION__, p_left);
abort ();
}
p += p_left;
p_left = 0;
goto finish;
}
last_line = 0;
while (s_left > 0) {
unsigned int length;
unsigned int f2start;
unsigned int i;
if (s->line > 0) {
if (s->line < last_line) {
fprintf (stderr,
"%s: Sliced lines not sorted.\n",
__FUNCTION__);
abort ();
}
last_line = s->line;
}
if (!(s->id & service_set))
goto skip;
f2start = 313;
if (s->id & (VBI_SLICED_CAPTION_525 |
VBI_SLICED_WSS_CPR1204))
f2start = 263;
if (fixed_length) {
length = 2 + 1 + 1 + 42;
} else if (s->id & VBI_SLICED_TELETEXT_B) {
length = 2 + 1 + 1 + 42;
} else if (s->id & (VBI_SLICED_VPS)) {
length = 2 + 1 + 13;
} else if (s->id & (VBI_SLICED_WSS_625 |
VBI_SLICED_CAPTION_525 |
VBI_SLICED_CAPTION_625)) {
length = 2 + 1 + 2;
} else if (s->id & (VBI_SLICED_WSS_CPR1204)) {
length = 2 + 1 + 3;
} else {
if (DVB_MUX_LOG)
fprintf (stderr,
"%s: Skipping sliced id 0x%08x\n",
__FUNCTION__, s->id);
goto skip;
}
if (length > p_left) {
/* EN 301 775 section 4.3.1: Data units
cannot cross PES packet boundaries. */
if (!stuffing (p, p_left, fixed_length)) {
fprintf (stderr,
"%s: only %u bytes left for "
"stuffing.\n",
__FUNCTION__, p_left);
abort ();
}
p += p_left;
p_left = 0;
break;
}
/* Pad to data_unit_length if necessary. */
if (fixed_length)
memset (p, 0xFF, length);
if (s->line < 32) {
/* Unknown line (0) or first field. */
p[2] = (3 << 6) + (1 << 5) + s->line;
} else if (s->line < f2start) {
if (DVB_MUX_LOG)
fprintf (stderr,
"%s: Sliced line %u exceeds limit "
"%u ... %u, %u ... %u\n",
__FUNCTION__, s->line, 0, 31,
f2start, f2start + 31);
goto skip;
} else if (s->line < f2start + 32) {
/* Second field. */
p[2] = (3 << 6) + (0 << 5) + s->line - f2start;
} else {
if (DVB_MUX_LOG)
fprintf (stderr,
"%s: Sliced line %u exceeds limit "
"%u ... %u, %u ... %u\n",
__FUNCTION__, s->line, 0, 31,
f2start, f2start + 31);
goto skip;
}
if (s->id & VBI_SLICED_TELETEXT_B) {
/* data_unit_id [8], data_unit_length [8],
reserved [2], field_parity, line_offset [5],
framing_code [8], magazine_and_packet_address [16],
data_block [320] (msb first transmitted) */
p[0] = DATA_UNIT_EBU_TELETEXT_NON_SUBTITLE;
p[1] = length - 2;
p[3] = 0xE4; /* vbi_rev8 (0x27); */
for (i = 0; i < 42; ++i)
p[4 + i] = vbi_rev8 (s->data[i]);
} else if (s->id & VBI_SLICED_CAPTION_525) {
/* data_unit_id [8], data_unit_length [8],
reserved [2], field_parity, line_offset [5],
data_block [16] (msb first) */
p[0] = DATA_UNIT_ZVBI_CLOSED_CAPTION_525;
p[1] = length - 2;
p[3] = vbi_rev8 (s->data[0]);
p[4] = vbi_rev8 (s->data[1]);
} else if (s->id & (VBI_SLICED_VPS)) {
/* data_unit_id [8], data_unit_length [8],
reserved [2], field_parity, line_offset [5],
vps_data_block [104] (msb first) */
p[0] = DATA_UNIT_VPS;
p[1] = length - 2;
for (i = 0; i < 13; ++i)
p[3 + i] = s->data[i];
} else if (s->id & VBI_SLICED_WSS_625) {
/* data_unit_id [8], data_unit_length [8],
reserved[2], field_parity, line_offset [5],
wss_data_block[14] (msb first), reserved[2] */
p[0] = DATA_UNIT_WSS;
p[1] = length - 2;
p[3] = vbi_rev8 (s->data[0]);
p[4] = vbi_rev8 (s->data[1]) | 3;
} else if (s->id & VBI_SLICED_CAPTION_625) {
/* data_unit_id [8], data_unit_length [8],
reserved[2], field_parity, line_offset [5],
data_block[16] (msb first) */
p[0] = DATA_UNIT_CLOSED_CAPTION;
p[1] = length - 2;
p[3] = vbi_rev8 (s->data[0]);
p[4] = vbi_rev8 (s->data[1]);
} else if (s->id & VBI_SLICED_WSS_CPR1204) {
/* data_unit_id [8], data_unit_length [8],
reserved[2], field_parity, line_offset [5],
wss_data_block[20] (msb first), reserved[4] */
p[0] = DATA_UNIT_ZVBI_WSS_CPR1204;
p[1] = length - 2;
p[3] = s->data[0];
p[4] = s->data[1];
p[5] = s->data[2] | 0xF;
} else {
if (DVB_MUX_LOG)
fprintf (stderr, "%s: Skipping sliced id "
"0x%08x\n", __FUNCTION__, s->id);
goto skip;
}
p += length;
p_left -= length;
skip:
++s;
--s_left;
}
finish:
*packet = p;
*packet_left = p_left;
*sliced = s;
*sliced_left = s_left;
}
/**
* @param packet *packet is the output buffer pointer and will be
* advanced by the number of bytes stored.
* @param packet_left *packet_left is the number of bytes left
* in the output buffer, and will be decremented by the number of
* bytes stored.
* @param samples *samples is the raw VBI data line to be
* multiplexed, in ITU-R BT.601 format, Y values only.
* *samples will be advanced by the number of bytes read.
* @param samples_left *samples_left is the number of bytes left
* in the *samples buffer, and will be decremented by the
* number of bytes read.
* @param samples_size Number of bytes in the *samples buffer.
* offset + samples_size must not exceed 720.
* @param data_identifier Compliant to EN 301 775 section
* 4.3.2, when the data_indentifier lies in range 0x10 ... 0x1F
* data units will be restricted to data_unit_length 0x2C.
* @param videostd_set Video standard.
* @param line ITU-R line number to be encoded (see vbi_sliced).
* Valid line numbers are 0 (unknown), 1 ... 31 and 313 ... 344
* for @c VBI_VIDEOSTD_SET_625, 1 ... 31 and 263 ... 294 for
* @c VBI_VIDEOSTD_SET_525.
* @param offset Offset of the first sample in the *samples buffer.
*
* Stores one line of raw VBI data in a MPEG-2 PES packet
* as defined in EN 301 775. When *packet_left becomes too small
* the function fills up the remaining space with stuffing bytes.
*
* EN 301 775 permits all video standards in the
* @c VBI_VIDEOSTD_SET_625. Additionally the function accepts
* @c VBI_VIDEOSTD_SET_525, these samples will be encoded
* with data_unit_id 0xB6.
*/
void
_vbi_dvb_multiplex_samples (uint8_t ** packet,
unsigned int * packet_left,
const uint8_t ** samples,
unsigned int * samples_left,
unsigned int samples_size,
unsigned int data_identifier,
vbi_videostd_set videostd_set,
unsigned int line,
unsigned int offset)
{
uint8_t *p;
const uint8_t *s;
unsigned int p_left;
unsigned int s_left;
unsigned int id;
unsigned int f2_start;
unsigned int min_space;
assert (NULL != packet);
assert (NULL != packet_left);
assert (NULL != samples);
assert (NULL != samples_left);
p = *packet;
p_left = *packet_left;
if (NULL == p || 0 == p_left) {
return;
}
if (videostd_set & VBI_VIDEOSTD_SET_525_60) {
if (videostd_set & VBI_VIDEOSTD_SET_625_50) {
fprintf (stderr,
"%s: Ambiguous videostd_set 0x%x\n",
__FUNCTION__,
(unsigned int) videostd_set);
abort ();
}
id = DATA_UNIT_ZVBI_MONOCHROME_SAMPLES_525;
f2_start = 263;
} else {
id = DATA_UNIT_MONOCHROME_SAMPLES;
f2_start = 313;
}
if (line < 32) {
/* Unknown line (0) or first field. */
line = (1 << 5) + line;
} else if (line >= f2_start && line < f2_start + 32) {
line = (0 << 5) + line - f2_start;
} else {
fprintf (stderr,
"%s: Line number %u exceeds limits "
"%u ... %u, %u ... %u",
__FUNCTION__, line, 0, 31, f2_start, f2_start + 31);
abort ();
}
s = *samples;
s_left = *samples_left;
if (offset + samples_size > 720) {
fprintf (stderr,
"%s: offset %u + samples_size %u > 720\n",
__FUNCTION__, offset, samples_size);
abort ();
}
if (s_left > samples_size) {
fprintf (stderr,
"%s: samples_left %u > samples_size %u",
__FUNCTION__, s_left, samples_size);
abort ();
}
if (data_identifier >= DATA_ID_EBU_TELETEXT_BEGIN
&& data_identifier < DATA_ID_EBU_TELETEXT_END)
min_space = 7;
else
min_space = 2 + 0x2C;
offset += samples_size - s_left;
while (s_left > 0) {
unsigned int n_pixels;
if (min_space > p_left) {
/* EN 301 775 section 4.3.1: Data units
cannot cross PES packet boundaries. */
if (!stuffing (p, p_left, min_space > 7)) {
fprintf (stderr,
"%s: only %u bytes left for "
"stuffing.\n",
__FUNCTION__, p_left);
abort ();
}
p += p_left;
p_left = 0;
goto finish;
}
if (min_space > 7) {
uint8_t *end;
n_pixels = MIN (s_left, 2u + 0x2Cu - 6u);
n_pixels = MIN (n_pixels, p_left - 6);
/* data_unit_id [8], data_unit_length [8],
first_segment_flag [1], last_segment_flag [1],
field_parity [1], line_offset [5],
first_pixel_position [16], n_pixels [8] */
p[0] = id;
p[1] = 0x2C;
p[2] = line
+ ((s_left == samples_size) << 7)
+ ((s_left == n_pixels) << 6);
p[3] = offset >> 8;
p[4] = offset;
p[5] = n_pixels;
memcpy (p + 6, s + offset, n_pixels);
end = p + 2 + 0x2C;
offset += n_pixels;
n_pixels += 6;
p += n_pixels;
p_left -= n_pixels;
/* Pad to data_unit_length 0x2C if necessary. */
while (p < end)
*p++ = 0xFF;
} else {
n_pixels = MIN (s_left, 251u);
n_pixels = MIN (n_pixels, p_left - 6);
/* data_unit_id [8], data_unit_length [8],
first_segment_flag [1], last_segment_flag [1],
field_parity [1], line_offset [5],
first_pixel_position [16], n_pixels [8] */
p[0] = id;
p[1] = n_pixels + 4;
p[2] = line
+ ((s_left == samples_size) << 7)
+ ((s_left == n_pixels) << 6);
p[3] = offset >> 8;
p[4] = offset;
p[5] = n_pixels;
memcpy (p + 6, s + offset, n_pixels);
offset += n_pixels;
n_pixels += 6;
p += n_pixels;
p_left -= n_pixels;
}
s += n_pixels;
s_left -= n_pixels;
}
finish:
*packet = p;
*packet_left = p_left;
*samples = s;
*samples_left = s_left;
}
struct _vbi_dvb_mux {
/* Must hold one PES packet, at most 356 * 184 = 6 + 65498 bytes,
in TS mode additionally one TS header of 4 bytes. */
uint8_t packet[65536];
unsigned int pid;
unsigned int continuity_counter;
unsigned int data_identifier;
unsigned int payload_size;
vbi_videostd_set videostd_set;
vbi_dvb_mux_cb * callback;
void * user_data;
};
void
_vbi_dvb_mux_reset (vbi_dvb_mux * mx)
{
assert (NULL != mx);
/* Nothing to do at this time. */
}
static void
timestamp (uint8_t * p,
int64_t pts,
unsigned int mark)
{
unsigned int t;
p[0] = mark + (unsigned int)((pts >> 29) & 0xE);
t = (unsigned int) pts;
p[1] = t >> 22;
p[2] = (t >> 14) | 1;
p[3] = t >> 7;
p[4] = t * 2 + 1;
}
static vbi_bool
ts_packet_output (vbi_dvb_mux * mx,
const uint8_t * end)
{
uint8_t *p;
unsigned int header1;
/* ISO 13818-1 section 2.4.3.3: payload_unit_start_indicator is
set if exactly one PES packet commences in this TS packet
immediately after the header. */
header1 = (1 << 6) | (mx->pid >> 8);
for (p = mx->packet; p < end; p += 184) {
/* NOTE this overwrites the end of the previous TS packet. */
/* sync_byte [8] = 0x47 */
p[0] = 0x47;
/* transport_error_indicator = 0 (no error),
payload_unit_start_indicator,
transport_priority = 0 (normal), PID[13] */
p[1] = header1;
p[2] = mx->pid;
header1 = (0 << 6) | (mx->pid >> 8);
/* transport_scrambling_control [2] = 0 (not scrambled),
adaptation_field_control [2] = 1 (payload only),
continuity_counter [4] */
p[3] = (1 << 4) + (mx->continuity_counter++ & 15);
mx->callback (mx, mx->user_data, p, 188);
}
return TRUE;
}
/* XXX must be able to encode raw data. Would be nice if we could
write into a client supplied buffer, that might save a copy. */
vbi_bool
_vbi_dvb_mux_feed (vbi_dvb_mux * mx,
int64_t pts,
const vbi_sliced * sliced,
unsigned int sliced_size,
vbi_service_set service_set)
{
uint8_t *p;
unsigned int left;
while (sliced_size > 0) {
if (pts >= 0) {
/* PTS_DTS_flags [2] = 2 (PTS only),
ESCR_flag, ES_rate_flag, DSM_trick_mode_flag,
additional_copy_info_flag,
PES_CRC_flag, PES_extension_flag - no extensions. */
mx->packet[11] = (2 << 6);
timestamp (&mx->packet[13], pts, 0x21);
} else {
mx->packet[11] = 0;
/* Stuffing bytes. */
memset (&mx->packet[13], 0xFF, 36);
}
p = &mx->packet[4 + 45 + 1];
left = mx->payload_size;
while (left > 0) {
_vbi_dvb_multiplex_sliced (&p, &left,
&sliced, &sliced_size,
mx->data_identifier,
service_set);
}
if (mx->pid) {
ts_packet_output (mx, p);
} else {
mx->callback (mx, mx->user_data,
&mx->packet[4],
p - &mx->packet[4]);
}
}
return TRUE;
}
void
_vbi_dvb_mux_delete (vbi_dvb_mux * mx)
{
if (NULL == mx)
return;
CLEAR (*mx);
free (mx);
}
/* XXX we're wasting a lot of space here on stuffing bytes.
packet_size == 0 "minimum" would be nice. */
vbi_dvb_mux *
_vbi_dvb_mux_pes_new (unsigned int data_identifier,
unsigned int packet_size,
vbi_videostd_set videostd_set,
vbi_dvb_mux_cb * callback,
void * user_data)
{
vbi_dvb_mux *mx;
unsigned int packet_length;
assert (NULL != callback);
assert (packet_size > 0);
assert (packet_size < 65535 + 6);
/* EN 300 472 section 4.2: packet_length must be N x 184 - 6
(4 start_code + 2 packet_length). */
assert (0 == (packet_size % 184));
if (!(mx = malloc (sizeof (*mx)))) {
return NULL;
}
/* Bytes 0 ... 3 reserved for TS header. */
/* packet_start_code_prefix [24] = 0x000001,
stream_id [8] = PRIVATE_STREAM_1 */
mx->packet[4 + 0] = 0x00;
mx->packet[4 + 1] = 0x00;
mx->packet[4 + 2] = 0x01;
mx->packet[4 + 3] = PRIVATE_STREAM_1;
packet_length = packet_size - 6;
/* packet_length[16] */
mx->packet[4 + 4] = packet_length >> 8;
mx->packet[4 + 5] = packet_length;
/* '10', PES_scrambling_control [2] == 0 (not scrambled), PES_priority,
data_alignment_indicator = 1 (EN 300 472 section 4.2),
copyright = 0 (undefined), original_or_copy = 0 (copy) */
mx->packet[4 + 6] = (2 << 6) + (1 << 2);
/* PTS_DTS_flags [2] = 0 (neither), ESCR_flag, ES_rate_flag,
DSM_trick_mode_flag, additional_copy_info_flag,
PES_CRC_flag, PES_extension_flag */
mx->packet[4 + 7] = 0;
/* PES_header_data_length [8] = 36 (EN 300 472 section 4.2) */
mx->packet[4 + 8] = 36;
/* Stuffing bytes. */
memset (&mx->packet[4 + 9], 0xFF, 36);
mx->packet[4 + 9 + 36] = data_identifier;
mx->pid = 0;
mx->data_identifier = data_identifier;
mx->payload_size = packet_size - 46;
mx->videostd_set = videostd_set;
mx->callback = callback;
mx->user_data = user_data;
return mx;
}
vbi_dvb_mux *
_vbi_dvb_mux_ts_new (unsigned int pid,
unsigned int data_identifier,
unsigned int packet_size,
vbi_videostd_set videostd_set,
vbi_dvb_mux_cb * callback,
void * user_data)
{
vbi_dvb_mux *mx;
assert (0 != pid);
mx = _vbi_dvb_mux_pes_new (data_identifier,
packet_size,
videostd_set,
callback,
user_data);
if (mx) {
mx->pid = pid & 0x1FFF;
mx->continuity_counter = 0;
}
return mx;
}
syntax highlighted by Code2HTML, v. 0.9.1