summaryrefslogtreecommitdiffstats
path: root/gst/mpegtsmux/tsmux/tsmux.c
diff options
context:
space:
mode:
authorSebastian Dröge <slomo@circular-chaos.org>2008-09-01 16:38:40 +0000
committerSebastian Dröge <slomo@circular-chaos.org>2008-09-01 16:38:40 +0000
commit845094c32edb03987f54977837db00e66cb40542 (patch)
tree621dacc29a53c2bf32088cdcf1a54bac57c74ef5 /gst/mpegtsmux/tsmux/tsmux.c
parent122498e169b1225ac74be914e60b7ac614bb83e9 (diff)
downloadgst-plugins-bad-845094c32edb03987f54977837db00e66cb40542.tar.gz
gst-plugins-bad-845094c32edb03987f54977837db00e66cb40542.tar.bz2
gst-plugins-bad-845094c32edb03987f54977837db00e66cb40542.zip
Add Fluendo MPEG-TS muxer and libtsmux to gst-plugins-bad. This is renamed to mpegtsmux to prevent conflicts. Also al...
Original commit message from CVS: * configure.ac: * gst/mpegtsmux/Makefile.am: * gst/mpegtsmux/mpegtsmux.c: (mpegtsmux_base_init), (mpegtsmux_class_init), (mpegtsmux_init), (mpegtsmux_dispose), (gst_mpegtsmux_set_property), (gst_mpegtsmux_get_property), (release_buffer_cb), (mpegtsmux_create_stream), (mpegtsmux_create_streams), (mpegtsmux_choose_best_stream), (mpegtsmux_collected), (mpegtsmux_request_new_pad), (mpegtsmux_release_pad), (new_packet_cb), (mpegtsdemux_prepare_srcpad), (mpegtsmux_change_state), (plugin_init): * gst/mpegtsmux/mpegtsmux.h: * gst/mpegtsmux/mpegtsmux_aac.c: (mpegtsmux_prepare_aac): * gst/mpegtsmux/mpegtsmux_aac.h: * gst/mpegtsmux/mpegtsmux_h264.c: (mpegtsmux_prepare_h264): * gst/mpegtsmux/mpegtsmux_h264.h: * gst/mpegtsmux/tsmux/Makefile.am: * gst/mpegtsmux/tsmux/crc.h: * gst/mpegtsmux/tsmux/tsmux.c: (tsmux_new), (tsmux_set_write_func), (tsmux_set_pat_frequency), (tsmux_get_pat_frequency), (tsmux_free), (tsmux_program_new), (tsmux_set_pmt_frequency), (tsmux_get_pmt_frequency), (tsmux_program_add_stream), (tsmux_program_set_pcr_stream), (tsmux_get_new_pid), (tsmux_create_stream), (tsmux_find_stream), (tsmux_packet_out), (tsmux_write_adaptation_field), (tsmux_write_ts_header), (tsmux_write_stream_packet), (tsmux_program_free), (tsmux_write_section), (tsmux_write_section_hdr), (tsmux_write_pat), (tsmux_write_pmt): * gst/mpegtsmux/tsmux/tsmux.h: * gst/mpegtsmux/tsmux/tsmuxcommon.h: * gst/mpegtsmux/tsmux/tsmuxstream.c: (tsmux_stream_new), (tsmux_stream_get_pid), (tsmux_stream_free), (tsmux_stream_set_buffer_release_func), (tsmux_stream_consume), (tsmux_stream_at_pes_start), (tsmux_stream_bytes_avail), (tsmux_stream_bytes_in_buffer), (tsmux_stream_get_data), (tsmux_stream_pes_header_length), (tsmux_stream_find_pts_dts_within), (tsmux_stream_write_pes_header), (tsmux_stream_add_data), (tsmux_stream_get_es_descrs), (tsmux_stream_pcr_ref), (tsmux_stream_pcr_unref), (tsmux_stream_is_pcr), (tsmux_stream_get_pts): * gst/mpegtsmux/tsmux/tsmuxstream.h: Add Fluendo MPEG-TS muxer and libtsmux to gst-plugins-bad. This is renamed to mpegtsmux to prevent conflicts. Also all relevant informations about copyright and license are added to the top of every file but apart from that no changes compared to the latest SVN versions happened.
Diffstat (limited to 'gst/mpegtsmux/tsmux/tsmux.c')
-rw-r--r--gst/mpegtsmux/tsmux/tsmux.c1056
1 files changed, 1056 insertions, 0 deletions
diff --git a/gst/mpegtsmux/tsmux/tsmux.c b/gst/mpegtsmux/tsmux/tsmux.c
new file mode 100644
index 00000000..0952f699
--- /dev/null
+++ b/gst/mpegtsmux/tsmux/tsmux.c
@@ -0,0 +1,1056 @@
+/*
+ * Copyright 2006 BBC and Fluendo S.A.
+ *
+ * This library is licensed under 4 different licenses and you
+ * can choose to use it under the terms of any one of them. The
+ * four licenses are the MPL 1.1, the LGPL, the GPL and the MIT
+ * license.
+ *
+ * MPL:
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * LGPL:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * GPL:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * MIT:
+ *
+ * Unless otherwise indicated, Source Code is licensed under MIT license.
+ * See further explanation attached in License Statement (distributed in the file
+ * LICENSE).
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "tsmux.h"
+#include "tsmuxstream.h"
+#include "crc.h"
+
+/* Maximum total data length for a PAT section is 1024 bytes, minus an
+ * 8 byte header, then the length of each program entry is 32 bits,
+ * then finally a 32 bit CRC. Thus the maximum number of programs in this mux
+ * is (1024 - 8 - 4) / 4 = 253 because it only supports single section PATs */
+#define TSMUX_MAX_PROGRAMS 253
+
+#define TSMUX_SECTION_HDR_SIZE 8
+
+#define TSMUX_DEFAULT_NETWORK_ID 0x0001
+#define TSMUX_DEFAULT_TS_ID 0x0001
+
+#define TSMUX_START_PROGRAM_ID 0x0001
+#define TSMUX_START_PMT_PID 0x0010
+#define TSMUX_START_ES_PID 0x0040
+
+/* HACK: We use a fixed buffering offset for the PCR at the moment -
+ * this is the amount 'in advance' of the stream that the PCR sits.
+ * 1/8 second atm */
+#define TSMUX_PCR_OFFSET (TSMUX_CLOCK_FREQ / 8)
+
+/* Times per second to write PCR */
+#define TSMUX_DEFAULT_PCR_FREQ (25)
+
+/* PAT frequency (1/10th sec) */
+#define TSMUX_DEFAULT_PAT_FREQ (TSMUX_CLOCK_FREQ / 10)
+/* PMT frequency (1/10th sec) */
+#define TSMUX_DEFAULT_PMT_FREQ (TSMUX_CLOCK_FREQ / 10)
+
+static gboolean tsmux_write_pat (TsMux * mux);
+static gboolean tsmux_write_pmt (TsMux * mux, TsMuxProgram * program);
+
+/**
+ * tsmux_new:
+ *
+ * Create a new muxer session.
+ *
+ * Returns: A new #TsMux object.
+ */
+TsMux *
+tsmux_new ()
+{
+ TsMux *mux;
+
+ mux = g_new0 (TsMux, 1);
+
+ mux->transport_id = TSMUX_DEFAULT_TS_ID;
+
+ mux->next_pgm_no = TSMUX_START_PROGRAM_ID;
+ mux->next_pmt_pid = TSMUX_START_PMT_PID;
+ mux->next_stream_pid = TSMUX_START_ES_PID;
+
+ mux->pat_changed = TRUE;
+ mux->last_pat_ts = -1;
+ mux->pat_frequency = TSMUX_DEFAULT_PAT_FREQ;
+
+ return mux;
+}
+
+/**
+ * tsmux_set_write_func:
+ * @mux: a #TsMux
+ * @func: a user callback function
+ * @user_data: user data passed to @func
+ *
+ * Set the callback function and user data to be called when @mux has output to
+ * produce. @user_data will be passed as user data in @func.
+ */
+void
+tsmux_set_write_func (TsMux * mux, TsMuxWriteFunc func, void *user_data)
+{
+ g_return_if_fail (mux != NULL);
+
+ mux->write_func = func;
+ mux->write_func_data = user_data;
+}
+
+/**
+ * tsmux_set_pat_frequency:
+ * @mux: a #TsMux
+ * @freq: a new PAT frequency
+ *
+ * Set the frequency (against the 90Hz clock) for writing out the PAT table.
+ * A frequency of 0 will only write the PAT table when it changes.
+ *
+ * Many transport stream clients might have problems if the PAT table is not
+ * inserted in the stream at regular intervals, especially when initially trying
+ * to figure out the contents of the stream.
+ */
+void
+tsmux_set_pat_frequency (TsMux * mux, guint freq)
+{
+ g_return_if_fail (mux != NULL);
+
+ mux->pat_frequency = freq;
+}
+
+/**
+ * tsmux_get_pat_frequency:
+ * @mux: a #TsMux
+ *
+ * Get the configured PAT frequency. See also tsmux_set_pat_frequency().
+ *
+ * Returns: the configured PAT frequency
+ */
+guint
+tsmux_get_pat_frequency (TsMux * mux)
+{
+ g_return_val_if_fail (mux != NULL, 0);
+
+ return mux->pat_frequency;
+}
+
+/**
+ * tsmux_free:
+ * @mux: a #TsMux
+ *
+ * Free all resources associated with @mux. After calling this function @mux can
+ * not be used anymore.
+ */
+void
+tsmux_free (TsMux * mux)
+{
+ GList *cur;
+
+ g_return_if_fail (mux != NULL);
+
+ /* Free all programs */
+ for (cur = g_list_first (mux->programs); cur != NULL; cur = g_list_next (cur)) {
+ TsMuxProgram *program = (TsMuxProgram *) cur->data;
+
+ tsmux_program_free (program);
+ }
+ g_list_free (mux->programs);
+
+ /* Free all streams */
+ for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
+ TsMuxStream *stream = (TsMuxStream *) cur->data;
+
+ tsmux_stream_free (stream);
+ }
+ g_list_free (mux->streams);
+
+ g_free (mux);
+}
+
+/**
+ * tsmux_program_new:
+ * @mux: a #TsMux
+ *
+ * Create a new program in the mising session @mux.
+ *
+ * Returns: a new #TsMuxProgram or %NULL when the maximum number of programs has
+ * been reached.
+ */
+TsMuxProgram *
+tsmux_program_new (TsMux * mux)
+{
+ TsMuxProgram *program;
+
+ g_return_val_if_fail (mux != NULL, NULL);
+
+ /* Ensure we have room for another program */
+ if (mux->nb_programs == TSMUX_MAX_PROGRAMS)
+ return NULL;
+
+ program = g_new0 (TsMuxProgram, 1);
+
+ program->pmt_changed = TRUE;
+ program->last_pmt_ts = -1;
+ program->pmt_frequency = TSMUX_DEFAULT_PMT_FREQ;
+
+ program->pgm_number = mux->next_pgm_no++;
+ program->pmt_pid = mux->next_pmt_pid++;
+ program->pcr_stream = NULL;
+ program->last_pcr = -1;
+
+ program->streams = g_array_sized_new (FALSE, TRUE, sizeof (TsMuxStream *), 1);
+
+ mux->programs = g_list_prepend (mux->programs, program);
+ mux->nb_programs++;
+ mux->pat_changed = TRUE;
+
+ return program;
+}
+
+/**
+ * tsmux_set_pmt_frequency:
+ * @program: a #TsMuxProgram
+ * @freq: a new PMT frequency
+ *
+ * Set the frequency (against the 90Hz clock) for writing out the PMT table.
+ * A frequency of 0 will only write the PMT table when it changes.
+ *
+ * Many transport stream clients might have problems if the PMT table is not
+ * inserted in the stream at regular intervals, especially when initially trying
+ * to figure out the contents of the stream.
+ */
+void
+tsmux_set_pmt_frequency (TsMuxProgram * program, guint freq)
+{
+ g_return_if_fail (program != NULL);
+
+ program->pmt_frequency = freq;
+}
+
+/**
+ * tsmux_get_pmt_frequency:
+ * @program: a #TsMuxProgram
+ *
+ * Get the configured PMT frequency. See also tsmux_set_pmt_frequency().
+ *
+ * Returns: the configured PMT frequency
+ */
+guint
+tsmux_get_pmt_frequency (TsMuxProgram * program)
+{
+ g_return_val_if_fail (program != NULL, 0);
+
+ return program->pmt_frequency;
+}
+
+/**
+ * tsmux_program_add_stream:
+ * @program: a #TsMuxProgram
+ * @stream: a #TsMuxStream
+ *
+ * Add @stream to @program.
+ */
+void
+tsmux_program_add_stream (TsMuxProgram * program, TsMuxStream * stream)
+{
+ g_return_if_fail (program != NULL);
+ g_return_if_fail (stream != NULL);
+
+ program->nb_streams++;
+ g_array_append_val (program->streams, stream);
+ program->pmt_changed = TRUE;
+}
+
+/**
+ * tsmux_program_set_pcr_stream:
+ * @program: a #TsMuxProgram
+ * @stream: a #TsMuxStream
+ *
+ * Set @stream as the PCR stream for @program, overwriting the previously
+ * configured PCR stream. When @stream is NULL, program will have no PCR stream
+ * configured.
+ */
+void
+tsmux_program_set_pcr_stream (TsMuxProgram * program, TsMuxStream * stream)
+{
+ g_return_if_fail (program != NULL);
+
+ if (program->pcr_stream == stream)
+ return;
+
+ if (program->pcr_stream != NULL)
+ tsmux_stream_pcr_unref (program->pcr_stream);
+ if (stream)
+ tsmux_stream_pcr_ref (stream);
+ program->pcr_stream = stream;
+
+ program->pmt_changed = TRUE;
+}
+
+/**
+ * tsmux_get_new_pid:
+ * @mux: a #TsMux
+ *
+ * Get a new free PID.
+ *
+ * Returns: a new free PID.
+ */
+guint16
+tsmux_get_new_pid (TsMux * mux)
+{
+ g_return_val_if_fail (mux != NULL, -1);
+
+ /* FIXME: It's possible that this next PID is already taken if a
+ * specific PID was requested earlier. We should find a free PID */
+ return mux->next_stream_pid++;
+}
+
+/**
+ * tsmux_create_stream:
+ * @mux: a #TsMux
+ * @stream_type: a #TsMuxStreamType
+ * @pid: the PID of the new stream.
+ *
+ * Create a new stream of @stream_type in the muxer session @mux.
+ *
+ * When @pid is set to #TSMUX_PID_AUTO, a new free PID will automatically
+ * be allocated for the new stream.
+ *
+ * Returns: a new #TsMuxStream.
+ */
+TsMuxStream *
+tsmux_create_stream (TsMux * mux, TsMuxStreamType stream_type, guint16 pid)
+{
+ TsMuxStream *stream;
+ guint16 new_pid;
+
+ g_return_val_if_fail (mux != NULL, NULL);
+
+ if (pid == TSMUX_PID_AUTO) {
+ new_pid = tsmux_get_new_pid (mux);
+ } else {
+ new_pid = pid & 0x1FFF;
+ }
+
+ /* Ensure we're not creating a PID collision */
+ if (tsmux_find_stream (mux, new_pid))
+ return NULL;
+
+ stream = tsmux_stream_new (new_pid, stream_type);
+
+ mux->streams = g_list_prepend (mux->streams, stream);
+ mux->nb_streams++;
+
+ return stream;
+}
+
+/**
+ * tsmux_find_stream:
+ * @mux: a #TsMux
+ * @pid: the PID to find.
+ *
+ * Find the stream associated wih PID.
+ *
+ * Returns: a #TsMuxStream with @pid or NULL when the stream was not found.
+ */
+TsMuxStream *
+tsmux_find_stream (TsMux * mux, guint16 pid)
+{
+ TsMuxStream *found = NULL;
+ GList *cur;
+
+ g_return_val_if_fail (mux != NULL, NULL);
+
+ for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
+ TsMuxStream *stream = (TsMuxStream *) cur->data;
+
+ if (tsmux_stream_get_pid (stream) == pid) {
+ found = stream;
+ break;
+ }
+ }
+ return found;
+}
+
+static gboolean
+tsmux_packet_out (TsMux * mux)
+{
+ if (G_UNLIKELY (mux->write_func == NULL))
+ return TRUE;
+
+ return mux->write_func (mux->packet_buf, TSMUX_PACKET_LENGTH,
+ mux->write_func_data, mux->new_pcr);
+}
+
+/*
+ * adaptation_field() {
+ * adaptation_field_length 8 uimsbf
+ * if(adaptation_field_length >0) {
+ * discontinuity_indicator 1 bslbf
+ * random_access_indicator 1 bslbf
+ * elementary_stream_priority_indicator 1 bslbf
+ * PCR_flag 1 bslbf
+ * OPCR_flag 1 bslbf
+ * splicing_point_flag 1 bslbf
+ * transport_private_data_flag 1 bslbf
+ * adaptation_field_extension_flag 1 bslbf
+ * if(PCR_flag == '1') {
+ * program_clock_reference_base 33 uimsbf
+ * reserved 6 bslbf
+ * program_clock_reference_extension 9 uimsbf
+ * }
+ * if(OPCR_flag == '1') {
+ * original_program_clock_reference_base 33 uimsbf
+ * reserved 6 bslbf
+ * original_program_clock_reference_extension 9 uimsbf
+ * }
+ * if (splicing_point_flag == '1') {
+ * splice_countdown 8 tcimsbf
+ * }
+ * if(transport_private_data_flag == '1') {
+ * transport_private_data_length 8 uimsbf
+ * for (i=0; i<transport_private_data_length;i++){
+ * private_data_byte 8 bslbf
+ * }
+ * }
+ * if (adaptation_field_extension_flag == '1' ) {
+ * adaptation_field_extension_length 8 uimsbf
+ * ltw_flag 1 bslbf
+ * piecewise_rate_flag 1 bslbf
+ * seamless_splice_flag 1 bslbf
+ * reserved 5 bslbf
+ * if (ltw_flag == '1') {
+ * ltw_valid_flag 1 bslbf
+ * ltw_offset 15 uimsbf
+ * }
+ * if (piecewise_rate_flag == '1') {
+ * reserved 2 bslbf
+ * piecewise_rate 22 uimsbf
+ * }
+ * if (seamless_splice_flag == '1'){
+ * splice_type 4 bslbf
+ * DTS_next_AU[32..30] 3 bslbf
+ * marker_bit 1 bslbf
+ * DTS_next_AU[29..15] 15 bslbf
+ * marker_bit 1 bslbf
+ * DTS_next_AU[14..0] 15 bslbf
+ * marker_bit 1 bslbf
+ * }
+ * for ( i=0;i<N;i++) {
+ * reserved 8 bslbf
+ * }
+ * }
+ * for (i=0;i<N;i++){
+ * stuffing_byte 8 bslbf
+ * }
+ * }
+ * }
+ */
+static gboolean
+tsmux_write_adaptation_field (guint8 * buf,
+ TsMuxPacketInfo * pi, guint8 min_length, guint8 * written)
+{
+ guint8 pos = 2;
+ guint8 flags = 0;
+
+ g_assert (min_length <= TSMUX_PAYLOAD_LENGTH);
+
+ /* Write out all the fields from the packet info only if the
+ * user set the flag to request the adaptation field - if the flag
+ * isn't set, we're just supposed to write stuffing bytes */
+ if (pi->flags & TSMUX_PACKET_FLAG_ADAPTATION) {
+ TS_DEBUG ("writing adaptation fields");
+ if (pi->flags & TSMUX_PACKET_FLAG_DISCONT)
+ flags |= 0x80;
+ if (pi->flags & TSMUX_PACKET_FLAG_RANDOM_ACCESS)
+ flags |= 0x40;
+ if (pi->flags & TSMUX_PACKET_FLAG_PRIORITY)
+ flags |= 0x20;
+ if (pi->flags & TSMUX_PACKET_FLAG_WRITE_PCR) {
+ guint64 pcr_base;
+ guint32 pcr_ext;
+
+ pcr_base = (pi->pcr / 300);
+ pcr_ext = (pi->pcr % 300);
+
+ flags |= 0x10;
+ TS_DEBUG ("Writing PCR %" G_GUINT64_FORMAT " + ext %u", pcr_base,
+ pcr_ext);
+ buf[pos++] = (pcr_base >> 25) & 0xff;
+ buf[pos++] = (pcr_base >> 17) & 0xff;
+ buf[pos++] = (pcr_base >> 9) & 0xff;
+ buf[pos++] = (pcr_base >> 1) & 0xff;
+ buf[pos++] = ((pcr_base << 7) & 0x80) | ((pcr_ext >> 8) & 0x01);
+ buf[pos++] = (pcr_ext) & 0xff;
+ }
+ if (pi->flags & TSMUX_PACKET_FLAG_WRITE_OPCR) {
+ guint64 opcr_base;
+ guint32 opcr_ext;
+
+ opcr_base = (pi->opcr / 300);
+ opcr_ext = (pi->opcr % 300);
+
+ flags |= 0x08;
+ TS_DEBUG ("Writing OPCR");
+ buf[pos++] = (opcr_base >> 25) & 0xff;
+ buf[pos++] = (opcr_base >> 17) & 0xff;
+ buf[pos++] = (opcr_base >> 9) & 0xff;
+ buf[pos++] = (opcr_base >> 1) & 0xff;
+ buf[pos++] = ((opcr_base << 7) & 0x80) | ((opcr_ext >> 8) & 0x01);
+ buf[pos++] = (opcr_ext) & 0xff;
+ }
+ if (pi->flags & TSMUX_PACKET_FLAG_WRITE_SPLICE) {
+ flags |= 0x04;
+ buf[pos++] = pi->splice_countdown;
+ }
+ if (pi->private_data_len > 0) {
+ flags |= 0x02;
+ /* Private data to write, ensure we have enough room */
+ if ((1 + pi->private_data_len) > (TSMUX_PAYLOAD_LENGTH - pos))
+ return FALSE;
+ buf[pos++] = pi->private_data_len;
+ memcpy (&(buf[pos]), pi->private_data, pi->private_data_len);
+ pos += pi->private_data_len;
+ TS_DEBUG ("%u bytes of private data", pi->private_data_len);
+ }
+ if (pi->flags & TSMUX_PACKET_FLAG_WRITE_ADAPT_EXT) {
+ flags |= 0x01;
+ TS_DEBUG ("FIXME: write Adaptation extension");
+ /* Write an empty extension for now */
+ buf[pos++] = 1;
+ buf[pos++] = 0;
+ }
+ }
+ /* Write the flags at the start */
+ buf[1] = flags;
+
+ /* Stuffing bytes if needed */
+ while (pos < min_length)
+ buf[pos++] = 0xff;
+
+ /* Write the adaptation field length, which doesn't include its own byte */
+ buf[0] = pos - 1;
+
+ if (written)
+ *written = pos;
+
+ return TRUE;
+}
+
+static gboolean
+tsmux_write_ts_header (guint8 * buf, TsMuxPacketInfo * pi,
+ guint * payload_len_out, guint * payload_offset_out)
+{
+ guint8 *tmp;
+ guint8 adaptation_flag;
+ guint8 adapt_min_length = 0;
+ guint8 adapt_len;
+ guint payload_len;
+ gboolean write_adapt = FALSE;
+
+ /* Sync byte */
+ buf[0] = TSMUX_SYNC_BYTE;
+
+ TS_DEBUG ("PID 0x%04x, counter = 0x%01x, %u bytes avail", pi->pid,
+ pi->packet_count & 0x0f, pi->stream_avail);
+
+ /* 3 bits:
+ * transport_error_indicator
+ * payload_unit_start_indicator
+ * transport_priority: (00)
+ * 13 bits: PID
+ */
+ tmp = buf + 1;
+ if (pi->packet_start_unit_indicator) {
+ tsmux_put16 (&tmp, 0x4000 | pi->pid);
+ } else
+ tsmux_put16 (&tmp, pi->pid);
+
+ /* 2 bits: scrambling_control (NOT SUPPORTED) (00)
+ * 2 bits: adaptation field control (1x has_adaptation_field | x1 has_payload)
+ * 4 bits: continuity counter (xxxx)
+ */
+ adaptation_flag = pi->packet_count & 0x0f;
+
+ if (pi->flags & TSMUX_PACKET_FLAG_ADAPTATION) {
+ write_adapt = TRUE;
+ }
+
+ if (pi->stream_avail < TSMUX_PAYLOAD_LENGTH) {
+ /* Need an adaptation field regardless for stuffing */
+ adapt_min_length = TSMUX_PAYLOAD_LENGTH - pi->stream_avail;
+ write_adapt = TRUE;
+ }
+
+ if (write_adapt) {
+ gboolean res;
+
+ /* Flag the adaptation field presence */
+ adaptation_flag |= 0x20;
+ res = tsmux_write_adaptation_field (buf + TSMUX_HEADER_LENGTH,
+ pi, adapt_min_length, &adapt_len);
+ if (G_UNLIKELY (res == FALSE))
+ return FALSE;
+
+ /* Should have written at least the number of bytes we requested */
+ g_assert (adapt_len >= adapt_min_length);
+ } else {
+ adapt_len = 0;
+ }
+
+ /* The amount of packet data we wrote is the remaining space after
+ * the adaptation field */
+ *payload_len_out = payload_len = TSMUX_PAYLOAD_LENGTH - adapt_len;
+ *payload_offset_out = TSMUX_HEADER_LENGTH + adapt_len;
+
+ /* Now if we are going to write out some payload, flag that fact */
+ if (payload_len > 0 && pi->stream_avail > 0) {
+ /* Flag the presence of a payload */
+ adaptation_flag |= 0x10;
+
+ /* We must have enough data to fill the payload, or some calculation
+ * went wrong */
+ g_assert (payload_len <= pi->stream_avail);
+
+ /* Packet with payload, increment the continuity counter */
+ pi->packet_count++;
+ }
+
+ /* Write the byte of transport_scrambling_control, adaptation_field_control
+ * + continuity counter out */
+ buf[3] = adaptation_flag;
+
+
+ if (write_adapt) {
+ TS_DEBUG ("Adaptation field of size >= %d + %d bytes payload",
+ adapt_len, payload_len);
+ } else {
+ TS_DEBUG ("Payload of %d bytes only", payload_len);
+ }
+
+ return TRUE;
+}
+
+/**
+ * tsmux_write_stream_packet:
+ * @mux: a #TsMux
+ * @stream: a #TsMuxStream
+ *
+ * Write a packet of @stream.
+ *
+ * Returns: TRUE if the packet could be written.
+ */
+gboolean
+tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
+{
+ guint payload_len, payload_offs;
+ TsMuxPacketInfo *pi = &stream->pi;
+ gboolean res;
+
+
+ mux->new_pcr = -1;
+ g_return_val_if_fail (mux != NULL, FALSE);
+ g_return_val_if_fail (stream != NULL, FALSE);
+
+ if (tsmux_stream_is_pcr (stream)) {
+ gint64 cur_pcr = 0;
+ gint64 cur_pts = tsmux_stream_get_pts (stream);
+ gboolean write_pat;
+ GList *cur;
+
+ if (cur_pts != -1) {
+ TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_pts);
+ }
+
+ /* FIXME: The current PCR needs more careful calculation than just
+ * writing a fixed offset */
+ if (cur_pts != -1 && (cur_pts >= TSMUX_PCR_OFFSET))
+ cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) *
+ (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
+
+ /* Need to decide whether to write a new PCR in this packet */
+ if (stream->last_pcr == -1 ||
+ (cur_pcr - stream->last_pcr >
+ (TSMUX_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ))) {
+
+ stream->pi.flags |=
+ TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
+ stream->pi.pcr = cur_pcr;
+ stream->last_pcr = cur_pcr;
+ mux->new_pcr = cur_pcr;
+ }
+
+ /* check if we need to rewrite pat */
+ if (mux->last_pat_ts == -1 || mux->pat_changed)
+ write_pat = TRUE;
+ else if (cur_pcr >= mux->last_pat_ts + mux->pat_frequency)
+ write_pat = TRUE;
+ else
+ write_pat = FALSE;
+
+ if (write_pat) {
+ mux->last_pat_ts = cur_pcr;
+ if (!tsmux_write_pat (mux))
+ return FALSE;
+ }
+
+ /* check if we need to rewrite any of the current pmts */
+ for (cur = g_list_first (mux->programs); cur != NULL;
+ cur = g_list_next (cur)) {
+ TsMuxProgram *program = (TsMuxProgram *) cur->data;
+ gboolean write_pmt;
+
+ if (program->last_pmt_ts == -1 || program->pmt_changed)
+ write_pmt = TRUE;
+ else if (cur_pcr >= program->last_pmt_ts + program->pmt_frequency)
+ write_pmt = TRUE;
+ else
+ write_pmt = FALSE;
+
+ if (write_pmt) {
+ program->last_pmt_ts = cur_pcr;
+ if (!tsmux_write_pmt (mux, program))
+ return FALSE;
+ }
+ }
+ }
+
+ pi->stream_avail = tsmux_stream_bytes_avail (stream);
+ pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
+
+ if (!tsmux_write_ts_header (mux->packet_buf, pi, &payload_len, &payload_offs))
+ return FALSE;
+
+ if (!tsmux_stream_get_data (stream, mux->packet_buf + payload_offs,
+ payload_len))
+ return FALSE;
+
+ res = tsmux_packet_out (mux);
+
+ /* Reset all dynamic flags */
+ stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
+
+ return res;
+}
+
+/**
+ * tsmux_program_free:
+ * @program: a #TsMuxProgram
+ *
+ * Free the resources of @program. After this call @program can not be used
+ * anymore.
+ */
+void
+tsmux_program_free (TsMuxProgram * program)
+{
+ g_return_if_fail (program != NULL);
+
+ g_array_free (program->streams, TRUE);
+ g_free (program);
+}
+
+static gboolean
+tsmux_write_section (TsMux * mux, TsMuxSection * section)
+{
+ guint8 *cur_in;
+ guint payload_remain;
+ guint payload_len, payload_offs;
+ TsMuxPacketInfo *pi;
+
+ pi = &section->pi;
+
+ pi->packet_start_unit_indicator = TRUE;
+
+ cur_in = section->data;
+ payload_remain = pi->stream_avail;
+
+ while (payload_remain > 0) {
+ if (pi->packet_start_unit_indicator) {
+ /* Need to write an extra single byte start pointer */
+ pi->stream_avail++;
+
+ if (!tsmux_write_ts_header (mux->packet_buf, pi,
+ &payload_len, &payload_offs)) {
+ pi->stream_avail--;
+ return FALSE;
+ }
+ pi->stream_avail--;
+
+ /* Write the pointer byte */
+ mux->packet_buf[payload_offs] = 0x00;
+
+ payload_offs++;
+ payload_len--;
+ pi->packet_start_unit_indicator = FALSE;
+ } else {
+ if (!tsmux_write_ts_header (mux->packet_buf, pi,
+ &payload_len, &payload_offs))
+ return FALSE;
+ }
+
+ TS_DEBUG ("Outputting %d bytes to section. %d remaining after",
+ payload_len, payload_remain - payload_len);
+
+ memcpy (mux->packet_buf + payload_offs, cur_in, payload_len);
+
+ cur_in += payload_len;
+ payload_remain -= payload_len;
+
+ if (G_UNLIKELY (!tsmux_packet_out (mux))) {
+ mux->new_pcr = -1;
+ return FALSE;
+ }
+ mux->new_pcr = -1;
+ }
+
+ return TRUE;
+}
+
+static void
+tsmux_write_section_hdr (guint8 * pos, guint8 table_id, guint16 len,
+ guint16 id, guint8 version, guint8 section_nr, guint8 last_section_nr)
+{
+ /* The length passed is the total length of the section, but we're not
+ * supposed to include the first 3 bytes of the header in the count */
+ len -= 3;
+
+ /* 1 byte table identifier */
+ *pos++ = table_id;
+ /* section_syntax_indicator = '0' | '0' | '11' reserved bits | (len >> 8) */
+ tsmux_put16 (&pos, 0xB000 | len);
+ /* 2 bytes transport/program id */
+ tsmux_put16 (&pos, id);
+
+ /* '11' reserved | version 'xxxxxx' | 'x' current_next */
+ *pos++ = 0xC0 | ((version & 0x1F) << 1) | 0x01;
+ *pos++ = section_nr;
+ *pos++ = last_section_nr;
+}
+
+static gboolean
+tsmux_write_pat (TsMux * mux)
+{
+ GList *cur;
+ TsMuxSection *pat = &mux->pat;
+
+ if (mux->pat_changed) {
+ /* program_association_section ()
+ * table_id 8 uimsbf
+ * section_syntax_indicator 1 bslbf
+ * '0' 1 bslbf
+ * reserved 2 bslbf
+ * section_length 12 uimsbf
+ * transport_stream_id 16 uimsbf
+ * reserved 2 bslbf
+ * version_number 5 uimsbf
+ * current_next_indicator 1 bslbf
+ * section_number 8 uimsbf
+ * last_section_number 8 uimsbf
+ * for (i = 0; i < N; i++) {
+ * program_number 16 uimsbf
+ * reserved 3 bslbf
+ * network_PID_or_program_map_PID 13 uimbsf
+ * }
+ * CRC_32 32 rbchof
+ */
+ guint8 *pos;
+ guint32 crc;
+
+ /* Prepare the section data after the section header */
+ pos = pat->data + TSMUX_SECTION_HDR_SIZE;
+
+ for (cur = g_list_first (mux->programs); cur != NULL;
+ cur = g_list_next (cur)) {
+ TsMuxProgram *program = (TsMuxProgram *) cur->data;
+
+ tsmux_put16 (&pos, program->pgm_number);
+ tsmux_put16 (&pos, 0xE000 | program->pmt_pid);
+ }
+
+ /* Measure the section length, include extra 4 bytes for CRC below */
+ pat->pi.stream_avail = pos - pat->data + 4;
+
+ /* Go back and write the header now that we know the final length.
+ * table_id = 0 for PAT */
+ tsmux_write_section_hdr (pat->data, 0x00, pat->pi.stream_avail,
+ mux->transport_id, mux->pat_version, 0, 0);
+
+ /* Calc and output CRC for data bytes, not including itself */
+ crc = calc_crc32 (pat->data, pat->pi.stream_avail - 4);
+ tsmux_put32 (&pos, crc);
+
+ TS_DEBUG ("PAT has %d programs, is %u bytes",
+ mux->nb_programs, pat->pi.stream_avail);
+ mux->pat_changed = FALSE;
+ mux->pat_version++;
+ }
+
+ return tsmux_write_section (mux, pat);
+}
+
+static gboolean
+tsmux_write_pmt (TsMux * mux, TsMuxProgram * program)
+{
+ TsMuxSection *pmt = &program->pmt;
+
+ if (program->pmt_changed) {
+ /* program_association_section ()
+ * table_id 8 uimsbf
+ * section_syntax_indicator 1 bslbf
+ * '0' 1 bslbf
+ * reserved 2 bslbf
+ * section_length 12 uimsbf
+ * program_id 16 uimsbf
+ * reserved 2 bslbf
+ * version_number 5 uimsbf
+ * current_next_indicator 1 bslbf
+ * section_number 8 uimsbf
+ * last_section_number 8 uimsbf
+ * reserved 3 bslbf
+ * PCR_PID 13 uimsbf
+ * reserved 4 bslbf
+ * program_info_length 12 uimsbf
+ * for (i = 0; i < N; i++)
+ * descriptor ()
+ *
+ * for (i = 0; i < N1; i++) {
+ * stream_type 8 uimsbf
+ * reserved 3 bslbf
+ * elementary_PID 13 uimbsf
+ * reserved 4 bslbf
+ * ES_info_length 12 uimbsf
+ * for (i = 0; i < N1; i++) {
+ * descriptor ();
+ * }
+ * }
+ * CRC_32 32 rbchof
+ */
+ guint8 *pos;
+ guint32 crc;
+ guint i;
+
+ /* Prepare the section data after the basic section header */
+ pos = pmt->data + TSMUX_SECTION_HDR_SIZE;
+
+ if (program->pcr_stream == NULL)
+ tsmux_put16 (&pos, 0xFFFF);
+ else
+ tsmux_put16 (&pos, 0xE000 | tsmux_stream_get_pid (program->pcr_stream));
+
+ /* FIXME: Write program descriptors if needed, for now write a
+ * length of 0 */
+ tsmux_put16 (&pos, 0xF000);
+
+ /* Write out the entries */
+ for (i = 0; i < program->nb_streams; i++) {
+ TsMuxStream *stream = g_array_index (program->streams, TsMuxStream *, i);
+ guint16 es_info_len;
+
+ /* FIXME: Use API to retrieve this from the stream */
+ *pos++ = stream->stream_type;
+ tsmux_put16 (&pos, 0xE000 | tsmux_stream_get_pid (stream));
+
+ /* Write any ES descriptors needed */
+ tsmux_stream_get_es_descrs (stream, mux->es_info_buf, &es_info_len);
+ tsmux_put16 (&pos, 0xF000 | es_info_len);
+
+ if (es_info_len > 0) {
+ TS_DEBUG ("Writing descriptor of len %d for PID 0x%04x",
+ es_info_len, tsmux_stream_get_pid (stream));
+ if (G_UNLIKELY (pos + es_info_len >=
+ pmt->data + TSMUX_MAX_SECTION_LENGTH))
+ return FALSE;
+
+ memcpy (pos, mux->es_info_buf, es_info_len);
+ pos += es_info_len;
+ }
+ }
+
+ /* Include the CRC in the byte count */
+ pmt->pi.stream_avail = pos - pmt->data + 4;
+
+ /* Go back and patch the pmt_header now that we know the length.
+ * table_id = 2 for PMT */
+ tsmux_write_section_hdr (pmt->data, 0x02, pmt->pi.stream_avail,
+ program->pgm_number, program->pmt_version, 0, 0);
+
+ /* Calc and output CRC for data bytes,
+ * but not counting the CRC bytes this time */
+ crc = calc_crc32 (pmt->data, pmt->pi.stream_avail - 4);
+ tsmux_put32 (&pos, crc);
+
+ TS_DEBUG ("PMT for program %d has %d streams, is %u bytes",
+ program->pgm_number, program->nb_streams, pmt->pi.stream_avail);
+
+ pmt->pi.pid = program->pmt_pid;
+ program->pmt_changed = FALSE;
+ program->pmt_version++;
+ }
+
+ return tsmux_write_section (mux, pmt);
+}