static GstFlowReturn gst_rtp_ssrc_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstFlowReturn ret; GstRtpSsrcDemux *demux; guint32 ssrc; GstRtpSsrcDemuxPad *dpad; GstRTPBuffer rtp = { NULL }; GstPad *srcpad; demux = GST_RTP_SSRC_DEMUX (parent); if (!gst_rtp_buffer_validate (buf)) goto invalid_payload; gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); ssrc = gst_rtp_buffer_get_ssrc (&rtp); gst_rtp_buffer_unmap (&rtp); GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc); GST_PAD_LOCK (demux); dpad = find_or_create_demux_pad_for_ssrc (demux, ssrc); if (dpad == NULL) { GST_PAD_UNLOCK (demux); goto create_failed; } srcpad = gst_object_ref (dpad->rtp_pad); GST_PAD_UNLOCK (demux); /* push to srcpad */ ret = gst_pad_push (srcpad, buf); 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_chain (GstPad * pad, GstBuffer * buf) { GstFlowReturn ret; GstRtpSsrcDemux *demux; guint32 ssrc; GstRtpSsrcDemuxPad *dpad; demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad)); if (!gst_rtp_buffer_validate (buf)) goto invalid_payload; ssrc = gst_rtp_buffer_get_ssrc (buf); GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc); GST_PAD_LOCK (demux); dpad = find_demux_pad_for_ssrc (demux, ssrc); if (dpad == NULL) { if (!(dpad = create_demux_pad_for_ssrc (demux, ssrc, GST_BUFFER_TIMESTAMP (buf)))) goto create_failed; } GST_PAD_UNLOCK (demux); /* push to srcpad */ ret = gst_pad_push (dpad->rtp_pad, buf); 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_PAD_UNLOCK (demux); gst_buffer_unref (buf); return GST_FLOW_ERROR; } }
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; }
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 GstIterator * gst_rtp_ssrc_demux_iterate_internal_links (GstPad * pad) { GstRtpSsrcDemux *demux; GstPad *otherpad = NULL; GstIterator *it; GSList *current; demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad)); GST_PAD_LOCK (demux); for (current = demux->srcpads; current; current = g_slist_next (current)) { GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) current->data; if (pad == demux->rtp_sink) { otherpad = dpad->rtp_pad; break; } else if (pad == demux->rtcp_sink) { otherpad = dpad->rtcp_pad; } else if (pad == dpad->rtp_pad) { otherpad = demux->rtp_sink; break; } else if (pad == dpad->rtcp_pad) { otherpad = demux->rtcp_sink; break; } } it = gst_iterator_new_single (GST_TYPE_PAD, otherpad, (GstCopyFunction) gst_object_ref, (GFreeFunc) gst_object_unref); GST_PAD_UNLOCK (demux); gst_object_unref (demux); return it; }
static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstRtpSsrcDemux *demux; gboolean res = FALSE; demux = GST_RTP_SSRC_DEMUX (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED); /* fallthrough */ default: { GSList *walk; GSList *pads = NULL; res = TRUE; /* need local snapshot of pads; * should not push downstream while holding lock as that might deadlock * with stuff traveling upstream tyring to get this lock while holding * other (stream)lock */ GST_PAD_LOCK (demux); for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data; pad = g_slice_dup (GstRtpSsrcDemuxPad, pad); gst_object_ref (pad->rtp_pad); pads = g_slist_prepend (pads, pad); } GST_PAD_UNLOCK (demux); for (walk = pads; walk; walk = g_slist_next (walk)) { GstRtpSsrcDemuxPad *dpad = walk->data; GstEvent *newevent; newevent = add_ssrc_and_ref (event, dpad->ssrc); res &= gst_pad_push_event (dpad->rtp_pad, newevent); gst_object_unref (dpad->rtp_pad); g_slice_free (GstRtpSsrcDemuxPad, dpad); } g_slist_free (pads); gst_event_unref (event); break; } } return res; }
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; } }
static gboolean gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad, GstEvent * event) { GstRtpSsrcDemux *demux; gboolean res = FALSE; demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: default: { GSList *walk; GSList *pads = NULL; res = TRUE; GST_PAD_LOCK (demux); for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data; pads = g_slist_prepend (pads, gst_object_ref (pad->rtcp_pad)); } GST_PAD_UNLOCK (demux); for (walk = pads; walk; walk = g_slist_next (walk)) { GstPad *pad = (GstPad *) walk->data; gst_event_ref (event); res &= gst_pad_push_event (pad, event); gst_object_unref (pad); } g_slist_free (pads); gst_event_unref (event); break; } } gst_object_unref (demux); return res; }
static gboolean gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstRtpSsrcDemux *demux; gboolean res = TRUE; GSList *walk; GSList *pads = NULL; demux = GST_RTP_SSRC_DEMUX (parent); GST_PAD_LOCK (demux); for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data; pad = g_slice_dup (GstRtpSsrcDemuxPad, pad); gst_object_ref (pad->rtcp_pad); pads = g_slist_prepend (pads, pad); } GST_PAD_UNLOCK (demux); for (walk = pads; walk; walk = g_slist_next (walk)) { GstRtpSsrcDemuxPad *dpad = walk->data; GstEvent *newevent; newevent = add_ssrc_and_ref (event, dpad->ssrc); res &= gst_pad_push_event (dpad->rtcp_pad, newevent); gst_object_unref (dpad->rtcp_pad); g_slice_free (GstRtpSsrcDemuxPad, dpad); } g_slist_free (pads); gst_event_unref (event); return res; }
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_reduced (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 in case of a reduced size RTCP packet * it must be APP, RTPFB or PSFB feeadback, 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; case GST_RTCP_TYPE_APP: case GST_RTCP_TYPE_RTPFB: case GST_RTCP_TYPE_PSFB: ssrc = gst_rtcp_packet_fb_get_sender_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 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 GstFlowReturn gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstFlowReturn ret; GstRtpSsrcDemux *demux; guint32 ssrc; GstRtpSsrcDemuxPad *dpad; GstRTCPPacket packet; GstRTCPBuffer rtcp = { NULL, }; GstPad *srcpad; 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; default: goto unexpected_rtcp; } gst_rtcp_buffer_unmap (&rtcp); GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc); GST_PAD_LOCK (demux); dpad = find_or_create_demux_pad_for_ssrc (demux, ssrc); if (dpad == NULL) { GST_PAD_UNLOCK (demux); goto create_failed; } srcpad = gst_object_ref (dpad->rtcp_pad); GST_PAD_UNLOCK (demux); /* push to srcpad */ ret = gst_pad_push (srcpad, buf); 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; } }