X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Frtpmanager%2Fgstrtpbin.c;h=d10cd01502eb9622d34bca0332624ea3d38b2520;hb=6c86b2832f30e0eb8ccd7c7e186892caceb34325;hp=f0b8b4e33aca8f4354b68ad48d8d058f0241a4dc;hpb=671b4d25cd1c5a25bd1086d72797388104dfdbcf;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index f0b8b4e..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 @@ -84,9 +85,12 @@ * 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 * - * - * Example pipelines * |[ * gst-launch-1.0 udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \ * rtpbin ! rtptheoradepay ! theoradec ! xvimagesink @@ -131,7 +135,7 @@ * synchronisation. * Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1 * on port 5007. - * + * */ #ifdef HAVE_CONFIG_H @@ -197,9 +201,6 @@ static GstStaticPadTemplate rtpbin_send_rtp_src_template = 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) @@ -223,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; @@ -235,8 +240,8 @@ 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; @@ -249,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, @@ -267,11 +275,22 @@ enum 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 }; @@ -288,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 { @@ -304,7 +336,20 @@ enum PROP_BUFFER_MODE, PROP_USE_PIPELINE_CLOCK, PROP_DO_SYNC_EVENT, - PROP_DO_RETRANSMISSION + PROP_DO_RETRANSMISSION, + PROP_RTP_PROFILE, + PROP_NTP_TIME_SOURCE, + PROP_RTCP_SYNC_SEND_TIME, + PROP_MAX_RTCP_RTP_TIME_DIFF, + PROP_MAX_DROPOUT_TIME, + PROP_MAX_MISORDER_TIME, + PROP_RFC7273_SYNC, + PROP_MAX_STREAMS, + PROP_MAX_TS_OFFSET_ADJUSTMENT, + PROP_MAX_TS_OFFSET, +#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()) @@ -342,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). @@ -367,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; @@ -394,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 { @@ -408,6 +468,9 @@ struct _GstRtpBinSession gulong demux_newpad_sig; gulong demux_padremoved_sig; + /* Fec support */ + GstElement *storage; + GMutex lock; /* list of GstRtpBinStream */ @@ -428,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; @@ -557,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) @@ -600,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))) @@ -608,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 */ @@ -645,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); @@ -656,6 +764,7 @@ 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; @@ -671,6 +780,13 @@ no_demux: 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 @@ -682,6 +798,10 @@ bin_manage_element (GstRtpBin * bin, GstElement * 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)) @@ -697,6 +817,7 @@ bin_manage_element (GstRtpBin * bin, GstElement * element) add_failed: { GST_WARNING_OBJECT (bin, "unable to add element"); + gst_object_unref (element); return FALSE; } } @@ -711,10 +832,13 @@ remove_bin_element (GstElement * element, GstRtpBin * bin) if (find) { priv->elements = g_list_delete_link (priv->elements, find); - if (!g_list_find (priv->elements, element)) + if (!g_list_find (priv->elements, element)) { + gst_element_set_locked_state (element, TRUE); gst_bin_remove (GST_BIN_CAST (bin), element); - else - gst_object_unref (element); + gst_element_set_state (element, GST_STATE_NULL); + } + + gst_object_unref (element); } } @@ -726,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); @@ -737,9 +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); @@ -759,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); @@ -772,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); @@ -808,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), @@ -880,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); } @@ -892,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) { @@ -899,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) { @@ -912,6 +1058,43 @@ gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id) } 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"); @@ -925,6 +1108,12 @@ gst_rtp_bin_request_decoder (GstRtpBin * bin, guint session_id) 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) @@ -938,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) @@ -992,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; @@ -1002,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); @@ -1034,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); @@ -1050,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, @@ -1100,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); @@ -1147,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); @@ -1199,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. */ @@ -1230,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; @@ -1324,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)) { @@ -1351,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); @@ -1480,67 +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_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); + } } @@ -1554,9 +1872,20 @@ 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 rtpjitterbuffer element"); @@ -1568,39 +1897,52 @@ no_demux: 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); @@ -1647,7 +1989,7 @@ 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, @@ -1692,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; @@ -1722,8 +2062,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP] = g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, request_pt_map), - _gst_caps_accumulator, NULL, g_cclosure_marshal_generic, GST_TYPE_CAPS, - 2, G_TYPE_UINT, G_TYPE_UINT); + _gst_caps_accumulator, NULL, NULL, GST_TYPE_CAPS, 2, G_TYPE_UINT, + G_TYPE_UINT); /** * GstRtpBin::payload-type-change: @@ -1736,8 +2076,7 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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: @@ -1749,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: @@ -1762,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: @@ -1775,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: @@ -1789,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 @@ -1802,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 @@ -1815,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 @@ -1828,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 @@ -1841,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: @@ -1855,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 @@ -1868,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 @@ -1881,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 @@ -1894,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: @@ -1908,8 +2282,7 @@ 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: @@ -1926,8 +2299,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_REQUEST_RTP_ENCODER] = g_signal_new ("request-rtp-encoder", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, - request_rtp_encoder), _gst_element_accumulator, NULL, - g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + request_rtp_encoder), _gst_element_accumulator, NULL, NULL, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); /** * GstRtpBin::request-rtp-decoder: @@ -1945,7 +2318,7 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) g_signal_new ("request-rtp-decoder", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, request_rtp_decoder), _gst_element_accumulator, NULL, - g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + NULL, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); /** * GstRtpBin::request-rtcp-encoder: @@ -1962,8 +2335,8 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_REQUEST_RTCP_ENCODER] = g_signal_new ("request-rtcp-encoder", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, - request_rtcp_encoder), _gst_element_accumulator, NULL, - g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + request_rtcp_encoder), _gst_element_accumulator, NULL, NULL, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); /** * GstRtpBin::request-rtcp-decoder: @@ -1980,7 +2353,35 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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, + 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); /** @@ -1998,10 +2399,27 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_NEW_JITTERBUFFER] = g_signal_new ("new-jitterbuffer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, - new_jitterbuffer), NULL, NULL, g_cclosure_marshal_generic, + 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 @@ -2016,8 +2434,9 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_REQUEST_AUX_SENDER] = g_signal_new ("request-aux-sender", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, - request_aux_sender), _gst_element_accumulator, NULL, - g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + 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 @@ -2033,36 +2452,108 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) gst_rtp_bin_signals[SIGNAL_REQUEST_AUX_RECEIVER] = g_signal_new ("request-aux-receiver", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, - request_aux_receiver), _gst_element_accumulator, NULL, - g_cclosure_marshal_generic, GST_TYPE_ELEMENT, 1, G_TYPE_UINT); - - g_object_class_install_property (gobject_class, PROP_SDES, - g_param_spec_boxed ("sdes", "SDES", - "The SDES items of this session", - GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_DO_LOST, - g_param_spec_boolean ("do-lost", "Do Lost", - "Send an event downstream when a packet is lost", DEFAULT_DO_LOST, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + request_aux_receiver), _gst_element_accumulator, NULL, NULL, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); - g_object_class_install_property (gobject_class, PROP_AUTOREMOVE, - g_param_spec_boolean ("autoremove", "Auto Remove", - "Automatically remove timed out sources", DEFAULT_AUTOREMOVE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_IGNORE_PT, - g_param_spec_boolean ("ignore-pt", "Ignore PT", - "Do not demultiplex based on PT values", DEFAULT_IGNORE_PT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - 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", - DEFAULT_USE_PIPELINE_CLOCK, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstRtpBin:buffer-mode: + * 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 +#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", + "Send an event downstream when a packet is lost", DEFAULT_DO_LOST, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_AUTOREMOVE, + g_param_spec_boolean ("autoremove", "Auto Remove", + "Automatically remove timed out sources", DEFAULT_AUTOREMOVE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_IGNORE_PT, + g_param_spec_boolean ("ignore-pt", "Ignore PT", + "Do not demultiplex based on PT values", DEFAULT_IGNORE_PT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + 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 " + "(DEPRECATED: Use ntp-time-source property)", + DEFAULT_USE_PIPELINE_CLOCK, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED)); + /** + * GstRtpBin:buffer-mode: * * Control the buffering and timestamping mode used by the jitterbuffer. */ @@ -2116,7 +2607,7 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) * * 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 + * set the #GstRtpJitterBuffer:do-retransmission property on the * #GstRtpJitterBuffer object instead. */ g_object_class_install_property (gobject_class, PROP_DO_RETRANSMISSION, @@ -2125,26 +2616,124 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) 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", @@ -2155,14 +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 @@ -2170,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); @@ -2187,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 ()); @@ -2275,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); @@ -2302,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)); @@ -2347,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; @@ -2362,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); @@ -2409,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; @@ -2451,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; @@ -2484,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; @@ -2500,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; @@ -2514,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; @@ -2569,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); @@ -2601,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; } @@ -2626,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; @@ -2693,21 +3510,46 @@ copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data) return TRUE; } -/* a new pad (SSRC) was created in @session. This signal is emited from the - * payload demuxer. */ static void -new_payload_found (GstElement * element, guint pt, GstPad * pad, - GstRtpBinStream * stream) +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"); + + 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; - GST_DEBUG ("new payload pad %d", pt); + 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); @@ -2726,15 +3568,51 @@ new_payload_found (GstElement * element, guint pt, GstPad * pad, gst_pad_sticky_events_foreach (pad, copy_sticky_events, gpad); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad); +done: + gst_object_unref (pad); + return; shutdown: { GST_DEBUG ("ignoring, we are shutting down"); - return; + goto done; + } +fec_decoder_sink_failed: + { + g_warning ("rtpbin: failed to get fec encoder sink pad for session %u", + stream->session->id); + goto done; + } +fec_decoder_src_failed: + { + g_warning ("rtpbin: failed to get fec encoder src pad for session %u", + stream->session->id); + goto done; + } +fec_decoder_link_failed: + { + g_warning ("rtpbin: failed to link fec decoder for session %u", + stream->session->id); + goto done; } } +/* a new pad (SSRC) was created in @session. This signal is 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) @@ -2764,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); @@ -2781,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) { @@ -2858,19 +3766,23 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad, 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_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING); - 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 @@ -2880,41 +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 rtpjitterbuffer src pad to pads */ - GstElementClass *klass; - GstPadTemplate *templ; - gchar *padname; - GstPad *gpad, *pad; + 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_pad_sticky_events_foreach (pad, copy_sticky_events, gpad); - 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 */ @@ -2932,15 +3834,14 @@ no_stream: } } -static gboolean +static GstPad * complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session) { - gchar *gname; guint sessid = session->id; GstPad *recv_rtp_sink; GstElement *decoder; - GstElementClass *klass; - GstPadTemplate *templ; + + g_assert (!session->recv_rtp_sink); /* get recv_rtp pad and store */ session->recv_rtp_sink = @@ -2969,6 +3870,7 @@ complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session) goto dec_src_failed; ret = gst_pad_link (decsrc, session->recv_rtp_sink); + gst_object_unref (decsrc); if (ret != GST_PAD_LINK_OK) @@ -2979,86 +3881,48 @@ complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session) recv_rtp_sink = gst_object_ref (session->recv_rtp_sink); } - GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad"); - klass = GST_ELEMENT_GET_CLASS (rtpbin); - gname = g_strdup_printf ("recv_rtp_sink_%u", sessid); - templ = gst_element_class_get_pad_template (klass, "recv_rtp_sink_%u"); - session->recv_rtp_sink_ghost = - gst_ghost_pad_new_from_template (gname, 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); - g_free (gname); - - return TRUE; + return recv_rtp_sink; /* ERRORS */ pad_failed: { g_warning ("rtpbin: failed to get session recv_rtp_sink pad"); - return FALSE; + return NULL; } dec_sink_failed: { - g_warning ("rtpbin: failed to get decoder sink pad for session %d", sessid); - return FALSE; + 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 %d", sessid); + g_warning ("rtpbin: failed to get decoder src pad for session %u", sessid); gst_object_unref (recv_rtp_sink); - return FALSE; + return NULL; } dec_link_failed: { - g_warning ("rtpbin: failed to link rtp decoder for session %d", sessid); + g_warning ("rtpbin: failed to link rtp decoder for session %u", sessid); gst_object_unref (recv_rtp_sink); - return FALSE; + return NULL; } } -/* 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) +static void +complete_session_receiver (GstRtpBin * rtpbin, GstRtpBinSession * session, + guint sessid) { - guint sessid; GstElement *aux; GstPad *recv_rtp_src; - GstRtpBinSession *session; - - /* 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); - - /* get or create session */ - session = find_session_by_id (rtpbin, sessid); - if (!session) { - GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid); - /* create session now */ - session = create_session (rtpbin, sessid); - if (session == NULL) - goto create_error; - } - /* check if pad was requested */ - if (session->recv_rtp_sink_ghost != NULL) - return session->recv_rtp_sink_ghost; - - /* setup the session sink pad */ - if (!complete_session_sink (rtpbin, session)) - goto session_sink_failed; + 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 or if we can go into the SSRC demuxer - * directly */ + /* find out if we need AUX elements */ aux = session_request_element (session, SIGNAL_REQUEST_AUX_RECEIVER); if (aux) { gchar *pname; @@ -3067,7 +3931,7 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) GST_DEBUG_OBJECT (rtpbin, "linking AUX receiver"); - pname = g_strdup_printf ("sink_%d", sessid); + pname = g_strdup_printf ("sink_%u", sessid); auxsink = gst_element_get_static_pad (aux, pname); g_free (pname); if (auxsink == NULL) @@ -3078,15 +3942,30 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (ret != GST_PAD_LINK_OK) goto aux_link_failed; - /* this can be NULL when this AUX element is not to be linked to - * an SSRC demuxer */ - pname = g_strdup_printf ("src_%d", sessid); + /* 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; @@ -3094,8 +3973,8 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) 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 (recv_rtp_src); 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, @@ -3103,12 +3982,82 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) 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) +{ + guint sessid; + GstRtpBinSession *session; + 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 %u", sessid); + + /* get or create session */ + session = find_session_by_id (rtpbin, sessid); + 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->recv_rtp_sink_ghost != NULL) + return session->recv_rtp_sink_ghost; + + /* 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, 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: @@ -3121,21 +4070,6 @@ session_sink_failed: /* warning already done */ return NULL; } -pad_failed: - { - g_warning ("rtpbin: failed to get session recv_rtp_src pad"); - return NULL; - } -aux_sink_failed: - { - g_warning ("rtpbin: failed to get AUX sink pad for session %d", sessid); - return NULL; - } -aux_link_failed: - { - g_warning ("rtpbin: failed to link AUX pad to session %d", sessid); - return NULL; - } } static void @@ -3166,37 +4100,13 @@ remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) } } -/* Create a pad for receiving RTCP for the session in @name. Must be called with - * RTP_BIN_LOCK. - */ static GstPad * -create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, - const gchar * name) +complete_session_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session, + guint sessid) { - guint sessid; GstElement *decoder; - GstRtpBinSession *session; - GstPad *sinkdpad, *decsink; - - /* 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); - - /* get or create the session */ - session = find_session_by_id (rtpbin, sessid); - if (!session) { - GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid); - /* create session now */ - session = create_session (rtpbin, sessid); - if (session == NULL) - goto create_error; - } - - /* check if pad was requested */ - if (session->recv_rtcp_sink_ghost != NULL) - return session->recv_rtcp_sink_ghost; + GstPad *sinkdpad; + GstPad *decsink = NULL; /* get recv_rtp pad and store */ GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad"); @@ -3222,6 +4132,7 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, goto dec_src_failed; ret = gst_pad_link (decsrc, session->recv_rtcp_sink); + gst_object_unref (decsrc); if (ret != GST_PAD_LINK_OK) @@ -3242,26 +4153,8 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, gst_pad_link_full (session->sync_src, sinkdpad, GST_PAD_LINK_CHECK_NOTHING); gst_object_unref (sinkdpad); - session->recv_rtcp_sink_ghost = - 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); - - return session->recv_rtcp_sink_ghost; + return decsink; - /* ERRORS */ -no_name: - { - g_warning ("rtpbin: invalid name given"); - return NULL; - } -create_error: - { - /* create_session already warned */ - return NULL; - } pad_failed: { g_warning ("rtpbin: failed to get session rtcp_sink pad"); @@ -3269,25 +4162,83 @@ pad_failed: } dec_sink_failed: { - g_warning ("rtpbin: failed to get decoder sink pad for session %d", sessid); + 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 %d", sessid); - gst_object_unref (decsink); - return NULL; + 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 %d", sessid); - gst_object_unref (decsink); - return NULL; + 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"); - gst_object_unref (decsink); + } + +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. + */ +static GstPad * +create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, + const gchar * name) +{ + guint sessid; + GstRtpBinSession *session; + 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 %u", sessid); + + /* get or create the session */ + session = find_session_by_id (rtpbin, sessid); + 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->recv_rtcp_sink_ghost != NULL) + return session->recv_rtcp_sink_ghost; + + decsink = complete_session_rtcp (rtpbin, session, sessid); + if (!decsink) + goto create_error; + + session->recv_rtcp_sink_ghost = + 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); + + return session->recv_rtcp_sink_ghost; + + /* ERRORS */ +no_name: + { + g_warning ("rtpbin: cannot find session id for pad: %s", + GST_STR_NULL (name)); + return NULL; + } +create_error: + { + /* create_session already warned */ return NULL; } } @@ -3322,11 +4273,12 @@ complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session) GstElement *encoder; GstElementClass *klass; GstPadTemplate *templ; + gboolean ret = FALSE; /* get srcpad */ - session->send_rtp_src = - gst_element_get_static_pad (session->session, "send_rtp_src"); - if (session->send_rtp_src == NULL) + 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"); @@ -3337,29 +4289,29 @@ complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session) GstPadLinkReturn ret; GST_DEBUG_OBJECT (rtpbin, "linking RTP encoder"); - ename = g_strdup_printf ("rtp_src_%d", sessid); + 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; - send_rtp_src = encsrc; - - ename = g_strdup_printf ("rtp_sink_%d", sessid); + 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 (session->send_rtp_src, encsink); + 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"); - send_rtp_src = gst_object_ref (session->send_rtp_src); } /* ghost the new source pad */ @@ -3368,37 +4320,43 @@ complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session) 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_object_unref (send_rtp_src); 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); - return TRUE; + 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 %d", sessid); - return FALSE; + g_warning ("rtpbin: failed to get rtp source pad for session %u", sessid); + goto done; } enc_src_failed: { - g_warning ("rtpbin: failed to get encoder src pad for session %d", sessid); - return FALSE; + 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 encoder sink pad for session %d", sessid); - gst_object_unref (send_rtp_src); - return FALSE; + 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 rtp encoder for session %d", sessid); - gst_object_unref (send_rtp_src); - return FALSE; + g_warning ("rtpbin: failed to link %" GST_PTR_FORMAT " for session %u", + encoder, sessid); + goto done; } } @@ -3458,22 +4416,23 @@ create_error: } existing_session: { - g_warning ("rtpbin: session %d is already a sender", sessid); - return FALSE; + 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 %d", sessid); + 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 %d", sessid); + 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 %d", sessid); + g_warning ("rtpbin: failed to complete AUX for session %u", sessid); return FALSE; } } @@ -3503,6 +4462,8 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) guint sessid; GstPad *send_rtp_sink; GstElement *aux; + GstElement *encoder; + GstElement *prev = NULL; GstRtpBinSession *session; /* first get the session number */ @@ -3526,19 +4487,47 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) 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_%d", sessid); - send_rtp_sink = gst_element_get_static_pad (aux, pname); + pname = g_strdup_printf ("sink_%u", sessid); + sinkpad = gst_element_get_static_pad (aux, pname); g_free (pname); - if (send_rtp_sink == NULL) + 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 = @@ -3549,7 +4538,17 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (!complete_session_src (rtpbin, session)) goto session_src_failed; - send_rtp_sink = gst_object_ref (session->send_rtp_sink); + 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 = @@ -3563,7 +4562,8 @@ create_send_rtp (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; } create_error: @@ -3573,27 +4573,45 @@ create_error: } existing_session: { - g_warning ("rtpbin: session %d is already in use", sessid); + 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 %d", sessid); + 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 %d", sessid); + 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; } session_src_failed: { - g_warning ("rtpbin: failed to setup source pads for session %d", sessid); + 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 %" GST_PTR_FORMAT + " sink pad for session %u", encoder, sessid); return NULL; } } @@ -3607,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); @@ -3629,7 +4643,8 @@ 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; @@ -3642,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) @@ -3664,13 +4684,13 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) GST_DEBUG_OBJECT (rtpbin, "linking RTCP encoder"); - ename = g_strdup_printf ("rtcp_src_%d", sessid); + 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_%d", sessid); + ename = g_strdup_printf ("rtcp_sink_%u", sessid); encsink = gst_element_get_static_pad (encoder, ename); g_free (ename); if (encsink == NULL) @@ -3697,33 +4717,34 @@ 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 %d", sessid); + 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 %d", sessid); + 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 %d", sessid); + g_warning ("rtpbin: failed to link rtcp encoder for session %u", sessid); gst_object_unref (encsrc); return NULL; } @@ -3746,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) @@ -3844,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;