X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Frtpmanager%2Fgstrtpbin.c;h=fbed41415536c4759bdd13c4d7bc1140794e52dd;hb=f05e14ba316d67b9cdf1440d9b2129da03625a30;hp=2207960b953e67d86adc6fd7a912746c97b4ed6d;hpb=ea397f60e437a528a499288a4bdffbc626031deb;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 2207960..fbed414 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -1,25 +1,25 @@ -/* 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. - */ + /* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ /** - * SECTION:element-gstrtpbin - * @see_also: gstrtpjitterbuffer, gstrtpsession, gstrtpptdemux, gstrtpssrcdemux + * SECTION:element-rtpbin + * @see_also: rtpjitterbuffer, rtpsession, rtpptdemux, rtpssrcdemux * * RTP bin combines the functions of #GstRtpSession, #GstRtpSsrcDemux, * #GstRtpJitterBuffer and #GstRtpPtDemux in one element. It allows for multiple @@ -36,7 +36,7 @@ * the packets are released from the jitterbuffer, they will be forwarded to a * #GstRtpPtDemux element. The #GstRtpPtDemux element will demux the packets based * on the payload type and will create a unique pad recv_rtp_src_\%u_\%u_\%u on - * gstrtpbin with the session number, SSRC and payload type respectively as the pad + * rtpbin 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_\%u pad. The @@ -58,20 +58,42 @@ * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map * signal. * - * Access to the internal statistics of gstrtpbin is provided with the + * Access to the internal statistics of rtpbin is provided with the * get-internal-session property. This action signal gives access to the * RTPSession object which further provides action signals to retrieve the * internal source and other sources. * + * #GstRtpBin also has signals (#GstRtpBin::request-rtp-encoder, + * #GstRtpBin::request-rtp-decoder, #GstRtpBin::request-rtcp-encoder and + * #GstRtpBin::request-rtp-decoder) to dynamically request for RTP and RTCP encoders + * and decoders in order to support SRTP. The encoders must provide the pads + * rtp_sink_\%u and rtp_src_\%u for RTP and rtcp_sink_\%u and rtcp_src_\%u for + * RTCP. The session number will be used in the pad name. The decoders must provide + * rtp_sink and rtp_src for RTP and rtcp_sink and rtcp_src for RTCP. The decoders will + * be placed before the #GstRtpSession element, thus they must support SSRC demuxing + * internally. + * + * #GstRtpBin has signals (#GstRtpBin::request-aux-sender and + * #GstRtpBin::request-aux-receiver to dynamically request an element that can be + * used to create or merge additional RTP streams. AUX elements are needed to + * implement FEC or retransmission (such as RFC 4588). An AUX sender must have one + * sink_\%u pad that matches the sessionid in the signal and it should have 1 or + * more src_\%u pads. For each src_%\u pad, a session will be made (if needed) + * and the pad will be linked to the session send_rtp_sink pad. Each session will + * then expose its source pad as send_rtp_src_\%u on #GstRtpBin. + * An AUX receiver has 1 src_\%u pad that much match the sessionid in the signal + * and 1 or more sink_\%u pads. A session will be made for each sink_\%u pad + * when the corresponding recv_rtp_sink_\%u pad is requested on #GstRtpBin. + * * * 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-1.0 udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \ + * rtpbin ! rtptheoradepay ! theoradec ! xvimagesink + * ]| Receive RTP data from port 5000 and send to the session 0 in rtpbin. * |[ - * gst-launch gstrtpbin name=rtpbin \ - * v4l2src ! ffmpegcolorspace ! ffenc_h263 ! rtph263ppay ! rtpbin.send_rtp_sink_0 \ + * gst-launch-1.0 rtpbin name=rtpbin \ + * v4l2src ! videoconvert ! 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 \ @@ -89,7 +111,7 @@ * 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 \ + * gst-launch-1.0 -v rtpbin 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 \ @@ -110,8 +132,6 @@ * 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 @@ -123,7 +143,6 @@ #include #include -#include "gstrtpbin-marshal.h" #include "gstrtpbin.h" #include "rtpsession.h" #include "gstrtpsession.h" @@ -136,17 +155,17 @@ GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug); /* sink pads */ static GstStaticPadTemplate rtpbin_recv_rtp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%u", + GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp") + GST_STATIC_CAPS ("application/x-rtp;application/x-srtp") ); static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%u", + GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") + GST_STATIC_CAPS ("application/x-rtcp;application/x-srtcp") ); static GstStaticPadTemplate rtpbin_send_rtp_sink_template = @@ -165,22 +184,19 @@ GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%u_%u_%u", ); static GstStaticPadTemplate rtpbin_send_rtcp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%u", + GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%u", GST_PAD_SRC, GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") + GST_STATIC_CAPS ("application/x-rtcp;application/x-srtcp") ); static GstStaticPadTemplate rtpbin_send_rtp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%u", + GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp") + GST_STATIC_CAPS ("application/x-rtp;application/x-srtp") ); -#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) @@ -204,6 +220,10 @@ G_STMT_START { \ #define GST_RTP_BIN_SHUTDOWN_UNLOCK(bin) \ GST_RTP_BIN_DYN_UNLOCK (bin); \ +/* Minimum time offset to apply. This compensates for rounding errors in NTP to + * RTP timestamp conversions */ +#define MIN_TS_OFFSET (4 * GST_MSECOND) + struct _GstRtpBinPrivate { GMutex bin_lock; @@ -216,8 +236,11 @@ struct _GstRtpBinPrivate gboolean autoremove; - /* UNIX (ntp) time of last SR sync used */ - guint64 last_unix; + /* NTP time in ns of last SR sync used */ + guint64 last_ntpnstime; + + /* list of extra elements */ + GList *elements; }; /* signals and args */ @@ -227,7 +250,10 @@ enum SIGNAL_PAYLOAD_TYPE_CHANGE, SIGNAL_CLEAR_PT_MAP, SIGNAL_RESET_SYNC, + SIGNAL_GET_SESSION, SIGNAL_GET_INTERNAL_SESSION, + SIGNAL_GET_STORAGE, + SIGNAL_GET_INTERNAL_STORAGE, SIGNAL_ON_NEW_SSRC, SIGNAL_ON_SSRC_COLLISION, @@ -239,10 +265,31 @@ enum SIGNAL_ON_TIMEOUT, SIGNAL_ON_SENDER_TIMEOUT, SIGNAL_ON_NPT_STOP, + + SIGNAL_REQUEST_RTP_ENCODER, + SIGNAL_REQUEST_RTP_DECODER, + SIGNAL_REQUEST_RTCP_ENCODER, + SIGNAL_REQUEST_RTCP_DECODER, + + SIGNAL_REQUEST_FEC_DECODER, + SIGNAL_REQUEST_FEC_ENCODER, + + SIGNAL_NEW_JITTERBUFFER, + SIGNAL_NEW_STORAGE, + + SIGNAL_REQUEST_AUX_SENDER, + SIGNAL_REQUEST_AUX_RECEIVER, + + SIGNAL_ON_NEW_SENDER_SSRC, + SIGNAL_ON_SENDER_SSRC_ACTIVE, + + SIGNAL_ON_BUNDLED_SSRC, + LAST_SIGNAL }; #define DEFAULT_LATENCY_MS 200 +#define DEFAULT_DROP_ON_LATENCY FALSE #define DEFAULT_SDES NULL #define DEFAULT_DO_LOST FALSE #define DEFAULT_IGNORE_PT FALSE @@ -252,11 +299,24 @@ enum #define DEFAULT_USE_PIPELINE_CLOCK FALSE #define DEFAULT_RTCP_SYNC GST_RTP_BIN_RTCP_SYNC_ALWAYS #define DEFAULT_RTCP_SYNC_INTERVAL 0 +#define DEFAULT_DO_SYNC_EVENT FALSE +#define DEFAULT_DO_RETRANSMISSION FALSE +#define DEFAULT_RTP_PROFILE GST_RTP_PROFILE_AVP +#define DEFAULT_NTP_TIME_SOURCE GST_RTP_NTP_TIME_SOURCE_NTP +#define DEFAULT_RTCP_SYNC_SEND_TIME TRUE +#define DEFAULT_MAX_RTCP_RTP_TIME_DIFF 1000 +#define DEFAULT_MAX_DROPOUT_TIME 60000 +#define DEFAULT_MAX_MISORDER_TIME 2000 +#define DEFAULT_RFC7273_SYNC FALSE +#define DEFAULT_MAX_STREAMS G_MAXUINT +#define DEFAULT_MAX_TS_OFFSET_ADJUSTMENT G_GUINT64_CONSTANT(0) +#define DEFAULT_MAX_TS_OFFSET G_GINT64_CONSTANT(3000000000) enum { PROP_0, PROP_LATENCY, + PROP_DROP_ON_LATENCY, PROP_SDES, PROP_DO_LOST, PROP_IGNORE_PT, @@ -266,14 +326,18 @@ enum PROP_AUTOREMOVE, PROP_BUFFER_MODE, PROP_USE_PIPELINE_CLOCK, - PROP_LAST -}; - -enum -{ - GST_RTP_BIN_RTCP_SYNC_ALWAYS, - GST_RTP_BIN_RTCP_SYNC_INITIAL, - GST_RTP_BIN_RTCP_SYNC_RTP + PROP_DO_SYNC_EVENT, + PROP_DO_RETRANSMISSION, + PROP_RTP_PROFILE, + PROP_NTP_TIME_SOURCE, + PROP_RTCP_SYNC_SEND_TIME, + PROP_MAX_RTCP_RTP_TIME_DIFF, + PROP_MAX_DROPOUT_TIME, + PROP_MAX_MISORDER_TIME, + PROP_RFC7273_SYNC, + PROP_MAX_STREAMS, + PROP_MAX_TS_OFFSET_ADJUSTMENT, + PROP_MAX_TS_OFFSET, }; #define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type()) @@ -310,7 +374,15 @@ static void remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session); static void remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session); static void remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session); static void free_client (GstRtpBinClient * client, GstRtpBin * bin); -static void free_stream (GstRtpBinStream * stream); +static void free_stream (GstRtpBinStream * stream, GstRtpBin * bin); +static GstRtpBinSession *create_session (GstRtpBin * rtpbin, gint id); +static GstPad *complete_session_sink (GstRtpBin * rtpbin, + GstRtpBinSession * session); +static void +complete_session_receiver (GstRtpBin * rtpbin, GstRtpBinSession * session, + guint sessid); +static GstPad *complete_session_rtcp (GstRtpBin * rtpbin, + GstRtpBinSession * session, guint sessid); /* Manages the RTP stream for one SSRC. * @@ -336,7 +408,9 @@ struct _GstRtpBinStream gulong buffer_ptreq_sig; gulong buffer_ntpstop_sig; gint percent; - +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + gint prev_percent; +#endif /* the PT demuxer of the SSRC */ GstElement *demux; gulong demux_newpad_sig; @@ -363,6 +437,9 @@ struct _GstRtpBinStream * 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). + * + * Before the SSRC demuxer, a storage element may be inserted for the purpose + * of Forward Error Correction. */ struct _GstRtpBinSession { @@ -377,11 +454,17 @@ struct _GstRtpBinSession gulong demux_newpad_sig; gulong demux_padremoved_sig; + /* Fec support */ + GstElement *storage; + GMutex lock; /* list of GstRtpBinStream */ GSList *streams; + /* list of elements */ + GSList *elements; + /* mapping of payload type to caps */ GHashTable *ptmap; @@ -394,7 +477,6 @@ struct _GstRtpBinSession 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; @@ -523,6 +605,21 @@ on_npt_stop (GstElement * jbuf, GstRtpBinStream * stream) stream->session->id, stream->ssrc); } +static void +on_new_sender_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess) +{ + g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_NEW_SENDER_SSRC], 0, + sess->id, ssrc); +} + +static void +on_sender_ssrc_active (GstElement * session, guint32 ssrc, + GstRtpBinSession * sess) +{ + g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SENDER_SSRC_ACTIVE], + 0, sess->id, ssrc); +} + /* must be called with the SESSION lock */ static GstRtpBinStream * find_stream_by_ssrc (GstRtpBinSession * session, guint32 ssrc) @@ -543,6 +640,11 @@ ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad, GstRtpBinSession * session) { GstRtpBinStream *stream = NULL; + GstRtpBin *rtpbin; + + rtpbin = session->bin; + + GST_RTP_BIN_LOCK (rtpbin); GST_RTP_SESSION_LOCK (session); if ((stream = find_stream_by_ssrc (session, ssrc))) @@ -550,7 +652,9 @@ ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad, GST_RTP_SESSION_UNLOCK (session); if (stream) - free_stream (stream); + free_stream (stream, rtpbin); + + GST_RTP_BIN_UNLOCK (rtpbin); } /* create a session with the given id. Must be called with RTP_BIN_LOCK */ @@ -559,6 +663,7 @@ create_session (GstRtpBin * rtpbin, gint id) { GstRtpBinSession *sess; GstElement *session, *demux; + GstElement *storage = NULL; GstState target; if (!(session = gst_element_factory_make ("rtpsession", NULL))) @@ -567,20 +672,40 @@ create_session (GstRtpBin * rtpbin, gint id) if (!(demux = gst_element_factory_make ("rtpssrcdemux", NULL))) goto no_demux; + if (!(storage = gst_element_factory_make ("rtpstorage", NULL))) + goto no_storage; + + /* need to sink the storage or otherwise signal handlers from bindings will + * take ownership of it and we don't own it anymore */ + gst_object_ref_sink (storage); + g_signal_emit (rtpbin, gst_rtp_bin_signals[SIGNAL_NEW_STORAGE], 0, storage, + id); + sess = g_new0 (GstRtpBinSession, 1); g_mutex_init (&sess->lock); sess->id = id; sess->bin = rtpbin; sess->session = session; sess->demux = demux; + sess->storage = storage; + sess->ptmap = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) gst_caps_unref); rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess); /* configure SDES items */ GST_OBJECT_LOCK (rtpbin); - g_object_set (session, "sdes", rtpbin->sdes, "use-pipeline-clock", - rtpbin->use_pipeline_clock, NULL); + g_object_set (session, "sdes", rtpbin->sdes, "rtp-profile", + rtpbin->rtp_profile, "rtcp-sync-send-time", rtpbin->rtcp_sync_send_time, + NULL); + if (rtpbin->use_pipeline_clock) + g_object_set (session, "use-pipeline-clock", rtpbin->use_pipeline_clock, + NULL); + else + g_object_set (session, "ntp-time-source", rtpbin->ntp_time_source, NULL); + + g_object_set (session, "max-dropout-time", rtpbin->max_dropout_time, + "max-misorder-time", rtpbin->max_misorder_time, NULL); GST_OBJECT_UNLOCK (rtpbin); /* provide clock_rate to the session manager when needed */ @@ -604,9 +729,18 @@ create_session (GstRtpBin * rtpbin, gint id) g_signal_connect (sess->session, "on-timeout", (GCallback) on_timeout, sess); g_signal_connect (sess->session, "on-sender-timeout", (GCallback) on_sender_timeout, sess); + g_signal_connect (sess->session, "on-new-sender-ssrc", + (GCallback) on_new_sender_ssrc, sess); + g_signal_connect (sess->session, "on-sender-ssrc-active", + (GCallback) on_sender_ssrc_active, sess); gst_bin_add (GST_BIN_CAST (rtpbin), session); gst_bin_add (GST_BIN_CAST (rtpbin), demux); + gst_bin_add (GST_BIN_CAST (rtpbin), storage); + + /* unref the storage again, the bin has a reference now and + * we don't need it anymore */ + gst_object_unref (storage); GST_OBJECT_LOCK (rtpbin); target = GST_STATE_TARGET (rtpbin); @@ -615,36 +749,97 @@ create_session (GstRtpBin * rtpbin, gint id) /* change state only to what's needed */ gst_element_set_state (demux, target); gst_element_set_state (session, target); + gst_element_set_state (storage, target); return sess; /* ERRORS */ no_session: { - g_warning ("rtpbin: could not create gstrtpsession element"); + g_warning ("rtpbin: could not create rtpsession element"); return NULL; } no_demux: { gst_object_unref (session); - g_warning ("rtpbin: could not create gstrtpssrcdemux element"); + g_warning ("rtpbin: could not create rtpssrcdemux element"); + return NULL; + } +no_storage: + { + gst_object_unref (session); + gst_object_unref (demux); + g_warning ("rtpbin: could not create rtpstorage element"); return NULL; } } +static gboolean +bin_manage_element (GstRtpBin * bin, GstElement * element) +{ + GstRtpBinPrivate *priv = bin->priv; + + if (g_list_find (priv->elements, element)) { + GST_DEBUG_OBJECT (bin, "requested element %p already in bin", element); + } else { + GST_DEBUG_OBJECT (bin, "adding requested element %p", element); + + if (g_object_is_floating (element)) + element = gst_object_ref_sink (element); + + if (!gst_bin_add (GST_BIN_CAST (bin), element)) + goto add_failed; + if (!gst_element_sync_state_with_parent (element)) + GST_WARNING_OBJECT (bin, "unable to sync element state with rtpbin"); + } + /* we add the element multiple times, each we need an equal number of + * removes to really remove the element from the bin */ + priv->elements = g_list_prepend (priv->elements, element); + + return TRUE; + + /* ERRORS */ +add_failed: + { + GST_WARNING_OBJECT (bin, "unable to add element"); + gst_object_unref (element); + return FALSE; + } +} + +static void +remove_bin_element (GstElement * element, GstRtpBin * bin) +{ + GstRtpBinPrivate *priv = bin->priv; + GList *find; + + find = g_list_find (priv->elements, element); + if (find) { + priv->elements = g_list_delete_link (priv->elements, find); + + if (!g_list_find (priv->elements, element)) { + gst_element_set_locked_state (element, TRUE); + gst_bin_remove (GST_BIN_CAST (bin), element); + gst_element_set_state (element, GST_STATE_NULL); + } + + gst_object_unref (element); + } +} + /* called with RTP_BIN_LOCK */ static void free_session (GstRtpBinSession * sess, GstRtpBin * bin) { - GSList *client_walk; - GST_DEBUG_OBJECT (bin, "freeing session %p", sess); gst_element_set_locked_state (sess->demux, TRUE); gst_element_set_locked_state (sess->session, TRUE); + gst_element_set_locked_state (sess->storage, TRUE); gst_element_set_state (sess->demux, GST_STATE_NULL); gst_element_set_state (sess->session, GST_STATE_NULL); + gst_element_set_state (sess->storage, GST_STATE_NULL); remove_recv_rtp (bin, sess); remove_recv_rtcp (bin, sess); @@ -653,41 +848,12 @@ free_session (GstRtpBinSession * sess, GstRtpBin * bin) gst_bin_remove (GST_BIN_CAST (bin), sess->session); gst_bin_remove (GST_BIN_CAST (bin), sess->demux); + gst_bin_remove (GST_BIN_CAST (bin), sess->storage); - /* remove any references in bin->clients to the streams in sess->streams */ - client_walk = bin->clients; - while (client_walk) { - GSList *client_node = client_walk; - GstRtpBinClient *client = (GstRtpBinClient *) client_node->data; - GSList *stream_walk = client->streams; - - while (stream_walk) { - GSList *stream_node = stream_walk; - GstRtpBinStream *stream = (GstRtpBinStream *) stream_node->data; - GSList *inner_walk; - - stream_walk = g_slist_next (stream_walk); - - for (inner_walk = sess->streams; inner_walk; - inner_walk = g_slist_next (inner_walk)) { - if ((GstRtpBinStream *) inner_walk->data == stream) { - client->streams = g_slist_delete_link (client->streams, stream_node); - --client->nstreams; - break; - } - } - } - client_walk = g_slist_next (client_walk); - - g_assert ((client->streams && client->nstreams > 0) || (!client->streams - && client->streams == 0)); - if (client->nstreams == 0) { - free_client (client, bin); - bin->clients = g_slist_delete_link (bin->clients, client_node); - } - } + g_slist_foreach (sess->elements, (GFunc) remove_bin_element, bin); + g_slist_free (sess->elements); - g_slist_foreach (sess->streams, (GFunc) free_stream, NULL); + g_slist_foreach (sess->streams, (GFunc) free_stream, bin); g_slist_free (sess->streams); g_mutex_clear (&sess->lock); @@ -705,7 +871,7 @@ get_pt_map (GstRtpBinSession * session, guint pt) GValue ret = { 0 }; GValue args[3] = { {0}, {0}, {0} }; - GST_DEBUG ("searching pt %d in cache", pt); + GST_DEBUG ("searching pt %u in cache", pt); GST_RTP_SESSION_LOCK (session); @@ -718,7 +884,7 @@ get_pt_map (GstRtpBinSession * session, guint pt) bin = session->bin; - GST_DEBUG ("emiting signal for pt %d in session %d", pt, session->id); + GST_DEBUG ("emiting signal for pt %u in session %u", pt, session->id); /* not in cache, send signal to request caps */ g_value_init (&args[0], GST_TYPE_ELEMENT); @@ -754,7 +920,7 @@ get_pt_map (GstRtpBinSession * session, guint pt) if (!caps) goto no_caps; - GST_DEBUG ("caching pt %d as %" GST_PTR_FORMAT, pt, caps); + GST_DEBUG ("caching pt %u as %" GST_PTR_FORMAT, pt, caps); /* store in cache, take additional ref */ g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt), @@ -838,6 +1004,23 @@ gst_rtp_bin_clear_pt_map (GstRtpBin * bin) gst_rtp_bin_reset_sync (bin); } +static GstElement * +gst_rtp_bin_get_session (GstRtpBin * bin, guint session_id) +{ + GstRtpBinSession *session; + GstElement *ret = NULL; + + GST_RTP_BIN_LOCK (bin); + GST_DEBUG_OBJECT (bin, "retrieving GstRtpSession, index: %u", session_id); + session = find_session_by_id (bin, (gint) session_id); + if (session) { + ret = gst_object_ref (session->session); + } + GST_RTP_BIN_UNLOCK (bin); + + return ret; +} + static RTPSession * gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id) { @@ -845,7 +1028,7 @@ gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id) GstRtpBinSession *session; GST_RTP_BIN_LOCK (bin); - GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %d", + GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %u", session_id); session = find_session_by_id (bin, (gint) session_id); if (session) { @@ -857,6 +1040,57 @@ gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id) return internal_session; } +static GstElement * +gst_rtp_bin_get_storage (GstRtpBin * bin, guint session_id) +{ + GstRtpBinSession *session; + GstElement *res = NULL; + + GST_RTP_BIN_LOCK (bin); + GST_DEBUG_OBJECT (bin, "retrieving internal storage object, index: %u", + session_id); + session = find_session_by_id (bin, (gint) session_id); + if (session && session->storage) { + res = gst_object_ref (session->storage); + } + GST_RTP_BIN_UNLOCK (bin); + + return res; +} + +static GObject * +gst_rtp_bin_get_internal_storage (GstRtpBin * bin, guint session_id) +{ + GObject *internal_storage = NULL; + GstRtpBinSession *session; + + GST_RTP_BIN_LOCK (bin); + GST_DEBUG_OBJECT (bin, "retrieving internal storage object, index: %u", + session_id); + session = find_session_by_id (bin, (gint) session_id); + if (session && session->storage) { + g_object_get (session->storage, "internal-storage", &internal_storage, + NULL); + } + GST_RTP_BIN_UNLOCK (bin); + + return internal_storage; +} + +static GstElement * +gst_rtp_bin_request_encoder (GstRtpBin * bin, guint session_id) +{ + GST_DEBUG_OBJECT (bin, "return NULL encoder"); + return NULL; +} + +static GstElement * +gst_rtp_bin_request_decoder (GstRtpBin * bin, guint session_id) +{ + GST_DEBUG_OBJECT (bin, "return NULL decoder"); + return NULL; +} + static void gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin, const gchar * name, const GValue * value) @@ -878,6 +1112,21 @@ gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin, GST_RTP_BIN_UNLOCK (bin); } +static void +gst_rtp_bin_propagate_property_to_session (GstRtpBin * bin, + const gchar * name, const GValue * value) +{ + GSList *sessions; + + GST_RTP_BIN_LOCK (bin); + for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) { + GstRtpBinSession *sess = (GstRtpBinSession *) sessions->data; + + g_object_set_property (G_OBJECT (sess->session), name, value); + } + 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) @@ -924,7 +1173,7 @@ static void get_current_times (GstRtpBin * bin, GstClockTime * running_time, guint64 * ntpnstime) { - guint64 ntpns; + guint64 ntpns = -1; GstClock *clock; GstClockTime base_time, rt, clock_time; @@ -934,24 +1183,42 @@ get_current_times (GstRtpBin * bin, GstClockTime * running_time, gst_object_ref (clock); GST_OBJECT_UNLOCK (bin); + /* get current clock time and convert to running time */ clock_time = gst_clock_get_time (clock); + rt = clock_time - base_time; if (bin->use_pipeline_clock) { - ntpns = clock_time; + ntpns = rt; + /* add constant to convert from 1970 based time to 1900 based time */ + ntpns += (2208988800LL * GST_SECOND); } else { - GTimeVal current; - - /* get current NTP time */ - g_get_current_time (¤t); - ntpns = GST_TIMEVAL_TO_TIME (current); + switch (bin->ntp_time_source) { + case GST_RTP_NTP_TIME_SOURCE_NTP: + case GST_RTP_NTP_TIME_SOURCE_UNIX:{ + GTimeVal current; + + /* get current NTP time */ + g_get_current_time (¤t); + ntpns = GST_TIMEVAL_TO_TIME (current); + + /* add constant to convert from 1970 based time to 1900 based time */ + if (bin->ntp_time_source == GST_RTP_NTP_TIME_SOURCE_NTP) + ntpns += (2208988800LL * GST_SECOND); + break; + } + case GST_RTP_NTP_TIME_SOURCE_RUNNING_TIME: + ntpns = rt; + break; + case GST_RTP_NTP_TIME_SOURCE_CLOCK_TIME: + ntpns = clock_time; + break; + default: + ntpns = -1; /* Fix uninited compiler warning */ + g_assert_not_reached (); + break; + } } - /* add constant to convert from 1970 based time to 1900 based time */ - ntpns += (2208988800LL * GST_SECOND); - - /* get current clock time and convert to running time */ - rt = clock_time - base_time; - gst_object_unref (clock); } else { GST_OBJECT_UNLOCK (bin); @@ -966,7 +1233,8 @@ get_current_times (GstRtpBin * bin, GstClockTime * running_time, static void stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream, - gint64 ts_offset) + gint64 ts_offset, gint64 max_ts_offset, gint64 min_ts_offset, + gboolean allow_positive_ts_offset) { gint64 prev_ts_offset; @@ -982,23 +1250,50 @@ stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream, "ts-offset %" G_GINT64_FORMAT ", prev %" G_GINT64_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 (ABS (diff) > 4 * GST_MSECOND) { - if (ABS (diff) < (3 * GST_SECOND)) { - g_object_set (stream->buffer, "ts-offset", ts_offset, NULL); - } else { - GST_WARNING_OBJECT (bin, "offset unusually large, ignoring"); - } - } else { + /* ignore minor offsets */ + if (ABS (diff) < min_ts_offset) { GST_DEBUG_OBJECT (bin, "offset too small, ignoring"); + return; } + + /* sanity check offset */ + if (max_ts_offset > 0) { + if (ts_offset > 0 && !allow_positive_ts_offset) { + GST_DEBUG_OBJECT (bin, + "offset is positive (clocks are out of sync), ignoring"); + return; + } + if (ABS (ts_offset) > max_ts_offset) { + GST_DEBUG_OBJECT (bin, "offset too large, ignoring"); + return; + } + } + + g_object_set (stream->buffer, "ts-offset", ts_offset, NULL); } GST_DEBUG_OBJECT (bin, "stream SSRC %08x, delta %" G_GINT64_FORMAT, stream->ssrc, ts_offset); } +static void +gst_rtp_bin_send_sync_event (GstRtpBinStream * stream) +{ + if (stream->bin->send_sync_event) { + GstEvent *event; + GstPad *srcpad; + + GST_DEBUG_OBJECT (stream->bin, + "sending GstRTCPSRReceived event downstream"); + + event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, + gst_structure_new_empty ("GstRTCPSRReceived")); + + srcpad = gst_element_get_static_pad (stream->buffer, "src"); + gst_pad_push_event (srcpad, event); + gst_object_unref (srcpad); + } +} + /* 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 */ @@ -1011,12 +1306,8 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, GstRtpBinClient *client; gboolean created; GSList *walk; - guint64 local_rt; - guint64 local_rtp; - GstClockTime running_time; + GstClockTime running_time, running_time_rtp; guint64 ntpnstime; - gint64 ntpdiff, rtdiff; - guint64 last_unix; /* first find or create the CNAME */ client = get_client (bin, len, data, &created); @@ -1058,51 +1349,59 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, * local rtptime. The local rtp time is used to construct timestamps on the * buffers so we will calculate what running_time corresponds to the RTP * timestamp in the SR packet. */ - local_rtp = last_extrtptime - base_rtptime; + running_time_rtp = last_extrtptime - base_rtptime; GST_DEBUG_OBJECT (bin, "base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d, " "clock-base %" G_GINT64_FORMAT, base_rtptime, - last_extrtptime, local_rtp, clock_rate, rtp_clock_base); + last_extrtptime, running_time_rtp, clock_rate, rtp_clock_base); /* calculate local RTP 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. Note that the base_time also * contains the drift between sender and receiver. */ - local_rt = gst_util_uint64_scale_int (local_rtp, GST_SECOND, clock_rate); - local_rt += base_time; + running_time = + gst_util_uint64_scale_int (running_time_rtp, GST_SECOND, clock_rate); + running_time += base_time; - /* convert ntptime to unix time since 1900 */ - last_unix = gst_util_uint64_scale (ntptime, GST_SECOND, + /* convert ntptime to nanoseconds */ + ntpnstime = gst_util_uint64_scale (ntptime, GST_SECOND, (G_GINT64_CONSTANT (1) << 32)); stream->have_sync = TRUE; GST_DEBUG_OBJECT (bin, - "local UNIX %" G_GUINT64_FORMAT ", remote UNIX %" G_GUINT64_FORMAT, - local_rt, last_unix); + "SR RTP running time %" G_GUINT64_FORMAT ", SR NTP %" G_GUINT64_FORMAT, + running_time, ntpnstime); /* recalc inter stream playout offset, but only if there is more than one * stream or we're doing NTP sync. */ if (bin->ntp_sync) { + gint64 ntpdiff, rtdiff; + guint64 local_ntpnstime; + GstClockTime local_running_time; + /* For NTP sync we need to first get a snapshot of running_time and NTP * time. We know at what running_time we play a certain RTP time, we also * calculated when we would play the RTP time in the SR packet. Now we need * to know how the running_time and the NTP time relate to eachother. */ - get_current_times (bin, &running_time, &ntpnstime); + get_current_times (bin, &local_running_time, &local_ntpnstime); /* see how far away the NTP time is. This is the difference between the * current NTP time and the NTP time in the last SR packet. */ - ntpdiff = ntpnstime - last_unix; + ntpdiff = local_ntpnstime - ntpnstime; /* see how far away the running_time is. This is the difference between the * current running_time and the running_time of the RTP timestamp in the * last SR packet. */ - rtdiff = running_time - local_rt; + rtdiff = local_running_time - running_time; GST_DEBUG_OBJECT (bin, - "NTP time %" G_GUINT64_FORMAT ", last unix %" G_GUINT64_FORMAT, - ntpnstime, last_unix); + "local NTP time %" G_GUINT64_FORMAT ", SR NTP time %" G_GUINT64_FORMAT, + local_ntpnstime, ntpnstime); + GST_DEBUG_OBJECT (bin, + "local running time %" G_GUINT64_FORMAT ", SR RTP running time %" + G_GUINT64_FORMAT, local_running_time, running_time); GST_DEBUG_OBJECT (bin, "NTP diff %" G_GINT64_FORMAT ", RT diff %" G_GINT64_FORMAT, ntpdiff, rtdiff); @@ -1110,18 +1409,19 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, /* combine to get the final diff to apply to the running_time */ stream->rt_delta = rtdiff - ntpdiff; - stream_set_ts_offset (bin, stream, stream->rt_delta); + stream_set_ts_offset (bin, stream, stream->rt_delta, bin->max_ts_offset, + 0, FALSE); } else { gint64 min, rtp_min, clock_base = stream->clock_base; gboolean all_sync, use_rtp; gboolean rtcp_sync = g_atomic_int_get (&bin->rtcp_sync); - /* calculate delta between server and receiver. last_unix is created by + /* calculate delta between server and receiver. ntpnstime 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. The * difference in itself doesn't mean much but we can combine the delta of * multiple streams to create a stream specific offset. */ - stream->rt_delta = last_unix - local_rt; + stream->rt_delta = ntpnstime - running_time; /* calculate the min of all deltas, ignoring streams that did not yet have a * valid rt_delta because we did not yet receive an SR packet for those @@ -1235,14 +1535,14 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, } /* bail out if we adjusted recently enough */ - if (all_sync && (last_unix - bin->priv->last_unix) < + if (all_sync && (ntpnstime - bin->priv->last_ntpnstime) < bin->rtcp_sync_interval * GST_MSECOND) { GST_DEBUG_OBJECT (bin, "discarding RTCP sender packet for sync; " "previous sender info too recent " - "(previous UNIX %" G_GUINT64_FORMAT ")", bin->priv->last_unix); + "(previous NTP %" G_GUINT64_FORMAT ")", bin->priv->last_ntpnstime); return; } - bin->priv->last_unix = last_unix; + bin->priv->last_ntpnstime = ntpnstime; /* calculate offsets for each stream */ for (walk = client->streams; walk; walk = g_slist_next (walk)) { @@ -1262,9 +1562,12 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, else ts_offset = ostream->rt_delta - min; - stream_set_ts_offset (bin, ostream, ts_offset); + stream_set_ts_offset (bin, ostream, ts_offset, bin->max_ts_offset, + MIN_TS_OFFSET, TRUE); } } + gst_rtp_bin_send_sync_event (stream); + return; } @@ -1389,20 +1692,30 @@ static GstRtpBinStream * create_stream (GstRtpBinSession * session, guint32 ssrc) { GstElement *buffer, *demux = NULL; +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + GstElement *queue2 = NULL; +#endif GstRtpBinStream *stream; GstRtpBin *rtpbin; GstState target; rtpbin = session->bin; + if (g_slist_length (session->streams) >= rtpbin->max_streams) + goto max_streams; + if (!(buffer = gst_element_factory_make ("rtpjitterbuffer", NULL))) goto no_jitterbuffer; - if (!rtpbin->ignore_pt) + if (!rtpbin->ignore_pt) { if (!(demux = gst_element_factory_make ("rtpptdemux", NULL))) goto no_demux; - - + } +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (session->bin->buffer_mode == RTP_JITTER_BUFFER_MODE_SLAVE) + if (!(queue2 = gst_element_factory_make ("queue2", NULL))) + goto no_queue2; +#endif stream = g_new0 (GstRtpBinStream, 1); stream->ssrc = ssrc; stream->bin = rtpbin; @@ -1414,6 +1727,9 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) stream->rt_delta = 0; stream->rtp_delta = 0; stream->percent = 100; +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + stream->prev_percent = 0; +#endif stream->clock_base = -100 * GST_SECOND; session->streams = g_slist_prepend (session->streams, stream); @@ -1428,17 +1744,65 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) /* configure latency and packet lost */ g_object_set (buffer, "latency", rtpbin->latency_ms, NULL); + g_object_set (buffer, "drop-on-latency", rtpbin->drop_on_latency, NULL); g_object_set (buffer, "do-lost", rtpbin->do_lost, NULL); g_object_set (buffer, "mode", rtpbin->buffer_mode, NULL); + g_object_set (buffer, "do-retransmission", rtpbin->do_retransmission, NULL); + g_object_set (buffer, "max-rtcp-rtp-time-diff", + rtpbin->max_rtcp_rtp_time_diff, NULL); + g_object_set (buffer, "max-dropout-time", rtpbin->max_dropout_time, + "max-misorder-time", rtpbin->max_misorder_time, NULL); + g_object_set (buffer, "rfc7273-sync", rtpbin->rfc7273_sync, NULL); + g_object_set (buffer, "max-ts-offset-adjustment", + rtpbin->max_ts_offset_adjustment, NULL); + +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + /* configure queue2 to use live buffering */ + if (queue2) { + g_object_set_data (G_OBJECT (queue2), "GstRTPBin.stream", stream); + g_object_set (queue2, "use-buffering", TRUE, NULL); + g_object_set (queue2, "buffer-mode", GST_BUFFERING_LIVE, NULL); + } +#endif + /* need to sink the jitterbufer or otherwise signal handlers from bindings will + * take ownership of it and we don't own it anymore */ + gst_object_ref_sink (buffer); + g_signal_emit (rtpbin, gst_rtp_bin_signals[SIGNAL_NEW_JITTERBUFFER], 0, + buffer, session->id, ssrc); if (!rtpbin->ignore_pt) gst_bin_add (GST_BIN_CAST (rtpbin), demux); + +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (queue2) + gst_bin_add (GST_BIN_CAST (rtpbin), queue2); +#endif + gst_bin_add (GST_BIN_CAST (rtpbin), buffer); + /* unref the jitterbuffer again, the bin has a reference now and + * we don't need it anymore */ + gst_object_unref (buffer); + /* link stuff */ +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (queue2) { + gst_element_link_pads_full (buffer, "src", queue2, "sink", + GST_PAD_LINK_CHECK_NOTHING); + if (demux) { + gst_element_link_pads_full (queue2, "src", demux, "sink", + GST_PAD_LINK_CHECK_NOTHING); + } + } else if (demux) { + gst_element_link_pads_full (buffer, "src", demux, "sink", + GST_PAD_LINK_CHECK_NOTHING); + } +#else if (demux) - gst_element_link (buffer, demux); + gst_element_link_pads_full (buffer, "src", demux, "sink", + GST_PAD_LINK_CHECK_NOTHING); +#endif if (rtpbin->buffering) { guint64 last_out; @@ -1458,28 +1822,49 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) gst_element_set_state (buffer, target); +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (queue2) + gst_element_set_state (queue2, target); +#endif + return stream; /* ERRORS */ +max_streams: + { + GST_WARNING_OBJECT (rtpbin, "stream exeeds maximum (%d)", + rtpbin->max_streams); + return NULL; + } no_jitterbuffer: { - g_warning ("rtpbin: could not create gstrtpjitterbuffer element"); + g_warning ("rtpbin: could not create rtpjitterbuffer element"); return NULL; } no_demux: { gst_object_unref (buffer); - g_warning ("rtpbin: could not create gstrtpptdemux element"); + g_warning ("rtpbin: could not create rtpptdemux element"); return NULL; } +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION +no_queue2: + { + gst_object_unref (buffer); + gst_object_unref (demux); + g_warning ("rtpbin: could not create queue2 element"); + return NULL; + } +#endif } +/* called with RTP_BIN_LOCK */ static void -free_stream (GstRtpBinStream * stream) +free_stream (GstRtpBinStream * stream, GstRtpBin * bin) { - GstRtpBinSession *session; + GSList *clients, *next_client; - session = stream->session; + GST_DEBUG_OBJECT (bin, "freeing stream %p", stream); if (stream->demux) { g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig); @@ -1503,10 +1888,33 @@ free_stream (GstRtpBinStream * stream) if (stream->demux) 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 (bin), stream->buffer); if (stream->demux) - gst_bin_remove (GST_BIN_CAST (session->bin), stream->demux); + gst_bin_remove (GST_BIN_CAST (bin), stream->demux); + + for (clients = bin->clients; clients; clients = next_client) { + GstRtpBinClient *client = (GstRtpBinClient *) clients->data; + GSList *streams, *next_stream; + + next_client = g_slist_next (clients); + + for (streams = client->streams; streams; streams = next_stream) { + GstRtpBinStream *ostream = (GstRtpBinStream *) streams->data; + + next_stream = g_slist_next (streams); + if (ostream == stream) { + client->streams = g_slist_delete_link (client->streams, streams); + /* If this was the last stream belonging to this client, + * clean up the client. */ + if (--client->nstreams == 0) { + bin->clients = g_slist_delete_link (bin->clients, clients); + free_client (client, bin); + break; + } + } + } + } g_free (stream); } @@ -1527,7 +1935,39 @@ static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad); static void gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message); #define gst_rtp_bin_parent_class parent_class -G_DEFINE_TYPE (GstRtpBin, gst_rtp_bin, GST_TYPE_BIN); +G_DEFINE_TYPE_WITH_PRIVATE (GstRtpBin, gst_rtp_bin, GST_TYPE_BIN); + +static gboolean +_gst_element_accumulator (GSignalInvocationHint * ihint, + GValue * return_accu, const GValue * handler_return, gpointer dummy) +{ + GstElement *element; + + element = g_value_get_object (handler_return); + GST_DEBUG ("got element %" GST_PTR_FORMAT, element); + + if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) + g_value_set_object (return_accu, element); + + /* stop emission if we have an element */ + return (element == NULL); +} + +static gboolean +_gst_caps_accumulator (GSignalInvocationHint * ihint, + GValue * return_accu, const GValue * handler_return, gpointer dummy) +{ + GstCaps *caps; + + caps = g_value_get_boxed (handler_return); + GST_DEBUG ("got caps %" GST_PTR_FORMAT, caps); + + if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) + g_value_set_boxed (return_accu, caps); + + /* stop emission if we have a caps */ + return (caps == NULL); +} static void gst_rtp_bin_class_init (GstRtpBinClass * klass) @@ -1540,8 +1980,6 @@ gst_rtp_bin_class_init (GstRtpBinClass * 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; @@ -1553,6 +1991,12 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) G_MAXUINT, DEFAULT_LATENCY_MS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + 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 | G_PARAM_STATIC_STRINGS)); + /** * GstRtpBin::request-pt-map: * @rtpbin: the object which received the signal @@ -1564,8 +2008,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + _gst_caps_accumulator, NULL, g_cclosure_marshal_generic, GST_TYPE_CAPS, + 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::payload-type-change: @@ -1574,14 +2018,12 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) * @pt: the pt * * Signal that the current payload type changed to @pt in @session. - * - * Since: 0.10.17 */ gst_rtp_bin_signals[SIGNAL_PAYLOAD_TYPE_CHANGE] = g_signal_new ("payload-type-change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, payload_type_change), - NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, - G_TYPE_UINT, G_TYPE_UINT); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::clear-pt-map: @@ -1610,6 +2052,21 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 0, G_TYPE_NONE); /** + * GstRtpBin::get-session: + * @rtpbin: the object which received the signal + * @id: the session id + * + * Request the related GstRtpSession as #GstElement related with session @id. + * + * Since: 1.8 + */ + gst_rtp_bin_signals[SIGNAL_GET_SESSION] = + g_signal_new ("get-session", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + get_session), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** * GstRtpBin::get-internal-session: * @rtpbin: the object which received the signal * @id: the session id @@ -1619,26 +2076,56 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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, + get_internal_session), NULL, NULL, g_cclosure_marshal_generic, RTP_TYPE_SESSION, 1, G_TYPE_UINT); /** - * GstRtpBin::on-new-ssrc: + * GstRtpBin::get-internal-storage: * @rtpbin: the object which received the signal - * @session: the session - * @ssrc: the SSRC + * @id: the session id * - * Notify of a new SSRC that entered @session. + * Request the internal RTPStorage object as #GObject in session @id. + * + * Since: 1.14 */ - 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); + gst_rtp_bin_signals[SIGNAL_GET_INTERNAL_STORAGE] = + g_signal_new ("get-internal-storage", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + get_internal_storage), NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_OBJECT, 1, G_TYPE_UINT); + /** - * GstRtpBin::on-ssrc-collision: + * GstRtpBin::get-storage: * @rtpbin: the object which received the signal - * @session: the session + * @id: the session id + * + * Request the RTPStorage element as #GObject in session @id. + * + * Since: 1.16 + */ + gst_rtp_bin_signals[SIGNAL_GET_STORAGE] = + g_signal_new ("get-storage", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + get_storage), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_ELEMENT, 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, g_cclosure_marshal_generic, 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 @@ -1646,8 +2133,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-ssrc-validated: * @rtpbin: the object which received the signal @@ -1659,8 +2146,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-ssrc-active: * @rtpbin: the object which received the signal @@ -1672,8 +2159,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-ssrc-sdes: * @rtpbin: the object which received the signal @@ -1685,8 +2172,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-bye-ssrc: @@ -1699,8 +2186,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-bye-timeout: * @rtpbin: the object which received the signal @@ -1712,8 +2199,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-timeout: * @rtpbin: the object which received the signal @@ -1725,8 +2212,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-sender-timeout: * @rtpbin: the object which received the signal @@ -1738,8 +2225,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::on-npt-stop: @@ -1752,8 +2239,218 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); + + /** + * GstRtpBin::request-rtp-encoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTP encoder element for the given @session. The encoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTP_ENCODER] = + g_signal_new ("request-rtp-encoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtp_encoder), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-rtp-decoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTP decoder element for the given @session. The decoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTP_DECODER] = + g_signal_new ("request-rtp-decoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtp_decoder), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-rtcp-encoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTCP encoder element for the given @session. The encoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTCP_ENCODER] = + g_signal_new ("request-rtcp-encoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtcp_encoder), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-rtcp-decoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTCP decoder element for the given @session. The decoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTCP_DECODER] = + g_signal_new ("request-rtcp-decoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtcp_decoder), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::new-jitterbuffer: + * @rtpbin: the object which received the signal + * @jitterbuffer: the new jitterbuffer + * @session: the session + * @ssrc: the SSRC + * + * Notify that a new @jitterbuffer was created for @session and @ssrc. + * This signal can, for example, be used to configure @jitterbuffer. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_NEW_JITTERBUFFER] = + g_signal_new ("new-jitterbuffer", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + new_jitterbuffer), NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_UINT, G_TYPE_UINT); + + /** + * GstRtpBin::new-storage: + * @rtpbin: the object which received the signal + * @storage: the new storage + * @session: the session + * + * Notify that a new @storage was created for @session. + * This signal can, for example, be used to configure @storage. + * + * Since: 1.14 + */ + gst_rtp_bin_signals[SIGNAL_NEW_STORAGE] = + g_signal_new ("new-storage", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + new_storage), NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 2, GST_TYPE_ELEMENT, G_TYPE_UINT); + + /** + * GstRtpBin::request-aux-sender: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an AUX sender element for the given @session. The AUX + * element will be added to the bin. + * + * If no handler is connected, no AUX element will be used. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_AUX_SENDER] = + g_signal_new ("request-aux-sender", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_aux_sender), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-aux-receiver: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an AUX receiver element for the given @session. The AUX + * element will be added to the bin. + * + * If no handler is connected, no AUX element will be used. + * + * Since: 1.4 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_AUX_RECEIVER] = + g_signal_new ("request-aux-receiver", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_aux_receiver), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-fec-decoder: + * @rtpbin: the object which received the signal + * @session: the session index + * + * Request a FEC decoder element for the given @session. The element + * will be added to the bin after the pt demuxer. + * + * If no handler is connected, no FEC decoder will be used. + * + * Since: 1.14 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_FEC_DECODER] = + g_signal_new ("request-fec-decoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_fec_decoder), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-fec-encoder: + * @rtpbin: the object which received the signal + * @session: the session index + * + * Request a FEC encoder element for the given @session. The element + * will be added to the bin after the RTPSession. + * + * If no handler is connected, no FEC encoder will be used. + * + * Since: 1.14 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_FEC_ENCODER] = + g_signal_new ("request-fec-encoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_fec_encoder), _gst_element_accumulator, NULL, + g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::on-new-sender-ssrc: + * @rtpbin: the object which received the signal + * @session: the session + * @ssrc: the sender SSRC + * + * Notify of a new sender SSRC that entered @session. + * + * Since: 1.8 + */ + gst_rtp_bin_signals[SIGNAL_ON_NEW_SENDER_SSRC] = + g_signal_new ("on-new-sender-ssrc", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_new_sender_ssrc), + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, + G_TYPE_UINT); + /** + * GstRtpBin::on-sender-ssrc-active: + * @rtpbin: the object which received the signal + * @session: the session + * @ssrc: the sender SSRC + * + * Notify of a sender SSRC that is active, i.e., sending RTCP. + * + * Since: 1.8 + */ + gst_rtp_bin_signals[SIGNAL_ON_SENDER_SSRC_ACTIVE] = + g_signal_new ("on-sender-ssrc-active", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + on_sender_ssrc_active), NULL, NULL, g_cclosure_marshal_generic, + 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", @@ -1777,28 +2474,27 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) g_object_class_install_property (gobject_class, PROP_USE_PIPELINE_CLOCK, g_param_spec_boolean ("use-pipeline-clock", "Use pipeline clock", - "Use the pipeline clock to set the NTP time in the RTCP SR messages", + "Use the pipeline running-time to set the NTP time in the RTCP SR messages " + "(DEPRECATED: Use ntp-time-source property)", DEFAULT_USE_PIPELINE_CLOCK, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED)); /** - * GstRtpBin::buffer-mode: + * GstRtpBin:buffer-mode: * * Control the buffering and timestamping mode used by the jitterbuffer. - * - * Since: 0.10.17 */ g_object_class_install_property (gobject_class, PROP_BUFFER_MODE, g_param_spec_enum ("buffer-mode", "Buffer Mode", "Control the buffering algorithm in use", RTP_TYPE_JITTER_BUFFER_MODE, DEFAULT_BUFFER_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstRtpBin::ntp-sync: - * - * Synchronize received streams to the NTP clock. When the NTP clock is shared - * between the receivers and the senders (such as when using ntpd) this option - * can be used to synchronize receivers on multiple machines. + * GstRtpBin:ntp-sync: * - * Since: 0.10.21 + * Set the NTP time from the sender reports as the running-time on the + * buffers. When both the sender and receiver have sychronized + * running-time, i.e. when the clock and base-time is shared + * between the receivers and the and the senders, this option can be + * used to synchronize receivers on multiple machines. */ g_object_class_install_property (gobject_class, PROP_NTP_SYNC, g_param_spec_boolean ("ntp-sync", "Sync on NTP clock", @@ -1806,12 +2502,10 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstRtpBin::rtcp-sync: + * GstRtpBin:rtcp-sync: * * If not synchronizing (directly) to the NTP clock, determines how to sync * the various streams. - * - * Since: 0.10.31 */ g_object_class_install_property (gobject_class, PROP_RTCP_SYNC, g_param_spec_enum ("rtcp-sync", "RTCP Sync", @@ -1819,11 +2513,9 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) DEFAULT_RTCP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstRtpBin::rtcp-sync-interval: + * GstRtpBin:rtcp-sync-interval: * * Determines how often to sync streams using RTCP data. - * - * Since: 0.10.31 */ g_object_class_install_property (gobject_class, PROP_RTCP_SYNC_INTERVAL, g_param_spec_uint ("rtcp-sync-interval", "RTCP Sync Interval", @@ -1831,26 +2523,135 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 0, G_MAXUINT, DEFAULT_RTCP_SYNC_INTERVAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DO_SYNC_EVENT, + g_param_spec_boolean ("do-sync-event", "Do Sync Event", + "Send event downstream when a stream is synchronized to the sender", + DEFAULT_DO_SYNC_EVENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstRtpBin:do-retransmission: + * + * Enables RTP retransmission on all streams. To control retransmission on + * a per-SSRC basis, connect to the #GstRtpBin::new-jitterbuffer signal and + * set the #GstRtpJitterBuffer::do-retransmission property on the + * #GstRtpJitterBuffer object instead. + */ + g_object_class_install_property (gobject_class, PROP_DO_RETRANSMISSION, + g_param_spec_boolean ("do-retransmission", "Do retransmission", + "Enable retransmission on all streams", + DEFAULT_DO_RETRANSMISSION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstRtpBin:rtp-profile: + * + * Sets the default RTP profile of newly created RTP sessions. The + * profile can be changed afterwards on a per-session basis. + */ + g_object_class_install_property (gobject_class, PROP_RTP_PROFILE, + g_param_spec_enum ("rtp-profile", "RTP Profile", + "Default RTP profile of newly created sessions", + GST_TYPE_RTP_PROFILE, DEFAULT_RTP_PROFILE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_NTP_TIME_SOURCE, + g_param_spec_enum ("ntp-time-source", "NTP Time Source", + "NTP time source for RTCP packets", + gst_rtp_ntp_time_source_get_type (), DEFAULT_NTP_TIME_SOURCE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_RTCP_SYNC_SEND_TIME, + g_param_spec_boolean ("rtcp-sync-send-time", "RTCP Sync Send Time", + "Use send time or capture time for RTCP sync " + "(TRUE = send time, FALSE = capture time)", + DEFAULT_RTCP_SYNC_SEND_TIME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_MAX_RTCP_RTP_TIME_DIFF, + g_param_spec_int ("max-rtcp-rtp-time-diff", "Max RTCP RTP Time Diff", + "Maximum amount of time in ms that the RTP time in RTCP SRs " + "is allowed to be ahead (-1 disabled)", -1, G_MAXINT, + DEFAULT_MAX_RTCP_RTP_TIME_DIFF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_MAX_DROPOUT_TIME, + g_param_spec_uint ("max-dropout-time", "Max dropout time", + "The maximum time (milliseconds) of missing packets tolerated.", + 0, G_MAXUINT, DEFAULT_MAX_DROPOUT_TIME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_MAX_MISORDER_TIME, + g_param_spec_uint ("max-misorder-time", "Max misorder time", + "The maximum time (milliseconds) of misordered packets tolerated.", + 0, G_MAXUINT, DEFAULT_MAX_MISORDER_TIME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_RFC7273_SYNC, + g_param_spec_boolean ("rfc7273-sync", "Sync on RFC7273 clock", + "Synchronize received streams to the RFC7273 clock " + "(requires clock and offset to be provided)", DEFAULT_RFC7273_SYNC, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_MAX_STREAMS, + g_param_spec_uint ("max-streams", "Max Streams", + "The maximum number of streams to create for one session", + 0, G_MAXUINT, DEFAULT_MAX_STREAMS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstRtpBin:max-ts-offset-adjustment: + * + * Syncing time stamps to NTP time adds a time offset. This parameter + * specifies the maximum number of nanoseconds per frame that this time offset + * may be adjusted with. This is used to avoid sudden large changes to time + * stamps. + * + * Since: 1.14 + */ + g_object_class_install_property (gobject_class, PROP_MAX_TS_OFFSET_ADJUSTMENT, + g_param_spec_uint64 ("max-ts-offset-adjustment", + "Max Timestamp Offset Adjustment", + "The maximum number of nanoseconds per frame that time stamp offsets " + "may be adjusted (0 = no limit).", 0, G_MAXUINT64, + DEFAULT_MAX_TS_OFFSET_ADJUSTMENT, G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * GstRtpBin:max-ts-offset: + * + * Used to set an upper limit of how large a time offset may be. This + * is used to protect against unrealistic values as a result of either + * client,server or clock issues. + * + * Since: 1.14 + */ + g_object_class_install_property (gobject_class, PROP_MAX_TS_OFFSET, + g_param_spec_int64 ("max-ts-offset", "Max TS Offset", + "The maximum absolute value of the time offset in (nanoseconds). " + "Note, if the ntp-sync parameter is set the default value is " + "changed to 0 (no limit)", 0, G_MAXINT64, DEFAULT_MAX_TS_OFFSET, + 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); /* sink pads */ - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&rtpbin_send_rtp_sink_template)); + gst_element_class_add_static_pad_template (gstelement_class, + &rtpbin_recv_rtp_sink_template); + gst_element_class_add_static_pad_template (gstelement_class, + &rtpbin_recv_rtcp_sink_template); + gst_element_class_add_static_pad_template (gstelement_class, + &rtpbin_send_rtp_sink_template); /* src pads */ - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&rtpbin_recv_rtp_src_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&rtpbin_send_rtcp_src_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&rtpbin_send_rtp_src_template)); + gst_element_class_add_static_pad_template (gstelement_class, + &rtpbin_recv_rtp_src_template); + gst_element_class_add_static_pad_template (gstelement_class, + &rtpbin_send_rtcp_src_template); + gst_element_class_add_static_pad_template (gstelement_class, + &rtpbin_send_rtp_src_template); gst_element_class_set_static_metadata (gstelement_class, "RTP Bin", "Filter/Network/RTP", @@ -1861,8 +2662,16 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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_session = GST_DEBUG_FUNCPTR (gst_rtp_bin_get_session); klass->get_internal_session = GST_DEBUG_FUNCPTR (gst_rtp_bin_get_internal_session); + klass->get_storage = GST_DEBUG_FUNCPTR (gst_rtp_bin_get_storage); + klass->get_internal_storage = + GST_DEBUG_FUNCPTR (gst_rtp_bin_get_internal_storage); + klass->request_rtp_encoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_encoder); + klass->request_rtp_decoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_decoder); + klass->request_rtcp_encoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_encoder); + klass->request_rtcp_decoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_decoder); GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin"); } @@ -1872,12 +2681,13 @@ gst_rtp_bin_init (GstRtpBin * rtpbin) { gchar *cname; - rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin); + rtpbin->priv = gst_rtp_bin_get_instance_private (rtpbin); g_mutex_init (&rtpbin->priv->bin_lock); g_mutex_init (&rtpbin->priv->dyn_lock); rtpbin->latency_ms = DEFAULT_LATENCY_MS; rtpbin->latency_ns = DEFAULT_LATENCY_MS * GST_MSECOND; + rtpbin->drop_on_latency = DEFAULT_DROP_ON_LATENCY; rtpbin->do_lost = DEFAULT_DO_LOST; rtpbin->ignore_pt = DEFAULT_IGNORE_PT; rtpbin->ntp_sync = DEFAULT_NTP_SYNC; @@ -1886,6 +2696,19 @@ gst_rtp_bin_init (GstRtpBin * rtpbin) rtpbin->priv->autoremove = DEFAULT_AUTOREMOVE; rtpbin->buffer_mode = DEFAULT_BUFFER_MODE; rtpbin->use_pipeline_clock = DEFAULT_USE_PIPELINE_CLOCK; + rtpbin->send_sync_event = DEFAULT_DO_SYNC_EVENT; + rtpbin->do_retransmission = DEFAULT_DO_RETRANSMISSION; + rtpbin->rtp_profile = DEFAULT_RTP_PROFILE; + rtpbin->ntp_time_source = DEFAULT_NTP_TIME_SOURCE; + rtpbin->rtcp_sync_send_time = DEFAULT_RTCP_SYNC_SEND_TIME; + rtpbin->max_rtcp_rtp_time_diff = DEFAULT_MAX_RTCP_RTP_TIME_DIFF; + rtpbin->max_dropout_time = DEFAULT_MAX_DROPOUT_TIME; + rtpbin->max_misorder_time = DEFAULT_MAX_MISORDER_TIME; + rtpbin->rfc7273_sync = DEFAULT_RFC7273_SYNC; + rtpbin->max_streams = DEFAULT_MAX_STREAMS; + rtpbin->max_ts_offset_adjustment = DEFAULT_MAX_TS_OFFSET_ADJUSTMENT; + rtpbin->max_ts_offset = DEFAULT_MAX_TS_OFFSET; + rtpbin->max_ts_offset_is_set = FALSE; /* some default SDES entries */ cname = g_strdup_printf ("user%u@host-%x", g_random_int (), g_random_int ()); @@ -1906,10 +2729,6 @@ gst_rtp_bin_dispose (GObject * object) 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; GST_RTP_BIN_UNLOCK (rtpbin); G_OBJECT_CLASS (parent_class)->dispose (object); @@ -1986,6 +2805,14 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, /* propagate the property down to the jitterbuffer */ gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "latency", value); break; + case PROP_DROP_ON_LATENCY: + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->drop_on_latency = g_value_get_boolean (value); + GST_RTP_BIN_UNLOCK (rtpbin); + /* propagate the property down to the jitterbuffer */ + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "drop-on-latency", value); + break; case PROP_SDES: gst_rtp_bin_set_sdes_struct (rtpbin, g_value_get_boxed (value)); break; @@ -1997,6 +2824,15 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, break; case PROP_NTP_SYNC: rtpbin->ntp_sync = g_value_get_boolean (value); + /* The default value of max_ts_offset depends on ntp_sync. If user + * hasn't set it then change default value */ + if (!rtpbin->max_ts_offset_is_set) { + if (rtpbin->ntp_sync) { + rtpbin->max_ts_offset = 0; + } else { + rtpbin->max_ts_offset = DEFAULT_MAX_TS_OFFSET; + } + } break; case PROP_RTCP_SYNC: g_atomic_int_set (&rtpbin->rtcp_sync, g_value_get_enum (value)); @@ -2025,6 +2861,9 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, GST_RTP_BIN_UNLOCK (rtpbin); } break; + case PROP_DO_SYNC_EVENT: + rtpbin->send_sync_event = g_value_get_boolean (value); + break; case PROP_BUFFER_MODE: GST_RTP_BIN_LOCK (rtpbin); rtpbin->buffer_mode = g_value_get_enum (value); @@ -2032,6 +2871,86 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, /* propagate the property down to the jitterbuffer */ gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "mode", value); break; + case PROP_DO_RETRANSMISSION: + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->do_retransmission = g_value_get_boolean (value); + GST_RTP_BIN_UNLOCK (rtpbin); + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "do-retransmission", value); + break; + case PROP_RTP_PROFILE: + rtpbin->rtp_profile = g_value_get_enum (value); + break; + case PROP_NTP_TIME_SOURCE:{ + GSList *sessions; + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->ntp_time_source = g_value_get_enum (value); + for (sessions = rtpbin->sessions; sessions; + sessions = g_slist_next (sessions)) { + GstRtpBinSession *session = (GstRtpBinSession *) sessions->data; + + g_object_set (G_OBJECT (session->session), + "ntp-time-source", rtpbin->ntp_time_source, NULL); + } + GST_RTP_BIN_UNLOCK (rtpbin); + break; + } + case PROP_RTCP_SYNC_SEND_TIME:{ + GSList *sessions; + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->rtcp_sync_send_time = g_value_get_boolean (value); + for (sessions = rtpbin->sessions; sessions; + sessions = g_slist_next (sessions)) { + GstRtpBinSession *session = (GstRtpBinSession *) sessions->data; + + g_object_set (G_OBJECT (session->session), + "rtcp-sync-send-time", rtpbin->rtcp_sync_send_time, NULL); + } + GST_RTP_BIN_UNLOCK (rtpbin); + break; + } + case PROP_MAX_RTCP_RTP_TIME_DIFF: + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->max_rtcp_rtp_time_diff = g_value_get_int (value); + GST_RTP_BIN_UNLOCK (rtpbin); + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "max-rtcp-rtp-time-diff", value); + break; + case PROP_MAX_DROPOUT_TIME: + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->max_dropout_time = g_value_get_uint (value); + GST_RTP_BIN_UNLOCK (rtpbin); + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "max-dropout-time", value); + gst_rtp_bin_propagate_property_to_session (rtpbin, "max-dropout-time", + value); + break; + case PROP_MAX_MISORDER_TIME: + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->max_misorder_time = g_value_get_uint (value); + GST_RTP_BIN_UNLOCK (rtpbin); + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "max-misorder-time", value); + gst_rtp_bin_propagate_property_to_session (rtpbin, "max-misorder-time", + value); + break; + case PROP_RFC7273_SYNC: + rtpbin->rfc7273_sync = g_value_get_boolean (value); + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "rfc7273-sync", value); + break; + case PROP_MAX_STREAMS: + rtpbin->max_streams = g_value_get_uint (value); + break; + case PROP_MAX_TS_OFFSET_ADJUSTMENT: + rtpbin->max_ts_offset_adjustment = g_value_get_uint64 (value); + gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, + "max-ts-offset-adjustment", value); + break; + case PROP_MAX_TS_OFFSET: + rtpbin->max_ts_offset = g_value_get_int64 (value); + rtpbin->max_ts_offset_is_set = TRUE; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2052,6 +2971,11 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id, g_value_set_uint (value, rtpbin->latency_ms); GST_RTP_BIN_UNLOCK (rtpbin); break; + case PROP_DROP_ON_LATENCY: + GST_RTP_BIN_LOCK (rtpbin); + g_value_set_boolean (value, rtpbin->drop_on_latency); + GST_RTP_BIN_UNLOCK (rtpbin); + break; case PROP_SDES: g_value_take_boxed (value, gst_rtp_bin_get_sdes_struct (rtpbin)); break; @@ -2081,6 +3005,46 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id, case PROP_USE_PIPELINE_CLOCK: g_value_set_boolean (value, rtpbin->use_pipeline_clock); break; + case PROP_DO_SYNC_EVENT: + g_value_set_boolean (value, rtpbin->send_sync_event); + break; + case PROP_DO_RETRANSMISSION: + GST_RTP_BIN_LOCK (rtpbin); + g_value_set_boolean (value, rtpbin->do_retransmission); + GST_RTP_BIN_UNLOCK (rtpbin); + break; + case PROP_RTP_PROFILE: + g_value_set_enum (value, rtpbin->rtp_profile); + break; + case PROP_NTP_TIME_SOURCE: + g_value_set_enum (value, rtpbin->ntp_time_source); + break; + case PROP_RTCP_SYNC_SEND_TIME: + g_value_set_boolean (value, rtpbin->rtcp_sync_send_time); + break; + case PROP_MAX_RTCP_RTP_TIME_DIFF: + GST_RTP_BIN_LOCK (rtpbin); + g_value_set_int (value, rtpbin->max_rtcp_rtp_time_diff); + GST_RTP_BIN_UNLOCK (rtpbin); + break; + case PROP_MAX_DROPOUT_TIME: + g_value_set_uint (value, rtpbin->max_dropout_time); + break; + case PROP_MAX_MISORDER_TIME: + g_value_set_uint (value, rtpbin->max_misorder_time); + break; + case PROP_RFC7273_SYNC: + g_value_set_boolean (value, rtpbin->rfc7273_sync); + break; + case PROP_MAX_STREAMS: + g_value_set_uint (value, rtpbin->max_streams); + break; + case PROP_MAX_TS_OFFSET_ADJUSTMENT: + g_value_set_uint64 (value, rtpbin->max_ts_offset_adjustment); + break; + case PROP_MAX_TS_OFFSET: + g_value_set_int64 (value, rtpbin->max_ts_offset); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2123,6 +3087,9 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) gint min_percent = 100; GSList *sessions, *streams; GstRtpBinStream *stream; +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + gboolean buffering_flag = FALSE, update_buffering_status = TRUE; +#endif gboolean change = FALSE, active = FALSE; GstClockTime min_out_time; GstBufferingMode mode; @@ -2155,13 +3122,52 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) for (streams = session->streams; streams; streams = g_slist_next (streams)) { GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; - +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + GstPad *temp_pad_src = NULL; + GstCaps *temp_caps_src = NULL; + GstStructure *caps_structure; + const gchar *caps_str_media = NULL; + temp_pad_src = gst_element_get_static_pad (stream->buffer, "src"); + temp_caps_src = gst_pad_get_current_caps (temp_pad_src); + GST_DEBUG_OBJECT (bin, + "stream %p percent %d : temp_caps_src=%" GST_PTR_FORMAT, + stream, stream->percent, temp_caps_src); + if (temp_caps_src) { + caps_structure = gst_caps_get_structure (temp_caps_src, 0); + caps_str_media = + gst_structure_get_string (caps_structure, "media"); + if (caps_str_media != NULL) { + if ((strcmp (caps_str_media, "video") != 0) + && (strcmp (caps_str_media, "audio") != 0)) { + GST_DEBUG_OBJECT (bin, + "Non Audio/Video Stream.. ignoring the same !!"); + gst_caps_unref (temp_caps_src); + gst_object_unref (temp_pad_src); + continue; + } else if (stream->percent >= 100) { + /* Most of the time buffering icon displays in rtsp playback. + Optimizing the buffering updation code. Whenever any stream percentage + reaches 100 do not post buffering messages. */ + if (stream->prev_percent < 100) + buffering_flag = TRUE; + else + update_buffering_status = FALSE; + } + } + gst_caps_unref (temp_caps_src); + } + gst_object_unref (temp_pad_src); +#else GST_DEBUG_OBJECT (bin, "stream %p percent %d", stream, stream->percent); - +#endif /* find min percent */ if (min_percent > stream->percent) min_percent = stream->percent; +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + /* Updating prev stream percentage */ + stream->prev_percent = stream->percent; +#endif } } else { GST_INFO_OBJECT (bin, @@ -2171,7 +3177,24 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) GST_RTP_SESSION_UNLOCK (session); } GST_DEBUG_OBJECT (bin, "min percent %d", min_percent); - +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (rtpbin->buffer_mode != RTP_JITTER_BUFFER_MODE_SLAVE) { + if (rtpbin->buffering) { + if (min_percent == 100) { + rtpbin->buffering = FALSE; + active = TRUE; + change = TRUE; + } + } else { + if (min_percent < 100) { + /* pause the streams */ + rtpbin->buffering = TRUE; + active = FALSE; + change = TRUE; + } + } + } +#else if (rtpbin->buffering) { if (min_percent == 100) { rtpbin->buffering = FALSE; @@ -2186,16 +3209,31 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) change = TRUE; } } +#endif GST_RTP_BIN_UNLOCK (rtpbin); gst_message_unref (message); +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (rtpbin->buffer_mode == RTP_JITTER_BUFFER_MODE_SLAVE) { + if (update_buffering_status == FALSE) + break; + if (buffering_flag) { + min_percent = 100; + GST_DEBUG_OBJECT (bin, "forcefully change min_percent to 100!!!"); + } + } +#endif /* make a new buffering message with the min value */ message = gst_message_new_buffering (GST_OBJECT_CAST (bin), min_percent); gst_message_set_buffering_stats (message, mode, avg_in, avg_out, buffering_left); +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (rtpbin->buffer_mode == RTP_JITTER_BUFFER_MODE_SLAVE) + goto slave_buffering; +#endif if (G_UNLIKELY (change)) { GstClock *clock; guint64 running_time = 0; @@ -2273,6 +3311,9 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) GST_RTP_BIN_UNLOCK (rtpbin); } } +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION +slave_buffering: +#endif GST_BIN_CLASS (parent_class)->handle_message (bin, message); break; } @@ -2298,7 +3339,7 @@ gst_rtp_bin_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - priv->last_unix = 0; + priv->last_ntpnstime = 0; GST_LOG_OBJECT (rtpbin, "clearing shutdown flag"); g_atomic_int_set (&priv->shutdown, 0); break; @@ -2330,25 +3371,85 @@ gst_rtp_bin_change_state (GstElement * element, GstStateChange transition) 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) +static GstElement * +session_request_element (GstRtpBinSession * session, guint signal) { - GstRtpBin *rtpbin; - GstElementClass *klass; - GstPadTemplate *templ; - gchar *padname; - GstPad *gpad; + GstElement *element = NULL; + GstRtpBin *bin = session->bin; - rtpbin = stream->bin; + g_signal_emit (bin, gst_rtp_bin_signals[signal], 0, session->id, &element); - GST_DEBUG ("new payload pad %d", pt); + if (element) { + if (!bin_manage_element (bin, element)) + goto manage_failed; + session->elements = g_slist_prepend (session->elements, element); + } + return element; - GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown); + /* ERRORS */ +manage_failed: + { + GST_WARNING_OBJECT (bin, "unable to manage element"); + gst_object_unref (element); + return NULL; + } +} - /* ghost the pad to the parent */ +static gboolean +copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data) +{ + GstPad *gpad = GST_PAD_CAST (user_data); + + GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event); + gst_pad_store_sticky_event (gpad, *event); + + return TRUE; +} + +static void +expose_recv_src_pad (GstRtpBin * rtpbin, GstPad * pad, GstRtpBinStream * stream, + guint8 pt) +{ + GstElementClass *klass; + GstPadTemplate *templ; + gchar *padname; + GstPad *gpad; + + gst_object_ref (pad); + + if (stream->session->storage) { + GstElement *fec_decoder = + session_request_element (stream->session, SIGNAL_REQUEST_FEC_DECODER); + + if (fec_decoder) { + GstPad *sinkpad, *srcpad; + GstPadLinkReturn ret; + + sinkpad = gst_element_get_static_pad (fec_decoder, "sink"); + + if (!sinkpad) + goto fec_decoder_sink_failed; + + ret = gst_pad_link (pad, sinkpad); + gst_object_unref (sinkpad); + + if (ret != GST_PAD_LINK_OK) + goto fec_decoder_link_failed; + + srcpad = gst_element_get_static_pad (fec_decoder, "src"); + + if (!srcpad) + goto fec_decoder_src_failed; + + gst_pad_sticky_events_foreach (pad, copy_sticky_events, srcpad); + gst_object_unref (pad); + pad = srcpad; + } + } + + 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_%u_%u_%u"); padname = g_strdup_printf ("recv_rtp_src_%u_%u_%u", @@ -2360,17 +3461,54 @@ new_payload_found (GstElement * element, guint pt, GstPad * pad, gst_pad_set_active (gpad, TRUE); GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); + gst_pad_sticky_events_foreach (pad, copy_sticky_events, gpad); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad); +done: + gst_object_unref (pad); + return; shutdown: { GST_DEBUG ("ignoring, we are shutting down"); - return; + goto done; + } +fec_decoder_sink_failed: + { + g_warning ("rtpbin: failed to get fec encoder sink pad for session %u", + stream->session->id); + goto done; + } +fec_decoder_src_failed: + { + g_warning ("rtpbin: failed to get fec encoder src pad for session %u", + stream->session->id); + goto done; + } +fec_decoder_link_failed: + { + g_warning ("rtpbin: failed to link fec decoder for session %u", + stream->session->id); + goto done; } } +/* 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; + + rtpbin = stream->bin; + + GST_DEBUG_OBJECT (rtpbin, "new payload pad %u", pt); + + expose_recv_src_pad (rtpbin, pad, stream, pt); +} + static void payload_pad_removed (GstElement * element, GstPad * pad, GstRtpBinStream * stream) @@ -2400,7 +3538,7 @@ pt_map_requested (GstElement * element, guint pt, GstRtpBinSession * session) rtpbin = session->bin; - GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %d in session %d", pt, + GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %u in session %u", pt, session->id); caps = get_pt_map (session, pt); @@ -2417,18 +3555,48 @@ no_caps: } } +static GstCaps * +ptdemux_pt_map_requested (GstElement * element, guint pt, + GstRtpBinSession * session) +{ + GstCaps *ret = pt_map_requested (element, pt, session); + + if (ret && gst_caps_get_size (ret) == 1) { + const GstStructure *s = gst_caps_get_structure (ret, 0); + gboolean is_fec; + + if (gst_structure_get_boolean (s, "is-fec", &is_fec) && is_fec) { + GValue v = G_VALUE_INIT; + GValue v2 = G_VALUE_INIT; + + GST_INFO_OBJECT (session->bin, "Will ignore FEC pt %u in session %u", pt, + session->id); + g_value_init (&v, GST_TYPE_ARRAY); + g_value_init (&v2, G_TYPE_INT); + g_object_get_property (G_OBJECT (element), "ignored-payload-types", &v); + g_value_set_int (&v2, pt); + gst_value_array_append_value (&v, &v2); + g_value_unset (&v2); + g_object_set_property (G_OBJECT (element), "ignored-payload-types", &v); + g_value_unset (&v); + } + } + + return ret; +} + static void payload_type_change (GstElement * element, guint pt, GstRtpBinSession * session) { GST_DEBUG_OBJECT (session->bin, - "emiting signal for pt type changed to %d in session %d", pt, + "emiting signal for pt type changed to %u in session %u", pt, session->id); g_signal_emit (session->bin, gst_rtp_bin_signals[SIGNAL_PAYLOAD_TYPE_CHANGE], 0, session->id, pt); } -/* emited when caps changed for the session */ +/* emitted when caps changed for the session */ static void caps_changed (GstPad * pad, GParamSpec * pspec, GstRtpBinSession * session) { @@ -2449,8 +3617,10 @@ caps_changed (GstPad * pad, GParamSpec * pspec, GstRtpBinSession * session) s = gst_caps_get_structure (caps, 0); /* get payload, finish when it's not there */ - if (!gst_structure_get_int (s, "payload", &payload)) + if (!gst_structure_get_int (s, "payload", &payload)) { + gst_caps_unref (caps); return; + } GST_RTP_SESSION_LOCK (session); GST_DEBUG_OBJECT (bin, "insert caps for payload %d", payload); @@ -2488,7 +3658,7 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad, 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_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING); gst_object_unref (sinkpad); gst_object_unref (srcpad); @@ -2497,7 +3667,7 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad, 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_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING); gst_object_unref (sinkpad); gst_object_unref (srcpad); @@ -2514,40 +3684,31 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad, 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 + /* connect to the request-pt-map signal. This signal will be emitted 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); + "request-pt-map", (GCallback) ptdemux_pt_map_requested, session); /* connect to the signal so it can be forwarded. */ stream->demux_ptchange_sig = g_signal_connect (stream->demux, "payload-type-change", (GCallback) payload_type_change, session); + + GST_RTP_SESSION_UNLOCK (session); + GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); } else { - /* add gstrtpjitterbuffer src pad to pads */ - GstElementClass *klass; - GstPadTemplate *templ; - gchar *padname; - GstPad *gpad, *pad; + /* add rtpjitterbuffer src pad to pads */ + GstPad *pad; pad = gst_element_get_static_pad (stream->buffer, "src"); - /* ghost the pad to the parent */ - klass = GST_ELEMENT_GET_CLASS (rtpbin); - templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%u_%u_%u"); - padname = g_strdup_printf ("recv_rtp_src_%u_%u_%u", - stream->session->id, stream->ssrc, 255); - gpad = gst_ghost_pad_new_from_template (padname, pad, templ); - g_free (padname); + GST_RTP_SESSION_UNLOCK (session); + GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); - gst_pad_set_active (gpad, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad); + expose_recv_src_pad (rtpbin, pad, stream, 255); gst_object_unref (pad); } - GST_RTP_SESSION_UNLOCK (session); - GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); - return; /* ERRORS */ @@ -2565,27 +3726,199 @@ no_stream: } } +static GstPad * +complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session) +{ + guint sessid = session->id; + GstPad *recv_rtp_sink; + GstElement *decoder; + + g_assert (!session->recv_rtp_sink); + + /* 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, "requesting RTP decoder"); + decoder = session_request_element (session, SIGNAL_REQUEST_RTP_DECODER); + if (decoder) { + GstPad *decsrc, *decsink; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTP decoder"); + decsink = gst_element_get_static_pad (decoder, "rtp_sink"); + if (decsink == NULL) + goto dec_sink_failed; + + recv_rtp_sink = decsink; + + decsrc = gst_element_get_static_pad (decoder, "rtp_src"); + if (decsrc == NULL) + goto dec_src_failed; + + ret = gst_pad_link (decsrc, session->recv_rtp_sink); + + gst_object_unref (decsrc); + + if (ret != GST_PAD_LINK_OK) + goto dec_link_failed; + + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTP decoder given"); + recv_rtp_sink = gst_object_ref (session->recv_rtp_sink); + } + + return recv_rtp_sink; + + /* ERRORS */ +pad_failed: + { + g_warning ("rtpbin: failed to get session recv_rtp_sink pad"); + return NULL; + } +dec_sink_failed: + { + g_warning ("rtpbin: failed to get decoder sink pad for session %u", sessid); + return NULL; + } +dec_src_failed: + { + g_warning ("rtpbin: failed to get decoder src pad for session %u", sessid); + gst_object_unref (recv_rtp_sink); + return NULL; + } +dec_link_failed: + { + g_warning ("rtpbin: failed to link rtp decoder for session %u", sessid); + gst_object_unref (recv_rtp_sink); + return NULL; + } +} + +static void +complete_session_receiver (GstRtpBin * rtpbin, GstRtpBinSession * session, + guint sessid) +{ + GstElement *aux; + GstPad *recv_rtp_src; + + g_assert (!session->recv_rtp_src); + + session->recv_rtp_src = + gst_element_get_static_pad (session->session, "recv_rtp_src"); + if (session->recv_rtp_src == NULL) + goto pad_failed; + + /* find out if we need AUX elements */ + aux = session_request_element (session, SIGNAL_REQUEST_AUX_RECEIVER); + if (aux) { + gchar *pname; + GstPad *auxsink; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking AUX receiver"); + + pname = g_strdup_printf ("sink_%u", sessid); + auxsink = gst_element_get_static_pad (aux, pname); + g_free (pname); + if (auxsink == NULL) + goto aux_sink_failed; + + ret = gst_pad_link (session->recv_rtp_src, auxsink); + gst_object_unref (auxsink); + if (ret != GST_PAD_LINK_OK) + goto aux_link_failed; + + /* this can be NULL when this AUX element is not to be linked any further */ + pname = g_strdup_printf ("src_%u", sessid); + recv_rtp_src = gst_element_get_static_pad (aux, pname); + g_free (pname); + } else { + recv_rtp_src = gst_object_ref (session->recv_rtp_src); + } + + /* Add a storage element if needed */ + if (recv_rtp_src && session->storage) { + GstPadLinkReturn ret; + GstPad *sinkpad = gst_element_get_static_pad (session->storage, "sink"); + + ret = gst_pad_link (recv_rtp_src, sinkpad); + + gst_object_unref (sinkpad); + gst_object_unref (recv_rtp_src); + + if (ret != GST_PAD_LINK_OK) + goto storage_link_failed; + + recv_rtp_src = gst_element_get_static_pad (session->storage, "src"); + } + + if (recv_rtp_src) { + GstPad *sinkdpad; + + 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"); + gst_pad_link_full (recv_rtp_src, sinkdpad, GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (sinkdpad); + gst_object_unref (recv_rtp_src); + + /* 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); + } + + return; + +pad_failed: + { + g_warning ("rtpbin: failed to get session recv_rtp_src pad"); + return; + } +aux_sink_failed: + { + g_warning ("rtpbin: failed to get AUX sink pad for session %u", sessid); + return; + } +aux_link_failed: + { + g_warning ("rtpbin: failed to link AUX pad to session %u", sessid); + return; + } +storage_link_failed: + { + g_warning ("rtpbin: failed to link storage"); + 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; + GstPad *recv_rtp_sink; /* first get the session number */ if (name == NULL || sscanf (name, "recv_rtp_sink_%u", &sessid) != 1) goto no_name; - GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid); + GST_DEBUG_OBJECT (rtpbin, "finding session %u", sessid); /* get or create session */ session = find_session_by_id (rtpbin, sessid); if (!session) { - GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid); + GST_DEBUG_OBJECT (rtpbin, "creating session %u", sessid); /* create session now */ session = create_session (rtpbin, sessid); if (session == NULL) @@ -2596,43 +3929,20 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) 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); + /* setup the session sink pad */ + recv_rtp_sink = complete_session_sink (rtpbin, session); + if (!recv_rtp_sink) + goto session_sink_failed; 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_ghost_pad_new_from_template (name, recv_rtp_sink, templ); + gst_object_unref (recv_rtp_sink); gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost); + complete_session_receiver (rtpbin, session, sessid); + return session->recv_rtp_sink_ghost; /* ERRORS */ @@ -2646,14 +3956,9 @@ create_error: /* create_session already warned */ return NULL; } -pad_failed: +session_sink_failed: { - g_warning ("rtpbin: failed to get session pad"); - return NULL; - } -link_failed: - { - g_warning ("rtpbin: failed to link pads"); + /* warning already done */ return NULL; } } @@ -2686,6 +3991,91 @@ remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) } } +static GstPad * +complete_session_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session, + guint sessid) +{ + GstElement *decoder; + GstPad *sinkdpad; + GstPad *decsink = NULL; + + /* 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; + + GST_DEBUG_OBJECT (rtpbin, "getting RTCP decoder"); + decoder = session_request_element (session, SIGNAL_REQUEST_RTCP_DECODER); + if (decoder) { + GstPad *decsrc; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTCP decoder"); + decsink = gst_element_get_static_pad (decoder, "rtcp_sink"); + decsrc = gst_element_get_static_pad (decoder, "rtcp_src"); + + if (decsink == NULL) + goto dec_sink_failed; + + if (decsrc == NULL) + goto dec_src_failed; + + ret = gst_pad_link (decsrc, session->recv_rtcp_sink); + + gst_object_unref (decsrc); + + if (ret != GST_PAD_LINK_OK) + goto dec_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTCP decoder given"); + decsink = gst_object_ref (session->recv_rtcp_sink); + } + + /* 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 src_pad_failed; + + GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTCP sink pad"); + sinkdpad = gst_element_get_static_pad (session->demux, "rtcp_sink"); + gst_pad_link_full (session->sync_src, sinkdpad, GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (sinkdpad); + + return decsink; + +pad_failed: + { + g_warning ("rtpbin: failed to get session rtcp_sink pad"); + return NULL; + } +dec_sink_failed: + { + g_warning ("rtpbin: failed to get decoder sink pad for session %u", sessid); + return NULL; + } +dec_src_failed: + { + g_warning ("rtpbin: failed to get decoder src pad for session %u", sessid); + goto cleanup; + } +dec_link_failed: + { + g_warning ("rtpbin: failed to link rtcp decoder for session %u", sessid); + goto cleanup; + } +src_pad_failed: + { + g_warning ("rtpbin: failed to get session sync_src pad"); + } + +cleanup: + gst_object_unref (decsink); + return NULL; +} + /* Create a pad for receiving RTCP for the session in @name. Must be called with * RTP_BIN_LOCK. */ @@ -2695,19 +4085,18 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, { guint sessid; GstRtpBinSession *session; - GstPad *sinkdpad; - GstPadLinkReturn lres; + GstPad *decsink = NULL; /* first get the session number */ if (name == NULL || sscanf (name, "recv_rtcp_sink_%u", &sessid) != 1) goto no_name; - GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid); + GST_DEBUG_OBJECT (rtpbin, "finding session %u", sessid); /* get or create the session */ session = find_session_by_id (rtpbin, sessid); if (!session) { - GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid); + GST_DEBUG_OBJECT (rtpbin, "creating session %u", sessid); /* create session now */ session = create_session (rtpbin, sessid); if (session == NULL) @@ -2718,28 +4107,13 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, 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; + decsink = complete_session_rtcp (rtpbin, session, sessid); + if (!decsink) + goto create_error; session->recv_rtcp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ); + gst_ghost_pad_new_from_template (name, decsink, templ); + gst_object_unref (decsink); gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtcp_sink_ghost); @@ -2757,16 +4131,6 @@ create_error: /* create_session already warned */ return NULL; } -pad_failed: - { - g_warning ("rtpbin: failed to get session pad"); - return NULL; - } -link_failed: - { - g_warning ("rtpbin: failed to link pads"); - return NULL; - } } static void @@ -2790,16 +4154,207 @@ remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session) } } +static gboolean +complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session) +{ + gchar *gname; + guint sessid = session->id; + GstPad *send_rtp_src; + GstElement *encoder; + GstElementClass *klass; + GstPadTemplate *templ; + gboolean ret = FALSE; + + /* get srcpad */ + send_rtp_src = gst_element_get_static_pad (session->session, "send_rtp_src"); + + if (send_rtp_src == NULL) + goto no_srcpad; + + GST_DEBUG_OBJECT (rtpbin, "getting RTP encoder"); + encoder = session_request_element (session, SIGNAL_REQUEST_RTP_ENCODER); + if (encoder) { + gchar *ename; + GstPad *encsrc, *encsink; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTP encoder"); + ename = g_strdup_printf ("rtp_src_%u", sessid); + encsrc = gst_element_get_static_pad (encoder, ename); + g_free (ename); + + if (encsrc == NULL) + goto enc_src_failed; + + ename = g_strdup_printf ("rtp_sink_%u", sessid); + encsink = gst_element_get_static_pad (encoder, ename); + g_free (ename); + if (encsink == NULL) + goto enc_sink_failed; + + ret = gst_pad_link (send_rtp_src, encsink); + gst_object_unref (encsink); + gst_object_unref (send_rtp_src); + + send_rtp_src = encsrc; + + if (ret != GST_PAD_LINK_OK) + goto enc_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTP encoder given"); + } + + /* ghost the new source pad */ + klass = GST_ELEMENT_GET_CLASS (rtpbin); + gname = g_strdup_printf ("send_rtp_src_%u", sessid); + templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%u"); + session->send_rtp_src_ghost = + gst_ghost_pad_new_from_template (gname, send_rtp_src, templ); + gst_pad_set_active (session->send_rtp_src_ghost, TRUE); + gst_pad_sticky_events_foreach (send_rtp_src, copy_sticky_events, + session->send_rtp_src_ghost); + gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_src_ghost); + g_free (gname); + + ret = TRUE; + +done: + if (send_rtp_src) + gst_object_unref (send_rtp_src); + + return ret; + + /* ERRORS */ +no_srcpad: + { + g_warning ("rtpbin: failed to get rtp source pad for session %u", sessid); + goto done; + } +enc_src_failed: + { + g_warning ("rtpbin: failed to get %" GST_PTR_FORMAT + " src pad for session %u", encoder, sessid); + goto done; + } +enc_sink_failed: + { + g_warning ("rtpbin: failed to get %" GST_PTR_FORMAT + " sink pad for session %u", encoder, sessid); + goto done; + } +enc_link_failed: + { + g_warning ("rtpbin: failed to link %" GST_PTR_FORMAT " for session %u", + encoder, sessid); + goto done; + } +} + +static gboolean +setup_aux_sender_fold (const GValue * item, GValue * result, gpointer user_data) +{ + GstPad *pad; + gchar *name; + guint sessid; + GstRtpBinSession *session = user_data, *newsess; + GstRtpBin *rtpbin = session->bin; + GstPadLinkReturn ret; + + pad = g_value_get_object (item); + name = gst_pad_get_name (pad); + + if (name == NULL || sscanf (name, "src_%u", &sessid) != 1) + goto no_name; + + g_free (name); + + newsess = find_session_by_id (rtpbin, sessid); + if (newsess == NULL) { + /* create new session */ + newsess = create_session (rtpbin, sessid); + if (newsess == NULL) + goto create_error; + } else if (newsess->send_rtp_sink != NULL) + goto existing_session; + + /* get send_rtp pad and store */ + newsess->send_rtp_sink = + gst_element_get_request_pad (newsess->session, "send_rtp_sink"); + if (newsess->send_rtp_sink == NULL) + goto pad_failed; + + ret = gst_pad_link (pad, newsess->send_rtp_sink); + if (ret != GST_PAD_LINK_OK) + goto aux_link_failed; + + if (!complete_session_src (rtpbin, newsess)) + goto session_src_failed; + + return TRUE; + + /* ERRORS */ +no_name: + { + GST_WARNING ("ignoring invalid pad name %s", GST_STR_NULL (name)); + g_free (name); + return TRUE; + } +create_error: + { + /* create_session already warned */ + return FALSE; + } +existing_session: + { + GST_DEBUG_OBJECT (rtpbin, + "skipping src_%i setup, since it is already configured.", sessid); + return TRUE; + } +pad_failed: + { + g_warning ("rtpbin: failed to get session pad for session %u", sessid); + return FALSE; + } +aux_link_failed: + { + g_warning ("rtpbin: failed to link AUX for session %u", sessid); + return FALSE; + } +session_src_failed: + { + g_warning ("rtpbin: failed to complete AUX for session %u", sessid); + return FALSE; + } +} + +static gboolean +setup_aux_sender (GstRtpBin * rtpbin, GstRtpBinSession * session, + GstElement * aux) +{ + GstIterator *it; + GValue result = { 0, }; + GstIteratorResult res; + + it = gst_element_iterate_src_pads (aux); + res = gst_iterator_fold (it, setup_aux_sender_fold, &result, session); + gst_iterator_free (it); + + return res == GST_ITERATOR_DONE; +} + /* 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; + gchar *pname; guint sessid; + GstPad *send_rtp_sink; + GstElement *aux; + GstElement *encoder; + GstElement *prev = NULL; GstRtpBinSession *session; - GstElementClass *klass; /* first get the session number */ if (name == NULL || sscanf (name, "send_rtp_sink_%u", &sessid) != 1) @@ -2818,33 +4373,80 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) 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; + /* check if we are already using this session as a sender */ + if (session->send_rtp_sink != NULL) + goto existing_session; + + encoder = session_request_element (session, SIGNAL_REQUEST_FEC_ENCODER); + + if (encoder) { + GST_DEBUG_OBJECT (rtpbin, "Linking FEC encoder"); + + send_rtp_sink = gst_element_get_static_pad (encoder, "sink"); + + if (!send_rtp_sink) + goto enc_sink_failed; + + prev = encoder; + } + + GST_DEBUG_OBJECT (rtpbin, "getting RTP AUX sender"); + aux = session_request_element (session, SIGNAL_REQUEST_AUX_SENDER); + if (aux) { + GstPad *sinkpad; + GST_DEBUG_OBJECT (rtpbin, "linking AUX sender"); + if (!setup_aux_sender (rtpbin, session, aux)) + goto aux_session_failed; + + pname = g_strdup_printf ("sink_%u", sessid); + sinkpad = gst_element_get_static_pad (aux, pname); + g_free (pname); + + if (sinkpad == NULL) + goto aux_sink_failed; + + if (!prev) { + send_rtp_sink = sinkpad; + } else { + GstPad *srcpad = gst_element_get_static_pad (prev, "src"); + GstPadLinkReturn ret; + + ret = gst_pad_link (srcpad, sinkpad); + gst_object_unref (srcpad); + if (ret != GST_PAD_LINK_OK) { + goto aux_link_failed; + } + } + prev = aux; + } else { + /* 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; + + if (!complete_session_src (rtpbin, session)) + goto session_src_failed; + + if (!prev) { + send_rtp_sink = gst_object_ref (session->send_rtp_sink); + } else { + GstPad *srcpad = gst_element_get_static_pad (prev, "src"); + GstPadLinkReturn ret; + + ret = gst_pad_link (srcpad, session->send_rtp_sink); + gst_object_unref (srcpad); + if (ret != GST_PAD_LINK_OK) + goto session_link_failed; + } + } session->send_rtp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ); + gst_ghost_pad_new_from_template (name, send_rtp_sink, templ); + gst_object_unref (send_rtp_sink); 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_%u", sessid); - templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%u"); - 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 */ @@ -2858,14 +4460,47 @@ create_error: /* create_session already warned */ return NULL; } +existing_session: + { + g_warning ("rtpbin: session %u is already in use", sessid); + return NULL; + } +aux_session_failed: + { + g_warning ("rtpbin: failed to get AUX sink pad for session %u", sessid); + return NULL; + } +aux_sink_failed: + { + g_warning ("rtpbin: failed to get AUX sink pad for session %u", sessid); + return NULL; + } +aux_link_failed: + { + g_warning ("rtpbin: failed to link %" GST_PTR_FORMAT " for session %u", + aux, sessid); + return NULL; + } pad_failed: { - g_warning ("rtpbin: failed to get session pad for session %d", sessid); + g_warning ("rtpbin: failed to get session pad for session %u", sessid); return NULL; } -no_srcpad: +session_src_failed: + { + g_warning ("rtpbin: failed to setup source pads for session %u", sessid); + return NULL; + } +session_link_failed: { - g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid); + g_warning ("rtpbin: failed to link %" GST_PTR_FORMAT " for session %u", + session, sessid); + return NULL; + } +enc_sink_failed: + { + g_warning ("rtpbin: failed to get %" GST_PTR_FORMAT + " sink pad for session %u", encoder, sessid); return NULL; } } @@ -2879,10 +4514,6 @@ remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) 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); @@ -2901,9 +4532,12 @@ remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) * RTP_BIN_LOCK. */ static GstPad * -create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) +create_send_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, + const gchar * name) { guint sessid; + GstPad *encsrc; + GstElement *encoder; GstRtpBinSession *session; /* first get the session number */ @@ -2912,8 +4546,13 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) /* get or create session */ session = find_session_by_id (rtpbin, sessid); - if (!session) - goto no_session; + if (!session) { + GST_DEBUG_OBJECT (rtpbin, "creating session %u", sessid); + /* create session now */ + session = create_session (rtpbin, sessid); + if (session == NULL) + goto create_error; + } /* check if pad was requested */ if (session->send_rtcp_src_ghost != NULL) @@ -2925,8 +4564,40 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (session->send_rtcp_src == NULL) goto pad_failed; + GST_DEBUG_OBJECT (rtpbin, "getting RTCP encoder"); + encoder = session_request_element (session, SIGNAL_REQUEST_RTCP_ENCODER); + if (encoder) { + gchar *ename; + GstPad *encsink; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTCP encoder"); + + ename = g_strdup_printf ("rtcp_src_%u", sessid); + encsrc = gst_element_get_static_pad (encoder, ename); + g_free (ename); + if (encsrc == NULL) + goto enc_src_failed; + + ename = g_strdup_printf ("rtcp_sink_%u", sessid); + encsink = gst_element_get_static_pad (encoder, ename); + g_free (ename); + if (encsink == NULL) + goto enc_sink_failed; + + ret = gst_pad_link (session->send_rtcp_src, encsink); + gst_object_unref (encsink); + + if (ret != GST_PAD_LINK_OK) + goto enc_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTCP encoder given"); + encsrc = gst_object_ref (session->send_rtcp_src); + } + session->send_rtcp_src_ghost = - gst_ghost_pad_new_from_template (name, session->send_rtcp_src, templ); + gst_ghost_pad_new_from_template (name, encsrc, templ); + gst_object_unref (encsrc); gst_pad_set_active (session->send_rtcp_src_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtcp_src_ghost); @@ -2938,14 +4609,31 @@ no_name: g_warning ("rtpbin: invalid name given"); return NULL; } -no_session: +create_error: { - g_warning ("rtpbin: session with id %d does not exist", sessid); + /* create_session already warned */ return NULL; } pad_failed: { - g_warning ("rtpbin: failed to get rtcp pad for session %d", sessid); + g_warning ("rtpbin: failed to get rtcp pad for session %u", sessid); + return NULL; + } +enc_src_failed: + { + g_warning ("rtpbin: failed to get encoder src pad for session %u", sessid); + return NULL; + } +enc_sink_failed: + { + g_warning ("rtpbin: failed to get encoder sink pad for session %u", sessid); + gst_object_unref (encsrc); + return NULL; + } +enc_link_failed: + { + g_warning ("rtpbin: failed to link rtcp encoder for session %u", sessid); + gst_object_unref (encsrc); return NULL; } } @@ -3065,7 +4753,7 @@ gst_rtp_bin_request_new_pad (GstElement * element, result = create_send_rtp (rtpbin, templ, pad_name); } else if (templ == gst_element_class_get_pad_template (klass, "send_rtcp_src_%u")) { - result = create_rtcp (rtpbin, templ, pad_name); + result = create_send_rtcp (rtpbin, templ, pad_name); } else goto wrong_template;