gstreamer/gst/rtpmanager/gstrtpmux.c
Sjoerd Simons 0ee384b251 rtpmux: Don't set PROXY_CAPS flag on the src pad
rtpmux behaves like a funnel in that it forwards whatever upstream is
sending buffers. So setting proxy caps doesn't make sense as the
upstream don't have to have compatible caps, thus resulting in an empty
caps set as a result of a caps query. Instead set fixed caps just
as funnel does.

https://bugzilla.gnome.org/show_bug.cgi?id=738722
2014-10-21 10:52:00 +02:00

883 lines
23 KiB
C

/* RTP muxer element for GStreamer
*
* gstrtpmux.c:
*
* Copyright (C) <2007-2010> Nokia Corporation.
* Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
* Copyright (C) <2007-2010> Collabora Ltd
* Contact: Olivier Crete <olivier.crete@collabora.co.uk>
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000,2005 Wim Taymans <wim@fluendo.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-rtpmux
* @see_also: rtpdtmfmux
*
* The rtp muxer takes multiple RTP streams having the same clock-rate and
* muxes into a single stream with a single SSRC.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
* gst-launch rtpmux name=mux ! udpsink host=127.0.0.1 port=8888 \
* alsasrc ! alawenc ! rtppcmapay ! \
* application/x-rtp, payload=8, rate=8000 ! mux.sink_0 \
* audiotestsrc is-live=1 ! \
* mulawenc ! rtppcmupay ! \
* application/x-rtp, payload=0, rate=8000 ! mux.sink_1
* ]|
* In this example, an audio stream is captured from ALSA and another is
* generated, both are encoded into different payload types and muxed together
* so they can be sent on the same port.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/rtp/gstrtpbuffer.h>
#include <string.h>
#include "gstrtpmux.h"
GST_DEBUG_CATEGORY_STATIC (gst_rtp_mux_debug);
#define GST_CAT_DEFAULT gst_rtp_mux_debug
enum
{
ARG_0,
PROP_TIMESTAMP_OFFSET,
PROP_SEQNUM_OFFSET,
PROP_SEQNUM,
PROP_SSRC
};
#define DEFAULT_TIMESTAMP_OFFSET -1
#define DEFAULT_SEQNUM_OFFSET -1
#define DEFAULT_SSRC -1
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp")
);
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS ("application/x-rtp")
);
static GstPad *gst_rtp_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
static void gst_rtp_mux_release_pad (GstElement * element, GstPad * pad);
static GstFlowReturn gst_rtp_mux_chain (GstPad * pad, GstObject * parent,
GstBuffer * buffer);
static GstFlowReturn gst_rtp_mux_chain_list (GstPad * pad, GstObject * parent,
GstBufferList * bufferlist);
static gboolean gst_rtp_mux_setcaps (GstPad * pad, GstRTPMux * rtp_mux,
GstCaps * caps);
static gboolean gst_rtp_mux_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_rtp_mux_sink_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static GstStateChangeReturn gst_rtp_mux_change_state (GstElement *
element, GstStateChange transition);
static void gst_rtp_mux_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_rtp_mux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_rtp_mux_dispose (GObject * object);
static gboolean gst_rtp_mux_src_event_real (GstRTPMux * rtp_mux,
GstEvent * event);
G_DEFINE_TYPE (GstRTPMux, gst_rtp_mux, GST_TYPE_ELEMENT);
static void
gst_rtp_mux_class_init (GstRTPMuxClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sink_factory));
gst_element_class_set_static_metadata (gstelement_class, "RTP muxer",
"Codec/Muxer",
"multiplex N rtp streams into one", "Zeeshan Ali <first.last@nokia.com>");
gobject_class->get_property = gst_rtp_mux_get_property;
gobject_class->set_property = gst_rtp_mux_set_property;
gobject_class->dispose = gst_rtp_mux_dispose;
klass->src_event = gst_rtp_mux_src_event_real;
g_object_class_install_property (G_OBJECT_CLASS (klass),
PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset",
"Timestamp Offset",
"Offset to add to all outgoing timestamps (-1 = random)", -1,
G_MAXINT, DEFAULT_TIMESTAMP_OFFSET,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM_OFFSET,
g_param_spec_int ("seqnum-offset", "Sequence number Offset",
"Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXINT,
DEFAULT_SEQNUM_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM,
g_param_spec_uint ("seqnum", "Sequence number",
"The RTP sequence number of the last processed packet",
0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSRC,
g_param_spec_uint ("ssrc", "SSRC",
"The SSRC of the packets (-1 == random)",
0, G_MAXUINT, DEFAULT_SSRC,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_rtp_mux_request_new_pad);
gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_mux_release_pad);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_mux_change_state);
}
static void
gst_rtp_mux_dispose (GObject * object)
{
GstRTPMux *rtp_mux = GST_RTP_MUX (object);
GList *item;
g_clear_object (&rtp_mux->last_pad);
restart:
for (item = GST_ELEMENT_PADS (object); item; item = g_list_next (item)) {
GstPad *pad = GST_PAD (item->data);
if (GST_PAD_IS_SINK (pad)) {
gst_element_release_request_pad (GST_ELEMENT (object), pad);
goto restart;
}
}
G_OBJECT_CLASS (gst_rtp_mux_parent_class)->dispose (object);
}
static gboolean
gst_rtp_mux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstRTPMux *rtp_mux = GST_RTP_MUX (parent);
GstRTPMuxClass *klass;
gboolean ret;
klass = GST_RTP_MUX_GET_CLASS (rtp_mux);
ret = klass->src_event (rtp_mux, event);
return ret;
}
static gboolean
gst_rtp_mux_src_event_real (GstRTPMux * rtp_mux, GstEvent * event)
{
GstIterator *iter;
gboolean result = FALSE;
gboolean done = FALSE;
iter = gst_element_iterate_sink_pads (GST_ELEMENT (rtp_mux));
while (!done) {
GValue item = { 0, };
switch (gst_iterator_next (iter, &item)) {
case GST_ITERATOR_OK:
gst_event_ref (event);
result |= gst_pad_push_event (g_value_get_object (&item), event);
g_value_reset (&item);
break;
case GST_ITERATOR_RESYNC:
gst_iterator_resync (iter);
result = FALSE;
break;
case GST_ITERATOR_ERROR:
GST_WARNING_OBJECT (rtp_mux, "Error iterating sinkpads");
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
gst_iterator_free (iter);
gst_event_unref (event);
return result;
}
static void
gst_rtp_mux_init (GstRTPMux * rtp_mux)
{
GstElementClass *klass = GST_ELEMENT_GET_CLASS (rtp_mux);
rtp_mux->srcpad =
gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
"src"), "src");
gst_pad_set_event_function (rtp_mux->srcpad,
GST_DEBUG_FUNCPTR (gst_rtp_mux_src_event));
gst_pad_use_fixed_caps (rtp_mux->srcpad);
gst_element_add_pad (GST_ELEMENT (rtp_mux), rtp_mux->srcpad);
rtp_mux->ssrc = DEFAULT_SSRC;
rtp_mux->ts_offset = DEFAULT_TIMESTAMP_OFFSET;
rtp_mux->seqnum_offset = DEFAULT_SEQNUM_OFFSET;
rtp_mux->last_stop = GST_CLOCK_TIME_NONE;
}
static void
gst_rtp_mux_setup_sinkpad (GstRTPMux * rtp_mux, GstPad * sinkpad)
{
GstRTPMuxPadPrivate *padpriv = g_slice_new0 (GstRTPMuxPadPrivate);
/* setup some pad functions */
gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_rtp_mux_chain));
gst_pad_set_chain_list_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_rtp_mux_chain_list));
gst_pad_set_event_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_rtp_mux_sink_event));
gst_pad_set_query_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_rtp_mux_sink_query));
gst_segment_init (&padpriv->segment, GST_FORMAT_UNDEFINED);
gst_pad_set_element_private (sinkpad, padpriv);
gst_pad_set_active (sinkpad, TRUE);
gst_element_add_pad (GST_ELEMENT (rtp_mux), sinkpad);
}
static GstPad *
gst_rtp_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
{
GstRTPMux *rtp_mux;
GstPad *newpad;
g_return_val_if_fail (templ != NULL, NULL);
g_return_val_if_fail (GST_IS_RTP_MUX (element), NULL);
rtp_mux = GST_RTP_MUX (element);
if (templ->direction != GST_PAD_SINK) {
GST_WARNING_OBJECT (rtp_mux, "request pad that is not a SINK pad");
return NULL;
}
newpad = gst_pad_new_from_template (templ, req_name);
if (newpad)
gst_rtp_mux_setup_sinkpad (rtp_mux, newpad);
else
GST_WARNING_OBJECT (rtp_mux, "failed to create request pad");
return newpad;
}
static void
gst_rtp_mux_release_pad (GstElement * element, GstPad * pad)
{
GstRTPMuxPadPrivate *padpriv;
GST_OBJECT_LOCK (element);
padpriv = gst_pad_get_element_private (pad);
gst_pad_set_element_private (pad, NULL);
GST_OBJECT_UNLOCK (element);
gst_element_remove_pad (element, pad);
if (padpriv) {
g_slice_free (GstRTPMuxPadPrivate, padpriv);
}
}
/* Put our own timestamp-offset on the buffer */
static void
gst_rtp_mux_readjust_rtp_timestamp_locked (GstRTPMux * rtp_mux,
GstRTPMuxPadPrivate * padpriv, GstRTPBuffer * rtpbuffer)
{
guint32 ts;
guint32 sink_ts_base = 0;
if (padpriv && padpriv->have_timestamp_offset)
sink_ts_base = padpriv->timestamp_offset;
ts = gst_rtp_buffer_get_timestamp (rtpbuffer) - sink_ts_base +
rtp_mux->ts_base;
GST_LOG_OBJECT (rtp_mux, "Re-adjusting RTP ts %u to %u",
gst_rtp_buffer_get_timestamp (rtpbuffer), ts);
gst_rtp_buffer_set_timestamp (rtpbuffer, ts);
}
static gboolean
process_buffer_locked (GstRTPMux * rtp_mux, GstRTPMuxPadPrivate * padpriv,
GstRTPBuffer * rtpbuffer)
{
GstRTPMuxClass *klass = GST_RTP_MUX_GET_CLASS (rtp_mux);
if (klass->accept_buffer_locked)
if (!klass->accept_buffer_locked (rtp_mux, padpriv, rtpbuffer))
return FALSE;
rtp_mux->seqnum++;
gst_rtp_buffer_set_seq (rtpbuffer, rtp_mux->seqnum);
gst_rtp_buffer_set_ssrc (rtpbuffer, rtp_mux->current_ssrc);
gst_rtp_mux_readjust_rtp_timestamp_locked (rtp_mux, padpriv, rtpbuffer);
GST_LOG_OBJECT (rtp_mux,
"Pushing packet size %" G_GSIZE_FORMAT ", seq=%d, ts=%u",
rtpbuffer->map[0].size, rtp_mux->seqnum,
gst_rtp_buffer_get_timestamp (rtpbuffer));
if (padpriv) {
if (padpriv->segment.format == GST_FORMAT_TIME)
GST_BUFFER_PTS (rtpbuffer->buffer) =
gst_segment_to_running_time (&padpriv->segment, GST_FORMAT_TIME,
GST_BUFFER_PTS (rtpbuffer->buffer));
}
return TRUE;
}
struct BufferListData
{
GstRTPMux *rtp_mux;
GstRTPMuxPadPrivate *padpriv;
gboolean drop;
};
static gboolean
process_list_item (GstBuffer ** buffer, guint idx, gpointer user_data)
{
struct BufferListData *bd = user_data;
GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
*buffer = gst_buffer_make_writable (*buffer);
gst_rtp_buffer_map (*buffer, GST_MAP_READWRITE, &rtpbuffer);
bd->drop = !process_buffer_locked (bd->rtp_mux, bd->padpriv, &rtpbuffer);
gst_rtp_buffer_unmap (&rtpbuffer);
if (bd->drop)
return FALSE;
if (GST_BUFFER_DURATION_IS_VALID (*buffer) &&
GST_BUFFER_TIMESTAMP_IS_VALID (*buffer))
bd->rtp_mux->last_stop = GST_BUFFER_TIMESTAMP (*buffer) +
GST_BUFFER_DURATION (*buffer);
else
bd->rtp_mux->last_stop = GST_CLOCK_TIME_NONE;
return TRUE;
}
static GstFlowReturn
gst_rtp_mux_chain_list (GstPad * pad, GstObject * parent,
GstBufferList * bufferlist)
{
GstRTPMux *rtp_mux;
GstFlowReturn ret;
GstRTPMuxPadPrivate *padpriv;
struct BufferListData bd;
rtp_mux = GST_RTP_MUX (parent);
GST_OBJECT_LOCK (rtp_mux);
padpriv = gst_pad_get_element_private (pad);
if (!padpriv) {
GST_OBJECT_UNLOCK (rtp_mux);
ret = GST_FLOW_NOT_LINKED;
gst_buffer_list_unref (bufferlist);
goto out;
}
bd.rtp_mux = rtp_mux;
bd.padpriv = padpriv;
bd.drop = FALSE;
bufferlist = gst_buffer_list_make_writable (bufferlist);
gst_buffer_list_foreach (bufferlist, process_list_item, &bd);
GST_OBJECT_UNLOCK (rtp_mux);
if (bd.drop) {
gst_buffer_list_unref (bufferlist);
ret = GST_FLOW_OK;
} else {
ret = gst_pad_push_list (rtp_mux->srcpad, bufferlist);
}
out:
return ret;
}
static gboolean
resend_events (GstPad * pad, GstEvent ** event, gpointer user_data)
{
GstRTPMux *rtp_mux = user_data;
if (GST_EVENT_TYPE (*event) == GST_EVENT_CAPS) {
GstCaps *caps;
gst_event_parse_caps (*event, &caps);
gst_rtp_mux_setcaps (pad, rtp_mux, caps);
} else {
gst_pad_push_event (rtp_mux->srcpad, gst_event_ref (*event));
}
return TRUE;
}
static GstFlowReturn
gst_rtp_mux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
GstRTPMux *rtp_mux;
GstFlowReturn ret;
GstRTPMuxPadPrivate *padpriv;
gboolean drop;
gboolean changed = FALSE;
GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
rtp_mux = GST_RTP_MUX (GST_OBJECT_PARENT (pad));
GST_OBJECT_LOCK (rtp_mux);
padpriv = gst_pad_get_element_private (pad);
if (!padpriv) {
GST_OBJECT_UNLOCK (rtp_mux);
gst_buffer_unref (buffer);
return GST_FLOW_NOT_LINKED;
}
buffer = gst_buffer_make_writable (buffer);
if (!gst_rtp_buffer_map (buffer, GST_MAP_READWRITE, &rtpbuffer)) {
GST_OBJECT_UNLOCK (rtp_mux);
gst_buffer_unref (buffer);
GST_ERROR_OBJECT (rtp_mux, "Invalid RTP buffer");
return GST_FLOW_ERROR;
}
drop = !process_buffer_locked (rtp_mux, padpriv, &rtpbuffer);
gst_rtp_buffer_unmap (&rtpbuffer);
if (!drop) {
if (pad != rtp_mux->last_pad) {
changed = TRUE;
g_clear_object (&rtp_mux->last_pad);
rtp_mux->last_pad = g_object_ref (pad);
}
if (GST_BUFFER_DURATION_IS_VALID (buffer) &&
GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
rtp_mux->last_stop = GST_BUFFER_TIMESTAMP (buffer) +
GST_BUFFER_DURATION (buffer);
else
rtp_mux->last_stop = GST_CLOCK_TIME_NONE;
}
GST_OBJECT_UNLOCK (rtp_mux);
if (changed)
gst_pad_sticky_events_foreach (pad, resend_events, rtp_mux);
if (drop) {
gst_buffer_unref (buffer);
ret = GST_FLOW_OK;
} else {
ret = gst_pad_push (rtp_mux->srcpad, buffer);
}
return ret;
}
static gboolean
gst_rtp_mux_setcaps (GstPad * pad, GstRTPMux * rtp_mux, GstCaps * caps)
{
GstStructure *structure;
gboolean ret = FALSE;
GstRTPMuxPadPrivate *padpriv;
structure = gst_caps_get_structure (caps, 0);
if (!structure)
return FALSE;
GST_OBJECT_LOCK (rtp_mux);
padpriv = gst_pad_get_element_private (pad);
if (padpriv &&
gst_structure_get_uint (structure, "timestamp-offset",
&padpriv->timestamp_offset)) {
padpriv->have_timestamp_offset = TRUE;
}
GST_OBJECT_UNLOCK (rtp_mux);
caps = gst_caps_copy (caps);
gst_caps_set_simple (caps,
"timestamp-offset", G_TYPE_UINT, rtp_mux->ts_base,
"seqnum-offset", G_TYPE_UINT, rtp_mux->seqnum_base, NULL);
if (rtp_mux->send_stream_start) {
gchar s_id[32];
/* stream-start (FIXME: create id based on input ids) */
g_snprintf (s_id, sizeof (s_id), "interleave-%08x", g_random_int ());
gst_pad_push_event (rtp_mux->srcpad, gst_event_new_stream_start (s_id));
rtp_mux->send_stream_start = FALSE;
}
GST_DEBUG_OBJECT (rtp_mux,
"setting caps %" GST_PTR_FORMAT " on src pad..", caps);
ret = gst_pad_set_caps (rtp_mux->srcpad, caps);
gst_structure_get_uint (structure, "ssrc", &rtp_mux->current_ssrc);
gst_caps_unref (caps);
return ret;
}
static void
clear_caps (GstCaps * caps, gboolean only_clock_rate)
{
gint i, j;
/* Lets only match on the clock-rate */
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstStructure *s = gst_caps_get_structure (caps, i);
for (j = 0; j < gst_structure_n_fields (s); j++) {
const gchar *name = gst_structure_nth_field_name (s, j);
if (strcmp (name, "clock-rate") && (only_clock_rate ||
(strcmp (name, "ssrc")))) {
gst_structure_remove_field (s, name);
j--;
}
}
}
}
static gboolean
same_clock_rate_fold (const GValue * item, GValue * ret, gpointer user_data)
{
GstPad *mypad = user_data;
GstPad *pad = g_value_get_object (item);
GstCaps *peercaps;
GstCaps *accumcaps;
GstCaps *intersect;
if (pad == mypad)
return TRUE;
accumcaps = g_value_get_boxed (ret);
peercaps = gst_pad_peer_query_caps (pad, accumcaps);
if (!peercaps) {
g_warning ("no peercaps");
return TRUE;
}
peercaps = gst_caps_make_writable (peercaps);
clear_caps (peercaps, TRUE);
intersect = gst_caps_intersect (accumcaps, peercaps);
g_value_take_boxed (ret, intersect);
gst_caps_unref (peercaps);
return !gst_caps_is_empty (intersect);
}
static GstCaps *
gst_rtp_mux_getcaps (GstPad * pad, GstRTPMux * mux, GstCaps * filter)
{
GstCaps *caps = NULL;
GstIterator *iter = NULL;
GValue v = { 0 };
GstIteratorResult res;
GstCaps *peercaps;
GstCaps *othercaps;
GstCaps *tcaps;
peercaps = gst_pad_peer_query_caps (mux->srcpad, filter);
if (peercaps) {
tcaps = gst_pad_get_pad_template_caps (pad);
othercaps = gst_caps_intersect_full (peercaps, tcaps,
GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (peercaps);
} else {
tcaps = gst_pad_get_pad_template_caps (mux->srcpad);
if (filter)
othercaps = gst_caps_intersect_full (filter, tcaps,
GST_CAPS_INTERSECT_FIRST);
else
othercaps = gst_caps_copy (tcaps);
}
gst_caps_unref (tcaps);
clear_caps (othercaps, FALSE);
g_value_init (&v, GST_TYPE_CAPS);
iter = gst_element_iterate_sink_pads (GST_ELEMENT (mux));
do {
gst_value_set_caps (&v, othercaps);
res = gst_iterator_fold (iter, same_clock_rate_fold, &v, pad);
gst_iterator_resync (iter);
} while (res == GST_ITERATOR_RESYNC);
gst_iterator_free (iter);
caps = (GstCaps *) gst_value_get_caps (&v);
if (res == GST_ITERATOR_ERROR) {
gst_caps_unref (caps);
caps = gst_caps_new_empty ();
}
gst_caps_unref (othercaps);
return caps;
}
static gboolean
gst_rtp_mux_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
GstRTPMux *mux = GST_RTP_MUX (parent);
gboolean res = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CAPS:
{
GstCaps *filter, *caps;
gst_query_parse_caps (query, &filter);
caps = gst_rtp_mux_getcaps (pad, mux, filter);
gst_query_set_caps_result (query, caps);
gst_caps_unref (caps);
res = TRUE;
break;
}
default:
res = gst_pad_query_default (pad, parent, query);
break;
}
return res;
}
static void
gst_rtp_mux_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstRTPMux *rtp_mux;
rtp_mux = GST_RTP_MUX (object);
switch (prop_id) {
case PROP_TIMESTAMP_OFFSET:
g_value_set_int (value, rtp_mux->ts_offset);
break;
case PROP_SEQNUM_OFFSET:
g_value_set_int (value, rtp_mux->seqnum_offset);
break;
case PROP_SEQNUM:
GST_OBJECT_LOCK (rtp_mux);
g_value_set_uint (value, rtp_mux->seqnum);
GST_OBJECT_UNLOCK (rtp_mux);
break;
case PROP_SSRC:
g_value_set_uint (value, rtp_mux->ssrc);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_rtp_mux_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstRTPMux *rtp_mux;
rtp_mux = GST_RTP_MUX (object);
switch (prop_id) {
case PROP_TIMESTAMP_OFFSET:
rtp_mux->ts_offset = g_value_get_int (value);
break;
case PROP_SEQNUM_OFFSET:
rtp_mux->seqnum_offset = g_value_get_int (value);
break;
case PROP_SSRC:
rtp_mux->ssrc = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gst_rtp_mux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstRTPMux *mux = GST_RTP_MUX (parent);
gboolean is_pad;
gboolean ret;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
ret = gst_rtp_mux_setcaps (pad, mux, caps);
gst_event_unref (event);
return ret;
}
case GST_EVENT_FLUSH_STOP:
{
GST_OBJECT_LOCK (mux);
mux->last_stop = GST_CLOCK_TIME_NONE;
GST_OBJECT_UNLOCK (mux);
break;
}
case GST_EVENT_SEGMENT:
{
GstRTPMuxPadPrivate *padpriv;
GST_OBJECT_LOCK (mux);
padpriv = gst_pad_get_element_private (pad);
if (padpriv) {
gst_event_copy_segment (event, &padpriv->segment);
}
GST_OBJECT_UNLOCK (mux);
break;
}
default:
break;
}
GST_OBJECT_LOCK (mux);
is_pad = (pad == mux->last_pad);
GST_OBJECT_UNLOCK (mux);
if (is_pad) {
return gst_pad_push_event (mux->srcpad, event);
} else {
gst_event_unref (event);
return TRUE;
}
}
static void
gst_rtp_mux_ready_to_paused (GstRTPMux * rtp_mux)
{
GST_OBJECT_LOCK (rtp_mux);
g_clear_object (&rtp_mux->last_pad);
rtp_mux->send_stream_start = TRUE;
if (rtp_mux->ssrc == -1)
rtp_mux->current_ssrc = g_random_int ();
else
rtp_mux->current_ssrc = rtp_mux->ssrc;
if (rtp_mux->seqnum_offset == -1)
rtp_mux->seqnum_base = g_random_int_range (0, G_MAXUINT16);
else
rtp_mux->seqnum_base = rtp_mux->seqnum_offset;
rtp_mux->seqnum = rtp_mux->seqnum_base;
if (rtp_mux->ts_offset == -1)
rtp_mux->ts_base = g_random_int ();
else
rtp_mux->ts_base = rtp_mux->ts_offset;
rtp_mux->last_stop = GST_CLOCK_TIME_NONE;
GST_DEBUG_OBJECT (rtp_mux, "set timestamp-offset to %u", rtp_mux->ts_base);
GST_OBJECT_UNLOCK (rtp_mux);
}
static GstStateChangeReturn
gst_rtp_mux_change_state (GstElement * element, GstStateChange transition)
{
GstRTPMux *rtp_mux;
GstStateChangeReturn ret;
rtp_mux = GST_RTP_MUX (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_rtp_mux_ready_to_paused (rtp_mux);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (gst_rtp_mux_parent_class)->change_state (element,
transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
g_clear_object (&rtp_mux->last_pad);
break;
default:
break;
}
return ret;
}
gboolean
gst_rtp_mux_plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_rtp_mux_debug, "rtpmux", 0, "rtp muxer");
return gst_element_register (plugin, "rtpmux", GST_RANK_NONE,
GST_TYPE_RTP_MUX);
}