rtpbin: allow dynamic RTP/RTCP encoders/decoders

* gst/rtpmanager/gstrtpbin.[ch]: four new action signals have been
  added (request-rtp-encoder, request-rtp-decoder, request-rtcp-encoder
  and request-rtcp-decoder). The user will be able to provide encoders
  or decoders dynamically. The encoders must follow the srtpenc API and
  the decoders the srtpdec API. Having separate signals for RTP and RTCP
  allows the user to use different encoders/decoders or provide the same
  one (e.g. that would be the case for srtpenc).

  Also, rtpbin now allows application/x-srtp in its pads.

  https://bugzilla.gnome.org/show_bug.cgi?id=719938
This commit is contained in:
Aleix Conchillo Flaqué 2013-12-05 15:53:52 -08:00 committed by Wim Taymans
parent f48bbabafc
commit 47c65fc269
2 changed files with 381 additions and 15 deletions

View file

@ -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.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
@ -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

View file

@ -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);