Implement support for ULP Forward Error Correction

In this initial commit, interface is only exposed for RECORD,
further work will be needed in rtspsrc to support this for
PLAY.

https://bugzilla.gnome.org/show_bug.cgi?id=794911
This commit is contained in:
Mathieu Duponchelle 2018-04-02 22:49:35 +02:00
parent 9f5d3ee7a8
commit bfc35ae1ae
7 changed files with 370 additions and 32 deletions

View file

@ -630,6 +630,13 @@ gst_rtsp_stream_seekable
GstRTSPStreamTransportFilterFunc
gst_rtsp_stream_transport_filter
gst_rtsp_stream_set_ulpfec_pt
gst_rtsp_stream_get_ulpfec_pt
gst_rtsp_stream_set_ulpfec_percentage
gst_rtsp_stream_get_ulpfec_percentage
gst_rtsp_stream_request_ulpfec_decoder
gst_rtsp_stream_request_ulpfec_encoder
<SUBSECTION Standard>
GST_RTSP_STREAM_CAST
GST_RTSP_STREAM_CLASS_CAST

View file

@ -1517,6 +1517,7 @@ void
gst_rtsp_media_set_latency (GstRTSPMedia * media, guint latency)
{
GstRTSPMediaPrivate *priv;
guint i;
g_return_if_fail (GST_IS_RTSP_MEDIA (media));
@ -1526,8 +1527,19 @@ gst_rtsp_media_set_latency (GstRTSPMedia * media, guint latency)
g_mutex_lock (&priv->lock);
priv->latency = latency;
if (priv->rtpbin)
if (priv->rtpbin) {
g_object_set (priv->rtpbin, "latency", latency, NULL);
for (i = 0; i < media->priv->streams->len; i++) {
GObject *storage = NULL;
g_signal_emit_by_name (G_OBJECT (media->priv->rtpbin), "get-storage",
i, &storage);
if (storage)
g_object_set (storage, "size-time", (media->priv->latency + 50) * GST_MSECOND, NULL);
}
}
g_mutex_unlock (&priv->lock);
}
@ -3108,6 +3120,38 @@ request_aux_receiver (GstElement * rtpbin, guint sessid, GstRTSPMedia * media)
return res;
}
static GstElement *
request_fec_decoder (GstElement * rtpbin, guint sessid, GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv = media->priv;
GstRTSPStream *stream = NULL;
guint i;
GstElement *res = NULL;
g_mutex_lock (&priv->lock);
for (i = 0; i < priv->streams->len; i++) {
stream = g_ptr_array_index (priv->streams, i);
if (sessid == gst_rtsp_stream_get_index (stream))
break;
stream = NULL;
}
g_mutex_unlock (&priv->lock);
if (stream) {
res = gst_rtsp_stream_request_ulpfec_decoder (stream, rtpbin, sessid);
}
return res;
}
static void
new_storage_cb (GstElement * rtpbin, GObject * storage, guint sessid, GstRTSPMedia * media)
{
g_object_set (storage, "size-time", (media->priv->latency + 50) * GST_MSECOND, NULL);
}
static gboolean
start_prepare (GstRTSPMedia * media)
{
@ -3119,6 +3163,9 @@ start_prepare (GstRTSPMedia * media)
if (priv->status != GST_RTSP_MEDIA_STATUS_PREPARING)
goto no_longer_preparing;
g_signal_connect (priv->rtpbin, "new-storage", G_CALLBACK (new_storage_cb), media);
g_signal_connect (priv->rtpbin, "request-fec-decoder", G_CALLBACK (request_fec_decoder), media);
/* link streams we already have, other streams might appear when we have
* dynamic elements */
for (i = 0; i < priv->streams->len; i++) {
@ -3145,7 +3192,7 @@ start_prepare (GstRTSPMedia * media)
if (priv->rtpbin)
g_object_set (priv->rtpbin, "do-retransmission", priv->do_retransmission,
NULL);
"do-lost", TRUE, NULL);
for (walk = priv->dynamic; walk; walk = g_list_next (walk)) {
GstElement *elem = walk->data;
@ -3806,6 +3853,9 @@ default_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp)
s = gst_caps_get_structure (caps, 0);
gst_structure_set_name (s, "application/x-rtp");
if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
gst_rtsp_stream_set_pt_map (stream, pt, caps);
gst_caps_unref (caps);
}

View file

@ -373,40 +373,75 @@ gst_rtsp_sdp_make_media (GstSDPMessage * sdp, GstSDPInfo * info,
update_sdp_from_tags (stream, smedia);
if ((profile == GST_RTSP_PROFILE_AVPF || profile == GST_RTSP_PROFILE_SAVPF)
&& (rtx_time = gst_rtsp_stream_get_retransmission_time (stream))) {
/* ssrc multiplexed retransmit functionality */
guint rtx_pt = gst_rtsp_stream_get_retransmission_pt (stream);
if (profile == GST_RTSP_PROFILE_AVPF || profile == GST_RTSP_PROFILE_SAVPF) {
if ((rtx_time = gst_rtsp_stream_get_retransmission_time (stream))) {
/* ssrc multiplexed retransmit functionality */
guint rtx_pt = gst_rtsp_stream_get_retransmission_pt (stream);
if (rtx_pt == 0) {
g_warning ("failed to find an available dynamic payload type. "
"Not adding retransmission");
} else {
gchar *tmp;
GstStructure *s;
gint caps_pt, caps_rate;
if (rtx_pt == 0) {
g_warning ("failed to find an available dynamic payload type. "
"Not adding retransmission");
} else {
gchar *tmp;
GstStructure *s;
gint caps_pt, caps_rate;
s = gst_caps_get_structure (caps, 0);
if (s == NULL)
goto no_caps_info;
s = gst_caps_get_structure (caps, 0);
if (s == NULL)
goto no_caps_info;
/* get payload type and clock rate */
gst_structure_get_int (s, "payload", &caps_pt);
gst_structure_get_int (s, "clock-rate", &caps_rate);
/* get payload type and clock rate */
gst_structure_get_int (s, "payload", &caps_pt);
gst_structure_get_int (s, "clock-rate", &caps_rate);
tmp = g_strdup_printf ("%d", rtx_pt);
gst_sdp_media_add_format (smedia, tmp);
g_free (tmp);
tmp = g_strdup_printf ("%d", rtx_pt);
gst_sdp_media_add_format (smedia, tmp);
g_free (tmp);
tmp = g_strdup_printf ("%d rtx/%d", rtx_pt, caps_rate);
gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
g_free (tmp);
tmp = g_strdup_printf ("%d rtx/%d", rtx_pt, caps_rate);
gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
g_free (tmp);
tmp =
g_strdup_printf ("%d apt=%d;rtx-time=%" G_GINT64_FORMAT, rtx_pt,
caps_pt, GST_TIME_AS_MSECONDS (rtx_time));
gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
g_free (tmp);
tmp =
g_strdup_printf ("%d apt=%d;rtx-time=%" G_GINT64_FORMAT, rtx_pt,
caps_pt, GST_TIME_AS_MSECONDS (rtx_time));
gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
g_free (tmp);
}
}
if (gst_rtsp_stream_get_ulpfec_percentage (stream)) {
guint ulpfec_pt = gst_rtsp_stream_get_ulpfec_pt (stream);
if (ulpfec_pt == 0) {
g_warning ("failed to find an available dynamic payload type. "
"Not adding ulpfec");
} else {
gchar *tmp;
GstStructure *s;
gint caps_pt, caps_rate;
s = gst_caps_get_structure (caps, 0);
if (s == NULL)
goto no_caps_info;
/* get payload type and clock rate */
gst_structure_get_int (s, "payload", &caps_pt);
gst_structure_get_int (s, "clock-rate", &caps_rate);
tmp = g_strdup_printf ("%d", ulpfec_pt);
gst_sdp_media_add_format (smedia, tmp);
g_free (tmp);
tmp = g_strdup_printf ("%d ulpfec/%d", ulpfec_pt, caps_rate);
gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
g_free (tmp);
tmp =
g_strdup_printf ("%d apt=%d", ulpfec_pt, caps_pt);
gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
g_free (tmp);
}
}
}

View file

@ -128,6 +128,13 @@ struct _GstRTSPStreamPrivate
guint rtx_pt;
GstClockTime rtx_time;
/* Forward Error Correction with RFC 5109 */
GstElement *ulpfec_decoder;
GstElement *ulpfec_encoder;
guint ulpfec_pt;
gboolean ulpfec_enabled;
guint ulpfec_percentage;
/* pool used to manage unicast and multicast addresses */
GstRTSPAddressPool *pool;
@ -301,6 +308,10 @@ gst_rtsp_stream_finalize (GObject * obj)
g_object_unref (priv->rtxsend);
if (priv->rtxreceive)
g_object_unref (priv->rtxreceive);
if (priv->ulpfec_encoder)
gst_object_unref (priv->ulpfec_encoder);
if (priv->ulpfec_decoder)
gst_object_unref (priv->ulpfec_decoder);
for (i = 0; i < 2; i++) {
if (priv->socket_v4[i])
@ -2362,6 +2373,28 @@ done:
return;
}
static void
retrieve_ulpfec_pt (gpointer key, GstCaps *caps, GstElement *ulpfec_decoder)
{
guint pt = GPOINTER_TO_INT (key);
const GstStructure *s = gst_caps_get_structure (caps, 0);
if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
g_object_set (ulpfec_decoder, "pt", pt, NULL);
}
static void
update_ulpfec_decoder_pt (GstRTSPStream * stream)
{
if (!stream->priv->ulpfec_decoder)
goto done;
g_hash_table_foreach (stream->priv->ptmap, (GHFunc) retrieve_ulpfec_pt, stream->priv->ulpfec_decoder);
done:
return;
}
/**
* gst_rtsp_stream_request_aux_receiver:
* @stream: a #GstRTSPStream
@ -2385,6 +2418,7 @@ gst_rtsp_stream_request_aux_receiver (GstRTSPStream * stream, guint sessid)
bin = gst_bin_new (NULL);
stream->priv->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
update_rtx_receive_pt_map (stream);
update_ulpfec_decoder_pt (stream);
gst_bin_add (GST_BIN (bin), gst_object_ref (stream->priv->rtxreceive));
pad = gst_element_get_static_pad (stream->priv->rtxreceive, "src");
@ -4982,3 +5016,133 @@ gst_rtsp_stream_handle_keymgmt (GstRTSPStream * stream, const gchar * keymgmt)
g_strfreev (specs);
return TRUE;
}
/**
* gst_rtsp_stream_get_ulpfec_pt:
*
* Returns: the payload type used for ULPFEC protection packets
*
* Since: 1.16
*/
guint
gst_rtsp_stream_get_ulpfec_pt (GstRTSPStream *stream)
{
guint res;
g_mutex_lock (&stream->priv->lock);
res = stream->priv->ulpfec_pt;
g_mutex_unlock (&stream->priv->lock);
return res;
}
/**
* gst_rtsp_stream_set_ulpfec_pt:
*
* Set the payload type to be used for ULPFEC protection packets
*
* Since: 1.16
*/
void
gst_rtsp_stream_set_ulpfec_pt (GstRTSPStream * stream, guint pt)
{
g_return_if_fail (GST_IS_RTSP_STREAM (stream));
g_mutex_lock (&stream->priv->lock);
stream->priv->ulpfec_pt = pt;
if (stream->priv->ulpfec_encoder) {
g_object_set (stream->priv->ulpfec_encoder, "pt", pt, NULL);
}
g_mutex_unlock (&stream->priv->lock);
}
/**
* gst_rtsp_stream_request_ulpfec_decoder:
*
* Creating a rtpulpfecdec element
*
* Returns: (transfer full) (nullable): a #GstElement.
*
* Since: 1.16
*/
GstElement *
gst_rtsp_stream_request_ulpfec_decoder (GstRTSPStream * stream, GstElement *rtpbin, guint sessid)
{
GObject *internal_storage = NULL;
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
stream->priv->ulpfec_decoder = gst_object_ref (gst_element_factory_make ("rtpulpfecdec", NULL));
g_signal_emit_by_name (G_OBJECT (rtpbin), "get-internal-storage", sessid, &internal_storage);
g_object_set (stream->priv->ulpfec_decoder, "storage", internal_storage, NULL);
g_object_unref (internal_storage);
update_ulpfec_decoder_pt (stream);
return stream->priv->ulpfec_decoder;
}
/**
* gst_rtsp_stream_request_ulpfec_encoder:
*
* Creating a rtpulpfecenc element
*
* Returns: (transfer full) (nullable): a #GstElement.
*
* Since: 1.16
*/
GstElement *
gst_rtsp_stream_request_ulpfec_encoder (GstRTSPStream * stream, guint sessid)
{
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
if (!stream->priv->ulpfec_percentage)
return NULL;
stream->priv->ulpfec_encoder = gst_object_ref (gst_element_factory_make ("rtpulpfecenc", NULL));
g_object_set (stream->priv->ulpfec_encoder, "pt", stream->priv->ulpfec_pt, "percentage", stream->priv->ulpfec_percentage, NULL);
return stream->priv->ulpfec_encoder;
}
/**
* gst_rtsp_stream_set_ulpfec_percentage:
*
* Sets the amount of redundancy to apply when creating ULPFEC
* protection packets.
*
* Since: 1.16
*/
void
gst_rtsp_stream_set_ulpfec_percentage (GstRTSPStream *stream, guint percentage)
{
g_return_if_fail (GST_IS_RTSP_STREAM (stream));
g_mutex_lock (&stream->priv->lock);
stream->priv->ulpfec_percentage = percentage;
if (stream->priv->ulpfec_encoder) {
g_object_set (stream->priv->ulpfec_encoder, "percentage", percentage, NULL);
}
g_mutex_unlock (&stream->priv->lock);
}
/**
* gst_rtsp_stream_get_ulpfec_percentage:
*
* Returns: the amount of redundancy applied when creating ULPFEC
* protection packets.
*
* Since: 1.16
*/
guint
gst_rtsp_stream_get_ulpfec_percentage (GstRTSPStream *stream)
{
guint res;
g_mutex_lock (&stream->priv->lock);
res = stream->priv->ulpfec_percentage;
g_mutex_unlock (&stream->priv->lock);
return res;
}

View file

@ -307,6 +307,28 @@ gboolean gst_rtsp_stream_is_receiver (GstRTSPStream * stream);
GST_RTSP_SERVER_API
gboolean gst_rtsp_stream_handle_keymgmt (GstRTSPStream *stream, const gchar *keymgmt);
/* ULP Forward Error Correction (RFC 5109) */
GST_RTSP_SERVER_API
gboolean gst_rtsp_stream_get_ulpfec_enabled (GstRTSPStream *stream);
GST_RTSP_SERVER_API
void gst_rtsp_stream_set_ulpfec_pt (GstRTSPStream *stream, guint pt);
GST_RTSP_SERVER_API
guint gst_rtsp_stream_get_ulpfec_pt (GstRTSPStream *stream);
GST_RTSP_SERVER_API
GstElement * gst_rtsp_stream_request_ulpfec_decoder (GstRTSPStream *stream, GstElement *rtpbin, guint sessid);
GST_RTSP_SERVER_API
GstElement * gst_rtsp_stream_request_ulpfec_encoder (GstRTSPStream *stream, guint sessid);
GST_RTSP_SERVER_API
void gst_rtsp_stream_set_ulpfec_percentage (GstRTSPStream *stream, guint percentage);
GST_RTSP_SERVER_API
guint gst_rtsp_stream_get_ulpfec_percentage (GstRTSPStream *stream);
/**
* GstRTSPStreamTransportFilterFunc:
* @stream: a #GstRTSPStream object

View file

@ -109,14 +109,18 @@ struct _GstRtspClientSinkPad
{
GstGhostPad parent;
GstElement *custom_payloader;
guint ulpfec_percentage;
};
enum
{
PROP_PAD_0,
PROP_PAD_PAYLOADER
PROP_PAD_PAYLOADER,
PROP_PAD_ULPFEC_PERCENTAGE
};
#define DEFAULT_PAD_ULPFEC_PERCENTAGE 0
static GType gst_rtsp_client_sink_pad_get_type (void);
G_DEFINE_TYPE (GstRtspClientSinkPad, gst_rtsp_client_sink_pad,
GST_TYPE_GHOST_PAD);
@ -140,6 +144,11 @@ gst_rtsp_client_sink_pad_set_property (GObject * object, guint prop_id,
gst_object_ref_sink (pad->custom_payloader);
GST_OBJECT_UNLOCK (pad);
break;
case PROP_PAD_ULPFEC_PERCENTAGE:
GST_OBJECT_LOCK (pad);
pad->ulpfec_percentage = g_value_get_uint (value);
GST_OBJECT_UNLOCK (pad);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -160,6 +169,11 @@ gst_rtsp_client_sink_pad_get_property (GObject * object, guint prop_id,
g_value_set_object (value, pad->custom_payloader);
GST_OBJECT_UNLOCK (pad);
break;
case PROP_PAD_ULPFEC_PERCENTAGE:
GST_OBJECT_LOCK (pad);
g_value_set_uint (value, pad->ulpfec_percentage);
GST_OBJECT_UNLOCK (pad);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -192,6 +206,11 @@ gst_rtsp_client_sink_pad_class_init (GstRtspClientSinkPadClass * klass)
g_param_spec_object ("payloader", "Payloader",
"The payloader element to use (NULL = default automatically selected)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_klass, PROP_PAD_ULPFEC_PERCENTAGE,
g_param_spec_uint ("ulpfec-percentage", "ULPFEC percentage",
"The percentage of ULP redundancy to apply", 0, 100, DEFAULT_PAD_ULPFEC_PERCENTAGE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
@ -1099,7 +1118,7 @@ gst_rtsp_client_sink_create_stream (GstRTSPClientSink * sink,
GstRTSPStreamContext * context, GstElement * payloader, GstPad * pad)
{
GstRTSPStream *stream = NULL;
guint pt, aux_pt;
guint pt, aux_pt, ulpfec_pt;
GST_OBJECT_LOCK (sink);
@ -1125,6 +1144,11 @@ gst_rtsp_client_sink_create_stream (GstRTSPClientSink * sink,
goto no_free_pt;
sink->next_dyn_pt++;
ulpfec_pt = sink->next_dyn_pt;
if (ulpfec_pt > 127)
goto no_free_pt;
sink->next_dyn_pt++;
GST_OBJECT_UNLOCK (sink);
@ -1143,6 +1167,9 @@ gst_rtsp_client_sink_create_stream (GstRTSPClientSink * sink,
gst_rtsp_stream_set_mtu (stream, sink->rtp_blocksize);
gst_rtsp_stream_set_multicast_iface (stream, sink->multi_iface);
gst_rtsp_stream_set_ulpfec_pt (stream, ulpfec_pt);
gst_rtsp_stream_set_ulpfec_percentage (stream, context->ulpfec_percentage);
#if 0
if (priv->pool)
gst_rtsp_stream_set_address_pool (stream, priv->pool);
@ -1229,6 +1256,8 @@ gst_rtsp_client_sink_setup_payloader (GstRTSPClientSink * sink, GstPad * pad,
gst_object_unref (GST_OBJECT (sinkpad));
GST_RTSP_STATE_UNLOCK (sink);
context->ulpfec_percentage = cspad->ulpfec_percentage;
gst_element_sync_state_with_parent (payloader);
gst_object_unref (payloader);
@ -3502,6 +3531,32 @@ request_aux_sender (GstElement * rtpbin, guint sessid, GstRTSPClientSink * sink)
return ret;
}
static GstElement *
request_fec_encoder (GstElement * rtpbin, guint sessid, GstRTSPClientSink * sink)
{
GstRTSPStream *stream = NULL;
GstElement *ret = NULL;
GList *walk;
GST_RTSP_STATE_LOCK (sink);
for (walk = sink->contexts; walk; walk = g_list_next (walk)) {
GstRTSPStreamContext *context = (GstRTSPStreamContext *) walk->data;
if (sessid == gst_rtsp_stream_get_index (context->stream)) {
stream = context->stream;
break;
}
}
if (stream != NULL) {
ret = gst_rtsp_stream_request_ulpfec_encoder (stream, sessid);
}
GST_RTSP_STATE_UNLOCK (sink);
return ret;
}
static gboolean
gst_rtsp_client_sink_collect_streams (GstRTSPClientSink * sink)
{
@ -3568,6 +3623,9 @@ gst_rtsp_client_sink_collect_streams (GstRTSPClientSink * sink)
(GCallback) request_aux_sender, sink);
}
g_signal_connect (sink->rtpbin, "request-fec-encoder",
(GCallback) request_fec_encoder, sink);
if (!gst_rtsp_stream_join_bin (context->stream,
GST_BIN (sink->internal_bin), sink->rtpbin, GST_STATE_PAUSED)) {
goto join_bin_failed;

View file

@ -118,6 +118,8 @@ struct _GstRTSPStreamContext {
guint8 channel[2];
GstRTSPStreamTransport *stream_transport;
guint ulpfec_percentage;
};
/**