diff --git a/ChangeLog b/ChangeLog index 390fa6025f..9ef881e4e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,53 @@ +2008-09-01 Sebastian Dröge + + * 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. + 2008-09-01 Wim Taymans * gst/selector/gstinputselector.c: (gst_input_selector_init), diff --git a/configure.ac b/configure.ac index d2192df223..c741e34f11 100644 --- a/configure.ac +++ b/configure.ac @@ -257,6 +257,7 @@ AG_GST_CHECK_PLUGIN(h264parse) AG_GST_CHECK_PLUGIN(librfb) AG_GST_CHECK_PLUGIN(modplug) AG_GST_CHECK_PLUGIN(mpegtsparse) +AG_GST_CHECK_PLUGIN(mpegtsmux) AG_GST_CHECK_PLUGIN(mpeg4videoparse) AG_GST_CHECK_PLUGIN(mpegvideoparse) AG_GST_CHECK_PLUGIN(mve) @@ -1399,6 +1400,8 @@ gst/librfb/Makefile gst/modplug/Makefile gst/modplug/libmodplug/Makefile gst/mpegtsparse/Makefile +gst/mpegtsmux/Makefile +gst/mpegtsmux/tsmux/Makefile gst/mpeg4videoparse/Makefile gst/mpegvideoparse/Makefile gst/mve/Makefile diff --git a/gst/mpegtsmux/Makefile.am b/gst/mpegtsmux/Makefile.am new file mode 100644 index 0000000000..a1d761392e --- /dev/null +++ b/gst/mpegtsmux/Makefile.am @@ -0,0 +1,17 @@ +plugin_LTLIBRARIES = libgstmpegtsmux.la + +SUBDIRS = tsmux + +libgstmpegtsmux_la_SOURCES = \ + mpegtsmux.c \ + mpegtsmux_h264.c \ + mpegtsmux_aac.c + +libgstmpegtsmux_la_CFLAGS = $(GST_CFLAGS) +libgstmpegtsmux_la_LIBADD = $(top_builddir)/gst/mpegtsmux/tsmux/libtsmux.la $(GST_LIBS) $(GST_BASE_LIBS) +libgstmpegtsmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = \ + mpegtsmux.h \ + mpegtsmux_h264.h \ + mpegtsmux_aac.h diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c new file mode 100644 index 0000000000..8013b51ad0 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux.c @@ -0,0 +1,821 @@ +/* + * Copyright 2006, 2007, 2008 Fluendo S.A. + * Authors: Jan Schmidt + * Kapil Agrawal + * Julien Moutte + * + * 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 + +#include "mpegtsmux.h" + +#include "mpegtsmux_h264.h" +#include "mpegtsmux_aac.h" + +GST_DEBUG_CATEGORY (mpegtsmux_debug); +#define GST_CAT_DEFAULT mpegtsmux_debug + +enum +{ + ARG_0, + ARG_M2TS_MODE +}; + +static GstStaticPadTemplate mpegtsmux_sink_factory = + GST_STATIC_PAD_TEMPLATE ("sink_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("video/mpeg, mpegversion=(int) { 1, 2 }, " + "systemstream = (boolean) false; " + "video/x-dirac;" + "video/x-h264;" "audio/mpeg, mpegversion = (int) { 1, 2, 4 }") + ); + +static GstStaticPadTemplate mpegtsmux_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpegts, " + "systemstream = (boolean) true, " "packetsize = (int) { 188, 192} ") + ); + +static void gst_mpegtsmux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_mpegtsmux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void mpegtsmux_dispose (GObject * object); +static gboolean new_packet_cb (guint8 * data, guint len, void *user_data, + gint64 new_pcr); +static void release_buffer_cb (guint8 * data, void *user_data); + +static gboolean mpegtsdemux_prepare_srcpad (MpegTsMux * mux); +static GstFlowReturn mpegtsmux_collected (GstCollectPads * pads, + MpegTsMux * mux); +static GstPad *mpegtsmux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void mpegtsmux_release_pad (GstElement * element, GstPad * pad); +static GstStateChangeReturn mpegtsmux_change_state (GstElement * element, + GstStateChange transition); + +GST_BOILERPLATE (MpegTsMux, mpegtsmux, GstElement, GST_TYPE_ELEMENT); + +static void +mpegtsmux_base_init (gpointer g_class) +{ + const GstElementDetails mpegtsmux_details = { + "MPEG Transport Stream Muxer", + "Codec/Muxer", + "Multiplexes media streams into an MPEG Transport Stream", + "Fluendo " + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mpegtsmux_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mpegtsmux_src_factory)); + + gst_element_class_set_details (element_class, &mpegtsmux_details); +} + +static void +mpegtsmux_class_init (MpegTsMuxClass * klass) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_get_property); + gobject_class->dispose = mpegtsmux_dispose; + + gstelement_class->request_new_pad = mpegtsmux_request_new_pad; + gstelement_class->release_pad = mpegtsmux_release_pad; + gstelement_class->change_state = mpegtsmux_change_state; + + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_M2TS_MODE, + g_param_spec_boolean ("m2ts_mode", "M2TS(192 bytes) Mode", + "Defines what packet size to use, normal TS format ie .ts(188 bytes) " + "or Blue-Ray disc ie .m2ts(192 bytes).", FALSE, G_PARAM_READWRITE)); + +} + +static void +mpegtsmux_init (MpegTsMux * mux, MpegTsMuxClass * g_class) +{ + mux->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get + (&mpegtsmux_src_factory), "src"); + gst_pad_use_fixed_caps (mux->srcpad); + gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); + + mux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (mux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (mpegtsmux_collected), mux); + + mux->tsmux = tsmux_new (); + tsmux_set_write_func (mux->tsmux, new_packet_cb, mux); + mux->program = tsmux_program_new (mux->tsmux); + + mux->first = TRUE; + mux->last_flow_ret = GST_FLOW_OK; + mux->adapter = gst_adapter_new (); + mux->m2ts_mode = FALSE; + mux->first_pcr = TRUE; + mux->last_ts = 0; +} + +static void +mpegtsmux_dispose (GObject * object) +{ + MpegTsMux *mux = GST_MPEG_TSMUX (object); + + if (mux->adapter) { + gst_adapter_clear (mux->adapter); + gst_object_unref (mux->adapter); + mux->adapter = NULL; + } + if (mux->collect) { + gst_object_unref (mux->collect); + mux->collect = NULL; + } + if (mux->tsmux) { + tsmux_free (mux->tsmux); + mux->tsmux = NULL; + } + + GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static void +gst_mpegtsmux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + MpegTsMux *mux = GST_MPEG_TSMUX (object); + + switch (prop_id) { + case ARG_M2TS_MODE: + /*set incase if the output stream need to be of 192 bytes */ + mux->m2ts_mode = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mpegtsmux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + MpegTsMux *mux = GST_MPEG_TSMUX (object); + + switch (prop_id) { + case ARG_M2TS_MODE: + g_value_set_boolean (value, mux->m2ts_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +release_buffer_cb (guint8 * data, void *user_data) +{ + GstBuffer *buf = (GstBuffer *) user_data; + gst_buffer_unref (buf); +} + +static GstFlowReturn +mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data, GstPad * pad) +{ + GstFlowReturn ret = GST_FLOW_ERROR; + GstCaps *caps = gst_pad_get_negotiated_caps (pad); + GstStructure *s; + + if (caps == NULL) { + GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing"); + return GST_FLOW_NOT_NEGOTIATED; + } + + s = gst_caps_get_structure (caps, 0); + g_return_val_if_fail (s != NULL, FALSE); + + if (gst_structure_has_name (s, "video/x-dirac")) { + GST_DEBUG_OBJECT (pad, "Creating Dirac stream with PID 0x%04x", + ts_data->pid); + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_VIDEO_DIRAC, + ts_data->pid); + } else if (gst_structure_has_name (s, "video/x-h264")) { + const GValue *value; + GST_DEBUG_OBJECT (pad, "Creating H264 stream with PID 0x%04x", + ts_data->pid); + /* Codec data contains SPS/PPS which need to go in stream for valid ES */ + value = gst_structure_get_value (s, "codec_data"); + if (value) { + ts_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value)); + GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", + GST_BUFFER_SIZE (ts_data->codec_data)); + ts_data->prepare_func = mpegtsmux_prepare_h264; + } else { + ts_data->codec_data = NULL; + } + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_VIDEO_H264, + ts_data->pid); + } else if (gst_structure_has_name (s, "audio/mpeg")) { + gint mpegversion; + if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) { + GST_ELEMENT_ERROR (pad, STREAM, FORMAT, + ("Invalid data format presented"), + ("Caps with type audio/mpeg did not have mpegversion")); + goto beach; + } + + switch (mpegversion) { + case 1: + GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 1 stream with " + "PID 0x%04x", ts_data->pid); + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_AUDIO_MPEG1, + ts_data->pid); + break; + case 2: + GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 2 stream with " + "PID 0x%04x", ts_data->pid); + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_AUDIO_MPEG2, + ts_data->pid); + break; + case 4: + { + const GValue *value; + /* Codec data contains SPS/PPS which need to go in stream for valid ES */ + value = gst_structure_get_value (s, "codec_data"); + if (value) { + ts_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value)); + GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", + GST_BUFFER_SIZE (ts_data->codec_data)); + ts_data->prepare_func = mpegtsmux_prepare_aac; + } else { + ts_data->codec_data = NULL; + } + GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 4 stream with " + "PID 0x%04x", ts_data->pid); + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_AUDIO_AAC, + ts_data->pid); + break; + } + default: + GST_WARNING_OBJECT (pad, "unsupported mpegversion %d", mpegversion); + goto beach; + } + } else if (gst_structure_has_name (s, "video/mpeg")) { + gint mpegversion; + if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) { + GST_ELEMENT_ERROR (mux, STREAM, FORMAT, + ("Invalid data format presented"), + ("Caps with type video/mpeg did not have mpegversion")); + goto beach; + } + + if (mpegversion == 1) { + GST_DEBUG_OBJECT (pad, + "Creating MPEG Video, version 1 stream with PID 0x%04x", + ts_data->pid); + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_VIDEO_MPEG1, + ts_data->pid); + } else { + GST_DEBUG_OBJECT (pad, + "Creating MPEG Video, version 2 stream with PID 0x%04x", + ts_data->pid); + ts_data->stream = tsmux_create_stream (mux->tsmux, TSMUX_ST_VIDEO_MPEG2, + ts_data->pid); + } + } + + if (ts_data->stream != NULL) { + tsmux_stream_set_buffer_release_func (ts_data->stream, release_buffer_cb); + tsmux_program_add_stream (mux->program, ts_data->stream); + + ret = GST_FLOW_OK; + } + +beach: + return ret; +} + +static GstFlowReturn +mpegtsmux_create_streams (MpegTsMux * mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + GSList *walk = mux->collect->data; + + /* Create the streams */ + while (walk) { + GstCollectData *c_data = (GstCollectData *) walk->data; + MpegTsPadData *ts_data = (MpegTsPadData *) walk->data; + + walk = g_slist_next (walk); + + if (ts_data->stream == NULL) { + ret = mpegtsmux_create_stream (mux, ts_data, c_data->pad); + if (ret != GST_FLOW_OK) + goto no_stream; + } + } + + return GST_FLOW_OK; +no_stream: + GST_ELEMENT_ERROR (mux, STREAM, MUX, + ("Could not create handler for stream"), (NULL)); + return ret; +} + +static MpegTsPadData * +mpegtsmux_choose_best_stream (MpegTsMux * mux) +{ + MpegTsPadData *best = NULL; + GSList *walk; + + for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) { + GstCollectData *c_data = (GstCollectData *) walk->data; + MpegTsPadData *ts_data = (MpegTsPadData *) walk->data; + + if (ts_data->eos == FALSE) { + if (ts_data->queued_buf == NULL) { + GstBuffer *buf; + + ts_data->queued_buf = buf = gst_collect_pads_pop (mux->collect, c_data); + + if (buf != NULL) { + if (ts_data->prepare_func) { + buf = ts_data->prepare_func (buf, ts_data, mux); + if (buf) { /* Take the prepared buffer instead */ + gst_buffer_unref (ts_data->queued_buf); + ts_data->queued_buf = buf; + } else { /* If data preparation returned NULL, use unprepared one */ + buf = ts_data->queued_buf; + } + } + if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { + /* Ignore timestamps that go backward for now. FIXME: Handle all + * incoming PTS */ + if (ts_data->last_ts == GST_CLOCK_TIME_NONE || + ts_data->last_ts < GST_BUFFER_TIMESTAMP (buf)) { + ts_data->cur_ts = ts_data->last_ts = + gst_segment_to_running_time (&c_data->segment, + GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); + } else { + GST_DEBUG_OBJECT (mux, "Ignoring PTS that has gone backward"); + } + } else + ts_data->cur_ts = GST_CLOCK_TIME_NONE; + + GST_DEBUG_OBJECT (mux, "Pulled buffer with ts %" GST_TIME_FORMAT + " (uncorrected ts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT + ") for PID 0x%04x", + GST_TIME_ARGS (ts_data->cur_ts), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_BUFFER_TIMESTAMP (buf), ts_data->pid); + + /* Choose a stream we've never seen a timestamp for to ensure + * we push enough buffers from it to reach a timestamp */ + if (ts_data->last_ts == GST_CLOCK_TIME_NONE) + best = ts_data; + } else { + ts_data->eos = TRUE; + continue; + } + } + + /* If we don't yet have a best pad, take this one, otherwise take + * whichever has the oldest timestamp */ + if (best != NULL) { + if (ts_data->last_ts != GST_CLOCK_TIME_NONE && + best->last_ts != GST_CLOCK_TIME_NONE && + ts_data->last_ts < best->last_ts) { + best = ts_data; + } + } else { + best = ts_data; + } + } + } + + return best; +} + +static GstFlowReturn +mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + MpegTsPadData *best = NULL; + + GST_DEBUG_OBJECT (mux, "Pads collected"); + + if (mux->first) { + ret = mpegtsmux_create_streams (mux); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + return ret; + + best = mpegtsmux_choose_best_stream (mux); + + if (mux->pcr_stream == NULL) { + if (best) { + GstCollectData *c_data = (GstCollectData *) best; + /* Take the first data stream for the PCR */ + GST_DEBUG_OBJECT (mux, "Use stream from pad %" GST_PTR_FORMAT " as PCR", + c_data->pad); + mux->pcr_stream = best->stream; + } + } + + /* Set the chosen PCR stream */ + g_return_val_if_fail (mux->pcr_stream != NULL, GST_FLOW_ERROR); + tsmux_program_set_pcr_stream (mux->program, mux->pcr_stream); + + if (!mpegtsdemux_prepare_srcpad (mux)) { + GST_DEBUG_OBJECT (mux, "Failed to send new segment"); + goto new_seg_fail; + } + + mux->first = FALSE; + } else { + best = mpegtsmux_choose_best_stream (mux); + } + + if (best != NULL) { + GstBuffer *buf = best->queued_buf; + GstCollectData *c_data = (GstCollectData *) best; + gint64 pts = -1; + + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + + GST_DEBUG_OBJECT (mux, + "Chose stream from pad %" GST_PTR_FORMAT " for output (PID: 0x%04x)", + c_data->pad, best->pid); + + if (GST_CLOCK_TIME_IS_VALID (best->cur_ts)) { + pts = GSTTIME_TO_MPEGTIME (best->cur_ts); + GST_DEBUG_OBJECT (mux, "Buffer has TS %" GST_TIME_FORMAT " pts %" + G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_ts), pts); + } + + tsmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf), buf, pts, -1); + best->queued_buf = NULL; + + while (tsmux_stream_bytes_in_buffer (best->stream) > 0) { + if (!tsmux_write_stream_packet (mux->tsmux, best->stream)) { + GST_DEBUG_OBJECT (mux, "Failed to write data packet"); + goto write_fail; + } + } + if (mux->pcr_stream == best->stream) { + mux->last_ts = best->last_ts; + } + } else { + /* FIXME: Drain all remaining streams */ + /* At EOS */ + gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); + } + + return ret; +new_seg_fail: + return GST_FLOW_ERROR; +write_fail: + /* FIXME: Failed writing data for some reason. Should set appropriate error */ + return mux->last_flow_ret; +} + +static GstPad * +mpegtsmux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name) +{ + MpegTsMux *mux = GST_MPEG_TSMUX (element); + gint pid = -1; + gchar *pad_name = NULL; + GstPad *pad = NULL; + MpegTsPadData *pad_data = NULL; + + if (name != NULL && sscanf (name, "sink_%d", &pid) == 1) { + if (tsmux_find_stream (mux->tsmux, pid)) + goto stream_exists; + } else { + pid = tsmux_get_new_pid (mux->tsmux); + } + + pad_name = g_strdup_printf ("sink_%d", pid); + pad = gst_pad_new_from_template (templ, pad_name); + g_free (pad_name); + + pad_data = (MpegTsPadData *) gst_collect_pads_add_pad (mux->collect, pad, + sizeof (MpegTsPadData)); + if (pad_data == NULL) + goto pad_failure; + + pad_data->pid = pid; + pad_data->last_ts = GST_CLOCK_TIME_NONE; + pad_data->codec_data = NULL; + pad_data->prepare_func = NULL; + + if (G_UNLIKELY (!gst_element_add_pad (element, pad))) + goto could_not_add; + + return pad; + +stream_exists: + GST_ELEMENT_ERROR (element, STREAM, MUX, ("Duplicate PID requested"), (NULL)); + return NULL; + +could_not_add: + GST_ELEMENT_ERROR (element, STREAM, FAILED, + ("Internal data stream error."), ("Could not add pad to element")); + gst_collect_pads_remove_pad (mux->collect, pad); + gst_object_unref (pad); + return NULL; +pad_failure: + GST_ELEMENT_ERROR (element, STREAM, FAILED, + ("Internal data stream error."), ("Could not add pad to collectpads")); + gst_object_unref (pad); + return NULL; +} + +static void +mpegtsmux_release_pad (GstElement * element, GstPad * pad) +{ + MpegTsMux *mux = GST_MPEG_TSMUX (element); + MpegTsPadData *pad_data = NULL; + + GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad); + + /* Get the MpegTsPadData out of the pad */ + GST_OBJECT_LOCK (pad); + pad_data = (MpegTsPadData *) gst_pad_get_element_private (pad); + if (G_LIKELY (pad_data)) { + /* Free codec data reference if any */ + if (pad_data->codec_data) { + GST_DEBUG_OBJECT (element, "releasing codec_data reference"); + gst_buffer_unref (pad_data->codec_data); + pad_data->codec_data = NULL; + } + } + GST_OBJECT_UNLOCK (pad); + + gst_collect_pads_remove_pad (mux->collect, pad); +} + +static gboolean +new_packet_cb (guint8 * data, guint len, void *user_data, gint64 new_pcr) +{ + /* Called when the TsMux has prepared a packet for output. Return FALSE + * on error */ + MpegTsMux *mux = (MpegTsMux *) user_data; + GstBuffer *buf, *out_buf; + GstFlowReturn ret; + gfloat current_ts; + gint64 m2ts_pcr, pcr_bytes, chunk_bytes; + gint8 *temp_ptr; + gint64 ts_rate; + + if (mux->m2ts_mode == TRUE) { + /* Enters when the m2ts-mode is set true */ + buf = gst_buffer_new_and_alloc (M2TS_PACKET_LENGTH); + if (G_UNLIKELY (buf == NULL)) { + mux->last_flow_ret = GST_FLOW_ERROR; + return FALSE; + } + /* copies the ts data of 188 bytes to the m2ts buffer at an offset + of 4 bytes of timestamp */ + memcpy (GST_BUFFER_DATA (buf) + 4, data, len); + + if (new_pcr >= 0) { + /*when there is a pcr value in ts data */ + pcr_bytes = 0; + if (mux->first_pcr) { + /*Incase of first pcr */ + /*writing the 4 byte timestamp value */ + GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), new_pcr); + + GST_LOG_OBJECT (mux, "Outputting a packet of length %d", + M2TS_PACKET_LENGTH); + ret = gst_pad_push (mux->srcpad, buf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + mux->last_flow_ret = ret; + return FALSE; + } + mux->first_pcr = FALSE; + mux->previous_pcr = new_pcr; + pcr_bytes = M2TS_PACKET_LENGTH; + } + chunk_bytes = gst_adapter_available (mux->adapter); + + if (G_UNLIKELY (chunk_bytes)) { + /* calculate rate based on latest and previous pcr values */ + ts_rate = ((chunk_bytes * STANDARD_TIME_CLOCK) / (new_pcr - + mux->previous_pcr)); + while (1) { + /*loop till all the accumulated ts packets are transformed to + m2ts packets and pushed */ + current_ts = ((gfloat) mux->previous_pcr / STANDARD_TIME_CLOCK) + + ((gfloat) pcr_bytes / ts_rate); + m2ts_pcr = (((gint64) (STANDARD_TIME_CLOCK * current_ts / 300) & + TWO_POW_33_MINUS1) * 300) + ((gint64) (STANDARD_TIME_CLOCK * + current_ts) % 300); + temp_ptr = (gint8 *) & m2ts_pcr; + + out_buf = gst_adapter_take_buffer (mux->adapter, M2TS_PACKET_LENGTH); + if (G_UNLIKELY (!out_buf)) + break; + + /*writing the 4 byte timestamp value */ + GST_WRITE_UINT32_BE (GST_BUFFER_DATA (out_buf), m2ts_pcr); + + GST_LOG_OBJECT (mux, "Outputting a packet of length %d", + M2TS_PACKET_LENGTH); + ret = gst_pad_push (mux->srcpad, out_buf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + mux->last_flow_ret = ret; + return FALSE; + } + pcr_bytes += M2TS_PACKET_LENGTH; + } + mux->previous_pcr = m2ts_pcr; + } + } else + /* If theres no pcr in current ts packet then push the packet + to an adapter, which is used to create m2ts packets */ + gst_adapter_push (mux->adapter, buf); + } else { + /* In case of Normal Ts packets */ + GST_LOG_OBJECT (mux, "Outputting a packet of length %d", len); + buf = gst_buffer_new_and_alloc (len); + if (G_UNLIKELY (buf == NULL)) { + mux->last_flow_ret = GST_FLOW_ERROR; + return FALSE; + } + + memcpy (GST_BUFFER_DATA (buf), data, len); + GST_BUFFER_TIMESTAMP (buf) = mux->last_ts; + ret = gst_pad_push (mux->srcpad, buf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + mux->last_flow_ret = ret; + return FALSE; + } + } + + return TRUE; +} + +static gboolean +mpegtsdemux_prepare_srcpad (MpegTsMux * mux) +{ + GstEvent *new_seg = + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0); + GstCaps *caps = gst_caps_new_simple ("video/mpegts", + "systemstream", G_TYPE_BOOLEAN, TRUE, + "packetsize", G_TYPE_INT, + (mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH), + NULL); + +// gst_static_pad_template_get_caps (&mpegtsmux_src_factory); + + /* Set caps on src pad from our template and push new segment */ + gst_pad_set_caps (mux->srcpad, caps); + + if (!gst_pad_push_event (mux->srcpad, new_seg)) { + GST_WARNING_OBJECT (mux, "New segment event was not handled"); + return FALSE; + } + + return TRUE; +} + +static GstStateChangeReturn +mpegtsmux_change_state (GstElement * element, GstStateChange transition) +{ + MpegTsMux *mux = GST_MPEG_TSMUX (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_collect_pads_start (mux->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (mux->collect); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (mux->adapter) + gst_adapter_clear (mux->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + default: + break; + } + + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "mpegtsmux", GST_RANK_PRIMARY, + mpegtsmux_get_type ())) + return FALSE; + + GST_DEBUG_CATEGORY_INIT (mpegtsmux_debug, "mpegtsmux", 0, + "MPEG Transport Stream muxer"); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, + "mpegtsmux", "MPEG-TS muxer", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/mpegtsmux/mpegtsmux.h b/gst/mpegtsmux/mpegtsmux.h new file mode 100644 index 0000000000..bb46d4757c --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux.h @@ -0,0 +1,163 @@ +/* + * Copyright 2006, 2007, 2008 Fluendo S.A. + * Authors: Jan Schmidt + * Kapil Agrawal + * Julien Moutte + * + * 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. + * + */ + +#ifndef __MPEGTSMUX_H__ +#define __MPEGTSMUX_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#include + +#define GST_TYPE_MPEG_TSMUX (mpegtsmux_get_type()) +#define GST_MPEG_TSMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_MPEG_TSMUX, MpegTsMux)) + +typedef struct MpegTsMux MpegTsMux; +typedef struct MpegTsMuxClass MpegTsMuxClass; +typedef struct MpegTsPadData MpegTsPadData; + +typedef GstBuffer * (*MpegTsPadDataPrepareFunction) (GstBuffer * buf, + MpegTsPadData * data, MpegTsMux * mux); + +struct MpegTsMux { + GstElement parent; + + GstPad *srcpad; + + GstCollectPads *collect; + + TsMux *tsmux; + TsMuxProgram *program; + + gboolean first; + TsMuxStream *pcr_stream; + GstFlowReturn last_flow_ret; + GstAdapter *adapter; + gint64 previous_pcr; + gboolean m2ts_mode; + gboolean first_pcr; + + GstClockTime last_ts; +}; + +struct MpegTsMuxClass { + GstElementClass parent_class; +}; + +struct MpegTsPadData { + GstCollectData collect; /* Parent */ + + gint pid; + TsMuxStream *stream; + + GstBuffer *queued_buf; /* Currently pulled buffer */ + GstClockTime cur_ts; /* Adjusted TS for the pulled buffer */ + GstClockTime last_ts; /* Most recent valid TS for this stream */ + + GstBuffer * codec_data; /* Optional codec data available in the caps */ + + MpegTsPadDataPrepareFunction prepare_func; /* Handler to prepare input data */ + + gboolean eos; +}; + +GType mpegtsmux_get_type (void); + +#define CLOCK_BASE 9LL +#define CLOCK_FREQ (CLOCK_BASE * 10000) + +#define MPEGTIME_TO_GSTTIME(time) (gst_util_uint64_scale ((time), \ + GST_MSECOND/10, CLOCK_BASE)) +#define GSTTIME_TO_MPEGTIME(time) (gst_util_uint64_scale ((time), \ + CLOCK_BASE, GST_MSECOND/10)) + +#define NORMAL_TS_PACKET_LENGTH 188 +#define M2TS_PACKET_LENGTH 192 +#define STANDARD_TIME_CLOCK 27000000 +/*33 bits as 1 ie 0x1ffffffff*/ +#define TWO_POW_33_MINUS1 ((0xffffffff * 2) - 1) +G_END_DECLS + +#endif diff --git a/gst/mpegtsmux/mpegtsmux_aac.c b/gst/mpegtsmux/mpegtsmux_aac.c new file mode 100644 index 0000000000..71727f2105 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_aac.c @@ -0,0 +1,151 @@ +/* + * Copyright 2006, 2007, 2008 Fluendo S.A. + * Authors: Jan Schmidt + * Kapil Agrawal + * Julien Moutte + * + * 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 "mpegtsmux_aac.h" +#include + +GST_DEBUG_CATEGORY_EXTERN (mpegtsmux_debug); +#define GST_CAT_DEFAULT mpegtsmux_debug + +GstBuffer * +mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux) +{ + guint8 *adts_header = g_malloc0 (7); + GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 7); + gsize out_offset = 0; + guint8 rate_idx = 0, channels = 0, obj_type = 0; + + GST_DEBUG_OBJECT (mux, "Preparing AAC buffer for output"); + + /* We want the same metadata */ + gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL); + + /* Generate ADTS header */ + obj_type = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0xC) >> 2; + obj_type++; + rate_idx = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0x3) << 1; + rate_idx |= + (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x80) >> 7; + channels = + (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x78) >> 3; + GST_DEBUG_OBJECT (mux, "Rate index %u, channels %u, object type %u", rate_idx, + channels, obj_type); + /* Sync point over a full byte */ + adts_header[0] = 0xFF; + /* Sync point continued over first 4 bits + static 4 bits + * (ID, layer, protection)*/ + adts_header[1] = 0xF1; + /* Object type over first 2 bits */ + adts_header[2] = obj_type << 6; + /* rate index over next 4 bits */ + adts_header[2] |= (rate_idx << 2); + /* channels over last 2 bits */ + adts_header[2] |= (channels & 0x4) >> 2; + /* channels continued over next 2 bits + 4 bits at zero */ + adts_header[3] = (channels & 0x3) << 6; + /* frame size over last 2 bits */ + adts_header[3] |= (GST_BUFFER_SIZE (out_buf) & 0x1800) >> 11; + /* frame size continued over full byte */ + adts_header[4] = (GST_BUFFER_SIZE (out_buf) & 0x1FF8) >> 3; + /* frame size continued first 3 bits */ + adts_header[5] = (GST_BUFFER_SIZE (out_buf) & 0x7) << 5; + /* buffer fullness (0x7FF for VBR) over 5 last bits */ + adts_header[5] |= 0x1F; + /* buffer fullness (0x7FF for VBR) continued over 6 first bits + 2 zeros for + * number of raw data blocks */ + adts_header[6] = 0xFC; + + /* Insert ADTS header */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, adts_header, 7); + g_free (adts_header); + out_offset += 7; + + /* Now copy complete frame */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + + return out_buf; +} diff --git a/gst/mpegtsmux/mpegtsmux_aac.h b/gst/mpegtsmux/mpegtsmux_aac.h new file mode 100644 index 0000000000..1e898b7afb --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_aac.h @@ -0,0 +1,91 @@ +/* + * Copyright 2006, 2007, 2008 Fluendo S.A. + * Authors: Jan Schmidt + * Kapil Agrawal + * Julien Moutte + * + * 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. + * + */ + +#ifndef __MPEGTSMUX_AAC_H__ +#define __MPEGTSMUX_AAC_H__ + +#include "mpegtsmux.h" + +GstBuffer * mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, + MpegTsMux * mux); + +#endif /* __MPEGTSMUX_AAC_H__ */ diff --git a/gst/mpegtsmux/mpegtsmux_h264.c b/gst/mpegtsmux/mpegtsmux_h264.c new file mode 100644 index 0000000000..5824f35846 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_h264.c @@ -0,0 +1,197 @@ +/* + * Copyright 2006, 2007, 2008 Fluendo S.A. + * Authors: Jan Schmidt + * Kapil Agrawal + * Julien Moutte + * + * 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 "mpegtsmux_h264.h" +#include + +GST_DEBUG_CATEGORY_EXTERN (mpegtsmux_debug); +#define GST_CAT_DEFAULT mpegtsmux_debug + +GstBuffer * +mpegtsmux_prepare_h264 (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux) +{ + guint8 nal_length_size = 0; + guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 }; + GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2); + gint offset = 4, i = 0, nb_sps = 0, nb_pps = 0; + gsize out_offset = 0, in_offset = 0; + + GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output"); + + /* We want the same metadata */ + gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL); + + /* Get NAL length size */ + nal_length_size = + (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x03) + 1; + GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes", nal_length_size); + offset++; + + /* Generate SPS */ + nb_sps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x1f; + GST_DEBUG_OBJECT (mux, "we have %d Sequence Parameter Set", nb_sps); + offset++; + + /* For each SPS */ + for (i = 0; i < nb_sps; i++) { + guint16 sps_size = + GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset); + + GST_LOG_OBJECT (mux, "Sequence Parameter Set is %d bytes", sps_size); + + /* Jump over SPS size */ + offset += 2; + + /* Fake a start code */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4); + out_offset += 4; + /* Now push the SPS */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, + GST_BUFFER_DATA (data->codec_data) + offset, sps_size); + + out_offset += sps_size; + offset += sps_size; + } + + nb_pps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset); + GST_LOG_OBJECT (mux, "we have %d Picture Parameter Set", nb_sps); + offset++; + + /* For each PPS */ + for (i = 0; i < nb_pps; i++) { + gint pps_size = + GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset); + + GST_LOG_OBJECT (mux, "Picture Parameter Set is %d bytes", pps_size); + + /* Jump over PPS size */ + offset += 2; + + /* Fake a start code */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4); + out_offset += 4; + /* Now push the PPS */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, + GST_BUFFER_DATA (data->codec_data) + offset, pps_size); + + out_offset += pps_size; + offset += pps_size; + } + + while (in_offset < GST_BUFFER_SIZE (buf) && + out_offset < GST_BUFFER_SIZE (out_buf) - 4) { + guint32 nal_size = 0; + + switch (nal_length_size) { + case 1: + nal_size = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + in_offset); + break; + case 2: + nal_size = GST_READ_UINT16_BE (GST_BUFFER_DATA (buf) + in_offset); + break; + case 4: + nal_size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf) + in_offset); + break; + default: + GST_WARNING_OBJECT (mux, "unsupported NAL length size %u", + nal_length_size); + } + in_offset += nal_length_size; + + /* Generate an Elementary stream buffer by inserting a startcode */ + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4); + out_offset += 4; + memcpy (GST_BUFFER_DATA (out_buf) + out_offset, + GST_BUFFER_DATA (buf) + in_offset, + MIN (nal_size, GST_BUFFER_SIZE (out_buf) - out_offset)); + in_offset += nal_size; + out_offset += nal_size; + } + + GST_BUFFER_SIZE (out_buf) = out_offset; + + return out_buf; +} diff --git a/gst/mpegtsmux/mpegtsmux_h264.h b/gst/mpegtsmux/mpegtsmux_h264.h new file mode 100644 index 0000000000..be5692a0b8 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_h264.h @@ -0,0 +1,91 @@ +/* + * Copyright 2006, 2007, 2008 Fluendo S.A. + * Authors: Jan Schmidt + * Kapil Agrawal + * Julien Moutte + * + * 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. + * + */ + +#ifndef __MPEGTSMUX_H264_H__ +#define __MPEGTSMUX_H264_H__ + +#include "mpegtsmux.h" + +GstBuffer * mpegtsmux_prepare_h264 (GstBuffer * buf, MpegTsPadData * data, + MpegTsMux * mux); + +#endif /* __MPEGTSMUX_H264_H__ */ diff --git a/gst/mpegtsmux/tsmux/Makefile.am b/gst/mpegtsmux/tsmux/Makefile.am new file mode 100644 index 0000000000..e995cf7cf6 --- /dev/null +++ b/gst/mpegtsmux/tsmux/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libtsmux.la + +libtsmux_la_CFLAGS = $(GST_CFLAGS) +libtsmux_la_LIBADD = $(GST_LIBS) +libtsmux_la_LDFLAGS = -module -avoid-version +libtsmux_la_SOURCES = tsmux.c tsmuxstream.c + +noinst_HEADERS = crc.h tsmuxcommon.h tsmux.h tsmuxstream.h diff --git a/gst/mpegtsmux/tsmux/crc.h b/gst/mpegtsmux/tsmux/crc.h new file mode 100644 index 0000000000..089ce2c540 --- /dev/null +++ b/gst/mpegtsmux/tsmux/crc.h @@ -0,0 +1,58 @@ +static guint32 crc_tab[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +static guint32 +calc_crc32 (guint8 *data, guint datalen) +{ + guint i; + guint32 crc = 0xffffffff; + + for (i=0; i> 24) ^ *data++) & 0xff]; + } + + return crc; +} diff --git a/gst/mpegtsmux/tsmux/tsmux.c b/gst/mpegtsmux/tsmux/tsmux.c new file mode 100644 index 0000000000..0952f699f0 --- /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 + +#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; iflags & 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 = §ion->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); +} diff --git a/gst/mpegtsmux/tsmux/tsmux.h b/gst/mpegtsmux/tsmux/tsmux.h new file mode 100644 index 0000000000..fb0a81b53a --- /dev/null +++ b/gst/mpegtsmux/tsmux/tsmux.h @@ -0,0 +1,183 @@ +/* + * 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. + * + */ + +#ifndef __TSMUX_H__ +#define __TSMUX_H__ + +#include + +#include +#include + +G_BEGIN_DECLS + +#define TSMUX_MAX_ES_INFO_LENGTH ((1 << 12) - 1) +#define TSMUX_MAX_SECTION_LENGTH (4096) + +#define TSMUX_PID_AUTO ((guint16)-1) + +typedef struct TsMuxSection TsMuxSection; +typedef struct TsMux TsMux; + +typedef gboolean (*TsMuxWriteFunc) (guint8 *data, guint len, void *user_data, gint64 new_pcr); + +struct TsMuxSection { + TsMuxPacketInfo pi; + + /* Private sections can be up to 4096 bytes */ + guint8 data[TSMUX_MAX_SECTION_LENGTH]; +}; + +/* Information for the streams associated with one program */ +struct TsMuxProgram { + TsMuxSection pmt; + guint8 pmt_version; + gboolean pmt_changed; + + guint pmt_frequency; + gint64 last_pmt_ts; + + guint16 pgm_number; /* program ID for the PAT */ + guint16 pmt_pid; /* PID to write the PMT */ + + TsMuxStream *pcr_stream; /* Stream which carries the PCR */ + gint64 last_pcr; + + GArray *streams; /* Array of TsMuxStream pointers */ + guint nb_streams; +}; + +struct TsMux { + guint nb_streams; + GList *streams; /* TsMuxStream* array of all streams */ + + guint nb_programs; + GList *programs; /* TsMuxProgram* array of all programs */ + + guint16 transport_id; + + guint16 next_pgm_no; + guint16 next_pmt_pid; + guint16 next_stream_pid; + + TsMuxSection pat; + guint8 pat_version; + gboolean pat_changed; + + guint pat_frequency; + gint64 last_pat_ts; + + guint8 packet_buf[TSMUX_PACKET_LENGTH]; + TsMuxWriteFunc write_func; + void *write_func_data; + + /* Scratch space for writing ES_info descriptors */ + guint8 es_info_buf[TSMUX_MAX_ES_INFO_LENGTH]; + gint64 new_pcr; +}; + +/* create/free new muxer session */ +TsMux * tsmux_new (void); +void tsmux_free (TsMux *mux); + +/* Setting muxing session properties */ +void tsmux_set_write_func (TsMux *mux, TsMuxWriteFunc func, void *user_data); +void tsmux_set_pat_frequency (TsMux *mux, guint freq); +guint tsmux_get_pat_frequency (TsMux *mux); +guint16 tsmux_get_new_pid (TsMux *mux); + +/* pid/program management */ +TsMuxProgram * tsmux_program_new (TsMux *mux); +void tsmux_program_free (TsMuxProgram *program); +void tsmux_set_pmt_frequency (TsMuxProgram *program, guint freq); +guint tsmux_get_pmt_frequency (TsMuxProgram *program); + +/* stream management */ +TsMuxStream * tsmux_create_stream (TsMux *mux, TsMuxStreamType stream_type, guint16 pid); +TsMuxStream * tsmux_find_stream (TsMux *mux, guint16 pid); + +void tsmux_program_add_stream (TsMuxProgram *program, TsMuxStream *stream); +void tsmux_program_set_pcr_stream (TsMuxProgram *program, TsMuxStream *stream); + +/* writing stuff */ +gboolean tsmux_write_stream_packet (TsMux *mux, TsMuxStream *stream); + +G_END_DECLS + +#endif diff --git a/gst/mpegtsmux/tsmux/tsmuxcommon.h b/gst/mpegtsmux/tsmux/tsmuxcommon.h new file mode 100644 index 0000000000..3eb709df5d --- /dev/null +++ b/gst/mpegtsmux/tsmux/tsmuxcommon.h @@ -0,0 +1,175 @@ +/* + * 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. + * + */ + +#ifndef __TSMUX_COMMON_H__ +#define __TSMUX_COMMON_H__ + +#include + +#undef TS_DEBUG_ON + +G_BEGIN_DECLS + +#define TSMUX_SYNC_BYTE 0x47 +#define TSMUX_PACKET_LENGTH 188 +#define TSMUX_HEADER_LENGTH 4 +#define TSMUX_PAYLOAD_LENGTH (TSMUX_PACKET_LENGTH - TSMUX_HEADER_LENGTH) + +#define TSMUX_MIN_ES_DESC_LEN 8 + +/* Frequency for PCR representation */ +#define TSMUX_SYS_CLOCK_FREQ (27000000L) +/* Frequency for PTS values */ +#define TSMUX_CLOCK_FREQ (TSMUX_SYS_CLOCK_FREQ / 300) + +#define TSMUX_PACKET_FLAG_NONE (0) +#define TSMUX_PACKET_FLAG_ADAPTATION (1 << 0) +#define TSMUX_PACKET_FLAG_DISCONT (1 << 1) +#define TSMUX_PACKET_FLAG_RANDOM_ACCESS (1 << 2) +#define TSMUX_PACKET_FLAG_PRIORITY (1 << 3) +#define TSMUX_PACKET_FLAG_WRITE_PCR (1 << 4) +#define TSMUX_PACKET_FLAG_WRITE_OPCR (1 << 5) +#define TSMUX_PACKET_FLAG_WRITE_SPLICE (1 << 6) +#define TSMUX_PACKET_FLAG_WRITE_ADAPT_EXT (1 << 7) + +/* PES stream specific flags */ +#define TSMUX_PACKET_FLAG_PES_FULL_HEADER (1 << 8) +#define TSMUX_PACKET_FLAG_PES_WRITE_PTS (1 << 9) +#define TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS (1 << 10) +#define TSMUX_PACKET_FLAG_PES_WRITE_ESCR (1 << 11) +#define TSMUX_PACKET_FLAG_PES_EXT_STREAMID (1 << 12) + +typedef struct TsMuxPacketInfo TsMuxPacketInfo; +typedef struct TsMuxProgram TsMuxProgram; +typedef struct TsMuxStream TsMuxStream; + +struct TsMuxPacketInfo { + guint16 pid; + guint32 flags; + + guint64 pcr; + guint64 opcr; + + guint8 splice_countdown; + + guint8 private_data_len; + guint8 private_data [256]; + + guint8 packet_count; /* continuity counter */ + + guint stream_avail; /* Number of payload bytes available */ + gboolean packet_start_unit_indicator; +}; + +static inline void +tsmux_put16 (guint8 **pos, guint16 val) +{ + *(*pos)++ = (val >> 8) & 0xff; + *(*pos)++ = val & 0xff; +} + +static inline void +tsmux_put32 (guint8 **pos, guint32 val) +{ + *(*pos)++ = (val >> 24) & 0xff; + *(*pos)++ = (val >> 16) & 0xff; + *(*pos)++ = (val >> 8) & 0xff; + *(*pos)++ = val & 0xff; +} + +static inline void +tsmux_put_ts (guint8 **pos, guint8 id, gint64 ts) +{ + /* 1: 4 bit id value | TS [32..30] | marker_bit */ + *(*pos)++ = ((id << 4) | ((ts >> 29) & 0x0E) | 0x01) & 0xff; + /* 2, 3: TS[29..15] | marker_bit */ + tsmux_put16 (pos, ((ts >> 14) & 0xfffe) | 0x01); + /* 4, 5: TS[14..0] | marker_bit */ + tsmux_put16 (pos, ((ts << 1) & 0xfffe) | 0x01); +} + +#ifdef TS_DEBUG_ON +#define TS_DEBUG(...) g_print(__VA_ARGS__); g_print ("\n") +#else +#define TS_DEBUG(...) +#endif + +G_END_DECLS + +#endif diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.c b/gst/mpegtsmux/tsmux/tsmuxstream.c new file mode 100644 index 0000000000..74b3c82bc4 --- /dev/null +++ b/gst/mpegtsmux/tsmux/tsmuxstream.c @@ -0,0 +1,692 @@ +/* + * 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 + +#include "tsmuxcommon.h" +#include "tsmuxstream.h" + +static guint8 tsmux_stream_pes_header_length (TsMuxStream * stream); +static void tsmux_stream_write_pes_header (TsMuxStream * stream, guint8 * data); +static void tsmux_stream_find_pts_dts_within (TsMuxStream * stream, guint bound, + gint64 * pts, gint64 * dts); + +struct TsMuxStreamBuffer +{ + guint8 *data; + guint32 size; + + /* PTS & DTS associated with the contents of this buffer */ + gint64 pts; + gint64 dts; + + void *user_data; +}; + +/** + * tsmux_stream_new: + * @pid: a PID + * @stream_type: the stream type + * + * Create a new stream with PID of @pid and @stream_type. + * + * Returns: a new #TsMuxStream. + */ +TsMuxStream * +tsmux_stream_new (guint16 pid, TsMuxStreamType stream_type) +{ + TsMuxStream *stream = g_new0 (TsMuxStream, 1); + + stream->state = TSMUX_STREAM_STATE_HEADER; + stream->pi.pid = pid; + stream->stream_type = stream_type; + + stream->pes_payload_size = 0; + stream->cur_pes_payload_size = 0; + stream->pes_bytes_written = 0; + + switch (stream_type) { + case TSMUX_ST_VIDEO_MPEG1: + case TSMUX_ST_VIDEO_MPEG2: + case TSMUX_ST_VIDEO_MPEG4: + case TSMUX_ST_VIDEO_H264: + /* FIXME: Assign sequential IDs? */ + stream->id = 0xE0; + stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER; + stream->is_video_stream = TRUE; + break; + case TSMUX_ST_AUDIO_AAC: + case TSMUX_ST_AUDIO_MPEG1: + case TSMUX_ST_AUDIO_MPEG2: + /* FIXME: Assign sequential IDs? */ + stream->id = 0xC0; + stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER; + break; + case TSMUX_ST_VIDEO_DIRAC: + stream->id = 0xFD; + /* FIXME: assign sequential extended IDs? */ + stream->id_extended = 0x60; + + stream->pi.flags |= + TSMUX_PACKET_FLAG_PES_FULL_HEADER | + TSMUX_PACKET_FLAG_PES_EXT_STREAMID; + stream->is_video_stream = TRUE; + break; + default: + g_critical ("Stream type 0x%0x not yet implemented", stream_type); + break; + } + + stream->last_pts = -1; + stream->last_dts = -1; + + stream->pcr_ref = 0; + stream->last_pcr = -1; + + return stream; +} + +/** + * tsmux_stream_get_pid: + * @stream: a #TsMuxStream + * + * Get the PID of @stream. + * + * Returns: The PID of @stream. 0xffff on error. + */ +guint16 +tsmux_stream_get_pid (TsMuxStream * stream) +{ + g_return_val_if_fail (stream != NULL, G_MAXUINT16); + + return stream->pi.pid; +} + +/** + * tsmux_stream_free: + * @stream: a #TsMuxStream + * + * Free the resources of @stream. + */ +void +tsmux_stream_free (TsMuxStream * stream) +{ + g_return_if_fail (stream != NULL); + + g_free (stream); +} + +/** + * tsmux_stream_set_buffer_release_func: + * @stream: a #TsMuxStream + * @func: the new #TsMuxStreamBufferReleaseFunc + * + * Set the function that will be called when a a piece of data fed to @stream + * with tsmux_stream_add_data() can be freed. @func will be called with user + * data as provided with the call to tsmux_stream_add_data(). + */ +void +tsmux_stream_set_buffer_release_func (TsMuxStream * stream, + TsMuxStreamBufferReleaseFunc func) +{ + g_return_if_fail (stream != NULL); + + stream->buffer_release = func; +} + +/* Advance the current packet stream position by len bytes. + * Mustn't consume more than available in the current packet */ +static void +tsmux_stream_consume (TsMuxStream * stream, guint len) +{ + g_assert (stream->cur_buffer != NULL); + g_assert (len <= stream->cur_buffer->size - stream->cur_buffer_consumed); + + stream->cur_buffer_consumed += len; + stream->bytes_avail -= len; + + if (stream->cur_buffer_consumed == 0) + return; + + if (stream->cur_buffer->pts != -1) { + stream->last_pts = stream->cur_buffer->pts; + stream->last_dts = stream->cur_buffer->dts; + } else if (stream->cur_buffer->dts != -1) + stream->last_dts = stream->cur_buffer->dts; + + if (stream->cur_buffer_consumed == stream->cur_buffer->size) { + /* Current packet is completed, move along */ + stream->buffers = g_list_delete_link (stream->buffers, stream->buffers); + + if (stream->buffer_release) { + stream->buffer_release (stream->cur_buffer->data, + stream->cur_buffer->user_data); + } + + g_free (stream->cur_buffer); + stream->cur_buffer = NULL; + /* FIXME: As a hack, for unbounded streams, start a new PES packet for each + * incoming packet we receive. This assumes that incoming data is + * packetised sensibly - ie, every video frame */ + if (stream->cur_pes_payload_size == 0) + stream->state = TSMUX_STREAM_STATE_HEADER; + } +} + +/** + * tsmux_stream_at_pes_start: + * @stream: a #TsMuxStream + * + * Check if @stream is at the start of a PES packet. + * + * Returns: TRUE if @stream is at a PES header packet. + */ +gboolean +tsmux_stream_at_pes_start (TsMuxStream * stream) +{ + g_return_val_if_fail (stream != NULL, FALSE); + + return stream->state == TSMUX_STREAM_STATE_HEADER; +} + +/** + * tsmux_stream_bytes_avail: + * @stream: a #TsMuxStream + * + * Calculate how much bytes are available. + * + * Returns: The number of bytes available. + */ +gint +tsmux_stream_bytes_avail (TsMuxStream * stream) +{ + gint bytes_avail; + + g_return_val_if_fail (stream != NULL, 0); + + if (stream->cur_pes_payload_size != 0) + bytes_avail = stream->cur_pes_payload_size - stream->pes_bytes_written; + else + bytes_avail = tsmux_stream_bytes_in_buffer (stream); + + bytes_avail = MIN (bytes_avail, tsmux_stream_bytes_in_buffer (stream)); + + /* Calculate the number of bytes available in the current PES */ + if (stream->state == TSMUX_STREAM_STATE_HEADER) + bytes_avail += tsmux_stream_pes_header_length (stream); + + return bytes_avail; +} + +/** + * tsmux_stream_bytes_in_buffer: + * @stream: a #TsMuxStream + * + * Calculate how much bytes are in the buffer. + * + * Returns: The number of bytes in the buffer. + */ +gint +tsmux_stream_bytes_in_buffer (TsMuxStream * stream) +{ + g_return_val_if_fail (stream != NULL, 0); + + return stream->bytes_avail; +} + +/** + * tsmux_stream_get_data: + * @stream: a #TsMuxStream + * @buf: a buffer to hold the result + * @len: the length of @buf + * + * Copy up to @len available data in @stream into the buffer @buf. + * + * Returns: TRUE if @len bytes could be retrieved. + */ +gboolean +tsmux_stream_get_data (TsMuxStream * stream, guint8 * buf, guint len) +{ + g_return_val_if_fail (stream != NULL, FALSE); + g_return_val_if_fail (buf != NULL, FALSE); + + if (stream->state == TSMUX_STREAM_STATE_HEADER) { + guint8 pes_hdr_length; + + if (stream->pes_payload_size != 0) { + /* Use prescribed fixed PES payload size */ + stream->cur_pes_payload_size = stream->pes_payload_size; + tsmux_stream_find_pts_dts_within (stream, stream->cur_pes_payload_size, + &stream->pts, &stream->dts); + } else if (stream->is_video_stream) { + /* Unbounded for video streams */ + stream->cur_pes_payload_size = 0; + tsmux_stream_find_pts_dts_within (stream, + tsmux_stream_bytes_in_buffer (stream), &stream->pts, &stream->dts); + } else { + /* Output a PES packet of all currently available bytes otherwise */ + stream->cur_pes_payload_size = tsmux_stream_bytes_in_buffer (stream); + tsmux_stream_find_pts_dts_within (stream, stream->cur_pes_payload_size, + &stream->pts, &stream->dts); + } + + stream->pi.flags &= ~(TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS | + TSMUX_PACKET_FLAG_PES_WRITE_PTS); + + if (stream->pts != -1 && stream->dts != -1) + stream->pi.flags |= TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS; + else { + if (stream->pts != -1) + stream->pi.flags |= TSMUX_PACKET_FLAG_PES_WRITE_PTS; + } + + pes_hdr_length = tsmux_stream_pes_header_length (stream); + + /* Submitted buffer must be at least as large as the PES header */ + if (len < pes_hdr_length) + return FALSE; + + TS_DEBUG ("Writing PES header of length %u and payload %d", + pes_hdr_length, stream->cur_pes_payload_size); + tsmux_stream_write_pes_header (stream, buf); + + len -= pes_hdr_length; + buf += pes_hdr_length; + + stream->state = TSMUX_STREAM_STATE_PACKET; + } + + if (len > (guint) tsmux_stream_bytes_avail (stream)) + return FALSE; + + stream->pes_bytes_written += len; + + if (stream->cur_pes_payload_size != 0 && + stream->pes_bytes_written == stream->cur_pes_payload_size) { + TS_DEBUG ("Finished PES packet"); + stream->state = TSMUX_STREAM_STATE_HEADER; + stream->pes_bytes_written = 0; + } + + while (len > 0) { + guint32 avail; + guint8 *cur; + + if (stream->cur_buffer == NULL) { + /* Start next packet */ + if (stream->buffers == NULL) + return FALSE; + stream->cur_buffer = (TsMuxStreamBuffer *) (stream->buffers->data); + stream->cur_buffer_consumed = 0; + } + + /* Take as much as we can from the current buffer */ + avail = stream->cur_buffer->size - stream->cur_buffer_consumed; + cur = stream->cur_buffer->data + stream->cur_buffer_consumed; + if (avail < len) { + memcpy (buf, cur, avail); + tsmux_stream_consume (stream, avail); + + buf += avail; + len -= avail; + } else { + memcpy (buf, cur, len); + tsmux_stream_consume (stream, len); + + len = 0; + } + } + + return TRUE; +} + +static guint8 +tsmux_stream_pes_header_length (TsMuxStream * stream) +{ + guint8 packet_len; + + /* Calculate the length of the header for this stream */ + + /* start_code prefix + stream_id + pes_packet_length = 6 bytes */ + packet_len = 6; + + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_FULL_HEADER) { + /* For a PES 'full header' we have at least 3 more bytes, + * and then more based on flags */ + packet_len += 3; + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) { + packet_len += 10; + } else if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS) { + packet_len += 5; + } + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_EXT_STREAMID) { + /* Need basic extension flags (1 byte), plus 2 more bytes for the + * length + extended stream id */ + packet_len += 3; + } + } + + return packet_len; +} + +/* Find a PTS/DTS to write into the pes header within the next bound bytes + * of the data */ +static void +tsmux_stream_find_pts_dts_within (TsMuxStream * stream, guint bound, + gint64 * pts, gint64 * dts) +{ + GList *cur; + + *pts = -1; + *dts = -1; + + for (cur = g_list_first (stream->buffers); cur != NULL; + cur = g_list_next (cur)) { + TsMuxStreamBuffer *curbuf = cur->data; + + /* FIXME: This isn't quite correct - if the 'bound' is within this + * buffer, we don't know if the timestamp is before or after the split + * so we shouldn't return it */ + if (bound <= curbuf->size) { + *pts = curbuf->pts; + *dts = curbuf->dts; + return; + } + + /* Have we found a buffer with pts/dts set? */ + if (curbuf->pts != -1 || curbuf->dts != -1) { + *pts = curbuf->pts; + *dts = curbuf->dts; + return; + } + + bound -= curbuf->size; + } +} + +static void +tsmux_stream_write_pes_header (TsMuxStream * stream, guint8 * data) +{ + guint16 length_to_write; + guint8 hdr_len = tsmux_stream_pes_header_length (stream); + + /* start_code prefix + stream_id + pes_packet_length = 6 bytes */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x01; + data[3] = stream->id; + data += 4; + + /* Write 2 byte PES packet length here. 0 (unbounded) is only + * valid for video packets */ + if (stream->cur_pes_payload_size != 0) { + length_to_write = hdr_len + stream->cur_pes_payload_size - 6; + } else { + length_to_write = 0; + } + + tsmux_put16 (&data, length_to_write); + + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_FULL_HEADER) { + guint8 flags = 0; + + /* Not scrambled, original, not-copyrighted, data_alignment not specified */ + *data++ = 0x81; + + /* Flags */ + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) + flags |= 0xC0; + else if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS) + flags |= 0x80; + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_EXT_STREAMID) + flags |= 0x01; /* Enable PES_extension_flag */ + *data++ = flags; + + /* Header length is the total pes length, + * minus the 9 bytes of start codes, flags + hdr_len */ + g_return_if_fail (hdr_len >= 9); + *data++ = (hdr_len - 9); + + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) { + tsmux_put_ts (&data, 0x3, stream->pts); + tsmux_put_ts (&data, 0x1, stream->dts); + } else if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS) { + tsmux_put_ts (&data, 0x2, stream->pts); + } + if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_EXT_STREAMID) { + guint8 ext_len; + + flags = 0x0f; /* (reserved bits) | PES_extension_flag_2 */ + *data++ = flags; + + ext_len = 1; /* Only writing 1 byte into the extended fields */ + *data++ = 0x80 | ext_len; + /* Write the extended streamID */ + *data++ = 0x80 | stream->id_extended; + } + } +} + +/** + * tsmux_stream_add_data: + * @stream: a #TsMuxStream + * @data: data to add + * @len: length of @data + * @user_data: user data to pass to release func + * @pts: PTS of access unit in @data + * @dts: DTS of access unit in @data + * + * Submit @len bytes of @data into @stream. @pts and @dts can be set to the + * timestamp (against a 90Hz clock) of the first access unit in @data. A + * timestamp of -1 for @pts or @dts means unknown. + * + * @user_data will be passed to the release function as set with + * tsmux_stream_set_buffer_release_func() when @data can be freed. + */ +void +tsmux_stream_add_data (TsMuxStream * stream, guint8 * data, guint len, + void *user_data, gint64 pts, gint64 dts) +{ + TsMuxStreamBuffer *packet; + + g_return_if_fail (stream != NULL); + + packet = g_new (TsMuxStreamBuffer, 1); + packet->data = data; + packet->size = len; + packet->user_data = user_data; + + packet->pts = pts; + packet->dts = dts; + + if (stream->bytes_avail == 0) + stream->last_pts = pts; + + stream->bytes_avail += len; + stream->buffers = g_list_append (stream->buffers, packet); +} + +/** + * tsmux_stream_get_es_descrs: + * @stream: a #TsMuxStream + * @buf: a buffer to hold the ES descriptor + * @len: the length used in @buf + * + * Write an Elementary Stream Descriptor for @stream into @buf. the number of + * bytes consumed in @buf will be updated in @len. + * + * @buf and @len must be at least #TSMUX_MIN_ES_DESC_LEN. + */ +void +tsmux_stream_get_es_descrs (TsMuxStream * stream, guint8 * buf, guint16 * len) +{ + guint8 *pos; + + g_return_if_fail (stream != NULL); + + if (buf == NULL) { + if (len != NULL) + *len = 0; + return; + } + + /* Based on the stream type, write out any descriptors to go in the + * PMT ES_info field */ + pos = buf; + + switch (stream->stream_type) { + case TSMUX_ST_VIDEO_DIRAC: + /* tag (registration_descriptor), length, format_identifier */ + *pos++ = 0x05; + *pos++ = 4; + *pos++ = 0x64; /* 'd' */ + *pos++ = 0x72; /* 'r' */ + *pos++ = 0x61; /* 'a' */ + *pos++ = 0x63; /* 'c' */ + break; + default: + break; + } + + if (len) + *len = (pos - buf); +} + +/** + * tsmux_stream_pcr_ref: + * @stream: a #TsMuxStream + * + * Mark the stream as being used as the PCR for some program. + */ +void +tsmux_stream_pcr_ref (TsMuxStream * stream) +{ + g_return_if_fail (stream != NULL); + + stream->pcr_ref++; +} + +/** + * tsmux_stream_pcr_unref: + * @stream: a #TsMuxStream + * + * Mark the stream as no longer being used as the PCR for some program. + */ +void +tsmux_stream_pcr_unref (TsMuxStream * stream) +{ + g_return_if_fail (stream != NULL); + + stream->pcr_ref--; +} + +/** + * tsmux_stream_is_pcr: + * @stream: a #TsMuxStream + * + * Check if @stream is used as the PCR for some program. + * + * Returns: TRUE if the stream is in use as the PCR for some program. + */ +gboolean +tsmux_stream_is_pcr (TsMuxStream * stream) +{ + return stream->pcr_ref != 0; +} + +/** + * tsmux_stream_get_pts: + * @stream: a #TsMuxStream + * + * Return the PTS of the last buffer that has had bytes written and + * which _had_ a PTS in @stream. + * + * Returns: the PTS of the last buffer in @stream. + */ +guint64 +tsmux_stream_get_pts (TsMuxStream * stream) +{ + g_return_val_if_fail (stream != NULL, -1); + + return stream->last_pts; +} diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.h b/gst/mpegtsmux/tsmux/tsmuxstream.h new file mode 100644 index 0000000000..9cc049ad2a --- /dev/null +++ b/gst/mpegtsmux/tsmux/tsmuxstream.h @@ -0,0 +1,217 @@ +/* + * 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. + * + */ + +#ifndef __TSMUXSTREAM_H__ +#define __TSMUXSTREAM_H__ + +#include + +#include + +G_BEGIN_DECLS + +typedef enum TsMuxStreamType TsMuxStreamType; +typedef enum TsMuxStreamState TsMuxStreamState; +typedef struct TsMuxStreamBuffer TsMuxStreamBuffer; + +typedef void (*TsMuxStreamBufferReleaseFunc) (guint8 *data, void *user_data); + +/* Stream type assignments + * + * 0x00 ITU-T | ISO/IEC Reserved + * 0x01 ISO/IEC 11172 Video + * 0x02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or + * ISO/IEC 11172-2 constrained parameter video + * stream + * 0x03 ISO/IEC 11172 Audio + * 0x04 ISO/IEC 13818-3 Audio + * 0x05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 + * private_sections + * 0x06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES + * packets containing private data + * 0x07 ISO/IEC 13522 MHEG + * 0x08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A + * DSM CC + * 0x09 ITU-T Rec. H.222.1 + * 0x0A ISO/IEC 13818-6 type A + * 0x0B ISO/IEC 13818-6 type B + * 0x0C ISO/IEC 13818-6 type C + * 0x0D ISO/IEC 13818-6 type D + * 0x0E ISO/IEC 13818-1 auxiliary + * 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved + * 0x80-0xFF User Private + */ +enum TsMuxStreamType { + TSMUX_ST_RESERVED = 0x00, + TSMUX_ST_VIDEO_MPEG1 = 0x01, + TSMUX_ST_VIDEO_MPEG2 = 0x02, + TSMUX_ST_AUDIO_MPEG1 = 0x03, + TSMUX_ST_AUDIO_MPEG2 = 0x04, + TSMUX_ST_PRIVATE_SECTIONS = 0x05, + TSMUX_ST_PRIVATE_DATA = 0x06, + TSMUX_ST_MHEG = 0x07, + TSMUX_ST_DSMCC = 0x08, + TSMUX_ST_H222_1 = 0x09, + + /* later extensions */ + TSMUX_ST_AUDIO_AAC = 0x0f, + TSMUX_ST_VIDEO_MPEG4 = 0x10, + TSMUX_ST_VIDEO_H264 = 0x1b, + + /* private stream types */ + TSMUX_ST_PS_AUDIO_AC3 = 0x81, + TSMUX_ST_PS_AUDIO_DTS = 0x8a, + TSMUX_ST_PS_AUDIO_LPCM = 0x8b, + TSMUX_ST_PS_DVD_SUBPICTURE = 0xff, + + /* Non-standard definitions */ + TSMUX_ST_VIDEO_DIRAC = 0xD1 +}; + +enum TsMuxStreamState { + TSMUX_STREAM_STATE_HEADER, + TSMUX_STREAM_STATE_PACKET +}; + +/* TsMuxStream receives elementary streams for parsing. + * Via the write_bytes() method, it can output a PES stream piecemeal */ +struct TsMuxStream { + TsMuxStreamState state; + TsMuxPacketInfo pi; + TsMuxStreamType stream_type; + guint8 id; /* stream id */ + guint8 id_extended; /* extended stream id (13818-1 Amdt 2) */ + + gboolean is_video_stream; + + /* List of data buffers available for writing out */ + GList *buffers; + guint32 bytes_avail; + + /* Current data buffer being consumed */ + TsMuxStreamBuffer *cur_buffer; + guint32 cur_buffer_consumed; + + TsMuxStreamBufferReleaseFunc buffer_release; + + guint16 pes_payload_size; + guint16 cur_pes_payload_size; + guint16 pes_bytes_written; + + /* PTS/DTS to write if the flags in the packet info are set */ + gint64 pts; + gint64 dts; + + gint64 last_pts; + gint64 last_dts; + + gint pcr_ref; + gint64 last_pcr; +}; + +/* stream management */ +TsMuxStream * tsmux_stream_new (guint16 pid, TsMuxStreamType stream_type); +void tsmux_stream_free (TsMuxStream *stream); + +guint16 tsmux_stream_get_pid (TsMuxStream *stream); + +void tsmux_stream_set_buffer_release_func (TsMuxStream *stream, + TsMuxStreamBufferReleaseFunc func); + +/* Add a new buffer to the pool of available bytes. If pts or dts are not -1, they + * indicate the PTS or DTS of the first access unit within this packet */ +void tsmux_stream_add_data (TsMuxStream *stream, guint8 *data, guint len, + void *user_data, gint64 pts, gint64 dts); + +void tsmux_stream_pcr_ref (TsMuxStream *stream); +void tsmux_stream_pcr_unref (TsMuxStream *stream); +gboolean tsmux_stream_is_pcr (TsMuxStream *stream); + +gboolean tsmux_stream_at_pes_start (TsMuxStream *stream); +void tsmux_stream_get_es_descrs (TsMuxStream *stream, guint8 *buf, guint16 *len); + +gint tsmux_stream_bytes_in_buffer (TsMuxStream *stream); +gint tsmux_stream_bytes_avail (TsMuxStream *stream); +gboolean tsmux_stream_get_data (TsMuxStream *stream, guint8 *buf, guint len); + +guint64 tsmux_stream_get_pts (TsMuxStream *stream); + +G_END_DECLS + +#endif