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
gst_rtsp_media_set_latency (GstRTSPMedia * media, guint latency)
{
GstRTSPMediaPrivate *priv;
+ guint i;
g_return_if_fail (GST_IS_RTSP_MEDIA (media));
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);
}
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)
{
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++) {
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;
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);
}
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 (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;
-
- /* 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/%d", rtx_pt, caps_rate);
- gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
- g_free (tmp);
+ 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;
+
+ 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", 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 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);
+ }
}
}
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;
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])
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
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");
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;
+}
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
{
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);
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;
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;
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
GstRTSPStreamContext * context, GstElement * payloader, GstPad * pad)
{
GstRTSPStream *stream = NULL;
- guint pt, aux_pt;
+ guint pt, aux_pt, ulpfec_pt;
GST_OBJECT_LOCK (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);
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);
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);
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)
{
(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;
guint8 channel[2];
GstRTSPStreamTransport *stream_transport;
+
+ guint ulpfec_percentage;
};
/**