From abe33a55261be63f292f7e7113557cda5b330462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 9 Aug 2009 14:55:26 +0200 Subject: autoconvert: Small cleanups --- gst/autoconvert/gstautoconvert.c | 60 ++++++++++++++-------------------------- gst/autoconvert/gstautoconvert.h | 3 ++ 2 files changed, 24 insertions(+), 39 deletions(-) (limited to 'gst') diff --git a/gst/autoconvert/gstautoconvert.c b/gst/autoconvert/gstautoconvert.c index 43110030..a5241551 100644 --- a/gst/autoconvert/gstautoconvert.c +++ b/gst/autoconvert/gstautoconvert.c @@ -46,13 +46,6 @@ GST_DEBUG_CATEGORY (autoconvert_debug); #define GST_CAT_DEFAULT (autoconvert_debug) /* elementfactory information */ -static const GstElementDetails gst_auto_convert_details = -GST_ELEMENT_DETAILS ("Select convertor based on caps", - "Generic/Bin", - "Selects the right transform element based on the caps", - "Olivier Crete "); - - static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -63,7 +56,6 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); - static GstStaticPadTemplate sink_internal_template = GST_STATIC_PAD_TEMPLATE ("sink_internal", GST_PAD_SINK, @@ -86,10 +78,9 @@ enum enum { PROP_0, - PROP_FACTORIES, + PROP_FACTORIES }; - static void gst_auto_convert_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_auto_convert_get_property (GObject * object, @@ -118,7 +109,6 @@ static gboolean gst_auto_convert_src_event (GstPad * pad, GstEvent * event); static gboolean gst_auto_convert_src_query (GstPad * pad, GstQuery * query); static const GstQueryType *gst_auto_convert_src_query_type (GstPad * pad); - static GstFlowReturn gst_auto_convert_internal_sink_chain (GstPad * pad, GstBuffer * buffer); static gboolean gst_auto_convert_internal_sink_event (GstPad * pad, @@ -140,18 +130,21 @@ static gboolean gst_auto_convert_internal_src_query (GstPad * pad, static const GstQueryType *gst_auto_convert_internal_src_query_type (GstPad * pad); - static void gst_auto_convert_load_factories (GstAutoConvert * autoconvert); -GQuark internal_srcpad_quark = 0; -GQuark internal_sinkpad_quark = 0; -GQuark parent_quark = 0; +static GQuark internal_srcpad_quark = 0; +static GQuark internal_sinkpad_quark = 0; +static GQuark parent_quark = 0; static void gst_auto_convert_do_init (GType type) { GST_DEBUG_CATEGORY_INIT (autoconvert_debug, "autoconvert", 0, "Auto convert based on caps"); + + internal_srcpad_quark = g_quark_from_static_string ("internal_srcpad"); + internal_sinkpad_quark = g_quark_from_static_string ("internal_sinkpad"); + parent_quark = g_quark_from_static_string ("parent"); } GST_BOILERPLATE_FULL (GstAutoConvert, gst_auto_convert, GstBin, @@ -167,53 +160,39 @@ gst_auto_convert_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sinktemplate)); - gst_element_class_set_details (element_class, &gst_auto_convert_details); + gst_element_class_set_details_simple (element_class, + "Select convertor based on caps", "Generic/Bin", + "Selects the right transform element based on the caps", + "Olivier Crete "); } static void gst_auto_convert_class_init (GstAutoConvertClass * klass) { - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GstBinClass *gstbin_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - gstbin_class = (GstBinClass *) klass; + GObjectClass *gobject_class = (GObjectClass *) klass; - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_auto_convert_dispose); + gobject_class->dispose = gst_auto_convert_dispose; - gobject_class->set_property = - GST_DEBUG_FUNCPTR (gst_auto_convert_set_property); - gobject_class->get_property = - GST_DEBUG_FUNCPTR (gst_auto_convert_get_property); + gobject_class->set_property = gst_auto_convert_set_property; + gobject_class->get_property = gst_auto_convert_get_property; g_object_class_install_property (gobject_class, PROP_FACTORIES, g_param_spec_pointer ("factories", "GList of GstElementFactory", "GList of GstElementFactory objects to pick from (the element takes" " ownership of the list (NULL means it will go through all possible" - " elements), can only be set once", G_PARAM_READWRITE)); - - parent_class = g_type_class_peek_parent (klass); - - internal_srcpad_quark = g_quark_from_static_string ("internal_srcpad"); - internal_sinkpad_quark = g_quark_from_static_string ("internal_sinkpad"); - parent_quark = g_quark_from_static_string ("parent"); + " elements), can only be set once", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void gst_auto_convert_init (GstAutoConvert * autoconvert, GstAutoConvertClass * klass) { - autoconvert->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); autoconvert->srcpad = gst_pad_new_from_static_template (&srctemplate, "src"); - gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->sinkpad); - gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->srcpad); - gst_pad_set_setcaps_function (autoconvert->sinkpad, GST_DEBUG_FUNCPTR (gst_auto_convert_sink_setcaps)); gst_pad_set_getcaps_function (autoconvert->sinkpad, @@ -235,6 +214,9 @@ gst_auto_convert_init (GstAutoConvert * autoconvert, GST_DEBUG_FUNCPTR (gst_auto_convert_src_query)); gst_pad_set_query_type_function (autoconvert->srcpad, GST_DEBUG_FUNCPTR (gst_auto_convert_src_query_type)); + + gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->sinkpad); + gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->srcpad); } static void diff --git a/gst/autoconvert/gstautoconvert.h b/gst/autoconvert/gstautoconvert.h index 4a3c8df0..12107011 100644 --- a/gst/autoconvert/gstautoconvert.h +++ b/gst/autoconvert/gstautoconvert.h @@ -52,6 +52,9 @@ struct _GstAutoConvert GstElement *current_subelement; GstPad *current_internal_srcpad; GstPad *current_internal_sinkpad; + + GstSegment segment; + GList *cached_events; }; struct _GstAutoConvertClass -- cgit v1.2.1 From cb1f2f2294befa4b1117e89bbfdf7f85e06b3ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 9 Aug 2009 15:20:48 +0200 Subject: autoconvert: Cache events and send them downstream once an element was selected --- gst/autoconvert/gstautoconvert.c | 82 +++++++++++++++++++++++++++++++++++++--- gst/autoconvert/gstautoconvert.h | 1 - 2 files changed, 77 insertions(+), 6 deletions(-) (limited to 'gst') diff --git a/gst/autoconvert/gstautoconvert.c b/gst/autoconvert/gstautoconvert.c index a5241551..39b9c784 100644 --- a/gst/autoconvert/gstautoconvert.c +++ b/gst/autoconvert/gstautoconvert.c @@ -87,6 +87,9 @@ static void gst_auto_convert_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_auto_convert_dispose (GObject * object); +static GstStateChangeReturn gst_auto_convert_change_state (GstElement * element, + GstStateChange transition); + static GstElement *gst_auto_convert_get_subelement (GstAutoConvert * autoconvert); static GstPad *gst_auto_convert_get_internal_sinkpad (GstAutoConvert * @@ -170,6 +173,7 @@ static void gst_auto_convert_class_init (GstAutoConvertClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; gobject_class->dispose = gst_auto_convert_dispose; @@ -183,6 +187,9 @@ gst_auto_convert_class_init (GstAutoConvertClass * klass) " ownership of the list (NULL means it will go through all possible" " elements), can only be set once", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_auto_convert_change_state); } static void @@ -233,6 +240,11 @@ gst_auto_convert_dispose (GObject * object) autoconvert->current_internal_sinkpad = NULL; autoconvert->current_internal_srcpad = NULL; } + + g_list_foreach (autoconvert->cached_events, (GFunc) gst_mini_object_unref, + NULL); + g_list_free (autoconvert->cached_events); + autoconvert->cached_events = NULL; GST_OBJECT_UNLOCK (object); G_OBJECT_CLASS (parent_class)->dispose (object); @@ -279,6 +291,35 @@ gst_auto_convert_get_property (GObject * object, } } +static GstStateChangeReturn +gst_auto_convert_change_state (GstElement * element, GstStateChange transition) +{ + GstAutoConvert *autoconvert = GST_AUTO_CONVERT (element); + GstStateChangeReturn ret; + + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + g_list_foreach (autoconvert->cached_events, (GFunc) gst_mini_object_unref, + NULL); + g_list_free (autoconvert->cached_events); + autoconvert->cached_events = NULL; + break; + default: + break; + } + + return ret; +} + static GstElement * gst_auto_convert_get_element_by_type (GstAutoConvert * autoconvert, GType type) { @@ -890,6 +931,20 @@ gst_auto_convert_sink_chain (GstPad * pad, GstBuffer * buffer) internal_srcpad = gst_auto_convert_get_internal_srcpad (autoconvert); if (internal_srcpad) { + GST_OBJECT_LOCK (autoconvert); + if (autoconvert->cached_events) { + GList *l; + + GST_DEBUG_OBJECT (autoconvert, "Sending cached events downstream"); + + autoconvert->cached_events = g_list_reverse (autoconvert->cached_events); + + for (l = autoconvert->cached_events; l; l = l->next) + gst_pad_push_event (internal_srcpad, l->data); + g_list_free (autoconvert->cached_events); + autoconvert->cached_events = NULL; + } + GST_OBJECT_UNLOCK (autoconvert); ret = gst_pad_push (internal_srcpad, buffer); gst_object_unref (internal_srcpad); } else { @@ -914,9 +969,26 @@ gst_auto_convert_sink_event (GstPad * pad, GstEvent * event) ret = gst_pad_push_event (internal_srcpad, event); gst_object_unref (internal_srcpad); } else { - GST_WARNING_OBJECT (autoconvert, "Got event while no element was selected," - "letting through"); - ret = gst_pad_push_event (autoconvert->srcpad, event); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + GST_OBJECT_LOCK (autoconvert); + g_list_foreach (autoconvert->cached_events, + (GFunc) gst_mini_object_unref, NULL); + g_list_free (autoconvert->cached_events); + autoconvert->cached_events = NULL; + GST_OBJECT_UNLOCK (autoconvert); + /* fall through */ + case GST_EVENT_FLUSH_START: + ret = gst_pad_push_event (autoconvert->srcpad, event); + break; + default: + GST_OBJECT_LOCK (autoconvert); + autoconvert->cached_events = + g_list_prepend (autoconvert->cached_events, event); + ret = TRUE; + GST_OBJECT_UNLOCK (autoconvert); + break; + } } gst_object_unref (autoconvert); @@ -943,7 +1015,7 @@ gst_auto_convert_sink_query (GstPad * pad, GstQuery * query) } else { GST_WARNING_OBJECT (autoconvert, "Got query while no element was selected," "letting through"); - ret = gst_pad_query_default (pad, query); + ret = gst_pad_peer_query (autoconvert->srcpad, query); } gst_object_unref (autoconvert); @@ -1181,7 +1253,7 @@ gst_auto_convert_src_query (GstPad * pad, GstQuery * query) } else { GST_WARNING_OBJECT (autoconvert, "Got query while not element was selected," "letting through"); - ret = gst_pad_query_default (pad, query); + ret = gst_pad_peer_query (autoconvert->sinkpad, query); } gst_object_unref (autoconvert); diff --git a/gst/autoconvert/gstautoconvert.h b/gst/autoconvert/gstautoconvert.h index 12107011..af463f4b 100644 --- a/gst/autoconvert/gstautoconvert.h +++ b/gst/autoconvert/gstautoconvert.h @@ -53,7 +53,6 @@ struct _GstAutoConvert GstPad *current_internal_srcpad; GstPad *current_internal_sinkpad; - GstSegment segment; GList *cached_events; }; -- cgit v1.2.1 From 1691621fafbe699d414e79526d49665e7fcf2faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 10 Aug 2009 22:16:37 +0100 Subject: Remove execute flags from source files --- gst/mixmatrix/Makefile.am | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 gst/mixmatrix/Makefile.am (limited to 'gst') diff --git a/gst/mixmatrix/Makefile.am b/gst/mixmatrix/Makefile.am old mode 100755 new mode 100644 -- cgit v1.2.1 From a6912096cdecd5bc9dc6d91b916ba3f6960d03de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 11 Aug 2009 02:46:54 +0100 Subject: Move rtpmanager from -bad to -good. --- gst/rtpmanager/.gitignore | 2 - gst/rtpmanager/Makefile.am | 49 - gst/rtpmanager/gstrtpbin-marshal.list | 8 - gst/rtpmanager/gstrtpbin.c | 2458 -------------------------------- gst/rtpmanager/gstrtpbin.h | 88 -- gst/rtpmanager/gstrtpjitterbuffer.c | 1972 -------------------------- gst/rtpmanager/gstrtpjitterbuffer.h | 88 -- gst/rtpmanager/gstrtpmanager.c | 60 - gst/rtpmanager/gstrtpptdemux.c | 492 ------- gst/rtpmanager/gstrtpptdemux.h | 62 - gst/rtpmanager/gstrtpsession.c | 1940 ------------------------- gst/rtpmanager/gstrtpsession.h | 81 -- gst/rtpmanager/gstrtpssrcdemux.c | 722 ---------- gst/rtpmanager/gstrtpssrcdemux.h | 62 - gst/rtpmanager/rtpjitterbuffer.c | 593 -------- gst/rtpmanager/rtpjitterbuffer.h | 101 -- gst/rtpmanager/rtpsession.c | 2521 --------------------------------- gst/rtpmanager/rtpsession.h | 306 ---- gst/rtpmanager/rtpsource.c | 1625 --------------------- gst/rtpmanager/rtpsource.h | 226 --- gst/rtpmanager/rtpstats.c | 176 --- gst/rtpmanager/rtpstats.h | 195 --- 22 files changed, 13827 deletions(-) delete mode 100644 gst/rtpmanager/.gitignore delete mode 100644 gst/rtpmanager/Makefile.am delete mode 100644 gst/rtpmanager/gstrtpbin-marshal.list delete mode 100644 gst/rtpmanager/gstrtpbin.c delete mode 100644 gst/rtpmanager/gstrtpbin.h delete mode 100644 gst/rtpmanager/gstrtpjitterbuffer.c delete mode 100644 gst/rtpmanager/gstrtpjitterbuffer.h delete mode 100644 gst/rtpmanager/gstrtpmanager.c delete mode 100644 gst/rtpmanager/gstrtpptdemux.c delete mode 100644 gst/rtpmanager/gstrtpptdemux.h delete mode 100644 gst/rtpmanager/gstrtpsession.c delete mode 100644 gst/rtpmanager/gstrtpsession.h delete mode 100644 gst/rtpmanager/gstrtpssrcdemux.c delete mode 100644 gst/rtpmanager/gstrtpssrcdemux.h delete mode 100644 gst/rtpmanager/rtpjitterbuffer.c delete mode 100644 gst/rtpmanager/rtpjitterbuffer.h delete mode 100644 gst/rtpmanager/rtpsession.c delete mode 100644 gst/rtpmanager/rtpsession.h delete mode 100644 gst/rtpmanager/rtpsource.c delete mode 100644 gst/rtpmanager/rtpsource.h delete mode 100644 gst/rtpmanager/rtpstats.c delete mode 100644 gst/rtpmanager/rtpstats.h (limited to 'gst') diff --git a/gst/rtpmanager/.gitignore b/gst/rtpmanager/.gitignore deleted file mode 100644 index 0962d997..00000000 --- a/gst/rtpmanager/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -gstrtpbin-marshal.h -gstrtpbin-marshal.c diff --git a/gst/rtpmanager/Makefile.am b/gst/rtpmanager/Makefile.am deleted file mode 100644 index 8080f303..00000000 --- a/gst/rtpmanager/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -plugin_LTLIBRARIES = libgstrtpmanager.la - -glib_enum_define = GST_RTP_BIN -glib_enum_prefix = gst_rtp_bin - -include $(top_srcdir)/common/glib-gen.mak - -built_sources = gstrtpbin-marshal.c -built_headers = gstrtpbin-marshal.h - -BUILT_SOURCES = $(built_sources) $(built_headers) - -libgstrtpmanager_la_SOURCES = gstrtpmanager.c \ - gstrtpbin.c \ - gstrtpjitterbuffer.c \ - gstrtpptdemux.c \ - gstrtpssrcdemux.c \ - rtpjitterbuffer.c \ - rtpsession.c \ - rtpsource.c \ - rtpstats.c \ - gstrtpsession.c - -nodist_libgstrtpmanager_la_SOURCES = \ - $(built_sources) - -noinst_HEADERS = gstrtpbin.h \ - gstrtpjitterbuffer.h \ - gstrtpptdemux.h \ - gstrtpssrcdemux.h \ - rtpjitterbuffer.h \ - rtpsession.h \ - rtpsource.h \ - rtpstats.h \ - gstrtpsession.h - -libgstrtpmanager_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ - $(ERROR_CFLAGS) -libgstrtpmanager_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ - -lgstnetbuffer-@GST_MAJORMINOR@ -lgstrtp-@GST_MAJORMINOR@ \ - $(GST_BASE_LIBS) $(GST_LIBS_LIBS) -libgstrtpmanager_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstrtpmanager_la_LIBTOOLFLAGS = --tag=disable-static - -CLEANFILES = $(BUILT_SOURCES) - -EXTRA_DIST = gstrtpbin-marshal.list - - diff --git a/gst/rtpmanager/gstrtpbin-marshal.list b/gst/rtpmanager/gstrtpbin-marshal.list deleted file mode 100644 index ed73e43b..00000000 --- a/gst/rtpmanager/gstrtpbin-marshal.list +++ /dev/null @@ -1,8 +0,0 @@ -UINT:UINT -BOXED:UINT -BOXED:UINT,UINT -OBJECT:UINT -VOID:UINT,OBJECT -VOID:UINT -VOID:UINT,UINT -VOID:OBJECT,OBJECT diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c deleted file mode 100644 index c09b0ab9..00000000 --- a/gst/rtpmanager/gstrtpbin.c +++ /dev/null @@ -1,2458 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -/** - * SECTION:element-gstrtpbin - * @see_also: gstrtpjitterbuffer, gstrtpsession, gstrtpptdemux, gstrtpssrcdemux - * - * RTP bin combines the functions of #GstRtpSession, #GstRtpsSrcDemux, - * #GstRtpJitterBuffer and #GstRtpPtDemux in one element. It allows for multiple - * RTP sessions that will be synchronized together using RTCP SR packets. - * - * #GstRtpBin is configured with a number of request pads that define the - * functionality that is activated, similar to the #GstRtpSession element. - * - * To use #GstRtpBin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session - * number must be specified in the pad name. - * Data received on the recv_rtp_sink_%%d pad will be processed in the gstrtpsession - * manager and after being validated forwarded on #GstRtpsSrcDemux element. Each - * RTP stream is demuxed based on the SSRC and send to a #GstRtpJitterBuffer. After - * the packets are released from the jitterbuffer, they will be forwarded to a - * #GstRtpsSrcDemux element. The #GstRtpsSrcDemux element will demux the packets based - * on the payload type and will create a unique pad recv_rtp_src_%%d_%%d_%%d on - * gstrtpbin with the session number, SSRC and payload type respectively as the pad - * name. - * - * To also use #GstRtpBin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The - * session number must be specified in the pad name. - * - * If you want the session manager to generate and send RTCP packets, request - * the send_rtcp_src_%%d pad with the session number in the pad name. Packet pushed - * on this pad contain SR/RR RTCP reports that should be sent to all participants - * in the session. - * - * To use #GstRtpBin as a sender, request a send_rtp_sink_%%d pad, which will - * automatically create a send_rtp_src_%%d pad. If the session number is not provided, - * the pad from the lowest available session will be returned. The session manager will modify the - * SSRC in the RTP packets to its own SSRC and wil forward the packets on the - * send_rtp_src_%%d pad after updating its internal state. - * - * The session manager needs the clock-rate of the payload types it is handling - * and will signal the #GstRtpSession::request-pt-map signal when it needs such a - * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map - * signal. - * - * - * Example pipelines - * |[ - * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \ - * gstrtpbin ! rtptheoradepay ! theoradec ! xvimagesink - * ]| Receive RTP data from port 5000 and send to the session 0 in gstrtpbin. - * |[ - * gst-launch gstrtpbin name=rtpbin \ - * v4l2src ! ffmpegcolorspace ! ffenc_h263 ! rtph263ppay ! rtpbin.send_rtp_sink_0 \ - * rtpbin.send_rtp_src_0 ! udpsink port=5000 \ - * rtpbin.send_rtcp_src_0 ! udpsink port=5001 sync=false async=false \ - * udpsrc port=5005 ! rtpbin.recv_rtcp_sink_0 \ - * audiotestsrc ! amrnbenc ! rtpamrpay ! rtpbin.send_rtp_sink_1 \ - * rtpbin.send_rtp_src_1 ! udpsink port=5002 \ - * rtpbin.send_rtcp_src_1 ! udpsink port=5003 sync=false async=false \ - * udpsrc port=5007 ! rtpbin.recv_rtcp_sink_1 - * ]| Encode and payload H263 video captured from a v4l2src. Encode and payload AMR - * audio generated from audiotestsrc. The video is sent to session 0 in rtpbin - * and the audio is sent to session 1. Video packets are sent on UDP port 5000 - * and audio packets on port 5002. The video RTCP packets for session 0 are sent - * on port 5001 and the audio RTCP packets for session 0 are sent on port 5003. - * RTCP packets for session 0 are received on port 5005 and RTCP for session 1 - * is received on port 5007. Since RTCP packets from the sender should be sent - * as soon as possible and do not participate in preroll, sync=false and - * async=false is configured on udpsink - * |[ - * gst-launch -v gstrtpbin name=rtpbin \ - * udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H263-1998" \ - * port=5000 ! rtpbin.recv_rtp_sink_0 \ - * rtpbin. ! rtph263pdepay ! ffdec_h263 ! xvimagesink \ - * udpsrc port=5001 ! rtpbin.recv_rtcp_sink_0 \ - * rtpbin.send_rtcp_src_0 ! udpsink port=5005 sync=false async=false \ - * udpsrc caps="application/x-rtp,media=(string)audio,clock-rate=(int)8000,encoding-name=(string)AMR,encoding-params=(string)1,octet-align=(string)1" \ - * port=5002 ! rtpbin.recv_rtp_sink_1 \ - * rtpbin. ! rtpamrdepay ! amrnbdec ! alsasink \ - * udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1 \ - * rtpbin.send_rtcp_src_1 ! udpsink port=5007 sync=false async=false - * ]| Receive H263 on port 5000, send it through rtpbin in session 0, depayload, - * decode and display the video. - * Receive AMR on port 5002, send it through rtpbin in session 1, depayload, - * decode and play the audio. - * Receive server RTCP packets for session 0 on port 5001 and RTCP packets for - * session 1 on port 5003. These packets will be used for session management and - * synchronisation. - * Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1 - * on port 5007. - * - * - * Last reviewed on 2007-08-30 (0.10.6) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include - -#include -#include - -#include "gstrtpbin-marshal.h" -#include "gstrtpbin.h" -#include "rtpsession.h" -#include "gstrtpsession.h" -#include "gstrtpjitterbuffer.h" - -GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug); -#define GST_CAT_DEFAULT gst_rtp_bin_debug - -/* elementfactory information */ -static const GstElementDetails rtpbin_details = GST_ELEMENT_DETAILS ("RTP Bin", - "Filter/Network/RTP", - "Implement an RTP bin", - "Wim Taymans "); - -/* sink pads */ -static GstStaticPadTemplate rtpbin_recv_rtp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp") - ); - -static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -static GstStaticPadTemplate rtpbin_send_rtp_sink_template = -GST_STATIC_PAD_TEMPLATE ("send_rtp_sink_%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp") - ); - -/* src pads */ -static GstStaticPadTemplate rtpbin_recv_rtp_src_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp") - ); - -static GstStaticPadTemplate rtpbin_send_rtcp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%d", - GST_PAD_SRC, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -static GstStaticPadTemplate rtpbin_send_rtp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp") - ); - -#define GST_RTP_BIN_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BIN, GstRtpBinPrivate)) - -#define GST_RTP_BIN_LOCK(bin) g_mutex_lock ((bin)->priv->bin_lock) -#define GST_RTP_BIN_UNLOCK(bin) g_mutex_unlock ((bin)->priv->bin_lock) - -/* lock to protect dynamic callbacks, like pad-added and new ssrc. */ -#define GST_RTP_BIN_DYN_LOCK(bin) g_mutex_lock ((bin)->priv->dyn_lock) -#define GST_RTP_BIN_DYN_UNLOCK(bin) g_mutex_unlock ((bin)->priv->dyn_lock) - -/* lock for shutdown */ -#define GST_RTP_BIN_SHUTDOWN_LOCK(bin,label) \ -G_STMT_START { \ - if (g_atomic_int_get (&bin->priv->shutdown)) \ - goto label; \ - GST_RTP_BIN_DYN_LOCK (bin); \ - if (g_atomic_int_get (&bin->priv->shutdown)) { \ - GST_RTP_BIN_DYN_UNLOCK (bin); \ - goto label; \ - } \ -} G_STMT_END - -/* unlock for shutdown */ -#define GST_RTP_BIN_SHUTDOWN_UNLOCK(bin) \ - GST_RTP_BIN_DYN_UNLOCK (bin); \ - -struct _GstRtpBinPrivate -{ - GMutex *bin_lock; - - /* lock protecting dynamic adding/removing */ - GMutex *dyn_lock; - - /* the time when we went to playing */ - GstClockTime ntp_ns_base; - - /* if we are shutting down or not */ - gint shutdown; -}; - -/* signals and args */ -enum -{ - SIGNAL_REQUEST_PT_MAP, - SIGNAL_CLEAR_PT_MAP, - SIGNAL_RESET_SYNC, - SIGNAL_GET_INTERNAL_SESSION, - - SIGNAL_ON_NEW_SSRC, - SIGNAL_ON_SSRC_COLLISION, - SIGNAL_ON_SSRC_VALIDATED, - SIGNAL_ON_SSRC_ACTIVE, - SIGNAL_ON_SSRC_SDES, - SIGNAL_ON_BYE_SSRC, - SIGNAL_ON_BYE_TIMEOUT, - SIGNAL_ON_TIMEOUT, - SIGNAL_ON_SENDER_TIMEOUT, - SIGNAL_ON_NPT_STOP, - LAST_SIGNAL -}; - -#define DEFAULT_LATENCY_MS 200 -#define DEFAULT_SDES NULL -#define DEFAULT_DO_LOST FALSE - -enum -{ - PROP_0, - PROP_LATENCY, - PROP_SDES, - PROP_DO_LOST, - PROP_LAST -}; - -/* helper objects */ -typedef struct _GstRtpBinSession GstRtpBinSession; -typedef struct _GstRtpBinStream GstRtpBinStream; -typedef struct _GstRtpBinClient GstRtpBinClient; - -static guint gst_rtp_bin_signals[LAST_SIGNAL] = { 0 }; - -static GstCaps *pt_map_requested (GstElement * element, guint pt, - GstRtpBinSession * session); -static void free_stream (GstRtpBinStream * stream); - -/* Manages the RTP stream for one SSRC. - * - * We pipe the stream (comming from the SSRC demuxer) into a jitterbuffer. - * If we see an SDES RTCP packet that links multiple SSRCs together based on a - * common CNAME, we create a GstRtpBinClient structure to group the SSRCs - * together (see below). - */ -struct _GstRtpBinStream -{ - /* the SSRC of this stream */ - guint32 ssrc; - - /* parent bin */ - GstRtpBin *bin; - - /* the session this SSRC belongs to */ - GstRtpBinSession *session; - - /* the jitterbuffer of the SSRC */ - GstElement *buffer; - gulong buffer_handlesync_sig; - gulong buffer_ptreq_sig; - gulong buffer_ntpstop_sig; - - /* the PT demuxer of the SSRC */ - GstElement *demux; - gulong demux_newpad_sig; - gulong demux_padremoved_sig; - gulong demux_ptreq_sig; - gulong demux_pt_change_sig; - - /* if we have calculated a valid unix_delta for this stream */ - gboolean have_sync; - /* mapping to local RTP and NTP time */ - gint64 unix_delta; -}; - -#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock) -#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock ((sess)->lock) - -/* Manages the receiving end of the packets. - * - * There is one such structure for each RTP session (audio/video/...). - * We get the RTP/RTCP packets and stuff them into the session manager. From - * there they are pushed into an SSRC demuxer that splits the stream based on - * SSRC. Each of the SSRC streams go into their own jitterbuffer (managed with - * the GstRtpBinStream above). - */ -struct _GstRtpBinSession -{ - /* session id */ - gint id; - /* the parent bin */ - GstRtpBin *bin; - /* the session element */ - GstElement *session; - /* the SSRC demuxer */ - GstElement *demux; - gulong demux_newpad_sig; - gulong demux_padremoved_sig; - - GMutex *lock; - - /* list of GstRtpBinStream */ - GSList *streams; - - /* mapping of payload type to caps */ - GHashTable *ptmap; - - /* the pads of the session */ - GstPad *recv_rtp_sink; - GstPad *recv_rtp_sink_ghost; - GstPad *recv_rtp_src; - GstPad *recv_rtcp_sink; - GstPad *recv_rtcp_sink_ghost; - GstPad *sync_src; - GstPad *send_rtp_sink; - GstPad *send_rtp_sink_ghost; - GstPad *send_rtp_src; - GstPad *send_rtp_src_ghost; - GstPad *send_rtcp_src; - GstPad *send_rtcp_src_ghost; -}; - -/* Manages the RTP streams that come from one client and should therefore be - * synchronized. - */ -struct _GstRtpBinClient -{ - /* the common CNAME for the streams */ - gchar *cname; - guint cname_len; - - /* the streams */ - guint nstreams; - GSList *streams; -}; - -/* find a session with the given id. Must be called with RTP_BIN_LOCK */ -static GstRtpBinSession * -find_session_by_id (GstRtpBin * rtpbin, gint id) -{ - GSList *walk; - - for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) { - GstRtpBinSession *sess = (GstRtpBinSession *) walk->data; - - if (sess->id == id) - return sess; - } - return NULL; -} - -/* find a session with the given request pad. Must be called with RTP_BIN_LOCK */ -static GstRtpBinSession * -find_session_by_pad (GstRtpBin * rtpbin, GstPad * pad) -{ - GSList *walk; - - for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) { - GstRtpBinSession *sess = (GstRtpBinSession *) walk->data; - - if ((sess->recv_rtp_sink_ghost == pad) || - (sess->recv_rtcp_sink_ghost == pad) || - (sess->send_rtp_sink_ghost == pad) - || (sess->send_rtcp_src_ghost == pad)) - return sess; - } - return NULL; -} - -static void -on_new_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_NEW_SSRC], 0, - sess->id, ssrc); -} - -static void -on_ssrc_collision (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_COLLISION], 0, - sess->id, ssrc); -} - -static void -on_ssrc_validated (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_VALIDATED], 0, - sess->id, ssrc); -} - -static void -on_ssrc_active (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_ACTIVE], 0, - sess->id, ssrc); -} - -static void -on_ssrc_sdes (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_SDES], 0, - sess->id, ssrc); -} - -static void -on_bye_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_BYE_SSRC], 0, - sess->id, ssrc); -} - -static void -on_bye_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_BYE_TIMEOUT], 0, - sess->id, ssrc); -} - -static void -on_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_TIMEOUT], 0, - sess->id, ssrc); -} - -static void -on_sender_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) -{ - g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT], 0, - sess->id, ssrc); -} - -static void -on_npt_stop (GstElement * jbuf, GstRtpBinStream * stream) -{ - g_signal_emit (stream->bin, gst_rtp_bin_signals[SIGNAL_ON_NPT_STOP], 0, - stream->session->id, stream->ssrc); -} - -/* must be called with the SESSION lock */ -static GstRtpBinStream * -find_stream_by_ssrc (GstRtpBinSession * session, guint32 ssrc) -{ - GSList *walk; - - for (walk = session->streams; walk; walk = g_slist_next (walk)) { - GstRtpBinStream *stream = (GstRtpBinStream *) walk->data; - - if (stream->ssrc == ssrc) - return stream; - } - return NULL; -} - -static void -ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad, - GstRtpBinSession * session) -{ - GstRtpBinStream *stream = NULL; - - GST_RTP_SESSION_LOCK (session); - if ((stream = find_stream_by_ssrc (session, ssrc))) - session->streams = g_slist_remove (session->streams, stream); - GST_RTP_SESSION_UNLOCK (session); - - if (stream) - free_stream (stream); -} - -/* create a session with the given id. Must be called with RTP_BIN_LOCK */ -static GstRtpBinSession * -create_session (GstRtpBin * rtpbin, gint id) -{ - GstRtpBinSession *sess; - GstElement *session, *demux; - GstState target; - - if (!(session = gst_element_factory_make ("gstrtpsession", NULL))) - goto no_session; - - if (!(demux = gst_element_factory_make ("gstrtpssrcdemux", NULL))) - goto no_demux; - - sess = g_new0 (GstRtpBinSession, 1); - sess->lock = g_mutex_new (); - sess->id = id; - sess->bin = rtpbin; - sess->session = session; - sess->demux = demux; - sess->ptmap = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) gst_caps_unref); - rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess); - - /* set NTP base or new session */ - g_object_set (session, "ntp-ns-base", rtpbin->priv->ntp_ns_base, NULL); - /* configure SDES items */ - GST_OBJECT_LOCK (rtpbin); - g_object_set (session, "sdes", rtpbin->sdes, NULL); - GST_OBJECT_UNLOCK (rtpbin); - - /* provide clock_rate to the session manager when needed */ - g_signal_connect (session, "request-pt-map", - (GCallback) pt_map_requested, sess); - - g_signal_connect (sess->session, "on-new-ssrc", - (GCallback) on_new_ssrc, sess); - g_signal_connect (sess->session, "on-ssrc-collision", - (GCallback) on_ssrc_collision, sess); - g_signal_connect (sess->session, "on-ssrc-validated", - (GCallback) on_ssrc_validated, sess); - g_signal_connect (sess->session, "on-ssrc-active", - (GCallback) on_ssrc_active, sess); - g_signal_connect (sess->session, "on-ssrc-sdes", - (GCallback) on_ssrc_sdes, sess); - g_signal_connect (sess->session, "on-bye-ssrc", - (GCallback) on_bye_ssrc, sess); - g_signal_connect (sess->session, "on-bye-timeout", - (GCallback) on_bye_timeout, sess); - g_signal_connect (sess->session, "on-timeout", (GCallback) on_timeout, sess); - g_signal_connect (sess->session, "on-sender-timeout", - (GCallback) on_sender_timeout, sess); - - gst_bin_add (GST_BIN_CAST (rtpbin), session); - gst_bin_add (GST_BIN_CAST (rtpbin), demux); - - GST_OBJECT_LOCK (rtpbin); - target = GST_STATE_TARGET (rtpbin); - GST_OBJECT_UNLOCK (rtpbin); - - /* change state only to what's needed */ - gst_element_set_state (demux, target); - gst_element_set_state (session, target); - - return sess; - - /* ERRORS */ -no_session: - { - g_warning ("gstrtpbin: could not create gstrtpsession element"); - return NULL; - } -no_demux: - { - gst_object_unref (session); - g_warning ("gstrtpbin: could not create gstrtpssrcdemux element"); - return NULL; - } -} - -static void -free_session (GstRtpBinSession * sess, GstRtpBin * bin) -{ - GST_DEBUG_OBJECT (bin, "freeing session %p", sess); - - gst_element_set_state (sess->demux, GST_STATE_NULL); - gst_element_set_state (sess->session, GST_STATE_NULL); - - if (sess->recv_rtp_sink != NULL) { - gst_element_release_request_pad (sess->session, sess->recv_rtp_sink); - gst_object_unref (sess->recv_rtp_sink); - } - if (sess->recv_rtp_src != NULL) - gst_object_unref (sess->recv_rtp_src); - if (sess->recv_rtcp_sink != NULL) { - gst_element_release_request_pad (sess->session, sess->recv_rtcp_sink); - gst_object_unref (sess->recv_rtcp_sink); - } - if (sess->sync_src != NULL) - gst_object_unref (sess->sync_src); - if (sess->send_rtp_sink != NULL) { - gst_element_release_request_pad (sess->session, sess->send_rtp_sink); - gst_object_unref (sess->send_rtp_sink); - } - if (sess->send_rtp_src != NULL) - gst_object_unref (sess->send_rtp_src); - if (sess->send_rtcp_src != NULL) { - gst_element_release_request_pad (sess->session, sess->send_rtcp_src); - gst_object_unref (sess->send_rtcp_src); - } - - gst_bin_remove (GST_BIN_CAST (bin), sess->session); - gst_bin_remove (GST_BIN_CAST (bin), sess->demux); - - g_slist_foreach (sess->streams, (GFunc) free_stream, NULL); - g_slist_free (sess->streams); - - g_mutex_free (sess->lock); - g_hash_table_destroy (sess->ptmap); - - g_free (sess); -} - -/* get the payload type caps for the specific payload @pt in @session */ -static GstCaps * -get_pt_map (GstRtpBinSession * session, guint pt) -{ - GstCaps *caps = NULL; - GstRtpBin *bin; - GValue ret = { 0 }; - GValue args[3] = { {0}, {0}, {0} }; - - GST_DEBUG ("searching pt %d in cache", pt); - - GST_RTP_SESSION_LOCK (session); - - /* first look in the cache */ - caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt)); - if (caps) { - gst_caps_ref (caps); - goto done; - } - - bin = session->bin; - - GST_DEBUG ("emiting signal for pt %d in session %d", pt, session->id); - - /* not in cache, send signal to request caps */ - g_value_init (&args[0], GST_TYPE_ELEMENT); - g_value_set_object (&args[0], bin); - g_value_init (&args[1], G_TYPE_UINT); - g_value_set_uint (&args[1], session->id); - g_value_init (&args[2], G_TYPE_UINT); - g_value_set_uint (&args[2], pt); - - g_value_init (&ret, GST_TYPE_CAPS); - g_value_set_boxed (&ret, NULL); - - GST_RTP_SESSION_UNLOCK (session); - - g_signal_emitv (args, gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret); - - GST_RTP_SESSION_LOCK (session); - - g_value_unset (&args[0]); - g_value_unset (&args[1]); - g_value_unset (&args[2]); - - /* look in the cache again because we let the lock go */ - caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt)); - if (caps) { - gst_caps_ref (caps); - g_value_unset (&ret); - goto done; - } - - caps = (GstCaps *) g_value_dup_boxed (&ret); - g_value_unset (&ret); - if (!caps) - goto no_caps; - - GST_DEBUG ("caching pt %d as %" GST_PTR_FORMAT, pt, caps); - - /* store in cache, take additional ref */ - g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt), - gst_caps_ref (caps)); - -done: - GST_RTP_SESSION_UNLOCK (session); - - return caps; - - /* ERRORS */ -no_caps: - { - GST_RTP_SESSION_UNLOCK (session); - GST_DEBUG ("no pt map could be obtained"); - return NULL; - } -} - -static gboolean -return_true (gpointer key, gpointer value, gpointer user_data) -{ - return TRUE; -} - -static void -gst_rtp_bin_reset_sync (GstRtpBin * rtpbin) -{ - GSList *clients, *streams; - - GST_DEBUG_OBJECT (rtpbin, "Reset sync on all clients"); - - GST_RTP_BIN_LOCK (rtpbin); - for (clients = rtpbin->clients; clients; clients = g_slist_next (clients)) { - GstRtpBinClient *client = (GstRtpBinClient *) clients->data; - - /* reset sync on all streams for this client */ - for (streams = client->streams; streams; streams = g_slist_next (streams)) { - GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; - - /* make use require a new SR packet for this stream before we attempt new - * lip-sync */ - stream->have_sync = FALSE; - stream->unix_delta = 0; - } - } - GST_RTP_BIN_UNLOCK (rtpbin); -} - -static void -gst_rtp_bin_clear_pt_map (GstRtpBin * bin) -{ - GSList *sessions, *streams; - - GST_RTP_BIN_LOCK (bin); - GST_DEBUG_OBJECT (bin, "clearing pt map"); - for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) { - GstRtpBinSession *session = (GstRtpBinSession *) sessions->data; - - GST_DEBUG_OBJECT (bin, "clearing session %p", session); - g_signal_emit_by_name (session->session, "clear-pt-map", NULL); - - GST_RTP_SESSION_LOCK (session); - g_hash_table_foreach_remove (session->ptmap, return_true, NULL); - - for (streams = session->streams; streams; streams = g_slist_next (streams)) { - GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; - - GST_DEBUG_OBJECT (bin, "clearing stream %p", stream); - g_signal_emit_by_name (stream->buffer, "clear-pt-map", NULL); - g_signal_emit_by_name (stream->demux, "clear-pt-map", NULL); - } - GST_RTP_SESSION_UNLOCK (session); - } - GST_RTP_BIN_UNLOCK (bin); - - /* reset sync too */ - gst_rtp_bin_reset_sync (bin); -} - -static RTPSession * -gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id) -{ - RTPSession *internal_session = NULL; - GstRtpBinSession *session; - - GST_RTP_BIN_LOCK (bin); - GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %d", - session_id); - session = find_session_by_id (bin, (gint) session_id); - if (session) { - g_object_get (session->session, "internal-session", &internal_session, - NULL); - } - GST_RTP_BIN_UNLOCK (bin); - - return internal_session; -} - -static void -gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin, - const gchar * name, const GValue * value) -{ - GSList *sessions, *streams; - - GST_RTP_BIN_LOCK (bin); - for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) { - GstRtpBinSession *session = (GstRtpBinSession *) sessions->data; - - GST_RTP_SESSION_LOCK (session); - for (streams = session->streams; streams; streams = g_slist_next (streams)) { - GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; - - g_object_set_property (G_OBJECT (stream->buffer), name, value); - } - GST_RTP_SESSION_UNLOCK (session); - } - GST_RTP_BIN_UNLOCK (bin); -} - -/* get a client with the given SDES name. Must be called with RTP_BIN_LOCK */ -static GstRtpBinClient * -get_client (GstRtpBin * bin, guint8 len, guint8 * data, gboolean * created) -{ - GstRtpBinClient *result = NULL; - GSList *walk; - - for (walk = bin->clients; walk; walk = g_slist_next (walk)) { - GstRtpBinClient *client = (GstRtpBinClient *) walk->data; - - if (len != client->cname_len) - continue; - - if (!strncmp ((gchar *) data, client->cname, client->cname_len)) { - GST_DEBUG_OBJECT (bin, "found existing client %p with CNAME %s", client, - client->cname); - result = client; - break; - } - } - - /* nothing found, create one */ - if (result == NULL) { - result = g_new0 (GstRtpBinClient, 1); - result->cname = g_strndup ((gchar *) data, len); - result->cname_len = len; - bin->clients = g_slist_prepend (bin->clients, result); - GST_DEBUG_OBJECT (bin, "created new client %p with CNAME %s", result, - result->cname); - } - return result; -} - -static void -free_client (GstRtpBinClient * client, GstRtpBin * bin) -{ - GST_DEBUG_OBJECT (bin, "freeing client %p", client); - g_slist_free (client->streams); - g_free (client->cname); - g_free (client); -} - -/* associate a stream to the given CNAME. This will make sure all streams for - * that CNAME are synchronized together. - * Must be called with GST_RTP_BIN_LOCK */ -static void -gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, - guint8 * data, guint64 last_unix, guint64 last_extrtptime, - guint64 clock_base, guint64 clock_base_time, guint clock_rate) -{ - GstRtpBinClient *client; - gboolean created; - GSList *walk; - guint64 local_unix; - guint64 local_rtp; - - /* first find or create the CNAME */ - client = get_client (bin, len, data, &created); - - /* find stream in the client */ - for (walk = client->streams; walk; walk = g_slist_next (walk)) { - GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data; - - if (ostream == stream) - break; - } - /* not found, add it to the list */ - if (walk == NULL) { - GST_DEBUG_OBJECT (bin, - "new association of SSRC %08x with client %p with CNAME %s", - stream->ssrc, client, client->cname); - client->streams = g_slist_prepend (client->streams, stream); - client->nstreams++; - } else { - GST_DEBUG_OBJECT (bin, - "found association of SSRC %08x with client %p with CNAME %s", - stream->ssrc, client, client->cname); - } - - /* take the extended rtptime we found in the SR packet and map it to the - * local rtptime. The local rtp time is used to construct timestamps on the - * buffers. */ - local_rtp = last_extrtptime - clock_base; - - GST_DEBUG_OBJECT (bin, - "base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT - ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", clock_base, - last_extrtptime, local_rtp, clock_rate); - - /* calculate local NTP time in gstreamer timestamp, we essentially perform the - * same conversion that a jitterbuffer would use to convert an rtp timestamp - * into a corresponding gstreamer timestamp. */ - local_unix = gst_util_uint64_scale_int (local_rtp, GST_SECOND, clock_rate); - local_unix += clock_base_time; - - /* calculate delta between server and receiver. last_unix is created by - * converting the ntptime in the last SR packet to a gstreamer timestamp. This - * delta expresses the difference to our timeline and the server timeline. */ - stream->unix_delta = last_unix - local_unix; - stream->have_sync = TRUE; - - GST_DEBUG_OBJECT (bin, - "local UNIX %" G_GUINT64_FORMAT ", remote UNIX %" G_GUINT64_FORMAT - ", delta %" G_GINT64_FORMAT, local_unix, last_unix, stream->unix_delta); - - /* recalc inter stream playout offset, but only if there is more than one - * stream. */ - if (client->nstreams > 1) { - gint64 min; - - /* calculate the min of all deltas, ignoring streams that did not yet have a - * valid unix_delta because we did not yet receive an SR packet for those - * streams. - * We calculate the mininum because we would like to only apply positive - * offsets to streams, delaying their playback instead of trying to speed up - * other streams (which might be imposible when we have to create negative - * latencies). - * The stream that has the smallest diff is selected as the reference stream, - * all other streams will have a positive offset to this difference. */ - min = G_MAXINT64; - for (walk = client->streams; walk; walk = g_slist_next (walk)) { - GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data; - - if (!ostream->have_sync) - continue; - - if (ostream->unix_delta < min) - min = ostream->unix_delta; - } - - GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client, - min); - - /* calculate offsets for each stream */ - for (walk = client->streams; walk; walk = g_slist_next (walk)) { - GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data; - gint64 ts_offset, prev_ts_offset; - - /* ignore streams for which we didn't receive an SR packet yet, we - * can't synchronize them yet. We can however sync other streams just - * fine. */ - if (!ostream->have_sync) - continue; - - /* calculate offset to our reference stream, this should always give a - * positive number. */ - ts_offset = ostream->unix_delta - min; - - g_object_get (ostream->buffer, "ts-offset", &prev_ts_offset, NULL); - - /* delta changed, see how much */ - if (prev_ts_offset != ts_offset) { - gint64 diff; - - if (prev_ts_offset > ts_offset) - diff = prev_ts_offset - ts_offset; - else - diff = ts_offset - prev_ts_offset; - - GST_DEBUG_OBJECT (bin, - "ts-offset %" G_GUINT64_FORMAT ", prev %" G_GUINT64_FORMAT - ", diff: %" G_GINT64_FORMAT, ts_offset, prev_ts_offset, diff); - - /* only change diff when it changed more than 4 milliseconds. This - * compensates for rounding errors in NTP to RTP timestamp - * conversions */ - if (diff > 4 * GST_MSECOND && diff < (3 * GST_SECOND)) { - g_object_set (ostream->buffer, "ts-offset", ts_offset, NULL); - } - } - GST_DEBUG_OBJECT (bin, "stream SSRC %08x, delta %" G_GINT64_FORMAT, - ostream->ssrc, ts_offset); - } - } - return; -} - -#define GST_RTCP_BUFFER_FOR_PACKETS(b,buffer,packet) \ - for ((b) = gst_rtcp_buffer_get_first_packet ((buffer), (packet)); (b); \ - (b) = gst_rtcp_packet_move_to_next ((packet))) - -#define GST_RTCP_SDES_FOR_ITEMS(b,packet) \ - for ((b) = gst_rtcp_packet_sdes_first_item ((packet)); (b); \ - (b) = gst_rtcp_packet_sdes_next_item ((packet))) - -#define GST_RTCP_SDES_FOR_ENTRIES(b,packet) \ - for ((b) = gst_rtcp_packet_sdes_first_entry ((packet)); (b); \ - (b) = gst_rtcp_packet_sdes_next_entry ((packet))) - -static void -gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s, - GstRtpBinStream * stream) -{ - GstRtpBin *bin; - GstRTCPPacket packet; - guint32 ssrc; - guint64 ntptime; - gboolean have_sr, have_sdes; - gboolean more; - guint64 clock_base; - guint64 clock_base_time; - guint clock_rate; - guint64 extrtptime; - GstBuffer *buffer; - - bin = stream->bin; - - GST_DEBUG_OBJECT (bin, "sync handler called"); - - /* get the last relation between the rtp timestamps and the gstreamer - * timestamps. We get this info directly from the jitterbuffer which - * constructs gstreamer timestamps from rtp timestamps and so it know exactly - * what the current situation is. */ - clock_base = g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime")); - clock_base_time = - g_value_get_uint64 (gst_structure_get_value (s, "base-time")); - clock_rate = g_value_get_uint (gst_structure_get_value (s, "clock-rate")); - extrtptime = - g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime")); - buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer")); - - have_sr = FALSE; - have_sdes = FALSE; - GST_RTCP_BUFFER_FOR_PACKETS (more, buffer, &packet) { - /* 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: - /* only parse first. There is only supposed to be one SR in the packet - * but we will deal with malformed packets gracefully */ - if (have_sr) - break; - /* get NTP and RTP times */ - gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, &ntptime, NULL, - NULL, NULL); - - GST_DEBUG_OBJECT (bin, "received sync packet from SSRC %08x", ssrc); - /* ignore SR that is not ours */ - if (ssrc != stream->ssrc) - continue; - - have_sr = TRUE; - break; - case GST_RTCP_TYPE_SDES: - { - gboolean more_items, more_entries; - - /* only deal with first SDES, there is only supposed to be one SDES in - * the RTCP packet but we deal with bad packets gracefully. Also bail - * out if we have not seen an SR item yet. */ - if (have_sdes || !have_sr) - break; - - GST_RTCP_SDES_FOR_ITEMS (more_items, &packet) { - /* skip items that are not about the SSRC of the sender */ - if (gst_rtcp_packet_sdes_get_ssrc (&packet) != ssrc) - continue; - - /* find the CNAME entry */ - GST_RTCP_SDES_FOR_ENTRIES (more_entries, &packet) { - GstRTCPSDESType type; - guint8 len; - guint8 *data; - - gst_rtcp_packet_sdes_get_entry (&packet, &type, &len, &data); - - if (type == GST_RTCP_SDES_CNAME) { - GST_RTP_BIN_LOCK (bin); - /* associate the stream to CNAME */ - gst_rtp_bin_associate (bin, stream, len, data, - gst_rtcp_ntp_to_unix (ntptime), extrtptime, - clock_base, clock_base_time, clock_rate); - GST_RTP_BIN_UNLOCK (bin); - } - } - } - have_sdes = TRUE; - break; - } - default: - /* we can ignore these packets */ - break; - } - } -} - -/* create a new stream with @ssrc in @session. Must be called with - * RTP_SESSION_LOCK. */ -static GstRtpBinStream * -create_stream (GstRtpBinSession * session, guint32 ssrc) -{ - GstElement *buffer, *demux; - GstRtpBinStream *stream; - GstRtpBin *rtpbin; - GstState target; - - if (!(buffer = gst_element_factory_make ("gstrtpjitterbuffer", NULL))) - goto no_jitterbuffer; - - if (!(demux = gst_element_factory_make ("gstrtpptdemux", NULL))) - goto no_demux; - - rtpbin = session->bin; - - stream = g_new0 (GstRtpBinStream, 1); - stream->ssrc = ssrc; - stream->bin = rtpbin; - stream->session = session; - stream->buffer = buffer; - stream->demux = demux; - stream->have_sync = FALSE; - stream->unix_delta = 0; - session->streams = g_slist_prepend (session->streams, stream); - - /* provide clock_rate to the jitterbuffer when needed */ - stream->buffer_ptreq_sig = g_signal_connect (buffer, "request-pt-map", - (GCallback) pt_map_requested, session); - stream->buffer_ntpstop_sig = g_signal_connect (buffer, "on-npt-stop", - (GCallback) on_npt_stop, stream); - - /* configure latency and packet lost */ - g_object_set (buffer, "latency", rtpbin->latency, NULL); - g_object_set (buffer, "do-lost", rtpbin->do_lost, NULL); - - gst_bin_add (GST_BIN_CAST (rtpbin), demux); - gst_bin_add (GST_BIN_CAST (rtpbin), buffer); - - /* link stuff */ - gst_element_link (buffer, demux); - - GST_OBJECT_LOCK (rtpbin); - target = GST_STATE_TARGET (rtpbin); - GST_OBJECT_UNLOCK (rtpbin); - - /* from sink to source */ - gst_element_set_state (demux, target); - gst_element_set_state (buffer, target); - - return stream; - - /* ERRORS */ -no_jitterbuffer: - { - g_warning ("gstrtpbin: could not create gstrtpjitterbuffer element"); - return NULL; - } -no_demux: - { - gst_object_unref (buffer); - g_warning ("gstrtpbin: could not create gstrtpptdemux element"); - return NULL; - } -} - -static void -free_stream (GstRtpBinStream * stream) -{ - GstRtpBinSession *session; - - session = stream->session; - - g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig); - g_signal_handler_disconnect (stream->demux, stream->demux_ptreq_sig); - g_signal_handler_disconnect (stream->buffer, stream->buffer_handlesync_sig); - g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig); - g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig); - - gst_element_set_state (stream->demux, GST_STATE_NULL); - gst_element_set_state (stream->buffer, GST_STATE_NULL); - - /* now remove this signal, we need this while going to NULL because it to - * do some cleanups */ - g_signal_handler_disconnect (stream->demux, stream->demux_padremoved_sig); - - gst_bin_remove (GST_BIN_CAST (session->bin), stream->buffer); - gst_bin_remove (GST_BIN_CAST (session->bin), stream->demux); - - g_free (stream); -} - -/* GObject vmethods */ -static void gst_rtp_bin_dispose (GObject * object); -static void gst_rtp_bin_finalize (GObject * object); -static void gst_rtp_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_rtp_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -/* GstElement vmethods */ -static GstStateChangeReturn gst_rtp_bin_change_state (GstElement * element, - GstStateChange transition); -static GstPad *gst_rtp_bin_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad); -static void gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message); -static void gst_rtp_bin_clear_pt_map (GstRtpBin * bin); - -GST_BOILERPLATE (GstRtpBin, gst_rtp_bin, GstBin, GST_TYPE_BIN); - -static void -gst_rtp_bin_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - /* sink pads */ - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpbin_send_rtp_sink_template)); - - /* src pads */ - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpbin_recv_rtp_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpbin_send_rtcp_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpbin_send_rtp_src_template)); - - gst_element_class_set_details (element_class, &rtpbin_details); -} - -static void -gst_rtp_bin_class_init (GstRtpBinClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GstBinClass *gstbin_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - gstbin_class = (GstBinClass *) klass; - - g_type_class_add_private (klass, sizeof (GstRtpBinPrivate)); - - gobject_class->dispose = gst_rtp_bin_dispose; - gobject_class->finalize = gst_rtp_bin_finalize; - gobject_class->set_property = gst_rtp_bin_set_property; - gobject_class->get_property = gst_rtp_bin_get_property; - - g_object_class_install_property (gobject_class, PROP_LATENCY, - g_param_spec_uint ("latency", "Buffer latency in ms", - "Default amount of ms to buffer in the jitterbuffers", 0, - G_MAXUINT, DEFAULT_LATENCY_MS, G_PARAM_READWRITE)); - - /** - * GstRtpBin::request-pt-map: - * @rtpbin: the object which received the signal - * @session: the session - * @pt: the pt - * - * Request the payload type as #GstCaps for @pt in @session. - */ - gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP] = - g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, request_pt_map), - NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT_UINT, GST_TYPE_CAPS, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::clear-pt-map: - * @rtpbin: the object which received the signal - * - * Clear all previously cached pt-mapping obtained with - * #GstRtpBin::request-pt-map. - */ - gst_rtp_bin_signals[SIGNAL_CLEAR_PT_MAP] = - g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, - clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, - 0, G_TYPE_NONE); - /** - * GstRtpBin::reset-sync: - * @rtpbin: the object which received the signal - * - * Reset all currently configured lip-sync parameters and require new SR - * packets for all streams before lip-sync is attempted again. - */ - gst_rtp_bin_signals[SIGNAL_RESET_SYNC] = - g_signal_new ("reset-sync", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, - reset_sync), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, - 0, G_TYPE_NONE); - - /** - * GstRtpBin::get-internal-session: - * @rtpbin: the object which received the signal - * @id: the session id - * - * Request the internal RTPSession object as #GObject in session @id. - */ - gst_rtp_bin_signals[SIGNAL_GET_INTERNAL_SESSION] = - g_signal_new ("get-internal-session", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, - get_internal_session), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT, - RTP_TYPE_SESSION, 1, G_TYPE_UINT); - - /** - * GstRtpBin::on-new-ssrc: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of a new SSRC that entered @session. - */ - gst_rtp_bin_signals[SIGNAL_ON_NEW_SSRC] = - g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_new_ssrc), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-ssrc-collision: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify when we have an SSRC collision - */ - gst_rtp_bin_signals[SIGNAL_ON_SSRC_COLLISION] = - g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_collision), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-ssrc-validated: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of a new SSRC that became validated. - */ - gst_rtp_bin_signals[SIGNAL_ON_SSRC_VALIDATED] = - g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_validated), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-ssrc-active: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of a SSRC that is active, i.e., sending RTCP. - */ - gst_rtp_bin_signals[SIGNAL_ON_SSRC_ACTIVE] = - g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_active), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-ssrc-sdes: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of a SSRC that is active, i.e., sending RTCP. - */ - gst_rtp_bin_signals[SIGNAL_ON_SSRC_SDES] = - g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_sdes), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - - /** - * GstRtpBin::on-bye-ssrc: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of an SSRC that became inactive because of a BYE packet. - */ - gst_rtp_bin_signals[SIGNAL_ON_BYE_SSRC] = - g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_bye_ssrc), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-bye-timeout: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of an SSRC that has timed out because of BYE - */ - gst_rtp_bin_signals[SIGNAL_ON_BYE_TIMEOUT] = - g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_bye_timeout), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-timeout: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of an SSRC that has timed out - */ - gst_rtp_bin_signals[SIGNAL_ON_TIMEOUT] = - g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_timeout), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - /** - * GstRtpBin::on-sender-timeout: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify of a sender SSRC that has timed out and became a receiver - */ - gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT] = - g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_sender_timeout), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - - /** - * GstRtpBin::on-npt-stop: - * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC - * - * Notify that SSRC sender has sent data up to the configured NPT stop time. - */ - gst_rtp_bin_signals[SIGNAL_ON_NPT_STOP] = - g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_npt_stop), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); - - g_object_class_install_property (gobject_class, PROP_SDES, - g_param_spec_boxed ("sdes", "SDES", - "The SDES items of this session", - GST_TYPE_STRUCTURE, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, PROP_DO_LOST, - g_param_spec_boolean ("do-lost", "Do Lost", - "Send an event downstream when a packet is lost", DEFAULT_DO_LOST, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state); - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad); - gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_release_pad); - - gstbin_class->handle_message = GST_DEBUG_FUNCPTR (gst_rtp_bin_handle_message); - - klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_bin_clear_pt_map); - klass->reset_sync = GST_DEBUG_FUNCPTR (gst_rtp_bin_reset_sync); - klass->get_internal_session = - GST_DEBUG_FUNCPTR (gst_rtp_bin_get_internal_session); - - GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin"); -} - -static void -gst_rtp_bin_init (GstRtpBin * rtpbin, GstRtpBinClass * klass) -{ - gchar *str; - - rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin); - rtpbin->priv->bin_lock = g_mutex_new (); - rtpbin->priv->dyn_lock = g_mutex_new (); - - rtpbin->latency = DEFAULT_LATENCY_MS; - rtpbin->do_lost = DEFAULT_DO_LOST; - - /* some default SDES entries */ - str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ()); - rtpbin->sdes = gst_structure_new ("application/x-rtp-source-sdes", - "cname", G_TYPE_STRING, str, - "name", G_TYPE_STRING, g_get_real_name (), - "tool", G_TYPE_STRING, "GStreamer", NULL); - g_free (str); -} - -static void -gst_rtp_bin_dispose (GObject * object) -{ - GstRtpBin *rtpbin; - - rtpbin = GST_RTP_BIN (object); - - GST_DEBUG_OBJECT (object, "freeing sessions"); - g_slist_foreach (rtpbin->sessions, (GFunc) free_session, rtpbin); - g_slist_free (rtpbin->sessions); - rtpbin->sessions = NULL; - GST_DEBUG_OBJECT (object, "freeing clients"); - g_slist_foreach (rtpbin->clients, (GFunc) free_client, rtpbin); - g_slist_free (rtpbin->clients); - rtpbin->clients = NULL; - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_rtp_bin_finalize (GObject * object) -{ - GstRtpBin *rtpbin; - - rtpbin = GST_RTP_BIN (object); - - if (rtpbin->sdes) - gst_structure_free (rtpbin->sdes); - - g_mutex_free (rtpbin->priv->bin_lock); - g_mutex_free (rtpbin->priv->dyn_lock); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - - -static void -gst_rtp_bin_set_sdes_struct (GstRtpBin * bin, const GstStructure * sdes) -{ - GSList *item; - - if (sdes == NULL) - return; - - GST_RTP_BIN_LOCK (bin); - - GST_OBJECT_LOCK (bin); - if (bin->sdes) - gst_structure_free (bin->sdes); - bin->sdes = gst_structure_copy (sdes); - - /* store in all sessions */ - for (item = bin->sessions; item; item = g_slist_next (item)) - g_object_set (item->data, "sdes", sdes, NULL); - GST_OBJECT_UNLOCK (bin); - - GST_RTP_BIN_UNLOCK (bin); -} - -static GstStructure * -gst_rtp_bin_get_sdes_struct (GstRtpBin * bin) -{ - GstStructure *result; - - GST_OBJECT_LOCK (bin); - result = gst_structure_copy (bin->sdes); - GST_OBJECT_UNLOCK (bin); - - return result; -} - -static void -gst_rtp_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstRtpBin *rtpbin; - - rtpbin = GST_RTP_BIN (object); - - switch (prop_id) { - case PROP_LATENCY: - GST_RTP_BIN_LOCK (rtpbin); - rtpbin->latency = g_value_get_uint (value); - GST_RTP_BIN_UNLOCK (rtpbin); - /* propegate the property down to the jitterbuffer */ - gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "latency", value); - break; - case PROP_SDES: - gst_rtp_bin_set_sdes_struct (rtpbin, g_value_get_boxed (value)); - break; - case PROP_DO_LOST: - GST_RTP_BIN_LOCK (rtpbin); - rtpbin->do_lost = g_value_get_boolean (value); - GST_RTP_BIN_UNLOCK (rtpbin); - gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "do-lost", value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_rtp_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstRtpBin *rtpbin; - - rtpbin = GST_RTP_BIN (object); - - switch (prop_id) { - case PROP_LATENCY: - GST_RTP_BIN_LOCK (rtpbin); - g_value_set_uint (value, rtpbin->latency); - GST_RTP_BIN_UNLOCK (rtpbin); - break; - case PROP_SDES: - g_value_take_boxed (value, gst_rtp_bin_get_sdes_struct (rtpbin)); - break; - case PROP_DO_LOST: - GST_RTP_BIN_LOCK (rtpbin); - g_value_set_boolean (value, rtpbin->do_lost); - GST_RTP_BIN_UNLOCK (rtpbin); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) -{ - GstRtpBin *rtpbin; - - rtpbin = GST_RTP_BIN (bin); - - switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_ELEMENT: - { - const GstStructure *s = gst_message_get_structure (message); - - /* we change the structure name and add the session ID to it */ - if (gst_structure_has_name (s, "application/x-rtp-source-sdes")) { - GSList *walk; - - /* find the session, the message source has it */ - for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) { - GstRtpBinSession *sess = (GstRtpBinSession *) walk->data; - - /* if we found the session, change message. else we exit the loop and - * leave the message unchanged */ - if (GST_OBJECT_CAST (sess->session) == GST_MESSAGE_SRC (message)) { - message = gst_message_make_writable (message); - s = gst_message_get_structure (message); - - gst_structure_set ((GstStructure *) s, "session", G_TYPE_UINT, - sess->id, NULL); - break; - } - } - } - /* fallthrough to forward the modified message to the parent */ - } - default: - { - GST_BIN_CLASS (parent_class)->handle_message (bin, message); - break; - } - } -} - -static void -calc_ntp_ns_base (GstRtpBin * bin) -{ - GstClockTime now; - GTimeVal current; - GSList *walk; - - /* get the current time and convert it to NTP time in nanoseconds */ - g_get_current_time (¤t); - now = GST_TIMEVAL_TO_TIME (current); - now += (2208988800LL * GST_SECOND); - - GST_RTP_BIN_LOCK (bin); - bin->priv->ntp_ns_base = now; - for (walk = bin->sessions; walk; walk = g_slist_next (walk)) { - GstRtpBinSession *session = (GstRtpBinSession *) walk->data; - - g_object_set (session->session, "ntp-ns-base", now, NULL); - } - GST_RTP_BIN_UNLOCK (bin); - - return; -} - -static GstStateChangeReturn -gst_rtp_bin_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn res; - GstRtpBin *rtpbin; - GstRtpBinPrivate *priv; - - rtpbin = GST_RTP_BIN (element); - priv = rtpbin->priv; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_LOG_OBJECT (rtpbin, "clearing shutdown flag"); - g_atomic_int_set (&priv->shutdown, 0); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - calc_ntp_ns_base (rtpbin); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_LOG_OBJECT (rtpbin, "setting shutdown flag"); - g_atomic_int_set (&priv->shutdown, 1); - /* wait for all callbacks to end by taking the lock. No new callbacks will - * be able to happen as we set the shutdown flag. */ - GST_RTP_BIN_DYN_LOCK (rtpbin); - GST_LOG_OBJECT (rtpbin, "dynamic lock taken, we can continue shutdown"); - GST_RTP_BIN_DYN_UNLOCK (rtpbin); - break; - default: - break; - } - - res = 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: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - return res; -} - -/* a new pad (SSRC) was created in @session. This signal is emited from the - * payload demuxer. */ -static void -new_payload_found (GstElement * element, guint pt, GstPad * pad, - GstRtpBinStream * stream) -{ - GstRtpBin *rtpbin; - GstElementClass *klass; - GstPadTemplate *templ; - gchar *padname; - GstPad *gpad; - - rtpbin = stream->bin; - - GST_DEBUG ("new payload pad %d", pt); - - GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown); - - /* ghost the pad to the parent */ - klass = GST_ELEMENT_GET_CLASS (rtpbin); - templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%d_%d_%d"); - padname = g_strdup_printf ("recv_rtp_src_%d_%u_%d", - stream->session->id, stream->ssrc, pt); - gpad = gst_ghost_pad_new_from_template (padname, pad, templ); - g_free (padname); - g_object_set_data (G_OBJECT (pad), "GstRTPBin.ghostpad", gpad); - - gst_pad_set_caps (gpad, GST_PAD_CAPS (pad)); - gst_pad_set_active (gpad, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad); - GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); - - return; - -shutdown: - { - GST_DEBUG ("ignoring, we are shutting down"); - return; - } -} - -static void -payload_pad_removed (GstElement * element, GstPad * pad, - GstRtpBinStream * stream) -{ - GstRtpBin *rtpbin; - GstPad *gpad; - - rtpbin = stream->bin; - - GST_DEBUG ("payload pad removed"); - - GST_RTP_BIN_DYN_LOCK (rtpbin); - if ((gpad = g_object_get_data (G_OBJECT (pad), "GstRTPBin.ghostpad"))) { - g_object_set_data (G_OBJECT (pad), "GstRTPBin.ghostpad", NULL); - - gst_pad_set_active (gpad, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), gpad); - } - GST_RTP_BIN_DYN_UNLOCK (rtpbin); -} - -static GstCaps * -pt_map_requested (GstElement * element, guint pt, GstRtpBinSession * session) -{ - GstRtpBin *rtpbin; - GstCaps *caps; - - rtpbin = session->bin; - - GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %d in session %d", pt, - session->id); - - caps = get_pt_map (session, pt); - if (!caps) - goto no_caps; - - return caps; - - /* ERRORS */ -no_caps: - { - GST_DEBUG_OBJECT (rtpbin, "could not get caps"); - return NULL; - } -} - -/* emited when caps changed for the session */ -static void -caps_changed (GstPad * pad, GParamSpec * pspec, GstRtpBinSession * session) -{ - GstRtpBin *bin; - GstCaps *caps; - gint payload; - const GstStructure *s; - - bin = session->bin; - - g_object_get (pad, "caps", &caps, NULL); - - if (caps == NULL) - return; - - GST_DEBUG_OBJECT (bin, "got caps %" GST_PTR_FORMAT, caps); - - s = gst_caps_get_structure (caps, 0); - - /* get payload, finish when it's not there */ - if (!gst_structure_get_int (s, "payload", &payload)) - return; - - GST_RTP_SESSION_LOCK (session); - GST_DEBUG_OBJECT (bin, "insert caps for payload %d", payload); - g_hash_table_insert (session->ptmap, GINT_TO_POINTER (payload), caps); - GST_RTP_SESSION_UNLOCK (session); -} - -/* a new pad (SSRC) was created in @session */ -static void -new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad, - GstRtpBinSession * session) -{ - GstRtpBin *rtpbin; - GstRtpBinStream *stream; - GstPad *sinkpad, *srcpad; - gchar *padname; - - rtpbin = session->bin; - - GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x, %s:%s", ssrc, - GST_DEBUG_PAD_NAME (pad)); - - GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown); - - GST_RTP_SESSION_LOCK (session); - - /* create new stream */ - stream = create_stream (session, ssrc); - if (!stream) - goto no_stream; - - /* get pad and link */ - GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTP"); - padname = g_strdup_printf ("src_%d", ssrc); - srcpad = gst_element_get_static_pad (element, padname); - g_free (padname); - sinkpad = gst_element_get_static_pad (stream->buffer, "sink"); - gst_pad_link (srcpad, sinkpad); - gst_object_unref (sinkpad); - gst_object_unref (srcpad); - - GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTCP"); - padname = g_strdup_printf ("rtcp_src_%d", ssrc); - srcpad = gst_element_get_static_pad (element, padname); - g_free (padname); - sinkpad = gst_element_get_request_pad (stream->buffer, "sink_rtcp"); - gst_pad_link (srcpad, sinkpad); - gst_object_unref (sinkpad); - gst_object_unref (srcpad); - - /* connect to the RTCP sync signal from the jitterbuffer */ - GST_DEBUG_OBJECT (rtpbin, "connecting sync signal"); - stream->buffer_handlesync_sig = g_signal_connect (stream->buffer, - "handle-sync", (GCallback) gst_rtp_bin_handle_sync, stream); - - /* connect to the new-pad signal of the payload demuxer, this will expose the - * new pad by ghosting it. */ - stream->demux_newpad_sig = g_signal_connect (stream->demux, - "new-payload-type", (GCallback) new_payload_found, stream); - stream->demux_padremoved_sig = g_signal_connect (stream->demux, - "pad-removed", (GCallback) payload_pad_removed, stream); - - /* connect to the request-pt-map signal. This signal will be emited by the - * demuxer so that it can apply a proper caps on the buffers for the - * depayloaders. */ - stream->demux_ptreq_sig = g_signal_connect (stream->demux, - "request-pt-map", (GCallback) pt_map_requested, session); - - GST_RTP_SESSION_UNLOCK (session); - GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); - - return; - - /* ERRORS */ -shutdown: - { - GST_DEBUG_OBJECT (rtpbin, "we are shutting down"); - return; - } -no_stream: - { - GST_RTP_SESSION_UNLOCK (session); - GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); - GST_DEBUG_OBJECT (rtpbin, "could not create stream"); - return; - } -} - -/* Create a pad for receiving RTP for the session in @name. Must be called with - * RTP_BIN_LOCK. - */ -static GstPad * -create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) -{ - GstPad *sinkdpad; - guint sessid; - GstRtpBinSession *session; - GstPadLinkReturn lres; - - /* first get the session number */ - if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1) - goto no_name; - - GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid); - - /* get or create session */ - session = find_session_by_id (rtpbin, sessid); - if (!session) { - GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid); - /* create session now */ - session = create_session (rtpbin, sessid); - if (session == NULL) - goto create_error; - } - - /* check if pad was requested */ - if (session->recv_rtp_sink_ghost != NULL) - return session->recv_rtp_sink_ghost; - - GST_DEBUG_OBJECT (rtpbin, "getting RTP sink pad"); - /* get recv_rtp pad and store */ - session->recv_rtp_sink = - gst_element_get_request_pad (session->session, "recv_rtp_sink"); - if (session->recv_rtp_sink == NULL) - goto pad_failed; - - g_signal_connect (session->recv_rtp_sink, "notify::caps", - (GCallback) caps_changed, session); - - GST_DEBUG_OBJECT (rtpbin, "getting RTP src pad"); - /* get srcpad, link to SSRCDemux */ - session->recv_rtp_src = - gst_element_get_static_pad (session->session, "recv_rtp_src"); - if (session->recv_rtp_src == NULL) - goto pad_failed; - - GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTP sink pad"); - sinkdpad = gst_element_get_static_pad (session->demux, "sink"); - GST_DEBUG_OBJECT (rtpbin, "linking demuxer RTP sink pad"); - lres = gst_pad_link (session->recv_rtp_src, sinkdpad); - gst_object_unref (sinkdpad); - if (lres != GST_PAD_LINK_OK) - goto link_failed; - - /* connect to the new-ssrc-pad signal of the SSRC demuxer */ - session->demux_newpad_sig = g_signal_connect (session->demux, - "new-ssrc-pad", (GCallback) new_ssrc_pad_found, session); - session->demux_padremoved_sig = g_signal_connect (session->demux, - "removed-ssrc-pad", (GCallback) ssrc_demux_pad_removed, session); - - GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad"); - session->recv_rtp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ); - gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost); - - return session->recv_rtp_sink_ghost; - - /* ERRORS */ -no_name: - { - g_warning ("gstrtpbin: invalid name given"); - return NULL; - } -create_error: - { - /* create_session already warned */ - return NULL; - } -pad_failed: - { - g_warning ("gstrtpbin: failed to get session pad"); - return NULL; - } -link_failed: - { - g_warning ("gstrtpbin: failed to link pads"); - return NULL; - } -} - -static void -remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) -{ - if (session->demux_newpad_sig) { - g_signal_handler_disconnect (session->demux, session->demux_newpad_sig); - session->demux_newpad_sig = 0; - } - if (session->demux_padremoved_sig) { - g_signal_handler_disconnect (session->demux, session->demux_padremoved_sig); - session->demux_padremoved_sig = 0; - } - if (session->recv_rtp_src) { - gst_object_unref (session->recv_rtp_src); - session->recv_rtp_src = NULL; - } - if (session->recv_rtp_sink) { - gst_element_release_request_pad (session->session, session->recv_rtp_sink); - gst_object_unref (session->recv_rtp_sink); - session->recv_rtp_sink = NULL; - } - if (session->recv_rtp_sink_ghost) { - gst_pad_set_active (session->recv_rtp_sink_ghost, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), - session->recv_rtp_sink_ghost); - session->recv_rtp_sink_ghost = NULL; - } -} - -/* Create a pad for receiving RTCP for the session in @name. Must be called with - * RTP_BIN_LOCK. - */ -static GstPad * -create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, - const gchar * name) -{ - guint sessid; - GstRtpBinSession *session; - GstPad *sinkdpad; - GstPadLinkReturn lres; - - /* first get the session number */ - if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1) - goto no_name; - - GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid); - - /* get or create the session */ - session = find_session_by_id (rtpbin, sessid); - if (!session) { - GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid); - /* create session now */ - session = create_session (rtpbin, sessid); - if (session == NULL) - goto create_error; - } - - /* check if pad was requested */ - if (session->recv_rtcp_sink_ghost != NULL) - return session->recv_rtcp_sink_ghost; - - /* get recv_rtp pad and store */ - GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad"); - session->recv_rtcp_sink = - gst_element_get_request_pad (session->session, "recv_rtcp_sink"); - if (session->recv_rtcp_sink == NULL) - goto pad_failed; - - /* get srcpad, link to SSRCDemux */ - GST_DEBUG_OBJECT (rtpbin, "getting sync src pad"); - session->sync_src = gst_element_get_static_pad (session->session, "sync_src"); - if (session->sync_src == NULL) - goto pad_failed; - - GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTCP sink pad"); - sinkdpad = gst_element_get_static_pad (session->demux, "rtcp_sink"); - lres = gst_pad_link (session->sync_src, sinkdpad); - gst_object_unref (sinkdpad); - if (lres != GST_PAD_LINK_OK) - goto link_failed; - - session->recv_rtcp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ); - gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), - session->recv_rtcp_sink_ghost); - - return session->recv_rtcp_sink_ghost; - - /* ERRORS */ -no_name: - { - g_warning ("gstrtpbin: invalid name given"); - return NULL; - } -create_error: - { - /* create_session already warned */ - return NULL; - } -pad_failed: - { - g_warning ("gstrtpbin: failed to get session pad"); - return NULL; - } -link_failed: - { - g_warning ("gstrtpbin: failed to link pads"); - return NULL; - } -} - -static void -remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session) -{ - if (session->recv_rtcp_sink_ghost) { - gst_pad_set_active (session->recv_rtcp_sink_ghost, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), - session->recv_rtcp_sink_ghost); - session->recv_rtcp_sink_ghost = NULL; - } - if (session->sync_src) { - /* releasing the request pad should also unref the sync pad */ - gst_object_unref (session->sync_src); - session->sync_src = NULL; - } - if (session->recv_rtcp_sink) { - gst_element_release_request_pad (session->session, session->recv_rtcp_sink); - gst_object_unref (session->recv_rtcp_sink); - session->recv_rtcp_sink = NULL; - } -} - -/* Create a pad for sending RTP for the session in @name. Must be called with - * RTP_BIN_LOCK. - */ -static GstPad * -create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) -{ - gchar *gname; - guint sessid; - GstRtpBinSession *session; - GstElementClass *klass; - - /* first get the session number */ - if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1) - goto no_name; - - /* get or create session */ - session = find_session_by_id (rtpbin, sessid); - if (!session) { - /* create session now */ - session = create_session (rtpbin, sessid); - if (session == NULL) - goto create_error; - } - - /* check if pad was requested */ - if (session->send_rtp_sink_ghost != NULL) - return session->send_rtp_sink_ghost; - - /* get send_rtp pad and store */ - session->send_rtp_sink = - gst_element_get_request_pad (session->session, "send_rtp_sink"); - if (session->send_rtp_sink == NULL) - goto pad_failed; - - session->send_rtp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ); - gst_pad_set_active (session->send_rtp_sink_ghost, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_sink_ghost); - - /* get srcpad */ - session->send_rtp_src = - gst_element_get_static_pad (session->session, "send_rtp_src"); - if (session->send_rtp_src == NULL) - goto no_srcpad; - - /* ghost the new source pad */ - klass = GST_ELEMENT_GET_CLASS (rtpbin); - gname = g_strdup_printf ("send_rtp_src_%d", sessid); - templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d"); - session->send_rtp_src_ghost = - gst_ghost_pad_new_from_template (gname, session->send_rtp_src, templ); - gst_pad_set_active (session->send_rtp_src_ghost, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_src_ghost); - g_free (gname); - - return session->send_rtp_sink_ghost; - - /* ERRORS */ -no_name: - { - g_warning ("gstrtpbin: invalid name given"); - return NULL; - } -create_error: - { - /* create_session already warned */ - return NULL; - } -pad_failed: - { - g_warning ("gstrtpbin: failed to get session pad for session %d", sessid); - return NULL; - } -no_srcpad: - { - g_warning ("gstrtpbin: failed to get rtp source pad for session %d", - sessid); - return NULL; - } -} - -static void -remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) -{ - if (session->send_rtp_src_ghost) { - gst_pad_set_active (session->send_rtp_src_ghost, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), - session->send_rtp_src_ghost); - session->send_rtp_src_ghost = NULL; - } - if (session->send_rtp_src) { - gst_object_unref (session->send_rtp_src); - session->send_rtp_src = NULL; - } - if (session->send_rtp_sink) { - gst_element_release_request_pad (GST_ELEMENT_CAST (session->session), - session->send_rtp_sink); - gst_object_unref (session->send_rtp_sink); - session->send_rtp_sink = NULL; - } - if (session->send_rtp_sink_ghost) { - gst_pad_set_active (session->send_rtp_sink_ghost, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), - session->send_rtp_sink_ghost); - session->send_rtp_sink_ghost = NULL; - } -} - -/* Create a pad for sending RTCP for the session in @name. Must be called with - * RTP_BIN_LOCK. - */ -static GstPad * -create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) -{ - guint sessid; - GstRtpBinSession *session; - - /* first get the session number */ - if (name == NULL || sscanf (name, "send_rtcp_src_%d", &sessid) != 1) - goto no_name; - - /* get or create session */ - session = find_session_by_id (rtpbin, sessid); - if (!session) - goto no_session; - - /* check if pad was requested */ - if (session->send_rtcp_src_ghost != NULL) - return session->send_rtcp_src_ghost; - - /* get rtcp_src pad and store */ - session->send_rtcp_src = - gst_element_get_request_pad (session->session, "send_rtcp_src"); - if (session->send_rtcp_src == NULL) - goto pad_failed; - - session->send_rtcp_src_ghost = - gst_ghost_pad_new_from_template (name, session->send_rtcp_src, templ); - gst_pad_set_active (session->send_rtcp_src_ghost, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtcp_src_ghost); - - return session->send_rtcp_src_ghost; - - /* ERRORS */ -no_name: - { - g_warning ("gstrtpbin: invalid name given"); - return NULL; - } -no_session: - { - g_warning ("gstrtpbin: session with id %d does not exist", sessid); - return NULL; - } -pad_failed: - { - g_warning ("gstrtpbin: failed to get rtcp pad for session %d", sessid); - return NULL; - } -} - -static void -remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session) -{ - if (session->send_rtcp_src_ghost) { - gst_pad_set_active (session->send_rtcp_src_ghost, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), - session->send_rtcp_src_ghost); - session->send_rtcp_src_ghost = NULL; - } - if (session->send_rtcp_src) { - gst_element_release_request_pad (session->session, session->send_rtcp_src); - gst_object_unref (session->send_rtcp_src); - session->send_rtcp_src = NULL; - } -} - -/* If the requested name is NULL we should create a name with - * the session number assuming we want the lowest posible session - * with a free pad like the template */ -static gchar * -gst_rtp_bin_get_free_pad_name (GstElement * element, GstPadTemplate * templ) -{ - gboolean name_found = FALSE; - gint session = 0; - GstPad *pad = NULL; - GstIterator *pad_it = NULL; - gchar *pad_name = NULL; - - GST_DEBUG_OBJECT (element, "find a free pad name for template"); - while (!name_found) { - g_free (pad_name); - pad_name = g_strdup_printf (templ->name_template, session++); - pad_it = gst_element_iterate_pads (GST_ELEMENT (element)); - name_found = TRUE; - while (gst_iterator_next (pad_it, (gpointer) & pad) == GST_ITERATOR_OK) { - gchar *name; - - name = gst_pad_get_name (pad); - if (strcmp (name, pad_name) == 0) - name_found = FALSE; - g_free (name); - } - gst_iterator_free (pad_it); - } - - GST_DEBUG_OBJECT (element, "free pad name found: '%s'", pad_name); - return pad_name; -} - -/* - */ -static GstPad * -gst_rtp_bin_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name) -{ - GstRtpBin *rtpbin; - GstElementClass *klass; - GstPad *result; - - gchar *pad_name = NULL; - - g_return_val_if_fail (templ != NULL, NULL); - g_return_val_if_fail (GST_IS_RTP_BIN (element), NULL); - - rtpbin = GST_RTP_BIN (element); - klass = GST_ELEMENT_GET_CLASS (element); - - GST_RTP_BIN_LOCK (rtpbin); - - if (name == NULL) { - /* use a free pad name */ - pad_name = gst_rtp_bin_get_free_pad_name (element, templ); - } else { - /* use the provided name */ - pad_name = g_strdup (name); - } - - GST_DEBUG_OBJECT (rtpbin, "Trying to request a pad with name %s", pad_name); - - /* figure out the template */ - if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) { - result = create_recv_rtp (rtpbin, templ, pad_name); - } else if (templ == gst_element_class_get_pad_template (klass, - "recv_rtcp_sink_%d")) { - result = create_recv_rtcp (rtpbin, templ, pad_name); - } else if (templ == gst_element_class_get_pad_template (klass, - "send_rtp_sink_%d")) { - result = create_send_rtp (rtpbin, templ, pad_name); - } else if (templ == gst_element_class_get_pad_template (klass, - "send_rtcp_src_%d")) { - result = create_rtcp (rtpbin, templ, pad_name); - } else - goto wrong_template; - - g_free (pad_name); - GST_RTP_BIN_UNLOCK (rtpbin); - - return result; - - /* ERRORS */ -wrong_template: - { - g_free (pad_name); - GST_RTP_BIN_UNLOCK (rtpbin); - g_warning ("gstrtpbin: this is not our template"); - return NULL; - } -} - -static void -gst_rtp_bin_release_pad (GstElement * element, GstPad * pad) -{ - GstRtpBinSession *session; - GstRtpBin *rtpbin; - - g_return_if_fail (GST_IS_GHOST_PAD (pad)); - g_return_if_fail (GST_IS_RTP_BIN (element)); - - rtpbin = GST_RTP_BIN (element); - - GST_RTP_BIN_LOCK (rtpbin); - GST_DEBUG_OBJECT (rtpbin, "Trying to release pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - - if (!(session = find_session_by_pad (rtpbin, pad))) - goto unknown_pad; - - if (session->recv_rtp_sink_ghost == pad) { - remove_recv_rtp (rtpbin, session); - } else if (session->recv_rtcp_sink_ghost == pad) { - remove_recv_rtcp (rtpbin, session); - } else if (session->send_rtp_sink_ghost == pad) { - remove_send_rtp (rtpbin, session); - } else if (session->send_rtcp_src_ghost == pad) { - remove_rtcp (rtpbin, session); - } - - /* no more request pads, free the complete session */ - if (session->recv_rtp_sink_ghost == NULL - && session->recv_rtcp_sink_ghost == NULL - && session->send_rtp_sink_ghost == NULL - && session->send_rtcp_src_ghost == NULL) { - GST_DEBUG_OBJECT (rtpbin, "no more pads for session %p", session); - rtpbin->sessions = g_slist_remove (rtpbin->sessions, session); - free_session (session, rtpbin); - } - GST_RTP_BIN_UNLOCK (rtpbin); - - return; - - /* ERROR */ -unknown_pad: - { - GST_RTP_BIN_UNLOCK (rtpbin); - g_warning ("gstrtpbin: %s:%s is not one of our request pads", - GST_DEBUG_PAD_NAME (pad)); - return; - } -} diff --git a/gst/rtpmanager/gstrtpbin.h b/gst/rtpmanager/gstrtpbin.h deleted file mode 100644 index bed6ad02..00000000 --- a/gst/rtpmanager/gstrtpbin.h +++ /dev/null @@ -1,88 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#ifndef __GST_RTP_BIN_H__ -#define __GST_RTP_BIN_H__ - -#include - -#include "rtpsession.h" - -#define GST_TYPE_RTP_BIN \ - (gst_rtp_bin_get_type()) -#define GST_RTP_BIN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_BIN,GstRtpBin)) -#define GST_RTP_BIN_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_BIN,GstRtpBinClass)) -#define GST_IS_RTP_BIN(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_BIN)) -#define GST_IS_RTP_BIN_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_BIN)) - -typedef struct _GstRtpBin GstRtpBin; -typedef struct _GstRtpBinClass GstRtpBinClass; -typedef struct _GstRtpBinPrivate GstRtpBinPrivate; - -struct _GstRtpBin { - GstBin bin; - - /*< private >*/ - /* default latency for sessions */ - guint latency; - gboolean do_lost; - /* a list of session */ - GSList *sessions; - - /* a list of clients, these are streams with the same CNAME */ - GSList *clients; - - /* the default SDES items for sessions */ - GstStructure *sdes; - - /*< private >*/ - GstRtpBinPrivate *priv; -}; - -struct _GstRtpBinClass { - GstBinClass parent_class; - - /* get the caps for pt */ - GstCaps* (*request_pt_map) (GstRtpBin *rtpbin, guint session, guint pt); - - /* action signals */ - void (*clear_pt_map) (GstRtpBin *rtpbin); - void (*reset_sync) (GstRtpBin *rtpbin); - RTPSession* (*get_internal_session) (GstRtpBin *rtpbin, guint session_id); - - /* session manager signals */ - void (*on_new_ssrc) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_ssrc_collision) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_ssrc_validated) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_ssrc_active) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_ssrc_sdes) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_bye_ssrc) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_bye_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_sender_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc); - void (*on_npt_stop) (GstRtpBin *rtpbin, guint session, guint32 ssrc); -}; - -GType gst_rtp_bin_get_type (void); - -#endif /* __GST_RTP_BIN_H__ */ diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c deleted file mode 100644 index 55126054..00000000 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ /dev/null @@ -1,1972 +0,0 @@ -/* - * Farsight Voice+Video library - * - * Copyright 2007 Collabora Ltd, - * Copyright 2007 Nokia Corporation - * @author: Philippe Kalaf . - * Copyright 2007 Wim Taymans - * - * 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. - * - */ - -/** - * SECTION:element-gstrtpjitterbuffer - * - * This element reorders and removes duplicate RTP packets as they are received - * from a network source. It will also wait for missing packets up to a - * configurable time limit using the #GstRtpJitterBuffer:latency property. - * Packets arriving too late are considered to be lost packets. - * - * This element acts as a live element and so adds #GstRtpJitterBuffer:latency - * to the pipeline. - * - * The element needs the clock-rate of the RTP payload in order to estimate the - * delay. This information is obtained either from the caps on the sink pad or, - * when no caps are present, from the #GstRtpJitterBuffer::request-pt-map signal. - * To clear the previous pt-map use the #GstRtpJitterBuffer::clear-pt-map signal. - * - * This element will automatically be used inside gstrtpbin. - * - * - * Example pipelines - * |[ - * gst-launch rtspsrc location=rtsp://192.168.1.133:8554/mpeg1or2AudioVideoTest ! gstrtpjitterbuffer ! rtpmpvdepay ! mpeg2dec ! xvimagesink - * ]| Connect to a streaming server and decode the MPEG video. The jitterbuffer is - * inserted into the pipeline to smooth out network jitter and to reorder the - * out-of-order RTP packets. - * - * - * Last reviewed on 2007-05-28 (0.10.5) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "gstrtpbin-marshal.h" - -#include "gstrtpjitterbuffer.h" -#include "rtpjitterbuffer.h" -#include "rtpstats.h" - -GST_DEBUG_CATEGORY (rtpjitterbuffer_debug); -#define GST_CAT_DEFAULT (rtpjitterbuffer_debug) - -/* low and high threshold tell the queue when to start and stop buffering */ -#define LOW_THRESHOLD 0.2 -#define HIGH_THRESHOLD 0.8 - -/* elementfactory information */ -static const GstElementDetails gst_rtp_jitter_buffer_details = -GST_ELEMENT_DETAILS ("RTP packet jitter-buffer", - "Filter/Network/RTP", - "A buffer that deals with network jitter and other transmission faults", - "Philippe Kalaf , " - "Wim Taymans "); - -/* RTPJitterBuffer signals and args */ -enum -{ - SIGNAL_REQUEST_PT_MAP, - SIGNAL_CLEAR_PT_MAP, - SIGNAL_HANDLE_SYNC, - SIGNAL_ON_NPT_STOP, - LAST_SIGNAL -}; - -#define DEFAULT_LATENCY_MS 200 -#define DEFAULT_DROP_ON_LATENCY FALSE -#define DEFAULT_TS_OFFSET 0 -#define DEFAULT_DO_LOST FALSE - -enum -{ - PROP_0, - PROP_LATENCY, - PROP_DROP_ON_LATENCY, - PROP_TS_OFFSET, - PROP_DO_LOST, - PROP_LAST -}; - -#define JBUF_LOCK(priv) (g_mutex_lock ((priv)->jbuf_lock)) - -#define JBUF_LOCK_CHECK(priv,label) G_STMT_START { \ - JBUF_LOCK (priv); \ - if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \ - goto label; \ -} G_STMT_END - -#define JBUF_UNLOCK(priv) (g_mutex_unlock ((priv)->jbuf_lock)) -#define JBUF_WAIT(priv) (g_cond_wait ((priv)->jbuf_cond, (priv)->jbuf_lock)) - -#define JBUF_WAIT_CHECK(priv,label) G_STMT_START { \ - JBUF_WAIT(priv); \ - if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \ - goto label; \ -} G_STMT_END - -#define JBUF_SIGNAL(priv) (g_cond_signal ((priv)->jbuf_cond)) - -struct _GstRtpJitterBufferPrivate -{ - GstPad *sinkpad, *srcpad; - GstPad *rtcpsinkpad; - - RTPJitterBuffer *jbuf; - GMutex *jbuf_lock; - GCond *jbuf_cond; - gboolean waiting; - gboolean discont; - - /* properties */ - guint latency_ms; - gboolean drop_on_latency; - gint64 ts_offset; - gboolean do_lost; - - /* the last seqnum we pushed out */ - guint32 last_popped_seqnum; - /* the next expected seqnum we push */ - guint32 next_seqnum; - /* last output time */ - GstClockTime last_out_time; - /* the next expected seqnum we receive */ - guint32 next_in_seqnum; - - /* start and stop ranges */ - GstClockTime npt_start; - GstClockTime npt_stop; - guint64 ext_timestamp; - guint64 last_elapsed; - guint64 estimated_eos; - GstClockID eos_id; - gboolean reached_npt_stop; - - /* state */ - gboolean eos; - - /* clock rate and rtp timestamp offset */ - gint last_pt; - gint32 clock_rate; - gint64 clock_base; - gint64 prev_ts_offset; - - /* when we are shutting down */ - GstFlowReturn srcresult; - gboolean blocked; - - /* for sync */ - GstSegment segment; - GstClockID clock_id; - gboolean unscheduled; - /* the latency of the upstream peer, we have to take this into account when - * synchronizing the buffers. */ - GstClockTime peer_latency; - - /* some accounting */ - guint64 num_late; - guint64 num_duplicates; -}; - -#define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_RTP_JITTER_BUFFER, \ - GstRtpJitterBufferPrivate)) - -static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-rtp, " - "clock-rate = (int) [ 1, 2147483647 ]" - /* "payload = (int) , " - * "encoding-name = (string) " - */ ) - ); - -static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_rtcp_template = -GST_STATIC_PAD_TEMPLATE ("sink_rtcp", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -static GstStaticPadTemplate gst_rtp_jitter_buffer_src_template = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-rtp" - /* "payload = (int) , " - * "clock-rate = (int) , " - * "encoding-name = (string) " - */ ) - ); - -static guint gst_rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; - -GST_BOILERPLATE (GstRtpJitterBuffer, gst_rtp_jitter_buffer, GstElement, - GST_TYPE_ELEMENT); - -/* object overrides */ -static void gst_rtp_jitter_buffer_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_rtp_jitter_buffer_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_rtp_jitter_buffer_finalize (GObject * object); - -/* element overrides */ -static GstStateChangeReturn gst_rtp_jitter_buffer_change_state (GstElement - * element, GstStateChange transition); -static GstPad *gst_rtp_jitter_buffer_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_rtp_jitter_buffer_release_pad (GstElement * element, - GstPad * pad); - -/* pad overrides */ -static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad); -static GList *gst_rtp_jitter_buffer_internal_links (GstPad * pad); - -/* sinkpad overrides */ -static gboolean gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps); -static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad, - GstEvent * event); -static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad, - GstBuffer * buffer); - -static gboolean gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad, - GstEvent * event); -static GstFlowReturn gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, - GstBuffer * buffer); - -/* srcpad overrides */ -static gboolean gst_rtp_jitter_buffer_src_event (GstPad * pad, - GstEvent * event); -static gboolean -gst_rtp_jitter_buffer_src_activate_push (GstPad * pad, gboolean active); -static void gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer); -static gboolean gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query); - -static void -gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer); - -static void -gst_rtp_jitter_buffer_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_rtp_jitter_buffer_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_rtcp_template)); - - gst_element_class_set_details (element_class, &gst_rtp_jitter_buffer_details); -} - -static void -gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - g_type_class_add_private (klass, sizeof (GstRtpJitterBufferPrivate)); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_finalize); - - gobject_class->set_property = gst_rtp_jitter_buffer_set_property; - gobject_class->get_property = gst_rtp_jitter_buffer_get_property; - - /** - * GstRtpJitterBuffer::latency: - * - * The maximum latency of the jitterbuffer. Packets will be kept in the buffer - * for at most this time. - */ - g_object_class_install_property (gobject_class, PROP_LATENCY, - g_param_spec_uint ("latency", "Buffer latency in ms", - "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS, - G_PARAM_READWRITE)); - /** - * GstRtpJitterBuffer::drop-on-latency: - * - * Drop oldest buffers when the queue is completely filled. - */ - g_object_class_install_property (gobject_class, PROP_DROP_ON_LATENCY, - g_param_spec_boolean ("drop-on-latency", - "Drop buffers when maximum latency is reached", - "Tells the jitterbuffer to never exceed the given latency in size", - DEFAULT_DROP_ON_LATENCY, G_PARAM_READWRITE)); - /** - * GstRtpJitterBuffer::ts-offset: - * - * Adjust GStreamer output buffer timestamps in the jitterbuffer with offset. - * This is mainly used to ensure interstream synchronisation. - */ - g_object_class_install_property (gobject_class, PROP_TS_OFFSET, - g_param_spec_int64 ("ts-offset", "Timestamp Offset", - "Adjust buffer timestamps with offset in nanoseconds", G_MININT64, - G_MAXINT64, DEFAULT_TS_OFFSET, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstRtpJitterBuffer::do-lost: - * - * Send out a GstRTPPacketLost event downstream when a packet is considered - * lost. - */ - g_object_class_install_property (gobject_class, PROP_DO_LOST, - g_param_spec_boolean ("do-lost", "Do Lost", - "Send an event downstream when a packet is lost", DEFAULT_DO_LOST, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstRtpJitterBuffer::request-pt-map: - * @buffer: the object which received the signal - * @pt: the pt - * - * Request the payload type as #GstCaps for @pt. - */ - gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP] = - g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass, - request_pt_map), NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, - GST_TYPE_CAPS, 1, G_TYPE_UINT); - /** - * GstRtpJitterBuffer::handle-sync: - * @buffer: the object which received the signal - * @struct: a GstStructure containing sync values. - * - * Be notified of new sync values. - */ - gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC] = - g_signal_new ("handle-sync", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass, - handle_sync), NULL, NULL, g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, 1, GST_TYPE_STRUCTURE | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** - * GstRtpJitterBuffer::on-npt-stop - * @buffer: the object which received the signal - * - * Signal that the jitterbufer has pushed the RTP packet that corresponds to - * the npt-stop position. - */ - gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] = - g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass, - on_npt_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0, G_TYPE_NONE); - - /** - * GstRtpJitterBuffer::clear-pt-map: - * @buffer: the object which received the signal - * - * Invalidate the clock-rate as obtained with the - * #GstRtpJitterBuffer::request-pt-map signal. - */ - gst_rtp_jitter_buffer_signals[SIGNAL_CLEAR_PT_MAP] = - g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstRtpJitterBufferClass, clear_pt_map), NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_change_state); - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_request_new_pad); - gstelement_class->release_pad = - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad); - - klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_clear_pt_map); - - GST_DEBUG_CATEGORY_INIT - (rtpjitterbuffer_debug, "gstrtpjitterbuffer", 0, "RTP Jitter Buffer"); -} - -static void -gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer, - GstRtpJitterBufferClass * klass) -{ - GstRtpJitterBufferPrivate *priv; - - priv = GST_RTP_JITTER_BUFFER_GET_PRIVATE (jitterbuffer); - jitterbuffer->priv = priv; - - priv->latency_ms = DEFAULT_LATENCY_MS; - priv->drop_on_latency = DEFAULT_DROP_ON_LATENCY; - priv->do_lost = DEFAULT_DO_LOST; - - priv->jbuf = rtp_jitter_buffer_new (); - priv->jbuf_lock = g_mutex_new (); - priv->jbuf_cond = g_cond_new (); - - priv->srcpad = - gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_src_template, - "src"); - - gst_pad_set_activatepush_function (priv->srcpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_activate_push)); - gst_pad_set_query_function (priv->srcpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_query)); - gst_pad_set_getcaps_function (priv->srcpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps)); - gst_pad_set_event_function (priv->srcpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_event)); - - priv->sinkpad = - gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_sink_template, - "sink"); - - gst_pad_set_chain_function (priv->sinkpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_chain)); - gst_pad_set_event_function (priv->sinkpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_sink_event)); - gst_pad_set_setcaps_function (priv->sinkpad, - GST_DEBUG_FUNCPTR (gst_jitter_buffer_sink_setcaps)); - gst_pad_set_getcaps_function (priv->sinkpad, - GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps)); - - gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->srcpad); - gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->sinkpad); -} - -static void -gst_rtp_jitter_buffer_finalize (GObject * object) -{ - GstRtpJitterBuffer *jitterbuffer; - - jitterbuffer = GST_RTP_JITTER_BUFFER (object); - - g_mutex_free (jitterbuffer->priv->jbuf_lock); - g_cond_free (jitterbuffer->priv->jbuf_cond); - - g_object_unref (jitterbuffer->priv->jbuf); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static GList * -gst_rtp_jitter_buffer_internal_links (GstPad * pad) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - GList *res = NULL; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - if (pad == priv->sinkpad) { - res = g_list_prepend (res, priv->srcpad); - } else if (pad == priv->srcpad) { - res = g_list_prepend (res, priv->sinkpad); - } else if (pad == priv->rtcpsinkpad) { - res = NULL; - } - - gst_object_unref (jitterbuffer); - - return res; -} - -static GstPad * -create_rtcp_sink (GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - GST_DEBUG_OBJECT (jitterbuffer, "creating RTCP sink pad"); - - priv->rtcpsinkpad = - gst_pad_new_from_static_template - (&gst_rtp_jitter_buffer_sink_rtcp_template, "sink_rtcp"); - gst_pad_set_chain_function (priv->rtcpsinkpad, - gst_rtp_jitter_buffer_chain_rtcp); - gst_pad_set_event_function (priv->rtcpsinkpad, - (GstPadEventFunction) gst_rtp_jitter_buffer_sink_rtcp_event); - gst_pad_set_internal_link_function (priv->rtcpsinkpad, - gst_rtp_jitter_buffer_internal_links); - gst_pad_set_active (priv->rtcpsinkpad, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad); - - return priv->rtcpsinkpad; -} - -static void -remove_rtcp_sink (GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - GST_DEBUG_OBJECT (jitterbuffer, "removing RTCP sink pad"); - - gst_pad_set_active (priv->rtcpsinkpad, FALSE); - - gst_element_remove_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad); - priv->rtcpsinkpad = NULL; -} - -static GstPad * -gst_rtp_jitter_buffer_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name) -{ - GstRtpJitterBuffer *jitterbuffer; - GstElementClass *klass; - GstPad *result; - GstRtpJitterBufferPrivate *priv; - - g_return_val_if_fail (templ != NULL, NULL); - g_return_val_if_fail (GST_IS_RTP_JITTER_BUFFER (element), NULL); - - jitterbuffer = GST_RTP_JITTER_BUFFER (element); - priv = jitterbuffer->priv; - klass = GST_ELEMENT_GET_CLASS (element); - - GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name)); - - /* figure out the template */ - if (templ == gst_element_class_get_pad_template (klass, "sink_rtcp")) { - if (priv->rtcpsinkpad != NULL) - goto exists; - - result = create_rtcp_sink (jitterbuffer); - } else - goto wrong_template; - - return result; - - /* ERRORS */ -wrong_template: - { - g_warning ("gstrtpjitterbuffer: this is not our template"); - return NULL; - } -exists: - { - g_warning ("gstrtpjitterbuffer: pad already requested"); - return NULL; - } -} - -static void -gst_rtp_jitter_buffer_release_pad (GstElement * element, GstPad * pad) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - - g_return_if_fail (GST_IS_RTP_JITTER_BUFFER (element)); - g_return_if_fail (GST_IS_PAD (pad)); - - jitterbuffer = GST_RTP_JITTER_BUFFER (element); - priv = jitterbuffer->priv; - - GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - if (priv->rtcpsinkpad == pad) { - remove_rtcp_sink (jitterbuffer); - } else - goto wrong_pad; - - return; - - /* ERRORS */ -wrong_pad: - { - g_warning ("gstjitterbuffer: asked to release an unknown pad"); - return; - } -} - -static void -gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - /* this will trigger a new pt-map request signal, FIXME, do something better. */ - priv->clock_rate = -1; -} - -static GstCaps * -gst_rtp_jitter_buffer_getcaps (GstPad * pad) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - GstPad *other; - GstCaps *caps; - const GstCaps *templ; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - other = (pad == priv->srcpad ? priv->sinkpad : priv->srcpad); - - caps = gst_pad_peer_get_caps (other); - - templ = gst_pad_get_pad_template_caps (pad); - if (caps == NULL) { - GST_DEBUG_OBJECT (jitterbuffer, "copy template"); - caps = gst_caps_copy (templ); - } else { - GstCaps *intersect; - - GST_DEBUG_OBJECT (jitterbuffer, "intersect with template"); - - intersect = gst_caps_intersect (caps, templ); - gst_caps_unref (caps); - - caps = intersect; - } - gst_object_unref (jitterbuffer); - - return caps; -} - -static gboolean -gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, - GstCaps * caps) -{ - GstRtpJitterBufferPrivate *priv; - GstStructure *caps_struct; - guint val; - GstClockTime tval; - - priv = jitterbuffer->priv; - - /* first parse the caps */ - caps_struct = gst_caps_get_structure (caps, 0); - - GST_DEBUG_OBJECT (jitterbuffer, "got caps"); - - /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to - * measure the amount of data in the buffer */ - if (!gst_structure_get_int (caps_struct, "clock-rate", &priv->clock_rate)) - goto error; - - if (priv->clock_rate <= 0) - goto wrong_rate; - - GST_DEBUG_OBJECT (jitterbuffer, "got clock-rate %d", priv->clock_rate); - - /* The clock base is the RTP timestamp corrsponding to the npt-start value. We - * can use this to track the amount of time elapsed on the sender. */ - if (gst_structure_get_uint (caps_struct, "clock-base", &val)) - priv->clock_base = val; - else - priv->clock_base = -1; - - priv->ext_timestamp = priv->clock_base; - - GST_DEBUG_OBJECT (jitterbuffer, "got clock-base %" G_GINT64_FORMAT, - priv->clock_base); - - if (gst_structure_get_uint (caps_struct, "seqnum-base", &val)) { - /* first expected seqnum, only update when we didn't have a previous base. */ - if (priv->next_in_seqnum == -1) - priv->next_in_seqnum = val; - if (priv->next_seqnum == -1) - priv->next_seqnum = val; - } - - GST_DEBUG_OBJECT (jitterbuffer, "got seqnum-base %d", priv->next_in_seqnum); - - /* the start and stop times. The seqnum-base corresponds to the start time. We - * will keep track of the seqnums on the output and when we reach the one - * corresponding to npt-stop, we emit the npt-stop-reached signal */ - if (gst_structure_get_clock_time (caps_struct, "npt-start", &tval)) - priv->npt_start = tval; - else - priv->npt_start = 0; - - if (gst_structure_get_clock_time (caps_struct, "npt-stop", &tval)) - priv->npt_stop = tval; - else - priv->npt_stop = -1; - - GST_DEBUG_OBJECT (jitterbuffer, - "npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop)); - - return TRUE; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (jitterbuffer, "No clock-rate in caps!"); - return FALSE; - } -wrong_rate: - { - GST_DEBUG_OBJECT (jitterbuffer, "Invalid clock-rate %d", priv->clock_rate); - return FALSE; - } -} - -static gboolean -gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - gboolean res; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps); - - /* set same caps on srcpad on success */ - if (res) - gst_pad_set_caps (priv->srcpad, caps); - - gst_object_unref (jitterbuffer); - - return res; -} - -static void -gst_rtp_jitter_buffer_flush_start (GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - JBUF_LOCK (priv); - /* mark ourselves as flushing */ - priv->srcresult = GST_FLOW_WRONG_STATE; - GST_DEBUG_OBJECT (jitterbuffer, "Disabling pop on queue"); - /* this unblocks any waiting pops on the src pad task */ - JBUF_SIGNAL (priv); - /* unlock clock, we just unschedule, the entry will be released by the - * locking streaming thread. */ - if (priv->clock_id) { - gst_clock_id_unschedule (priv->clock_id); - priv->unscheduled = TRUE; - } - JBUF_UNLOCK (priv); -} - -static void -gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - JBUF_LOCK (priv); - GST_DEBUG_OBJECT (jitterbuffer, "Enabling pop on queue"); - /* Mark as non flushing */ - priv->srcresult = GST_FLOW_OK; - gst_segment_init (&priv->segment, GST_FORMAT_TIME); - priv->last_popped_seqnum = -1; - priv->last_out_time = -1; - priv->next_seqnum = -1; - priv->next_in_seqnum = -1; - priv->clock_rate = -1; - priv->eos = FALSE; - priv->estimated_eos = -1; - priv->last_elapsed = 0; - priv->reached_npt_stop = FALSE; - priv->ext_timestamp = -1; - GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer"); - rtp_jitter_buffer_flush (priv->jbuf); - rtp_jitter_buffer_reset_skew (priv->jbuf); - JBUF_UNLOCK (priv); -} - -static gboolean -gst_rtp_jitter_buffer_src_activate_push (GstPad * pad, gboolean active) -{ - gboolean result = TRUE; - GstRtpJitterBuffer *jitterbuffer = NULL; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - - if (active) { - /* allow data processing */ - gst_rtp_jitter_buffer_flush_stop (jitterbuffer); - - /* start pushing out buffers */ - GST_DEBUG_OBJECT (jitterbuffer, "Starting task on srcpad"); - gst_pad_start_task (jitterbuffer->priv->srcpad, - (GstTaskFunction) gst_rtp_jitter_buffer_loop, jitterbuffer); - } else { - /* make sure all data processing stops ASAP */ - gst_rtp_jitter_buffer_flush_start (jitterbuffer); - - /* NOTE this will hardlock if the state change is called from the src pad - * task thread because we will _join() the thread. */ - GST_DEBUG_OBJECT (jitterbuffer, "Stopping task on srcpad"); - result = gst_pad_stop_task (pad); - } - - gst_object_unref (jitterbuffer); - - return result; -} - -static GstStateChangeReturn -gst_rtp_jitter_buffer_change_state (GstElement * element, - GstStateChange transition) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - jitterbuffer = GST_RTP_JITTER_BUFFER (element); - priv = jitterbuffer->priv; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - JBUF_LOCK (priv); - /* reset negotiated values */ - priv->clock_rate = -1; - priv->clock_base = -1; - priv->peer_latency = 0; - priv->last_pt = -1; - /* block until we go to PLAYING */ - priv->blocked = TRUE; - /* reset skew detection initialy */ - rtp_jitter_buffer_reset_skew (priv->jbuf); - JBUF_UNLOCK (priv); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - JBUF_LOCK (priv); - /* unblock to allow streaming in PLAYING */ - priv->blocked = FALSE; - JBUF_SIGNAL (priv); - JBUF_UNLOCK (priv); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* we are a live element because we sync to the clock, which we can only - * do in the PLAYING state */ - if (ret != GST_STATE_CHANGE_FAILURE) - ret = GST_STATE_CHANGE_NO_PREROLL; - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - JBUF_LOCK (priv); - /* block to stop streaming when PAUSED */ - priv->blocked = TRUE; - JBUF_UNLOCK (priv); - if (ret != GST_STATE_CHANGE_FAILURE) - ret = GST_STATE_CHANGE_NO_PREROLL; - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - -static gboolean -gst_rtp_jitter_buffer_src_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = TRUE; - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - default: - ret = gst_pad_push_event (priv->sinkpad, event); - break; - } - gst_object_unref (jitterbuffer); - - return ret; -} - -static gboolean -gst_rtp_jitter_buffer_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = TRUE; - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* we need time for now */ - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (jitterbuffer, - "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (time)); - - /* now configure the values, we need these to time the release of the - * buffers on the srcpad. */ - gst_segment_set_newsegment_full (&priv->segment, update, - rate, arate, format, start, stop, time); - - /* FIXME, push SEGMENT in the queue. Sorting order might be difficult. */ - ret = gst_pad_push_event (priv->srcpad, event); - break; - } - case GST_EVENT_FLUSH_START: - gst_rtp_jitter_buffer_flush_start (jitterbuffer); - ret = gst_pad_push_event (priv->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - ret = gst_pad_push_event (priv->srcpad, event); - ret = gst_rtp_jitter_buffer_src_activate_push (priv->srcpad, TRUE); - break; - case GST_EVENT_EOS: - { - /* push EOS in queue. We always push it at the head */ - JBUF_LOCK (priv); - /* check for flushing, we need to discard the event and return FALSE when - * we are flushing */ - ret = priv->srcresult == GST_FLOW_OK; - if (ret && !priv->eos) { - GST_DEBUG_OBJECT (jitterbuffer, "queuing EOS"); - priv->eos = TRUE; - JBUF_SIGNAL (priv); - } else if (priv->eos) { - GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, we are already EOS"); - } else { - GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, reason %s", - gst_flow_get_name (priv->srcresult)); - } - JBUF_UNLOCK (priv); - gst_event_unref (event); - break; - } - default: - ret = gst_pad_push_event (priv->srcpad, event); - break; - } - -done: - gst_object_unref (jitterbuffer); - - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment"); - ret = FALSE; - goto done; - } -} - -static gboolean -gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad, GstEvent * event) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - break; - case GST_EVENT_FLUSH_STOP: - break; - default: - break; - } - gst_event_unref (event); - gst_object_unref (jitterbuffer); - - return TRUE; -} - -static gboolean -gst_rtp_jitter_buffer_get_clock_rate (GstRtpJitterBuffer * jitterbuffer, - guint8 pt) -{ - GValue ret = { 0 }; - GValue args[2] = { {0}, {0} }; - GstCaps *caps; - gboolean res; - - g_value_init (&args[0], GST_TYPE_ELEMENT); - g_value_set_object (&args[0], jitterbuffer); - g_value_init (&args[1], G_TYPE_UINT); - g_value_set_uint (&args[1], pt); - - g_value_init (&ret, GST_TYPE_CAPS); - g_value_set_boxed (&ret, NULL); - - g_signal_emitv (args, gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP], 0, - &ret); - - g_value_unset (&args[0]); - g_value_unset (&args[1]); - caps = (GstCaps *) g_value_dup_boxed (&ret); - g_value_unset (&ret); - if (!caps) - goto no_caps; - - res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps); - - gst_caps_unref (caps); - - return res; - - /* ERRORS */ -no_caps: - { - GST_DEBUG_OBJECT (jitterbuffer, "could not get caps"); - return FALSE; - } -} - -static GstFlowReturn -gst_rtp_jitter_buffer_chain (GstPad * pad, GstBuffer * buffer) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - guint16 seqnum; - GstFlowReturn ret = GST_FLOW_OK; - GstClockTime timestamp; - guint64 latency_ts; - gboolean tail; - guint8 pt; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - - if (G_UNLIKELY (!gst_rtp_buffer_validate (buffer))) - goto invalid_buffer; - - priv = jitterbuffer->priv; - - pt = gst_rtp_buffer_get_payload_type (buffer); - - if (G_UNLIKELY (priv->last_pt != pt)) { - GstCaps *caps; - - GST_DEBUG_OBJECT (jitterbuffer, "pt changed from %u to %u", priv->last_pt, - pt); - - priv->last_pt = pt; - /* reset clock-rate so that we get a new one */ - priv->clock_rate = -1; - /* Try to get the clock-rate from the caps first if we can. If there are no - * caps we must fire the signal to get the clock-rate. */ - if ((caps = GST_BUFFER_CAPS (buffer))) { - gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps); - } - } - - if (G_UNLIKELY (priv->clock_rate == -1)) { - /* no clock rate given on the caps, try to get one with the signal */ - gst_rtp_jitter_buffer_get_clock_rate (jitterbuffer, pt); - if (G_UNLIKELY (priv->clock_rate == -1)) - goto no_clock_rate; - } - - /* take the timestamp of the buffer. This is the time when the packet was - * received and is used to calculate jitter and clock skew. We will adjust - * this timestamp with the smoothed value after processing it in the - * jitterbuffer. */ - timestamp = GST_BUFFER_TIMESTAMP (buffer); - /* bring to running time */ - timestamp = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME, - timestamp); - - seqnum = gst_rtp_buffer_get_seq (buffer); - - GST_DEBUG_OBJECT (jitterbuffer, - "Received packet #%d at time %" GST_TIME_FORMAT, seqnum, - GST_TIME_ARGS (timestamp)); - - JBUF_LOCK_CHECK (priv, out_flushing); - /* don't accept more data on EOS */ - if (G_UNLIKELY (priv->eos)) - goto have_eos; - - /* now check against our expected seqnum */ - if (G_LIKELY (priv->next_in_seqnum != -1)) { - gint gap; - gboolean reset = FALSE; - - gap = gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, seqnum); - if (G_UNLIKELY (gap != 0)) { - GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d", - priv->next_in_seqnum, seqnum, gap); - /* priv->next_in_seqnum >= seqnum, this packet is too late or the - * sender might have been restarted with different seqnum. */ - if (gap < -RTP_MAX_MISORDER) { - GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too old %d", gap); - reset = TRUE; - } - /* priv->next_in_seqnum < seqnum, this is a new packet */ - else if (G_UNLIKELY (gap > RTP_MAX_DROPOUT)) { - GST_DEBUG_OBJECT (jitterbuffer, "reset: too many dropped packets %d", - gap); - reset = TRUE; - } else { - GST_DEBUG_OBJECT (jitterbuffer, "tolerable gap"); - } - } - if (G_UNLIKELY (reset)) { - GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer"); - rtp_jitter_buffer_flush (priv->jbuf); - rtp_jitter_buffer_reset_skew (priv->jbuf); - priv->last_popped_seqnum = -1; - priv->next_seqnum = seqnum; - } - } - priv->next_in_seqnum = (seqnum + 1) & 0xffff; - - /* let's check if this buffer is too late, we can only accept packets with - * bigger seqnum than the one we last pushed. */ - if (G_LIKELY (priv->last_popped_seqnum != -1)) { - gint gap; - - gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum); - - /* priv->last_popped_seqnum >= seqnum, we're too late. */ - if (G_UNLIKELY (gap <= 0)) - goto too_late; - } - - /* let's drop oldest packet if the queue is already full and drop-on-latency - * is set. We can only do this when there actually is a latency. When no - * latency is set, we just pump it in the queue and let the other end push it - * out as fast as possible. */ - if (priv->latency_ms && priv->drop_on_latency) { - latency_ts = - gst_util_uint64_scale_int (priv->latency_ms, priv->clock_rate, 1000); - - if (G_UNLIKELY (rtp_jitter_buffer_get_ts_diff (priv->jbuf) >= latency_ts)) { - GstBuffer *old_buf; - - old_buf = rtp_jitter_buffer_pop (priv->jbuf); - - GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet #%d", - gst_rtp_buffer_get_seq (old_buf)); - - gst_buffer_unref (old_buf); - } - } - - /* we need to make the metadata writable before pushing it in the jitterbuffer - * because the jitterbuffer will update the timestamp */ - buffer = gst_buffer_make_metadata_writable (buffer); - - /* now insert the packet into the queue in sorted order. This function returns - * FALSE if a packet with the same seqnum was already in the queue, meaning we - * have a duplicate. */ - if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, buffer, timestamp, - priv->clock_rate, &tail))) - goto duplicate; - - /* signal addition of new buffer when the _loop is waiting. */ - if (priv->waiting) - JBUF_SIGNAL (priv); - - /* let's unschedule and unblock any waiting buffers. We only want to do this - * when the tail buffer changed */ - if (G_UNLIKELY (priv->clock_id && tail)) { - GST_DEBUG_OBJECT (jitterbuffer, - "Unscheduling waiting buffer, new tail buffer"); - gst_clock_id_unschedule (priv->clock_id); - priv->unscheduled = TRUE; - } - - GST_DEBUG_OBJECT (jitterbuffer, "Pushed packet #%d, now %d packets, tail: %d", - seqnum, rtp_jitter_buffer_num_packets (priv->jbuf), tail); - -finished: - JBUF_UNLOCK (priv); - - gst_object_unref (jitterbuffer); - - return ret; - - /* ERRORS */ -invalid_buffer: - { - /* this is not fatal but should be filtered earlier */ - GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL), - ("Received invalid RTP payload, dropping")); - gst_buffer_unref (buffer); - gst_object_unref (jitterbuffer); - return GST_FLOW_OK; - } -no_clock_rate: - { - GST_WARNING_OBJECT (jitterbuffer, - "No clock-rate in caps!, dropping buffer"); - gst_buffer_unref (buffer); - gst_object_unref (jitterbuffer); - return GST_FLOW_OK; - } -out_flushing: - { - ret = priv->srcresult; - GST_DEBUG_OBJECT (jitterbuffer, "flushing %s", gst_flow_get_name (ret)); - gst_buffer_unref (buffer); - goto finished; - } -have_eos: - { - ret = GST_FLOW_UNEXPECTED; - GST_WARNING_OBJECT (jitterbuffer, "we are EOS, refusing buffer"); - gst_buffer_unref (buffer); - goto finished; - } -too_late: - { - GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already" - " popped, dropping", seqnum, priv->last_popped_seqnum); - priv->num_late++; - gst_buffer_unref (buffer); - goto finished; - } -duplicate: - { - GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping", - seqnum); - priv->num_duplicates++; - gst_buffer_unref (buffer); - goto finished; - } -} - -static GstClockTime -apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - if (timestamp == -1) - return -1; - - /* apply the timestamp offset */ - timestamp += priv->ts_offset; - - return timestamp; -} - -static GstClockTime -get_sync_time (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp) -{ - GstClockTime result; - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - result = timestamp + GST_ELEMENT_CAST (jitterbuffer)->base_time; - /* add latency, this includes our own latency and the peer latency. */ - result += (priv->latency_ms * GST_MSECOND); - result += priv->peer_latency; - - return result; -} - -static gboolean -eos_reached (GstClock * clock, GstClockTime time, GstClockID id, - GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - - priv = jitterbuffer->priv; - - JBUF_LOCK_CHECK (priv, flushing); - if (priv->waiting) { - GST_DEBUG_OBJECT (jitterbuffer, "got the NPT timeout"); - priv->reached_npt_stop = TRUE; - JBUF_SIGNAL (priv); - } - JBUF_UNLOCK (priv); - - return TRUE; - - /* ERRORS */ -flushing: - { - JBUF_UNLOCK (priv); - return FALSE; - } -} - -/** - * This funcion will push out buffers on the source pad. - * - * For each pushed buffer, the seqnum is recorded, if the next buffer B has a - * different seqnum (missing packets before B), this function will wait for the - * missing packet to arrive up to the timestamp of buffer B. - */ -static void -gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer) -{ - GstRtpJitterBufferPrivate *priv; - GstBuffer *outbuf; - GstFlowReturn result; - guint16 seqnum; - guint32 next_seqnum; - GstClockTime timestamp, out_time; - gboolean discont = FALSE; - gint gap; - GstClock *clock; - GstClockID id; - GstClockTime sync_time; - - priv = jitterbuffer->priv; - - JBUF_LOCK_CHECK (priv, flushing); -again: - GST_DEBUG_OBJECT (jitterbuffer, "Peeking item"); - while (TRUE) { - id = NULL; - /* always wait if we are blocked */ - if (G_LIKELY (!priv->blocked)) { - /* if we have a packet, we can exit the loop and grab it */ - if (rtp_jitter_buffer_num_packets (priv->jbuf) > 0) - break; - /* no packets but we are EOS, do eos logic */ - if (G_UNLIKELY (priv->eos)) - goto do_eos; - /* underrun, wait for packets or flushing now if we are expecting an EOS - * timeout, set the async timer for it too */ - if (priv->estimated_eos != -1 && !priv->reached_npt_stop) { - sync_time = get_sync_time (jitterbuffer, priv->estimated_eos); - - GST_OBJECT_LOCK (jitterbuffer); - clock = GST_ELEMENT_CLOCK (jitterbuffer); - if (clock) { - GST_DEBUG_OBJECT (jitterbuffer, "scheduling timeout"); - id = gst_clock_new_single_shot_id (clock, sync_time); - gst_clock_id_wait_async (id, (GstClockCallback) eos_reached, - jitterbuffer); - } - GST_OBJECT_UNLOCK (jitterbuffer); - } - } - /* now we wait */ - priv->waiting = TRUE; - JBUF_WAIT (priv); - priv->waiting = FALSE; - - if (id) { - /* unschedule any pending async notifications we might have */ - gst_clock_id_unschedule (id); - gst_clock_id_unref (id); - } - if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) - goto flushing; - - if (id && priv->reached_npt_stop) { - goto do_npt_stop; - } - } - - /* peek a buffer, we're just looking at the timestamp and the sequence number. - * If all is fine, we'll pop and push it. If the sequence number is wrong we - * wait on the timestamp. In the chain function we will unlock the wait when a - * new buffer is available. The peeked buffer is valid for as long as we hold - * the jitterbuffer lock. */ - outbuf = rtp_jitter_buffer_peek (priv->jbuf); - - /* get the seqnum and the next expected seqnum */ - seqnum = gst_rtp_buffer_get_seq (outbuf); - next_seqnum = priv->next_seqnum; - - /* get the timestamp, this is already corrected for clock skew by the - * jitterbuffer */ - timestamp = GST_BUFFER_TIMESTAMP (outbuf); - - GST_DEBUG_OBJECT (jitterbuffer, - "Peeked buffer #%d, expect #%d, timestamp %" GST_TIME_FORMAT - ", now %d left", seqnum, next_seqnum, GST_TIME_ARGS (timestamp), - rtp_jitter_buffer_num_packets (priv->jbuf)); - - /* apply our timestamp offset to the incomming buffer, this will be our output - * timestamp. */ - out_time = apply_offset (jitterbuffer, timestamp); - - /* get the gap between this and the previous packet. If we don't know the - * previous packet seqnum assume no gap. */ - if (G_LIKELY (next_seqnum != -1)) { - gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum); - - /* if we have a packet that we already pushed or considered dropped, pop it - * off and get the next packet */ - if (G_UNLIKELY (gap < 0)) { - GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping", - seqnum, next_seqnum); - outbuf = rtp_jitter_buffer_pop (priv->jbuf); - gst_buffer_unref (outbuf); - goto again; - } - } else { - GST_DEBUG_OBJECT (jitterbuffer, "no next seqnum known, first packet"); - gap = -1; - } - - /* If we don't know what the next seqnum should be (== -1) we have to wait - * because it might be possible that we are not receiving this buffer in-order, - * a buffer with a lower seqnum could arrive later and we want to push that - * earlier buffer before this buffer then. - * If we know the expected seqnum, we can compare it to the current seqnum to - * determine if we have missing a packet. If we have a missing packet (which - * must be before this packet) we can wait for it until the deadline for this - * packet expires. */ - if (G_UNLIKELY (gap != 0 && out_time != -1)) { - GstClockReturn ret; - GstClockTime duration = GST_CLOCK_TIME_NONE; - - if (gap > 0) { - /* we have a gap */ - GST_WARNING_OBJECT (jitterbuffer, - "Sequence number GAP detected: expected %d instead of %d (%d missing)", - next_seqnum, seqnum, gap); - - if (priv->last_out_time != -1) { - GST_DEBUG_OBJECT (jitterbuffer, - "out_time %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_time), GST_TIME_ARGS (priv->last_out_time)); - /* interpolate between the current time and the last time based on - * number of packets we are missing, this is the estimated duration - * for the missing packet based on equidistant packet spacing. Also make - * sure we never go negative. */ - if (out_time > priv->last_out_time) - duration = (out_time - priv->last_out_time) / (gap + 1); - else - goto lost; - - GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT, - GST_TIME_ARGS (duration)); - /* add this duration to the timestamp of the last packet we pushed */ - out_time = (priv->last_out_time + duration); - } - } else { - /* we don't know what the next_seqnum should be, wait for the last - * possible moment to push this buffer, maybe we get an earlier seqnum - * while we wait */ - GST_DEBUG_OBJECT (jitterbuffer, "First buffer %d, do sync", seqnum); - } - - GST_OBJECT_LOCK (jitterbuffer); - clock = GST_ELEMENT_CLOCK (jitterbuffer); - if (!clock) { - GST_OBJECT_UNLOCK (jitterbuffer); - /* let's just push if there is no clock */ - goto push_buffer; - } - - GST_DEBUG_OBJECT (jitterbuffer, "sync to timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_time)); - - /* prepare for sync against clock */ - sync_time = get_sync_time (jitterbuffer, out_time); - - /* create an entry for the clock */ - id = priv->clock_id = gst_clock_new_single_shot_id (clock, sync_time); - priv->unscheduled = FALSE; - GST_OBJECT_UNLOCK (jitterbuffer); - - /* release the lock so that the other end can push stuff or unlock */ - JBUF_UNLOCK (priv); - - ret = gst_clock_id_wait (id, NULL); - - JBUF_LOCK (priv); - /* and free the entry */ - gst_clock_id_unref (id); - priv->clock_id = NULL; - - /* at this point, the clock could have been unlocked by a timeout, a new - * tail element was added to the queue or because we are shutting down. Check - * for shutdown first. */ - if G_UNLIKELY - ((priv->srcresult != GST_FLOW_OK)) - goto flushing; - - /* if we got unscheduled and we are not flushing, it's because a new tail - * element became available in the queue or we flushed the queue. - * Grab it and try to push or sync. */ - if (ret == GST_CLOCK_UNSCHEDULED || priv->unscheduled) { - GST_DEBUG_OBJECT (jitterbuffer, - "Wait got unscheduled, will retry to push with new buffer"); - goto again; - } - - lost: - /* we now timed out, this means we lost a packet or finished synchronizing - * on the first buffer. */ - if (gap > 0) { - GstEvent *event; - - /* we had a gap and thus we lost a packet. Create an event for this. */ - GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", next_seqnum); - priv->num_late++; - discont = TRUE; - - /* update our expected next packet */ - priv->last_popped_seqnum = next_seqnum; - priv->last_out_time = out_time; - priv->next_seqnum = (next_seqnum + 1) & 0xffff; - - if (priv->do_lost) { - /* create paket lost event */ - event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, - gst_structure_new ("GstRTPPacketLost", - "seqnum", G_TYPE_UINT, (guint) next_seqnum, - "timestamp", G_TYPE_UINT64, out_time, - "duration", G_TYPE_UINT64, duration, NULL)); - - JBUF_UNLOCK (priv); - gst_pad_push_event (priv->srcpad, event); - JBUF_LOCK_CHECK (priv, flushing); - } - /* look for next packet */ - goto again; - } - - /* there was no known gap,just the first packet, exit the loop and push */ - GST_DEBUG_OBJECT (jitterbuffer, "First packet #%d synced", seqnum); - - /* get new timestamp, latency might have changed */ - out_time = apply_offset (jitterbuffer, timestamp); - } -push_buffer: - - /* when we get here we are ready to pop and push the buffer */ - outbuf = rtp_jitter_buffer_pop (priv->jbuf); - - if (G_UNLIKELY (discont || priv->discont)) { - /* set DISCONT flag when we missed a packet. We pushed the buffer writable - * into the jitterbuffer so we can modify now. */ - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - priv->discont = FALSE; - } - - /* apply timestamp with offset to buffer now */ - GST_BUFFER_TIMESTAMP (outbuf) = out_time; - - /* update the elapsed time when we need to check against the npt stop time. */ - if (priv->npt_stop != -1 && priv->ext_timestamp != -1 - && priv->clock_base != -1) { - guint64 ext_time, elapsed, estimated; - guint32 rtp_time; - - rtp_time = gst_rtp_buffer_get_timestamp (outbuf); - - GST_LOG_OBJECT (jitterbuffer, "rtp %" G_GUINT32_FORMAT ", ext %" - G_GUINT64_FORMAT, rtp_time, priv->ext_timestamp); - - if (rtp_time < priv->ext_timestamp) { - ext_time = priv->ext_timestamp; - } else { - ext_time = gst_rtp_buffer_ext_timestamp (&priv->ext_timestamp, rtp_time); - } - - if (ext_time > priv->clock_base) - elapsed = ext_time - priv->clock_base; - else - elapsed = 0; - - elapsed = gst_util_uint64_scale_int (elapsed, GST_SECOND, priv->clock_rate); - - if (elapsed > priv->last_elapsed) { - guint64 left; - - priv->last_elapsed = elapsed; - - left = priv->npt_stop - priv->npt_start; - - if (elapsed > 0) - estimated = gst_util_uint64_scale (out_time, left, elapsed); - else - estimated = -1; - - GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %" - GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated)); - - priv->estimated_eos = estimated; - } - } - - /* now we are ready to push the buffer. Save the seqnum and release the lock - * so the other end can push stuff in the queue again. */ - priv->last_popped_seqnum = seqnum; - priv->last_out_time = out_time; - priv->next_seqnum = (seqnum + 1) & 0xffff; - JBUF_UNLOCK (priv); - - /* push buffer */ - GST_DEBUG_OBJECT (jitterbuffer, - "Pushing buffer %d, timestamp %" GST_TIME_FORMAT, seqnum, - GST_TIME_ARGS (out_time)); - result = gst_pad_push (priv->srcpad, outbuf); - if (G_UNLIKELY (result != GST_FLOW_OK)) - goto pause; - - return; - - /* ERRORS */ -do_eos: - { - /* store result, we are flushing now */ - GST_DEBUG_OBJECT (jitterbuffer, "We are EOS, pushing EOS downstream"); - priv->srcresult = GST_FLOW_UNEXPECTED; - gst_pad_pause_task (priv->srcpad); - JBUF_UNLOCK (priv); - gst_pad_push_event (priv->srcpad, gst_event_new_eos ()); - return; - } -do_npt_stop: - { - /* store result, we are flushing now */ - GST_DEBUG_OBJECT (jitterbuffer, "We reached the NPT stop"); - JBUF_UNLOCK (priv); - - g_signal_emit (jitterbuffer, - gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP], 0, NULL); - return; - } -flushing: - { - GST_DEBUG_OBJECT (jitterbuffer, "we are flushing"); - gst_pad_pause_task (priv->srcpad); - JBUF_UNLOCK (priv); - return; - } -pause: - { - GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s", - gst_flow_get_name (result)); - - JBUF_LOCK (priv); - /* store result */ - priv->srcresult = result; - /* we don't post errors or anything because upstream will do that for us - * when we pass the return value upstream. */ - gst_pad_pause_task (priv->srcpad); - JBUF_UNLOCK (priv); - return; - } -} - -static GstFlowReturn -gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - GstFlowReturn ret = GST_FLOW_OK; - guint64 base_rtptime, timestamp; - guint32 clock_rate; - guint64 last_rtptime; - guint32 ssrc; - GstRTCPPacket packet; - guint64 ext_rtptime, diff; - guint32 rtptime; - gboolean drop = FALSE; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - - if (G_UNLIKELY (!gst_rtcp_buffer_validate (buffer))) - goto invalid_buffer; - - priv = jitterbuffer->priv; - - if (!gst_rtcp_buffer_get_first_packet (buffer, &packet)) - goto invalid_buffer; - - /* 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: - gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, &rtptime, - NULL, NULL); - break; - default: - goto ignore_buffer; - } - - GST_DEBUG_OBJECT (jitterbuffer, "received RTCP of SSRC %08x", ssrc); - - JBUF_LOCK (priv); - /* convert the RTP timestamp to our extended timestamp, using the same offset - * we used in the jitterbuffer */ - ext_rtptime = priv->jbuf->ext_rtptime; - ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime); - - /* get the last values from the jitterbuffer */ - rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, ×tamp, - &clock_rate, &last_rtptime); - - GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %" - G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT, - ext_rtptime, base_rtptime, clock_rate); - - if (base_rtptime == -1 || clock_rate == -1 || timestamp == -1) { - GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values"); - drop = TRUE; - } else { - /* we can't accept anything that happened before we did the last resync */ - if (base_rtptime > ext_rtptime) { - GST_DEBUG_OBJECT (jitterbuffer, "dropping, older than base time"); - drop = TRUE; - } else { - /* the SR RTP timestamp must be something close to what we last observed - * in the jitterbuffer */ - if (ext_rtptime > last_rtptime) { - /* check how far ahead it is to our RTP timestamps */ - diff = ext_rtptime - last_rtptime; - /* if bigger than 1 second, we drop it */ - if (diff > clock_rate) { - GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead"); - drop = TRUE; - } - GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %" - G_GUINT64_FORMAT, last_rtptime, diff); - } - } - } - JBUF_UNLOCK (priv); - - if (!drop) { - GstStructure *s; - - s = gst_structure_new ("application/x-rtp-sync", - "base-rtptime", G_TYPE_UINT64, base_rtptime, - "base-time", G_TYPE_UINT64, timestamp, - "clock-rate", G_TYPE_UINT, clock_rate, - "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime, - "sr-buffer", GST_TYPE_BUFFER, buffer, NULL); - - GST_DEBUG_OBJECT (jitterbuffer, "signaling sync"); - g_signal_emit (jitterbuffer, - gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s); - gst_structure_free (s); - } else { - GST_DEBUG_OBJECT (jitterbuffer, "dropping RTCP packet"); - ret = GST_FLOW_OK; - } - -done: - gst_buffer_unref (buffer); - gst_object_unref (jitterbuffer); - - return ret; - -invalid_buffer: - { - /* this is not fatal but should be filtered earlier */ - GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL), - ("Received invalid RTCP payload, dropping")); - ret = GST_FLOW_OK; - goto done; - } -ignore_buffer: - { - GST_DEBUG_OBJECT (jitterbuffer, "ignoring RTCP packet"); - ret = GST_FLOW_OK; - goto done; - } -} - -static gboolean -gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - gboolean res = FALSE; - - jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); - priv = jitterbuffer->priv; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - { - /* We need to send the query upstream and add the returned latency to our - * own */ - GstClockTime min_latency, max_latency; - gboolean us_live; - GstClockTime our_latency; - - if ((res = gst_pad_peer_query (priv->sinkpad, query))) { - gst_query_parse_latency (query, &us_live, &min_latency, &max_latency); - - GST_DEBUG_OBJECT (jitterbuffer, "Peer latency: min %" - GST_TIME_FORMAT " max %" GST_TIME_FORMAT, - GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency)); - - /* store this so that we can safely sync on the peer buffers. */ - JBUF_LOCK (priv); - priv->peer_latency = min_latency; - our_latency = ((guint64) priv->latency_ms) * GST_MSECOND; - JBUF_UNLOCK (priv); - - GST_DEBUG_OBJECT (jitterbuffer, "Our latency: %" GST_TIME_FORMAT, - GST_TIME_ARGS (our_latency)); - - /* we add some latency but can buffer an infinite amount of time */ - min_latency += our_latency; - max_latency = -1; - - GST_DEBUG_OBJECT (jitterbuffer, "Calculated total latency : min %" - GST_TIME_FORMAT " max %" GST_TIME_FORMAT, - GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency)); - - gst_query_set_latency (query, TRUE, min_latency, max_latency); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (jitterbuffer); - - return res; -} - -static void -gst_rtp_jitter_buffer_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - - jitterbuffer = GST_RTP_JITTER_BUFFER (object); - priv = jitterbuffer->priv; - - switch (prop_id) { - case PROP_LATENCY: - { - guint new_latency, old_latency; - - new_latency = g_value_get_uint (value); - - JBUF_LOCK (priv); - old_latency = priv->latency_ms; - priv->latency_ms = new_latency; - JBUF_UNLOCK (priv); - - /* post message if latency changed, this will inform the parent pipeline - * that a latency reconfiguration is possible/needed. */ - if (new_latency != old_latency) { - GST_DEBUG_OBJECT (jitterbuffer, "latency changed to: %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_latency * GST_MSECOND)); - - gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), - gst_message_new_latency (GST_OBJECT_CAST (jitterbuffer))); - } - break; - } - case PROP_DROP_ON_LATENCY: - JBUF_LOCK (priv); - priv->drop_on_latency = g_value_get_boolean (value); - JBUF_UNLOCK (priv); - break; - case PROP_TS_OFFSET: - JBUF_LOCK (priv); - priv->ts_offset = g_value_get_int64 (value); - /* FIXME, we don't really have a method for signaling a timestamp - * DISCONT without also making this a data discont. */ - /* priv->discont = TRUE; */ - JBUF_UNLOCK (priv); - break; - case PROP_DO_LOST: - JBUF_LOCK (priv); - priv->do_lost = g_value_get_boolean (value); - JBUF_UNLOCK (priv); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_rtp_jitter_buffer_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstRtpJitterBuffer *jitterbuffer; - GstRtpJitterBufferPrivate *priv; - - jitterbuffer = GST_RTP_JITTER_BUFFER (object); - priv = jitterbuffer->priv; - - switch (prop_id) { - case PROP_LATENCY: - JBUF_LOCK (priv); - g_value_set_uint (value, priv->latency_ms); - JBUF_UNLOCK (priv); - break; - case PROP_DROP_ON_LATENCY: - JBUF_LOCK (priv); - g_value_set_boolean (value, priv->drop_on_latency); - JBUF_UNLOCK (priv); - break; - case PROP_TS_OFFSET: - JBUF_LOCK (priv); - g_value_set_int64 (value, priv->ts_offset); - JBUF_UNLOCK (priv); - break; - case PROP_DO_LOST: - JBUF_LOCK (priv); - g_value_set_boolean (value, priv->do_lost); - JBUF_UNLOCK (priv); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/gst/rtpmanager/gstrtpjitterbuffer.h b/gst/rtpmanager/gstrtpjitterbuffer.h deleted file mode 100644 index 6d7610e5..00000000 --- a/gst/rtpmanager/gstrtpjitterbuffer.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Farsight Voice+Video library - * - * Copyright 2007 Collabora Ltd, - * Copyright 2007 Nokia Corporation - * @author: Philippe Kalaf . - * Copyright 2007 Wim Taymans - * - * 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. - * - */ - -#ifndef __GST_RTP_JITTER_BUFFER_H__ -#define __GST_RTP_JITTER_BUFFER_H__ - -#include -#include - -G_BEGIN_DECLS - -/* #define's don't like whitespacey bits */ -#define GST_TYPE_RTP_JITTER_BUFFER \ - (gst_rtp_jitter_buffer_get_type()) -#define GST_RTP_JITTER_BUFFER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - GST_TYPE_RTP_JITTER_BUFFER,GstRtpJitterBuffer)) -#define GST_RTP_JITTER_BUFFER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), \ - GST_TYPE_RTP_JITTER_BUFFER,GstRtpJitterBufferClass)) -#define GST_IS_RTP_JITTER_BUFFER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_JITTER_BUFFER)) -#define GST_IS_RTP_JITTER_BUFFER_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_JITTER_BUFFER)) - -typedef struct _GstRtpJitterBuffer GstRtpJitterBuffer; -typedef struct _GstRtpJitterBufferClass GstRtpJitterBufferClass; -typedef struct _GstRtpJitterBufferPrivate GstRtpJitterBufferPrivate; - -/** - * GstRtpJitterBuffer: - * - * Opaque jitterbuffer structure. - */ -struct _GstRtpJitterBuffer -{ - GstElement parent; - - /*< private >*/ - GstRtpJitterBufferPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstRtpJitterBufferClass -{ - GstElementClass parent_class; - - /* signals */ - GstCaps* (*request_pt_map) (GstRtpJitterBuffer *buffer, guint pt); - - void (*handle_sync) (GstRtpJitterBuffer *buffer, GstStructure *s); - void (*on_npt_stop) (GstRtpJitterBuffer *buffer); - - /* actions */ - void (*clear_pt_map) (GstRtpJitterBuffer *buffer); - - /*< private > */ - gpointer _gst_reserved[GST_PADDING]; -}; - -GType gst_rtp_jitter_buffer_get_type (void); - -G_END_DECLS - -#endif /* __GST_RTP_JITTER_BUFFER_H__ */ diff --git a/gst/rtpmanager/gstrtpmanager.c b/gst/rtpmanager/gstrtpmanager.c deleted file mode 100644 index f38a77a8..00000000 --- a/gst/rtpmanager/gstrtpmanager.c +++ /dev/null @@ -1,60 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstrtpbin.h" -#include "gstrtpjitterbuffer.h" -#include "gstrtpptdemux.h" -#include "gstrtpsession.h" -#include "gstrtpssrcdemux.h" - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_element_register (plugin, "gstrtpbin", GST_RANK_NONE, - GST_TYPE_RTP_BIN)) - return FALSE; - - if (!gst_element_register (plugin, "gstrtpjitterbuffer", GST_RANK_NONE, - GST_TYPE_RTP_JITTER_BUFFER)) - return FALSE; - - if (!gst_element_register (plugin, "gstrtpptdemux", GST_RANK_NONE, - GST_TYPE_RTP_PT_DEMUX)) - return FALSE; - - if (!gst_element_register (plugin, "gstrtpsession", GST_RANK_NONE, - GST_TYPE_RTP_SESSION)) - return FALSE; - - if (!gst_element_register (plugin, "gstrtpssrcdemux", GST_RANK_NONE, - GST_TYPE_RTP_SSRC_DEMUX)) - return FALSE; - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gstrtpmanager", - "RTP session management plugin library", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/rtpmanager/gstrtpptdemux.c b/gst/rtpmanager/gstrtpptdemux.c deleted file mode 100644 index 6e34705e..00000000 --- a/gst/rtpmanager/gstrtpptdemux.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * RTP Demux element - * - * Copyright (C) 2005 Nokia Corporation. - * @author Kai Vehmanen - * - * Loosely based on GStreamer gstdecodebin - * Copyright (C) <2004> Wim Taymans - * - * 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. - */ - -/** - * SECTION:element-gstrtpptdemux - * - * gstrtpptdemux acts as a demuxer for RTP packets based on the payload type of - * the packets. Its main purpose is to allow an application to easily receive - * and decode an RTP stream with multiple payload types. - * - * For each payload type that is detected, a new pad will be created and the - * #GstRtpPtDemux::new-payload-type signal will be emitted. When the payload for - * the RTP stream changes, the #GstRtpPtDemux::payload-type-change signal will be - * emitted. - * - * The element will try to set complete and unique application/x-rtp caps on the - * outgoing buffers and pads based on the result of the - * #GstRtpPtDemux::request-pt-map signal. - * - * - * Example pipelines - * |[ - * gst-launch udpsrc caps="application/x-rtp" ! gstrtpptdemux ! fakesink - * ]| Takes an RTP stream and send the RTP packets with the first detected - * payload type to fakesink, discarding the other payload types. - * - * - * Last reviewed on 2007-05-28 (0.10.5) - */ - -/* - * Contributors: - * Andre Moreira Magalhaes - */ -/* - * Status: - * - works with the test_rtpdemux.c tool - * - * Check: - * - is emitting a signal enough, or should we - * use GstEvent to notify downstream elements - * of the new packet... no? - * - * Notes: - * - emits event both for new PTs, and whenever - * a PT is changed - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "gstrtpbin-marshal.h" -#include "gstrtpptdemux.h" - -/* generic templates */ -static GstStaticPadTemplate rtp_pt_demux_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-rtp") - ); - -static GstStaticPadTemplate rtp_pt_demux_src_template = -GST_STATIC_PAD_TEMPLATE ("src_%d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 255 ]") - ); - -GST_DEBUG_CATEGORY_STATIC (gst_rtp_pt_demux_debug); -#define GST_CAT_DEFAULT gst_rtp_pt_demux_debug - -/** - * Item for storing GstPad<->pt pairs. - */ -struct _GstRtpPtDemuxPad -{ - GstPad *pad; /**< pointer to the actual pad */ - gint pt; /**< RTP payload-type attached to pad */ - gboolean newcaps; -}; - -/* signals */ -enum -{ - SIGNAL_REQUEST_PT_MAP, - SIGNAL_NEW_PAYLOAD_TYPE, - SIGNAL_PAYLOAD_TYPE_CHANGE, - SIGNAL_CLEAR_PT_MAP, - LAST_SIGNAL -}; - -GST_BOILERPLATE (GstRtpPtDemux, gst_rtp_pt_demux, GstElement, GST_TYPE_ELEMENT); - -static void gst_rtp_pt_demux_finalize (GObject * object); - -static void gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux); -static gboolean gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux); - -static GstFlowReturn gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf); -static GstStateChangeReturn gst_rtp_pt_demux_change_state (GstElement * element, - GstStateChange transition); -static void gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux); - -static GstRtpPtDemuxPad *find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt); - -static guint gst_rtp_pt_demux_signals[LAST_SIGNAL] = { 0 }; - -static GstElementDetails gst_rtp_pt_demux_details = { - "RTP Demux", - "Demux/Network/RTP", - "Parses codec streams transmitted in the same RTP session", - "Kai Vehmanen " -}; - -static void -gst_rtp_pt_demux_base_init (gpointer g_class) -{ - GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&rtp_pt_demux_sink_template)); - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&rtp_pt_demux_src_template)); - - gst_element_class_set_details (gstelement_klass, &gst_rtp_pt_demux_details); -} - -static void -gst_rtp_pt_demux_class_init (GstRtpPtDemuxClass * klass) -{ - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; - - gobject_klass = (GObjectClass *) klass; - gstelement_klass = (GstElementClass *) klass; - - /** - * GstRtpPtDemux::request-pt-map: - * @demux: the object which received the signal - * @pt: the payload type - * - * Request the payload type as #GstCaps for @pt. - */ - gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP] = - g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, request_pt_map), - NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1, - G_TYPE_UINT); - - /** - * GstRtpPtDemux::new-payload-type: - * @demux: the object which received the signal - * @pt: the payload type - * @pad: the pad with the new payload - * - * Emited when a new payload type pad has been created in @demux. - */ - gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE] = - g_signal_new ("new-payload-type", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, new_payload_type), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT, G_TYPE_NONE, 2, - G_TYPE_UINT, GST_TYPE_PAD); - - /** - * GstRtpPtDemux::payload-type-change: - * @demux: the object which received the signal - * @pt: the new payload type - * - * Emited when the payload type changed. - */ - gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE] = - g_signal_new ("payload-type-change", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, - payload_type_change), NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - - /** - * GstRtpPtDemux::clear-pt-map: - * @demux: the object which received the signal - * - * The application can call this signal to instruct the element to discard the - * currently cached payload type map. - */ - gst_rtp_pt_demux_signals[SIGNAL_CLEAR_PT_MAP] = - g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, - clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0, G_TYPE_NONE); - - gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_finalize); - - gstelement_klass->change_state = - GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_change_state); - - klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_clear_pt_map); - - GST_DEBUG_CATEGORY_INIT (gst_rtp_pt_demux_debug, - "rtpptdemux", 0, "RTP codec demuxer"); -} - -static void -gst_rtp_pt_demux_init (GstRtpPtDemux * ptdemux, GstRtpPtDemuxClass * g_class) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (ptdemux); - - ptdemux->sink = - gst_pad_new_from_template (gst_element_class_get_pad_template (klass, - "sink"), "sink"); - g_assert (ptdemux->sink != NULL); - - gst_pad_set_chain_function (ptdemux->sink, gst_rtp_pt_demux_chain); - - gst_element_add_pad (GST_ELEMENT (ptdemux), ptdemux->sink); -} - -static void -gst_rtp_pt_demux_finalize (GObject * object) -{ - gst_rtp_pt_demux_release (GST_RTP_PT_DEMUX (object)); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static GstCaps * -gst_rtp_pt_demux_get_caps (GstRtpPtDemux * rtpdemux, guint pt) -{ - GstCaps *caps; - GValue ret = { 0 }; - GValue args[2] = { {0}, {0} }; - - /* figure out the caps */ - g_value_init (&args[0], GST_TYPE_ELEMENT); - g_value_set_object (&args[0], rtpdemux); - g_value_init (&args[1], G_TYPE_UINT); - g_value_set_uint (&args[1], pt); - - g_value_init (&ret, GST_TYPE_CAPS); - g_value_set_boxed (&ret, NULL); - - g_signal_emitv (args, gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP], 0, - &ret); - - g_value_unset (&args[0]); - g_value_unset (&args[1]); - caps = g_value_dup_boxed (&ret); - g_value_unset (&ret); - if (caps == NULL) { - caps = GST_PAD_CAPS (rtpdemux->sink); - if (caps) - gst_caps_ref (caps); - } - - GST_DEBUG ("pt %d, got caps %" GST_PTR_FORMAT, pt, caps); - - return caps; -} - -static void -gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux) -{ - GSList *walk; - - GST_OBJECT_LOCK (rtpdemux); - GST_DEBUG ("clearing pt map"); - for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) { - GstRtpPtDemuxPad *pad = walk->data; - - pad->newcaps = TRUE; - } - GST_OBJECT_UNLOCK (rtpdemux); -} - -static GstFlowReturn -gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstRtpPtDemux *rtpdemux; - GstElement *element = GST_ELEMENT (GST_OBJECT_PARENT (pad)); - guint8 pt; - GstPad *srcpad; - GstRtpPtDemuxPad *rtpdemuxpad; - GstCaps *caps; - - rtpdemux = GST_RTP_PT_DEMUX (GST_OBJECT_PARENT (pad)); - - if (!gst_rtp_buffer_validate (buf)) - goto invalid_buffer; - - pt = gst_rtp_buffer_get_payload_type (buf); - - GST_DEBUG_OBJECT (rtpdemux, "received buffer for pt %d", pt); - - rtpdemuxpad = find_pad_for_pt (rtpdemux, pt); - if (rtpdemuxpad == NULL) { - /* new PT, create a src pad */ - GstElementClass *klass; - GstPadTemplate *templ; - gchar *padname; - - klass = GST_ELEMENT_GET_CLASS (rtpdemux); - templ = gst_element_class_get_pad_template (klass, "src_%d"); - padname = g_strdup_printf ("src_%d", pt); - srcpad = gst_pad_new_from_template (templ, padname); - gst_pad_use_fixed_caps (srcpad); - g_free (padname); - - caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt); - if (!caps) - goto no_caps; - - caps = gst_caps_make_writable (caps); - gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL); - gst_pad_set_caps (srcpad, caps); - gst_caps_unref (caps); - - GST_DEBUG ("Adding pt=%d to the list.", pt); - rtpdemuxpad = g_new0 (GstRtpPtDemuxPad, 1); - rtpdemuxpad->pt = pt; - rtpdemuxpad->newcaps = FALSE; - rtpdemuxpad->pad = srcpad; - GST_OBJECT_LOCK (rtpdemux); - rtpdemux->srcpads = g_slist_append (rtpdemux->srcpads, rtpdemuxpad); - GST_OBJECT_UNLOCK (rtpdemux); - - gst_pad_set_active (srcpad, TRUE); - gst_element_add_pad (element, srcpad); - - GST_DEBUG ("emitting new-payload-type for pt %d", pt); - g_signal_emit (G_OBJECT (rtpdemux), - gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE], 0, pt, srcpad); - } - - srcpad = rtpdemuxpad->pad; - - if (pt != rtpdemux->last_pt) { - gint emit_pt = pt; - - /* our own signal with an extra flag that this is the only pad */ - rtpdemux->last_pt = pt; - GST_DEBUG ("emitting payload-type-changed for pt %d", emit_pt); - g_signal_emit (G_OBJECT (rtpdemux), - gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE], 0, emit_pt); - } - - if (rtpdemuxpad->newcaps) { - GST_DEBUG ("need new caps"); - caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt); - if (!caps) - goto no_caps; - - caps = gst_caps_make_writable (caps); - gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL); - gst_pad_set_caps (srcpad, caps); - gst_caps_unref (caps); - rtpdemuxpad->newcaps = FALSE; - } - - gst_buffer_set_caps (buf, GST_PAD_CAPS (srcpad)); - - /* push to srcpad */ - ret = gst_pad_push (srcpad, buf); - - return ret; - - /* ERRORS */ -invalid_buffer: - { - /* this is fatal and should be filtered earlier */ - GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL), - ("Dropping invalid RTP payload")); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -no_caps: - { - GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL), - ("Could not get caps for payload")); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -} - -static GstRtpPtDemuxPad * -find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt) -{ - GstRtpPtDemuxPad *respad = NULL; - GSList *walk; - - for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) { - GstRtpPtDemuxPad *pad = walk->data; - - if (pad->pt == pt) { - respad = pad; - break; - } - } - return respad; -} - -/** - * Reserves resources for the object. - */ -static gboolean -gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux) -{ - ptdemux->srcpads = NULL; - ptdemux->last_pt = 0xFFFF; - - return TRUE; -} - -/** - * Free resources for the object. - */ -static void -gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux) -{ - GSList *walk; - - for (walk = ptdemux->srcpads; walk; walk = g_slist_next (walk)) { - GstRtpPtDemuxPad *pad = walk->data; - - gst_pad_set_active (pad->pad, FALSE); - gst_element_remove_pad (GST_ELEMENT_CAST (ptdemux), pad->pad); - g_free (pad); - } - g_slist_free (ptdemux->srcpads); - ptdemux->srcpads = NULL; -} - -static GstStateChangeReturn -gst_rtp_pt_demux_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstRtpPtDemux *ptdemux; - - ptdemux = GST_RTP_PT_DEMUX (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (gst_rtp_pt_demux_setup (ptdemux) != TRUE) - ret = GST_STATE_CHANGE_FAILURE; - break; - 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: - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_rtp_pt_demux_release (ptdemux); - break; - default: - break; - } - - return ret; -} diff --git a/gst/rtpmanager/gstrtpptdemux.h b/gst/rtpmanager/gstrtpptdemux.h deleted file mode 100644 index 028c97d4..00000000 --- a/gst/rtpmanager/gstrtpptdemux.h +++ /dev/null @@ -1,62 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#ifndef __GST_RTP_PT_DEMUX_H__ -#define __GST_RTP_PT_DEMUX_H__ - -#include - -#define GST_TYPE_RTP_PT_DEMUX (gst_rtp_pt_demux_get_type()) -#define GST_RTP_PT_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PT_DEMUX,GstRtpPtDemux)) -#define GST_RTP_PT_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PT_DEMUX,GstRtpPtDemuxClass)) -#define GST_IS_RTP_PT_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PT_DEMUX)) -#define GST_IS_RTP_PT_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PT_DEMUX)) - -typedef struct _GstRtpPtDemux GstRtpPtDemux; -typedef struct _GstRtpPtDemuxClass GstRtpPtDemuxClass; -typedef struct _GstRtpPtDemuxPad GstRtpPtDemuxPad; - -struct _GstRtpPtDemux -{ - GstElement parent; /**< parent class */ - - GstPad *sink; /**< the sink pad */ - guint16 last_pt; /**< pt of the last packet 0xFFFF if none */ - GSList *srcpads; /**< a linked list of GstRtpPtDemuxPad objects */ -}; - -struct _GstRtpPtDemuxClass -{ - GstElementClass parent_class; - - /* get the caps for pt */ - GstCaps* (*request_pt_map) (GstRtpPtDemux *demux, guint pt); - - /* signal emmited when a new PT is found from the incoming stream */ - void (*new_payload_type) (GstRtpPtDemux *demux, guint pt, GstPad * pad); - - /* signal emitted when the payload type changes */ - void (*payload_type_change) (GstRtpPtDemux *demux, guint pt); - - void (*clear_pt_map) (GstRtpPtDemux *demux); -}; - -GType gst_rtp_pt_demux_get_type (void); - -#endif /* __GST_RTP_PT_DEMUX_H__ */ diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c deleted file mode 100644 index dcddb689..00000000 --- a/gst/rtpmanager/gstrtpsession.c +++ /dev/null @@ -1,1940 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -/** - * SECTION:element-gstrtpsession - * @see_also: gstrtpjitterbuffer, gstrtpbin, gstrtpptdemux, gstrtpssrcdemux - * - * The RTP session manager models one participant with a unique SSRC in an RTP - * session. This session can be used to send and receive RTP and RTCP packets. - * Based on what REQUEST pads are requested from the session manager, specific - * functionality can be activated. - * - * The session manager currently implements RFC 3550 including: - * - * - * RTP packet validation based on consecutive sequence numbers. - * - * - * Maintainance of the SSRC participant database. - * - * - * Keeping per participant statistics based on received RTCP packets. - * - * - * Scheduling of RR/SR RTCP packets. - * - * - * - * The gstrtpsession will not demux packets based on SSRC or payload type, nor will - * it correct for packet reordering and jitter. Use #GstRtpsSrcDemux, - * #GstRtpPtDemux and GstRtpJitterBuffer in addition to #GstRtpSession to - * perform these tasks. It is usually a good idea to use #GstRtpBin, which - * combines all these features in one element. - * - * To use #GstRtpSession as an RTP receiver, request a recv_rtp_sink pad, which will - * automatically create recv_rtp_src pad. Data received on the recv_rtp_sink pad - * will be processed in the session and after being validated forwarded on the - * recv_rtp_src pad. - * - * To also use #GstRtpSession as an RTCP receiver, request a recv_rtcp_sink pad, - * which will automatically create a sync_src pad. Packets received on the RTCP - * pad will be used by the session manager to update the stats and database of - * the other participants. SR packets will be forwarded on the sync_src pad - * so that they can be used to perform inter-stream synchronisation when needed. - * - * If you want the session manager to generate and send RTCP packets, request - * the send_rtcp_src pad. Packet pushed on this pad contain SR/RR RTCP reports - * that should be sent to all participants in the session. - * - * To use #GstRtpSession as a sender, request a send_rtp_sink pad, which will - * automatically create a send_rtp_src pad. The session manager will modify the - * SSRC in the RTP packets to its own SSRC and wil forward the packets on the - * send_rtp_src pad after updating its internal state. - * - * The session manager needs the clock-rate of the payload types it is handling - * and will signal the #GstRtpSession::request-pt-map signal when it needs such a - * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map - * signal. - * - * - * Example pipelines - * |[ - * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink gstrtpsession .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink - * ]| Receive theora RTP packets from port 5000 and send them to the depayloader, - * decoder and display. Note that the application/x-rtp caps on udpsrc should be - * configured based on some negotiation process such as RTSP for this pipeline - * to work correctly. - * |[ - * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink gstrtpsession name=session \ - * .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink \ - * udpsrc port=5001 caps="application/x-rtcp" ! session.recv_rtcp_sink - * ]| Receive theora RTP packets from port 5000 and send them to the depayloader, - * decoder and display. Receive RTCP packets from port 5001 and process them in - * the session manager. - * Note that the application/x-rtp caps on udpsrc should be - * configured based on some negotiation process such as RTSP for this pipeline - * to work correctly. - * |[ - * gst-launch videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink gstrtpsession .send_rtp_src ! udpsink port=5000 - * ]| Send theora RTP packets through the session manager and out on UDP port - * 5000. - * |[ - * gst-launch videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink gstrtpsession name=session .send_rtp_src \ - * ! udpsink port=5000 session.send_rtcp_src ! udpsink port=5001 - * ]| Send theora RTP packets through the session manager and out on UDP port - * 5000. Send RTCP packets on port 5001. Note that this pipeline will not preroll - * correctly because the second udpsink will not preroll correctly (no RTCP - * packets are sent in the PAUSED state). Applications should manually set and - * keep (see gst_element_set_locked_state()) the RTCP udpsink to the PLAYING state. - * - * - * Last reviewed on 2007-05-28 (0.10.5) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "gstrtpbin-marshal.h" -#include "gstrtpsession.h" -#include "rtpsession.h" - -GST_DEBUG_CATEGORY_STATIC (gst_rtp_session_debug); -#define GST_CAT_DEFAULT gst_rtp_session_debug - -/* elementfactory information */ -static const GstElementDetails rtpsession_details = -GST_ELEMENT_DETAILS ("RTP Session", - "Filter/Network/RTP", - "Implement an RTP session", - "Wim Taymans "); - -/* sink pads */ -static GstStaticPadTemplate rtpsession_recv_rtp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp") - ); - -static GstStaticPadTemplate rtpsession_recv_rtcp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -static GstStaticPadTemplate rtpsession_send_rtp_sink_template = -GST_STATIC_PAD_TEMPLATE ("send_rtp_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp") - ); - -/* src pads */ -static GstStaticPadTemplate rtpsession_recv_rtp_src_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtp_src", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp") - ); - -static GstStaticPadTemplate rtpsession_sync_src_template = -GST_STATIC_PAD_TEMPLATE ("sync_src", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -static GstStaticPadTemplate rtpsession_send_rtp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtp_src", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp") - ); - -static GstStaticPadTemplate rtpsession_send_rtcp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtcp_src", - GST_PAD_SRC, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -/* signals and args */ -enum -{ - SIGNAL_REQUEST_PT_MAP, - SIGNAL_CLEAR_PT_MAP, - - SIGNAL_ON_NEW_SSRC, - SIGNAL_ON_SSRC_COLLISION, - SIGNAL_ON_SSRC_VALIDATED, - SIGNAL_ON_SSRC_ACTIVE, - SIGNAL_ON_SSRC_SDES, - SIGNAL_ON_BYE_SSRC, - SIGNAL_ON_BYE_TIMEOUT, - SIGNAL_ON_TIMEOUT, - SIGNAL_ON_SENDER_TIMEOUT, - LAST_SIGNAL -}; - -#define DEFAULT_NTP_NS_BASE 0 -#define DEFAULT_BANDWIDTH RTP_STATS_BANDWIDTH -#define DEFAULT_RTCP_FRACTION RTP_STATS_RTCP_BANDWIDTH -#define DEFAULT_SDES NULL -#define DEFAULT_NUM_SOURCES 0 -#define DEFAULT_NUM_ACTIVE_SOURCES 0 - -enum -{ - PROP_0, - PROP_NTP_NS_BASE, - PROP_BANDWIDTH, - PROP_RTCP_FRACTION, - PROP_SDES, - PROP_NUM_SOURCES, - PROP_NUM_ACTIVE_SOURCES, - PROP_INTERNAL_SESSION, - PROP_LAST -}; - -#define GST_RTP_SESSION_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_SESSION, GstRtpSessionPrivate)) - -#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->priv->lock) -#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock ((sess)->priv->lock) - -struct _GstRtpSessionPrivate -{ - GMutex *lock; - GstClock *sysclock; - - RTPSession *session; - - /* thread for sending out RTCP */ - GstClockID id; - gboolean stop_thread; - GThread *thread; - gboolean thread_stopped; - - /* caps mapping */ - GHashTable *ptmap; - - /* NTP base time */ - guint64 ntpnsbase; -}; - -/* callbacks to handle actions from the session manager */ -static GstFlowReturn gst_rtp_session_process_rtp (RTPSession * sess, - RTPSource * src, GstBuffer * buffer, gpointer user_data); -static GstFlowReturn gst_rtp_session_send_rtp (RTPSession * sess, - RTPSource * src, gpointer data, gpointer user_data); -static GstFlowReturn gst_rtp_session_send_rtcp (RTPSession * sess, - RTPSource * src, GstBuffer * buffer, gboolean eos, gpointer user_data); -static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess, - RTPSource * src, GstBuffer * buffer, gpointer user_data); -static gint gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload, - gpointer user_data); -static void gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data); - -static RTPSessionCallbacks callbacks = { - gst_rtp_session_process_rtp, - gst_rtp_session_send_rtp, - gst_rtp_session_sync_rtcp, - gst_rtp_session_send_rtcp, - gst_rtp_session_clock_rate, - gst_rtp_session_reconsider -}; - -/* GObject vmethods */ -static void gst_rtp_session_finalize (GObject * object); -static void gst_rtp_session_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_rtp_session_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -/* GstElement vmethods */ -static GstStateChangeReturn gst_rtp_session_change_state (GstElement * element, - GstStateChange transition); -static GstPad *gst_rtp_session_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_rtp_session_release_pad (GstElement * element, GstPad * pad); - -static void gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession); - -static guint gst_rtp_session_signals[LAST_SIGNAL] = { 0 }; - -static void -on_new_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0, - src->ssrc); -} - -static void -on_ssrc_collision (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0, - src->ssrc); -} - -static void -on_ssrc_validated (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0, - src->ssrc); -} - -static void -on_ssrc_active (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0, - src->ssrc); -} - -static void -on_ssrc_sdes (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - GstStructure *s; - GstMessage *m; - - /* convert the new SDES info into a message */ - RTP_SESSION_LOCK (session); - g_object_get (src, "sdes", &s, NULL); - RTP_SESSION_UNLOCK (session); - - m = gst_message_new_custom (GST_MESSAGE_ELEMENT, GST_OBJECT (sess), s); - gst_element_post_message (GST_ELEMENT_CAST (sess), m); - - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0, - src->ssrc); -} - -static void -on_bye_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0, - src->ssrc); -} - -static void -on_bye_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0, - src->ssrc); -} - -static void -on_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_TIMEOUT], 0, - src->ssrc); -} - -static void -on_sender_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess) -{ - g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0, - src->ssrc); -} - -GST_BOILERPLATE (GstRtpSession, gst_rtp_session, GstElement, GST_TYPE_ELEMENT); - -static void -gst_rtp_session_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - /* sink pads */ - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_recv_rtp_sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_recv_rtcp_sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_send_rtp_sink_template)); - - /* src pads */ - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_recv_rtp_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_sync_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_send_rtp_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rtpsession_send_rtcp_src_template)); - - gst_element_class_set_details (element_class, &rtpsession_details); -} - -static void -gst_rtp_session_class_init (GstRtpSessionClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - g_type_class_add_private (klass, sizeof (GstRtpSessionPrivate)); - - gobject_class->finalize = gst_rtp_session_finalize; - gobject_class->set_property = gst_rtp_session_set_property; - gobject_class->get_property = gst_rtp_session_get_property; - - /** - * GstRtpSession::request-pt-map: - * @sess: the object which received the signal - * @pt: the pt - * - * Request the payload type as #GstCaps for @pt. - */ - gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP] = - g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, request_pt_map), - NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1, - G_TYPE_UINT); - /** - * GstRtpSession::clear-pt-map: - * @sess: the object which received the signal - * - * Clear the cached pt-maps requested with #GstRtpSession::request-pt-map. - */ - gst_rtp_session_signals[SIGNAL_CLEAR_PT_MAP] = - g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpSessionClass, clear_pt_map), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); - - /** - * GstRtpSession::on-new-ssrc: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of a new SSRC that entered @session. - */ - gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC] = - g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_new_ssrc), - NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-ssrc_collision: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify when we have an SSRC collision - */ - gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] = - g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, - on_ssrc_collision), NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-ssrc_validated: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of a new SSRC that became validated. - */ - gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] = - g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, - on_ssrc_validated), NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-ssrc_active: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of a SSRC that is active, i.e., sending RTCP. - */ - gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] = - g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, - on_ssrc_active), NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-ssrc-sdes: - * @session: the object which received the signal - * @src: the SSRC - * - * Notify that a new SDES was received for SSRC. - */ - gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES] = - g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_ssrc_sdes), - NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - - /** - * GstRtpSession::on-bye-ssrc: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of an SSRC that became inactive because of a BYE packet. - */ - gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC] = - g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_ssrc), - NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-bye-timeout: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of an SSRC that has timed out because of BYE - */ - gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] = - g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_timeout), - NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-timeout: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of an SSRC that has timed out - */ - gst_rtp_session_signals[SIGNAL_ON_TIMEOUT] = - g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_timeout), - NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - /** - * GstRtpSession::on-sender-timeout: - * @sess: the object which received the signal - * @ssrc: the SSRC - * - * Notify of a sender SSRC that has timed out and became a receiver - */ - gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] = - g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, - on_sender_timeout), NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - - g_object_class_install_property (gobject_class, PROP_NTP_NS_BASE, - g_param_spec_uint64 ("ntp-ns-base", "NTP base time", - "The NTP base time corresponding to running_time 0", 0, - G_MAXUINT64, DEFAULT_NTP_NS_BASE, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, PROP_BANDWIDTH, - g_param_spec_double ("bandwidth", "Bandwidth", - "The bandwidth of the session", - 0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION, - g_param_spec_double ("rtcp-fraction", "RTCP Fraction", - "The fraction of the bandwidth used for RTCP", - 0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, PROP_SDES, - g_param_spec_boxed ("sdes", "SDES", - "The SDES items of this session", - GST_TYPE_STRUCTURE, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, PROP_NUM_SOURCES, - g_param_spec_uint ("num-sources", "Num Sources", - "The number of sources in the session", 0, G_MAXUINT, - DEFAULT_NUM_SOURCES, G_PARAM_READABLE)); - - g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES, - g_param_spec_uint ("num-active-sources", "Num Active Sources", - "The number of active sources in the session", 0, G_MAXUINT, - DEFAULT_NUM_ACTIVE_SOURCES, G_PARAM_READABLE)); - - g_object_class_install_property (gobject_class, PROP_INTERNAL_SESSION, - g_param_spec_object ("internal-session", "Internal Session", - "The internal RTPSession object", RTP_TYPE_SESSION, - G_PARAM_READABLE)); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_rtp_session_change_state); - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_rtp_session_request_new_pad); - gstelement_class->release_pad = - GST_DEBUG_FUNCPTR (gst_rtp_session_release_pad); - - klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_session_clear_pt_map); - - GST_DEBUG_CATEGORY_INIT (gst_rtp_session_debug, - "rtpsession", 0, "RTP Session"); -} - -static void -gst_rtp_session_init (GstRtpSession * rtpsession, GstRtpSessionClass * klass) -{ - rtpsession->priv = GST_RTP_SESSION_GET_PRIVATE (rtpsession); - rtpsession->priv->lock = g_mutex_new (); - rtpsession->priv->sysclock = gst_system_clock_obtain (); - rtpsession->priv->session = rtp_session_new (); - - /* configure callbacks */ - rtp_session_set_callbacks (rtpsession->priv->session, &callbacks, rtpsession); - /* configure signals */ - g_signal_connect (rtpsession->priv->session, "on-new-ssrc", - (GCallback) on_new_ssrc, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-ssrc-collision", - (GCallback) on_ssrc_collision, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-ssrc-validated", - (GCallback) on_ssrc_validated, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-ssrc-active", - (GCallback) on_ssrc_active, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-ssrc-sdes", - (GCallback) on_ssrc_sdes, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-bye-ssrc", - (GCallback) on_bye_ssrc, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-bye-timeout", - (GCallback) on_bye_timeout, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-timeout", - (GCallback) on_timeout, rtpsession); - g_signal_connect (rtpsession->priv->session, "on-sender-timeout", - (GCallback) on_sender_timeout, rtpsession); - rtpsession->priv->ptmap = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) gst_caps_unref); - - gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED); - gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED); - - rtpsession->priv->thread_stopped = TRUE; -} - -static void -gst_rtp_session_finalize (GObject * object) -{ - GstRtpSession *rtpsession; - - rtpsession = GST_RTP_SESSION (object); - - if (rtpsession->recv_rtp_sink != NULL) - gst_object_unref (rtpsession->recv_rtp_sink); - if (rtpsession->recv_rtcp_sink != NULL) - gst_object_unref (rtpsession->recv_rtcp_sink); - if (rtpsession->send_rtp_sink != NULL) - gst_object_unref (rtpsession->send_rtp_sink); - if (rtpsession->send_rtcp_src != NULL) - gst_object_unref (rtpsession->send_rtcp_src); - - g_hash_table_destroy (rtpsession->priv->ptmap); - g_mutex_free (rtpsession->priv->lock); - g_object_unref (rtpsession->priv->sysclock); - g_object_unref (rtpsession->priv->session); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_rtp_session_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (object); - priv = rtpsession->priv; - - switch (prop_id) { - case PROP_NTP_NS_BASE: - GST_OBJECT_LOCK (rtpsession); - priv->ntpnsbase = g_value_get_uint64 (value); - GST_DEBUG_OBJECT (rtpsession, "setting NTP base to %" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->ntpnsbase)); - GST_OBJECT_UNLOCK (rtpsession); - break; - case PROP_BANDWIDTH: - rtp_session_set_bandwidth (priv->session, g_value_get_double (value)); - break; - case PROP_RTCP_FRACTION: - rtp_session_set_rtcp_fraction (priv->session, g_value_get_double (value)); - break; - case PROP_SDES: - rtp_session_set_sdes_struct (priv->session, g_value_get_boxed (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_rtp_session_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (object); - priv = rtpsession->priv; - - switch (prop_id) { - case PROP_NTP_NS_BASE: - GST_OBJECT_LOCK (rtpsession); - g_value_set_uint64 (value, priv->ntpnsbase); - GST_OBJECT_UNLOCK (rtpsession); - break; - case PROP_BANDWIDTH: - g_value_set_double (value, rtp_session_get_bandwidth (priv->session)); - break; - case PROP_RTCP_FRACTION: - g_value_set_double (value, rtp_session_get_rtcp_fraction (priv->session)); - break; - case PROP_SDES: - g_value_take_boxed (value, rtp_session_get_sdes_struct (priv->session)); - break; - case PROP_NUM_SOURCES: - g_value_set_uint (value, rtp_session_get_num_sources (priv->session)); - break; - case PROP_NUM_ACTIVE_SOURCES: - g_value_set_uint (value, - rtp_session_get_num_active_sources (priv->session)); - break; - case PROP_INTERNAL_SESSION: - g_value_set_object (value, priv->session); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -get_current_times (GstRtpSession * rtpsession, - GstClockTime * running_time, guint64 * ntpnstime) -{ - guint64 ntpns; - GstClock *clock; - GstClockTime base_time, ntpnsbase, rt; - - GST_OBJECT_LOCK (rtpsession); - if ((clock = GST_ELEMENT_CLOCK (rtpsession))) { - base_time = GST_ELEMENT_CAST (rtpsession)->base_time; - ntpnsbase = rtpsession->priv->ntpnsbase; - gst_object_ref (clock); - GST_OBJECT_UNLOCK (rtpsession); - - /* get current clock time and convert to running time */ - rt = gst_clock_get_time (clock) - base_time; - /* add NTP base offset to get NTP ns time */ - ntpns = rt + ntpnsbase; - - gst_object_unref (clock); - } else { - GST_OBJECT_UNLOCK (rtpsession); - rt = -1; - ntpns = -1; - } - if (running_time) - *running_time = rt; - if (ntpnstime) - *ntpnstime = ntpns; -} - -static void -rtcp_thread (GstRtpSession * rtpsession) -{ - GstClockID id; - GstClockTime current_time; - GstClockTime next_timeout; - guint64 ntpnstime; - - GST_DEBUG_OBJECT (rtpsession, "entering RTCP thread"); - - GST_RTP_SESSION_LOCK (rtpsession); - - current_time = gst_clock_get_time (rtpsession->priv->sysclock); - - while (!rtpsession->priv->stop_thread) { - GstClockReturn res; - - /* get initial estimate */ - next_timeout = - rtp_session_next_timeout (rtpsession->priv->session, current_time); - - GST_DEBUG_OBJECT (rtpsession, "next check time %" GST_TIME_FORMAT, - GST_TIME_ARGS (next_timeout)); - - /* leave if no more timeouts, the session ended */ - if (next_timeout == GST_CLOCK_TIME_NONE) - break; - - id = rtpsession->priv->id = - gst_clock_new_single_shot_id (rtpsession->priv->sysclock, next_timeout); - GST_RTP_SESSION_UNLOCK (rtpsession); - - res = gst_clock_id_wait (id, NULL); - - GST_RTP_SESSION_LOCK (rtpsession); - gst_clock_id_unref (id); - rtpsession->priv->id = NULL; - - if (rtpsession->priv->stop_thread) - break; - - /* update current time */ - current_time = gst_clock_get_time (rtpsession->priv->sysclock); - - /* get current NTP time */ - get_current_times (rtpsession, NULL, &ntpnstime); - - /* we get unlocked because we need to perform reconsideration, don't perform - * the timeout but get a new reporting estimate. */ - GST_DEBUG_OBJECT (rtpsession, "unlocked %d, current %" GST_TIME_FORMAT, - res, GST_TIME_ARGS (current_time)); - - /* perform actions, we ignore result. Release lock because it might push. */ - GST_RTP_SESSION_UNLOCK (rtpsession); - rtp_session_on_timeout (rtpsession->priv->session, current_time, ntpnstime); - GST_RTP_SESSION_LOCK (rtpsession); - } - /* mark the thread as stopped now */ - rtpsession->priv->thread_stopped = TRUE; - GST_RTP_SESSION_UNLOCK (rtpsession); - - GST_DEBUG_OBJECT (rtpsession, "leaving RTCP thread"); -} - -static gboolean -start_rtcp_thread (GstRtpSession * rtpsession) -{ - GError *error = NULL; - gboolean res; - - GST_DEBUG_OBJECT (rtpsession, "starting RTCP thread"); - - GST_RTP_SESSION_LOCK (rtpsession); - rtpsession->priv->stop_thread = FALSE; - if (rtpsession->priv->thread_stopped) { - /* if the thread stopped, and we still have a handle to the thread, join it - * now. We can safely join with the lock held, the thread will not take it - * anymore. */ - if (rtpsession->priv->thread) - g_thread_join (rtpsession->priv->thread); - /* only create a new thread if the old one was stopped. Otherwise we can - * just reuse the currently running one. */ - rtpsession->priv->thread = - g_thread_create ((GThreadFunc) rtcp_thread, rtpsession, TRUE, &error); - rtpsession->priv->thread_stopped = FALSE; - } - GST_RTP_SESSION_UNLOCK (rtpsession); - - if (error != NULL) { - res = FALSE; - GST_DEBUG_OBJECT (rtpsession, "failed to start thread, %s", error->message); - g_error_free (error); - } else { - res = TRUE; - } - return res; -} - -static void -stop_rtcp_thread (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "stopping RTCP thread"); - - GST_RTP_SESSION_LOCK (rtpsession); - rtpsession->priv->stop_thread = TRUE; - if (rtpsession->priv->id) - gst_clock_id_unschedule (rtpsession->priv->id); - GST_RTP_SESSION_UNLOCK (rtpsession); -} - -static void -join_rtcp_thread (GstRtpSession * rtpsession) -{ - GST_RTP_SESSION_LOCK (rtpsession); - /* don't try to join when we have no thread */ - if (rtpsession->priv->thread != NULL) { - GST_DEBUG_OBJECT (rtpsession, "joining RTCP thread"); - GST_RTP_SESSION_UNLOCK (rtpsession); - - g_thread_join (rtpsession->priv->thread); - - GST_RTP_SESSION_LOCK (rtpsession); - /* after the join, take the lock and clear the thread structure. The caller - * is supposed to not concurrently call start and join. */ - rtpsession->priv->thread = NULL; - } - GST_RTP_SESSION_UNLOCK (rtpsession); -} - -static GstStateChangeReturn -gst_rtp_session_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn res; - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (element); - priv = rtpsession->priv; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* no need to join yet, we might want to continue later. Also, the - * dataflow could block downstream so that a join could just block - * forever. */ - stop_rtcp_thread (rtpsession); - break; - default: - break; - } - - res = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - if (!start_rtcp_thread (rtpsession)) - goto failed_thread; - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* downstream is now releasing the dataflow and we can join. */ - join_rtcp_thread (rtpsession); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - return res; - - /* ERRORS */ -failed_thread: - { - return GST_STATE_CHANGE_FAILURE; - } -} - -static gboolean -return_true (gpointer key, gpointer value, gpointer user_data) -{ - return TRUE; -} - -static void -gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession) -{ - g_hash_table_foreach_remove (rtpsession->priv->ptmap, return_true, NULL); -} - -/* called when the session manager has an RTP packet or a list of packets - * ready for further processing */ -static GstFlowReturn -gst_rtp_session_process_rtp (RTPSession * sess, RTPSource * src, - GstBuffer * buffer, gpointer user_data) -{ - GstFlowReturn result; - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (user_data); - priv = rtpsession->priv; - - if (rtpsession->recv_rtp_src) { - GST_LOG_OBJECT (rtpsession, "pushing received RTP packet"); - result = gst_pad_push (rtpsession->recv_rtp_src, buffer); - } else { - GST_DEBUG_OBJECT (rtpsession, "dropping received RTP packet"); - gst_buffer_unref (buffer); - result = GST_FLOW_OK; - } - return result; -} - -/* called when the session manager has an RTP packet ready for further - * sending */ -static GstFlowReturn -gst_rtp_session_send_rtp (RTPSession * sess, RTPSource * src, - gpointer data, gpointer user_data) -{ - GstFlowReturn result; - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (user_data); - priv = rtpsession->priv; - - if (rtpsession->send_rtp_src) { - if (GST_IS_BUFFER (data)) { - GST_LOG_OBJECT (rtpsession, "sending RTP packet"); - result = gst_pad_push (rtpsession->send_rtp_src, GST_BUFFER_CAST (data)); - } else { - GST_LOG_OBJECT (rtpsession, "sending RTP list"); - result = gst_pad_push_list (rtpsession->send_rtp_src, - GST_BUFFER_LIST_CAST (data)); - } - } else { - gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); - result = GST_FLOW_OK; - } - return result; -} - -/* called when the session manager has an RTCP packet ready for further - * sending. The eos flag is set when an EOS event should be sent downstream as - * well. */ -static GstFlowReturn -gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src, - GstBuffer * buffer, gboolean eos, gpointer user_data) -{ - GstFlowReturn result; - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (user_data); - priv = rtpsession->priv; - - if (rtpsession->send_rtcp_src) { - GstCaps *caps; - - /* set rtcp caps on output pad */ - if (!(caps = GST_PAD_CAPS (rtpsession->send_rtcp_src))) { - caps = gst_caps_new_simple ("application/x-rtcp", NULL); - gst_pad_set_caps (rtpsession->send_rtcp_src, caps); - gst_caps_unref (caps); - } - gst_buffer_set_caps (buffer, caps); - GST_LOG_OBJECT (rtpsession, "sending RTCP"); - result = gst_pad_push (rtpsession->send_rtcp_src, buffer); - - /* we have to send EOS after this packet */ - if (eos) { - GST_LOG_OBJECT (rtpsession, "sending EOS"); - gst_pad_push_event (rtpsession->send_rtcp_src, gst_event_new_eos ()); - } - } else { - GST_DEBUG_OBJECT (rtpsession, "not sending RTCP, no output pad"); - gst_buffer_unref (buffer); - result = GST_FLOW_OK; - } - return result; -} - -/* called when the session manager has an SR RTCP packet ready for handling - * inter stream synchronisation */ -static GstFlowReturn -gst_rtp_session_sync_rtcp (RTPSession * sess, - RTPSource * src, GstBuffer * buffer, gpointer user_data) -{ - GstFlowReturn result; - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (user_data); - priv = rtpsession->priv; - - if (rtpsession->sync_src) { - GstCaps *caps; - - /* set rtcp caps on output pad */ - if (!(caps = GST_PAD_CAPS (rtpsession->sync_src))) { - caps = gst_caps_new_simple ("application/x-rtcp", NULL); - gst_pad_set_caps (rtpsession->sync_src, caps); - gst_caps_unref (caps); - } - gst_buffer_set_caps (buffer, caps); - GST_LOG_OBJECT (rtpsession, "sending Sync RTCP"); - result = gst_pad_push (rtpsession->sync_src, buffer); - } else { - GST_DEBUG_OBJECT (rtpsession, "not sending Sync RTCP, no output pad"); - gst_buffer_unref (buffer); - result = GST_FLOW_OK; - } - return result; -} - -static void -gst_rtp_session_cache_caps (GstRtpSession * rtpsession, GstCaps * caps) -{ - GstRtpSessionPrivate *priv; - const GstStructure *s; - gint payload; - - priv = rtpsession->priv; - - GST_DEBUG_OBJECT (rtpsession, "parsing caps"); - - s = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (s, "payload", &payload)) - return; - - if (g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (payload))) - return; - - g_hash_table_insert (priv->ptmap, GINT_TO_POINTER (payload), - gst_caps_ref (caps)); -} - -/* called when the session manager needs the clock rate */ -static gint -gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload, - gpointer user_data) -{ - gint ipayload, result = -1; - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GValue ret = { 0 }; - GValue args[2] = { {0}, {0} }; - GstCaps *caps; - const GstStructure *s; - - rtpsession = GST_RTP_SESSION_CAST (user_data); - priv = rtpsession->priv; - - GST_RTP_SESSION_LOCK (rtpsession); - ipayload = payload; /* make compiler happy */ - caps = g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (ipayload)); - if (caps) { - gst_caps_ref (caps); - goto found; - } - - /* not found in the cache, try to get it with a signal */ - g_value_init (&args[0], GST_TYPE_ELEMENT); - g_value_set_object (&args[0], rtpsession); - g_value_init (&args[1], G_TYPE_UINT); - g_value_set_uint (&args[1], payload); - - g_value_init (&ret, GST_TYPE_CAPS); - g_value_set_boxed (&ret, NULL); - - g_signal_emitv (args, gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP], 0, - &ret); - - g_value_unset (&args[0]); - g_value_unset (&args[1]); - caps = (GstCaps *) g_value_dup_boxed (&ret); - g_value_unset (&ret); - if (!caps) - goto no_caps; - - gst_rtp_session_cache_caps (rtpsession, caps); - -found: - s = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (s, "clock-rate", &result)) - goto no_clock_rate; - - gst_caps_unref (caps); - - GST_DEBUG_OBJECT (rtpsession, "parsed clock-rate %d", result); - -done: - GST_RTP_SESSION_UNLOCK (rtpsession); - - return result; - - /* ERRORS */ -no_caps: - { - GST_DEBUG_OBJECT (rtpsession, "could not get caps"); - goto done; - } -no_clock_rate: - { - gst_caps_unref (caps); - GST_DEBUG_OBJECT (rtpsession, "No clock-rate in caps!"); - goto done; - } -} - -/* called when the session manager asks us to reconsider the timeout */ -static void -gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data) -{ - GstRtpSession *rtpsession; - - rtpsession = GST_RTP_SESSION_CAST (user_data); - - GST_RTP_SESSION_LOCK (rtpsession); - GST_DEBUG_OBJECT (rtpsession, "unlock timer for reconsideration"); - if (rtpsession->priv->id) - gst_clock_id_unschedule (rtpsession->priv->id); - GST_RTP_SESSION_UNLOCK (rtpsession); -} - -static gboolean -gst_rtp_session_event_recv_rtp_sink (GstPad * pad, GstEvent * event) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - gboolean ret = FALSE; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_DEBUG_OBJECT (rtpsession, "received event %s", - GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED); - ret = gst_pad_push_event (rtpsession->recv_rtp_src, event); - break; - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - gdouble rate, arate; - GstFormat format; - gint64 start, stop, time; - GstSegment *segment; - - segment = &rtpsession->recv_rtp_seg; - - /* the newsegment event is needed to convert the RTP timestamp to - * running_time, which is needed to generate a mapping from RTP to NTP - * timestamps in SR reports */ - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - GST_DEBUG_OBJECT (rtpsession, - "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " - "format GST_FORMAT_TIME, " - "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT - ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (segment->start), - GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time), - GST_TIME_ARGS (segment->accum)); - - gst_segment_set_newsegment_full (segment, update, rate, - arate, format, start, stop, time); - - /* push event forward */ - ret = gst_pad_push_event (rtpsession->recv_rtp_src, event); - break; - } - default: - ret = gst_pad_push_event (rtpsession->recv_rtp_src, event); - break; - } - gst_object_unref (rtpsession); - - return ret; - -} - -static GList * -gst_rtp_session_internal_links (GstPad * pad) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GList *res = NULL; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - if (pad == rtpsession->recv_rtp_src) { - res = g_list_prepend (res, rtpsession->recv_rtp_sink); - } else if (pad == rtpsession->recv_rtp_sink) { - res = g_list_prepend (res, rtpsession->recv_rtp_src); - } else if (pad == rtpsession->send_rtp_src) { - res = g_list_prepend (res, rtpsession->send_rtp_sink); - } else if (pad == rtpsession->send_rtp_sink) { - res = g_list_prepend (res, rtpsession->send_rtp_src); - } - - gst_object_unref (rtpsession); - - return res; -} - -static gboolean -gst_rtp_session_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_RTP_SESSION_LOCK (rtpsession); - gst_rtp_session_cache_caps (rtpsession, caps); - GST_RTP_SESSION_UNLOCK (rtpsession); - - gst_object_unref (rtpsession); - - return TRUE; -} - -/* receive a packet from a sender, send it to the RTP session manager and - * forward the packet on the rtp_src pad - */ -static GstFlowReturn -gst_rtp_session_chain_recv_rtp (GstPad * pad, GstBuffer * buffer) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GstFlowReturn ret; - GstClockTime current_time, running_time; - guint64 ntpnstime; - GstClockTime timestamp; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_LOG_OBJECT (rtpsession, "received RTP packet"); - - /* get NTP time when this packet was captured, this depends on the timestamp. */ - timestamp = GST_BUFFER_TIMESTAMP (buffer); - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - /* convert to running time using the segment values */ - running_time = - gst_segment_to_running_time (&rtpsession->recv_rtp_seg, GST_FORMAT_TIME, - timestamp); - /* add constant to convert running time to NTP time */ - ntpnstime = running_time + priv->ntpnsbase; - } else { - get_current_times (rtpsession, &running_time, &ntpnstime); - } - current_time = gst_clock_get_time (priv->sysclock); - - ret = rtp_session_process_rtp (priv->session, buffer, current_time, - running_time, ntpnstime); - if (ret != GST_FLOW_OK) - goto push_error; - -done: - gst_object_unref (rtpsession); - - return ret; - - /* ERRORS */ -push_error: - { - GST_DEBUG_OBJECT (rtpsession, "process returned %s", - gst_flow_get_name (ret)); - goto done; - } -} - -static gboolean -gst_rtp_session_event_recv_rtcp_sink (GstPad * pad, GstEvent * event) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - gboolean ret = FALSE; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_DEBUG_OBJECT (rtpsession, "received event %s", - GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - default: - ret = gst_pad_push_event (rtpsession->sync_src, event); - break; - } - gst_object_unref (rtpsession); - - return ret; -} - -/* Receive an RTCP packet from a sender, send it to the RTP session manager and - * forward the SR packets to the sync_src pad. - */ -static GstFlowReturn -gst_rtp_session_chain_recv_rtcp (GstPad * pad, GstBuffer * buffer) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GstClockTime current_time; - GstFlowReturn ret; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_LOG_OBJECT (rtpsession, "received RTCP packet"); - - current_time = gst_clock_get_time (priv->sysclock); - ret = rtp_session_process_rtcp (priv->session, buffer, current_time); - - gst_object_unref (rtpsession); - - return GST_FLOW_OK; -} - -static gboolean -gst_rtp_session_query_send_rtcp_src (GstPad * pad, GstQuery * query) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - gboolean ret = FALSE; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_DEBUG_OBJECT (rtpsession, "received QUERY"); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - ret = TRUE; - /* use the defaults for the latency query. */ - gst_query_set_latency (query, FALSE, 0, -1); - break; - default: - /* other queries simply fail for now */ - break; - } - - gst_object_unref (rtpsession); - - return ret; -} - -static gboolean -gst_rtp_session_event_send_rtcp_src (GstPad * pad, GstEvent * event) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - gboolean ret; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_DEBUG_OBJECT (rtpsession, "received EVENT"); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - case GST_EVENT_LATENCY: - gst_event_unref (event); - ret = TRUE; - break; - default: - /* other events simply fail for now */ - gst_event_unref (event); - ret = FALSE; - break; - } - - gst_object_unref (rtpsession); - - return ret; -} - - -static gboolean -gst_rtp_session_event_send_rtp_sink (GstPad * pad, GstEvent * event) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - gboolean ret = FALSE; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_DEBUG_OBJECT (rtpsession, "received event"); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED); - ret = gst_pad_push_event (rtpsession->send_rtp_src, event); - break; - case GST_EVENT_NEWSEGMENT:{ - gboolean update; - gdouble rate, arate; - GstFormat format; - gint64 start, stop, time; - GstSegment *segment; - - segment = &rtpsession->send_rtp_seg; - - /* the newsegment event is needed to convert the RTP timestamp to - * running_time, which is needed to generate a mapping from RTP to NTP - * timestamps in SR reports */ - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - GST_DEBUG_OBJECT (rtpsession, - "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " - "format GST_FORMAT_TIME, " - "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT - ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (segment->start), - GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time), - GST_TIME_ARGS (segment->accum)); - - gst_segment_set_newsegment_full (segment, update, rate, - arate, format, start, stop, time); - - /* push event forward */ - ret = gst_pad_push_event (rtpsession->send_rtp_src, event); - break; - } - case GST_EVENT_EOS:{ - GstClockTime current_time; - - /* push downstream FIXME, we are not supposed to leave the session just - * because we stop sending. */ - ret = gst_pad_push_event (rtpsession->send_rtp_src, event); - current_time = gst_clock_get_time (rtpsession->priv->sysclock); - GST_DEBUG_OBJECT (rtpsession, "scheduling BYE message"); - rtp_session_schedule_bye (rtpsession->priv->session, "End of stream", - current_time); - break; - } - default: - ret = gst_pad_push_event (rtpsession->send_rtp_src, event); - break; - } - gst_object_unref (rtpsession); - - return ret; -} - -static GstCaps * -gst_rtp_session_getcaps_send_rtp (GstPad * pad) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GstCaps *result; - GstStructure *s1, *s2; - guint ssrc; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - ssrc = rtp_session_get_internal_ssrc (priv->session); - - /* we can basically accept anything but we prefer to receive packets with our - * internal SSRC so that we don't have to patch it. Create a structure with - * the SSRC and another one without. */ - s1 = gst_structure_new ("application/x-rtp", "ssrc", G_TYPE_UINT, ssrc, NULL); - s2 = gst_structure_new ("application/x-rtp", NULL); - - result = gst_caps_new_full (s1, s2, NULL); - - GST_DEBUG_OBJECT (rtpsession, "getting caps %" GST_PTR_FORMAT, result); - - gst_object_unref (rtpsession); - - return result; -} - -static gboolean -gst_rtp_session_setcaps_send_rtp (GstPad * pad, GstCaps * caps) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GstStructure *s = gst_caps_get_structure (caps, 0); - guint ssrc; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - if (gst_structure_get_uint (s, "ssrc", &ssrc)) { - GST_DEBUG_OBJECT (rtpsession, "setting internal SSRC to %08x", ssrc); - rtp_session_set_internal_ssrc (priv->session, ssrc); - } - - gst_object_unref (rtpsession); - - return TRUE; -} - -/* Recieve an RTP packet or a list of packets to be send to the receivers, - * send to RTP session manager and forward to send_rtp_src. - */ -static GstFlowReturn -gst_rtp_session_chain_send_rtp_common (GstPad * pad, gpointer data, - gboolean is_list) -{ - GstRtpSession *rtpsession; - GstRtpSessionPrivate *priv; - GstFlowReturn ret; - GstClockTime timestamp; - GstClockTime current_time; - guint64 ntpnstime; - - rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); - priv = rtpsession->priv; - - GST_LOG_OBJECT (rtpsession, "received RTP %s", is_list ? "list" : "packet"); - - /* get NTP time when this packet was captured, this depends on the timestamp. */ - if (is_list) { - GstBuffer *buffer = NULL; - - /* All groups in an list have the same timestamp. - * So, just take it from the first group. */ - buffer = gst_buffer_list_get (GST_BUFFER_LIST_CAST (data), 0, 0); - if (buffer) - timestamp = GST_BUFFER_TIMESTAMP (buffer); - else - timestamp = -1; - } else { - timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (data)); - } - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - /* convert to running time using the segment start value. */ - ntpnstime = - gst_segment_to_running_time (&rtpsession->send_rtp_seg, GST_FORMAT_TIME, - timestamp); - /* convert to NTP time by adding the NTP base */ - ntpnstime += priv->ntpnsbase; - } else { - /* no timestamp, we could take the current running_time and convert it to - * NTP time. */ - ntpnstime = -1; - } - - current_time = gst_clock_get_time (priv->sysclock); - ret = - rtp_session_send_rtp (priv->session, data, is_list, current_time, - ntpnstime); - if (ret != GST_FLOW_OK) - goto push_error; - -done: - gst_object_unref (rtpsession); - - return ret; - - /* ERRORS */ -push_error: - { - GST_DEBUG_OBJECT (rtpsession, "process returned %s", - gst_flow_get_name (ret)); - goto done; - } -} - -static GstFlowReturn -gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer) -{ - return gst_rtp_session_chain_send_rtp_common (pad, buffer, FALSE); -} - -static GstFlowReturn -gst_rtp_session_chain_send_rtp_list (GstPad * pad, GstBufferList * list) -{ - return gst_rtp_session_chain_send_rtp_common (pad, list, TRUE); -} - -/* Create sinkpad to receive RTP packets from senders. This will also create a - * srcpad for the RTP packets. - */ -static GstPad * -create_recv_rtp_sink (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "creating RTP sink pad"); - - rtpsession->recv_rtp_sink = - gst_pad_new_from_static_template (&rtpsession_recv_rtp_sink_template, - "recv_rtp_sink"); - gst_pad_set_chain_function (rtpsession->recv_rtp_sink, - gst_rtp_session_chain_recv_rtp); - gst_pad_set_event_function (rtpsession->recv_rtp_sink, - (GstPadEventFunction) gst_rtp_session_event_recv_rtp_sink); - gst_pad_set_setcaps_function (rtpsession->recv_rtp_sink, - gst_rtp_session_sink_setcaps); - gst_pad_set_internal_link_function (rtpsession->recv_rtp_sink, - gst_rtp_session_internal_links); - gst_pad_set_active (rtpsession->recv_rtp_sink, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->recv_rtp_sink); - - GST_DEBUG_OBJECT (rtpsession, "creating RTP src pad"); - rtpsession->recv_rtp_src = - gst_pad_new_from_static_template (&rtpsession_recv_rtp_src_template, - "recv_rtp_src"); - gst_pad_set_internal_link_function (rtpsession->recv_rtp_src, - gst_rtp_session_internal_links); - gst_pad_use_fixed_caps (rtpsession->recv_rtp_src); - gst_pad_set_active (rtpsession->recv_rtp_src, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->recv_rtp_src); - - return rtpsession->recv_rtp_sink; -} - -/* Remove sinkpad to receive RTP packets from senders. This will also remove - * the srcpad for the RTP packets. - */ -static void -remove_recv_rtp_sink (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "removing RTP sink pad"); - - /* deactivate from source to sink */ - gst_pad_set_active (rtpsession->recv_rtp_src, FALSE); - gst_pad_set_active (rtpsession->recv_rtp_sink, FALSE); - - /* remove pads */ - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->recv_rtp_sink); - rtpsession->recv_rtp_sink = NULL; - - GST_DEBUG_OBJECT (rtpsession, "removing RTP src pad"); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->recv_rtp_src); - rtpsession->recv_rtp_src = NULL; -} - -/* Create a sinkpad to receive RTCP messages from senders, this will also create a - * sync_src pad for the SR packets. - */ -static GstPad * -create_recv_rtcp_sink (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "creating RTCP sink pad"); - - rtpsession->recv_rtcp_sink = - gst_pad_new_from_static_template (&rtpsession_recv_rtcp_sink_template, - "recv_rtcp_sink"); - gst_pad_set_chain_function (rtpsession->recv_rtcp_sink, - gst_rtp_session_chain_recv_rtcp); - gst_pad_set_event_function (rtpsession->recv_rtcp_sink, - (GstPadEventFunction) gst_rtp_session_event_recv_rtcp_sink); - gst_pad_set_internal_link_function (rtpsession->recv_rtcp_sink, - gst_rtp_session_internal_links); - gst_pad_set_active (rtpsession->recv_rtcp_sink, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->recv_rtcp_sink); - - GST_DEBUG_OBJECT (rtpsession, "creating sync src pad"); - rtpsession->sync_src = - gst_pad_new_from_static_template (&rtpsession_sync_src_template, - "sync_src"); - gst_pad_set_internal_link_function (rtpsession->sync_src, - gst_rtp_session_internal_links); - gst_pad_use_fixed_caps (rtpsession->sync_src); - gst_pad_set_active (rtpsession->sync_src, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->sync_src); - - return rtpsession->recv_rtcp_sink; -} - -static void -remove_recv_rtcp_sink (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "removing RTCP sink pad"); - - gst_pad_set_active (rtpsession->sync_src, FALSE); - gst_pad_set_active (rtpsession->recv_rtcp_sink, FALSE); - - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->recv_rtcp_sink); - rtpsession->recv_rtcp_sink = NULL; - - GST_DEBUG_OBJECT (rtpsession, "removing sync src pad"); - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->sync_src); - rtpsession->sync_src = NULL; -} - -/* Create a sinkpad to receive RTP packets for receivers. This will also create a - * send_rtp_src pad. - */ -static GstPad * -create_send_rtp_sink (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "creating pad"); - - rtpsession->send_rtp_sink = - gst_pad_new_from_static_template (&rtpsession_send_rtp_sink_template, - "send_rtp_sink"); - gst_pad_set_chain_function (rtpsession->send_rtp_sink, - gst_rtp_session_chain_send_rtp); - gst_pad_set_chain_list_function (rtpsession->send_rtp_sink, - gst_rtp_session_chain_send_rtp_list); - gst_pad_set_getcaps_function (rtpsession->send_rtp_sink, - gst_rtp_session_getcaps_send_rtp); - gst_pad_set_setcaps_function (rtpsession->send_rtp_sink, - gst_rtp_session_setcaps_send_rtp); - gst_pad_set_event_function (rtpsession->send_rtp_sink, - (GstPadEventFunction) gst_rtp_session_event_send_rtp_sink); - gst_pad_set_internal_link_function (rtpsession->send_rtp_sink, - gst_rtp_session_internal_links); - gst_pad_set_active (rtpsession->send_rtp_sink, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->send_rtp_sink); - - rtpsession->send_rtp_src = - gst_pad_new_from_static_template (&rtpsession_send_rtp_src_template, - "send_rtp_src"); - gst_pad_set_internal_link_function (rtpsession->send_rtp_src, - gst_rtp_session_internal_links); - gst_pad_set_active (rtpsession->send_rtp_src, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->send_rtp_src); - - return rtpsession->send_rtp_sink; -} - -static void -remove_send_rtp_sink (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "removing pad"); - - gst_pad_set_active (rtpsession->send_rtp_src, FALSE); - gst_pad_set_active (rtpsession->send_rtp_sink, FALSE); - - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->send_rtp_sink); - rtpsession->send_rtp_sink = NULL; - - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->send_rtp_src); - rtpsession->send_rtp_src = NULL; -} - -/* Create a srcpad with the RTCP packets to send out. - * This pad will be driven by the RTP session manager when it wants to send out - * RTCP packets. - */ -static GstPad * -create_send_rtcp_src (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "creating pad"); - - rtpsession->send_rtcp_src = - gst_pad_new_from_static_template (&rtpsession_send_rtcp_src_template, - "send_rtcp_src"); - gst_pad_use_fixed_caps (rtpsession->send_rtcp_src); - gst_pad_set_active (rtpsession->send_rtcp_src, TRUE); - gst_pad_set_internal_link_function (rtpsession->send_rtcp_src, - gst_rtp_session_internal_links); - gst_pad_set_query_function (rtpsession->send_rtcp_src, - gst_rtp_session_query_send_rtcp_src); - gst_pad_set_event_function (rtpsession->send_rtcp_src, - gst_rtp_session_event_send_rtcp_src); - gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->send_rtcp_src); - - return rtpsession->send_rtcp_src; -} - -static void -remove_send_rtcp_src (GstRtpSession * rtpsession) -{ - GST_DEBUG_OBJECT (rtpsession, "removing pad"); - - gst_pad_set_active (rtpsession->send_rtcp_src, FALSE); - - gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), - rtpsession->send_rtcp_src); - rtpsession->send_rtcp_src = NULL; -} - -static GstPad * -gst_rtp_session_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name) -{ - GstRtpSession *rtpsession; - GstElementClass *klass; - GstPad *result; - - g_return_val_if_fail (templ != NULL, NULL); - g_return_val_if_fail (GST_IS_RTP_SESSION (element), NULL); - - rtpsession = GST_RTP_SESSION (element); - klass = GST_ELEMENT_GET_CLASS (element); - - GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name)); - - GST_RTP_SESSION_LOCK (rtpsession); - - /* figure out the template */ - if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink")) { - if (rtpsession->recv_rtp_sink != NULL) - goto exists; - - result = create_recv_rtp_sink (rtpsession); - } else if (templ == gst_element_class_get_pad_template (klass, - "recv_rtcp_sink")) { - if (rtpsession->recv_rtcp_sink != NULL) - goto exists; - - result = create_recv_rtcp_sink (rtpsession); - } else if (templ == gst_element_class_get_pad_template (klass, - "send_rtp_sink")) { - if (rtpsession->send_rtp_sink != NULL) - goto exists; - - result = create_send_rtp_sink (rtpsession); - } else if (templ == gst_element_class_get_pad_template (klass, - "send_rtcp_src")) { - if (rtpsession->send_rtcp_src != NULL) - goto exists; - - result = create_send_rtcp_src (rtpsession); - } else - goto wrong_template; - - GST_RTP_SESSION_UNLOCK (rtpsession); - - return result; - - /* ERRORS */ -wrong_template: - { - GST_RTP_SESSION_UNLOCK (rtpsession); - g_warning ("gstrtpsession: this is not our template"); - return NULL; - } -exists: - { - GST_RTP_SESSION_UNLOCK (rtpsession); - g_warning ("gstrtpsession: pad already requested"); - return NULL; - } -} - -static void -gst_rtp_session_release_pad (GstElement * element, GstPad * pad) -{ - GstRtpSession *rtpsession; - - g_return_if_fail (GST_IS_RTP_SESSION (element)); - g_return_if_fail (GST_IS_PAD (pad)); - - rtpsession = GST_RTP_SESSION (element); - - GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - GST_RTP_SESSION_LOCK (rtpsession); - - if (rtpsession->recv_rtp_sink == pad) { - remove_recv_rtp_sink (rtpsession); - } else if (rtpsession->recv_rtcp_sink == pad) { - remove_recv_rtcp_sink (rtpsession); - } else if (rtpsession->send_rtp_sink == pad) { - remove_send_rtp_sink (rtpsession); - } else if (rtpsession->send_rtcp_src == pad) { - remove_send_rtcp_src (rtpsession); - } else - goto wrong_pad; - - GST_RTP_SESSION_UNLOCK (rtpsession); - - return; - - /* ERRORS */ -wrong_pad: - { - GST_RTP_SESSION_UNLOCK (rtpsession); - g_warning ("gstrtpsession: asked to release an unknown pad"); - return; - } -} diff --git a/gst/rtpmanager/gstrtpsession.h b/gst/rtpmanager/gstrtpsession.h deleted file mode 100644 index 9481a1c2..00000000 --- a/gst/rtpmanager/gstrtpsession.h +++ /dev/null @@ -1,81 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#ifndef __GST_RTP_SESSION_H__ -#define __GST_RTP_SESSION_H__ - -#include - -#define GST_TYPE_RTP_SESSION \ - (gst_rtp_session_get_type()) -#define GST_RTP_SESSION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SESSION,GstRtpSession)) -#define GST_RTP_SESSION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SESSION,GstRtpSessionClass)) -#define GST_IS_RTP_SESSION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SESSION)) -#define GST_IS_RTP_SESSION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SESSION)) -#define GST_RTP_SESSION_CAST(obj) ((GstRtpSession *)(obj)) - -typedef struct _GstRtpSession GstRtpSession; -typedef struct _GstRtpSessionClass GstRtpSessionClass; -typedef struct _GstRtpSessionPrivate GstRtpSessionPrivate; - -struct _GstRtpSession { - GstElement element; - - /*< private >*/ - GstPad *recv_rtp_sink; - GstSegment recv_rtp_seg; - GstPad *recv_rtcp_sink; - GstPad *send_rtp_sink; - GstSegment send_rtp_seg; - - GstPad *recv_rtp_src; - GstPad *sync_src; - GstPad *send_rtp_src; - GstPad *send_rtcp_src; - - GstRtpSessionPrivate *priv; -}; - -struct _GstRtpSessionClass { - GstElementClass parent_class; - - /* signals */ - GstCaps* (*request_pt_map) (GstRtpSession *sess, guint pt); - void (*clear_pt_map) (GstRtpSession *sess); - - void (*on_new_ssrc) (GstRtpSession *sess, guint32 ssrc); - void (*on_ssrc_collision) (GstRtpSession *sess, guint32 ssrc); - void (*on_ssrc_validated) (GstRtpSession *sess, guint32 ssrc); - void (*on_ssrc_active) (GstRtpSession *sess, guint32 ssrc); - void (*on_ssrc_sdes) (GstRtpSession *sess, guint32 ssrc); - void (*on_bye_ssrc) (GstRtpSession *sess, guint32 ssrc); - void (*on_bye_timeout) (GstRtpSession *sess, guint32 ssrc); - void (*on_timeout) (GstRtpSession *sess, guint32 ssrc); - void (*on_sender_timeout) (GstRtpSession *sess, guint32 ssrc); -}; - -GType gst_rtp_session_get_type (void); - -void gst_rtp_session_set_ssrc (GstRtpSession *sess, guint32 ssrc); - -#endif /* __GST_RTP_SESSION_H__ */ diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c deleted file mode 100644 index 6a305d8e..00000000 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ /dev/null @@ -1,722 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:element-gstrtpssrcdemux - * - * gstrtpssrcdemux 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. - * - * - * Example pipelines - * |[ - * gst-launch udpsrc caps="application/x-rtp" ! gstrtpssrcdemux ! fakesink - * ]| Takes an RTP stream and send the RTP packets with the first detected SSRC - * to fakesink, discarding the other SSRCs. - * - * - * Last reviewed on 2007-05-28 (0.10.5) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "gstrtpbin-marshal.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_%d", - 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_%d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtcp") - ); - -static GstElementDetails gst_rtp_ssrc_demux_details = { - "RTP SSRC Demux", - "Demux/Network/RTP", - "Splits RTP streams based on the SSRC", - "Wim Taymans " -}; - -#define GST_PAD_LOCK(obj) (g_mutex_lock ((obj)->padlock)) -#define GST_PAD_UNLOCK(obj) (g_mutex_unlock ((obj)->padlock)) - -/* signals */ -enum -{ - SIGNAL_NEW_SSRC_PAD, - SIGNAL_REMOVED_SSRC_PAD, - SIGNAL_CLEAR_SSRC, - LAST_SIGNAL -}; - -GST_BOILERPLATE (GstRtpSsrcDemux, gst_rtp_ssrc_demux, GstElement, - 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, GstBuffer * buf); -static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event); - -static GstFlowReturn gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, - GstBuffer * buf); -static gboolean gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad, - GstEvent * event); - -/* srcpad stuff */ -static gboolean gst_rtp_ssrc_demux_src_event (GstPad * pad, GstEvent * event); -static GList *gst_rtp_ssrc_demux_internal_links (GstPad * pad); -static gboolean gst_rtp_ssrc_demux_src_query (GstPad * pad, 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; -}; - -/* 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; -} - -/* with PAD_LOCK */ -static GstRtpSsrcDemuxPad * -create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc, - GstClockTime timestamp) -{ - GstPad *rtp_pad, *rtcp_pad; - GstElementClass *klass; - GstPadTemplate *templ; - gchar *padname; - GstRtpSsrcDemuxPad *demuxpad; - - GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc); - - klass = GST_ELEMENT_GET_CLASS (demux); - templ = gst_element_class_get_pad_template (klass, "src_%d"); - padname = g_strdup_printf ("src_%d", ssrc); - rtp_pad = gst_pad_new_from_template (templ, padname); - g_free (padname); - - templ = gst_element_class_get_pad_template (klass, "rtcp_src_%d"); - padname = g_strdup_printf ("rtcp_src_%d", ssrc); - rtcp_pad = gst_pad_new_from_template (templ, padname); - g_free (padname); - - /* we use the first timestamp received to calculate the difference between - * timestamps on all streams */ - GST_DEBUG_OBJECT (demux, "SSRC %08x, first timestamp %" GST_TIME_FORMAT, - ssrc, GST_TIME_ARGS (timestamp)); - - /* 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_DEBUG_OBJECT (demux, "first timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - - gst_pad_set_element_private (rtp_pad, demuxpad); - gst_pad_set_element_private (rtcp_pad, demuxpad); - - demux->srcpads = g_slist_prepend (demux->srcpads, demuxpad); - - /* copy caps from input */ - gst_pad_set_caps (rtp_pad, GST_PAD_CAPS (demux->rtp_sink)); - gst_pad_use_fixed_caps (rtp_pad); - gst_pad_set_caps (rtcp_pad, GST_PAD_CAPS (demux->rtcp_sink)); - gst_pad_use_fixed_caps (rtcp_pad); - - gst_pad_set_event_function (rtp_pad, gst_rtp_ssrc_demux_src_event); - gst_pad_set_query_function (rtp_pad, gst_rtp_ssrc_demux_src_query); - gst_pad_set_internal_link_function (rtp_pad, - gst_rtp_ssrc_demux_internal_links); - gst_pad_set_active (rtp_pad, TRUE); - - gst_pad_set_internal_link_function (rtcp_pad, - gst_rtp_ssrc_demux_internal_links); - gst_pad_set_active (rtcp_pad, TRUE); - - gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad); - gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad); - - g_signal_emit (G_OBJECT (demux), - gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD], 0, ssrc, rtp_pad); - - return demuxpad; -} - -static void -gst_rtp_ssrc_demux_base_init (gpointer g_class) -{ - GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (g_class); - - 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_details (gstelement_klass, &gst_rtp_ssrc_demux_details); -} - -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_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_dispose); - gobject_klass->finalize = GST_DEBUG_FUNCPTR (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, gst_rtp_bin_marshal_VOID__UINT_OBJECT, - 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, gst_rtp_bin_marshal_VOID__UINT_OBJECT, - 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, gst_rtp_bin_marshal_VOID__UINT, 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_DEBUG_CATEGORY_INIT (gst_rtp_ssrc_demux_debug, - "rtpssrcdemux", 0, "RTP SSRC demuxer"); -} - -static void -gst_rtp_ssrc_demux_init (GstRtpSsrcDemux * demux, - GstRtpSsrcDemuxClass * g_class) -{ - 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_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_rtcp_sink_event); - gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtcp_sink); - - demux->padlock = g_mutex_new (); - - gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED); -} - -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_mutex_free (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) - 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: - { - g_warning ("unknown SSRC %08x", ssrc); - return; - } -} - -static gboolean -gst_rtp_ssrc_demux_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_FLUSH_STOP: - gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED); - case GST_EVENT_NEWSEGMENT: - default: - { - GSList *walk; - - res = TRUE; - GST_PAD_LOCK (demux); - for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { - GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data; - - gst_event_ref (event); - res &= gst_pad_push_event (pad->rtp_pad, event); - } - GST_PAD_UNLOCK (demux); - gst_event_unref (event); - break; - } - } - - gst_object_unref (demux); - return res; -} - -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; - - res = TRUE; - GST_PAD_LOCK (demux); - for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { - GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data; - - gst_event_ref (event); - res &= gst_pad_push_event (pad->rtcp_pad, event); - } - GST_PAD_UNLOCK (demux); - gst_event_unref (event); - break; - } - } - gst_object_unref (demux); - return res; -} - -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 GstFlowReturn -gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf) -{ - GstFlowReturn ret; - GstRtpSsrcDemux *demux; - guint32 ssrc; - GstRtpSsrcDemuxPad *dpad; - GstRTCPPacket packet; - - demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad)); - - if (!gst_rtcp_buffer_validate (buf)) - goto invalid_rtcp; - - if (!gst_rtcp_buffer_get_first_packet (buf, &packet)) - 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_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc); - - GST_PAD_LOCK (demux); - dpad = find_demux_pad_for_ssrc (demux, ssrc); - if (dpad == NULL) { - GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc); - if (!(dpad = create_demux_pad_for_ssrc (demux, ssrc, -1))) - goto create_failed; - } - GST_PAD_UNLOCK (demux); - - /* push to srcpad */ - ret = gst_pad_push (dpad->rtcp_pad, buf); - - 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_PAD_UNLOCK (demux); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -} - -static gboolean -gst_rtp_ssrc_demux_src_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_SEEK: - default: - res = gst_pad_event_default (pad, event); - break; - } - gst_object_unref (demux); - return res; -} - -static GList * -gst_rtp_ssrc_demux_internal_links (GstPad * pad) -{ - GstRtpSsrcDemux *demux; - GList *res = NULL; - GSList *walk; - - demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad)); - - GST_PAD_LOCK (demux); - for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { - GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data; - - if (pad == demux->rtp_sink) { - res = g_list_prepend (res, dpad->rtp_pad); - } else if (pad == demux->rtcp_sink) { - res = g_list_prepend (res, dpad->rtcp_pad); - } else if (pad == dpad->rtp_pad) { - res = g_list_prepend (res, demux->rtp_sink); - break; - } else if (pad == dpad->rtcp_pad) { - res = g_list_prepend (res, demux->rtcp_sink); - break; - } - } - GST_PAD_UNLOCK (demux); - - gst_object_unref (demux); - return res; -} - -static gboolean -gst_rtp_ssrc_demux_src_query (GstPad * pad, GstQuery * query) -{ - GstRtpSsrcDemux *demux; - gboolean res = FALSE; - - demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad)); - - 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, query); - break; - } - gst_object_unref (demux); - - 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; -} diff --git a/gst/rtpmanager/gstrtpssrcdemux.h b/gst/rtpmanager/gstrtpssrcdemux.h deleted file mode 100644 index d5a13caf..00000000 --- a/gst/rtpmanager/gstrtpssrcdemux.h +++ /dev/null @@ -1,62 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#ifndef __GST_RTP_SSRC_DEMUX_H__ -#define __GST_RTP_SSRC_DEMUX_H__ - -#include - -#define GST_TYPE_RTP_SSRC_DEMUX (gst_rtp_ssrc_demux_get_type()) -#define GST_RTP_SSRC_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SSRC_DEMUX,GstRtpSsrcDemux)) -#define GST_RTP_SSRC_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SSRC_DEMUX,GstRtpSsrcDemuxClass)) -#define GST_IS_RTP_SSRC_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SSRC_DEMUX)) -#define GST_IS_RTP_SSRC_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SSRC_DEMUX)) - -typedef struct _GstRtpSsrcDemux GstRtpSsrcDemux; -typedef struct _GstRtpSsrcDemuxClass GstRtpSsrcDemuxClass; -typedef struct _GstRtpSsrcDemuxPad GstRtpSsrcDemuxPad; - -struct _GstRtpSsrcDemux -{ - GstElement parent; - - GstSegment segment; - - GstPad *rtp_sink; - GstPad *rtcp_sink; - - GMutex *padlock; - GSList *srcpads; -}; - -struct _GstRtpSsrcDemuxClass -{ - GstElementClass parent_class; - - /* signals */ - void (*new_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad); - void (*removed_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad); - - /* actions */ - void (*clear_ssrc) (GstRtpSsrcDemux *demux, guint32 ssrc); -}; - -GType gst_rtp_ssrc_demux_get_type (void); - -#endif /* __GST_RTP_SSRC_DEMUX_H__ */ diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c deleted file mode 100644 index 123d26f0..00000000 --- a/gst/rtpmanager/rtpjitterbuffer.c +++ /dev/null @@ -1,593 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ -#include -#include - -#include -#include - -#include "rtpjitterbuffer.h" - -GST_DEBUG_CATEGORY_STATIC (rtp_jitter_buffer_debug); -#define GST_CAT_DEFAULT rtp_jitter_buffer_debug - -#define MAX_WINDOW RTP_JITTER_BUFFER_MAX_WINDOW -#define MAX_TIME (2 * GST_SECOND) - -/* signals and args */ -enum -{ - LAST_SIGNAL -}; - -enum -{ - PROP_0 -}; - -/* GObject vmethods */ -static void rtp_jitter_buffer_finalize (GObject * object); - -/* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */ - -G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT); - -static void -rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - - gobject_class->finalize = rtp_jitter_buffer_finalize; - - GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0, - "RTP Jitter Buffer"); -} - -static void -rtp_jitter_buffer_init (RTPJitterBuffer * jbuf) -{ - jbuf->packets = g_queue_new (); - - rtp_jitter_buffer_reset_skew (jbuf); -} - -static void -rtp_jitter_buffer_finalize (GObject * object) -{ - RTPJitterBuffer *jbuf; - - jbuf = RTP_JITTER_BUFFER_CAST (object); - - rtp_jitter_buffer_flush (jbuf); - g_queue_free (jbuf->packets); - - G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object); -} - -/** - * rtp_jitter_buffer_new: - * - * Create an #RTPJitterBuffer. - * - * Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage. - */ -RTPJitterBuffer * -rtp_jitter_buffer_new (void) -{ - RTPJitterBuffer *jbuf; - - jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL); - - return jbuf; -} - -void -rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf) -{ - jbuf->base_time = -1; - jbuf->base_rtptime = -1; - jbuf->base_extrtp = -1; - jbuf->clock_rate = -1; - jbuf->ext_rtptime = -1; - jbuf->last_rtptime = -1; - jbuf->window_pos = 0; - jbuf->window_filling = TRUE; - jbuf->window_min = 0; - jbuf->skew = 0; - jbuf->prev_send_diff = -1; - jbuf->prev_out_time = -1; - GST_DEBUG ("reset skew correction"); -} - -/* For the clock skew we use a windowed low point averaging algorithm as can be - * found in http://www.grame.fr/pub/TR-050601.pdf. The idea is that the jitter is - * composed of: - * - * J = N + n - * - * N : a constant network delay. - * n : random added noise. The noise is concentrated around 0 - * - * In the receiver we can track the elapsed time at the sender with: - * - * send_diff(i) = (Tsi - Ts0); - * - * Tsi : The time at the sender at packet i - * Ts0 : The time at the sender at the first packet - * - * This is the difference between the RTP timestamp in the first received packet - * and the current packet. - * - * At the receiver we have to deal with the jitter introduced by the network. - * - * recv_diff(i) = (Tri - Tr0) - * - * Tri : The time at the receiver at packet i - * Tr0 : The time at the receiver at the first packet - * - * Both of these values contain a jitter Ji, a jitter for packet i, so we can - * write: - * - * recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0)) - * - * Cri : The time of the clock at the receiver for packet i - * D + ni : The jitter when receiving packet i - * - * We see that the network delay is irrelevant here as we can elliminate D: - * - * recv_diff(i) = (Cri + ni) - (Cr0 + n0)) - * - * The drift is now expressed as: - * - * Drift(i) = recv_diff(i) - send_diff(i); - * - * We now keep the W latest values of Drift and find the minimum (this is the - * one with the lowest network jitter and thus the one which is least affected - * by it). We average this lowest value to smooth out the resulting network skew. - * - * Both the window and the weighting used for averaging influence the accuracy - * of the drift estimation. Finding the correct parameters turns out to be a - * compromise between accuracy and inertia. - * - * We use a 2 second window or up to 512 data points, which is statistically big - * enough to catch spikes (FIXME, detect spikes). - * We also use a rather large weighting factor (125) to smoothly adapt. During - * startup, when filling the window, we use a parabolic weighting factor, the - * more the window is filled, the faster we move to the detected possible skew. - * - * Returns: @time adjusted with the clock skew. - */ -static GstClockTime -calculate_skew (RTPJitterBuffer * jbuf, guint32 rtptime, GstClockTime time, - guint32 clock_rate) -{ - guint64 ext_rtptime; - guint64 send_diff, recv_diff; - gint64 delta; - gint64 old; - gint pos, i; - GstClockTime gstrtptime, out_time; - - ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime); - - gstrtptime = gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, clock_rate); - - /* keep track of the last extended rtptime */ - jbuf->last_rtptime = ext_rtptime; - - if (jbuf->clock_rate != clock_rate) { - GST_WARNING ("Clock rate changed from %" G_GUINT32_FORMAT " to %" - G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate); - jbuf->base_time = -1; - jbuf->base_rtptime = -1; - jbuf->clock_rate = clock_rate; - jbuf->prev_out_time = -1; - jbuf->prev_send_diff = -1; - } - - /* first time, lock on to time and gstrtptime */ - if (G_UNLIKELY (jbuf->base_time == -1)) { - jbuf->base_time = time; - jbuf->prev_out_time = -1; - GST_DEBUG ("Taking new base time %" GST_TIME_FORMAT, GST_TIME_ARGS (time)); - } - if (G_UNLIKELY (jbuf->base_rtptime == -1)) { - jbuf->base_rtptime = gstrtptime; - jbuf->base_extrtp = ext_rtptime; - jbuf->prev_send_diff = -1; - GST_DEBUG ("Taking new base rtptime %" GST_TIME_FORMAT, - GST_TIME_ARGS (gstrtptime)); - } - - if (G_LIKELY (gstrtptime >= jbuf->base_rtptime)) - send_diff = gstrtptime - jbuf->base_rtptime; - else { - /* elapsed time at sender, timestamps can go backwards and thus be smaller - * than our base time, take a new base time in that case. */ - GST_WARNING ("backward timestamps at server, taking new base time"); - jbuf->base_time = time; - jbuf->base_rtptime = gstrtptime; - jbuf->base_extrtp = ext_rtptime; - jbuf->prev_out_time = -1; - jbuf->prev_send_diff = -1; - send_diff = 0; - } - - GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %" - GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime, - GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime), - GST_TIME_ARGS (send_diff)); - - /* we don't have an arrival timestamp so we can't do skew detection. we - * should still apply a timestamp based on RTP timestamp and base_time */ - if (time == -1 || jbuf->base_time == -1) - goto no_skew; - - /* elapsed time at receiver, includes the jitter */ - recv_diff = time - jbuf->base_time; - - GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %" - GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (jbuf->base_time), - GST_TIME_ARGS (recv_diff)); - - /* measure the diff */ - delta = ((gint64) recv_diff) - ((gint64) send_diff); - - /* if the difference between the sender timeline and the receiver timeline - * changed too quickly we have to resync because the server likely restarted - * its timestamps. */ - if (ABS (delta - jbuf->skew) > GST_SECOND) { - GST_WARNING ("delta %" GST_TIME_FORMAT " too big, reset skew", - GST_TIME_ARGS (delta - jbuf->skew)); - jbuf->base_time = time; - jbuf->base_rtptime = gstrtptime; - jbuf->base_extrtp = ext_rtptime; - jbuf->prev_out_time = -1; - jbuf->prev_send_diff = -1; - send_diff = 0; - delta = 0; - } - - pos = jbuf->window_pos; - - if (G_UNLIKELY (jbuf->window_filling)) { - /* we are filling the window */ - GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta); - jbuf->window[pos++] = delta; - /* calc the min delta we observed */ - if (G_UNLIKELY (pos == 1 || delta < jbuf->window_min)) - jbuf->window_min = delta; - - if (G_UNLIKELY (send_diff >= MAX_TIME || pos >= MAX_WINDOW)) { - jbuf->window_size = pos; - - /* window filled */ - GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min); - - /* the skew is now the min */ - jbuf->skew = jbuf->window_min; - jbuf->window_filling = FALSE; - } else { - gint perc_time, perc_window, perc; - - /* figure out how much we filled the window, this depends on the amount of - * time we have or the max number of points we keep. */ - perc_time = send_diff * 100 / MAX_TIME; - perc_window = pos * 100 / MAX_WINDOW; - perc = MAX (perc_time, perc_window); - - /* make a parabolic function, the closer we get to the MAX, the more value - * we give to the scaling factor of the new value */ - perc = perc * perc; - - /* quickly go to the min value when we are filling up, slowly when we are - * just starting because we're not sure it's a good value yet. */ - jbuf->skew = - (perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000; - jbuf->window_size = pos + 1; - } - } else { - /* pick old value and store new value. We keep the previous value in order - * to quickly check if the min of the window changed */ - old = jbuf->window[pos]; - jbuf->window[pos++] = delta; - - if (G_UNLIKELY (delta <= jbuf->window_min)) { - /* if the new value we inserted is smaller or equal to the current min, - * it becomes the new min */ - jbuf->window_min = delta; - } else if (G_UNLIKELY (old == jbuf->window_min)) { - gint64 min = G_MAXINT64; - - /* if we removed the old min, we have to find a new min */ - for (i = 0; i < jbuf->window_size; i++) { - /* we found another value equal to the old min, we can stop searching now */ - if (jbuf->window[i] == old) { - min = old; - break; - } - if (jbuf->window[i] < min) - min = jbuf->window[i]; - } - jbuf->window_min = min; - } - /* average the min values */ - jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125; - GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT, - delta, jbuf->window_min); - } - /* wrap around in the window */ - if (G_UNLIKELY (pos >= jbuf->window_size)) - pos = 0; - jbuf->window_pos = pos; - -no_skew: - /* the output time is defined as the base timestamp plus the RTP time - * adjusted for the clock skew .*/ - if (jbuf->base_time != -1) { - out_time = jbuf->base_time + send_diff + jbuf->skew; - /* check if timestamps are not going backwards, we can only check this if we - * have a previous out time and a previous send_diff */ - if (G_LIKELY (jbuf->prev_out_time != -1 && jbuf->prev_send_diff != -1)) { - /* now check for backwards timestamps */ - if (G_UNLIKELY ( - /* if the server timestamps went up and the out_time backwards */ - (send_diff > jbuf->prev_send_diff - && out_time < jbuf->prev_out_time) || - /* if the server timestamps went backwards and the out_time forwards */ - (send_diff < jbuf->prev_send_diff - && out_time > jbuf->prev_out_time) || - /* if the server timestamps did not change */ - send_diff == jbuf->prev_send_diff)) { - GST_DEBUG ("backwards timestamps, using previous time"); - out_time = jbuf->prev_out_time; - } - } - } else - out_time = -1; - - jbuf->prev_out_time = out_time; - jbuf->prev_send_diff = send_diff; - - GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT, - jbuf->skew, GST_TIME_ARGS (out_time)); - - return out_time; -} - -/** - * rtp_jitter_buffer_insert: - * @jbuf: an #RTPJitterBuffer - * @buf: a buffer - * @time: a running_time when this buffer was received in nanoseconds - * @clock_rate: the clock-rate of the payload of @buf - * @tail: TRUE when the tail element changed. - * - * Inserts @buf into the packet queue of @jbuf. The sequence number of the - * packet will be used to sort the packets. This function takes ownerhip of - * @buf when the function returns %TRUE. - * @buf should have writable metadata when calling this function. - * - * Returns: %FALSE if a packet with the same number already existed. - */ -gboolean -rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf, - GstClockTime time, guint32 clock_rate, gboolean * tail) -{ - GList *list; - guint32 rtptime; - guint16 seqnum; - - g_return_val_if_fail (jbuf != NULL, FALSE); - g_return_val_if_fail (buf != NULL, FALSE); - - seqnum = gst_rtp_buffer_get_seq (buf); - - /* loop the list to skip strictly smaller seqnum buffers */ - for (list = jbuf->packets->head; list; list = g_list_next (list)) { - guint16 qseq; - gint gap; - - qseq = gst_rtp_buffer_get_seq (GST_BUFFER_CAST (list->data)); - - /* compare the new seqnum to the one in the buffer */ - gap = gst_rtp_buffer_compare_seqnum (seqnum, qseq); - - /* we hit a packet with the same seqnum, notify a duplicate */ - if (G_UNLIKELY (gap == 0)) - goto duplicate; - - /* seqnum > qseq, we can stop looking */ - if (G_LIKELY (gap < 0)) - break; - } - - /* do skew calculation by measuring the difference between rtptime and the - * receive time, this function will retimestamp @buf with the skew corrected - * running time. */ - rtptime = gst_rtp_buffer_get_timestamp (buf); - time = calculate_skew (jbuf, rtptime, time, clock_rate); - GST_BUFFER_TIMESTAMP (buf) = time; - - /* It's more likely that the packet was inserted in the front of the buffer */ - if (G_LIKELY (list)) - g_queue_insert_before (jbuf->packets, list, buf); - else - g_queue_push_tail (jbuf->packets, buf); - - /* tail was changed when we did not find a previous packet, we set the return - * flag when requested. */ - if (G_LIKELY (tail)) - *tail = (list == NULL); - - return TRUE; - - /* ERRORS */ -duplicate: - { - GST_WARNING ("duplicate packet %d found", (gint) seqnum); - return FALSE; - } -} - -/** - * rtp_jitter_buffer_pop: - * @jbuf: an #RTPJitterBuffer - * - * Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will - * have its timestamp adjusted with the incomming running_time and the detected - * clock skew. - * - * Returns: a #GstBuffer or %NULL when there was no packet in the queue. - */ -GstBuffer * -rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf) -{ - GstBuffer *buf; - - g_return_val_if_fail (jbuf != NULL, FALSE); - - buf = g_queue_pop_tail (jbuf->packets); - - return buf; -} - -/** - * rtp_jitter_buffer_peek: - * @jbuf: an #RTPJitterBuffer - * - * Peek the oldest buffer from the packet queue of @jbuf. Register a callback - * with rtp_jitter_buffer_set_tail_changed() to be notified when an older packet - * was inserted in the queue. - * - * Returns: a #GstBuffer or %NULL when there was no packet in the queue. - */ -GstBuffer * -rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf) -{ - GstBuffer *buf; - - g_return_val_if_fail (jbuf != NULL, FALSE); - - buf = g_queue_peek_tail (jbuf->packets); - - return buf; -} - -/** - * rtp_jitter_buffer_flush: - * @jbuf: an #RTPJitterBuffer - * - * Flush all packets from the jitterbuffer. - */ -void -rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf) -{ - GstBuffer *buffer; - - g_return_if_fail (jbuf != NULL); - - while ((buffer = g_queue_pop_head (jbuf->packets))) - gst_buffer_unref (buffer); -} - -/** - * rtp_jitter_buffer_num_packets: - * @jbuf: an #RTPJitterBuffer - * - * Get the number of packets currently in "jbuf. - * - * Returns: The number of packets in @jbuf. - */ -guint -rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf) -{ - g_return_val_if_fail (jbuf != NULL, 0); - - return jbuf->packets->length; -} - -/** - * rtp_jitter_buffer_get_ts_diff: - * @jbuf: an #RTPJitterBuffer - * - * Get the difference between the timestamps of first and last packet in the - * jitterbuffer. - * - * Returns: The difference expressed in the timestamp units of the packets. - */ -guint32 -rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf) -{ - guint64 high_ts, low_ts; - GstBuffer *high_buf, *low_buf; - guint32 result; - - g_return_val_if_fail (jbuf != NULL, 0); - - high_buf = g_queue_peek_head (jbuf->packets); - low_buf = g_queue_peek_tail (jbuf->packets); - - if (!high_buf || !low_buf || high_buf == low_buf) - return 0; - - high_ts = gst_rtp_buffer_get_timestamp (high_buf); - low_ts = gst_rtp_buffer_get_timestamp (low_buf); - - /* it needs to work if ts wraps */ - if (high_ts >= low_ts) { - result = (guint32) (high_ts - low_ts); - } else { - result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts); - } - return result; -} - -/** - * rtp_jitter_buffer_get_sync: - * @jbuf: an #RTPJitterBuffer - * @rtptime: result RTP time - * @timestamp: result GStreamer timestamp - * @clock_rate: clock-rate of @rtptime - * @last_rtptime: last seen rtptime. - * - * Calculates the relation between the RTP timestamp and the GStreamer timestamp - * used for constructing timestamps. - * - * For extended RTP timestamp @rtptime with a clock-rate of @clock_rate, - * the GStreamer timestamp is currently @timestamp. - * - * The last seen extended RTP timestamp with clock-rate @clock-rate is returned in - * @last_rtptime. - */ -void -rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime, - guint64 * timestamp, guint32 * clock_rate, guint64 * last_rtptime) -{ - if (rtptime) - *rtptime = jbuf->base_extrtp; - if (timestamp) - *timestamp = jbuf->base_time + jbuf->skew; - if (clock_rate) - *clock_rate = jbuf->clock_rate; - if (last_rtptime) - *last_rtptime = jbuf->last_rtptime; -} diff --git a/gst/rtpmanager/rtpjitterbuffer.h b/gst/rtpmanager/rtpjitterbuffer.h deleted file mode 100644 index ff1a16b0..00000000 --- a/gst/rtpmanager/rtpjitterbuffer.h +++ /dev/null @@ -1,101 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#ifndef __RTP_JITTER_BUFFER_H__ -#define __RTP_JITTER_BUFFER_H__ - -#include -#include - -typedef struct _RTPJitterBuffer RTPJitterBuffer; -typedef struct _RTPJitterBufferClass RTPJitterBufferClass; - -#define RTP_TYPE_JITTER_BUFFER (rtp_jitter_buffer_get_type()) -#define RTP_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,RTPJitterBuffer)) -#define RTP_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,RTPJitterBufferClass)) -#define RTP_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_JITTER_BUFFER)) -#define RTP_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_JITTER_BUFFER)) -#define RTP_JITTER_BUFFER_CAST(src) ((RTPJitterBuffer *)(src)) - -/** - * RTPTailChanged: - * @jbuf: an #RTPJitterBuffer - * @user_data: user data specified when registering - * - * This callback will be called when the tail buffer of @jbuf changed. - */ -typedef void (*RTPTailChanged) (RTPJitterBuffer *jbuf, gpointer user_data); - -#define RTP_JITTER_BUFFER_MAX_WINDOW 512 -/** - * RTPJitterBuffer: - * - * A JitterBuffer in the #RTPSession - */ -struct _RTPJitterBuffer { - GObject object; - - GQueue *packets; - - /* for calculating skew */ - GstClockTime base_time; - GstClockTime base_rtptime; - guint32 clock_rate; - GstClockTime base_extrtp; - GstClockTime prev_out_time; - guint64 ext_rtptime; - guint64 last_rtptime; - gint64 window[RTP_JITTER_BUFFER_MAX_WINDOW]; - guint window_pos; - guint window_size; - gboolean window_filling; - gint64 window_min; - gint64 skew; - gint64 prev_send_diff; -}; - -struct _RTPJitterBufferClass { - GObjectClass parent_class; -}; - -GType rtp_jitter_buffer_get_type (void); - -/* managing lifetime */ -RTPJitterBuffer* rtp_jitter_buffer_new (void); - -void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf); - -gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf, GstBuffer *buf, - GstClockTime time, - guint32 clock_rate, - gboolean *tail); -GstBuffer * rtp_jitter_buffer_peek (RTPJitterBuffer *jbuf); -GstBuffer * rtp_jitter_buffer_pop (RTPJitterBuffer *jbuf); - -void rtp_jitter_buffer_flush (RTPJitterBuffer *jbuf); - -guint rtp_jitter_buffer_num_packets (RTPJitterBuffer *jbuf); -guint32 rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer *jbuf); - -void rtp_jitter_buffer_get_sync (RTPJitterBuffer *jbuf, guint64 *rtptime, - guint64 *timestamp, guint32 *clock_rate, - guint64 *last_rtptime); - - -#endif /* __RTP_JITTER_BUFFER_H__ */ diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c deleted file mode 100644 index 3e17ec12..00000000 --- a/gst/rtpmanager/rtpsession.c +++ /dev/null @@ -1,2521 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * 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. - */ - -#include - -#include -#include -#include - -#include "gstrtpbin-marshal.h" -#include "rtpsession.h" - -GST_DEBUG_CATEGORY_STATIC (rtp_session_debug); -#define GST_CAT_DEFAULT rtp_session_debug - -/* signals and args */ -enum -{ - SIGNAL_GET_SOURCE_BY_SSRC, - SIGNAL_ON_NEW_SSRC, - SIGNAL_ON_SSRC_COLLISION, - SIGNAL_ON_SSRC_VALIDATED, - SIGNAL_ON_SSRC_ACTIVE, - SIGNAL_ON_SSRC_SDES, - SIGNAL_ON_BYE_SSRC, - SIGNAL_ON_BYE_TIMEOUT, - SIGNAL_ON_TIMEOUT, - SIGNAL_ON_SENDER_TIMEOUT, - LAST_SIGNAL -}; - -#define DEFAULT_INTERNAL_SOURCE NULL -#define DEFAULT_BANDWIDTH RTP_STATS_BANDWIDTH -#define DEFAULT_RTCP_FRACTION RTP_STATS_RTCP_BANDWIDTH -#define DEFAULT_RTCP_MTU 1400 -#define DEFAULT_SDES NULL -#define DEFAULT_NUM_SOURCES 0 -#define DEFAULT_NUM_ACTIVE_SOURCES 0 -#define DEFAULT_SOURCES NULL - -enum -{ - PROP_0, - PROP_INTERNAL_SSRC, - PROP_INTERNAL_SOURCE, - PROP_BANDWIDTH, - PROP_RTCP_FRACTION, - PROP_RTCP_MTU, - PROP_SDES, - PROP_NUM_SOURCES, - PROP_NUM_ACTIVE_SOURCES, - PROP_SOURCES, - PROP_LAST -}; - -/* update average packet size, we keep this scaled by 16 to keep enough - * precision. */ -#define UPDATE_AVG(avg, val) \ - if ((avg) == 0) \ - (avg) = (val) << 4; \ - else \ - (avg) = ((val) + (15 * (avg))) >> 4; - -/* The number RTCP intervals after which to timeout entries in the - * collision table - */ -#define RTCP_INTERVAL_COLLISION_TIMEOUT 10 - -/* GObject vmethods */ -static void rtp_session_finalize (GObject * object); -static void rtp_session_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void rtp_session_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static guint rtp_session_signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE (RTPSession, rtp_session, G_TYPE_OBJECT); - -static RTPSource *obtain_source (RTPSession * sess, guint32 ssrc, - gboolean * created, RTPArrivalStats * arrival, gboolean rtp); -static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess, - const gchar * reason, GstClockTime current_time); -static GstClockTime calculate_rtcp_interval (RTPSession * sess, - gboolean deterministic, gboolean first); - -static void -rtp_session_class_init (RTPSessionClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - - gobject_class->finalize = rtp_session_finalize; - gobject_class->set_property = rtp_session_set_property; - gobject_class->get_property = rtp_session_get_property; - - /** - * RTPSession::get-source-by-ssrc: - * @session: the object which received the signal - * @ssrc: the SSRC of the RTPSource - * - * Request the #RTPSource object with SSRC @ssrc in @session. - */ - rtp_session_signals[SIGNAL_GET_SOURCE_BY_SSRC] = - g_signal_new ("get-source-by-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (RTPSessionClass, - get_source_by_ssrc), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT, - RTP_TYPE_SOURCE, 1, G_TYPE_UINT); - - /** - * RTPSession::on-new-ssrc: - * @session: the object which received the signal - * @src: the new RTPSource - * - * Notify of a new SSRC that entered @session. - */ - rtp_session_signals[SIGNAL_ON_NEW_SSRC] = - g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_new_ssrc), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-ssrc-collision: - * @session: the object which received the signal - * @src: the #RTPSource that caused a collision - * - * Notify when we have an SSRC collision - */ - rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] = - g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_collision), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-ssrc-validated: - * @session: the object which received the signal - * @src: the new validated RTPSource - * - * Notify of a new SSRC that became validated. - */ - rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] = - g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_validated), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-ssrc-active: - * @session: the object which received the signal - * @src: the active RTPSource - * - * Notify of a SSRC that is active, i.e., sending RTCP. - */ - rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] = - g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_active), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-ssrc-sdes: - * @session: the object which received the signal - * @src: the RTPSource - * - * Notify that a new SDES was received for SSRC. - */ - rtp_session_signals[SIGNAL_ON_SSRC_SDES] = - g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_sdes), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-bye-ssrc: - * @session: the object which received the signal - * @src: the RTPSource that went away - * - * Notify of an SSRC that became inactive because of a BYE packet. - */ - rtp_session_signals[SIGNAL_ON_BYE_SSRC] = - g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_ssrc), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-bye-timeout: - * @session: the object which received the signal - * @src: the RTPSource that timed out - * - * Notify of an SSRC that has timed out because of BYE - */ - rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] = - g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_timeout), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-timeout: - * @session: the object which received the signal - * @src: the RTPSource that timed out - * - * Notify of an SSRC that has timed out - */ - rtp_session_signals[SIGNAL_ON_TIMEOUT] = - g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_timeout), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - /** - * RTPSession::on-sender-timeout: - * @session: the object which received the signal - * @src: the RTPSource that timed out - * - * Notify of an SSRC that was a sender but timed out and became a receiver. - */ - rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] = - g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_sender_timeout), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - RTP_TYPE_SOURCE); - - g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC, - g_param_spec_uint ("internal-ssrc", "Internal SSRC", - "The internal SSRC used for the session", - 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_INTERNAL_SOURCE, - g_param_spec_object ("internal-source", "Internal Source", - "The internal source element of the session", - RTP_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_BANDWIDTH, - g_param_spec_double ("bandwidth", "Bandwidth", - "The bandwidth of the session", - 0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION, - g_param_spec_double ("rtcp-fraction", "RTCP Fraction", - "The fraction of the bandwidth used for RTCP", - 0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_RTCP_MTU, - g_param_spec_uint ("rtcp-mtu", "RTCP MTU", - "The maximum size of the RTCP packets", - 16, G_MAXINT16, DEFAULT_RTCP_MTU, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_SDES, - g_param_spec_boxed ("sdes", "SDES", - "The SDES items of this session", - GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_NUM_SOURCES, - g_param_spec_uint ("num-sources", "Num Sources", - "The number of sources in the session", 0, G_MAXUINT, - DEFAULT_NUM_SOURCES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES, - g_param_spec_uint ("num-active-sources", "Num Active Sources", - "The number of active sources in the session", 0, G_MAXUINT, - DEFAULT_NUM_ACTIVE_SOURCES, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - /** - * RTPSource::sources - * - * Get a GValue Array of all sources in the session. - * - * - * Getting the #RTPSources of a session - * <programlisting> - * { - * GValueArray *arr; - * GValue *val; - * guint i; - * - * g_object_get (sess, "sources", &arr, NULL); - * - * for (i = 0; i < arr->n_values; i++) { - * RTPSource *source; - * - * val = g_value_array_get_nth (arr, i); - * source = g_value_get_object (val); - * } - * g_value_array_free (arr); - * } - * </programlisting> - * </example> - */ - g_object_class_install_property (gobject_class, PROP_SOURCES, - g_param_spec_boxed ("sources", "Sources", - "An array of all known sources in the session", - G_TYPE_VALUE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - klass->get_source_by_ssrc = - GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc); - - GST_DEBUG_CATEGORY_INIT (rtp_session_debug, "rtpsession", 0, "RTP Session"); -} - -static void -rtp_session_init (RTPSession * sess) -{ - gint i; - gchar *str; - - sess->lock = g_mutex_new (); - sess->key = g_random_int (); - sess->mask_idx = 0; - sess->mask = 0; - - for (i = 0; i < 32; i++) { - sess->ssrcs[i] = - g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) g_object_unref); - } - sess->cnames = g_hash_table_new_full (NULL, NULL, g_free, NULL); - - rtp_stats_init_defaults (&sess->stats); - - /* create an active SSRC for this session manager */ - sess->source = rtp_session_create_source (sess); - sess->source->validated = TRUE; - sess->source->internal = TRUE; - sess->stats.active_sources++; - - /* default UDP header length */ - sess->header_len = 28; - sess->mtu = DEFAULT_RTCP_MTU; - - /* some default SDES entries */ - str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ()); - rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_CNAME, str); - g_free (str); - - rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_NAME, - g_get_real_name ()); - rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_TOOL, "GStreamer"); - - sess->first_rtcp = TRUE; - - GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc); -} - -static void -rtp_session_finalize (GObject * object) -{ - RTPSession *sess; - gint i; - - sess = RTP_SESSION_CAST (object); - - g_mutex_free (sess->lock); - for (i = 0; i < 32; i++) - g_hash_table_destroy (sess->ssrcs[i]); - - g_list_foreach (sess->conflicting_addresses, (GFunc) g_free, NULL); - g_list_free (sess->conflicting_addresses); - - g_free (sess->bye_reason); - - g_hash_table_destroy (sess->cnames); - g_object_unref (sess->source); - - G_OBJECT_CLASS (rtp_session_parent_class)->finalize (object); -} - -static void -copy_source (gpointer key, RTPSource * source, GValueArray * arr) -{ - GValue value = { 0 }; - - g_value_init (&value, RTP_TYPE_SOURCE); - g_value_take_object (&value, source); - /* copies the value */ - g_value_array_append (arr, &value); -} - -static GValueArray * -rtp_session_create_sources (RTPSession * sess) -{ - GValueArray *res; - guint size; - - RTP_SESSION_LOCK (sess); - /* get number of elements in the table */ - size = g_hash_table_size (sess->ssrcs[sess->mask_idx]); - /* create the result value array */ - res = g_value_array_new (size); - - /* and copy all values into the array */ - g_hash_table_foreach (sess->ssrcs[sess->mask_idx], (GHFunc) copy_source, res); - RTP_SESSION_UNLOCK (sess); - - return res; -} - -static void -rtp_session_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - RTPSession *sess; - - sess = RTP_SESSION (object); - - switch (prop_id) { - case PROP_INTERNAL_SSRC: - rtp_session_set_internal_ssrc (sess, g_value_get_uint (value)); - break; - case PROP_BANDWIDTH: - rtp_session_set_bandwidth (sess, g_value_get_double (value)); - break; - case PROP_RTCP_FRACTION: - rtp_session_set_rtcp_fraction (sess, g_value_get_double (value)); - break; - case PROP_RTCP_MTU: - sess->mtu = g_value_get_uint (value); - break; - case PROP_SDES: - rtp_session_set_sdes_struct (sess, g_value_get_boxed (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -rtp_session_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - RTPSession *sess; - - sess = RTP_SESSION (object); - - switch (prop_id) { - case PROP_INTERNAL_SSRC: - g_value_set_uint (value, rtp_session_get_internal_ssrc (sess)); - break; - case PROP_INTERNAL_SOURCE: - g_value_take_object (value, rtp_session_get_internal_source (sess)); - break; - case PROP_BANDWIDTH: - g_value_set_double (value, rtp_session_get_bandwidth (sess)); - break; - case PROP_RTCP_FRACTION: - g_value_set_double (value, rtp_session_get_rtcp_fraction (sess)); - break; - case PROP_RTCP_MTU: - g_value_set_uint (value, sess->mtu); - break; - case PROP_SDES: - g_value_take_boxed (value, rtp_session_get_sdes_struct (sess)); - break; - case PROP_NUM_SOURCES: - g_value_set_uint (value, rtp_session_get_num_sources (sess)); - break; - case PROP_NUM_ACTIVE_SOURCES: - g_value_set_uint (value, rtp_session_get_num_active_sources (sess)); - break; - case PROP_SOURCES: - g_value_take_boxed (value, rtp_session_create_sources (sess)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -on_new_ssrc (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0, source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_ssrc_collision (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0, - source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_ssrc_validated (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0, - source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_ssrc_active (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0, source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_ssrc_sdes (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - GST_DEBUG ("SDES changed for SSRC %08x", source->ssrc); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0, source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_bye_ssrc (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0, source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_bye_timeout (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0, source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_timeout (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_TIMEOUT], 0, source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -static void -on_sender_timeout (RTPSession * sess, RTPSource * source) -{ - g_object_ref (source); - RTP_SESSION_UNLOCK (sess); - g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0, - source); - RTP_SESSION_LOCK (sess); - g_object_unref (source); -} - -/** - * rtp_session_new: - * - * Create a new session object. - * - * Returns: a new #RTPSession. g_object_unref() after usage. - */ -RTPSession * -rtp_session_new (void) -{ - RTPSession *sess; - - sess = g_object_new (RTP_TYPE_SESSION, NULL); - - return sess; -} - -/** - * rtp_session_set_callbacks: - * @sess: an #RTPSession - * @callbacks: callbacks to configure - * @user_data: user data passed in the callbacks - * - * Configure a set of callbacks to be notified of actions. - */ -void -rtp_session_set_callbacks (RTPSession * sess, RTPSessionCallbacks * callbacks, - gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - if (callbacks->process_rtp) { - sess->callbacks.process_rtp = callbacks->process_rtp; - sess->process_rtp_user_data = user_data; - } - if (callbacks->send_rtp) { - sess->callbacks.send_rtp = callbacks->send_rtp; - sess->send_rtp_user_data = user_data; - } - if (callbacks->send_rtcp) { - sess->callbacks.send_rtcp = callbacks->send_rtcp; - sess->send_rtcp_user_data = user_data; - } - if (callbacks->sync_rtcp) { - sess->callbacks.sync_rtcp = callbacks->sync_rtcp; - sess->sync_rtcp_user_data = user_data; - } - if (callbacks->clock_rate) { - sess->callbacks.clock_rate = callbacks->clock_rate; - sess->clock_rate_user_data = user_data; - } - if (callbacks->reconsider) { - sess->callbacks.reconsider = callbacks->reconsider; - sess->reconsider_user_data = user_data; - } -} - -/** - * rtp_session_set_process_rtp_callback: - * @sess: an #RTPSession - * @callback: callback to set - * @user_data: user data passed in the callback - * - * Configure only the process_rtp callback to be notified of the process_rtp action. - */ -void -rtp_session_set_process_rtp_callback (RTPSession * sess, - RTPSessionProcessRTP callback, gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - sess->callbacks.process_rtp = callback; - sess->process_rtp_user_data = user_data; -} - -/** - * rtp_session_set_send_rtp_callback: - * @sess: an #RTPSession - * @callback: callback to set - * @user_data: user data passed in the callback - * - * Configure only the send_rtp callback to be notified of the send_rtp action. - */ -void -rtp_session_set_send_rtp_callback (RTPSession * sess, - RTPSessionSendRTP callback, gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - sess->callbacks.send_rtp = callback; - sess->send_rtp_user_data = user_data; -} - -/** - * rtp_session_set_send_rtcp_callback: - * @sess: an #RTPSession - * @callback: callback to set - * @user_data: user data passed in the callback - * - * Configure only the send_rtcp callback to be notified of the send_rtcp action. - */ -void -rtp_session_set_send_rtcp_callback (RTPSession * sess, - RTPSessionSendRTCP callback, gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - sess->callbacks.send_rtcp = callback; - sess->send_rtcp_user_data = user_data; -} - -/** - * rtp_session_set_sync_rtcp_callback: - * @sess: an #RTPSession - * @callback: callback to set - * @user_data: user data passed in the callback - * - * Configure only the sync_rtcp callback to be notified of the sync_rtcp action. - */ -void -rtp_session_set_sync_rtcp_callback (RTPSession * sess, - RTPSessionSyncRTCP callback, gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - sess->callbacks.sync_rtcp = callback; - sess->sync_rtcp_user_data = user_data; -} - -/** - * rtp_session_set_clock_rate_callback: - * @sess: an #RTPSession - * @callback: callback to set - * @user_data: user data passed in the callback - * - * Configure only the clock_rate callback to be notified of the clock_rate action. - */ -void -rtp_session_set_clock_rate_callback (RTPSession * sess, - RTPSessionClockRate callback, gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - sess->callbacks.clock_rate = callback; - sess->clock_rate_user_data = user_data; -} - -/** - * rtp_session_set_reconsider_callback: - * @sess: an #RTPSession - * @callback: callback to set - * @user_data: user data passed in the callback - * - * Configure only the reconsider callback to be notified of the reconsider action. - */ -void -rtp_session_set_reconsider_callback (RTPSession * sess, - RTPSessionReconsider callback, gpointer user_data) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - sess->callbacks.reconsider = callback; - sess->reconsider_user_data = user_data; -} - -/** - * rtp_session_set_bandwidth: - * @sess: an #RTPSession - * @bandwidth: the bandwidth allocated - * - * Set the session bandwidth in bytes per second. - */ -void -rtp_session_set_bandwidth (RTPSession * sess, gdouble bandwidth) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - RTP_SESSION_LOCK (sess); - sess->stats.bandwidth = bandwidth; - RTP_SESSION_UNLOCK (sess); -} - -/** - * rtp_session_get_bandwidth: - * @sess: an #RTPSession - * - * Get the session bandwidth. - * - * Returns: the session bandwidth. - */ -gdouble -rtp_session_get_bandwidth (RTPSession * sess) -{ - gdouble result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), 0); - - RTP_SESSION_LOCK (sess); - result = sess->stats.bandwidth; - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_set_rtcp_fraction: - * @sess: an #RTPSession - * @bandwidth: the RTCP bandwidth - * - * Set the bandwidth that should be used for RTCP - * messages. - */ -void -rtp_session_set_rtcp_fraction (RTPSession * sess, gdouble bandwidth) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - RTP_SESSION_LOCK (sess); - sess->stats.rtcp_bandwidth = bandwidth; - RTP_SESSION_UNLOCK (sess); -} - -/** - * rtp_session_get_rtcp_fraction: - * @sess: an #RTPSession - * - * Get the session bandwidth used for RTCP. - * - * Returns: The bandwidth used for RTCP messages. - */ -gdouble -rtp_session_get_rtcp_fraction (RTPSession * sess) -{ - gdouble result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), 0.0); - - RTP_SESSION_LOCK (sess); - result = sess->stats.rtcp_bandwidth; - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_set_sdes_string: - * @sess: an #RTPSession - * @type: the type of the SDES item - * @item: a null-terminated string to set. - * - * Store an SDES item of @type in @sess. - * - * Returns: %FALSE if the data was unchanged @type is invalid. - */ -gboolean -rtp_session_set_sdes_string (RTPSession * sess, GstRTCPSDESType type, - const gchar * item) -{ - gboolean result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE); - - RTP_SESSION_LOCK (sess); - result = rtp_source_set_sdes_string (sess->source, type, item); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_get_sdes_string: - * @sess: an #RTPSession - * @type: the type of the SDES item - * - * Get the SDES item of @type from @sess. - * - * Returns: a null-terminated copy of the SDES item or NULL when @type was not - * valid. g_free() after usage. - */ -gchar * -rtp_session_get_sdes_string (RTPSession * sess, GstRTCPSDESType type) -{ - gchar *result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), NULL); - - RTP_SESSION_LOCK (sess); - result = rtp_source_get_sdes_string (sess->source, type); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_get_sdes_struct: - * @sess: an #RTSPSession - * - * Get the SDES data as a #GstStructure - * - * Returns: a GstStructure with SDES items for @sess. - */ -GstStructure * -rtp_session_get_sdes_struct (RTPSession * sess) -{ - GstStructure *result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), NULL); - - RTP_SESSION_LOCK (sess); - result = rtp_source_get_sdes_struct (sess->source); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_set_sdes_struct: - * @sess: an #RTSPSession - * @sdes: a #GstStructure - * - * Set the SDES data as a #GstStructure. - */ -void -rtp_session_set_sdes_struct (RTPSession * sess, const GstStructure * sdes) -{ - g_return_if_fail (RTP_IS_SESSION (sess)); - - RTP_SESSION_LOCK (sess); - rtp_source_set_sdes_struct (sess->source, sdes); - RTP_SESSION_UNLOCK (sess); -} - -static GstFlowReturn -source_push_rtp (RTPSource * source, gpointer data, RTPSession * session) -{ - GstFlowReturn result = GST_FLOW_OK; - - if (source == session->source) { - GST_LOG ("source %08x pushed sender RTP packet", source->ssrc); - - RTP_SESSION_UNLOCK (session); - - if (session->callbacks.send_rtp) - result = - session->callbacks.send_rtp (session, source, data, - session->send_rtp_user_data); - else { - gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); - } - } else { - GST_LOG ("source %08x pushed receiver RTP packet", source->ssrc); - RTP_SESSION_UNLOCK (session); - - if (session->callbacks.process_rtp) - result = - session->callbacks.process_rtp (session, source, - GST_BUFFER_CAST (data), session->process_rtp_user_data); - else - gst_buffer_unref (GST_BUFFER_CAST (data)); - } - RTP_SESSION_LOCK (session); - - return result; -} - -static gint -source_clock_rate (RTPSource * source, guint8 pt, RTPSession * session) -{ - gint result; - - RTP_SESSION_UNLOCK (session); - - if (session->callbacks.clock_rate) - result = - session->callbacks.clock_rate (session, pt, - session->clock_rate_user_data); - else - result = -1; - - RTP_SESSION_LOCK (session); - - GST_DEBUG ("got clock-rate %d for pt %d", result, pt); - - return result; -} - -static RTPSourceCallbacks callbacks = { - (RTPSourcePushRTP) source_push_rtp, - (RTPSourceClockRate) source_clock_rate, -}; - -/** - * find_add_conflicting_addresses: - * @sess: The session to check in - * @arrival: The arrival stats for the buffer - * - * Checks if an address which has a conflict is already known, - * otherwise remembers it to prevent loops. - * - * Returns: TRUE if it was a known conflict, FALSE otherwise - */ - -static gboolean -find_add_conflicting_addresses (RTPSession * sess, RTPArrivalStats * arrival) -{ - GList *item; - RTPConflictingAddress *new_conflict; - - for (item = g_list_first (sess->conflicting_addresses); - item; item = g_list_next (item)) { - RTPConflictingAddress *known_conflict = item->data; - - if (gst_netaddress_equal (&arrival->address, &known_conflict->address)) { - known_conflict->time = arrival->time; - return TRUE; - } - } - - new_conflict = g_new0 (RTPConflictingAddress, 1); - - memcpy (&new_conflict->address, &arrival->address, sizeof (GstNetAddress)); - new_conflict->time = arrival->time; - - sess->conflicting_addresses = g_list_prepend (sess->conflicting_addresses, - new_conflict); - - return FALSE; -} - -static gboolean -check_collision (RTPSession * sess, RTPSource * source, - RTPArrivalStats * arrival, gboolean rtp) -{ - /* If we have no arrival address, we can't do collision checking */ - if (!arrival->have_address) - return FALSE; - - if (sess->source != source) { - /* This is not our local source, but lets check if two remote - * source collide - */ - if (rtp) { - if (source->have_rtp_from) { - if (gst_netaddress_equal (&source->rtp_from, &arrival->address)) - /* Address is the same */ - return FALSE; - } else { - /* We don't already have a from address for RTP, just set it */ - rtp_source_set_rtp_from (source, &arrival->address); - return FALSE; - } - } else { - if (source->have_rtcp_from) { - if (gst_netaddress_equal (&source->rtcp_from, &arrival->address)) - /* Address is the same */ - return FALSE; - } else { - /* We don't already have a from address for RTCP, just set it */ - rtp_source_set_rtcp_from (source, &arrival->address); - return FALSE; - } - } - /* We received RTP or RTCP from this source before but the network address - * changed. In this case, we have third-party collision or loop */ - GST_DEBUG ("we have a third-party collision or loop"); - - /* FIXME: Log 3rd party collision somehow - * Maybe should be done in upper layer, only the SDES can tell us - * if its a collision or a loop - */ - } else { - /* This is sending with our ssrc, is it an address we already know */ - - if (find_add_conflicting_addresses (sess, arrival)) { - /* Its a known conflict, its probably a loop, not a collision - * lets just drop the incoming packet - */ - GST_DEBUG ("Our packets are being looped back to us, dropping"); - } else { - /* Its a new collision, lets change our SSRC */ - - GST_DEBUG ("Collision for SSRC %x", rtp_source_get_ssrc (source)); - on_ssrc_collision (sess, source); - - rtp_session_schedule_bye_locked (sess, "SSRC Collision", arrival->time); - - sess->change_ssrc = TRUE; - } - } - - return TRUE; -} - - -/* must be called with the session lock, the returned source needs to be - * unreffed after usage. */ -static RTPSource * -obtain_source (RTPSession * sess, guint32 ssrc, gboolean * created, - RTPArrivalStats * arrival, gboolean rtp) -{ - RTPSource *source; - - source = - g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc)); - if (source == NULL) { - /* make new Source in probation and insert */ - source = rtp_source_new (ssrc); - - /* for RTP packets we need to set the source in probation. Receiving RTCP - * packets of an SSRC, on the other hand, is a strong indication that we - * are dealing with a valid source. */ - if (rtp) - source->probation = RTP_DEFAULT_PROBATION; - else - source->probation = 0; - - /* store from address, if any */ - if (arrival->have_address) { - if (rtp) - rtp_source_set_rtp_from (source, &arrival->address); - else - rtp_source_set_rtcp_from (source, &arrival->address); - } - - /* configure a callback on the source */ - rtp_source_set_callbacks (source, &callbacks, sess); - - g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc), - source); - - /* we have one more source now */ - sess->total_sources++; - *created = TRUE; - } else { - *created = FALSE; - /* check for collision, this updates the address when not previously set */ - if (check_collision (sess, source, arrival, rtp)) { - return NULL; - } - } - /* update last activity */ - source->last_activity = arrival->time; - if (rtp) - source->last_rtp_activity = arrival->time; - g_object_ref (source); - - return source; -} - -/** - * rtp_session_get_internal_source: - * @sess: a #RTPSession - * - * Get the internal #RTPSource of @sess. - * - * Returns: The internal #RTPSource. g_object_unref() after usage. - */ -RTPSource * -rtp_session_get_internal_source (RTPSession * sess) -{ - RTPSource *result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), NULL); - - result = g_object_ref (sess->source); - - return result; -} - -/** - * rtp_session_set_internal_ssrc: - * @sess: a #RTPSession - * @ssrc: an SSRC - * - * Set the SSRC of @sess to @ssrc. - */ -void -rtp_session_set_internal_ssrc (RTPSession * sess, guint32 ssrc) -{ - RTP_SESSION_LOCK (sess); - if (ssrc != sess->source->ssrc) { - g_hash_table_steal (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (sess->source->ssrc)); - - GST_DEBUG ("setting internal SSRC to %08x", ssrc); - /* After this call, any receiver of the old SSRC either in RTP or RTCP - * packets will timeout on the old SSRC, we could potentially schedule a - * BYE RTCP for the old SSRC... */ - sess->source->ssrc = ssrc; - rtp_source_reset (sess->source); - - /* rehash with the new SSRC */ - g_hash_table_insert (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (sess->source->ssrc), sess->source); - } - RTP_SESSION_UNLOCK (sess); - - g_object_notify (G_OBJECT (sess), "internal-ssrc"); -} - -/** - * rtp_session_get_internal_ssrc: - * @sess: a #RTPSession - * - * Get the internal SSRC of @sess. - * - * Returns: The SSRC of the session. - */ -guint32 -rtp_session_get_internal_ssrc (RTPSession * sess) -{ - guint32 ssrc; - - RTP_SESSION_LOCK (sess); - ssrc = sess->source->ssrc; - RTP_SESSION_UNLOCK (sess); - - return ssrc; -} - -/** - * rtp_session_add_source: - * @sess: a #RTPSession - * @src: #RTPSource to add - * - * Add @src to @session. - * - * Returns: %TRUE on success, %FALSE if a source with the same SSRC already - * existed in the session. - */ -gboolean -rtp_session_add_source (RTPSession * sess, RTPSource * src) -{ - gboolean result = FALSE; - RTPSource *find; - - g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE); - g_return_val_if_fail (src != NULL, FALSE); - - RTP_SESSION_LOCK (sess); - find = - g_hash_table_lookup (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (src->ssrc)); - if (find == NULL) { - g_hash_table_insert (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (src->ssrc), src); - /* we have one more source now */ - sess->total_sources++; - result = TRUE; - } - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_get_num_sources: - * @sess: an #RTPSession - * - * Get the number of sources in @sess. - * - * Returns: The number of sources in @sess. - */ -guint -rtp_session_get_num_sources (RTPSession * sess) -{ - guint result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE); - - RTP_SESSION_LOCK (sess); - result = sess->total_sources; - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_get_num_active_sources: - * @sess: an #RTPSession - * - * Get the number of active sources in @sess. A source is considered active when - * it has been validated and has not yet received a BYE RTCP message. - * - * Returns: The number of active sources in @sess. - */ -guint -rtp_session_get_num_active_sources (RTPSession * sess) -{ - guint result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), 0); - - RTP_SESSION_LOCK (sess); - result = sess->stats.active_sources; - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_get_source_by_ssrc: - * @sess: an #RTPSession - * @ssrc: an SSRC - * - * Find the source with @ssrc in @sess. - * - * Returns: a #RTPSource with SSRC @ssrc or NULL if the source was not found. - * g_object_unref() after usage. - */ -RTPSource * -rtp_session_get_source_by_ssrc (RTPSession * sess, guint32 ssrc) -{ - RTPSource *result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), NULL); - - RTP_SESSION_LOCK (sess); - result = - g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc)); - if (result) - g_object_ref (result); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_get_source_by_cname: - * @sess: a #RTPSession - * @cname: an CNAME - * - * Find the source with @cname in @sess. - * - * Returns: a #RTPSource with CNAME @cname or NULL if the source was not found. - * g_object_unref() after usage. - */ -RTPSource * -rtp_session_get_source_by_cname (RTPSession * sess, const gchar * cname) -{ - RTPSource *result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), NULL); - g_return_val_if_fail (cname != NULL, NULL); - - RTP_SESSION_LOCK (sess); - result = g_hash_table_lookup (sess->cnames, cname); - if (result) - g_object_ref (result); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -static guint32 -rtp_session_create_new_ssrc (RTPSession * sess) -{ - guint32 ssrc; - - while (TRUE) { - ssrc = g_random_int (); - - /* see if it exists in the session, we're done if it doesn't */ - if (g_hash_table_lookup (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (ssrc)) == NULL) - break; - } - return ssrc; -} - - -/** - * rtp_session_create_source: - * @sess: an #RTPSession - * - * Create an #RTPSource for use in @sess. This function will create a source - * with an ssrc that is currently not used by any participants in the session. - * - * Returns: an #RTPSource. - */ -RTPSource * -rtp_session_create_source (RTPSession * sess) -{ - guint32 ssrc; - RTPSource *source; - - RTP_SESSION_LOCK (sess); - ssrc = rtp_session_create_new_ssrc (sess); - source = rtp_source_new (ssrc); - rtp_source_set_callbacks (source, &callbacks, sess); - /* we need an additional ref for the source in the hashtable */ - g_object_ref (source); - g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc), - source); - /* we have one more source now */ - sess->total_sources++; - RTP_SESSION_UNLOCK (sess); - - return source; -} - -/* update the RTPArrivalStats structure with the current time and other bits - * about the current buffer we are handling. - * This function is typically called when a validated packet is received. - * This function should be called with the SESSION_LOCK - */ -static void -update_arrival_stats (RTPSession * sess, RTPArrivalStats * arrival, - gboolean rtp, GstBuffer * buffer, GstClockTime current_time, - GstClockTime running_time, guint64 ntpnstime) -{ - /* get time of arrival */ - arrival->time = current_time; - arrival->running_time = running_time; - arrival->ntpnstime = ntpnstime; - - /* get packet size including header overhead */ - arrival->bytes = GST_BUFFER_SIZE (buffer) + sess->header_len; - - if (rtp) { - arrival->payload_len = gst_rtp_buffer_get_payload_len (buffer); - } else { - arrival->payload_len = 0; - } - - /* for netbuffer we can store the IP address to check for collisions */ - arrival->have_address = GST_IS_NETBUFFER (buffer); - if (arrival->have_address) { - GstNetBuffer *netbuf = (GstNetBuffer *) buffer; - - memcpy (&arrival->address, &netbuf->from, sizeof (GstNetAddress)); - } -} - -/** - * rtp_session_process_rtp: - * @sess: and #RTPSession - * @buffer: an RTP buffer - * @current_time: the current system time - * @ntpnstime: the NTP arrival time in nanoseconds - * - * Process an RTP buffer in the session manager. This function takes ownership - * of @buffer. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_session_process_rtp (RTPSession * sess, GstBuffer * buffer, - GstClockTime current_time, GstClockTime running_time, guint64 ntpnstime) -{ - GstFlowReturn result; - guint32 ssrc; - RTPSource *source; - gboolean created; - gboolean prevsender, prevactive; - RTPArrivalStats arrival; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); - - if (!gst_rtp_buffer_validate (buffer)) - goto invalid_packet; - - RTP_SESSION_LOCK (sess); - /* update arrival stats */ - update_arrival_stats (sess, &arrival, TRUE, buffer, current_time, - running_time, ntpnstime); - - /* ignore more RTP packets when we left the session */ - if (sess->source->received_bye) - goto ignore; - - /* get SSRC and look up in session database */ - ssrc = gst_rtp_buffer_get_ssrc (buffer); - source = obtain_source (sess, ssrc, &created, &arrival, TRUE); - if (!source) - goto collision; - - prevsender = RTP_SOURCE_IS_SENDER (source); - prevactive = RTP_SOURCE_IS_ACTIVE (source); - - /* we need to ref so that we can process the CSRCs later */ - gst_buffer_ref (buffer); - - /* let source process the packet */ - result = rtp_source_process_rtp (source, buffer, &arrival); - - /* source became active */ - if (prevactive != RTP_SOURCE_IS_ACTIVE (source)) { - sess->stats.active_sources++; - GST_DEBUG ("source: %08x became active, %d active sources", ssrc, - sess->stats.active_sources); - on_ssrc_validated (sess, source); - } - if (prevsender != RTP_SOURCE_IS_SENDER (source)) { - sess->stats.sender_sources++; - GST_DEBUG ("source: %08x became sender, %d sender sources", ssrc, - sess->stats.sender_sources); - } - - if (created) - on_new_ssrc (sess, source); - - if (source->validated) { - guint8 i, count; - gboolean created; - - /* for validated sources, we add the CSRCs as well */ - count = gst_rtp_buffer_get_csrc_count (buffer); - - for (i = 0; i < count; i++) { - guint32 csrc; - RTPSource *csrc_src; - - csrc = gst_rtp_buffer_get_csrc (buffer, i); - - /* get source */ - csrc_src = obtain_source (sess, csrc, &created, &arrival, TRUE); - if (!csrc_src) - continue; - - if (created) { - GST_DEBUG ("created new CSRC: %08x", csrc); - rtp_source_set_as_csrc (csrc_src); - if (RTP_SOURCE_IS_ACTIVE (csrc_src)) - sess->stats.active_sources++; - on_new_ssrc (sess, csrc_src); - } - g_object_unref (csrc_src); - } - } - g_object_unref (source); - gst_buffer_unref (buffer); - - RTP_SESSION_UNLOCK (sess); - - return result; - - /* ERRORS */ -invalid_packet: - { - gst_buffer_unref (buffer); - GST_DEBUG ("invalid RTP packet received"); - return GST_FLOW_OK; - } -ignore: - { - gst_buffer_unref (buffer); - RTP_SESSION_UNLOCK (sess); - GST_DEBUG ("ignoring RTP packet because we are leaving"); - return GST_FLOW_OK; - } -collision: - { - gst_buffer_unref (buffer); - RTP_SESSION_UNLOCK (sess); - GST_DEBUG ("ignoring packet because its collisioning"); - return GST_FLOW_OK; - } -} - -static void -rtp_session_process_rb (RTPSession * sess, RTPSource * source, - GstRTCPPacket * packet, RTPArrivalStats * arrival) -{ - guint count, i; - - count = gst_rtcp_packet_get_rb_count (packet); - for (i = 0; i < count; i++) { - guint32 ssrc, exthighestseq, jitter, lsr, dlsr; - guint8 fractionlost; - gint32 packetslost; - - gst_rtcp_packet_get_rb (packet, i, &ssrc, &fractionlost, - &packetslost, &exthighestseq, &jitter, &lsr, &dlsr); - - GST_DEBUG ("RB %d: SSRC %08x, jitter %" G_GUINT32_FORMAT, i, ssrc, jitter); - - if (ssrc == sess->source->ssrc) { - /* only deal with report blocks for our session, we update the stats of - * the sender of the RTCP message. We could also compare our stats against - * the other sender to see if we are better or worse. */ - rtp_source_process_rb (source, arrival->time, fractionlost, packetslost, - exthighestseq, jitter, lsr, dlsr); - - on_ssrc_active (sess, source); - } - } -} - -/* A Sender report contains statistics about how the sender is doing. This - * includes timing informataion such as the relation between RTP and NTP - * timestamps and the number of packets/bytes it sent to us. - * - * In this report is also included a set of report blocks related to how this - * sender is receiving data (in case we (or somebody else) is also sending stuff - * to it). This info includes the packet loss, jitter and seqnum. It also - * contains information to calculate the round trip time (LSR/DLSR). - */ -static void -rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival, gboolean * do_sync) -{ - guint32 senderssrc, rtptime, packet_count, octet_count; - guint64 ntptime; - RTPSource *source; - gboolean created, prevsender; - - gst_rtcp_packet_sr_get_sender_info (packet, &senderssrc, &ntptime, &rtptime, - &packet_count, &octet_count); - - GST_DEBUG ("got SR packet: SSRC %08x, time %" GST_TIME_FORMAT, - senderssrc, GST_TIME_ARGS (arrival->time)); - - source = obtain_source (sess, senderssrc, &created, arrival, FALSE); - if (!source) - return; - - /* don't try to do lip-sync for sources that sent a BYE */ - if (rtp_source_received_bye (source)) - *do_sync = FALSE; - else - *do_sync = TRUE; - - prevsender = RTP_SOURCE_IS_SENDER (source); - - /* first update the source */ - rtp_source_process_sr (source, arrival->time, ntptime, rtptime, packet_count, - octet_count); - - if (prevsender != RTP_SOURCE_IS_SENDER (source)) { - sess->stats.sender_sources++; - GST_DEBUG ("source: %08x became sender, %d sender sources", senderssrc, - sess->stats.sender_sources); - } - - if (created) - on_new_ssrc (sess, source); - - rtp_session_process_rb (sess, source, packet, arrival); - g_object_unref (source); -} - -/* A receiver report contains statistics about how a receiver is doing. It - * includes stuff like packet loss, jitter and the seqnum it received last. It - * also contains info to calculate the round trip time. - * - * We are only interested in how the sender of this report is doing wrt to us. - */ -static void -rtp_session_process_rr (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival) -{ - guint32 senderssrc; - RTPSource *source; - gboolean created; - - senderssrc = gst_rtcp_packet_rr_get_ssrc (packet); - - GST_DEBUG ("got RR packet: SSRC %08x", senderssrc); - - source = obtain_source (sess, senderssrc, &created, arrival, FALSE); - if (!source) - return; - - if (created) - on_new_ssrc (sess, source); - - rtp_session_process_rb (sess, source, packet, arrival); - g_object_unref (source); -} - -/* Get SDES items and store them in the SSRC */ -static void -rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival) -{ - guint items, i, j; - gboolean more_items, more_entries; - - items = gst_rtcp_packet_sdes_get_item_count (packet); - GST_DEBUG ("got SDES packet with %d items", items); - - more_items = gst_rtcp_packet_sdes_first_item (packet); - i = 0; - while (more_items) { - guint32 ssrc; - gboolean changed, created; - RTPSource *source; - - ssrc = gst_rtcp_packet_sdes_get_ssrc (packet); - - GST_DEBUG ("item %d, SSRC %08x", i, ssrc); - - changed = FALSE; - - /* find src, no probation when dealing with RTCP */ - source = obtain_source (sess, ssrc, &created, arrival, FALSE); - if (!source) - return; - - more_entries = gst_rtcp_packet_sdes_first_entry (packet); - j = 0; - while (more_entries) { - GstRTCPSDESType type; - guint8 len; - guint8 *data; - - gst_rtcp_packet_sdes_get_entry (packet, &type, &len, &data); - - GST_DEBUG ("entry %d, type %d, len %d, data %.*s", j, type, len, len, - data); - - changed |= rtp_source_set_sdes (source, type, data, len); - - more_entries = gst_rtcp_packet_sdes_next_entry (packet); - j++; - } - - source->validated = TRUE; - - if (created) - on_new_ssrc (sess, source); - if (changed) - on_ssrc_sdes (sess, source); - - g_object_unref (source); - - more_items = gst_rtcp_packet_sdes_next_item (packet); - i++; - } -} - -/* BYE is sent when a client leaves the session - */ -static void -rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival) -{ - guint count, i; - gchar *reason; - gboolean reconsider = FALSE; - - reason = gst_rtcp_packet_bye_get_reason (packet); - GST_DEBUG ("got BYE packet (reason: %s)", GST_STR_NULL (reason)); - - count = gst_rtcp_packet_bye_get_ssrc_count (packet); - for (i = 0; i < count; i++) { - guint32 ssrc; - RTPSource *source; - gboolean created, prevactive, prevsender; - guint pmembers, members; - - ssrc = gst_rtcp_packet_bye_get_nth_ssrc (packet, i); - GST_DEBUG ("SSRC: %08x", ssrc); - - /* find src and mark bye, no probation when dealing with RTCP */ - source = obtain_source (sess, ssrc, &created, arrival, FALSE); - if (!source) - return; - - /* store time for when we need to time out this source */ - source->bye_time = arrival->time; - - prevactive = RTP_SOURCE_IS_ACTIVE (source); - prevsender = RTP_SOURCE_IS_SENDER (source); - - /* let the source handle the rest */ - rtp_source_process_bye (source, reason); - - pmembers = sess->stats.active_sources; - - if (prevactive && !RTP_SOURCE_IS_ACTIVE (source)) { - sess->stats.active_sources--; - GST_DEBUG ("source: %08x became inactive, %d active sources", ssrc, - sess->stats.active_sources); - } - if (prevsender && !RTP_SOURCE_IS_SENDER (source)) { - sess->stats.sender_sources--; - GST_DEBUG ("source: %08x became non sender, %d sender sources", ssrc, - sess->stats.sender_sources); - } - members = sess->stats.active_sources; - - if (!sess->source->received_bye && members < pmembers) { - /* some members went away since the previous timeout estimate. - * Perform reverse reconsideration but only when we are not scheduling a - * BYE ourselves. */ - if (arrival->time < sess->next_rtcp_check_time) { - GstClockTime time_remaining; - - time_remaining = sess->next_rtcp_check_time - arrival->time; - sess->next_rtcp_check_time = - gst_util_uint64_scale (time_remaining, members, pmembers); - - GST_DEBUG ("reverse reconsideration %" GST_TIME_FORMAT, - GST_TIME_ARGS (sess->next_rtcp_check_time)); - - sess->next_rtcp_check_time += arrival->time; - - /* mark pending reconsider. We only want to signal the reconsideration - * once after we handled all the source in the bye packet */ - reconsider = TRUE; - } - } - - if (created) - on_new_ssrc (sess, source); - - on_bye_ssrc (sess, source); - - g_object_unref (source); - } - if (reconsider) { - RTP_SESSION_UNLOCK (sess); - /* notify app of reconsideration */ - if (sess->callbacks.reconsider) - sess->callbacks.reconsider (sess, sess->reconsider_user_data); - RTP_SESSION_LOCK (sess); - } - g_free (reason); -} - -static void -rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival) -{ - GST_DEBUG ("received APP"); -} - -/** - * rtp_session_process_rtcp: - * @sess: and #RTPSession - * @buffer: an RTCP buffer - * @current_time: the current system time - * - * Process an RTCP buffer in the session manager. This function takes ownership - * of @buffer. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, - GstClockTime current_time) -{ - GstRTCPPacket packet; - gboolean more, is_bye = FALSE, do_sync = FALSE; - RTPArrivalStats arrival; - GstFlowReturn result = GST_FLOW_OK; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); - - if (!gst_rtcp_buffer_validate (buffer)) - goto invalid_packet; - - GST_DEBUG ("received RTCP packet"); - - RTP_SESSION_LOCK (sess); - /* update arrival stats */ - update_arrival_stats (sess, &arrival, FALSE, buffer, current_time, -1, -1); - - if (sess->sent_bye) - goto ignore; - - /* make writable, we might want to change the buffer */ - buffer = gst_buffer_make_metadata_writable (buffer); - - /* start processing the compound packet */ - more = gst_rtcp_buffer_get_first_packet (buffer, &packet); - while (more) { - GstRTCPType type; - - type = gst_rtcp_packet_get_type (&packet); - - /* when we are leaving the session, we should ignore all non-BYE messages */ - if (sess->source->received_bye && type != GST_RTCP_TYPE_BYE) { - GST_DEBUG ("ignoring non-BYE RTCP packet because we are leaving"); - goto next; - } - - switch (type) { - case GST_RTCP_TYPE_SR: - rtp_session_process_sr (sess, &packet, &arrival, &do_sync); - break; - case GST_RTCP_TYPE_RR: - rtp_session_process_rr (sess, &packet, &arrival); - break; - case GST_RTCP_TYPE_SDES: - rtp_session_process_sdes (sess, &packet, &arrival); - break; - case GST_RTCP_TYPE_BYE: - is_bye = TRUE; - /* don't try to attempt lip-sync anymore for streams with a BYE */ - do_sync = FALSE; - rtp_session_process_bye (sess, &packet, &arrival); - break; - case GST_RTCP_TYPE_APP: - rtp_session_process_app (sess, &packet, &arrival); - break; - default: - GST_WARNING ("got unknown RTCP packet"); - break; - } - next: - more = gst_rtcp_packet_move_to_next (&packet); - } - - /* if we are scheduling a BYE, we only want to count bye packets, else we - * count everything */ - if (sess->source->received_bye) { - if (is_bye) { - sess->stats.bye_members++; - UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes); - } - } else { - /* keep track of average packet size */ - UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes); - } - RTP_SESSION_UNLOCK (sess); - - /* notify caller of sr packets in the callback */ - if (do_sync && sess->callbacks.sync_rtcp) - result = sess->callbacks.sync_rtcp (sess, sess->source, buffer, - sess->sync_rtcp_user_data); - else - gst_buffer_unref (buffer); - - return result; - - /* ERRORS */ -invalid_packet: - { - GST_DEBUG ("invalid RTCP packet received"); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } -ignore: - { - gst_buffer_unref (buffer); - RTP_SESSION_UNLOCK (sess); - GST_DEBUG ("ignoring RTP packet because we left"); - return GST_FLOW_OK; - } -} - -/** - * rtp_session_send_rtp: - * @sess: an #RTPSession - * @data: pointer to either an RTP buffer or a list of RTP buffers - * @current_time: the current system time - * @ntpnstime: the NTP time in nanoseconds of when this buffer was captured. - * This is the buffer timestamp converted to NTP time. - * - * Send the RTP buffer in the session manager. This function takes ownership of - * @buffer. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list, - GstClockTime current_time, guint64 ntpnstime) -{ - GstFlowReturn result; - RTPSource *source; - gboolean prevsender; - gboolean valid_packet; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR); - - if (is_list) { - valid_packet = gst_rtp_buffer_list_validate (GST_BUFFER_LIST_CAST (data)); - } else { - valid_packet = gst_rtp_buffer_validate (GST_BUFFER_CAST (data)); - } - - if (!valid_packet) - goto invalid_packet; - - GST_LOG ("received RTP %s for sending", is_list ? "list" : "packet"); - - RTP_SESSION_LOCK (sess); - source = sess->source; - - /* update last activity */ - source->last_rtp_activity = current_time; - - prevsender = RTP_SOURCE_IS_SENDER (source); - - /* we use our own source to send */ - result = rtp_source_send_rtp (source, data, is_list, ntpnstime); - - if (RTP_SOURCE_IS_SENDER (source) && !prevsender) - sess->stats.sender_sources++; - RTP_SESSION_UNLOCK (sess); - - return result; - - /* ERRORS */ -invalid_packet: - { - gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); - GST_DEBUG ("invalid RTP packet received"); - return GST_FLOW_OK; - } -} - -static GstClockTime -calculate_rtcp_interval (RTPSession * sess, gboolean deterministic, - gboolean first) -{ - GstClockTime result; - - if (sess->source->received_bye) { - result = rtp_stats_calculate_bye_interval (&sess->stats); - } else { - result = rtp_stats_calculate_rtcp_interval (&sess->stats, - RTP_SOURCE_IS_SENDER (sess->source), first); - } - - GST_DEBUG ("next deterministic interval: %" GST_TIME_FORMAT ", first %d", - GST_TIME_ARGS (result), first); - - if (!deterministic) - result = rtp_stats_add_rtcp_jitter (&sess->stats, result); - - GST_DEBUG ("next interval: %" GST_TIME_FORMAT, GST_TIME_ARGS (result)); - - return result; -} - -/* Stop the current @sess and schedule a BYE message for the other members. - * One must have the session lock to call this function - */ -static GstFlowReturn -rtp_session_schedule_bye_locked (RTPSession * sess, const gchar * reason, - GstClockTime current_time) -{ - GstFlowReturn result = GST_FLOW_OK; - RTPSource *source; - GstClockTime interval; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - - source = sess->source; - - /* ignore more BYEs */ - if (source->received_bye) - goto done; - - /* we have BYE now */ - source->received_bye = TRUE; - /* at least one member wants to send a BYE */ - g_free (sess->bye_reason); - sess->bye_reason = g_strdup (reason); - sess->stats.avg_rtcp_packet_size = 100; - sess->stats.bye_members = 1; - sess->first_rtcp = TRUE; - sess->sent_bye = FALSE; - - /* reschedule transmission */ - sess->last_rtcp_send_time = current_time; - interval = calculate_rtcp_interval (sess, FALSE, TRUE); - sess->next_rtcp_check_time = current_time + interval; - - GST_DEBUG ("Schedule BYE for %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT, - GST_TIME_ARGS (interval), GST_TIME_ARGS (sess->next_rtcp_check_time)); - - RTP_SESSION_UNLOCK (sess); - /* notify app of reconsideration */ - if (sess->callbacks.reconsider) - sess->callbacks.reconsider (sess, sess->reconsider_user_data); - RTP_SESSION_LOCK (sess); -done: - - return result; -} - -/** - * rtp_session_schedule_bye: - * @sess: an #RTPSession - * @reason: a reason or NULL - * @current_time: the current system time - * - * Stop the current @sess and schedule a BYE message for the other members. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_session_schedule_bye (RTPSession * sess, const gchar * reason, - GstClockTime current_time) -{ - GstFlowReturn result = GST_FLOW_OK; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - - RTP_SESSION_LOCK (sess); - result = rtp_session_schedule_bye_locked (sess, reason, current_time); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -/** - * rtp_session_next_timeout: - * @sess: an #RTPSession - * @current_time: the current system time - * - * Get the next time we should perform session maintenance tasks. - * - * Returns: a time when rtp_session_on_timeout() should be called with the - * current system time. - */ -GstClockTime -rtp_session_next_timeout (RTPSession * sess, GstClockTime current_time) -{ - GstClockTime result; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - - RTP_SESSION_LOCK (sess); - - result = sess->next_rtcp_check_time; - - GST_DEBUG ("current time: %" GST_TIME_FORMAT ", next :%" GST_TIME_FORMAT, - GST_TIME_ARGS (current_time), GST_TIME_ARGS (result)); - - if (result < current_time) { - GST_DEBUG ("take current time as base"); - /* our previous check time expired, start counting from the current time - * again. */ - result = current_time; - } - - if (sess->source->received_bye) { - if (sess->sent_bye) { - GST_DEBUG ("we sent BYE already"); - result = GST_CLOCK_TIME_NONE; - } else if (sess->stats.active_sources >= 50) { - GST_DEBUG ("reconsider BYE, more than 50 sources"); - /* reconsider BYE if members >= 50 */ - result += calculate_rtcp_interval (sess, FALSE, TRUE); - } - } else { - if (sess->first_rtcp) { - GST_DEBUG ("first RTCP packet"); - /* we are called for the first time */ - result += calculate_rtcp_interval (sess, FALSE, TRUE); - } else if (sess->next_rtcp_check_time < current_time) { - GST_DEBUG ("old check time expired, getting new timeout"); - /* get a new timeout when we need to */ - result += calculate_rtcp_interval (sess, FALSE, FALSE); - } - } - sess->next_rtcp_check_time = result; - - GST_DEBUG ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (result)); - RTP_SESSION_UNLOCK (sess); - - return result; -} - -typedef struct -{ - RTPSession *sess; - GstBuffer *rtcp; - GstClockTime current_time; - guint64 ntpnstime; - GstClockTime interval; - GstRTCPPacket packet; - gboolean is_bye; - gboolean has_sdes; -} ReportData; - -static void -session_start_rtcp (RTPSession * sess, ReportData * data) -{ - GstRTCPPacket *packet = &data->packet; - RTPSource *own = sess->source; - - data->rtcp = gst_rtcp_buffer_new (sess->mtu); - - if (RTP_SOURCE_IS_SENDER (own)) { - guint64 ntptime; - guint32 rtptime; - guint32 packet_count, octet_count; - - /* we are a sender, create SR */ - GST_DEBUG ("create SR for SSRC %08x", own->ssrc); - gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SR, packet); - - /* get latest stats */ - rtp_source_get_new_sr (own, data->ntpnstime, &ntptime, &rtptime, - &packet_count, &octet_count); - /* store stats */ - rtp_source_process_sr (own, data->current_time, ntptime, rtptime, - packet_count, octet_count); - - /* fill in sender report info */ - gst_rtcp_packet_sr_set_sender_info (packet, own->ssrc, - ntptime, rtptime, packet_count, octet_count); - } else { - /* we are only receiver, create RR */ - GST_DEBUG ("create RR for SSRC %08x", own->ssrc); - gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_RR, packet); - gst_rtcp_packet_rr_set_ssrc (packet, own->ssrc); - } -} - -/* construct a Sender or Receiver Report */ -static void -session_report_blocks (const gchar * key, RTPSource * source, ReportData * data) -{ - RTPSession *sess = data->sess; - GstRTCPPacket *packet = &data->packet; - - /* create a new buffer if needed */ - if (data->rtcp == NULL) { - session_start_rtcp (sess, data); - } - if (gst_rtcp_packet_get_rb_count (packet) < GST_RTCP_MAX_RB_COUNT) { - /* only report about other sender sources */ - if (source != sess->source && RTP_SOURCE_IS_SENDER (source)) { - guint8 fractionlost; - gint32 packetslost; - guint32 exthighestseq, jitter; - guint32 lsr, dlsr; - - /* get new stats */ - rtp_source_get_new_rb (source, data->current_time, &fractionlost, - &packetslost, &exthighestseq, &jitter, &lsr, &dlsr); - - /* packet is not yet filled, add report block for this source. */ - gst_rtcp_packet_add_rb (packet, source->ssrc, fractionlost, packetslost, - exthighestseq, jitter, lsr, dlsr); - } - } -} - -/* perform cleanup of sources that timed out */ -static gboolean -session_cleanup (const gchar * key, RTPSource * source, ReportData * data) -{ - gboolean remove = FALSE; - gboolean byetimeout = FALSE; - gboolean sendertimeout = FALSE; - gboolean is_sender, is_active; - RTPSession *sess = data->sess; - GstClockTime interval; - - is_sender = RTP_SOURCE_IS_SENDER (source); - is_active = RTP_SOURCE_IS_ACTIVE (source); - - /* check for our own source, we don't want to delete our own source. */ - if (!(source == sess->source)) { - if (source->received_bye) { - /* if we received a BYE from the source, remove the source after some - * time. */ - if (data->current_time > source->bye_time && - data->current_time - source->bye_time > sess->stats.bye_timeout) { - GST_DEBUG ("removing BYE source %08x", source->ssrc); - remove = TRUE; - byetimeout = TRUE; - } - } - /* sources that were inactive for more than 5 times the deterministic reporting - * interval get timed out. the min timeout is 5 seconds. */ - if (data->current_time > source->last_activity) { - interval = MAX (data->interval * 5, 5 * GST_SECOND); - if (data->current_time - source->last_activity > interval) { - GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT, - source->ssrc, GST_TIME_ARGS (source->last_activity)); - remove = TRUE; - } - } - } - - /* senders that did not send for a long time become a receiver, this also - * holds for our own source. */ - if (is_sender) { - if (data->current_time > source->last_rtp_activity) { - interval = MAX (data->interval * 2, 5 * GST_SECOND); - if (data->current_time - source->last_rtp_activity > interval) { - GST_DEBUG ("sender source %08x timed out and became receiver, last %" - GST_TIME_FORMAT, source->ssrc, - GST_TIME_ARGS (source->last_rtp_activity)); - source->is_sender = FALSE; - sess->stats.sender_sources--; - sendertimeout = TRUE; - } - } - } - - if (remove) { - sess->total_sources--; - if (is_sender) - sess->stats.sender_sources--; - if (is_active) - sess->stats.active_sources--; - - if (byetimeout) - on_bye_timeout (sess, source); - else - on_timeout (sess, source); - } else { - if (sendertimeout) - on_sender_timeout (sess, source); - } - return remove; -} - -static void -session_sdes (RTPSession * sess, ReportData * data) -{ - GstRTCPPacket *packet = &data->packet; - guint8 *sdes_data; - guint sdes_len; - - /* add SDES packet */ - gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SDES, packet); - - gst_rtcp_packet_sdes_add_item (packet, sess->source->ssrc); - - rtp_source_get_sdes (sess->source, GST_RTCP_SDES_CNAME, &sdes_data, - &sdes_len); - gst_rtcp_packet_sdes_add_entry (packet, GST_RTCP_SDES_CNAME, sdes_len, - sdes_data); - - /* other SDES items must only be added at regular intervals and only when the - * user requests to since it might be a privacy problem */ -#if 0 - gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_NAME, - strlen (sess->name), (guint8 *) sess->name); - gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_TOOL, - strlen (sess->tool), (guint8 *) sess->tool); -#endif - - data->has_sdes = TRUE; -} - -/* schedule a BYE packet */ -static void -session_bye (RTPSession * sess, ReportData * data) -{ - GstRTCPPacket *packet = &data->packet; - - /* open packet */ - session_start_rtcp (sess, data); - - /* add SDES */ - session_sdes (sess, data); - - /* add a BYE packet */ - gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_BYE, packet); - gst_rtcp_packet_bye_add_ssrc (packet, sess->source->ssrc); - if (sess->bye_reason) - gst_rtcp_packet_bye_set_reason (packet, sess->bye_reason); - - /* we have a BYE packet now */ - data->is_bye = TRUE; -} - -static gboolean -is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data) -{ - GstClockTime new_send_time, elapsed; - gboolean result; - - /* no need to check yet */ - if (sess->next_rtcp_check_time > current_time) { - GST_DEBUG ("no check time yet, next %" GST_TIME_FORMAT " > now %" - GST_TIME_FORMAT, GST_TIME_ARGS (sess->next_rtcp_check_time), - GST_TIME_ARGS (current_time)); - return FALSE; - } - - /* get elapsed time since we last reported */ - elapsed = current_time - sess->last_rtcp_send_time; - - /* perform forward reconsideration */ - new_send_time = rtp_stats_add_rtcp_jitter (&sess->stats, data->interval); - - GST_DEBUG ("forward reconsideration %" GST_TIME_FORMAT ", elapsed %" - GST_TIME_FORMAT, GST_TIME_ARGS (new_send_time), GST_TIME_ARGS (elapsed)); - - new_send_time += sess->last_rtcp_send_time; - - /* check if reconsideration */ - if (current_time < new_send_time) { - GST_DEBUG ("reconsider RTCP for %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_send_time)); - result = FALSE; - /* store new check time */ - sess->next_rtcp_check_time = new_send_time; - } else { - result = TRUE; - new_send_time = calculate_rtcp_interval (sess, FALSE, FALSE); - - GST_DEBUG ("can send RTCP now, next interval %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_send_time)); - sess->next_rtcp_check_time = current_time + new_send_time; - } - return result; -} - -/** - * rtp_session_on_timeout: - * @sess: an #RTPSession - * @current_time: the current system time - * @ntpnstime: the current NTP time in nanoseconds - * - * Perform maintenance actions after the timeout obtained with - * rtp_session_next_timeout() expired. - * - * This function will perform timeouts of receivers and senders, send a BYE - * packet or generate RTCP packets with current session stats. - * - * This function can call the #RTPSessionSendRTCP callback, possibly multiple - * times, for each packet that should be processed. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, - guint64 ntpnstime) -{ - GstFlowReturn result = GST_FLOW_OK; - GList *item; - ReportData data; - RTPSource *own; - gboolean notify = FALSE; - - g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR); - - GST_DEBUG ("reporting at %" GST_TIME_FORMAT ", NTP time %" GST_TIME_FORMAT, - GST_TIME_ARGS (current_time), GST_TIME_ARGS (ntpnstime)); - - data.sess = sess; - data.rtcp = NULL; - data.current_time = current_time; - data.ntpnstime = ntpnstime; - data.is_bye = FALSE; - data.has_sdes = FALSE; - - own = sess->source; - - RTP_SESSION_LOCK (sess); - /* get a new interval, we need this for various cleanups etc */ - data.interval = calculate_rtcp_interval (sess, TRUE, sess->first_rtcp); - - /* first perform cleanups */ - g_hash_table_foreach_remove (sess->ssrcs[sess->mask_idx], - (GHRFunc) session_cleanup, &data); - - /* see if we need to generate SR or RR packets */ - if (is_rtcp_time (sess, current_time, &data)) { - if (own->received_bye) { - /* generate BYE instead */ - GST_DEBUG ("generating BYE message"); - session_bye (sess, &data); - sess->sent_bye = TRUE; - } else { - /* loop over all known sources and do something */ - g_hash_table_foreach (sess->ssrcs[sess->mask_idx], - (GHFunc) session_report_blocks, &data); - } - } - - if (data.rtcp) { - guint size; - - /* we keep track of the last report time in order to timeout inactive - * receivers or senders */ - sess->last_rtcp_send_time = data.current_time; - sess->first_rtcp = FALSE; - - /* add SDES for this source when not already added */ - if (!data.has_sdes) - session_sdes (sess, &data); - - /* update average RTCP size before sending */ - size = GST_BUFFER_SIZE (data.rtcp) + sess->header_len; - UPDATE_AVG (sess->stats.avg_rtcp_packet_size, size); - } - - /* check for outdated collisions */ - GST_DEBUG ("checking collision list"); - item = g_list_first (sess->conflicting_addresses); - while (item) { - RTPConflictingAddress *known_conflict = item->data; - GList *next_item = g_list_next (item); - - if (known_conflict->time < current_time - (data.interval * - RTCP_INTERVAL_COLLISION_TIMEOUT)) { - sess->conflicting_addresses = - g_list_delete_link (sess->conflicting_addresses, item); - GST_DEBUG ("collision %p timed out", known_conflict); - g_free (known_conflict); - } - item = next_item; - } - - if (sess->change_ssrc) { - GST_DEBUG ("need to change our SSRC (%08x)", own->ssrc); - g_hash_table_steal (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (own->ssrc)); - - own->ssrc = rtp_session_create_new_ssrc (sess); - rtp_source_reset (own); - - g_hash_table_insert (sess->ssrcs[sess->mask_idx], - GINT_TO_POINTER (own->ssrc), own); - - g_free (sess->bye_reason); - sess->bye_reason = NULL; - sess->sent_bye = FALSE; - sess->change_ssrc = FALSE; - notify = TRUE; - GST_DEBUG ("changed our SSRC to %08x", own->ssrc); - } - RTP_SESSION_UNLOCK (sess); - - if (notify) - g_object_notify (G_OBJECT (sess), "internal-ssrc"); - - /* push out the RTCP packet */ - if (data.rtcp) { - /* close the RTCP packet */ - gst_rtcp_buffer_end (data.rtcp); - - GST_DEBUG ("sending packet"); - if (sess->callbacks.send_rtcp) - result = sess->callbacks.send_rtcp (sess, own, data.rtcp, - sess->sent_bye, sess->send_rtcp_user_data); - else { - GST_DEBUG ("freeing packet"); - gst_buffer_unref (data.rtcp); - } - } - - return result; -} diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h deleted file mode 100644 index 25e228b0..00000000 --- a/gst/rtpmanager/rtpsession.h +++ /dev/null @@ -1,306 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __RTP_SESSION_H__ -#define __RTP_SESSION_H__ - -#include <gst/gst.h> -#include <gst/netbuffer/gstnetbuffer.h> - -#include "rtpsource.h" - -typedef struct _RTPSession RTPSession; -typedef struct _RTPSessionClass RTPSessionClass; - -#define RTP_TYPE_SESSION (rtp_session_get_type()) -#define RTP_SESSION(sess) (G_TYPE_CHECK_INSTANCE_CAST((sess),RTP_TYPE_SESSION,RTPSession)) -#define RTP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_SESSION,RTPSessionClass)) -#define RTP_IS_SESSION(sess) (G_TYPE_CHECK_INSTANCE_TYPE((sess),RTP_TYPE_SESSION)) -#define RTP_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_SESSION)) -#define RTP_SESSION_CAST(sess) ((RTPSession *)(sess)) - -#define RTP_SESSION_LOCK(sess) (g_mutex_lock ((sess)->lock)) -#define RTP_SESSION_UNLOCK(sess) (g_mutex_unlock ((sess)->lock)) - -/** - * RTPSessionProcessRTP: - * @sess: an #RTPSession - * @src: the #RTPSource - * @buffer: the RTP buffer ready for processing - * @user_data: user data specified when registering - * - * This callback will be called when @sess has @buffer ready for further - * processing. Processing the buffer typically includes decoding and displaying - * the buffer. - * - * Returns: a #GstFlowReturn. - */ -typedef GstFlowReturn (*RTPSessionProcessRTP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data); - -/** - * RTPSessionSendRTP: - * @sess: an #RTPSession - * @src: the #RTPSource - * @buffer: the RTP buffer ready for sending - * @user_data: user data specified when registering - * - * This callback will be called when @sess has @buffer ready for sending to - * all listening participants in this session. - * - * Returns: a #GstFlowReturn. - */ -typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, gpointer data, gpointer user_data); - -/** - * RTPSessionSendRTCP: - * @sess: an #RTPSession - * @src: the #RTPSource - * @buffer: the RTCP buffer ready for sending - * @eos: if an EOS event should be pushed - * @user_data: user data specified when registering - * - * This callback will be called when @sess has @buffer ready for sending to - * all listening participants in this session. - * - * Returns: a #GstFlowReturn. - */ -typedef GstFlowReturn (*RTPSessionSendRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, - gboolean eos, gpointer user_data); - -/** - * RTPSessionSyncRTCP: - * @sess: an #RTPSession - * @src: the #RTPSource - * @buffer: the RTCP buffer ready for synchronisation - * @user_data: user data specified when registering - * - * This callback will be called when @sess has an SR @buffer ready for doing - * synchronisation between streams. - * - * Returns: a #GstFlowReturn. - */ -typedef GstFlowReturn (*RTPSessionSyncRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data); - -/** - * RTPSessionClockRate: - * @sess: an #RTPSession - * @payload: the payload - * @user_data: user data specified when registering - * - * This callback will be called when @sess needs the clock-rate of @payload. - * - * Returns: the clock-rate of @pt. - */ -typedef gint (*RTPSessionClockRate) (RTPSession *sess, guint8 payload, gpointer user_data); - -/** - * RTPSessionReconsider: - * @sess: an #RTPSession - * @user_data: user data specified when registering - * - * This callback will be called when @sess needs to cancel the current timeout. - * The currently running timeout should be canceled and a new reporting interval - * should be requested from @sess. - */ -typedef void (*RTPSessionReconsider) (RTPSession *sess, gpointer user_data); - -/** - * RTPSessionCallbacks: - * @RTPSessionProcessRTP: callback to process RTP packets - * @RTPSessionSendRTP: callback for sending RTP packets - * @RTPSessionSendRTCP: callback for sending RTCP packets - * @RTPSessionSyncRTCP: callback for handling SR packets - * @RTPSessionReconsider: callback for reconsidering the timeout - * - * These callbacks can be installed on the session manager to get notification - * when RTP and RTCP packets are ready for further processing. These callbacks - * are not implemented with signals for performance reasons. - */ -typedef struct { - RTPSessionProcessRTP process_rtp; - RTPSessionSendRTP send_rtp; - RTPSessionSyncRTCP sync_rtcp; - RTPSessionSendRTCP send_rtcp; - RTPSessionClockRate clock_rate; - RTPSessionReconsider reconsider; -} RTPSessionCallbacks; - -/** - * RTPConflictingAddress: - * @address: #GstNetAddress which conflicted - * @last_conflict_time: time when the last conflict was seen - * - * This structure is used to account for addresses that have conflicted to find - * loops. - */ -typedef struct { - GstNetAddress address; - GstClockTime time; -} RTPConflictingAddress; - -/** - * RTPSession: - * @lock: lock to protect the session - * @source: the source of this session - * @ssrcs: Hashtable of sources indexed by SSRC - * @cnames: Hashtable of sources indexed by CNAME - * @num_sources: the number of sources - * @activecount: the number of active sources - * @callbacks: callbacks - * @user_data: user data passed in callbacks - * @stats: session statistics - * @conflicting_addresses: GList of conflicting addresses - * - * The RTP session manager object - */ -struct _RTPSession { - GObject object; - - GMutex *lock; - - guint header_len; - guint mtu; - - RTPSource *source; - - /* for sender/receiver counting */ - guint32 key; - guint32 mask_idx; - guint32 mask; - GHashTable *ssrcs[32]; - GHashTable *cnames; - guint total_sources; - - GstClockTime next_rtcp_check_time; - GstClockTime last_rtcp_send_time; - gboolean first_rtcp; - - gchar *bye_reason; - gboolean sent_bye; - - RTPSessionCallbacks callbacks; - gpointer process_rtp_user_data; - gpointer send_rtp_user_data; - gpointer send_rtcp_user_data; - gpointer sync_rtcp_user_data; - gpointer clock_rate_user_data; - gpointer reconsider_user_data; - - RTPSessionStats stats; - - GList *conflicting_addresses; - gboolean change_ssrc; -}; - -/** - * RTPSessionClass: - * @on_new_ssrc: emited when a new source is found - * @on_bye_ssrc: emited when a source is gone - * - * The session class. - */ -struct _RTPSessionClass { - GObjectClass parent_class; - - /* action signals */ - RTPSource* (*get_source_by_ssrc) (RTPSession *sess, guint32 ssrc); - - /* signals */ - void (*on_new_ssrc) (RTPSession *sess, RTPSource *source); - void (*on_ssrc_collision) (RTPSession *sess, RTPSource *source); - void (*on_ssrc_validated) (RTPSession *sess, RTPSource *source); - void (*on_ssrc_active) (RTPSession *sess, RTPSource *source); - void (*on_ssrc_sdes) (RTPSession *sess, RTPSource *source); - void (*on_bye_ssrc) (RTPSession *sess, RTPSource *source); - void (*on_bye_timeout) (RTPSession *sess, RTPSource *source); - void (*on_timeout) (RTPSession *sess, RTPSource *source); - void (*on_sender_timeout) (RTPSession *sess, RTPSource *source); -}; - -GType rtp_session_get_type (void); - -/* create and configure */ -RTPSession* rtp_session_new (void); -void rtp_session_set_callbacks (RTPSession *sess, - RTPSessionCallbacks *callbacks, - gpointer user_data); -void rtp_session_set_process_rtp_callback (RTPSession * sess, - RTPSessionProcessRTP callback, - gpointer user_data); -void rtp_session_set_send_rtp_callback (RTPSession * sess, - RTPSessionSendRTP callback, - gpointer user_data); -void rtp_session_set_send_rtcp_callback (RTPSession * sess, - RTPSessionSendRTCP callback, - gpointer user_data); -void rtp_session_set_sync_rtcp_callback (RTPSession * sess, - RTPSessionSyncRTCP callback, - gpointer user_data); -void rtp_session_set_clock_rate_callback (RTPSession * sess, - RTPSessionClockRate callback, - gpointer user_data); -void rtp_session_set_reconsider_callback (RTPSession * sess, - RTPSessionReconsider callback, - gpointer user_data); -void rtp_session_set_bandwidth (RTPSession *sess, gdouble bandwidth); -gdouble rtp_session_get_bandwidth (RTPSession *sess); -void rtp_session_set_rtcp_fraction (RTPSession *sess, gdouble fraction); -gdouble rtp_session_get_rtcp_fraction (RTPSession *sess); - -gboolean rtp_session_set_sdes_string (RTPSession *sess, GstRTCPSDESType type, - const gchar *cname); -gchar* rtp_session_get_sdes_string (RTPSession *sess, GstRTCPSDESType type); - -GstStructure * rtp_session_get_sdes_struct (RTPSession *sess); -void rtp_session_set_sdes_struct (RTPSession *sess, const GstStructure *sdes); - -/* handling sources */ -RTPSource* rtp_session_get_internal_source (RTPSession *sess); - -void rtp_session_set_internal_ssrc (RTPSession *sess, guint32 ssrc); -guint32 rtp_session_get_internal_ssrc (RTPSession *sess); - -gboolean rtp_session_add_source (RTPSession *sess, RTPSource *src); -guint rtp_session_get_num_sources (RTPSession *sess); -guint rtp_session_get_num_active_sources (RTPSession *sess); -RTPSource* rtp_session_get_source_by_ssrc (RTPSession *sess, guint32 ssrc); -RTPSource* rtp_session_get_source_by_cname (RTPSession *sess, const gchar *cname); -RTPSource* rtp_session_create_source (RTPSession *sess); - -/* processing packets from receivers */ -GstFlowReturn rtp_session_process_rtp (RTPSession *sess, GstBuffer *buffer, - GstClockTime current_time, - GstClockTime running_time, guint64 ntpnstime); -GstFlowReturn rtp_session_process_rtcp (RTPSession *sess, GstBuffer *buffer, - GstClockTime current_time); - -/* processing packets for sending */ -GstFlowReturn rtp_session_send_rtp (RTPSession *sess, gpointer data, gboolean is_list, - GstClockTime current_time, guint64 ntpnstime); - -/* stopping the session */ -GstFlowReturn rtp_session_schedule_bye (RTPSession *sess, const gchar *reason, - GstClockTime current_time); - -/* get interval for next RTCP interval */ -GstClockTime rtp_session_next_timeout (RTPSession *sess, GstClockTime current_time); -GstFlowReturn rtp_session_on_timeout (RTPSession *sess, GstClockTime current_time, - guint64 ntpnstime); - -#endif /* __RTP_SESSION_H__ */ diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c deleted file mode 100644 index 28fa23ef..00000000 --- a/gst/rtpmanager/rtpsource.c +++ /dev/null @@ -1,1625 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -#include <string.h> - -#include <gst/rtp/gstrtpbuffer.h> -#include <gst/rtp/gstrtcpbuffer.h> - -#include "rtpsource.h" - -GST_DEBUG_CATEGORY_STATIC (rtp_source_debug); -#define GST_CAT_DEFAULT rtp_source_debug - -#define RTP_MAX_PROBATION_LEN 32 - -/* signals and args */ -enum -{ - LAST_SIGNAL -}; - -#define DEFAULT_SSRC 0 -#define DEFAULT_IS_CSRC FALSE -#define DEFAULT_IS_VALIDATED FALSE -#define DEFAULT_IS_SENDER FALSE -#define DEFAULT_SDES NULL - -enum -{ - PROP_0, - PROP_SSRC, - PROP_IS_CSRC, - PROP_IS_VALIDATED, - PROP_IS_SENDER, - PROP_SDES, - PROP_STATS, - PROP_LAST -}; - -/* GObject vmethods */ -static void rtp_source_finalize (GObject * object); -static void rtp_source_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void rtp_source_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -/* static guint rtp_source_signals[LAST_SIGNAL] = { 0 }; */ - -G_DEFINE_TYPE (RTPSource, rtp_source, G_TYPE_OBJECT); - -static void -rtp_source_class_init (RTPSourceClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - - gobject_class->finalize = rtp_source_finalize; - - gobject_class->set_property = rtp_source_set_property; - gobject_class->get_property = rtp_source_get_property; - - g_object_class_install_property (gobject_class, PROP_SSRC, - g_param_spec_uint ("ssrc", "SSRC", - "The SSRC of this source", 0, G_MAXUINT, DEFAULT_SSRC, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_IS_CSRC, - g_param_spec_boolean ("is-csrc", "Is CSRC", - "If this SSRC is acting as a contributing source", - DEFAULT_IS_CSRC, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_IS_VALIDATED, - g_param_spec_boolean ("is-validated", "Is Validated", - "If this SSRC is validated", DEFAULT_IS_VALIDATED, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_IS_SENDER, - g_param_spec_boolean ("is-sender", "Is Sender", - "If this SSRC is a sender", DEFAULT_IS_SENDER, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - /** - * RTPSource::sdes - * - * The current SDES items of the source. Returns a structure with the - * following fields: - * - * 'cname' G_TYPE_STRING : The canonical name - * 'name' G_TYPE_STRING : The user name - * 'email' G_TYPE_STRING : The user's electronic mail address - * 'phone' G_TYPE_STRING : The user's phone number - * 'location' G_TYPE_STRING : The geographic user location - * 'tool' G_TYPE_STRING : The name of application or tool - * 'note' G_TYPE_STRING : A notice about the source - */ - g_object_class_install_property (gobject_class, PROP_SDES, - g_param_spec_boxed ("sdes", "SDES", - "The SDES information for this source", - GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - /** - * RTPSource::stats - * - * The statistics of the source. This property returns a GstStructure with - * name application/x-rtp-source-stats with the following fields: - * - */ - g_object_class_install_property (gobject_class, PROP_STATS, - g_param_spec_boxed ("stats", "Stats", - "The stats of this source", GST_TYPE_STRUCTURE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - GST_DEBUG_CATEGORY_INIT (rtp_source_debug, "rtpsource", 0, "RTP Source"); -} - -/** - * rtp_source_reset: - * @src: an #RTPSource - * - * Reset the stats of @src. - */ -void -rtp_source_reset (RTPSource * src) -{ - src->received_bye = FALSE; - - src->stats.cycles = -1; - src->stats.jitter = 0; - src->stats.transit = -1; - src->stats.curr_sr = 0; - src->stats.curr_rr = 0; -} - -static void -rtp_source_init (RTPSource * src) -{ - /* sources are initialy on probation until we receive enough valid RTP - * packets or a valid RTCP packet */ - src->validated = FALSE; - src->internal = FALSE; - src->probation = RTP_DEFAULT_PROBATION; - - src->payload = -1; - src->clock_rate = -1; - src->packets = g_queue_new (); - src->seqnum_base = -1; - src->last_rtptime = -1; - - rtp_source_reset (src); -} - -static void -rtp_source_finalize (GObject * object) -{ - RTPSource *src; - GstBuffer *buffer; - gint i; - - src = RTP_SOURCE_CAST (object); - - while ((buffer = g_queue_pop_head (src->packets))) - gst_buffer_unref (buffer); - g_queue_free (src->packets); - - for (i = 0; i < 9; i++) - g_free (src->sdes[i]); - - g_free (src->bye_reason); - - gst_caps_replace (&src->caps, NULL); - - G_OBJECT_CLASS (rtp_source_parent_class)->finalize (object); -} - -static GstStructure * -rtp_source_create_stats (RTPSource * src) -{ - GstStructure *s; - gboolean is_sender = src->is_sender; - gboolean internal = src->internal; - gchar address_str[GST_NETADDRESS_MAX_LEN]; - - /* common data for all types of sources */ - s = gst_structure_new ("application/x-rtp-source-stats", - "ssrc", G_TYPE_UINT, (guint) src->ssrc, - "internal", G_TYPE_BOOLEAN, internal, - "validated", G_TYPE_BOOLEAN, src->validated, - "received-bye", G_TYPE_BOOLEAN, src->received_bye, - "is-csrc", G_TYPE_BOOLEAN, src->is_csrc, - "is-sender", G_TYPE_BOOLEAN, is_sender, NULL); - - /* add address and port */ - if (src->have_rtp_from) { - gst_netaddress_to_string (&src->rtp_from, address_str, - sizeof (address_str)); - gst_structure_set (s, "rtp-from", G_TYPE_STRING, address_str, NULL); - } - if (src->have_rtcp_from) { - gst_netaddress_to_string (&src->rtcp_from, address_str, - sizeof (address_str)); - gst_structure_set (s, "rtcp-from", G_TYPE_STRING, address_str, NULL); - } - - if (internal) { - /* our internal source */ - if (is_sender) { - /* if we are sending, report about how much we sent, other sources will - * have a RB with info on reception. */ - gst_structure_set (s, - "octets-sent", G_TYPE_UINT64, src->stats.octets_sent, - "packets-sent", G_TYPE_UINT64, src->stats.packets_sent, - "bitrate", G_TYPE_UINT64, src->bitrate, NULL); - } else { - /* if we are not sending we have nothing more to report */ - } - } else { - gboolean have_rb; - guint8 fractionlost = 0; - gint32 packetslost = 0; - guint32 exthighestseq = 0; - guint32 jitter = 0; - guint32 lsr = 0; - guint32 dlsr = 0; - guint32 round_trip = 0; - - /* other sources */ - if (is_sender) { - gboolean have_sr; - GstClockTime time = 0; - guint64 ntptime = 0; - guint32 rtptime = 0; - guint32 packet_count = 0; - guint32 octet_count = 0; - - /* this source is sending to us, get the last SR. */ - have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime, - &packet_count, &octet_count); - gst_structure_set (s, - "octets-received", G_TYPE_UINT64, src->stats.octets_received, - "packets-received", G_TYPE_UINT64, src->stats.packets_received, - "have-sr", G_TYPE_BOOLEAN, have_sr, - "sr-ntptime", G_TYPE_UINT64, ntptime, - "sr-rtptime", G_TYPE_UINT, (guint) rtptime, - "sr-octet-count", G_TYPE_UINT, (guint) octet_count, - "sr-packet-count", G_TYPE_UINT, (guint) packet_count, NULL); - } - /* we might be sending to this SSRC so we report about how it is - * receiving our data */ - have_rb = rtp_source_get_last_rb (src, &fractionlost, &packetslost, - &exthighestseq, &jitter, &lsr, &dlsr, &round_trip); - - gst_structure_set (s, - "have-rb", G_TYPE_BOOLEAN, have_rb, - "rb-fractionlost", G_TYPE_UINT, (guint) fractionlost, - "rb-packetslost", G_TYPE_INT, (gint) packetslost, - "rb-exthighestseq", G_TYPE_UINT, (guint) exthighestseq, - "rb-jitter", G_TYPE_UINT, (guint) jitter, - "rb-lsr", G_TYPE_UINT, (guint) lsr, - "rb-dlsr", G_TYPE_UINT, (guint) dlsr, - "rb-round-trip", G_TYPE_UINT, (guint) round_trip, NULL); - } - - return s; -} - -/** - * rtp_source_get_sdes_struct: - * @src: an #RTSPSource - * - * Get the SDES data as a GstStructure - * - * Returns: a GstStructure with SDES items for @src. - */ -GstStructure * -rtp_source_get_sdes_struct (RTPSource * src) -{ - GstStructure *s; - gchar *str; - - s = gst_structure_new ("application/x-rtp-source-sdes", - "ssrc", G_TYPE_UINT, (guint) src->ssrc, NULL); - - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_CNAME))) { - gst_structure_set (s, "cname", G_TYPE_STRING, str, NULL); - g_free (str); - } - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NAME))) { - gst_structure_set (s, "name", G_TYPE_STRING, str, NULL); - g_free (str); - } - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_EMAIL))) { - gst_structure_set (s, "email", G_TYPE_STRING, str, NULL); - g_free (str); - } - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_PHONE))) { - gst_structure_set (s, "phone", G_TYPE_STRING, str, NULL); - g_free (str); - } - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_LOC))) { - gst_structure_set (s, "location", G_TYPE_STRING, str, NULL); - g_free (str); - } - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_TOOL))) { - gst_structure_set (s, "tool", G_TYPE_STRING, str, NULL); - g_free (str); - } - if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NOTE))) { - gst_structure_set (s, "note", G_TYPE_STRING, str, NULL); - g_free (str); - } - return s; -} - -/** - * rtp_source_set_sdes_struct: - * @src: an #RTSPSource - * @sdes: a #GstStructure with SDES info - * - * Set the SDES items from @sdes. - */ -void -rtp_source_set_sdes_struct (RTPSource * src, const GstStructure * sdes) -{ - const gchar *str; - - if (!gst_structure_has_name (sdes, "application/x-rtp-source-sdes")) - return; - - if ((str = gst_structure_get_string (sdes, "cname"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_CNAME, str); - } - if ((str = gst_structure_get_string (sdes, "name"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_NAME, str); - } - if ((str = gst_structure_get_string (sdes, "email"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_EMAIL, str); - } - if ((str = gst_structure_get_string (sdes, "phone"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_PHONE, str); - } - if ((str = gst_structure_get_string (sdes, "location"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_LOC, str); - } - if ((str = gst_structure_get_string (sdes, "tool"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_TOOL, str); - } - if ((str = gst_structure_get_string (sdes, "note"))) { - rtp_source_set_sdes_string (src, GST_RTCP_SDES_NOTE, str); - } -} - -static void -rtp_source_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - RTPSource *src; - - src = RTP_SOURCE (object); - - switch (prop_id) { - case PROP_SSRC: - src->ssrc = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -rtp_source_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - RTPSource *src; - - src = RTP_SOURCE (object); - - switch (prop_id) { - case PROP_SSRC: - g_value_set_uint (value, rtp_source_get_ssrc (src)); - break; - case PROP_IS_CSRC: - g_value_set_boolean (value, rtp_source_is_as_csrc (src)); - break; - case PROP_IS_VALIDATED: - g_value_set_boolean (value, rtp_source_is_validated (src)); - break; - case PROP_IS_SENDER: - g_value_set_boolean (value, rtp_source_is_sender (src)); - break; - case PROP_SDES: - g_value_take_boxed (value, rtp_source_get_sdes_struct (src)); - break; - case PROP_STATS: - g_value_take_boxed (value, rtp_source_create_stats (src)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/** - * rtp_source_new: - * @ssrc: an SSRC - * - * Create a #RTPSource with @ssrc. - * - * Returns: a new #RTPSource. Use g_object_unref() after usage. - */ -RTPSource * -rtp_source_new (guint32 ssrc) -{ - RTPSource *src; - - src = g_object_new (RTP_TYPE_SOURCE, NULL); - src->ssrc = ssrc; - - return src; -} - -/** - * rtp_source_set_callbacks: - * @src: an #RTPSource - * @cb: callback functions - * @user_data: user data - * - * Set the callbacks for the source. - */ -void -rtp_source_set_callbacks (RTPSource * src, RTPSourceCallbacks * cb, - gpointer user_data) -{ - g_return_if_fail (RTP_IS_SOURCE (src)); - - src->callbacks.push_rtp = cb->push_rtp; - src->callbacks.clock_rate = cb->clock_rate; - src->user_data = user_data; -} - -/** - * rtp_source_get_ssrc: - * @src: an #RTPSource - * - * Get the SSRC of @source. - * - * Returns: the SSRC of src. - */ -guint32 -rtp_source_get_ssrc (RTPSource * src) -{ - guint32 result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), 0); - - result = src->ssrc; - - return result; -} - -/** - * rtp_source_set_as_csrc: - * @src: an #RTPSource - * - * Configure @src as a CSRC, this will also validate @src. - */ -void -rtp_source_set_as_csrc (RTPSource * src) -{ - g_return_if_fail (RTP_IS_SOURCE (src)); - - src->validated = TRUE; - src->is_csrc = TRUE; -} - -/** - * rtp_source_is_as_csrc: - * @src: an #RTPSource - * - * Check if @src is a contributing source. - * - * Returns: %TRUE if @src is acting as a contributing source. - */ -gboolean -rtp_source_is_as_csrc (RTPSource * src) -{ - gboolean result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - result = src->is_csrc; - - return result; -} - -/** - * rtp_source_is_active: - * @src: an #RTPSource - * - * Check if @src is an active source. A source is active if it has been - * validated and has not yet received a BYE packet - * - * Returns: %TRUE if @src is an qactive source. - */ -gboolean -rtp_source_is_active (RTPSource * src) -{ - gboolean result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - result = RTP_SOURCE_IS_ACTIVE (src); - - return result; -} - -/** - * rtp_source_is_validated: - * @src: an #RTPSource - * - * Check if @src is a validated source. - * - * Returns: %TRUE if @src is a validated source. - */ -gboolean -rtp_source_is_validated (RTPSource * src) -{ - gboolean result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - result = src->validated; - - return result; -} - -/** - * rtp_source_is_sender: - * @src: an #RTPSource - * - * Check if @src is a sending source. - * - * Returns: %TRUE if @src is a sending source. - */ -gboolean -rtp_source_is_sender (RTPSource * src) -{ - gboolean result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - result = RTP_SOURCE_IS_SENDER (src); - - return result; -} - -/** - * rtp_source_received_bye: - * @src: an #RTPSource - * - * Check if @src has receoved a BYE packet. - * - * Returns: %TRUE if @src has received a BYE packet. - */ -gboolean -rtp_source_received_bye (RTPSource * src) -{ - gboolean result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - result = src->received_bye; - - return result; -} - - -/** - * rtp_source_get_bye_reason: - * @src: an #RTPSource - * - * Get the BYE reason for @src. Check if the source receoved a BYE message first - * with rtp_source_received_bye(). - * - * Returns: The BYE reason or NULL when no reason was given or the source did - * not receive a BYE message yet. g_fee() after usage. - */ -gchar * -rtp_source_get_bye_reason (RTPSource * src) -{ - gchar *result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), NULL); - - result = g_strdup (src->bye_reason); - - return result; -} - -/** - * rtp_source_update_caps: - * @src: an #RTPSource - * @caps: a #GstCaps - * - * Parse @caps and store all relevant information in @source. - */ -void -rtp_source_update_caps (RTPSource * src, GstCaps * caps) -{ - GstStructure *s; - guint val; - gint ival; - - /* nothing changed, return */ - if (caps == NULL || src->caps == caps) - return; - - s = gst_caps_get_structure (caps, 0); - - if (gst_structure_get_int (s, "payload", &ival)) - src->payload = ival; - else - src->payload = -1; - GST_DEBUG ("got payload %d", src->payload); - - if (gst_structure_get_int (s, "clock-rate", &ival)) - src->clock_rate = ival; - else - src->clock_rate = -1; - - GST_DEBUG ("got clock-rate %d", src->clock_rate); - - if (gst_structure_get_uint (s, "seqnum-base", &val)) - src->seqnum_base = val; - else - src->seqnum_base = -1; - - GST_DEBUG ("got seqnum-base %" G_GINT32_FORMAT, src->seqnum_base); - - gst_caps_replace (&src->caps, caps); -} - -/** - * rtp_source_set_sdes: - * @src: an #RTPSource - * @type: the type of the SDES item - * @data: the SDES data - * @len: the SDES length - * - * Store an SDES item of @type in @src. - * - * Returns: %FALSE if the SDES item was unchanged or @type is unknown. - */ -gboolean -rtp_source_set_sdes (RTPSource * src, GstRTCPSDESType type, - const guint8 * data, guint len) -{ - guint8 *old; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - if (type < 0 || type > GST_RTCP_SDES_PRIV) - return FALSE; - - old = src->sdes[type]; - - /* lengths are the same, check if the data is the same */ - if ((src->sdes_len[type] == len)) - if (data != NULL && old != NULL && (memcmp (old, data, len) == 0)) - return FALSE; - - /* NULL data, make sure we store 0 length or if no length is given, - * take strlen */ - if (data == NULL) - len = 0; - - g_free (src->sdes[type]); - src->sdes[type] = g_memdup (data, len); - src->sdes_len[type] = len; - - return TRUE; -} - -/** - * rtp_source_set_sdes_string: - * @src: an #RTPSource - * @type: the type of the SDES item - * @data: the SDES data - * - * Store an SDES item of @type in @src. This function is similar to - * rtp_source_set_sdes() but takes a null-terminated string for convenience. - * - * Returns: %FALSE if the SDES item was unchanged or @type is unknown. - */ -gboolean -rtp_source_set_sdes_string (RTPSource * src, GstRTCPSDESType type, - const gchar * data) -{ - guint len; - gboolean result; - - if (data) - len = strlen (data); - else - len = 0; - - result = rtp_source_set_sdes (src, type, (guint8 *) data, len); - - return result; -} - -/** - * rtp_source_get_sdes: - * @src: an #RTPSource - * @type: the type of the SDES item - * @data: location to store the SDES data or NULL - * @len: location to store the SDES length or NULL - * - * Get the SDES item of @type from @src. Note that @data does not always point - * to a null-terminated string, use rtp_source_get_sdes_string() to retrieve a - * null-terminated string instead. - * - * @data remains valid until the next call to rtp_source_set_sdes(). - * - * Returns: %TRUE if @type was valid and @data and @len contain valid - * data. @data can be NULL when the item was unset. - */ -gboolean -rtp_source_get_sdes (RTPSource * src, GstRTCPSDESType type, guint8 ** data, - guint * len) -{ - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - if (type < 0 || type > GST_RTCP_SDES_PRIV) - return FALSE; - - if (data) - *data = src->sdes[type]; - if (len) - *len = src->sdes_len[type]; - - return TRUE; -} - -/** - * rtp_source_get_sdes_string: - * @src: an #RTPSource - * @type: the type of the SDES item - * - * Get the SDES item of @type from @src. - * - * Returns: a null-terminated copy of the SDES item or NULL when @type was not - * valid or the SDES item was unset. g_free() after usage. - */ -gchar * -rtp_source_get_sdes_string (RTPSource * src, GstRTCPSDESType type) -{ - gchar *result; - - g_return_val_if_fail (RTP_IS_SOURCE (src), NULL); - - if (type < 0 || type > GST_RTCP_SDES_PRIV) - return NULL; - - result = g_strndup ((const gchar *) src->sdes[type], src->sdes_len[type]); - - return result; -} - -/** - * rtp_source_set_rtp_from: - * @src: an #RTPSource - * @address: the RTP address to set - * - * Set that @src is receiving RTP packets from @address. This is used for - * collistion checking. - */ -void -rtp_source_set_rtp_from (RTPSource * src, GstNetAddress * address) -{ - g_return_if_fail (RTP_IS_SOURCE (src)); - - src->have_rtp_from = TRUE; - memcpy (&src->rtp_from, address, sizeof (GstNetAddress)); -} - -/** - * rtp_source_set_rtcp_from: - * @src: an #RTPSource - * @address: the RTCP address to set - * - * Set that @src is receiving RTCP packets from @address. This is used for - * collistion checking. - */ -void -rtp_source_set_rtcp_from (RTPSource * src, GstNetAddress * address) -{ - g_return_if_fail (RTP_IS_SOURCE (src)); - - src->have_rtcp_from = TRUE; - memcpy (&src->rtcp_from, address, sizeof (GstNetAddress)); -} - -static GstFlowReturn -push_packet (RTPSource * src, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - - /* push queued packets first if any */ - while (!g_queue_is_empty (src->packets)) { - GstBuffer *buffer = GST_BUFFER_CAST (g_queue_pop_head (src->packets)); - - GST_LOG ("pushing queued packet"); - if (src->callbacks.push_rtp) - src->callbacks.push_rtp (src, buffer, src->user_data); - else - gst_buffer_unref (buffer); - } - GST_LOG ("pushing new packet"); - /* push packet */ - if (src->callbacks.push_rtp) - ret = src->callbacks.push_rtp (src, buffer, src->user_data); - else - gst_buffer_unref (buffer); - - return ret; -} - -static gint -get_clock_rate (RTPSource * src, guint8 payload) -{ - if (src->payload == -1) { - /* first payload received, nothing was in the caps, lock on to this payload */ - src->payload = payload; - GST_DEBUG ("first payload %d", payload); - } else if (payload != src->payload) { - /* we have a different payload than before, reset the clock-rate */ - GST_DEBUG ("new payload %d", payload); - src->payload = payload; - src->clock_rate = -1; - src->stats.transit = -1; - } - - if (src->clock_rate == -1) { - gint clock_rate = -1; - - if (src->callbacks.clock_rate) - clock_rate = src->callbacks.clock_rate (src, payload, src->user_data); - - GST_DEBUG ("got clock-rate %d", clock_rate); - - src->clock_rate = clock_rate; - } - return src->clock_rate; -} - -/* Jitter is the variation in the delay of received packets in a flow. It is - * measured by comparing the interval when RTP packets were sent to the interval - * at which they were received. For instance, if packet #1 and packet #2 leave - * 50 milliseconds apart and arrive 60 milliseconds apart, then the jitter is 10 - * milliseconds. */ -static void -calculate_jitter (RTPSource * src, GstBuffer * buffer, - RTPArrivalStats * arrival) -{ - guint64 ntpnstime; - guint32 rtparrival, transit, rtptime; - gint32 diff; - gint clock_rate; - guint8 pt; - - /* get arrival time */ - if ((ntpnstime = arrival->ntpnstime) == GST_CLOCK_TIME_NONE) - goto no_time; - - pt = gst_rtp_buffer_get_payload_type (buffer); - - GST_LOG ("SSRC %08x got payload %d", src->ssrc, pt); - - /* get clockrate */ - if ((clock_rate = get_clock_rate (src, pt)) == -1) - goto no_clock_rate; - - rtptime = gst_rtp_buffer_get_timestamp (buffer); - - /* convert arrival time to RTP timestamp units, truncate to 32 bits, we don't - * care about the absolute value, just the difference. */ - rtparrival = gst_util_uint64_scale_int (ntpnstime, clock_rate, GST_SECOND); - - /* transit time is difference with RTP timestamp */ - transit = rtparrival - rtptime; - - /* get ABS diff with previous transit time */ - if (src->stats.transit != -1) { - if (transit > src->stats.transit) - diff = transit - src->stats.transit; - else - diff = src->stats.transit - transit; - } else - diff = 0; - - src->stats.transit = transit; - - /* update jitter, the value we store is scaled up so we can keep precision. */ - src->stats.jitter += diff - ((src->stats.jitter + 8) >> 4); - - src->stats.prev_rtptime = src->stats.last_rtptime; - src->stats.last_rtptime = rtparrival; - - GST_LOG ("rtparrival %u, rtptime %u, clock-rate %d, diff %d, jitter: %f", - rtparrival, rtptime, clock_rate, diff, (src->stats.jitter) / 16.0); - - return; - - /* ERRORS */ -no_time: - { - GST_WARNING ("cannot get current time"); - return; - } -no_clock_rate: - { - GST_WARNING ("cannot get clock-rate for pt %d", pt); - return; - } -} - -static void -init_seq (RTPSource * src, guint16 seq) -{ - src->stats.base_seq = seq; - src->stats.max_seq = seq; - src->stats.bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ - src->stats.cycles = 0; - src->stats.packets_received = 0; - src->stats.octets_received = 0; - src->stats.bytes_received = 0; - src->stats.prev_received = 0; - src->stats.prev_expected = 0; - - GST_DEBUG ("base_seq %d", seq); -} - -/** - * rtp_source_process_rtp: - * @src: an #RTPSource - * @buffer: an RTP buffer - * - * Let @src handle the incomming RTP @buffer. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_source_process_rtp (RTPSource * src, GstBuffer * buffer, - RTPArrivalStats * arrival) -{ - GstFlowReturn result = GST_FLOW_OK; - guint16 seqnr, udelta; - RTPSourceStats *stats; - - g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR); - g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); - - stats = &src->stats; - - seqnr = gst_rtp_buffer_get_seq (buffer); - - rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); - - if (stats->cycles == -1) { - GST_DEBUG ("received first buffer"); - /* first time we heard of this source */ - init_seq (src, seqnr); - src->stats.max_seq = seqnr - 1; - src->probation = RTP_DEFAULT_PROBATION; - } - - udelta = seqnr - stats->max_seq; - - /* if we are still on probation, check seqnum */ - if (src->probation) { - guint16 expected; - - expected = src->stats.max_seq + 1; - - /* when in probation, we require consecutive seqnums */ - if (seqnr == expected) { - /* expected packet */ - GST_DEBUG ("probation: seqnr %d == expected %d", seqnr, expected); - src->probation--; - src->stats.max_seq = seqnr; - if (src->probation == 0) { - GST_DEBUG ("probation done!"); - init_seq (src, seqnr); - } else { - GstBuffer *q; - - GST_DEBUG ("probation %d: queue buffer", src->probation); - /* when still in probation, keep packets in a list. */ - g_queue_push_tail (src->packets, buffer); - /* remove packets from queue if there are too many */ - while (g_queue_get_length (src->packets) > RTP_MAX_PROBATION_LEN) { - q = g_queue_pop_head (src->packets); - gst_buffer_unref (q); - } - goto done; - } - } else { - GST_DEBUG ("probation: seqnr %d != expected %d", seqnr, expected); - src->probation = RTP_DEFAULT_PROBATION; - src->stats.max_seq = seqnr; - goto done; - } - } else if (udelta < RTP_MAX_DROPOUT) { - /* in order, with permissible gap */ - if (seqnr < stats->max_seq) { - /* sequence number wrapped - count another 64K cycle. */ - stats->cycles += RTP_SEQ_MOD; - } - stats->max_seq = seqnr; - } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) { - /* the sequence number made a very large jump */ - if (seqnr == stats->bad_seq) { - /* two sequential packets -- assume that the other side - * restarted without telling us so just re-sync - * (i.e., pretend this was the first packet). */ - init_seq (src, seqnr); - } else { - /* unacceptable jump */ - stats->bad_seq = (seqnr + 1) & (RTP_SEQ_MOD - 1); - goto bad_sequence; - } - } else { - /* duplicate or reordered packet, will be filtered by jitterbuffer. */ - GST_WARNING ("duplicate or reordered packet"); - } - - src->stats.octets_received += arrival->payload_len; - src->stats.bytes_received += arrival->bytes; - src->stats.packets_received++; - /* the source that sent the packet must be a sender */ - src->is_sender = TRUE; - src->validated = TRUE; - - GST_LOG ("seq %d, PC: %" G_GUINT64_FORMAT ", OC: %" G_GUINT64_FORMAT, - seqnr, src->stats.packets_received, src->stats.octets_received); - - /* calculate jitter for the stats */ - calculate_jitter (src, buffer, arrival); - - /* we're ready to push the RTP packet now */ - result = push_packet (src, buffer); - -done: - return result; - - /* ERRORS */ -bad_sequence: - { - GST_WARNING ("unacceptable seqnum received"); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } -} - -/** - * rtp_source_process_bye: - * @src: an #RTPSource - * @reason: the reason for leaving - * - * Notify @src that a BYE packet has been received. This will make the source - * inactive. - */ -void -rtp_source_process_bye (RTPSource * src, const gchar * reason) -{ - g_return_if_fail (RTP_IS_SOURCE (src)); - - GST_DEBUG ("marking SSRC %08x as BYE, reason: %s", src->ssrc, - GST_STR_NULL (reason)); - - /* copy the reason and mark as received_bye */ - g_free (src->bye_reason); - src->bye_reason = g_strdup (reason); - src->received_bye = TRUE; -} - -static GstBufferListItem -set_ssrc (GstBuffer ** buffer, guint group, guint idx, RTPSource * src) -{ - *buffer = gst_buffer_make_writable (*buffer); - gst_rtp_buffer_set_ssrc (*buffer, src->ssrc); - return GST_BUFFER_LIST_SKIP_GROUP; -} - -/** - * rtp_source_send_rtp: - * @src: an #RTPSource - * @data: an RTP buffer or a list of RTP buffers - * @is_list: if @data is a buffer or list - * @ntpnstime: the NTP time when this buffer was captured in nanoseconds. This - * is the buffer timestamp converted to NTP time. - * - * Send @data (an RTP buffer or list of buffers) originating from @src. - * This will make @src a sender. This function takes ownership of @data and - * modifies the SSRC in the RTP packet to that of @src when needed. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list, - guint64 ntpnstime) -{ - GstFlowReturn result; - guint len; - guint32 rtptime; - guint64 ext_rtptime; - guint64 ntp_diff, rtp_diff; - guint64 elapsed; - GstBufferList *list = NULL; - GstBuffer *buffer = NULL; - guint packets; - guint32 ssrc; - - g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR); - g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR); - - if (is_list) { - list = GST_BUFFER_LIST_CAST (data); - - /* We can grab the caps from the first group, since all - * groups of a buffer list have same caps. */ - buffer = gst_buffer_list_get (list, 0, 0); - if (!buffer) - goto no_buffer; - } else { - buffer = GST_BUFFER_CAST (data); - } - rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); - - /* we are a sender now */ - src->is_sender = TRUE; - - if (is_list) { - /* Each group makes up a network packet. */ - packets = gst_buffer_list_n_groups (list); - len = gst_rtp_buffer_list_get_payload_len (list); - } else { - packets = 1; - len = gst_rtp_buffer_get_payload_len (buffer); - } - - /* update stats for the SR */ - src->stats.packets_sent += packets; - src->stats.octets_sent += len; - src->bytes_sent += len; - - if (src->prev_ntpnstime) { - elapsed = ntpnstime - src->prev_ntpnstime; - - if (elapsed > (G_GINT64_CONSTANT (1) << 31)) { - guint64 rate; - - rate = - gst_util_uint64_scale (src->bytes_sent, elapsed, - (G_GINT64_CONSTANT (1) << 29)); - - GST_LOG ("Elapsed %" G_GUINT64_FORMAT ", bytes %" G_GUINT64_FORMAT - ", rate %" G_GUINT64_FORMAT, elapsed, src->bytes_sent, rate); - - if (src->bitrate == 0) - src->bitrate = rate; - else - src->bitrate = ((src->bitrate * 3) + rate) / 4; - - src->prev_ntpnstime = ntpnstime; - src->bytes_sent = 0; - } - } else { - GST_LOG ("Reset bitrate measurement"); - src->prev_ntpnstime = ntpnstime; - src->bitrate = 0; - } - - if (is_list) { - rtptime = gst_rtp_buffer_list_get_timestamp (list); - } else { - rtptime = gst_rtp_buffer_get_timestamp (buffer); - } - ext_rtptime = src->last_rtptime; - ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime); - - GST_LOG ("SSRC %08x, RTP %" G_GUINT64_FORMAT ", NTP %" GST_TIME_FORMAT, - src->ssrc, ext_rtptime, GST_TIME_ARGS (ntpnstime)); - - if (ext_rtptime > src->last_rtptime) { - rtp_diff = ext_rtptime - src->last_rtptime; - ntp_diff = ntpnstime - src->last_ntpnstime; - - /* calc the diff so we can detect drift at the sender. This can also be used - * to guestimate the clock rate if the NTP time is locked to the RTP - * timestamps (as is the case when the capture device is providing the clock). */ - GST_LOG ("SSRC %08x, diff RTP %" G_GUINT64_FORMAT ", diff NTP %" - GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (ntp_diff)); - } - - /* we keep track of the last received RTP timestamp and the corresponding - * NTP timestamp so that we can use this info when constructing SR reports */ - src->last_rtptime = ext_rtptime; - src->last_ntpnstime = ntpnstime; - - /* push packet */ - if (!src->callbacks.push_rtp) - goto no_callback; - - if (is_list) { - ssrc = gst_rtp_buffer_list_get_ssrc (list); - } else { - ssrc = gst_rtp_buffer_get_ssrc (buffer); - } - - if (ssrc != src->ssrc) { - /* the SSRC of the packet is not correct, make a writable buffer and - * update the SSRC. This could involve a complete copy of the packet when - * it is not writable. Usually the payloader will use caps negotiation to - * get the correct SSRC from the session manager before pushing anything. */ - - /* FIXME, we don't want to warn yet because we can't inform any payloader - * of the changes SSRC yet because we don't implement pad-alloc. */ - GST_LOG ("updating SSRC from %08x to %08x, fix the payloader", ssrc, - src->ssrc); - - if (is_list) { - list = gst_buffer_list_make_writable (list); - gst_buffer_list_foreach (list, (GstBufferListFunc) set_ssrc, src); - } else { - set_ssrc (&buffer, 0, 0, src); - } - } - GST_LOG ("pushing RTP %s %" G_GUINT64_FORMAT, is_list ? "list" : "packet", - src->stats.packets_sent); - - result = src->callbacks.push_rtp (src, data, src->user_data); - - return result; - - /* ERRORS */ -no_buffer: - { - GST_WARNING ("no buffers in buffer list"); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); - return GST_FLOW_OK; - } -no_callback: - { - GST_WARNING ("no callback installed, dropping packet"); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); - return GST_FLOW_OK; - } -} - -/** - * rtp_source_process_sr: - * @src: an #RTPSource - * @time: time of packet arrival - * @ntptime: the NTP time in 32.32 fixed point - * @rtptime: the RTP time - * @packet_count: the packet count - * @octet_count: the octect count - * - * Update the sender report in @src. - */ -void -rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime, - guint32 rtptime, guint32 packet_count, guint32 octet_count) -{ - RTPSenderReport *curr; - gint curridx; - - g_return_if_fail (RTP_IS_SOURCE (src)); - - GST_DEBUG ("got SR packet: SSRC %08x, NTP %08x:%08x, RTP %" G_GUINT32_FORMAT - ", PC %" G_GUINT32_FORMAT ", OC %" G_GUINT32_FORMAT, src->ssrc, - (guint32) (ntptime >> 32), (guint32) (ntptime & 0xffffffff), rtptime, - packet_count, octet_count); - - curridx = src->stats.curr_sr ^ 1; - curr = &src->stats.sr[curridx]; - - /* this is a sender now */ - src->is_sender = TRUE; - - /* update current */ - curr->is_valid = TRUE; - curr->ntptime = ntptime; - curr->rtptime = rtptime; - curr->packet_count = packet_count; - curr->octet_count = octet_count; - curr->time = time; - - /* make current */ - src->stats.curr_sr = curridx; -} - -/** - * rtp_source_process_rb: - * @src: an #RTPSource - * @time: the current time in nanoseconds since 1970 - * @fractionlost: fraction lost since last SR/RR - * @packetslost: the cumululative number of packets lost - * @exthighestseq: the extended last sequence number received - * @jitter: the interarrival jitter - * @lsr: the last SR packet from this source - * @dlsr: the delay since last SR packet - * - * Update the report block in @src. - */ -void -rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost, - gint32 packetslost, guint32 exthighestseq, guint32 jitter, guint32 lsr, - guint32 dlsr) -{ - RTPReceiverReport *curr; - gint curridx; - guint32 ntp, A; - - g_return_if_fail (RTP_IS_SOURCE (src)); - - GST_DEBUG ("got RB packet: SSRC %08x, FL %2x, PL %d, HS %" G_GUINT32_FORMAT - ", jitter %" G_GUINT32_FORMAT ", LSR %04x:%04x, DLSR %04x:%04x", - src->ssrc, fractionlost, packetslost, exthighestseq, jitter, lsr >> 16, - lsr & 0xffff, dlsr >> 16, dlsr & 0xffff); - - curridx = src->stats.curr_rr ^ 1; - curr = &src->stats.rr[curridx]; - - /* update current */ - curr->is_valid = TRUE; - curr->fractionlost = fractionlost; - curr->packetslost = packetslost; - curr->exthighestseq = exthighestseq; - curr->jitter = jitter; - curr->lsr = lsr; - curr->dlsr = dlsr; - - /* calculate round trip, round the time up */ - ntp = ((gst_rtcp_unix_to_ntp (time) + 0xffff) >> 16) & 0xffffffff; - A = dlsr + lsr; - if (A > 0 && ntp > A) - A = ntp - A; - else - A = 0; - curr->round_trip = A; - - GST_DEBUG ("NTP %04x:%04x, round trip %04x:%04x", ntp >> 16, ntp & 0xffff, - A >> 16, A & 0xffff); - - /* make current */ - src->stats.curr_rr = curridx; -} - -/** - * rtp_source_get_new_sr: - * @src: an #RTPSource - * @ntpnstime: the current time in nanoseconds since 1970 - * @ntptime: the NTP time in 32.32 fixed point - * @rtptime: the RTP time corresponding to @ntptime - * @packet_count: the packet count - * @octet_count: the octect count - * - * Get new values to put into a new SR report from this source. - * - * Returns: %TRUE on success. - */ -gboolean -rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime, - guint64 * ntptime, guint32 * rtptime, guint32 * packet_count, - guint32 * octet_count) -{ - guint64 t_rtp; - guint64 t_current_ntp; - GstClockTimeDiff diff; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - /* use the sync params to interpolate the date->time member to rtptime. We - * use the last sent timestamp and rtptime as reference points. We assume - * that the slope of the rtptime vs timestamp curve is 1, which is certainly - * sufficient for the frequency at which we report SR and the rate we send - * out RTP packets. */ - t_rtp = src->last_rtptime; - - GST_DEBUG ("last_ntpnstime %" GST_TIME_FORMAT ", last_rtptime %" - G_GUINT64_FORMAT, GST_TIME_ARGS (src->last_ntpnstime), t_rtp); - - if (src->clock_rate != -1) { - /* get the diff with the SR time */ - diff = GST_CLOCK_DIFF (src->last_ntpnstime, ntpnstime); - - /* now translate the diff to RTP time, handle positive and negative cases. - * If there is no diff, we already set rtptime correctly above. */ - if (diff > 0) { - GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT, - GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff)); - t_rtp += gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND); - } else { - diff = -diff; - GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT, - GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff)); - t_rtp -= gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND); - } - } else { - GST_WARNING ("no clock-rate, cannot interpolate rtp time"); - } - - /* convert the NTP time in nanoseconds to 32.32 fixed point */ - t_current_ntp = gst_util_uint64_scale (ntpnstime, (1LL << 32), GST_SECOND); - - GST_DEBUG ("NTP %08x:%08x, RTP %" G_GUINT32_FORMAT, - (guint32) (t_current_ntp >> 32), (guint32) (t_current_ntp & 0xffffffff), - (guint32) t_rtp); - - if (ntptime) - *ntptime = t_current_ntp; - if (rtptime) - *rtptime = t_rtp; - if (packet_count) - *packet_count = src->stats.packets_sent; - if (octet_count) - *octet_count = src->stats.octets_sent; - - return TRUE; -} - -/** - * rtp_source_get_new_rb: - * @src: an #RTPSource - * @time: the current time of the system clock - * @fractionlost: fraction lost since last SR/RR - * @packetslost: the cumululative number of packets lost - * @exthighestseq: the extended last sequence number received - * @jitter: the interarrival jitter - * @lsr: the last SR packet from this source - * @dlsr: the delay since last SR packet - * - * Get new values to put into a new report block from this source. - * - * Returns: %TRUE on success. - */ -gboolean -rtp_source_get_new_rb (RTPSource * src, GstClockTime time, - guint8 * fractionlost, gint32 * packetslost, guint32 * exthighestseq, - guint32 * jitter, guint32 * lsr, guint32 * dlsr) -{ - RTPSourceStats *stats; - guint64 extended_max, expected; - guint64 expected_interval, received_interval, ntptime; - gint64 lost, lost_interval; - guint32 fraction, LSR, DLSR; - GstClockTime sr_time; - - stats = &src->stats; - - extended_max = stats->cycles + stats->max_seq; - expected = extended_max - stats->base_seq + 1; - - GST_DEBUG ("ext_max %" G_GUINT64_FORMAT ", expected %" G_GUINT64_FORMAT - ", received %" G_GUINT64_FORMAT ", base_seq %" G_GUINT32_FORMAT, - extended_max, expected, stats->packets_received, stats->base_seq); - - lost = expected - stats->packets_received; - lost = CLAMP (lost, -0x800000, 0x7fffff); - - expected_interval = expected - stats->prev_expected; - stats->prev_expected = expected; - received_interval = stats->packets_received - stats->prev_received; - stats->prev_received = stats->packets_received; - - lost_interval = expected_interval - received_interval; - - if (expected_interval == 0 || lost_interval <= 0) - fraction = 0; - else - fraction = (lost_interval << 8) / expected_interval; - - GST_DEBUG ("add RR for SSRC %08x", src->ssrc); - /* we scaled the jitter up for additional precision */ - GST_DEBUG ("fraction %" G_GUINT32_FORMAT ", lost %" G_GINT64_FORMAT - ", extseq %" G_GUINT64_FORMAT ", jitter %d", fraction, lost, - extended_max, stats->jitter >> 4); - - if (rtp_source_get_last_sr (src, &sr_time, &ntptime, NULL, NULL, NULL)) { - GstClockTime diff; - - /* LSR is middle 32 bits of the last ntptime */ - LSR = (ntptime >> 16) & 0xffffffff; - diff = time - sr_time; - GST_DEBUG ("last SR time diff %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); - /* DLSR, delay since last SR is expressed in 1/65536 second units */ - DLSR = gst_util_uint64_scale_int (diff, 65536, GST_SECOND); - } else { - /* No valid SR received, LSR/DLSR are set to 0 then */ - GST_DEBUG ("no valid SR received"); - LSR = 0; - DLSR = 0; - } - GST_DEBUG ("LSR %04x:%04x, DLSR %04x:%04x", LSR >> 16, LSR & 0xffff, - DLSR >> 16, DLSR & 0xffff); - - if (fractionlost) - *fractionlost = fraction; - if (packetslost) - *packetslost = lost; - if (exthighestseq) - *exthighestseq = extended_max; - if (jitter) - *jitter = stats->jitter >> 4; - if (lsr) - *lsr = LSR; - if (dlsr) - *dlsr = DLSR; - - return TRUE; -} - -/** - * rtp_source_get_last_sr: - * @src: an #RTPSource - * @time: time of packet arrival - * @ntptime: the NTP time in 32.32 fixed point - * @rtptime: the RTP time - * @packet_count: the packet count - * @octet_count: the octect count - * - * Get the values of the last sender report as set with rtp_source_process_sr(). - * - * Returns: %TRUE if there was a valid SR report. - */ -gboolean -rtp_source_get_last_sr (RTPSource * src, GstClockTime * time, guint64 * ntptime, - guint32 * rtptime, guint32 * packet_count, guint32 * octet_count) -{ - RTPSenderReport *curr; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - curr = &src->stats.sr[src->stats.curr_sr]; - if (!curr->is_valid) - return FALSE; - - if (ntptime) - *ntptime = curr->ntptime; - if (rtptime) - *rtptime = curr->rtptime; - if (packet_count) - *packet_count = curr->packet_count; - if (octet_count) - *octet_count = curr->octet_count; - if (time) - *time = curr->time; - - return TRUE; -} - -/** - * rtp_source_get_last_rb: - * @src: an #RTPSource - * @fractionlost: fraction lost since last SR/RR - * @packetslost: the cumululative number of packets lost - * @exthighestseq: the extended last sequence number received - * @jitter: the interarrival jitter - * @lsr: the last SR packet from this source - * @dlsr: the delay since last SR packet - * @round_trip: the round trip time - * - * Get the values of the last RB report set with rtp_source_process_rb(). - * - * Returns: %TRUE if there was a valid SB report. - */ -gboolean -rtp_source_get_last_rb (RTPSource * src, guint8 * fractionlost, - gint32 * packetslost, guint32 * exthighestseq, guint32 * jitter, - guint32 * lsr, guint32 * dlsr, guint32 * round_trip) -{ - RTPReceiverReport *curr; - - g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - - curr = &src->stats.rr[src->stats.curr_rr]; - if (!curr->is_valid) - return FALSE; - - if (fractionlost) - *fractionlost = curr->fractionlost; - if (packetslost) - *packetslost = curr->packetslost; - if (exthighestseq) - *exthighestseq = curr->exthighestseq; - if (jitter) - *jitter = curr->jitter; - if (lsr) - *lsr = curr->lsr; - if (dlsr) - *dlsr = curr->dlsr; - if (round_trip) - *round_trip = curr->round_trip; - - return TRUE; -} diff --git a/gst/rtpmanager/rtpsource.h b/gst/rtpmanager/rtpsource.h deleted file mode 100644 index 8355bc0c..00000000 --- a/gst/rtpmanager/rtpsource.h +++ /dev/null @@ -1,226 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __RTP_SOURCE_H__ -#define __RTP_SOURCE_H__ - -#include <gst/gst.h> -#include <gst/rtp/gstrtcpbuffer.h> -#include <gst/netbuffer/gstnetbuffer.h> - -#include "rtpstats.h" - -/* the default number of consecutive RTP packets we need to receive before the - * source is considered valid */ -#define RTP_NO_PROBATION 0 -#define RTP_DEFAULT_PROBATION 2 - -#define RTP_SEQ_MOD (1 << 16) - -typedef struct _RTPSource RTPSource; -typedef struct _RTPSourceClass RTPSourceClass; - -#define RTP_TYPE_SOURCE (rtp_source_get_type()) -#define RTP_SOURCE(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_SOURCE,RTPSource)) -#define RTP_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_SOURCE,RTPSourceClass)) -#define RTP_IS_SOURCE(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_SOURCE)) -#define RTP_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_SOURCE)) -#define RTP_SOURCE_CAST(src) ((RTPSource *)(src)) - -/** - * RTP_SOURCE_IS_ACTIVE: - * @src: an #RTPSource - * - * Check if @src is active. A source is active when it has been validated - * and has not yet received a BYE packet. - */ -#define RTP_SOURCE_IS_ACTIVE(src) (src->validated && !src->received_bye) - -/** - * RTP_SOURCE_IS_SENDER: - * @src: an #RTPSource - * - * Check if @src is a sender. - */ -#define RTP_SOURCE_IS_SENDER(src) (src->is_sender) - -/** - * RTPSourcePushRTP: - * @src: an #RTPSource - * @buffer: the RTP buffer ready for processing - * @user_data: user data specified when registering - * - * This callback will be called when @src has @buffer ready for further - * processing. - * - * Returns: a #GstFlowReturn. - */ -typedef GstFlowReturn (*RTPSourcePushRTP) (RTPSource *src, GstBuffer *buffer, - gpointer user_data); - -/** - * RTPSourceClockRate: - * @src: an #RTPSource - * @payload: a payload type - * @user_data: user data specified when registering - * - * This callback will be called when @src needs the clock-rate of the - * @payload. - * - * Returns: a clock-rate for @payload. - */ -typedef gint (*RTPSourceClockRate) (RTPSource *src, guint8 payload, gpointer user_data); - -/** - * RTPSourceCallbacks: - * @push_rtp: a packet becomes available for handling - * @clock_rate: a clock-rate is requested - * @get_time: the current clock time is requested - * - * Callbacks performed by #RTPSource when actions need to be performed. - */ -typedef struct { - RTPSourcePushRTP push_rtp; - RTPSourceClockRate clock_rate; -} RTPSourceCallbacks; - -/** - * RTPSource: - * - * A source in the #RTPSession - */ -struct _RTPSource { - GObject object; - - /*< private >*/ - guint32 ssrc; - - gint probation; - gboolean validated; - gboolean internal; - gboolean is_csrc; - gboolean is_sender; - - guint8 *sdes[9]; - guint sdes_len[9]; - - gboolean received_bye; - gchar *bye_reason; - - gboolean have_rtp_from; - GstNetAddress rtp_from; - gboolean have_rtcp_from; - GstNetAddress rtcp_from; - - gint payload; - GstCaps *caps; - gint clock_rate; - gint32 seqnum_base; - - GstClockTime bye_time; - GstClockTime last_activity; - GstClockTime last_rtp_activity; - - GstClockTime last_rtptime; - GstClockTime last_ntpnstime; - - /* for bitrate estimation */ - guint64 bitrate; - GstClockTime prev_ntpnstime; - guint64 bytes_sent; - - GQueue *packets; - - RTPSourceCallbacks callbacks; - gpointer user_data; - - RTPSourceStats stats; -}; - -struct _RTPSourceClass { - GObjectClass parent_class; -}; - -GType rtp_source_get_type (void); - -/* managing lifetime of sources */ -RTPSource* rtp_source_new (guint32 ssrc); -void rtp_source_set_callbacks (RTPSource *src, RTPSourceCallbacks *cb, gpointer data); - -/* properties */ -guint32 rtp_source_get_ssrc (RTPSource *src); - -void rtp_source_set_as_csrc (RTPSource *src); -gboolean rtp_source_is_as_csrc (RTPSource *src); - -gboolean rtp_source_is_active (RTPSource *src); -gboolean rtp_source_is_validated (RTPSource *src); -gboolean rtp_source_is_sender (RTPSource *src); - -gboolean rtp_source_received_bye (RTPSource *src); -gchar * rtp_source_get_bye_reason (RTPSource *src); - -void rtp_source_update_caps (RTPSource *src, GstCaps *caps); - -/* SDES info */ -gboolean rtp_source_set_sdes (RTPSource *src, GstRTCPSDESType type, - const guint8 *data, guint len); -gboolean rtp_source_set_sdes_string (RTPSource *src, GstRTCPSDESType type, - const gchar *data); -gboolean rtp_source_get_sdes (RTPSource *src, GstRTCPSDESType type, - guint8 **data, guint *len); -gchar* rtp_source_get_sdes_string (RTPSource *src, GstRTCPSDESType type); - -GstStructure * rtp_source_get_sdes_struct (RTPSource * src); -void rtp_source_set_sdes_struct (RTPSource * src, const GstStructure *sdes); - -/* handling network address */ -void rtp_source_set_rtp_from (RTPSource *src, GstNetAddress *address); -void rtp_source_set_rtcp_from (RTPSource *src, GstNetAddress *address); - -/* handling RTP */ -GstFlowReturn rtp_source_process_rtp (RTPSource *src, GstBuffer *buffer, RTPArrivalStats *arrival); - -GstFlowReturn rtp_source_send_rtp (RTPSource *src, gpointer data, gboolean is_list, guint64 ntpnstime); - -/* RTCP messages */ -void rtp_source_process_bye (RTPSource *src, const gchar *reason); -void rtp_source_process_sr (RTPSource *src, GstClockTime time, guint64 ntptime, - guint32 rtptime, guint32 packet_count, guint32 octet_count); -void rtp_source_process_rb (RTPSource *src, GstClockTime time, guint8 fractionlost, - gint32 packetslost, guint32 exthighestseq, guint32 jitter, - guint32 lsr, guint32 dlsr); - -gboolean rtp_source_get_new_sr (RTPSource *src, guint64 ntpnstime, guint64 *ntptime, - guint32 *rtptime, guint32 *packet_count, - guint32 *octet_count); -gboolean rtp_source_get_new_rb (RTPSource *src, GstClockTime time, guint8 *fractionlost, - gint32 *packetslost, guint32 *exthighestseq, guint32 *jitter, - guint32 *lsr, guint32 *dlsr); - -gboolean rtp_source_get_last_sr (RTPSource *src, GstClockTime *time, guint64 *ntptime, - guint32 *rtptime, guint32 *packet_count, - guint32 *octet_count); -gboolean rtp_source_get_last_rb (RTPSource *src, guint8 *fractionlost, gint32 *packetslost, - guint32 *exthighestseq, guint32 *jitter, - guint32 *lsr, guint32 *dlsr, guint32 *round_trip); - -void rtp_source_reset (RTPSource * src); - -#endif /* __RTP_SOURCE_H__ */ diff --git a/gst/rtpmanager/rtpstats.c b/gst/rtpmanager/rtpstats.c deleted file mode 100644 index 640c3194..00000000 --- a/gst/rtpmanager/rtpstats.c +++ /dev/null @@ -1,176 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "rtpstats.h" - -/** - * rtp_stats_init_defaults: - * @stats: an #RTPSessionStats struct - * - * Initialize @stats with its default values. - */ -void -rtp_stats_init_defaults (RTPSessionStats * stats) -{ - stats->bandwidth = RTP_STATS_BANDWIDTH; - stats->sender_fraction = RTP_STATS_SENDER_FRACTION; - stats->receiver_fraction = RTP_STATS_RECEIVER_FRACTION; - stats->rtcp_bandwidth = RTP_STATS_RTCP_BANDWIDTH; - stats->min_interval = RTP_STATS_MIN_INTERVAL; - stats->bye_timeout = RTP_STATS_BYE_TIMEOUT; -} - -/** - * rtp_stats_calculate_rtcp_interval: - * @stats: an #RTPSessionStats struct - * @sender: if we are a sender - * @first: if this is the first time - * - * Calculate the RTCP interval. The result of this function is the amount of - * time to wait (in nanoseconds) before sending a new RTCP message. - * - * Returns: the RTCP interval. - */ -GstClockTime -rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send, - gboolean first) -{ - gdouble members, senders, n; - gdouble avg_rtcp_size, rtcp_bw; - gdouble interval; - gdouble rtcp_min_time; - - /* Very first call at application start-up uses half the min - * delay for quicker notification while still allowing some time - * before reporting for randomization and to learn about other - * sources so the report interval will converge to the correct - * interval more quickly. - */ - rtcp_min_time = stats->min_interval; - if (first) - rtcp_min_time /= 2.0; - - /* Dedicate a fraction of the RTCP bandwidth to senders unless - * the number of senders is large enough that their share is - * more than that fraction. - */ - n = members = stats->active_sources; - senders = (gdouble) stats->sender_sources; - rtcp_bw = stats->rtcp_bandwidth; - - if (senders <= members * RTP_STATS_SENDER_FRACTION) { - if (we_send) { - rtcp_bw *= RTP_STATS_SENDER_FRACTION; - n = senders; - } else { - rtcp_bw *= RTP_STATS_RECEIVER_FRACTION; - n -= senders; - } - } - - avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0; - /* - * The effective number of sites times the average packet size is - * the total number of octets sent when each site sends a report. - * Dividing this by the effective bandwidth gives the time - * interval over which those packets must be sent in order to - * meet the bandwidth target, with a minimum enforced. In that - * time interval we send one report so this time is also our - * average time between reports. - */ - interval = avg_rtcp_size * n / rtcp_bw; - if (interval < rtcp_min_time) - interval = rtcp_min_time; - - return interval * GST_SECOND; -} - -/** - * rtp_stats_add_rtcp_jitter: - * @stats: an #RTPSessionStats struct - * @interval: an RTCP interval - * - * Apply a random jitter to the @interval. @interval is typically obtained with - * rtp_stats_calculate_rtcp_interval(). - * - * Returns: the new RTCP interval. - */ -GstClockTime -rtp_stats_add_rtcp_jitter (RTPSessionStats * stats, GstClockTime interval) -{ - gdouble temp; - - /* see RFC 3550 p 30 - * To compensate for "unconditional reconsideration" converging to a - * value below the intended average. - */ -#define COMPENSATION (2.71828 - 1.5); - - temp = (interval * g_random_double_range (0.5, 1.5)) / COMPENSATION; - - return (GstClockTime) temp; -} - - -/** - * rtp_stats_calculate_bye_interval: - * @stats: an #RTPSessionStats struct - * - * Calculate the BYE interval. The result of this function is the amount of - * time to wait (in nanoseconds) before sending a BYE message. - * - * Returns: the BYE interval. - */ -GstClockTime -rtp_stats_calculate_bye_interval (RTPSessionStats * stats) -{ - gdouble members; - gdouble avg_rtcp_size, rtcp_bw; - gdouble interval; - gdouble rtcp_min_time; - - /* no interval when we have less than 50 members */ - if (stats->active_sources < 50) - return 0; - - rtcp_min_time = (stats->min_interval) / 2.0; - - /* Dedicate a fraction of the RTCP bandwidth to senders unless - * the number of senders is large enough that their share is - * more than that fraction. - */ - members = stats->bye_members; - rtcp_bw = stats->rtcp_bandwidth * RTP_STATS_RECEIVER_FRACTION; - - avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0; - /* - * The effective number of sites times the average packet size is - * the total number of octets sent when each site sends a report. - * Dividing this by the effective bandwidth gives the time - * interval over which those packets must be sent in order to - * meet the bandwidth target, with a minimum enforced. In that - * time interval we send one report so this time is also our - * average time between reports. - */ - interval = avg_rtcp_size * members / rtcp_bw; - if (interval < rtcp_min_time) - interval = rtcp_min_time; - - return interval * GST_SECOND; -} diff --git a/gst/rtpmanager/rtpstats.h b/gst/rtpmanager/rtpstats.h deleted file mode 100644 index e5824315..00000000 --- a/gst/rtpmanager/rtpstats.h +++ /dev/null @@ -1,195 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __RTP_STATS_H__ -#define __RTP_STATS_H__ - -#include <gst/gst.h> -#include <gst/netbuffer/gstnetbuffer.h> - -/** - * RTPSenderReport: - * - * A sender report structure. - */ -typedef struct { - gboolean is_valid; - guint64 ntptime; - guint32 rtptime; - guint32 packet_count; - guint32 octet_count; - GstClockTime time; -} RTPSenderReport; - -/** - * RTPReceiverReport: - * - * A receiver report structure. - */ -typedef struct { - gboolean is_valid; - guint32 ssrc; /* who the report is from */ - guint8 fractionlost; - guint32 packetslost; - guint32 exthighestseq; - guint32 jitter; - guint32 lsr; - guint32 dlsr; - guint32 round_trip; -} RTPReceiverReport; - -/** - * RTPArrivalStats: - * @time: arrival time of a packet according to the system clock - * @running_time: arrival time of a packet as buffer running_time - * @ntpnstime: arrival time of a packet as NTP time in nanoseconds - * @have_address: if the @address field contains a valid address - * @address: address of the sender of the packet - * @bytes: bytes of the packet including lowlevel overhead - * @payload_len: bytes of the RTP payload - * - * Structure holding information about the arrival stats of a packet. - */ -typedef struct { - GstClockTime time; - GstClockTime running_time; - guint64 ntpnstime; - gboolean have_address; - GstNetAddress address; - guint bytes; - guint payload_len; -} RTPArrivalStats; - -/** - * RTPSourceStats: - * @packetsreceived: number of received packets in total - * @prevpacketsreceived: number of packets received in previous reporting - * interval - * @octetsreceived: number of payload bytes received - * @bytesreceived: number of total bytes received including headers and lower - * protocol level overhead - * @max_seqnr: highest sequence number received - * @transit: previous transit time used for calculating @jitter - * @jitter: current jitter - * @prev_rtptime: previous time when an RTP packet was received - * @prev_rtcptime: previous time when an RTCP packet was received - * @last_rtptime: time when last RTP packet received - * @last_rtcptime: time when last RTCP packet received - * @curr_rr: index of current @rr block - * @rr: previous and current receiver report block - * @curr_sr: index of current @sr block - * @sr: previous and current sender report block - * - * Stats about a source. - */ -typedef struct { - guint64 packets_received; - guint64 octets_received; - guint64 bytes_received; - - guint32 prev_expected; - guint32 prev_received; - - guint16 max_seq; - guint64 cycles; - guint32 base_seq; - guint32 bad_seq; - guint32 transit; - guint32 jitter; - - guint64 packets_sent; - guint64 octets_sent; - - /* when we received stuff */ - GstClockTime prev_rtptime; - GstClockTime prev_rtcptime; - GstClockTime last_rtptime; - GstClockTime last_rtcptime; - - /* sender and receiver reports */ - gint curr_rr; - RTPReceiverReport rr[2]; - gint curr_sr; - RTPSenderReport sr[2]; -} RTPSourceStats; - -#define RTP_STATS_BANDWIDTH 64000.0 -#define RTP_STATS_RTCP_BANDWIDTH 3000.0 -/* - * Minimum average time between RTCP packets from this site (in - * seconds). This time prevents the reports from `clumping' when - * sessions are small and the law of large numbers isn't helping - * to smooth out the traffic. It also keeps the report interval - * from becoming ridiculously small during transient outages like - * a network partition. - */ -#define RTP_STATS_MIN_INTERVAL 5.0 -/* - * Fraction of the RTCP bandwidth to be shared among active - * senders. (This fraction was chosen so that in a typical - * session with one or two active senders, the computed report - * time would be roughly equal to the minimum report time so that - * we don't unnecessarily slow down receiver reports.) The - * receiver fraction must be 1 - the sender fraction. - */ -#define RTP_STATS_SENDER_FRACTION (0.25) -#define RTP_STATS_RECEIVER_FRACTION (1.0 - RTP_STATS_SENDER_FRACTION) - -/* - * When receiving a BYE from a source, remove the source from the database - * after this timeout. - */ -#define RTP_STATS_BYE_TIMEOUT (2 * GST_SECOND) - -/* - * The maximum number of missing packets we tollerate. These are packets with a - * sequence number bigger than the last seen packet. - */ -#define RTP_MAX_DROPOUT 3000 -/* - * The maximum number of misordered packets we tollerate. These are packets with - * a sequence number smaller than the last seen packet. - */ -#define RTP_MAX_MISORDER 100 - -/** - * RTPSessionStats: - * - * Stats kept for a session and used to produce RTCP packet timeouts. - */ -typedef struct { - gdouble bandwidth; - gdouble sender_fraction; - gdouble receiver_fraction; - gdouble rtcp_bandwidth; - gdouble min_interval; - GstClockTime bye_timeout; - guint sender_sources; - guint active_sources; - guint avg_rtcp_packet_size; - guint bye_members; -} RTPSessionStats; - -void rtp_stats_init_defaults (RTPSessionStats *stats); - -GstClockTime rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gboolean sender, gboolean first); -GstClockTime rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval); -GstClockTime rtp_stats_calculate_bye_interval (RTPSessionStats *stats); - -#endif /* __RTP_STATS_H__ */ -- cgit v1.2.1