| /* GStreamer |
| * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> |
| * |
| * RTP SSRC demuxer |
| * |
| * 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-rtpssrcdemux |
| * |
| * rtpssrcdemux acts as a demuxer for RTP packets based on the SSRC of the |
| * packets. Its main purpose is to allow an application to easily receive and |
| * decode an RTP stream with multiple SSRCs. |
| * |
| * For each SSRC that is detected, a new pad will be created and the |
| * #GstRtpSsrcDemux::new-ssrc-pad signal will be emitted. |
| * |
| * <refsect2> |
| * <title>Example pipelines</title> |
| * |[ |
| * gst-launch-1.0 udpsrc caps="application/x-rtp" ! rtpssrcdemux ! fakesink |
| * ]| Takes an RTP stream and send the RTP packets with the first detected SSRC |
| * to fakesink, discarding the other SSRCs. |
| * </refsect2> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| #include <gst/rtp/gstrtpbuffer.h> |
| #include <gst/rtp/gstrtcpbuffer.h> |
| |
| #include "gstrtpssrcdemux.h" |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_rtp_ssrc_demux_debug); |
| #define GST_CAT_DEFAULT gst_rtp_ssrc_demux_debug |
| |
| /* generic templates */ |
| static GstStaticPadTemplate rtp_ssrc_demux_sink_template = |
| GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("application/x-rtp") |
| ); |
| |
| static GstStaticPadTemplate rtp_ssrc_demux_rtcp_sink_template = |
| GST_STATIC_PAD_TEMPLATE ("rtcp_sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("application/x-rtcp") |
| ); |
| |
| static GstStaticPadTemplate rtp_ssrc_demux_src_template = |
| GST_STATIC_PAD_TEMPLATE ("src_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS ("application/x-rtp") |
| ); |
| |
| static GstStaticPadTemplate rtp_ssrc_demux_rtcp_src_template = |
| GST_STATIC_PAD_TEMPLATE ("rtcp_src_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS ("application/x-rtcp") |
| ); |
| |
| #define GST_PAD_LOCK(obj) (g_rec_mutex_lock (&(obj)->padlock)) |
| #define GST_PAD_UNLOCK(obj) (g_rec_mutex_unlock (&(obj)->padlock)) |
| |
| typedef enum |
| { |
| RTP_PAD, |
| RTCP_PAD |
| } PadType; |
| |
| /* signals */ |
| enum |
| { |
| SIGNAL_NEW_SSRC_PAD, |
| SIGNAL_REMOVED_SSRC_PAD, |
| SIGNAL_CLEAR_SSRC, |
| LAST_SIGNAL |
| }; |
| |
| #define gst_rtp_ssrc_demux_parent_class parent_class |
| G_DEFINE_TYPE (GstRtpSsrcDemux, gst_rtp_ssrc_demux, GST_TYPE_ELEMENT); |
| |
| /* GObject vmethods */ |
| static void gst_rtp_ssrc_demux_dispose (GObject * object); |
| static void gst_rtp_ssrc_demux_finalize (GObject * object); |
| |
| /* GstElement vmethods */ |
| static GstStateChangeReturn gst_rtp_ssrc_demux_change_state (GstElement * |
| element, GstStateChange transition); |
| |
| static void gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux, |
| guint32 ssrc); |
| |
| /* sinkpad stuff */ |
| static GstFlowReturn gst_rtp_ssrc_demux_chain (GstPad * pad, GstObject * parent, |
| GstBuffer * buf); |
| static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstObject * parent, |
| GstEvent * event); |
| |
| static GstFlowReturn gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, |
| GstObject * parent, GstBuffer * buf); |
| static GstIterator *gst_rtp_ssrc_demux_iterate_internal_links_sink (GstPad * |
| pad, GstObject * parent); |
| |
| /* srcpad stuff */ |
| static gboolean gst_rtp_ssrc_demux_src_event (GstPad * pad, GstObject * parent, |
| GstEvent * event); |
| static GstIterator *gst_rtp_ssrc_demux_iterate_internal_links_src (GstPad * pad, |
| GstObject * parent); |
| static gboolean gst_rtp_ssrc_demux_src_query (GstPad * pad, GstObject * parent, |
| GstQuery * query); |
| |
| static guint gst_rtp_ssrc_demux_signals[LAST_SIGNAL] = { 0 }; |
| |
| /* |
| * Item for storing GstPad <-> SSRC pairs. |
| */ |
| struct _GstRtpSsrcDemuxPad |
| { |
| guint32 ssrc; |
| GstPad *rtp_pad; |
| GstCaps *caps; |
| GstPad *rtcp_pad; |
| |
| gboolean pushed_initial_rtp_events; |
| gboolean pushed_initial_rtcp_events; |
| }; |
| |
| /* find a src pad for a given SSRC, returns NULL if the SSRC was not found |
| */ |
| static GstRtpSsrcDemuxPad * |
| find_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) |
| { |
| GSList *walk; |
| |
| for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { |
| GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data; |
| |
| if (pad->ssrc == ssrc) |
| return pad; |
| } |
| return NULL; |
| } |
| |
| static GstEvent * |
| add_ssrc_and_ref (GstEvent * event, guint32 ssrc) |
| { |
| /* Set the ssrc on the output caps */ |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_CAPS: |
| { |
| GstCaps *caps; |
| GstCaps *newcaps; |
| GstStructure *s; |
| |
| gst_event_parse_caps (event, &caps); |
| newcaps = gst_caps_copy (caps); |
| |
| s = gst_caps_get_structure (newcaps, 0); |
| gst_structure_set (s, "ssrc", G_TYPE_UINT, ssrc, NULL); |
| event = gst_event_new_caps (newcaps); |
| gst_caps_unref (newcaps); |
| break; |
| } |
| default: |
| gst_event_ref (event); |
| break; |
| } |
| |
| return event; |
| } |
| |
| struct ForwardStickyEventData |
| { |
| GstPad *pad; |
| guint32 ssrc; |
| }; |
| |
| static gboolean |
| forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data) |
| { |
| struct ForwardStickyEventData *data = user_data; |
| GstEvent *newevent; |
| |
| newevent = add_ssrc_and_ref (*event, data->ssrc); |
| |
| gst_pad_push_event (data->pad, newevent); |
| |
| return TRUE; |
| } |
| |
| static void |
| forward_initial_events (GstRtpSsrcDemux * demux, guint32 ssrc, GstPad * pad, |
| PadType padtype) |
| { |
| struct ForwardStickyEventData fdata; |
| GstPad *sinkpad = NULL; |
| |
| if (padtype == RTP_PAD) |
| sinkpad = demux->rtp_sink; |
| else if (padtype == RTCP_PAD) |
| sinkpad = demux->rtcp_sink; |
| else |
| g_assert_not_reached (); |
| |
| fdata.ssrc = ssrc; |
| fdata.pad = pad; |
| |
| gst_pad_sticky_events_foreach (sinkpad, forward_sticky_events, &fdata); |
| } |
| |
| static GstPad * |
| find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc, |
| PadType padtype) |
| { |
| GstPad *rtp_pad, *rtcp_pad; |
| GstElementClass *klass; |
| GstPadTemplate *templ; |
| gchar *padname; |
| GstRtpSsrcDemuxPad *demuxpad; |
| GstPad *retpad; |
| gulong rtp_block, rtcp_block; |
| |
| GST_PAD_LOCK (demux); |
| |
| demuxpad = find_demux_pad_for_ssrc (demux, ssrc); |
| if (demuxpad != NULL) { |
| gboolean forward = FALSE; |
| |
| switch (padtype) { |
| case RTP_PAD: |
| retpad = gst_object_ref (demuxpad->rtp_pad); |
| if (!demuxpad->pushed_initial_rtp_events) { |
| forward = TRUE; |
| demuxpad->pushed_initial_rtp_events = TRUE; |
| } |
| break; |
| case RTCP_PAD: |
| retpad = gst_object_ref (demuxpad->rtcp_pad); |
| if (!demuxpad->pushed_initial_rtcp_events) { |
| forward = TRUE; |
| demuxpad->pushed_initial_rtcp_events = TRUE; |
| } |
| break; |
| default: |
| retpad = NULL; |
| g_assert_not_reached (); |
| } |
| |
| GST_PAD_UNLOCK (demux); |
| |
| if (forward) |
| forward_initial_events (demux, ssrc, retpad, padtype); |
| return retpad; |
| } |
| |
| GST_DEBUG_OBJECT (demux, "creating new pad for SSRC %08x", ssrc); |
| |
| klass = GST_ELEMENT_GET_CLASS (demux); |
| templ = gst_element_class_get_pad_template (klass, "src_%u"); |
| padname = g_strdup_printf ("src_%u", ssrc); |
| rtp_pad = gst_pad_new_from_template (templ, padname); |
| g_free (padname); |
| |
| templ = gst_element_class_get_pad_template (klass, "rtcp_src_%u"); |
| padname = g_strdup_printf ("rtcp_src_%u", ssrc); |
| rtcp_pad = gst_pad_new_from_template (templ, padname); |
| g_free (padname); |
| |
| /* wrap in structure and add to list */ |
| demuxpad = g_new0 (GstRtpSsrcDemuxPad, 1); |
| demuxpad->ssrc = ssrc; |
| demuxpad->rtp_pad = rtp_pad; |
| demuxpad->rtcp_pad = rtcp_pad; |
| |
| gst_pad_set_element_private (rtp_pad, demuxpad); |
| gst_pad_set_element_private (rtcp_pad, demuxpad); |
| |
| demux->srcpads = g_slist_prepend (demux->srcpads, demuxpad); |
| |
| gst_pad_set_query_function (rtp_pad, gst_rtp_ssrc_demux_src_query); |
| gst_pad_set_iterate_internal_links_function (rtp_pad, |
| gst_rtp_ssrc_demux_iterate_internal_links_src); |
| gst_pad_set_event_function (rtp_pad, gst_rtp_ssrc_demux_src_event); |
| gst_pad_use_fixed_caps (rtp_pad); |
| gst_pad_set_active (rtp_pad, TRUE); |
| |
| gst_pad_set_event_function (rtcp_pad, gst_rtp_ssrc_demux_src_event); |
| gst_pad_set_iterate_internal_links_function (rtcp_pad, |
| gst_rtp_ssrc_demux_iterate_internal_links_src); |
| gst_pad_use_fixed_caps (rtcp_pad); |
| gst_pad_set_active (rtcp_pad, TRUE); |
| |
| if (padtype == RTP_PAD) { |
| demuxpad->pushed_initial_rtp_events = TRUE; |
| forward_initial_events (demux, ssrc, rtp_pad, padtype); |
| } else if (padtype == RTCP_PAD) { |
| demuxpad->pushed_initial_rtcp_events = TRUE; |
| forward_initial_events (demux, ssrc, rtcp_pad, padtype); |
| } else { |
| g_assert_not_reached (); |
| } |
| |
| gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad); |
| gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad); |
| |
| switch (padtype) { |
| case RTP_PAD: |
| retpad = gst_object_ref (demuxpad->rtp_pad); |
| break; |
| case RTCP_PAD: |
| retpad = gst_object_ref (demuxpad->rtcp_pad); |
| break; |
| default: |
| retpad = NULL; |
| g_assert_not_reached (); |
| } |
| |
| gst_object_ref (rtp_pad); |
| gst_object_ref (rtcp_pad); |
| |
| rtp_block = gst_pad_add_probe (rtp_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, |
| NULL, NULL, NULL); |
| rtcp_block = gst_pad_add_probe (rtcp_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, |
| NULL, NULL, NULL); |
| |
| GST_PAD_UNLOCK (demux); |
| |
| g_signal_emit (G_OBJECT (demux), |
| gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD], 0, ssrc, rtp_pad); |
| |
| gst_pad_remove_probe (rtp_pad, rtp_block); |
| gst_pad_remove_probe (rtcp_pad, rtcp_block); |
| |
| gst_object_unref (rtp_pad); |
| gst_object_unref (rtcp_pad); |
| |
| return retpad; |
| } |
| |
| static void |
| gst_rtp_ssrc_demux_class_init (GstRtpSsrcDemuxClass * klass) |
| { |
| GObjectClass *gobject_klass; |
| GstElementClass *gstelement_klass; |
| GstRtpSsrcDemuxClass *gstrtpssrcdemux_klass; |
| |
| gobject_klass = (GObjectClass *) klass; |
| gstelement_klass = (GstElementClass *) klass; |
| gstrtpssrcdemux_klass = (GstRtpSsrcDemuxClass *) klass; |
| |
| gobject_klass->dispose = gst_rtp_ssrc_demux_dispose; |
| gobject_klass->finalize = gst_rtp_ssrc_demux_finalize; |
| |
| /** |
| * GstRtpSsrcDemux::new-ssrc-pad: |
| * @demux: the object which received the signal |
| * @ssrc: the SSRC of the pad |
| * @pad: the new pad. |
| * |
| * Emited when a new SSRC pad has been created. |
| */ |
| gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD] = |
| g_signal_new ("new-ssrc-pad", |
| G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, |
| G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, new_ssrc_pad), |
| NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, |
| GST_TYPE_PAD); |
| |
| /** |
| * GstRtpSsrcDemux::removed-ssrc-pad: |
| * @demux: the object which received the signal |
| * @ssrc: the SSRC of the pad |
| * @pad: the removed pad. |
| * |
| * Emited when a SSRC pad has been removed. |
| */ |
| gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD] = |
| g_signal_new ("removed-ssrc-pad", |
| G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, |
| G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, removed_ssrc_pad), |
| NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, |
| GST_TYPE_PAD); |
| |
| /** |
| * GstRtpSsrcDemux::clear-ssrc: |
| * @demux: the object which received the signal |
| * @ssrc: the SSRC of the pad |
| * |
| * Action signal to remove the pad for SSRC. |
| */ |
| gst_rtp_ssrc_demux_signals[SIGNAL_CLEAR_SSRC] = |
| g_signal_new ("clear-ssrc", |
| G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, |
| G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, clear_ssrc), |
| NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT); |
| |
| gstelement_klass->change_state = |
| GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_change_state); |
| gstrtpssrcdemux_klass->clear_ssrc = |
| GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_clear_ssrc); |
| |
| gst_element_class_add_pad_template (gstelement_klass, |
| gst_static_pad_template_get (&rtp_ssrc_demux_sink_template)); |
| gst_element_class_add_pad_template (gstelement_klass, |
| gst_static_pad_template_get (&rtp_ssrc_demux_rtcp_sink_template)); |
| gst_element_class_add_pad_template (gstelement_klass, |
| gst_static_pad_template_get (&rtp_ssrc_demux_src_template)); |
| gst_element_class_add_pad_template (gstelement_klass, |
| gst_static_pad_template_get (&rtp_ssrc_demux_rtcp_src_template)); |
| |
| gst_element_class_set_static_metadata (gstelement_klass, "RTP SSRC Demux", |
| "Demux/Network/RTP", |
| "Splits RTP streams based on the SSRC", |
| "Wim Taymans <wim.taymans@gmail.com>"); |
| |
| GST_DEBUG_CATEGORY_INIT (gst_rtp_ssrc_demux_debug, |
| "rtpssrcdemux", 0, "RTP SSRC demuxer"); |
| } |
| |
| static void |
| gst_rtp_ssrc_demux_init (GstRtpSsrcDemux * demux) |
| { |
| GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux); |
| |
| demux->rtp_sink = |
| gst_pad_new_from_template (gst_element_class_get_pad_template (klass, |
| "sink"), "sink"); |
| gst_pad_set_chain_function (demux->rtp_sink, gst_rtp_ssrc_demux_chain); |
| gst_pad_set_event_function (demux->rtp_sink, gst_rtp_ssrc_demux_sink_event); |
| gst_pad_set_iterate_internal_links_function (demux->rtp_sink, |
| gst_rtp_ssrc_demux_iterate_internal_links_sink); |
| gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtp_sink); |
| |
| demux->rtcp_sink = |
| gst_pad_new_from_template (gst_element_class_get_pad_template (klass, |
| "rtcp_sink"), "rtcp_sink"); |
| gst_pad_set_chain_function (demux->rtcp_sink, gst_rtp_ssrc_demux_rtcp_chain); |
| gst_pad_set_event_function (demux->rtcp_sink, gst_rtp_ssrc_demux_sink_event); |
| gst_pad_set_iterate_internal_links_function (demux->rtcp_sink, |
| gst_rtp_ssrc_demux_iterate_internal_links_sink); |
| gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtcp_sink); |
| |
| g_rec_mutex_init (&demux->padlock); |
| } |
| |
| static void |
| gst_rtp_ssrc_demux_reset (GstRtpSsrcDemux * demux) |
| { |
| GSList *walk; |
| |
| for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { |
| GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data; |
| |
| gst_pad_set_active (dpad->rtp_pad, FALSE); |
| gst_pad_set_active (dpad->rtcp_pad, FALSE); |
| |
| gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad); |
| gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad); |
| g_free (dpad); |
| } |
| g_slist_free (demux->srcpads); |
| demux->srcpads = NULL; |
| } |
| |
| static void |
| gst_rtp_ssrc_demux_dispose (GObject * object) |
| { |
| GstRtpSsrcDemux *demux; |
| |
| demux = GST_RTP_SSRC_DEMUX (object); |
| |
| gst_rtp_ssrc_demux_reset (demux); |
| |
| G_OBJECT_CLASS (parent_class)->dispose (object); |
| } |
| |
| static void |
| gst_rtp_ssrc_demux_finalize (GObject * object) |
| { |
| GstRtpSsrcDemux *demux; |
| |
| demux = GST_RTP_SSRC_DEMUX (object); |
| g_rec_mutex_clear (&demux->padlock); |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static void |
| gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) |
| { |
| GstRtpSsrcDemuxPad *dpad; |
| |
| GST_PAD_LOCK (demux); |
| dpad = find_demux_pad_for_ssrc (demux, ssrc); |
| if (dpad == NULL) { |
| GST_PAD_UNLOCK (demux); |
| goto unknown_pad; |
| } |
| |
| GST_DEBUG_OBJECT (demux, "clearing pad for SSRC %08x", ssrc); |
| |
| demux->srcpads = g_slist_remove (demux->srcpads, dpad); |
| GST_PAD_UNLOCK (demux); |
| |
| gst_pad_set_active (dpad->rtp_pad, FALSE); |
| gst_pad_set_active (dpad->rtcp_pad, FALSE); |
| |
| g_signal_emit (G_OBJECT (demux), |
| gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD], 0, ssrc, |
| dpad->rtp_pad); |
| |
| gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad); |
| gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad); |
| |
| g_free (dpad); |
| |
| return; |
| |
| /* ERRORS */ |
| unknown_pad: |
| { |
| GST_WARNING_OBJECT (demux, "unknown SSRC %08x", ssrc); |
| return; |
| } |
| } |
| |
| struct ForwardEventData |
| { |
| GstRtpSsrcDemux *demux; |
| GstEvent *event; |
| gboolean res; |
| GstPad *pad; |
| }; |
| |
| static gboolean |
| forward_event (GstPad * pad, gpointer user_data) |
| { |
| struct ForwardEventData *fdata = user_data; |
| GSList *walk = NULL; |
| GstEvent *newevent = NULL; |
| |
| GST_PAD_LOCK (fdata->demux); |
| for (walk = fdata->demux->srcpads; walk; walk = walk->next) { |
| GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data; |
| |
| /* Only forward the event if the initial events have been through first, |
| * the initial events should be forwarded before any other event |
| * or buffer is pushed */ |
| if ((pad == dpad->rtp_pad && dpad->pushed_initial_rtp_events) || |
| (pad == dpad->rtcp_pad && dpad->pushed_initial_rtcp_events)) { |
| newevent = add_ssrc_and_ref (fdata->event, dpad->ssrc); |
| break; |
| } |
| } |
| GST_PAD_UNLOCK (fdata->demux); |
| |
| if (newevent) |
| fdata->res &= gst_pad_push_event (pad, newevent); |
| |
| return TRUE; |
| } |
| |
| |
| static gboolean |
| gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstObject * parent, |
| GstEvent * event) |
| { |
| GstRtpSsrcDemux *demux; |
| struct ForwardEventData fdata; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| fdata.demux = demux; |
| fdata.pad = pad; |
| fdata.event = event; |
| fdata.res = TRUE; |
| |
| gst_pad_forward (pad, forward_event, &fdata); |
| |
| gst_event_unref (event); |
| |
| return fdata.res; |
| } |
| |
| static GstFlowReturn |
| gst_rtp_ssrc_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| { |
| GstFlowReturn ret; |
| GstRtpSsrcDemux *demux; |
| guint32 ssrc; |
| GstRTPBuffer rtp = { NULL }; |
| GstPad *srcpad; |
| GstRtpSsrcDemuxPad *dpad; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp)) |
| goto invalid_payload; |
| |
| ssrc = gst_rtp_buffer_get_ssrc (&rtp); |
| gst_rtp_buffer_unmap (&rtp); |
| |
| GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc); |
| |
| srcpad = find_or_create_demux_pad_for_ssrc (demux, ssrc, RTP_PAD); |
| if (srcpad == NULL) |
| goto create_failed; |
| |
| /* push to srcpad */ |
| ret = gst_pad_push (srcpad, buf); |
| |
| if (ret != GST_FLOW_OK) { |
| /* check if the ssrc still there, may have been removed */ |
| GST_PAD_LOCK (demux); |
| dpad = find_demux_pad_for_ssrc (demux, ssrc); |
| if (dpad == NULL || dpad->rtp_pad != srcpad) { |
| /* SSRC was removed during the push ... ignore the error */ |
| ret = GST_FLOW_OK; |
| } |
| GST_PAD_UNLOCK (demux); |
| } |
| |
| gst_object_unref (srcpad); |
| |
| return ret; |
| |
| /* ERRORS */ |
| invalid_payload: |
| { |
| /* this is fatal and should be filtered earlier */ |
| GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), |
| ("Dropping invalid RTP payload")); |
| gst_buffer_unref (buf); |
| return GST_FLOW_ERROR; |
| } |
| create_failed: |
| { |
| GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), |
| ("Could not create new pad")); |
| gst_buffer_unref (buf); |
| return GST_FLOW_ERROR; |
| } |
| } |
| |
| static GstFlowReturn |
| gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstObject * parent, |
| GstBuffer * buf) |
| { |
| GstFlowReturn ret; |
| GstRtpSsrcDemux *demux; |
| guint32 ssrc; |
| GstRTCPPacket packet; |
| GstRTCPBuffer rtcp = { NULL, }; |
| GstPad *srcpad; |
| GstRtpSsrcDemuxPad *dpad; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| if (!gst_rtcp_buffer_validate (buf)) |
| goto invalid_rtcp; |
| |
| gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp); |
| if (!gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) { |
| gst_rtcp_buffer_unmap (&rtcp); |
| goto invalid_rtcp; |
| } |
| |
| /* first packet must be SR or RR or else the validate would have failed */ |
| switch (gst_rtcp_packet_get_type (&packet)) { |
| case GST_RTCP_TYPE_SR: |
| /* get the ssrc so that we can route it to the right source pad */ |
| gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL, |
| NULL); |
| break; |
| case GST_RTCP_TYPE_RR: |
| ssrc = gst_rtcp_packet_rr_get_ssrc (&packet); |
| break; |
| default: |
| goto unexpected_rtcp; |
| } |
| gst_rtcp_buffer_unmap (&rtcp); |
| |
| GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc); |
| |
| srcpad = find_or_create_demux_pad_for_ssrc (demux, ssrc, RTCP_PAD); |
| if (srcpad == NULL) |
| goto create_failed; |
| |
| /* push to srcpad */ |
| ret = gst_pad_push (srcpad, buf); |
| |
| if (ret != GST_FLOW_OK) { |
| /* check if the ssrc still there, may have been removed */ |
| GST_PAD_LOCK (demux); |
| dpad = find_demux_pad_for_ssrc (demux, ssrc); |
| if (dpad == NULL || dpad->rtcp_pad != srcpad) { |
| /* SSRC was removed during the push ... ignore the error */ |
| ret = GST_FLOW_OK; |
| } |
| GST_PAD_UNLOCK (demux); |
| } |
| |
| gst_object_unref (srcpad); |
| |
| return ret; |
| |
| /* ERRORS */ |
| invalid_rtcp: |
| { |
| /* this is fatal and should be filtered earlier */ |
| GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), |
| ("Dropping invalid RTCP packet")); |
| gst_buffer_unref (buf); |
| return GST_FLOW_ERROR; |
| } |
| unexpected_rtcp: |
| { |
| GST_DEBUG_OBJECT (demux, "dropping unexpected RTCP packet"); |
| gst_buffer_unref (buf); |
| return GST_FLOW_OK; |
| } |
| create_failed: |
| { |
| GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), |
| ("Could not create new pad")); |
| gst_buffer_unref (buf); |
| return GST_FLOW_ERROR; |
| } |
| } |
| |
| static GstRtpSsrcDemuxPad * |
| find_demux_pad_for_pad (GstRtpSsrcDemux * demux, GstPad * pad) |
| { |
| GSList *walk; |
| |
| for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { |
| GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data; |
| if (dpad->rtp_pad == pad || dpad->rtcp_pad == pad) { |
| return dpad; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| |
| static gboolean |
| gst_rtp_ssrc_demux_src_event (GstPad * pad, GstObject * parent, |
| GstEvent * event) |
| { |
| GstRtpSsrcDemux *demux; |
| const GstStructure *s; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_CUSTOM_UPSTREAM: |
| case GST_EVENT_CUSTOM_BOTH: |
| case GST_EVENT_CUSTOM_BOTH_OOB: |
| s = gst_event_get_structure (event); |
| if (s && !gst_structure_has_field (s, "ssrc")) { |
| GstRtpSsrcDemuxPad *dpad = find_demux_pad_for_pad (demux, pad); |
| |
| if (dpad) { |
| GstStructure *ws; |
| |
| event = gst_event_make_writable (event); |
| ws = gst_event_writable_structure (event); |
| gst_structure_set (ws, "ssrc", G_TYPE_UINT, dpad->ssrc, NULL); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return gst_pad_event_default (pad, parent, event); |
| } |
| |
| static GstIterator * |
| gst_rtp_ssrc_demux_iterate_internal_links_src (GstPad * pad, GstObject * parent) |
| { |
| GstRtpSsrcDemux *demux; |
| GstPad *otherpad = NULL; |
| GstIterator *it = NULL; |
| GSList *current; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| GST_PAD_LOCK (demux); |
| for (current = demux->srcpads; current; current = g_slist_next (current)) { |
| GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) current->data; |
| |
| if (pad == dpad->rtp_pad) { |
| otherpad = demux->rtp_sink; |
| break; |
| } else if (pad == dpad->rtcp_pad) { |
| otherpad = demux->rtcp_sink; |
| break; |
| } |
| } |
| if (otherpad) { |
| GValue val = { 0, }; |
| |
| g_value_init (&val, GST_TYPE_PAD); |
| g_value_set_object (&val, otherpad); |
| it = gst_iterator_new_single (GST_TYPE_PAD, &val); |
| g_value_unset (&val); |
| |
| } |
| GST_PAD_UNLOCK (demux); |
| |
| return it; |
| } |
| |
| /* Should return 0 for elements to be included */ |
| static gint |
| src_pad_compare_func (gconstpointer a, gconstpointer b) |
| { |
| GstPad *pad = GST_PAD (g_value_get_object (a)); |
| const gchar *prefix = g_value_get_string (b); |
| gint res; |
| |
| /* 0 means equal means we accept the pad, accepted if there is a name |
| * and it starts with the prefix */ |
| GST_OBJECT_LOCK (pad); |
| res = !GST_PAD_NAME (pad) || !g_str_has_prefix (GST_PAD_NAME (pad), prefix); |
| GST_OBJECT_UNLOCK (pad); |
| |
| return res; |
| } |
| |
| static GstIterator * |
| gst_rtp_ssrc_demux_iterate_internal_links_sink (GstPad * pad, |
| GstObject * parent) |
| { |
| GstRtpSsrcDemux *demux; |
| GstIterator *it = NULL; |
| GValue gval = { 0, }; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| g_value_init (&gval, G_TYPE_STRING); |
| if (pad == demux->rtp_sink) |
| g_value_set_static_string (&gval, "src_"); |
| else if (pad == demux->rtcp_sink) |
| g_value_set_static_string (&gval, "rtcp_src_"); |
| else |
| g_assert_not_reached (); |
| |
| it = gst_element_iterate_src_pads (GST_ELEMENT_CAST (demux)); |
| it = gst_iterator_filter (it, src_pad_compare_func, &gval); |
| |
| return it; |
| } |
| |
| |
| static gboolean |
| gst_rtp_ssrc_demux_src_query (GstPad * pad, GstObject * parent, |
| GstQuery * query) |
| { |
| GstRtpSsrcDemux *demux; |
| gboolean res = FALSE; |
| |
| demux = GST_RTP_SSRC_DEMUX (parent); |
| |
| switch (GST_QUERY_TYPE (query)) { |
| case GST_QUERY_LATENCY: |
| { |
| |
| if ((res = gst_pad_peer_query (demux->rtp_sink, query))) { |
| gboolean live; |
| GstClockTime min_latency, max_latency; |
| GstRtpSsrcDemuxPad *demuxpad; |
| |
| demuxpad = gst_pad_get_element_private (pad); |
| |
| gst_query_parse_latency (query, &live, &min_latency, &max_latency); |
| |
| GST_DEBUG_OBJECT (demux, "peer min latency %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (min_latency)); |
| |
| GST_DEBUG_OBJECT (demux, "latency for SSRC %08x", demuxpad->ssrc); |
| |
| gst_query_set_latency (query, live, min_latency, max_latency); |
| } |
| break; |
| } |
| default: |
| res = gst_pad_query_default (pad, parent, query); |
| break; |
| } |
| |
| return res; |
| } |
| |
| static GstStateChangeReturn |
| gst_rtp_ssrc_demux_change_state (GstElement * element, |
| GstStateChange transition) |
| { |
| GstStateChangeReturn ret; |
| GstRtpSsrcDemux *demux; |
| |
| demux = GST_RTP_SSRC_DEMUX (element); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_NULL_TO_READY: |
| case GST_STATE_CHANGE_READY_TO_PAUSED: |
| case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
| default: |
| break; |
| } |
| |
| ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_READY: |
| gst_rtp_ssrc_demux_reset (demux); |
| break; |
| case GST_STATE_CHANGE_READY_TO_NULL: |
| default: |
| break; |
| } |
| return ret; |
| } |