mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-02 08:42:32 +00:00
webrtc: support renegotiating adding/removing RTX
We need to always add the RTX/RED/ULPFEC elements as rtpbin will only call us once to request aux/fec senders/receivers. We also need to regenerate the media section of the SDP instead of blindly copying from the previous offer. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1762>
This commit is contained in:
parent
8a598deef2
commit
b7d0ddd1a4
6 changed files with 484 additions and 350 deletions
|
@ -2943,10 +2943,10 @@ _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
|
||||||
/* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
|
/* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
|
||||||
static gboolean
|
static gboolean
|
||||||
sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
|
sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
|
||||||
GstWebRTCRTPTransceiver * trans, guint media_idx,
|
const GstSDPMedia * last_media, GstWebRTCRTPTransceiver * trans,
|
||||||
GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
|
guint media_idx, GString * bundled_mids, guint bundle_idx,
|
||||||
gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids,
|
gchar * bundle_ufrag, gchar * bundle_pwd, GArray * reserved_pts,
|
||||||
GError ** error)
|
GHashTable * all_mids, GError ** error)
|
||||||
{
|
{
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* rtp header extensions
|
* rtp header extensions
|
||||||
|
@ -2965,8 +2965,7 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
|
||||||
GstStructure *extmap;
|
GstStructure *extmap;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
|
if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
|
||||||
|| trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
g_assert (trans->mline == -1 || trans->mline == media_idx);
|
g_assert (trans->mline == -1 || trans->mline == media_idx);
|
||||||
|
@ -2974,8 +2973,49 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
|
||||||
bundle_only = bundled_mids && bundle_idx != media_idx
|
bundle_only = bundled_mids && bundle_idx != media_idx
|
||||||
&& webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
|
&& webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
|
||||||
|
|
||||||
/* mandated by JSEP */
|
caps = _find_codec_preferences (webrtc, trans, media_idx, error);
|
||||||
gst_sdp_media_add_attribute (media, "setup", "actpass");
|
caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
|
||||||
|
caps);
|
||||||
|
|
||||||
|
if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
|
||||||
|
gst_clear_caps (&caps);
|
||||||
|
|
||||||
|
if (last_media) {
|
||||||
|
guint i, n;
|
||||||
|
|
||||||
|
n = gst_sdp_media_formats_len (last_media);
|
||||||
|
if (n > 0) {
|
||||||
|
caps = gst_caps_new_empty ();
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
guint fmt = atoi (gst_sdp_media_get_format (last_media, i));
|
||||||
|
GstCaps *tmp = gst_sdp_media_get_caps_from_media (last_media, fmt);
|
||||||
|
GstStructure *s = gst_caps_get_structure (tmp, 0);
|
||||||
|
gst_structure_set_name (s, "application/x-rtp");
|
||||||
|
gst_caps_append_structure (caps, gst_structure_copy (s));
|
||||||
|
gst_clear_caps (&tmp);
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (webrtc, "using previously negotiated caps for "
|
||||||
|
"transceiver %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, trans, caps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caps) {
|
||||||
|
GST_WARNING_OBJECT (webrtc, "no caps available for transceiver %"
|
||||||
|
GST_PTR_FORMAT ", skipping", trans);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_media) {
|
||||||
|
const char *setup = gst_sdp_media_get_attribute_val (last_media, "setup");
|
||||||
|
if (setup)
|
||||||
|
gst_sdp_media_add_attribute (media, "setup", setup);
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
/* mandated by JSEP */
|
||||||
|
gst_sdp_media_add_attribute (media, "setup", "actpass");
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: deal with ICE restarts */
|
/* FIXME: deal with ICE restarts */
|
||||||
if (last_offer && trans->mline != -1 && trans->mid) {
|
if (last_offer && trans->mline != -1 && trans->mid) {
|
||||||
|
@ -3022,15 +3062,6 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
|
||||||
gst_sdp_media_add_attribute (media, direction, "");
|
gst_sdp_media_add_attribute (media, direction, "");
|
||||||
g_free (direction);
|
g_free (direction);
|
||||||
|
|
||||||
caps = _find_codec_preferences (webrtc, trans, media_idx, error);
|
|
||||||
|
|
||||||
if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
|
|
||||||
GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
|
|
||||||
if (caps)
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
caps = gst_caps_make_writable (caps);
|
caps = gst_caps_make_writable (caps);
|
||||||
|
|
||||||
/* When an extmap is defined twice for the same ID, firefox complains and
|
/* When an extmap is defined twice for the same ID, firefox complains and
|
||||||
|
@ -3385,9 +3416,11 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
|
||||||
trans = g_ptr_array_index (webrtc->priv->transceivers, j);
|
trans = g_ptr_array_index (webrtc->priv->transceivers, j);
|
||||||
|
|
||||||
if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
|
if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
|
||||||
GstSDPMedia *media;
|
|
||||||
const gchar *mid;
|
|
||||||
WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
|
WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
|
||||||
|
const char *mid;
|
||||||
|
GstSDPMedia media;
|
||||||
|
|
||||||
|
memset (&media, 0, sizeof (media));
|
||||||
|
|
||||||
g_assert (!g_list_find (seen_transceivers, trans));
|
g_assert (!g_list_find (seen_transceivers, trans));
|
||||||
|
|
||||||
|
@ -3405,30 +3438,35 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
|
||||||
GST_PTR_FORMAT " with mid %s into media index %u", trans,
|
GST_PTR_FORMAT " with mid %s into media index %u", trans,
|
||||||
trans->mid, media_idx);
|
trans->mid, media_idx);
|
||||||
|
|
||||||
/* FIXME: deal with format changes */
|
if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
|
||||||
gst_sdp_media_copy (last_media, &media);
|
reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
|
||||||
_media_replace_direction (media, trans->direction);
|
|
||||||
|
|
||||||
mid = gst_sdp_media_get_attribute_val (media, "mid");
|
|
||||||
g_assert (mid);
|
|
||||||
|
|
||||||
if (g_hash_table_contains (all_mids, mid)) {
|
|
||||||
gst_sdp_media_free (media);
|
|
||||||
g_set_error (error, GST_WEBRTC_ERROR,
|
|
||||||
GST_WEBRTC_ERROR_INTERNAL_FAILURE,
|
|
||||||
"Duplicate mid %s when creating offer", mid);
|
|
||||||
goto cancel_offer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_insert (all_mids, g_strdup (mid), NULL);
|
gst_sdp_media_init (&media);
|
||||||
|
if (!sdp_media_from_transceiver (webrtc, &media, last_media, trans,
|
||||||
|
media_idx, bundled_mids, 0, bundle_ufrag, bundle_pwd,
|
||||||
|
reserved_pts, all_mids, error)) {
|
||||||
|
gst_sdp_media_uninit (&media);
|
||||||
|
if (!*error)
|
||||||
|
g_set_error_literal (error, GST_WEBRTC_ERROR,
|
||||||
|
GST_WEBRTC_ERROR_INTERNAL_FAILURE,
|
||||||
|
"Could not reuse transceiver");
|
||||||
|
}
|
||||||
|
|
||||||
if (bundled_mids)
|
if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
|
||||||
g_string_append_printf (bundled_mids, " %s", mid);
|
g_array_free (reserved_pts, TRUE);
|
||||||
|
reserved_pts = NULL;
|
||||||
|
}
|
||||||
|
if (*error)
|
||||||
|
goto cancel_offer;
|
||||||
|
|
||||||
gst_sdp_message_add_media (ret, media);
|
mid = gst_sdp_media_get_attribute_val (&media, "mid");
|
||||||
|
g_assert (mid && g_strcmp0 (last_mid, mid) == 0);
|
||||||
|
|
||||||
|
gst_sdp_message_add_media (ret, &media);
|
||||||
media_idx++;
|
media_idx++;
|
||||||
|
|
||||||
gst_sdp_media_free (media);
|
gst_sdp_media_uninit (&media);
|
||||||
seen_transceivers = g_list_prepend (seen_transceivers, trans);
|
seen_transceivers = g_list_prepend (seen_transceivers, trans);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3559,7 +3597,7 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
|
||||||
GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
|
GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
|
||||||
"index %u", trans, media_idx);
|
"index %u", trans, media_idx);
|
||||||
|
|
||||||
if (sdp_media_from_transceiver (webrtc, &media, trans, media_idx,
|
if (sdp_media_from_transceiver (webrtc, &media, NULL, trans, media_idx,
|
||||||
bundled_mids, 0, bundle_ufrag, bundle_pwd, reserved_pts, all_mids,
|
bundled_mids, 0, bundle_ufrag, bundle_pwd, reserved_pts, all_mids,
|
||||||
error)) {
|
error)) {
|
||||||
/* as per JSEP, a=rtcp-mux-only is only added for new streams */
|
/* as per JSEP, a=rtcp-mux-only is only added for new streams */
|
||||||
|
@ -3728,6 +3766,7 @@ _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
|
||||||
str = g_strdup_printf ("%u", target_ssrc);
|
str = g_strdup_printf ("%u", target_ssrc);
|
||||||
gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
|
gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
|
||||||
g_random_int (), NULL);
|
g_random_int (), NULL);
|
||||||
|
g_free (str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4379,12 +4418,10 @@ out:
|
||||||
static GstElement *
|
static GstElement *
|
||||||
_build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
|
_build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
|
||||||
{
|
{
|
||||||
GstElement *ret = NULL;
|
|
||||||
GstElement *prev = NULL;
|
|
||||||
guint ulpfec_pt = 0;
|
|
||||||
guint red_pt = 0;
|
|
||||||
GstPad *sinkpad = NULL;
|
|
||||||
GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
|
GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
|
||||||
|
guint ulpfec_pt = 0, red_pt = 0;
|
||||||
|
GstPad *sinkpad, *srcpad, *ghost;
|
||||||
|
GstElement *ret;
|
||||||
|
|
||||||
if (trans->stream) {
|
if (trans->stream) {
|
||||||
ulpfec_pt =
|
ulpfec_pt =
|
||||||
|
@ -4392,69 +4429,215 @@ _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
|
||||||
red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
|
red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ulpfec_pt || red_pt)
|
if (trans->ulpfecenc || trans->redenc) {
|
||||||
ret = gst_bin_new (NULL);
|
g_critical ("webrtcbin: duplicate call to create a fec encoder or "
|
||||||
|
"red encoder!");
|
||||||
if (ulpfec_pt) {
|
return NULL;
|
||||||
GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
|
|
||||||
GstCaps *caps = transport_stream_get_caps_for_pt (trans->stream, ulpfec_pt);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (webrtc,
|
|
||||||
"Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
|
|
||||||
ulpfec_pt);
|
|
||||||
|
|
||||||
gst_bin_add (GST_BIN (ret), fecenc);
|
|
||||||
sinkpad = gst_element_get_static_pad (fecenc, "sink");
|
|
||||||
g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
|
|
||||||
trans->fec_percentage, NULL);
|
|
||||||
|
|
||||||
g_object_bind_property (rtp_trans, "fec-percentage", fecenc, "percentage",
|
|
||||||
G_BINDING_BIDIRECTIONAL);
|
|
||||||
|
|
||||||
if (caps && !gst_caps_is_empty (caps)) {
|
|
||||||
const GstStructure *s = gst_caps_get_structure (caps, 0);
|
|
||||||
const gchar *media = gst_structure_get_string (s, "media");
|
|
||||||
|
|
||||||
if (!g_strcmp0 (media, "video"))
|
|
||||||
g_object_set (fecenc, "multipacket", TRUE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = fecenc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (red_pt) {
|
GST_DEBUG_OBJECT (webrtc,
|
||||||
GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
|
"Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
|
||||||
|
ulpfec_pt);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
|
ret = gst_bin_new (NULL);
|
||||||
rtp_trans->mline, red_pt);
|
|
||||||
|
|
||||||
gst_bin_add (GST_BIN (ret), redenc);
|
trans->ulpfecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
|
||||||
if (prev)
|
gst_object_ref_sink (trans->ulpfecenc);
|
||||||
gst_element_link (prev, redenc);
|
if (!gst_bin_add (GST_BIN (ret), trans->ulpfecenc))
|
||||||
else
|
g_warn_if_reached ();
|
||||||
sinkpad = gst_element_get_static_pad (redenc, "sink");
|
sinkpad = gst_element_get_static_pad (trans->ulpfecenc, "sink");
|
||||||
|
|
||||||
g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
|
g_object_bind_property (rtp_trans, "fec-percentage", trans->ulpfecenc,
|
||||||
|
"percentage", G_BINDING_BIDIRECTIONAL);
|
||||||
|
|
||||||
prev = redenc;
|
trans->redenc = gst_element_factory_make ("rtpredenc", NULL);
|
||||||
}
|
gst_object_ref_sink (trans->redenc);
|
||||||
|
|
||||||
if (sinkpad) {
|
GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
|
||||||
GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
|
rtp_trans->mline, red_pt);
|
||||||
gst_object_unref (sinkpad);
|
|
||||||
gst_element_add_pad (ret, ghost);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev) {
|
gst_bin_add (GST_BIN (ret), trans->redenc);
|
||||||
GstPad *srcpad = gst_element_get_static_pad (prev, "src");
|
gst_element_link (trans->ulpfecenc, trans->redenc);
|
||||||
GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
|
|
||||||
gst_object_unref (srcpad);
|
ghost = gst_ghost_pad_new ("sink", sinkpad);
|
||||||
gst_element_add_pad (ret, ghost);
|
gst_clear_object (&sinkpad);
|
||||||
}
|
gst_element_add_pad (ret, ghost);
|
||||||
|
ghost = NULL;
|
||||||
|
|
||||||
|
srcpad = gst_element_get_static_pad (trans->redenc, "src");
|
||||||
|
ghost = gst_ghost_pad_new ("src", srcpad);
|
||||||
|
gst_clear_object (&srcpad);
|
||||||
|
gst_element_add_pad (ret, ghost);
|
||||||
|
ghost = NULL;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstStructure *s = user_data;
|
||||||
|
|
||||||
|
gst_structure_id_set_value (s, field_id, value);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GST_WEBRTC_PAYLOAD_TYPE "gst.webrtcbin.payload.type"
|
||||||
|
|
||||||
|
static void
|
||||||
|
try_match_transceiver_with_fec_decoder (GstWebRTCBin * webrtc,
|
||||||
|
WebRTCTransceiver * trans)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = trans->stream->fecdecs; l; l = l->next) {
|
||||||
|
GstElement *fecdec = GST_ELEMENT (l->data);
|
||||||
|
gboolean found_transceiver = FALSE;
|
||||||
|
int original_pt;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
original_pt =
|
||||||
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fecdec),
|
||||||
|
GST_WEBRTC_PAYLOAD_TYPE));
|
||||||
|
if (original_pt <= 0) {
|
||||||
|
GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
|
||||||
|
"transceiver, fec decoder %" GST_PTR_FORMAT " does not contain a "
|
||||||
|
"valid payload type", fecdec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < trans->stream->ptmap->len; i++) {
|
||||||
|
PtMapItem *item = &g_array_index (trans->stream->ptmap, PtMapItem, i);
|
||||||
|
|
||||||
|
/* FIXME: this only works for a 1-1 original_pt->fec_pt mapping */
|
||||||
|
if (original_pt == item->pt && item->media_idx != -1
|
||||||
|
&& item->media_idx == trans->parent.mline) {
|
||||||
|
if (trans->ulpfecdec) {
|
||||||
|
GST_FIXME_OBJECT (trans, "cannot");
|
||||||
|
gst_clear_object (&trans->ulpfecdec);
|
||||||
|
}
|
||||||
|
trans->ulpfecdec = gst_object_ref (fecdec);
|
||||||
|
found_transceiver = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_transceiver) {
|
||||||
|
GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
|
||||||
|
"transceiver");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_set_internal_rtpbin_element_props_from_stream (GstWebRTCBin * webrtc,
|
||||||
|
TransportStream * stream)
|
||||||
|
{
|
||||||
|
GstStructure *merged_local_rtx_ssrc_map;
|
||||||
|
GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
|
||||||
|
GValue red_pt_array = { 0, };
|
||||||
|
gint *rtx_pt;
|
||||||
|
gsize rtx_count;
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
gst_value_array_init (&red_pt_array, 0);
|
||||||
|
|
||||||
|
rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
|
||||||
|
GST_DEBUG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
|
||||||
|
|
||||||
|
for (i = 0; i < rtx_count; i++) {
|
||||||
|
GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
|
||||||
|
const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
|
||||||
|
const gchar *apt = gst_structure_get_string (s, "apt");
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
|
||||||
|
gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
|
||||||
|
GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
|
||||||
|
stream->rtxsend, pt_map);
|
||||||
|
|
||||||
|
if (stream->rtxreceive)
|
||||||
|
g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
|
||||||
|
if (stream->rtxsend)
|
||||||
|
g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
|
||||||
|
|
||||||
|
gst_structure_free (pt_map);
|
||||||
|
g_clear_pointer (&rtx_pt, g_free);
|
||||||
|
|
||||||
|
merged_local_rtx_ssrc_map =
|
||||||
|
gst_structure_new_empty ("application/x-rtp-ssrc-map");
|
||||||
|
|
||||||
|
for (i = 0; i < webrtc->priv->transceivers->len; i++) {
|
||||||
|
GstWebRTCRTPTransceiver *rtp_trans =
|
||||||
|
g_ptr_array_index (webrtc->priv->transceivers, i);
|
||||||
|
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
|
||||||
|
|
||||||
|
if (trans->stream == stream) {
|
||||||
|
gint ulpfec_pt, red_pt = 0;
|
||||||
|
|
||||||
|
ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC", rtp_trans->mline);
|
||||||
|
if (ulpfec_pt <= 0)
|
||||||
|
ulpfec_pt = 0;
|
||||||
|
|
||||||
|
red_pt = transport_stream_get_pt (stream, "RED", rtp_trans->mline);
|
||||||
|
if (red_pt <= 0) {
|
||||||
|
red_pt = -1;
|
||||||
|
} else {
|
||||||
|
GValue ptval = { 0, };
|
||||||
|
|
||||||
|
g_value_init (&ptval, G_TYPE_INT);
|
||||||
|
g_value_set_int (&ptval, red_pt);
|
||||||
|
gst_value_array_append_value (&red_pt_array, &ptval);
|
||||||
|
g_value_unset (&ptval);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " transceiever %"
|
||||||
|
GST_PTR_FORMAT " has FEC payload %d and RED payload %d", stream,
|
||||||
|
trans, ulpfec_pt, red_pt);
|
||||||
|
|
||||||
|
if (trans->ulpfecenc) {
|
||||||
|
g_object_set (trans->ulpfecenc, "pt", ulpfec_pt, "multipacket",
|
||||||
|
rtp_trans->kind == GST_WEBRTC_KIND_VIDEO, "percentage",
|
||||||
|
trans->fec_percentage, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
try_match_transceiver_with_fec_decoder (webrtc, trans);
|
||||||
|
if (trans->ulpfecdec) {
|
||||||
|
g_object_set (trans->ulpfecdec, "pt", ulpfec_pt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trans->redenc) {
|
||||||
|
gboolean always_produce = TRUE;
|
||||||
|
if (red_pt == -1) {
|
||||||
|
/* passthrough settings */
|
||||||
|
red_pt = 0;
|
||||||
|
always_produce = FALSE;
|
||||||
|
}
|
||||||
|
g_object_set (trans->redenc, "pt", red_pt, "allow-no-red-blocks",
|
||||||
|
always_produce, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trans->local_rtx_ssrc_map) {
|
||||||
|
gst_structure_foreach (trans->local_rtx_ssrc_map,
|
||||||
|
_merge_structure, merged_local_rtx_ssrc_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->rtxsend)
|
||||||
|
g_object_set (stream->rtxsend, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
|
||||||
|
gst_clear_structure (&merged_local_rtx_ssrc_map);
|
||||||
|
|
||||||
|
if (stream->reddec) {
|
||||||
|
g_object_set_property (G_OBJECT (stream->reddec), "payloads",
|
||||||
|
&red_pt_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_unset (&red_pt_array);
|
||||||
|
}
|
||||||
|
|
||||||
static GstPad *
|
static GstPad *
|
||||||
_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
|
_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
|
||||||
|
@ -4511,21 +4694,26 @@ _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
|
||||||
gst_element_sync_state_with_parent (clocksync);
|
gst_element_sync_state_with_parent (clocksync);
|
||||||
|
|
||||||
srcpad = gst_element_get_static_pad (clocksync, "src");
|
srcpad = gst_element_get_static_pad (clocksync, "src");
|
||||||
sinkpad = gst_element_get_static_pad (clocksync, "sink");
|
|
||||||
|
|
||||||
if ((fec_encoder = _build_fec_encoder (webrtc, trans))) {
|
fec_encoder = _build_fec_encoder (webrtc, trans);
|
||||||
GstPad *fec_sink;
|
if (!fec_encoder) {
|
||||||
|
g_warn_if_reached ();
|
||||||
gst_bin_add (GST_BIN (webrtc), fec_encoder);
|
return NULL;
|
||||||
gst_element_sync_state_with_parent (fec_encoder);
|
|
||||||
|
|
||||||
fec_sink = gst_element_get_static_pad (fec_encoder, "sink");
|
|
||||||
gst_pad_link (srcpad, fec_sink);
|
|
||||||
gst_object_unref (srcpad);
|
|
||||||
gst_object_unref (fec_sink);
|
|
||||||
srcpad = gst_element_get_static_pad (fec_encoder, "src");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
|
||||||
|
|
||||||
|
gst_bin_add (GST_BIN (webrtc), fec_encoder);
|
||||||
|
gst_element_sync_state_with_parent (fec_encoder);
|
||||||
|
|
||||||
|
sinkpad = gst_element_get_static_pad (fec_encoder, "sink");
|
||||||
|
if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)
|
||||||
|
g_warn_if_reached ();
|
||||||
|
gst_clear_object (&srcpad);
|
||||||
|
gst_clear_object (&sinkpad);
|
||||||
|
sinkpad = gst_element_get_static_pad (clocksync, "sink");
|
||||||
|
srcpad = gst_element_get_static_pad (fec_encoder, "src");
|
||||||
|
|
||||||
if (!webrtc->rtpfunnel) {
|
if (!webrtc->rtpfunnel) {
|
||||||
rtp_templ =
|
rtp_templ =
|
||||||
_find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
|
_find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
|
||||||
|
@ -4539,8 +4727,6 @@ _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
|
||||||
gst_pad_link (srcpad, rtp_sink);
|
gst_pad_link (srcpad, rtp_sink);
|
||||||
gst_object_unref (rtp_sink);
|
gst_object_unref (rtp_sink);
|
||||||
|
|
||||||
gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
|
|
||||||
|
|
||||||
pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
|
pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
|
||||||
if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
|
if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
|
||||||
GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
|
GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
|
||||||
|
@ -4552,14 +4738,15 @@ _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
|
||||||
gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
|
gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
|
||||||
|
|
||||||
gst_pad_link (srcpad, funnel_sinkpad);
|
gst_pad_link (srcpad, funnel_sinkpad);
|
||||||
gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
|
|
||||||
|
|
||||||
g_free (pad_name);
|
g_free (pad_name);
|
||||||
gst_object_unref (funnel_sinkpad);
|
gst_object_unref (funnel_sinkpad);
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_object_unref (srcpad);
|
gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
|
||||||
gst_object_unref (sinkpad);
|
|
||||||
|
gst_clear_object (&srcpad);
|
||||||
|
gst_clear_object (&sinkpad);
|
||||||
|
|
||||||
gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
|
gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
|
||||||
|
|
||||||
|
@ -4715,41 +4902,6 @@ _filter_sdp_fields (GQuark field_id, const GValue * value,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
_set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
|
|
||||||
{
|
|
||||||
gint *rtx_pt;
|
|
||||||
gsize rtx_count;
|
|
||||||
|
|
||||||
rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
|
|
||||||
GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
|
|
||||||
if (rtx_pt) {
|
|
||||||
GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
|
|
||||||
gsize i;
|
|
||||||
|
|
||||||
for (i = 0; i < rtx_count; i++) {
|
|
||||||
GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
|
|
||||||
const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
|
|
||||||
const gchar *apt = gst_structure_get_string (s, "apt");
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
|
|
||||||
gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
|
|
||||||
GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
|
|
||||||
stream->rtxsend, pt_map);
|
|
||||||
|
|
||||||
if (stream->rtxreceive)
|
|
||||||
g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
|
|
||||||
if (stream->rtxsend)
|
|
||||||
g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
|
|
||||||
|
|
||||||
gst_structure_free (pt_map);
|
|
||||||
g_free (rtx_pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
|
_update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
|
||||||
TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
|
TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
|
||||||
|
@ -5026,7 +5178,7 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
|
||||||
|
|
||||||
if (!bundled || bundle_idx == media_idx) {
|
if (!bundled || bundle_idx == media_idx) {
|
||||||
if (stream->rtxsend || stream->rtxreceive) {
|
if (stream->rtxsend || stream->rtxreceive) {
|
||||||
_set_rtx_ptmap_from_stream (webrtc, stream);
|
_set_internal_rtpbin_element_props_from_stream (webrtc, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_set (stream, "dtls-client",
|
g_object_set (stream, "dtls-client",
|
||||||
|
@ -6467,82 +6619,56 @@ unknown_session:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
|
|
||||||
{
|
|
||||||
GstStructure *s = user_data;
|
|
||||||
|
|
||||||
gst_structure_id_set_value (s, field_id, value);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstElement *
|
static GstElement *
|
||||||
on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
|
on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
|
||||||
GstWebRTCBin * webrtc)
|
GstWebRTCBin * webrtc)
|
||||||
{
|
{
|
||||||
TransportStream *stream;
|
TransportStream *stream;
|
||||||
gboolean have_rtx = FALSE;
|
GstElement *ret, *rtx;
|
||||||
GstElement *ret = NULL;
|
GstPad *pad;
|
||||||
|
char *name;
|
||||||
|
|
||||||
stream = _find_transport_for_session (webrtc, session_id);
|
stream = _find_transport_for_session (webrtc, session_id);
|
||||||
|
if (!stream) {
|
||||||
if (stream)
|
/* a rtp session without a stream is a webrtcbin bug */
|
||||||
have_rtx = transport_stream_get_pt (stream, "RTX", -1) != 0;
|
g_warn_if_reached ();
|
||||||
|
return NULL;
|
||||||
GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT,
|
|
||||||
stream);
|
|
||||||
|
|
||||||
if (have_rtx) {
|
|
||||||
GstElement *rtx;
|
|
||||||
GstPad *pad;
|
|
||||||
gchar *name;
|
|
||||||
GstStructure *merged_local_rtx_ssrc_map =
|
|
||||||
gst_structure_new_empty ("application/x-rtp-ssrc-map");
|
|
||||||
guint i;
|
|
||||||
|
|
||||||
if (stream->rtxsend) {
|
|
||||||
GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_INFO ("creating AUX sender");
|
|
||||||
ret = gst_bin_new (NULL);
|
|
||||||
rtx = gst_element_factory_make ("rtprtxsend", NULL);
|
|
||||||
g_object_set (rtx, "max-size-packets", 500, NULL);
|
|
||||||
_set_rtx_ptmap_from_stream (webrtc, stream);
|
|
||||||
|
|
||||||
for (i = 0; i < webrtc->priv->transceivers->len; i++) {
|
|
||||||
WebRTCTransceiver *trans =
|
|
||||||
WEBRTC_TRANSCEIVER (g_ptr_array_index (webrtc->priv->transceivers,
|
|
||||||
i));
|
|
||||||
|
|
||||||
if (trans->stream == stream && trans->local_rtx_ssrc_map)
|
|
||||||
gst_structure_foreach (trans->local_rtx_ssrc_map,
|
|
||||||
_merge_structure, merged_local_rtx_ssrc_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
|
|
||||||
gst_structure_free (merged_local_rtx_ssrc_map);
|
|
||||||
|
|
||||||
gst_bin_add (GST_BIN (ret), rtx);
|
|
||||||
|
|
||||||
pad = gst_element_get_static_pad (rtx, "src");
|
|
||||||
name = g_strdup_printf ("src_%u", session_id);
|
|
||||||
gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
|
|
||||||
g_free (name);
|
|
||||||
gst_object_unref (pad);
|
|
||||||
|
|
||||||
pad = gst_element_get_static_pad (rtx, "sink");
|
|
||||||
name = g_strdup_printf ("sink_%u", session_id);
|
|
||||||
gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
|
|
||||||
g_free (name);
|
|
||||||
gst_object_unref (pad);
|
|
||||||
|
|
||||||
stream->rtxsend = gst_object_ref (rtx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
if (stream->rtxsend) {
|
||||||
|
GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
|
||||||
|
g_warn_if_reached ();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
|
||||||
|
"stream %" GST_PTR_FORMAT, session_id, stream);
|
||||||
|
|
||||||
|
ret = gst_bin_new (NULL);
|
||||||
|
rtx = gst_element_factory_make ("rtprtxsend", NULL);
|
||||||
|
/* XXX: allow control from outside? */
|
||||||
|
g_object_set (rtx, "max-size-packets", 500, NULL);
|
||||||
|
|
||||||
|
if (!gst_bin_add (GST_BIN (ret), rtx))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
|
||||||
|
stream->rtxsend = gst_object_ref (rtx);
|
||||||
|
_set_internal_rtpbin_element_props_from_stream (webrtc, stream);
|
||||||
|
|
||||||
|
name = g_strdup_printf ("src_%u", session_id);
|
||||||
|
pad = gst_element_get_static_pad (rtx, "src");
|
||||||
|
if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
gst_clear_object (&pad);
|
||||||
|
g_clear_pointer (&name, g_free);
|
||||||
|
|
||||||
|
name = g_strdup_printf ("sink_%u", session_id);
|
||||||
|
pad = gst_element_get_static_pad (rtx, "sink");
|
||||||
|
if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
gst_clear_object (&pad);
|
||||||
|
g_clear_pointer (&name, g_free);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6550,108 +6676,68 @@ static GstElement *
|
||||||
on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
|
on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
|
||||||
GstWebRTCBin * webrtc)
|
GstWebRTCBin * webrtc)
|
||||||
{
|
{
|
||||||
GstElement *ret = NULL;
|
|
||||||
GstElement *prev = NULL;
|
|
||||||
GstPad *sinkpad = NULL;
|
|
||||||
TransportStream *stream;
|
TransportStream *stream;
|
||||||
gint rtx_pt = 0;
|
GstPad *pad, *ghost;
|
||||||
GValue red_pt_array = { 0, };
|
GstElement *ret;
|
||||||
gboolean have_red_pt = FALSE;
|
char *name;
|
||||||
|
|
||||||
g_value_init (&red_pt_array, GST_TYPE_ARRAY);
|
|
||||||
|
|
||||||
stream = _find_transport_for_session (webrtc, session_id);
|
stream = _find_transport_for_session (webrtc, session_id);
|
||||||
|
if (!stream) {
|
||||||
if (stream) {
|
/* no transport stream before the session has been created is a webrtcbin
|
||||||
guint i = 0;
|
* programming error! */
|
||||||
|
g_warn_if_reached ();
|
||||||
for (i = 0; i < stream->ptmap->len; i++) {
|
return NULL;
|
||||||
PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
|
|
||||||
|
|
||||||
if (!gst_caps_is_empty (item->caps)) {
|
|
||||||
GstStructure *s = gst_caps_get_structure (item->caps, 0);
|
|
||||||
|
|
||||||
if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "RED")) {
|
|
||||||
GValue ptval = { 0, };
|
|
||||||
|
|
||||||
g_value_init (&ptval, G_TYPE_INT);
|
|
||||||
g_value_set_int (&ptval, item->pt);
|
|
||||||
gst_value_array_append_value (&red_pt_array, &ptval);
|
|
||||||
g_value_unset (&ptval);
|
|
||||||
|
|
||||||
have_red_pt = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rtx_pt = transport_stream_get_pt (stream, "RTX", -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
|
if (stream->rtxreceive) {
|
||||||
stream);
|
GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
|
||||||
|
g_warn_if_reached ();
|
||||||
if (have_red_pt || rtx_pt)
|
return NULL;
|
||||||
ret = gst_bin_new (NULL);
|
|
||||||
|
|
||||||
if (rtx_pt) {
|
|
||||||
if (stream->rtxreceive) {
|
|
||||||
GST_WARNING_OBJECT (webrtc,
|
|
||||||
"rtprtxreceive already created! rtpbin bug?!");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
|
|
||||||
_set_rtx_ptmap_from_stream (webrtc, stream);
|
|
||||||
|
|
||||||
gst_bin_add (GST_BIN (ret), stream->rtxreceive);
|
|
||||||
|
|
||||||
sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
|
|
||||||
|
|
||||||
prev = gst_object_ref (stream->rtxreceive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (have_red_pt) {
|
if (stream->reddec) {
|
||||||
GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
|
GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
|
||||||
|
g_warn_if_reached ();
|
||||||
GST_DEBUG_OBJECT (webrtc, "Creating RED decoder in session %u", session_id);
|
return NULL;
|
||||||
|
|
||||||
gst_bin_add (GST_BIN (ret), rtpreddec);
|
|
||||||
|
|
||||||
g_object_set_property (G_OBJECT (rtpreddec), "payloads", &red_pt_array);
|
|
||||||
|
|
||||||
if (prev)
|
|
||||||
gst_element_link (prev, rtpreddec);
|
|
||||||
else
|
|
||||||
sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
|
|
||||||
|
|
||||||
prev = rtpreddec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sinkpad) {
|
GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
|
||||||
gchar *name = g_strdup_printf ("sink_%u", session_id);
|
"stream %" GST_PTR_FORMAT, session_id, stream);
|
||||||
GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
|
|
||||||
g_free (name);
|
|
||||||
gst_object_unref (sinkpad);
|
|
||||||
gst_element_add_pad (ret, ghost);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev) {
|
ret = gst_bin_new (NULL);
|
||||||
gchar *name = g_strdup_printf ("src_%u", session_id);
|
|
||||||
GstPad *srcpad = gst_element_get_static_pad (prev, "src");
|
stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
|
||||||
GstPad *ghost = gst_ghost_pad_new (name, srcpad);
|
gst_object_ref (stream->rtxreceive);
|
||||||
g_free (name);
|
if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
|
||||||
gst_object_unref (srcpad);
|
g_warn_if_reached ();
|
||||||
gst_element_add_pad (ret, ghost);
|
|
||||||
}
|
stream->reddec = gst_element_factory_make ("rtpreddec", NULL);
|
||||||
|
gst_object_ref (stream->reddec);
|
||||||
|
if (!gst_bin_add (GST_BIN (ret), stream->reddec))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
|
||||||
|
_set_internal_rtpbin_element_props_from_stream (webrtc, stream);
|
||||||
|
|
||||||
|
if (!gst_element_link (stream->rtxreceive, stream->reddec))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
|
||||||
|
name = g_strdup_printf ("sink_%u", session_id);
|
||||||
|
pad = gst_element_get_static_pad (stream->rtxreceive, "sink");
|
||||||
|
ghost = gst_ghost_pad_new (name, pad);
|
||||||
|
g_clear_pointer (&name, g_free);
|
||||||
|
gst_clear_object (&pad);
|
||||||
|
if (!gst_element_add_pad (ret, ghost))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
|
||||||
|
name = g_strdup_printf ("src_%u", session_id);
|
||||||
|
pad = gst_element_get_static_pad (stream->reddec, "src");
|
||||||
|
ghost = gst_ghost_pad_new (name, pad);
|
||||||
|
g_clear_pointer (&name, g_free);
|
||||||
|
gst_clear_object (&pad);
|
||||||
|
if (!gst_element_add_pad (ret, ghost))
|
||||||
|
g_warn_if_reached ();
|
||||||
|
|
||||||
out:
|
|
||||||
g_value_unset (&red_pt_array);
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
error:
|
|
||||||
if (ret)
|
|
||||||
gst_object_unref (ret);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstElement *
|
static GstElement *
|
||||||
|
@ -6660,10 +6746,14 @@ on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
|
||||||
{
|
{
|
||||||
TransportStream *stream;
|
TransportStream *stream;
|
||||||
GstElement *ret = NULL;
|
GstElement *ret = NULL;
|
||||||
gint fec_pt = 0;
|
|
||||||
GObject *internal_storage;
|
GObject *internal_storage;
|
||||||
|
|
||||||
stream = _find_transport_for_session (webrtc, session_id);
|
stream = _find_transport_for_session (webrtc, session_id);
|
||||||
|
if (!stream) {
|
||||||
|
/* a rtp session without a stream is a webrtcbin bug */
|
||||||
|
g_warn_if_reached ();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: for now, we only support ulpfec, but once we support
|
/* TODO: for now, we only support ulpfec, but once we support
|
||||||
* more algorithms, if the remote may use more than one algorithm,
|
* more algorithms, if the remote may use more than one algorithm,
|
||||||
|
@ -6671,33 +6761,25 @@ on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
|
||||||
*
|
*
|
||||||
* + Return a bin here, with the relevant FEC decoders plugged in
|
* + Return a bin here, with the relevant FEC decoders plugged in
|
||||||
* and their payload type set to 0
|
* and their payload type set to 0
|
||||||
* + Enable the decoders by setting the payload type only when
|
|
||||||
* we detect it (by connecting to ptdemux:new-payload-type for
|
|
||||||
* example)
|
|
||||||
*/
|
*/
|
||||||
if (stream) {
|
GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
|
||||||
guint i;
|
"stream %" GST_PTR_FORMAT, pt, session_id, stream);
|
||||||
|
|
||||||
for (i = 0; i < stream->ptmap->len; i++) {
|
ret = gst_element_factory_make ("rtpulpfecdec", NULL);
|
||||||
PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
|
|
||||||
|
|
||||||
if (item->pt == pt) {
|
g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
|
||||||
fec_pt = transport_stream_get_pt (stream, "ULPFEC", item->media_idx);
|
&internal_storage);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fec_pt) {
|
g_object_set (ret, "storage", internal_storage, NULL);
|
||||||
GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
|
g_clear_object (&internal_storage);
|
||||||
fec_pt, session_id);
|
|
||||||
ret = gst_element_factory_make ("rtpulpfecdec", NULL);
|
|
||||||
g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
|
|
||||||
&internal_storage);
|
|
||||||
|
|
||||||
g_object_set (ret, "pt", fec_pt, "storage", internal_storage, NULL);
|
g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
|
||||||
g_object_unref (internal_storage);
|
GINT_TO_POINTER (pt));
|
||||||
}
|
|
||||||
|
PC_LOCK (webrtc);
|
||||||
|
stream->fecdecs = g_list_prepend (stream->fecdecs, gst_object_ref (ret));
|
||||||
|
_set_internal_rtpbin_element_props_from_stream (webrtc, stream);
|
||||||
|
PC_UNLOCK (webrtc);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ transport_stream_get_pt (TransportStream * stream, const gchar * encoding_name,
|
||||||
guint media_idx)
|
guint media_idx)
|
||||||
{
|
{
|
||||||
guint i;
|
guint i;
|
||||||
gint ret = 0;
|
gint ret = -1;
|
||||||
|
|
||||||
for (i = 0; i < stream->ptmap->len; i++) {
|
for (i = 0; i < stream->ptmap->len; i++) {
|
||||||
PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
|
PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
|
||||||
|
@ -165,25 +165,14 @@ transport_stream_dispose (GObject * object)
|
||||||
{
|
{
|
||||||
TransportStream *stream = TRANSPORT_STREAM (object);
|
TransportStream *stream = TRANSPORT_STREAM (object);
|
||||||
|
|
||||||
if (stream->send_bin)
|
gst_clear_object (&stream->send_bin);
|
||||||
gst_object_unref (stream->send_bin);
|
gst_clear_object (&stream->receive_bin);
|
||||||
stream->send_bin = NULL;
|
gst_clear_object (&stream->transport);
|
||||||
|
gst_clear_object (&stream->rtxsend);
|
||||||
if (stream->receive_bin)
|
gst_clear_object (&stream->rtxreceive);
|
||||||
gst_object_unref (stream->receive_bin);
|
gst_clear_object (&stream->reddec);
|
||||||
stream->receive_bin = NULL;
|
g_list_free_full (stream->fecdecs, (GDestroyNotify) gst_object_unref);
|
||||||
|
stream->fecdecs = NULL;
|
||||||
if (stream->transport)
|
|
||||||
gst_object_unref (stream->transport);
|
|
||||||
stream->transport = NULL;
|
|
||||||
|
|
||||||
if (stream->rtxsend)
|
|
||||||
gst_object_unref (stream->rtxsend);
|
|
||||||
stream->rtxsend = NULL;
|
|
||||||
|
|
||||||
if (stream->rtxreceive)
|
|
||||||
gst_object_unref (stream->rtxreceive);
|
|
||||||
stream->rtxreceive = NULL;
|
|
||||||
|
|
||||||
GST_OBJECT_PARENT (object) = NULL;
|
GST_OBJECT_PARENT (object) = NULL;
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,9 @@ struct _TransportStream
|
||||||
|
|
||||||
GstElement *rtxsend;
|
GstElement *rtxsend;
|
||||||
GstElement *rtxreceive;
|
GstElement *rtxreceive;
|
||||||
|
|
||||||
|
GstElement *reddec;
|
||||||
|
GList *fecdecs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _TransportStreamClass
|
struct _TransportStreamClass
|
||||||
|
|
|
@ -148,9 +148,10 @@ webrtc_transceiver_finalize (GObject * object)
|
||||||
{
|
{
|
||||||
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (object);
|
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (object);
|
||||||
|
|
||||||
if (trans->stream)
|
gst_clear_object (&trans->stream);
|
||||||
gst_object_unref (trans->stream);
|
gst_clear_object (&trans->ulpfecdec);
|
||||||
trans->stream = NULL;
|
gst_clear_object (&trans->ulpfecenc);
|
||||||
|
gst_clear_object (&trans->redenc);
|
||||||
|
|
||||||
if (trans->local_rtx_ssrc_map)
|
if (trans->local_rtx_ssrc_map)
|
||||||
gst_structure_free (trans->local_rtx_ssrc_map);
|
gst_structure_free (trans->local_rtx_ssrc_map);
|
||||||
|
|
|
@ -51,6 +51,10 @@ struct _WebRTCTransceiver
|
||||||
GstCaps *last_configured_caps;
|
GstCaps *last_configured_caps;
|
||||||
|
|
||||||
gboolean mline_locked;
|
gboolean mline_locked;
|
||||||
|
|
||||||
|
GstElement *ulpfecdec;
|
||||||
|
GstElement *ulpfecenc;
|
||||||
|
GstElement *redenc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _WebRTCTransceiverClass
|
struct _WebRTCTransceiverClass
|
||||||
|
|
|
@ -4371,6 +4371,60 @@ GST_START_TEST (test_codec_preferences_in_on_new_transceiver)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_renego_rtx)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
|
VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads,
|
||||||
|
NULL, NULL);
|
||||||
|
guint media_format_count[] = { 1, 1 };
|
||||||
|
VAL_SDP_INIT (media_formats, on_sdp_media_count_formats,
|
||||||
|
media_format_count, &no_duplicate_payloads);
|
||||||
|
VAL_SDP_INIT (count_media, _count_num_sdp_media, GUINT_TO_POINTER (2),
|
||||||
|
&media_formats);
|
||||||
|
VAL_SDP_INIT (payloads, on_sdp_media_payload_types,
|
||||||
|
GUINT_TO_POINTER (1), &count_media);
|
||||||
|
const gchar *expected_offer_direction[] = { "sendrecv", "sendrecv", };
|
||||||
|
VAL_SDP_INIT (offer_direction, on_sdp_media_direction,
|
||||||
|
expected_offer_direction, &payloads);
|
||||||
|
const gchar *expected_answer_direction[] = { "recvonly", "recvonly", };
|
||||||
|
VAL_SDP_INIT (answer_direction, on_sdp_media_direction,
|
||||||
|
expected_answer_direction, &payloads);
|
||||||
|
const gchar *expected_offer_setup[] = { "actpass", "actpass", };
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_setup, expected_offer_setup,
|
||||||
|
&offer_direction);
|
||||||
|
const gchar *expected_answer_setup[] = { "active", "active", };
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_setup, expected_answer_setup,
|
||||||
|
&answer_direction);
|
||||||
|
GstWebRTCRTPTransceiver *trans;
|
||||||
|
|
||||||
|
t->on_negotiation_needed = NULL;
|
||||||
|
t->on_ice_candidate = NULL;
|
||||||
|
t->on_pad_added = _pad_added_fakesink;
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
test_webrtc_reset_negotiation (t);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (t->webrtc1, "get-transceiver", 1, &trans);
|
||||||
|
g_object_set (trans, "do-nack", TRUE, "fec-type",
|
||||||
|
GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
|
||||||
|
g_clear_object (&trans);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (t->webrtc2, "get-transceiver", 1, &trans);
|
||||||
|
g_object_set (trans, "do-nack", TRUE, "fec-type",
|
||||||
|
GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
|
||||||
|
g_clear_object (&trans);
|
||||||
|
|
||||||
|
/* adding RTX/RED/FEC increases the number of media formats */
|
||||||
|
media_format_count[1] = 5;
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
static Suite *
|
static Suite *
|
||||||
webrtcbin_suite (void)
|
webrtcbin_suite (void)
|
||||||
{
|
{
|
||||||
|
@ -4425,6 +4479,7 @@ webrtcbin_suite (void)
|
||||||
tcase_add_test (tc, test_codec_preferences_no_duplicate_extmaps);
|
tcase_add_test (tc, test_codec_preferences_no_duplicate_extmaps);
|
||||||
tcase_add_test (tc, test_codec_preferences_incompatible_extmaps);
|
tcase_add_test (tc, test_codec_preferences_incompatible_extmaps);
|
||||||
tcase_add_test (tc, test_codec_preferences_invalid_extmap);
|
tcase_add_test (tc, test_codec_preferences_invalid_extmap);
|
||||||
|
tcase_add_test (tc, test_renego_rtx);
|
||||||
if (sctpenc && sctpdec) {
|
if (sctpenc && sctpdec) {
|
||||||
tcase_add_test (tc, test_data_channel_create);
|
tcase_add_test (tc, test_data_channel_create);
|
||||||
tcase_add_test (tc, test_data_channel_remote_notify);
|
tcase_add_test (tc, test_data_channel_remote_notify);
|
||||||
|
|
Loading…
Reference in a new issue