4 * Copyright (C) 2005 Nokia Corporation.
5 * @author Kai Vehmanen <kai.vehmanen@nokia.com>
7 * Loosely based on GStreamer gstdecodebin
8 * Copyright (C) <2004> Wim Taymans <wim.taymans@gmail.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 * SECTION:element-rtpptdemux
30 * rtpptdemux acts as a demuxer for RTP packets based on the payload type of
31 * the packets. Its main purpose is to allow an application to easily receive
32 * and decode an RTP stream with multiple payload types.
34 * For each payload type that is detected, a new pad will be created and the
35 * #GstRtpPtDemux::new-payload-type signal will be emitted. When the payload for
36 * the RTP stream changes, the #GstRtpPtDemux::payload-type-change signal will be
39 * The element will try to set complete and unique application/x-rtp caps
40 * on the output pads based on the result of the #GstRtpPtDemux::request-pt-map
43 * ## Example pipelines
45 * gst-launch-1.0 udpsrc caps="application/x-rtp" ! rtpptdemux ! fakesink
46 * ]| Takes an RTP stream and send the RTP packets with the first detected
47 * payload type to fakesink, discarding the other payload types.
53 * Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
57 * - works with the test_rtpdemux.c tool
60 * - is emitting a signal enough, or should we
61 * use GstEvent to notify downstream elements
62 * of the new packet... no?
65 * - emits event both for new PTs, and whenever
75 #include <gst/rtp/gstrtpbuffer.h>
77 #include "gstrtpptdemux.h"
79 /* generic templates */
80 static GstStaticPadTemplate rtp_pt_demux_sink_template =
81 GST_STATIC_PAD_TEMPLATE ("sink",
84 GST_STATIC_CAPS ("application/x-rtp")
87 static GstStaticPadTemplate rtp_pt_demux_src_template =
88 GST_STATIC_PAD_TEMPLATE ("src_%u",
91 GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 255 ]")
94 GST_DEBUG_CATEGORY_STATIC (gst_rtp_pt_demux_debug);
95 #define GST_CAT_DEFAULT gst_rtp_pt_demux_debug
98 * Item for storing GstPad<->pt pairs.
100 struct _GstRtpPtDemuxPad
102 GstPad *pad; /*< pointer to the actual pad */
103 gint pt; /*< RTP payload-type attached to pad */
110 SIGNAL_REQUEST_PT_MAP,
111 SIGNAL_NEW_PAYLOAD_TYPE,
112 SIGNAL_PAYLOAD_TYPE_CHANGE,
123 #define gst_rtp_pt_demux_parent_class parent_class
124 G_DEFINE_TYPE (GstRtpPtDemux, gst_rtp_pt_demux, GST_TYPE_ELEMENT);
126 static void gst_rtp_pt_demux_finalize (GObject * object);
128 static void gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux);
129 static gboolean gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux);
131 static gboolean gst_rtp_pt_demux_sink_event (GstPad * pad, GstObject * parent,
133 static GstFlowReturn gst_rtp_pt_demux_chain (GstPad * pad, GstObject * parent,
135 static GstStateChangeReturn gst_rtp_pt_demux_change_state (GstElement * element,
136 GstStateChange transition);
137 static void gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux);
139 static GstPad *find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt);
141 static gboolean gst_rtp_pt_demux_src_event (GstPad * pad, GstObject * parent,
145 static guint gst_rtp_pt_demux_signals[LAST_SIGNAL] = { 0 };
148 gst_rtp_pt_demux_set_property (GObject * object, guint prop_id,
149 const GValue * value, GParamSpec * pspec)
151 GstRtpPtDemux *rtpptdemux = GST_RTP_PT_DEMUX (object);
154 case PROP_IGNORED_PTS:
155 g_value_copy (value, &rtpptdemux->ignored_pts);
158 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
164 gst_rtp_pt_demux_get_property (GObject * object, guint prop_id,
165 GValue * value, GParamSpec * pspec)
167 GstRtpPtDemux *rtpptdemux = GST_RTP_PT_DEMUX (object);
170 case PROP_IGNORED_PTS:
171 g_value_copy (&rtpptdemux->ignored_pts, value);
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180 gst_rtp_pt_demux_class_init (GstRtpPtDemuxClass * klass)
182 GObjectClass *gobject_klass;
183 GstElementClass *gstelement_klass;
185 gobject_klass = (GObjectClass *) klass;
186 gstelement_klass = (GstElementClass *) klass;
189 * GstRtpPtDemux::request-pt-map:
190 * @demux: the object which received the signal
191 * @pt: the payload type
193 * Request the payload type as #GstCaps for @pt.
195 gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP] =
196 g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
197 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, request_pt_map),
198 NULL, NULL, NULL, GST_TYPE_CAPS, 1, G_TYPE_UINT);
201 * GstRtpPtDemux::new-payload-type:
202 * @demux: the object which received the signal
203 * @pt: the payload type
204 * @pad: the pad with the new payload
206 * Emitted when a new payload type pad has been created in @demux.
208 gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE] =
209 g_signal_new ("new-payload-type", G_TYPE_FROM_CLASS (klass),
210 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, new_payload_type),
211 NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD);
214 * GstRtpPtDemux::payload-type-change:
215 * @demux: the object which received the signal
216 * @pt: the new payload type
218 * Emitted when the payload type changed.
220 gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE] =
221 g_signal_new ("payload-type-change", G_TYPE_FROM_CLASS (klass),
222 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
223 payload_type_change), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT);
226 * GstRtpPtDemux::clear-pt-map:
227 * @demux: the object which received the signal
229 * The application can call this signal to instruct the element to discard the
230 * currently cached payload type map.
232 gst_rtp_pt_demux_signals[SIGNAL_CLEAR_PT_MAP] =
233 g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
234 G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
235 clear_pt_map), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE);
237 gobject_klass->set_property = gst_rtp_pt_demux_set_property;
238 gobject_klass->get_property = gst_rtp_pt_demux_get_property;
241 * GstRtpPtDemux:ignored-payload-types:
243 * If specified, packets with an ignored payload type will be dropped,
244 * instead of causing a new pad to be exposed for these to be pushed on.
246 * This is for example useful to drop FEC protection packets, as they
247 * need to go through the #GstRtpJitterBuffer, but cease to be useful
248 * past that point, #GstRtpBin will make use of this property for that
253 g_object_class_install_property (gobject_klass, PROP_IGNORED_PTS,
254 gst_param_spec_array ("ignored-payload-types",
255 "Ignored payload types",
256 "Packets with these payload types will be dropped",
257 g_param_spec_int ("payload-types", "payload-types", "Payload types",
259 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
260 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
262 gobject_klass->finalize = gst_rtp_pt_demux_finalize;
264 gstelement_klass->change_state =
265 GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_change_state);
267 klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_clear_pt_map);
269 gst_element_class_add_static_pad_template (gstelement_klass,
270 &rtp_pt_demux_sink_template);
271 gst_element_class_add_static_pad_template (gstelement_klass,
272 &rtp_pt_demux_src_template);
274 gst_element_class_set_static_metadata (gstelement_klass, "RTP Demux",
276 "Parses codec streams transmitted in the same RTP session",
277 "Kai Vehmanen <kai.vehmanen@nokia.com>");
279 GST_DEBUG_CATEGORY_INIT (gst_rtp_pt_demux_debug,
280 "rtpptdemux", 0, "RTP codec demuxer");
282 GST_DEBUG_REGISTER_FUNCPTR (gst_rtp_pt_demux_chain);
286 gst_rtp_pt_demux_init (GstRtpPtDemux * ptdemux)
288 GstElementClass *klass = GST_ELEMENT_GET_CLASS (ptdemux);
291 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
293 g_assert (ptdemux->sink != NULL);
295 gst_pad_set_chain_function (ptdemux->sink, gst_rtp_pt_demux_chain);
296 gst_pad_set_event_function (ptdemux->sink, gst_rtp_pt_demux_sink_event);
298 gst_element_add_pad (GST_ELEMENT (ptdemux), ptdemux->sink);
300 g_value_init (&ptdemux->ignored_pts, GST_TYPE_ARRAY);
304 gst_rtp_pt_demux_finalize (GObject * object)
306 gst_rtp_pt_demux_release (GST_RTP_PT_DEMUX (object));
308 g_value_unset (&GST_RTP_PT_DEMUX (object)->ignored_pts);
310 G_OBJECT_CLASS (parent_class)->finalize (object);
314 gst_rtp_pt_demux_get_caps (GstRtpPtDemux * rtpdemux, guint pt)
317 gboolean have_ssrc = FALSE;
318 GstCaps *caps, *sink_caps;
320 GValue args[2] = { {0}, {0} };
322 /* figure out the caps */
323 g_value_init (&args[0], GST_TYPE_ELEMENT);
324 g_value_set_object (&args[0], rtpdemux);
325 g_value_init (&args[1], G_TYPE_UINT);
326 g_value_set_uint (&args[1], pt);
328 g_value_init (&ret, GST_TYPE_CAPS);
329 g_value_set_boxed (&ret, NULL);
331 g_signal_emitv (args, gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP], 0,
334 g_value_unset (&args[0]);
335 g_value_unset (&args[1]);
336 caps = g_value_dup_boxed (&ret);
337 sink_caps = gst_pad_get_current_caps (rtpdemux->sink);
338 g_value_unset (&ret);
342 } else if (sink_caps) {
344 gst_structure_get_uint (gst_caps_get_structure (sink_caps, 0), "ssrc",
346 gst_caps_unref (sink_caps);
350 caps = gst_caps_make_writable (caps);
351 gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
353 gst_caps_set_simple (caps, "ssrc", G_TYPE_UINT, ssrc, NULL);
356 GST_DEBUG_OBJECT (rtpdemux, "pt %d, got caps %" GST_PTR_FORMAT, pt, caps);
362 gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux)
366 GST_OBJECT_LOCK (rtpdemux);
367 GST_DEBUG_OBJECT (rtpdemux, "clearing pt map");
368 for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
369 GstRtpPtDemuxPad *pad = walk->data;
373 GST_OBJECT_UNLOCK (rtpdemux);
377 need_caps_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
380 gboolean ret = FALSE;
382 GST_OBJECT_LOCK (rtpdemux);
383 for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
384 GstRtpPtDemuxPad *pad = walk->data;
390 GST_OBJECT_UNLOCK (rtpdemux);
397 clear_newcaps_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
401 GST_OBJECT_LOCK (rtpdemux);
402 for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
403 GstRtpPtDemuxPad *pad = walk->data;
406 pad->newcaps = FALSE;
410 GST_OBJECT_UNLOCK (rtpdemux);
414 forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
416 GstPad *srcpad = GST_PAD_CAST (user_data);
418 /* Stream start and caps have already been pushed */
419 if (GST_EVENT_TYPE (*event) >= GST_EVENT_SEGMENT)
420 gst_pad_push_event (srcpad, gst_event_ref (*event));
426 gst_rtp_pt_demux_pt_is_ignored (GstRtpPtDemux * ptdemux, guint8 pt)
428 gboolean ret = FALSE;
431 for (i = 0; i < gst_value_array_get_size (&ptdemux->ignored_pts); i++) {
432 const GValue *tmp = gst_value_array_get_value (&ptdemux->ignored_pts, i);
434 if (g_value_get_int (tmp) == pt) {
444 gst_rtp_pt_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
446 GstFlowReturn ret = GST_FLOW_OK;
447 GstRtpPtDemux *rtpdemux;
451 GstRTPBuffer rtp = { NULL };
453 rtpdemux = GST_RTP_PT_DEMUX (parent);
455 if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp))
458 pt = gst_rtp_buffer_get_payload_type (&rtp);
459 gst_rtp_buffer_unmap (&rtp);
461 if (gst_rtp_pt_demux_pt_is_ignored (rtpdemux, pt))
464 GST_DEBUG_OBJECT (rtpdemux, "received buffer for pt %d", pt);
466 srcpad = find_pad_for_pt (rtpdemux, pt);
467 if (srcpad == NULL) {
468 /* new PT, create a src pad */
469 GstRtpPtDemuxPad *rtpdemuxpad;
470 GstElementClass *klass;
471 GstPadTemplate *templ;
474 caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
478 if (gst_rtp_pt_demux_pt_is_ignored (rtpdemux, pt))
481 klass = GST_ELEMENT_GET_CLASS (rtpdemux);
482 templ = gst_element_class_get_pad_template (klass, "src_%u");
483 padname = g_strdup_printf ("src_%u", pt);
484 srcpad = gst_pad_new_from_template (templ, padname);
485 gst_pad_use_fixed_caps (srcpad);
487 gst_pad_set_event_function (srcpad, gst_rtp_pt_demux_src_event);
489 GST_DEBUG_OBJECT (rtpdemux, "Adding pt=%d to the list.", pt);
490 rtpdemuxpad = g_slice_new0 (GstRtpPtDemuxPad);
491 rtpdemuxpad->pt = pt;
492 rtpdemuxpad->newcaps = FALSE;
493 rtpdemuxpad->pad = srcpad;
494 gst_object_ref (srcpad);
495 GST_OBJECT_LOCK (rtpdemux);
496 rtpdemux->srcpads = g_slist_append (rtpdemux->srcpads, rtpdemuxpad);
497 GST_OBJECT_UNLOCK (rtpdemux);
499 gst_pad_set_active (srcpad, TRUE);
501 /* First push the stream-start event, it must always come first */
502 gst_pad_push_event (srcpad,
503 gst_pad_get_sticky_event (rtpdemux->sink, GST_EVENT_STREAM_START, 0));
505 /* Then caps event is sent */
506 gst_pad_set_caps (srcpad, caps);
507 gst_caps_unref (caps);
509 /* First sticky events on sink pad are forwarded to the new src pad */
510 gst_pad_sticky_events_foreach (rtpdemux->sink, forward_sticky_events,
513 gst_element_add_pad (GST_ELEMENT_CAST (rtpdemux), srcpad);
515 GST_DEBUG_OBJECT (rtpdemux, "emitting new-payload-type for pt %d", pt);
516 g_signal_emit (G_OBJECT (rtpdemux),
517 gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE], 0, pt, srcpad);
520 if (pt != rtpdemux->last_pt) {
523 /* our own signal with an extra flag that this is the only pad */
524 rtpdemux->last_pt = pt;
525 GST_DEBUG_OBJECT (rtpdemux, "emitting payload-type-changed for pt %d",
527 g_signal_emit (G_OBJECT (rtpdemux),
528 gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE], 0, emit_pt);
531 while (need_caps_for_pt (rtpdemux, pt)) {
532 GST_DEBUG_OBJECT (rtpdemux, "need new caps for %d", pt);
533 caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
537 clear_newcaps_for_pt (rtpdemux, pt);
539 gst_pad_set_caps (srcpad, caps);
540 gst_caps_unref (caps);
544 ret = gst_pad_push (srcpad, buf);
546 gst_object_unref (srcpad);
552 GST_DEBUG_OBJECT (rtpdemux, "Dropped buffer for pt %d", pt);
553 gst_buffer_unref (buf);
560 /* this should not be fatal */
561 GST_ELEMENT_WARNING (rtpdemux, STREAM, DEMUX, (NULL),
562 ("Dropping invalid RTP payload"));
563 gst_buffer_unref (buf);
564 return GST_FLOW_ERROR;
568 GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL),
569 ("Could not get caps for payload"));
570 gst_buffer_unref (buf);
572 gst_object_unref (srcpad);
573 return GST_FLOW_ERROR;
578 find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
580 GstPad *respad = NULL;
583 GST_OBJECT_LOCK (rtpdemux);
584 for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
585 GstRtpPtDemuxPad *pad = walk->data;
588 respad = gst_object_ref (pad->pad);
592 GST_OBJECT_UNLOCK (rtpdemux);
598 gst_rtp_pt_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
600 GstRtpPtDemux *rtpdemux;
601 gboolean res = FALSE;
603 rtpdemux = GST_RTP_PT_DEMUX (parent);
605 switch (GST_EVENT_TYPE (event)) {
608 gst_rtp_pt_demux_clear_pt_map (rtpdemux);
609 /* don't forward the event, we cleared the ptmap and on the next buffer we
610 * will add the pt to the caps and push a new caps event */
611 gst_event_unref (event);
615 case GST_EVENT_CUSTOM_DOWNSTREAM:
617 const GstStructure *s;
619 s = gst_event_get_structure (event);
621 if (gst_structure_has_name (s, "GstRTPPacketLost")) {
622 GstPad *srcpad = find_pad_for_pt (rtpdemux, rtpdemux->last_pt);
625 res = gst_pad_push_event (srcpad, event);
626 gst_object_unref (srcpad);
628 gst_event_unref (event);
632 res = gst_pad_event_default (pad, parent, event);
637 res = gst_pad_event_default (pad, parent, event);
646 gst_rtp_pt_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
648 GstRtpPtDemux *demux;
649 const GstStructure *s;
651 demux = GST_RTP_PT_DEMUX (parent);
653 switch (GST_EVENT_TYPE (event)) {
654 case GST_EVENT_CUSTOM_UPSTREAM:
655 case GST_EVENT_CUSTOM_BOTH:
656 case GST_EVENT_CUSTOM_BOTH_OOB:
657 s = gst_event_get_structure (event);
658 if (s && !gst_structure_has_field (s, "payload")) {
661 GST_OBJECT_LOCK (demux);
662 for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
663 GstRtpPtDemuxPad *dpad = (GstRtpPtDemuxPad *) walk->data;
665 if (dpad->pad == pad) {
669 GST_EVENT_CAST (gst_mini_object_make_writable
670 (GST_MINI_OBJECT_CAST (event)));
671 ws = gst_event_writable_structure (event);
672 gst_structure_set (ws, "payload", G_TYPE_UINT, dpad->pt, NULL);
676 GST_OBJECT_UNLOCK (demux);
683 return gst_pad_event_default (pad, parent, event);
687 * Reserves resources for the object.
690 gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux)
692 ptdemux->srcpads = NULL;
693 ptdemux->last_pt = 0xFFFF;
699 * Free resources for the object.
702 gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux)
707 GST_OBJECT_LOCK (ptdemux);
708 tmppads = ptdemux->srcpads;
709 ptdemux->srcpads = NULL;
710 GST_OBJECT_UNLOCK (ptdemux);
712 for (walk = tmppads; walk; walk = g_slist_next (walk)) {
713 GstRtpPtDemuxPad *pad = walk->data;
715 gst_pad_set_active (pad->pad, FALSE);
716 gst_element_remove_pad (GST_ELEMENT_CAST (ptdemux), pad->pad);
717 g_slice_free (GstRtpPtDemuxPad, pad);
719 g_slist_free (tmppads);
722 static GstStateChangeReturn
723 gst_rtp_pt_demux_change_state (GstElement * element, GstStateChange transition)
725 GstStateChangeReturn ret;
726 GstRtpPtDemux *ptdemux;
728 ptdemux = GST_RTP_PT_DEMUX (element);
730 switch (transition) {
731 case GST_STATE_CHANGE_NULL_TO_READY:
732 if (gst_rtp_pt_demux_setup (ptdemux) != TRUE)
733 ret = GST_STATE_CHANGE_FAILURE;
735 case GST_STATE_CHANGE_READY_TO_PAUSED:
736 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
741 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
743 switch (transition) {
744 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
745 case GST_STATE_CHANGE_PAUSED_TO_READY:
747 case GST_STATE_CHANGE_READY_TO_NULL:
748 gst_rtp_pt_demux_release (ptdemux);