summaryrefslogtreecommitdiffstats
path: root/gst/mpegtsmux/mpegtsmux.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/mpegtsmux/mpegtsmux.c')
-rw-r--r--gst/mpegtsmux/mpegtsmux.c821
1 files changed, 821 insertions, 0 deletions
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);