diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 447be9a0af..7258dedd59 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -63,6 +63,16 @@ * RTPSession object which further provides action signals to retrieve the * internal source and other sources. * + * #GstRtpBin also has action signals (#GstRtpBin::request-rtp-encoder, + * #GstRtpBin::request-rtp-decoder, #GstRtpBin::request-rtcp-encoder and + * #GstRtpBin::request-rtp-decoder) to dynamically request for RTP and RTCP encoders + * and decoders in order to support SRTP. The encoders must provide the pads + * rtp_sink_\%d and rtp_src_\%d for RTP and rtcp_sink_\%d and rtcp_src_\%d for + * RTCP. The session number will be used in the pad name. The decoders must provide + * rtp_sink and rtp_src for RTP and rtcp_sink and rtcp_src for RTCP. The decoders will + * be placed before the #GstRtpSession element, thus they must support SSRC demuxing + * internally. + * * * Example pipelines * |[ @@ -135,17 +145,17 @@ GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug); /* sink pads */ static GstStaticPadTemplate rtpbin_recv_rtp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%u", + GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp") + GST_STATIC_CAPS ("application/x-rtp;application/x-srtp") ); static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template = -GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%u", + GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") + GST_STATIC_CAPS ("application/x-rtcp;application/x-srtcp") ); static GstStaticPadTemplate rtpbin_send_rtp_sink_template = @@ -164,17 +174,17 @@ GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%u_%u_%u", ); static GstStaticPadTemplate rtpbin_send_rtcp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%u", + GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%u", GST_PAD_SRC, GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtcp") + GST_STATIC_CAPS ("application/x-rtcp;application/x-srtcp") ); static GstStaticPadTemplate rtpbin_send_rtp_src_template = -GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%u", + GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp") + GST_STATIC_CAPS ("application/x-rtp;application/x-srtp") ); #define GST_RTP_BIN_GET_PRIVATE(obj) \ @@ -238,6 +248,12 @@ enum SIGNAL_ON_TIMEOUT, SIGNAL_ON_SENDER_TIMEOUT, SIGNAL_ON_NPT_STOP, + + SIGNAL_REQUEST_RTP_ENCODER, + SIGNAL_REQUEST_RTP_DECODER, + SIGNAL_REQUEST_RTCP_ENCODER, + SIGNAL_REQUEST_RTCP_DECODER, + LAST_SIGNAL }; @@ -387,6 +403,12 @@ struct _GstRtpBinSession /* list of GstRtpBinStream */ GSList *streams; + /* list of encoders */ + GSList *encoders; + + /* list of decoders */ + GSList *decoders; + /* mapping of payload type to caps */ GHashTable *ptmap; @@ -644,6 +666,12 @@ no_demux: } } +static void +remove_bin_element (GstElement * element, GstRtpBin * bin) +{ + gst_bin_remove (GST_BIN_CAST (bin), element); +} + /* called with RTP_BIN_LOCK */ static void free_session (GstRtpBinSession * sess, GstRtpBin * bin) @@ -664,6 +692,12 @@ free_session (GstRtpBinSession * sess, GstRtpBin * bin) gst_bin_remove (GST_BIN_CAST (bin), sess->session); gst_bin_remove (GST_BIN_CAST (bin), sess->demux); + g_slist_foreach (sess->encoders, (GFunc) remove_bin_element, bin); + g_slist_free (sess->encoders); + + g_slist_foreach (sess->decoders, (GFunc) remove_bin_element, bin); + g_slist_free (sess->decoders); + g_slist_foreach (sess->streams, (GFunc) free_stream, bin); g_slist_free (sess->streams); @@ -834,6 +868,18 @@ gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id) return internal_session; } +static GstElement * +gst_rtp_bin_request_encoder (GstRtpBin * bin, guint session_id) +{ + return NULL; +} + +static GstElement * +gst_rtp_bin_request_decoder (GstRtpBin * bin, guint session_id) +{ + return NULL; +} + static void gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin, const gchar * name, const GValue * value) @@ -1786,6 +1832,70 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + /** + * GstRtpBin::request-rtp-encoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTP encoder element for the given @session. The encoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTP_ENCODER] = + g_signal_new ("request-rtp-encoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtp_encoder), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-rtp-decoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTP decoder element for the given @session. The decoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTP_DECODER] = + g_signal_new ("request-rtp-decoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtp_decoder), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-rtcp-encoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTCP encoder element for the given @session. The encoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTCP_ENCODER] = + g_signal_new ("request-rtcp-encoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtcp_encoder), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_ELEMENT, 1, G_TYPE_UINT); + + /** + * GstRtpBin::request-rtcp-decoder: + * @rtpbin: the object which received the signal + * @session: the session + * + * Request an RTCP decoder element for the given @session. The decoder + * element will be added to the bin if not previously added. + * + * If no handler is connected, no encoder will be used. + */ + gst_rtp_bin_signals[SIGNAL_REQUEST_RTCP_DECODER] = + g_signal_new ("request-rtcp-decoder", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass, + request_rtcp_decoder), NULL, 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", @@ -1899,6 +2009,10 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) klass->reset_sync = GST_DEBUG_FUNCPTR (gst_rtp_bin_reset_sync); klass->get_internal_session = GST_DEBUG_FUNCPTR (gst_rtp_bin_get_internal_session); + klass->request_rtp_encoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_encoder); + klass->request_rtp_decoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_decoder); + klass->request_rtcp_encoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_encoder); + klass->request_rtcp_decoder = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_decoder); GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin"); } @@ -2396,6 +2510,74 @@ gst_rtp_bin_change_state (GstElement * element, GstStateChange transition) return res; } +static GstElement * +session_request_encoder (GstRtpBinSession * session, guint signal) +{ + GstElement *encoder = NULL; + + g_signal_emit (session->bin, gst_rtp_bin_signals[signal], 0, session->id, + &encoder); + + if (encoder) { + if (g_slist_find (session->encoders, encoder)) { + GST_DEBUG_OBJECT (session->bin, "requested encoder %p already in bin", + encoder); + } else { + GST_DEBUG_OBJECT (session->bin, "adding requested encoder %p", encoder); + if (!gst_bin_add (GST_BIN_CAST (session->bin), encoder)) + goto add_failed; + if (!gst_element_sync_state_with_parent (encoder)) + GST_WARNING_OBJECT (session->bin, + "unable to sync encoder state with rtpbin"); + session->encoders = g_slist_append (session->encoders, encoder); + } + } + + return encoder; + + /* ERRORS */ +add_failed: + { + GST_WARNING_OBJECT (session->bin, "unable to add encoder"); + gst_object_unref (encoder); + return NULL; + } +} + +static GstElement * +session_request_decoder (GstRtpBinSession * session, guint signal) +{ + GstElement *decoder = NULL; + + g_signal_emit (session->bin, gst_rtp_bin_signals[signal], 0, session->id, + &decoder); + + if (decoder) { + if (g_slist_find (session->decoders, decoder)) { + GST_DEBUG_OBJECT (session->bin, "requested decoder %p already in bin", + decoder); + } else { + GST_DEBUG_OBJECT (session->bin, "adding requested decoder %p", decoder); + if (!gst_bin_add (GST_BIN_CAST (session->bin), decoder)) + goto add_failed; + if (!gst_element_sync_state_with_parent (decoder)) + GST_WARNING_OBJECT (session->bin, + "unable to sync decoder state with rtpbin"); + session->decoders = g_slist_append (session->decoders, decoder); + } + } + + return decoder; + + /* ERRORS */ +add_failed: + { + GST_WARNING_OBJECT (session->bin, "unable to add decoder"); + gst_object_unref (decoder); + return NULL; + } +} + /* a new pad (SSRC) was created in @session. This signal is emited from the * payload demuxer. */ static void @@ -2637,8 +2819,9 @@ no_stream: static GstPad * create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { - GstPad *sinkdpad; guint sessid; + GstElement *decoder; + GstPad *sinkdpad, *decsink; GstRtpBinSession *session; /* first get the session number */ @@ -2671,6 +2854,32 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) g_signal_connect (session->recv_rtp_sink, "notify::caps", (GCallback) caps_changed, session); + GST_DEBUG_OBJECT (rtpbin, "requesting RTP decoder"); + decoder = session_request_decoder (session, SIGNAL_REQUEST_RTP_DECODER); + if (decoder) { + GstPad *decsrc; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTP decoder"); + decsink = gst_element_get_static_pad (decoder, "rtp_sink"); + decsrc = gst_element_get_static_pad (decoder, "rtp_src"); + + if (decsink == NULL) + goto dec_sink_failed; + + if (decsrc == NULL) + goto dec_src_failed; + + ret = gst_pad_link (decsrc, session->recv_rtp_sink); + gst_object_unref (decsrc); + + if (ret != GST_PAD_LINK_OK) + goto dec_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTP decoder given"); + decsink = session->recv_rtp_sink; + } + GST_DEBUG_OBJECT (rtpbin, "getting RTP src pad"); /* get srcpad, link to SSRCDemux */ session->recv_rtp_src = @@ -2693,7 +2902,7 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad"); session->recv_rtp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ); + gst_ghost_pad_new_from_template (name, decsink, templ); gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost); @@ -2715,6 +2924,21 @@ pad_failed: g_warning ("rtpbin: failed to get session pad"); return NULL; } +dec_sink_failed: + { + g_warning ("rtpbin: failed to get decoder sink pad for session %d", sessid); + return NULL; + } +dec_src_failed: + { + g_warning ("rtpbin: failed to get decoder src pad for session %d", sessid); + return NULL; + } +dec_link_failed: + { + g_warning ("rtpbin: failed to link rtp decoder for session %d", sessid); + return NULL; + } } static void @@ -2753,8 +2977,9 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { guint sessid; + GstElement *decoder; GstRtpBinSession *session; - GstPad *sinkdpad; + GstPad *sinkdpad, *decsink; /* first get the session number */ if (name == NULL || sscanf (name, "recv_rtcp_sink_%u", &sessid) != 1) @@ -2783,6 +3008,32 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, if (session->recv_rtcp_sink == NULL) goto pad_failed; + GST_DEBUG_OBJECT (rtpbin, "getting RTCP decoder"); + decoder = session_request_decoder (session, SIGNAL_REQUEST_RTCP_DECODER); + if (decoder) { + GstPad *decsrc; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTCP decoder"); + decsink = gst_element_get_static_pad (decoder, "rtcp_sink"); + decsrc = gst_element_get_static_pad (decoder, "rtcp_src"); + + if (decsink == NULL) + goto dec_sink_failed; + + if (decsrc == NULL) + goto dec_src_failed; + + ret = gst_pad_link (decsrc, session->recv_rtcp_sink); + gst_object_unref (decsrc); + + if (ret != GST_PAD_LINK_OK) + goto dec_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTCP decoder given"); + decsink = session->recv_rtcp_sink; + } + /* get srcpad, link to SSRCDemux */ GST_DEBUG_OBJECT (rtpbin, "getting sync src pad"); session->sync_src = gst_element_get_static_pad (session->session, "sync_src"); @@ -2795,7 +3046,7 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, gst_object_unref (sinkdpad); session->recv_rtcp_sink_ghost = - gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ); + gst_ghost_pad_new_from_template (name, decsink, templ); gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtcp_sink_ghost); @@ -2818,6 +3069,21 @@ pad_failed: g_warning ("rtpbin: failed to get session pad"); return NULL; } +dec_sink_failed: + { + g_warning ("rtpbin: failed to get decoder sink pad for session %d", sessid); + return NULL; + } +dec_src_failed: + { + g_warning ("rtpbin: failed to get decoder src pad for session %d", sessid); + return NULL; + } +dec_link_failed: + { + g_warning ("rtpbin: failed to link rtcp decoder for session %d", sessid); + return NULL; + } } static void @@ -2849,6 +3115,8 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { gchar *gname; guint sessid; + GstPad *encsrc; + GstElement *encoder; GstRtpBinSession *session; GstElementClass *klass; @@ -2886,12 +3154,43 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (session->send_rtp_src == NULL) goto no_srcpad; + GST_DEBUG_OBJECT (rtpbin, "getting RTP encoder"); + encoder = session_request_encoder (session, SIGNAL_REQUEST_RTP_ENCODER); + if (encoder) { + gchar *ename; + GstPad *encsink; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTP encoder"); + ename = g_strdup_printf ("rtp_sink_%d", sessid); + encsink = gst_element_get_request_pad (encoder, ename); + g_free (ename); + ename = g_strdup_printf ("rtp_src_%d", sessid); + encsrc = gst_element_get_static_pad (encoder, ename); + g_free (ename); + + if (encsink == NULL) + goto enc_sink_failed; + + if (encsrc == NULL) + goto enc_src_failed; + + ret = gst_pad_link (session->send_rtp_src, encsink); + gst_object_unref (encsink); + + if (ret != GST_PAD_LINK_OK) + goto enc_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTP encoder given"); + encsrc = session->send_rtp_src; + } + /* ghost the new source pad */ klass = GST_ELEMENT_GET_CLASS (rtpbin); gname = g_strdup_printf ("send_rtp_src_%u", sessid); templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%u"); session->send_rtp_src_ghost = - gst_ghost_pad_new_from_template (gname, session->send_rtp_src, templ); + gst_ghost_pad_new_from_template (gname, encsrc, templ); gst_pad_set_active (session->send_rtp_src_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_src_ghost); g_free (gname); @@ -2919,6 +3218,21 @@ no_srcpad: g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid); return NULL; } +enc_sink_failed: + { + g_warning ("rtpbin: failed to get encoder sink pad for session %d", sessid); + return NULL; + } +enc_src_failed: + { + g_warning ("rtpbin: failed to get encoder src pad for session %d", sessid); + return NULL; + } +enc_link_failed: + { + g_warning ("rtpbin: failed to link rtp encoder for session %d", sessid); + return NULL; + } } static void @@ -2955,6 +3269,8 @@ static GstPad * create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { guint sessid; + GstPad *encsrc; + GstElement *encoder; GstRtpBinSession *session; /* first get the session number */ @@ -2976,8 +3292,39 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (session->send_rtcp_src == NULL) goto pad_failed; + GST_DEBUG_OBJECT (rtpbin, "getting RTCP encoder"); + encoder = session_request_encoder (session, SIGNAL_REQUEST_RTCP_ENCODER); + if (encoder) { + gchar *ename; + GstPad *encsink; + GstPadLinkReturn ret; + + GST_DEBUG_OBJECT (rtpbin, "linking RTCP encoder"); + ename = g_strdup_printf ("rtcp_sink_%d", sessid); + encsink = gst_element_get_request_pad (encoder, ename); + g_free (ename); + ename = g_strdup_printf ("rtcp_src_%d", sessid); + encsrc = gst_element_get_static_pad (encoder, ename); + g_free (ename); + + if (encsink == NULL) + goto enc_sink_failed; + + if (encsrc == NULL) + goto enc_src_failed; + + ret = gst_pad_link (session->send_rtcp_src, encsink); + gst_object_unref (encsink); + + if (ret != GST_PAD_LINK_OK) + goto enc_link_failed; + } else { + GST_DEBUG_OBJECT (rtpbin, "no RTCP encoder given"); + encsrc = session->send_rtcp_src; + } + session->send_rtcp_src_ghost = - gst_ghost_pad_new_from_template (name, session->send_rtcp_src, templ); + gst_ghost_pad_new_from_template (name, encsrc, templ); gst_pad_set_active (session->send_rtcp_src_ghost, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtcp_src_ghost); @@ -2999,6 +3346,21 @@ pad_failed: g_warning ("rtpbin: failed to get rtcp pad for session %d", sessid); return NULL; } +enc_sink_failed: + { + g_warning ("rtpbin: failed to get encoder sink pad for session %d", sessid); + return NULL; + } +enc_src_failed: + { + g_warning ("rtpbin: failed to get encoder src pad for session %d", sessid); + return NULL; + } +enc_link_failed: + { + g_warning ("rtpbin: failed to link rtcp encoder for session %d", sessid); + return NULL; + } } static void diff --git a/gst/rtpmanager/gstrtpbin.h b/gst/rtpmanager/gstrtpbin.h index e46399274e..e907405b68 100644 --- a/gst/rtpmanager/gstrtpbin.h +++ b/gst/rtpmanager/gstrtpbin.h @@ -83,7 +83,11 @@ struct _GstRtpBinClass { /* action signals */ void (*clear_pt_map) (GstRtpBin *rtpbin); void (*reset_sync) (GstRtpBin *rtpbin); - RTPSession* (*get_internal_session) (GstRtpBin *rtpbin, guint session_id); + RTPSession* (*get_internal_session) (GstRtpBin *rtpbin, guint session); + GstElement* (*request_rtp_encoder) (GstRtpBin *rtpbin, guint session); + GstElement* (*request_rtp_decoder) (GstRtpBin *rtpbin, guint session); + GstElement* (*request_rtcp_encoder) (GstRtpBin *rtpbin, guint session); + GstElement* (*request_rtcp_decoder) (GstRtpBin *rtpbin, guint session); /* session manager signals */ void (*on_new_ssrc) (GstRtpBin *rtpbin, guint session, guint32 ssrc);