X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Frtpmanager%2Fgstrtpbin.c;h=d10cd01502eb9622d34bca0332624ea3d38b2520;hb=6c86b2832f30e0eb8ccd7c7e186892caceb34325;hp=4236cd160b1280e6c7b7d9cc54dc2102037688a3;hpb=84833bed1115075b72fd6253eb94624066994a35;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 4236cd1..d10cd01 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -19,6 +19,7 @@ /** * SECTION:element-rtpbin + * @title: rtpbin * @see_also: rtpjitterbuffer, rtpsession, rtpptdemux, rtpssrcdemux * * RTP bin combines the functions of #GstRtpSession, #GstRtpSsrcDemux, @@ -50,7 +51,7 @@ * To use #GstRtpBin as a sender, request a send_rtp_sink_\%u pad, which will * automatically create a send_rtp_src_\%u pad. If the session number is not provided, * the pad from the lowest available session will be returned. The session manager will modify the - * SSRC in the RTP packets to its own SSRC and wil forward the packets on the + * SSRC in the RTP packets to its own SSRC and will forward the packets on the * send_rtp_src_\%u pad after updating its internal state. * * The session manager needs the clock-rate of the payload types it is handling @@ -63,8 +64,33 @@ * RTPSession object which further provides action signals to retrieve the * internal source and other sources. * - * - * Example pipelines + * #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. + * The #GstRtpBin::request-jitterbuffer signal can be used to provide a custom + * element to perform arrival time smoothing, reordering and optionally packet + * loss detection and retransmission requests. + * + * ## Example pipelines + * * |[ * gst-launch-1.0 udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \ * rtpbin ! rtptheoradepay ! theoradec ! xvimagesink @@ -109,9 +135,7 @@ * synchronisation. * Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1 * on port 5007. - * * - * Last reviewed on 2007-08-30 (0.10.6) */ #ifdef HAVE_CONFIG_H @@ -135,17 +159,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 = @@ -164,22 +188,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) @@ -203,6 +224,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; @@ -215,8 +240,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 */ @@ -226,7 +254,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, @@ -238,6 +269,28 @@ 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_REQUEST_JITTERBUFFER, + + 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 }; @@ -254,6 +307,19 @@ enum #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) +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION +#define DEFAULT_RTSP_USE_BUFFERING FALSE +#endif enum { @@ -271,14 +337,19 @@ enum PROP_USE_PIPELINE_CLOCK, PROP_DO_SYNC_EVENT, PROP_DO_RETRANSMISSION, - PROP_LAST -}; - -enum -{ - GST_RTP_BIN_RTCP_SYNC_ALWAYS, - GST_RTP_BIN_RTCP_SYNC_INITIAL, - GST_RTP_BIN_RTCP_SYNC_RTP + 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, +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + PROP_USE_RTSP_BUFFERING /* use for player RTSP buffering */ +#endif }; #define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type()) @@ -316,10 +387,20 @@ 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, 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); +static GstElement *session_request_element (GstRtpBinSession * session, + guint signal); /* Manages the RTP stream for one SSRC. * - * We pipe the stream (comming from the SSRC demuxer) into a jitterbuffer. + * We pipe the stream (coming from the SSRC demuxer) into a jitterbuffer. * If we see an SDES RTCP packet that links multiple SSRCs together based on a * common CNAME, we create a GstRtpBinClient structure to group the SSRCs * together (see below). @@ -341,7 +422,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; @@ -368,6 +451,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 { @@ -382,11 +468,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; @@ -399,7 +491,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; @@ -528,6 +619,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) @@ -571,6 +677,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))) @@ -579,20 +686,41 @@ 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 (demux, "max-streams", rtpbin->max_streams, 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 */ @@ -616,9 +744,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); @@ -627,23 +764,84 @@ 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) @@ -652,9 +850,11 @@ free_session (GstRtpBinSession * sess, GstRtpBin * bin) 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); @@ -663,6 +863,11 @@ 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); + + g_slist_foreach (sess->elements, (GFunc) remove_bin_element, bin); + g_slist_free (sess->elements); + sess->elements = NULL; g_slist_foreach (sess->streams, (GFunc) free_stream, bin); g_slist_free (sess->streams); @@ -682,7 +887,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); @@ -695,7 +900,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 ("emitting 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); @@ -731,7 +936,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), @@ -803,7 +1008,8 @@ gst_rtp_bin_clear_pt_map (GstRtpBin * bin) GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; GST_DEBUG_OBJECT (bin, "clearing stream %p", stream); - g_signal_emit_by_name (stream->buffer, "clear-pt-map", NULL); + if (g_signal_lookup ("clear-pt-map", G_OBJECT_TYPE (stream->buffer)) != 0) + g_signal_emit_by_name (stream->buffer, "clear-pt-map", NULL); if (stream->demux) g_signal_emit_by_name (stream->demux, "clear-pt-map", NULL); } @@ -815,6 +1021,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) { @@ -822,7 +1045,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) { @@ -834,6 +1057,63 @@ 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 GstElement * +gst_rtp_bin_request_jitterbuffer (GstRtpBin * bin, guint session_id) +{ + return gst_element_factory_make ("rtpjitterbuffer", NULL); +} + static void gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin, const gchar * name, const GValue * value) @@ -847,14 +1127,35 @@ gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin, GST_RTP_SESSION_LOCK (session); for (streams = session->streams; streams; streams = g_slist_next (streams)) { GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; + GObjectClass *jb_class; - g_object_set_property (G_OBJECT (stream->buffer), name, value); + jb_class = G_OBJECT_GET_CLASS (G_OBJECT (stream->buffer)); + if (g_object_class_find_property (jb_class, name)) + g_object_set_property (G_OBJECT (stream->buffer), name, value); + else + GST_WARNING_OBJECT (bin, + "Stream jitterbuffer does not expose property %s", name); } GST_RTP_SESSION_UNLOCK (session); } 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) @@ -901,7 +1202,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; @@ -911,24 +1212,39 @@ 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 - base_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:{ + /* get current NTP time */ + ntpns = g_get_real_time () * GST_USECOND; + + /* 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); @@ -943,9 +1259,19 @@ get_current_times (GstRtpBin * bin, GstClockTime * running_time, static void stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream, - gint64 ts_offset, gboolean check) + gint64 ts_offset, gint64 max_ts_offset, gint64 min_ts_offset, + gboolean allow_positive_ts_offset) { gint64 prev_ts_offset; + GObjectClass *jb_class; + + jb_class = G_OBJECT_GET_CLASS (G_OBJECT (stream->buffer)); + + if (!g_object_class_find_property (jb_class, "ts-offset")) { + GST_LOG_OBJECT (bin, + "stream's jitterbuffer does not expose ts-offset property"); + return; + } g_object_get (stream->buffer, "ts-offset", &prev_ts_offset, NULL); @@ -959,19 +1285,25 @@ 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); - if (check) { - /* 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) { - GST_DEBUG_OBJECT (bin, "offset too small, ignoring"); + /* 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 (diff) > (3 * GST_SECOND)) { - GST_WARNING_OBJECT (bin, "offset unusually large, ignoring"); + 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, @@ -1009,12 +1341,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); @@ -1056,51 +1384,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); + * to know how the running_time and the NTP time relate to each other. */ + 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); @@ -1108,25 +1444,26 @@ 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, FALSE); + 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 * streams. - * We calculate the mininum because we would like to only apply positive + * We calculate the minimum because we would like to only apply positive * offsets to streams, delaying their playback instead of trying to speed up - * other streams (which might be imposible when we have to create negative + * other streams (which might be impossible when we have to create negative * latencies). * The stream that has the smallest diff is selected as the reference stream, * all other streams will have a positive offset to this difference. */ @@ -1139,7 +1476,7 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, guint64 ext_base; use_rtp = TRUE; - /* signed version for convienience */ + /* signed version for convenience */ clock_base = base_rtptime; /* deal with possible wrap-around */ ext_base = base_rtptime; @@ -1233,14 +1570,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)) { @@ -1260,7 +1597,8 @@ 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, TRUE); + stream_set_ts_offset (bin, ostream, ts_offset, bin->max_ts_offset, + MIN_TS_OFFSET, TRUE); } } gst_rtp_bin_send_sync_event (stream); @@ -1389,64 +1727,138 @@ 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; + GObjectClass *jb_class; rtpbin = session->bin; - if (!(buffer = gst_element_factory_make ("rtpjitterbuffer", NULL))) + if (g_slist_length (session->streams) >= rtpbin->max_streams) + goto max_streams; + + if (!(buffer = + session_request_element (session, SIGNAL_REQUEST_JITTERBUFFER))) 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 (rtpbin->use_rtsp_buffering && + rtpbin->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; stream->session = session; - stream->buffer = buffer; + stream->buffer = gst_object_ref (buffer); stream->demux = demux; stream->have_sync = FALSE; 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); - /* provide clock_rate to the jitterbuffer when needed */ - stream->buffer_ptreq_sig = g_signal_connect (buffer, "request-pt-map", - (GCallback) pt_map_requested, session); - stream->buffer_ntpstop_sig = g_signal_connect (buffer, "on-npt-stop", - (GCallback) on_npt_stop, stream); + jb_class = G_OBJECT_GET_CLASS (G_OBJECT (buffer)); + + if (g_signal_lookup ("request-pt-map", G_OBJECT_TYPE (buffer)) != 0) { + /* provide clock_rate to the jitterbuffer when needed */ + stream->buffer_ptreq_sig = g_signal_connect (buffer, "request-pt-map", + (GCallback) pt_map_requested, session); + } + if (g_signal_lookup ("on-npt-stop", G_OBJECT_TYPE (buffer)) != 0) { + stream->buffer_ntpstop_sig = g_signal_connect (buffer, "on-npt-stop", + (GCallback) on_npt_stop, stream); + } g_object_set_data (G_OBJECT (buffer), "GstRTPBin.session", session); g_object_set_data (G_OBJECT (buffer), "GstRTPBin.stream", stream); /* 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); + + if (g_object_class_find_property (jb_class, "drop-on-latency")) + g_object_set (buffer, "drop-on-latency", rtpbin->drop_on_latency, NULL); + if (g_object_class_find_property (jb_class, "do-lost")) + g_object_set (buffer, "do-lost", rtpbin->do_lost, NULL); + if (g_object_class_find_property (jb_class, "mode")) + g_object_set (buffer, "mode", rtpbin->buffer_mode, NULL); + if (g_object_class_find_property (jb_class, "do-retransmission")) + g_object_set (buffer, "do-retransmission", rtpbin->do_retransmission, NULL); + if (g_object_class_find_property (jb_class, "max-rtcp-rtp-time-diff")) + g_object_set (buffer, "max-rtcp-rtp-time-diff", + rtpbin->max_rtcp_rtp_time_diff, NULL); + if (g_object_class_find_property (jb_class, "max-dropout-time")) + g_object_set (buffer, "max-dropout-time", rtpbin->max_dropout_time, NULL); + if (g_object_class_find_property (jb_class, "max-misorder-time")) + g_object_set (buffer, "max-misorder-time", rtpbin->max_misorder_time, NULL); + if (g_object_class_find_property (jb_class, "rfc7273-sync")) + g_object_set (buffer, "rfc7273-sync", rtpbin->rfc7273_sync, NULL); + if (g_object_class_find_property (jb_class, "max-ts-offset-adjustment")) + 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 + 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); - gst_bin_add (GST_BIN_CAST (rtpbin), buffer); + +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (queue2) + gst_bin_add (GST_BIN_CAST (rtpbin), queue2); +#endif /* 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; - GST_INFO_OBJECT (rtpbin, - "bin is buffering, set jitterbuffer as not active"); - g_signal_emit_by_name (buffer, "set-active", FALSE, (gint64) 0, &last_out); + if (g_signal_lookup ("set-active", G_OBJECT_TYPE (buffer)) != 0) { + GST_INFO_OBJECT (rtpbin, + "bin is buffering, set jitterbuffer as not active"); + g_signal_emit_by_name (buffer, "set-active", FALSE, (gint64) 0, + &last_out); + } } @@ -1460,53 +1872,77 @@ 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 exceeds 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, GstRtpBin * bin) { + GstRtpBinSession *sess = stream->session; GSList *clients, *next_client; GST_DEBUG_OBJECT (bin, "freeing stream %p", stream); - if (stream->demux) { - g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig); - g_signal_handler_disconnect (stream->demux, stream->demux_ptreq_sig); - g_signal_handler_disconnect (stream->demux, stream->demux_ptchange_sig); - } - g_signal_handler_disconnect (stream->buffer, stream->buffer_handlesync_sig); - g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig); - g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig); - + gst_element_set_locked_state (stream->buffer, TRUE); if (stream->demux) gst_element_set_locked_state (stream->demux, TRUE); - gst_element_set_locked_state (stream->buffer, TRUE); + gst_element_set_state (stream->buffer, GST_STATE_NULL); if (stream->demux) gst_element_set_state (stream->demux, GST_STATE_NULL); - gst_element_set_state (stream->buffer, GST_STATE_NULL); - /* now remove this signal, we need this while going to NULL because it to - * do some cleanups */ - if (stream->demux) + if (stream->demux) { + g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig); + g_signal_handler_disconnect (stream->demux, stream->demux_ptreq_sig); + g_signal_handler_disconnect (stream->demux, stream->demux_ptchange_sig); g_signal_handler_disconnect (stream->demux, stream->demux_padremoved_sig); + } + + if (stream->buffer_handlesync_sig) + g_signal_handler_disconnect (stream->buffer, stream->buffer_handlesync_sig); + if (stream->buffer_ptreq_sig) + g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig); + if (stream->buffer_ntpstop_sig) + g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig); + + sess->elements = g_slist_remove (sess->elements, stream->buffer); + remove_bin_element (stream->buffer, bin); + gst_object_unref (stream->buffer); - gst_bin_remove (GST_BIN_CAST (bin), stream->buffer); if (stream->demux) gst_bin_remove (GST_BIN_CAST (bin), stream->demux); @@ -1553,7 +1989,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) @@ -1566,8 +2034,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; @@ -1596,7 +2062,7 @@ 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, g_cclosure_marshal_generic, GST_TYPE_CAPS, 2, G_TYPE_UINT, + _gst_caps_accumulator, NULL, NULL, GST_TYPE_CAPS, 2, G_TYPE_UINT, G_TYPE_UINT); /** @@ -1606,14 +2072,11 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::clear-pt-map: @@ -1625,8 +2088,7 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_CLEAR_PT_MAP] = g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, - clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, - 0, G_TYPE_NONE); + clear_pt_map), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE); /** * GstRtpBin::reset-sync: @@ -1638,8 +2100,21 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_RESET_SYNC] = g_signal_new ("reset-sync", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, - reset_sync), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, - 0, G_TYPE_NONE); + reset_sync), NULL, NULL, NULL, G_TYPE_NONE, 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, NULL, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); /** * GstRtpBin::get-internal-session: @@ -1651,8 +2126,40 @@ 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, g_cclosure_marshal_generic, - RTP_TYPE_SESSION, 1, G_TYPE_UINT); + get_internal_session), NULL, NULL, NULL, RTP_TYPE_SESSION, 1, + G_TYPE_UINT); + + /** + * GstRtpBin::get-internal-storage: + * @rtpbin: the object which received the signal + * @id: the session id + * + * Request the internal RTPStorage object as #GObject in session @id. This + * is the internal storage used by the RTPStorage element, which is used to + * keep a backlog of received RTP packets for the session @id. + * + * Since: 1.14 + */ + 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, NULL, G_TYPE_OBJECT, 1, + G_TYPE_UINT); + + /** + * GstRtpBin::get-storage: + * @rtpbin: the object which received the signal + * @id: the session id + * + * Request the RTPStorage element as #GObject in session @id. This element + * is used to keep a backlog of received RTP packets for the 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, NULL, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); /** * GstRtpBin::on-new-ssrc: @@ -1665,8 +2172,7 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-ssrc-collision: * @rtpbin: the object which received the signal @@ -1678,8 +2184,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-ssrc-validated: * @rtpbin: the object which received the signal @@ -1691,8 +2196,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-ssrc-active: * @rtpbin: the object which received the signal @@ -1704,8 +2208,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-ssrc-sdes: * @rtpbin: the object which received the signal @@ -1717,8 +2220,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-bye-ssrc: @@ -1731,8 +2233,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-bye-timeout: * @rtpbin: the object which received the signal @@ -1744,8 +2245,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-timeout: * @rtpbin: the object which received the signal @@ -1757,8 +2257,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-sender-timeout: * @rtpbin: the object which received the signal @@ -1770,8 +2269,7 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * GstRtpBin::on-npt-stop: @@ -1784,13 +2282,254 @@ 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, - G_TYPE_UINT); + NULL, NULL, NULL, 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, NULL, + 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, + NULL, 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, NULL, + 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, NULL, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-jitterbuffer: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request a jitterbuffer element for the given @session. + * + * If no handler is connected, the default jitterbuffer will be used. + * + * Note: The provided element is expected to conform to the API exposed + * by the standard #GstRtpJitterBuffer. Runtime checks will be made to + * determine whether it exposes properties and signals before attempting + * to set, call or connect to them, and some functionalities of #GstRtpBin + * may not be available when that is not the case. + * + * This should be considered experimental API, as the standard jitterbuffer + * API is susceptible to change, provided elements will have to update their + * custom jitterbuffer's API to match the API of #GstRtpJitterBuffer if and + * when it changes. + * + * Since: 1.18 + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_JITTERBUFFER] = + g_signal_new ("request-jitterbuffer", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, + request_jitterbuffer), _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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, 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, NULL, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); g_object_class_install_property (gobject_class, PROP_SDES, g_param_spec_boxed ("sdes", "SDES", "The SDES items of this session", - GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS +#ifndef TIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK + | GST_PARAM_DOC_SHOW_DEFAULT)); +#else + )); +#endif g_object_class_install_property (gobject_class, PROP_DO_LOST, g_param_spec_boolean ("do-lost", "Do Lost", @@ -1809,30 +2548,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 running-time 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: + * GstRtpBin:ntp-sync: * * 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. - * - * Since: 0.10.21 */ g_object_class_install_property (gobject_class, PROP_NTP_SYNC, g_param_spec_boolean ("ntp-sync", "Sync on NTP clock", @@ -1840,12 +2576,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", @@ -1853,11 +2587,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", @@ -1870,32 +2602,138 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) "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", - "Send an event downstream to request packet 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)); + +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + g_object_class_install_property (gobject_class, PROP_USE_RTSP_BUFFERING, + g_param_spec_boolean ("use-rtsp-buffering", "Use RTSP buffering", + "Use RTSP buffering in RTP_JITTER_BUFFER_MODE_SLAVE buffer mode", + DEFAULT_RTSP_USE_BUFFERING, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif + 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", @@ -1906,10 +2744,24 @@ 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); + klass->request_jitterbuffer = + GST_DEBUG_FUNCPTR (gst_rtp_bin_request_jitterbuffer); GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin"); + +#ifndef TIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK + gst_type_mark_as_plugin_api (GST_RTP_BIN_RTCP_SYNC_TYPE, 0); +#endif } static void @@ -1917,7 +2769,7 @@ 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); @@ -1934,6 +2786,20 @@ gst_rtp_bin_init (GstRtpBin * rtpbin) 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; +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + rtpbin->use_rtsp_buffering = FALSE; +#endif /* some default SDES entries */ cname = g_strdup_printf ("user%u@host-%x", g_random_int (), g_random_int ()); @@ -2022,6 +2888,13 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, rtpbin = GST_RTP_BIN (object); switch (prop_id) { +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + case PROP_USE_RTSP_BUFFERING: + GST_RTP_BIN_LOCK (rtpbin); + rtpbin->use_rtsp_buffering = g_value_get_boolean (value); + GST_RTP_BIN_UNLOCK (rtpbin); + break; +#endif case PROP_LATENCY: GST_RTP_BIN_LOCK (rtpbin); rtpbin->latency_ms = g_value_get_uint (value); @@ -2049,6 +2922,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)); @@ -2094,6 +2976,79 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, 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; @@ -2109,6 +3064,13 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id, rtpbin = GST_RTP_BIN (object); switch (prop_id) { +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + case PROP_USE_RTSP_BUFFERING: + GST_RTP_BIN_LOCK (rtpbin); + g_value_set_boolean (value, rtpbin->use_rtsp_buffering); + GST_RTP_BIN_UNLOCK (rtpbin); + break; +#endif case PROP_LATENCY: GST_RTP_BIN_LOCK (rtpbin); g_value_set_uint (value, rtpbin->latency_ms); @@ -2156,6 +3118,38 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id, 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; @@ -2198,6 +3192,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; @@ -2231,9 +3228,54 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) streams = g_slist_next (streams)) { GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (rtpbin->use_rtsp_buffering && + rtpbin->buffer_mode == RTP_JITTER_BUFFER_MODE_SLAVE) { + 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); + /* Updating prev stream percentage */ + stream->prev_percent = stream->percent; + } else { + GST_DEBUG_OBJECT (bin, "stream %p percent %d", stream, + stream->percent); + } +#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; @@ -2247,6 +3289,10 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) } GST_DEBUG_OBJECT (bin, "min percent %d", min_percent); +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (!(rtpbin->use_rtsp_buffering && + rtpbin->buffer_mode == RTP_JITTER_BUFFER_MODE_SLAVE)) { +#endif if (rtpbin->buffering) { if (min_percent == 100) { rtpbin->buffering = FALSE; @@ -2261,16 +3307,35 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) change = TRUE; } } +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + } +#endif GST_RTP_BIN_UNLOCK (rtpbin); gst_message_unref (message); +#ifdef TIZEN_FEATURE_RTSP_MODIFICATION + if (rtpbin->use_rtsp_buffering && + 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->use_rtsp_buffering && + rtpbin->buffer_mode == RTP_JITTER_BUFFER_MODE_SLAVE) + goto slave_buffering; +#endif if (G_UNLIKELY (change)) { GstClock *clock; guint64 running_time = 0; @@ -2316,10 +3381,12 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) streams = g_slist_next (streams)) { GstRtpBinStream *stream = (GstRtpBinStream *) streams->data; GstElement *element = stream->buffer; - guint64 last_out; + guint64 last_out = -1; - g_signal_emit_by_name (element, "set-active", active, offset, - &last_out); + if (g_signal_lookup ("set-active", G_OBJECT_TYPE (element)) != 0) { + g_signal_emit_by_name (element, "set-active", active, offset, + &last_out); + } if (!active) { g_object_get (element, "percent", &stream->percent, NULL); @@ -2348,6 +3415,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; } @@ -2373,7 +3443,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; @@ -2405,21 +3475,81 @@ 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 GstElement * +session_request_element (GstRtpBinSession * session, guint signal) +{ + GstElement *element = NULL; + GstRtpBin *bin = session->bin; + + g_signal_emit (bin, gst_rtp_bin_signals[signal], 0, session->id, &element); + + if (element) { + if (!bin_manage_element (bin, element)) + goto manage_failed; + session->elements = g_slist_prepend (session->elements, element); + } + return element; + + /* ERRORS */ +manage_failed: + { + GST_WARNING_OBJECT (bin, "unable to manage element"); + gst_object_unref (element); + return NULL; + } +} + +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 -new_payload_found (GstElement * element, guint pt, GstPad * pad, - GstRtpBinStream * stream) +expose_recv_src_pad (GstRtpBin * rtpbin, GstPad * pad, GstRtpBinStream * stream, + guint8 pt) { - GstRtpBin *rtpbin; GstElementClass *klass; GstPadTemplate *templ; gchar *padname; GstPad *gpad; - rtpbin = stream->bin; + 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"); - GST_DEBUG ("new payload pad %d", pt); + 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); @@ -2435,17 +3565,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); - return; +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 emitted 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) @@ -2475,7 +3642,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); @@ -2492,18 +3659,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, + "emitting 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) { @@ -2524,8 +3721,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); @@ -2563,23 +3762,27 @@ 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); - GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTCP"); - padname = g_strdup_printf ("rtcp_src_%u", ssrc); - srcpad = gst_element_get_static_pad (element, padname); - g_free (padname); sinkpad = gst_element_get_request_pad (stream->buffer, "sink_rtcp"); - gst_pad_link (srcpad, sinkpad); - gst_object_unref (sinkpad); - gst_object_unref (srcpad); + if (sinkpad) { + GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTCP"); + padname = g_strdup_printf ("rtcp_src_%u", ssrc); + srcpad = gst_element_get_static_pad (element, padname); + g_free (padname); + gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + } - /* connect to the RTCP sync signal from the jitterbuffer */ - GST_DEBUG_OBJECT (rtpbin, "connecting sync signal"); - stream->buffer_handlesync_sig = g_signal_connect (stream->buffer, - "handle-sync", (GCallback) gst_rtp_bin_handle_sync, stream); + if (g_signal_lookup ("handle-sync", G_OBJECT_TYPE (stream->buffer)) != 0) { + /* connect to the RTCP sync signal from the jitterbuffer */ + GST_DEBUG_OBJECT (rtpbin, "connecting sync signal"); + stream->buffer_handlesync_sig = g_signal_connect (stream->buffer, + "handle-sync", (GCallback) gst_rtp_bin_handle_sync, stream); + } if (stream->demux) { /* connect to the new-pad signal of the payload demuxer, this will expose the @@ -2589,40 +3792,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 */ @@ -2640,27 +3834,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) @@ -2671,49 +4037,27 @@ 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 */ no_name: { - g_warning ("rtpbin: invalid name given"); + g_warning ("rtpbin: cannot find session id for pad: %s", + GST_STR_NULL (name)); return NULL; } create_error: @@ -2721,14 +4065,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; } } @@ -2761,6 +4100,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. */ @@ -2770,19 +4194,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) @@ -2793,28 +4216,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); @@ -2824,7 +4232,8 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, /* ERRORS */ no_name: { - g_warning ("rtpbin: invalid name given"); + g_warning ("rtpbin: cannot find session id for pad: %s", + GST_STR_NULL (name)); return NULL; } create_error: @@ -2832,16 +4241,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 @@ -2865,16 +4264,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) @@ -2893,39 +4483,87 @@ 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 */ no_name: { - g_warning ("rtpbin: invalid name given"); + g_warning ("rtpbin: cannot find session id for pad: %s", + GST_STR_NULL (name)); return NULL; } create_error: @@ -2933,14 +4571,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 link %" GST_PTR_FORMAT " for session %u", + session, sessid); + return NULL; + } +enc_sink_failed: { - g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid); + g_warning ("rtpbin: failed to get %" GST_PTR_FORMAT + " sink pad for session %u", encoder, sessid); return NULL; } } @@ -2954,10 +4625,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); @@ -2976,9 +4643,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 */ @@ -2987,8 +4657,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) @@ -3000,8 +4675,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); @@ -3010,17 +4717,35 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) /* ERRORS */ no_name: { - g_warning ("rtpbin: invalid name given"); + g_warning ("rtpbin: cannot find session id for pad: %s", + GST_STR_NULL (name)); 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; } } @@ -3042,7 +4767,7 @@ remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session) } /* If the requested name is NULL we should create a name with - * the session number assuming we want the lowest posible session + * the session number assuming we want the lowest possible session * with a free pad like the template */ static gchar * gst_rtp_bin_get_free_pad_name (GstElement * element, GstPadTemplate * templ) @@ -3140,7 +4865,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;