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