/* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
static gboolean
sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
- GstWebRTCRTPTransceiver * trans, guint media_idx,
- GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
- gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids,
- GError ** error)
+ const GstSDPMedia * last_media, GstWebRTCRTPTransceiver * trans,
+ guint media_idx, GString * bundled_mids, guint bundle_idx,
+ gchar * bundle_ufrag, gchar * bundle_pwd, GArray * reserved_pts,
+ GHashTable * all_mids, GError ** error)
{
/* TODO:
* rtp header extensions
GstStructure *extmap;
int i;
- if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
- || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
+ if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
return FALSE;
g_assert (trans->mline == -1 || trans->mline == media_idx);
bundle_only = bundled_mids && bundle_idx != media_idx
&& webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
- /* mandated by JSEP */
- gst_sdp_media_add_attribute (media, "setup", "actpass");
+ caps = _find_codec_preferences (webrtc, trans, media_idx, error);
+ 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 */
if (last_offer && trans->mline != -1 && trans->mid) {
gst_sdp_media_add_attribute (media, 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);
/* When an extmap is defined twice for the same ID, firefox complains and
trans = g_ptr_array_index (webrtc->priv->transceivers, j);
if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
- GstSDPMedia *media;
- const gchar *mid;
WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
+ const char *mid;
+ GstSDPMedia media;
+
+ memset (&media, 0, sizeof (media));
g_assert (!g_list_find (seen_transceivers, trans));
GST_PTR_FORMAT " with mid %s into media index %u", trans,
trans->mid, media_idx);
- /* FIXME: deal with format changes */
- gst_sdp_media_copy (last_media, &media);
- _media_replace_direction (media, trans->direction);
-
- mid = gst_sdp_media_get_attribute_val (media, "mid");
- g_assert (mid);
+ if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
+ reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
+ }
- 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;
+ 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");
}
- g_hash_table_insert (all_mids, g_strdup (mid), NULL);
+ if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
+ g_array_free (reserved_pts, TRUE);
+ reserved_pts = NULL;
+ }
+ if (*error)
+ goto cancel_offer;
- if (bundled_mids)
- g_string_append_printf (bundled_mids, " %s", mid);
+ 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);
+ gst_sdp_message_add_media (ret, &media);
media_idx++;
- gst_sdp_media_free (media);
+ gst_sdp_media_uninit (&media);
seen_transceivers = g_list_prepend (seen_transceivers, trans);
break;
}
GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
"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,
error)) {
/* as per JSEP, a=rtcp-mux-only is only added for new streams */
str = g_strdup_printf ("%u", target_ssrc);
gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
g_random_int (), NULL);
+ g_free (str);
}
}
}
static GstElement *
_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);
+ guint ulpfec_pt = 0, red_pt = 0;
+ GstPad *sinkpad, *srcpad, *ghost;
+ GstElement *ret;
if (trans->stream) {
ulpfec_pt =
red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
}
- if (ulpfec_pt || red_pt)
- ret = gst_bin_new (NULL);
+ if (trans->ulpfecenc || trans->redenc) {
+ g_critical ("webrtcbin: duplicate call to create a fec encoder or "
+ "red encoder!");
+ return NULL;
+ }
- if (ulpfec_pt) {
- 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_DEBUG_OBJECT (webrtc,
- "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
- ulpfec_pt);
+ ret = gst_bin_new (NULL);
+
+ trans->ulpfecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
+ gst_object_ref_sink (trans->ulpfecenc);
+ if (!gst_bin_add (GST_BIN (ret), trans->ulpfecenc))
+ g_warn_if_reached ();
+ sinkpad = gst_element_get_static_pad (trans->ulpfecenc, "sink");
+
+ g_object_bind_property (rtp_trans, "fec-percentage", trans->ulpfecenc,
+ "percentage", G_BINDING_BIDIRECTIONAL);
+
+ trans->redenc = gst_element_factory_make ("rtpredenc", NULL);
+ gst_object_ref_sink (trans->redenc);
+
+ GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
+ rtp_trans->mline, red_pt);
+
+ gst_bin_add (GST_BIN (ret), trans->redenc);
+ gst_element_link (trans->ulpfecenc, trans->redenc);
+
+ ghost = gst_ghost_pad_new ("sink", sinkpad);
+ 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;
+}
+
+static gboolean
+_merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
+{
+ GstStructure *s = user_data;
- 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);
+ gst_structure_id_set_value (s, field_id, value);
- g_object_bind_property (rtp_trans, "fec-percentage", fecenc, "percentage",
- G_BINDING_BIDIRECTIONAL);
+ return TRUE;
+}
- 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");
+#define GST_WEBRTC_PAYLOAD_TYPE "gst.webrtcbin.payload.type"
- if (!g_strcmp0 (media, "video"))
- g_object_set (fecenc, "multipacket", TRUE, NULL);
+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;
}
- prev = fecenc;
+ 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");
+ }
}
+}
- if (red_pt) {
- GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
+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_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
- rtp_trans->mline, red_pt);
+ gst_value_array_init (&red_pt_array, 0);
- gst_bin_add (GST_BIN (ret), redenc);
- if (prev)
- gst_element_link (prev, redenc);
- else
- sinkpad = gst_element_get_static_pad (redenc, "sink");
+ rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
+ GST_DEBUG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
- g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
+ 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");
- prev = redenc;
+ 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);
}
- if (sinkpad) {
- GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
- gst_object_unref (sinkpad);
- gst_element_add_pad (ret, ghost);
+ 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 (prev) {
- GstPad *srcpad = gst_element_get_static_pad (prev, "src");
- GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
- gst_object_unref (srcpad);
- gst_element_add_pad (ret, ghost);
+ 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);
}
- return ret;
+ g_value_unset (&red_pt_array);
}
-
static GstPad *
_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
{
gst_element_sync_state_with_parent (clocksync);
srcpad = gst_element_get_static_pad (clocksync, "src");
- sinkpad = gst_element_get_static_pad (clocksync, "sink");
- if ((fec_encoder = _build_fec_encoder (webrtc, trans))) {
- GstPad *fec_sink;
+ fec_encoder = _build_fec_encoder (webrtc, trans);
+ if (!fec_encoder) {
+ g_warn_if_reached ();
+ return NULL;
+ }
- gst_bin_add (GST_BIN (webrtc), fec_encoder);
- gst_element_sync_state_with_parent (fec_encoder);
+ _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
- 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");
- }
+ 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) {
rtp_templ =
gst_pad_link (srcpad, 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);
if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
gst_pad_link (srcpad, funnel_sinkpad);
- gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
g_free (pad_name);
gst_object_unref (funnel_sinkpad);
}
- gst_object_unref (srcpad);
- gst_object_unref (sinkpad);
+ gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
+
+ gst_clear_object (&srcpad);
+ gst_clear_object (&sinkpad);
gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
}
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
_update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
{
if (!bundled || bundle_idx == media_idx) {
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",
}
}
-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 *
on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
GstWebRTCBin * webrtc)
{
TransportStream *stream;
- gboolean have_rtx = FALSE;
- GstElement *ret = NULL;
+ GstElement *ret, *rtx;
+ GstPad *pad;
+ char *name;
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;
+ }
- if (stream)
- have_rtx = transport_stream_get_pt (stream, "RTX", -1) != 0;
-
- 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 (stream->rtxsend) {
+ GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
+ g_warn_if_reached ();
+ return NULL;
+ }
- if (trans->stream == stream && trans->local_rtx_ssrc_map)
- gst_structure_foreach (trans->local_rtx_ssrc_map,
- _merge_structure, merged_local_rtx_ssrc_map);
- }
+ GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
+ "stream %" GST_PTR_FORMAT, session_id, stream);
- g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
- gst_structure_free (merged_local_rtx_ssrc_map);
+ 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);
- gst_bin_add (GST_BIN (ret), rtx);
+ if (!gst_bin_add (GST_BIN (ret), rtx))
+ g_warn_if_reached ();
- 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);
+ stream->rtxsend = gst_object_ref (rtx);
+ _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
- 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);
+ 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);
- stream->rtxsend = gst_object_ref (rtx);
- }
+ 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);
-out:
return ret;
}
on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
GstWebRTCBin * webrtc)
{
- GstElement *ret = NULL;
- GstElement *prev = NULL;
- GstPad *sinkpad = NULL;
TransportStream *stream;
- gint rtx_pt = 0;
- GValue red_pt_array = { 0, };
- gboolean have_red_pt = FALSE;
-
- g_value_init (&red_pt_array, GST_TYPE_ARRAY);
+ GstPad *pad, *ghost;
+ GstElement *ret;
+ char *name;
stream = _find_transport_for_session (webrtc, session_id);
-
- if (stream) {
- guint i = 0;
-
- for (i = 0; i < stream->ptmap->len; i++) {
- 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);
+ if (!stream) {
+ /* no transport stream before the session has been created is a webrtcbin
+ * programming error! */
+ g_warn_if_reached ();
+ return NULL;
}
- GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
- stream);
-
- if (have_red_pt || rtx_pt)
- 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");
+ if (stream->rtxreceive) {
+ GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
+ g_warn_if_reached ();
+ return NULL;
+ }
- prev = gst_object_ref (stream->rtxreceive);
+ if (stream->reddec) {
+ GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
+ g_warn_if_reached ();
+ return NULL;
}
- if (have_red_pt) {
- GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
+ GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
+ "stream %" GST_PTR_FORMAT, session_id, stream);
- GST_DEBUG_OBJECT (webrtc, "Creating RED decoder in session %u", session_id);
+ ret = gst_bin_new (NULL);
- gst_bin_add (GST_BIN (ret), rtpreddec);
+ stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
+ gst_object_ref (stream->rtxreceive);
+ if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
+ g_warn_if_reached ();
- g_object_set_property (G_OBJECT (rtpreddec), "payloads", &red_pt_array);
+ 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 ();
- if (prev)
- gst_element_link (prev, rtpreddec);
- else
- sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
+ _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
- prev = rtpreddec;
- }
+ if (!gst_element_link (stream->rtxreceive, stream->reddec))
+ g_warn_if_reached ();
- if (sinkpad) {
- gchar *name = g_strdup_printf ("sink_%u", session_id);
- GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
- g_free (name);
- gst_object_unref (sinkpad);
- gst_element_add_pad (ret, ghost);
- }
+ 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 ();
- if (prev) {
- gchar *name = g_strdup_printf ("src_%u", session_id);
- GstPad *srcpad = gst_element_get_static_pad (prev, "src");
- GstPad *ghost = gst_ghost_pad_new (name, srcpad);
- g_free (name);
- gst_object_unref (srcpad);
- gst_element_add_pad (ret, ghost);
- }
+ 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;
-
-error:
- if (ret)
- gst_object_unref (ret);
- goto out;
}
static GstElement *
{
TransportStream *stream;
GstElement *ret = NULL;
- gint fec_pt = 0;
GObject *internal_storage;
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
* more algorithms, if the remote may use more than one algorithm,
*
* + Return a bin here, with the relevant FEC decoders plugged in
* 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) {
- guint i;
+ GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
+ "stream %" GST_PTR_FORMAT, pt, session_id, stream);
- for (i = 0; i < stream->ptmap->len; i++) {
- PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
+ ret = gst_element_factory_make ("rtpulpfecdec", NULL);
- if (item->pt == pt) {
- fec_pt = transport_stream_get_pt (stream, "ULPFEC", item->media_idx);
- break;
- }
- }
- }
+ g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
+ &internal_storage);
- if (fec_pt) {
- GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
- 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, "storage", internal_storage, NULL);
+ g_clear_object (&internal_storage);
- g_object_set (ret, "pt", fec_pt, "storage", internal_storage, NULL);
- g_object_unref (internal_storage);
- }
+ g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
+ 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;
}