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