{
}
+static GstPadProbeReturn
+webrtc_bin_pad_buffer_cb (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstWebRTCBinPad *wpad;
+ GstBuffer *buf;
+ GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT;
+
+ if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
+ buf = GST_PAD_PROBE_INFO_BUFFER (info);
+ } else {
+ GstBufferList *list;
+
+ list = GST_PAD_PROBE_INFO_BUFFER_LIST (info);
+ buf = gst_buffer_list_get (list, 0);
+ }
+
+ if (buf == NULL)
+ return GST_PAD_PROBE_OK;
+
+ if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf))
+ return GST_PAD_PROBE_OK;
+
+ wpad = GST_WEBRTC_BIN_PAD (pad);
+ wpad->last_ssrc = gst_rtp_buffer_get_ssrc (&rtpbuf);
+
+ gst_rtp_buffer_unmap (&rtpbuf);
+
+ return GST_PAD_PROBE_OK;
+}
+
static GstWebRTCBinPad *
gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
{
gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
+ gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BUFFER |
+ GST_PAD_PROBE_TYPE_BUFFER_LIST, webrtc_bin_pad_buffer_cb, NULL, NULL);
+
GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
direction == GST_PAD_SRC ? "src" : "sink");
return pad;
if (op->promise) {
GError *error =
- g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
+ g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
"webrtcbin is closed. aborting execution.");
GstStructure *s = gst_structure_new ("application/x-gst-promise",
"error", G_TYPE_ERROR, error, NULL);
GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
{
GstCaps *caps;
+ guint i, n;
caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
+ /* Only return an error if actual empty caps were returned from the query. */
if (gst_caps_is_empty (caps)) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
gst_clear_caps (&caps);
- } else if (!gst_caps_is_fixed (caps) || gst_caps_is_equal (caps, filter)
- || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
+ gst_caps_unref (filter);
+ return NULL;
+ }
+
+ n = gst_caps_get_size (caps);
+ if (n > 0) {
+ /* Make sure the caps are complete enough to figure out the media type and
+ * encoding-name, otherwise they would match with basically any media. */
+ caps = gst_caps_make_writable (caps);
+ for (i = n; i > 0; i--) {
+ const GstStructure *s = gst_caps_get_structure (caps, i - 1);
+
+ if (!gst_structure_has_name (s, "application/x-rtp") ||
+ !gst_structure_has_field (s, "media") ||
+ !gst_structure_has_field (s, "encoding-name")) {
+ gst_caps_remove_structure (caps, i - 1);
+ }
+ }
+ }
+
+ /* If the filtering above resulted in empty caps, or the caps were ANY to
+ * begin with, then don't report and error but just NULL.
+ *
+ * This would be the case if negotiation would not fail but the peer does
+ * not have any specific enough preferred caps that would allow us to
+ * use them further.
+ */
+ if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
+ GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
gst_clear_caps (&caps);
}
gst_clear_caps (&caps);
if (gst_caps_is_empty (intersection)) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
- "Caps negotiation on pad %s failed againt codec preferences",
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
+ "Caps negotiation on pad %s failed against codec preferences",
GST_PAD_NAME (pad));
gst_clear_caps (&intersection);
} else {
g_free (val);
}
+static gchar *
+_parse_extmap (GQuark field_id, const GValue * value, GError ** error)
+{
+ gchar *ret = NULL;
+
+ if (G_VALUE_HOLDS_STRING (value)) {
+ ret = g_value_dup_string (value);
+ } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
+ && gst_value_array_get_size (value) == 3) {
+ const GValue *val;
+ const gchar *direction, *extensionname, *extensionattributes;
+
+ val = gst_value_array_get_value (value, 0);
+ direction = g_value_get_string (val);
+
+ val = gst_value_array_get_value (value, 1);
+ extensionname = g_value_get_string (val);
+
+ val = gst_value_array_get_value (value, 2);
+ extensionattributes = g_value_get_string (val);
+
+ if (!extensionname || *extensionname == '\0')
+ goto done;
+
+ if (direction && *direction != '\0' && extensionattributes
+ && *extensionattributes != '\0') {
+ ret =
+ g_strdup_printf ("/%s %s %s", direction, extensionname,
+ extensionattributes);
+ } else if (direction && *direction != '\0') {
+ ret = g_strdup_printf ("/%s %s", direction, extensionname);
+ } else if (extensionattributes && *extensionattributes != '\0') {
+ ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
+ } else {
+ ret = g_strdup (extensionname);
+ }
+ }
+
+ if (!ret && error) {
+ gchar *val_str = gst_value_serialize (value);
+
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
+ "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
+ g_free (val_str);
+ }
+
+done:
+ return ret;
+}
+
+typedef struct
+{
+ gboolean ret;
+ GstStructure *extmap;
+ GError **error;
+} ExtmapData;
+
+static gboolean
+_dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
+{
+ gboolean is_extmap =
+ g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
+
+ if (!data->ret)
+ goto done;
+
+ if (is_extmap) {
+ gchar *new_value = _parse_extmap (field_id, value, data->error);
+
+ if (!new_value) {
+ data->ret = FALSE;
+ goto done;
+ }
+
+ if (gst_structure_id_has_field (data->extmap, field_id)) {
+ gchar *old_value =
+ _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
+ field_id), NULL);
+
+ g_assert (old_value);
+
+ if (g_strcmp0 (new_value, old_value)) {
+ GST_ERROR
+ ("extmap contains different values for id %s (%s != %s)",
+ g_quark_to_string (field_id), old_value, new_value);
+ g_set_error (data->error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
+ "extmap contains different values for id %s (%s != %s)",
+ g_quark_to_string (field_id), old_value, new_value);
+ data->ret = FALSE;
+ }
+
+ g_free (old_value);
+
+ }
+
+ if (data->ret) {
+ gst_structure_id_set_value (data->extmap, field_id, value);
+ }
+
+ g_free (new_value);
+ }
+
+done:
+ return !is_extmap;
+}
+
+static GstStructure *
+_gather_extmap (GstCaps * caps, GError ** error)
+{
+ ExtmapData edata =
+ { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
+ guint i, n;
+
+ n = gst_caps_get_size (caps);
+
+ for (i = 0; i < n; i++) {
+ GstStructure *s = gst_caps_get_structure (caps, i);
+
+ gst_structure_filter_and_map_in_place (s,
+ (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
+
+ if (!edata.ret) {
+ gst_clear_structure (&edata.extmap);
+ break;
+ }
+ }
+
+ return edata.extmap;
+}
+
+static gboolean
+_copy_field (GQuark field_id, const GValue * value, GstStructure * s)
+{
+ gst_structure_id_set_value (s, field_id, value);
+
+ return TRUE;
+}
+
/* 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,
gchar *direction, *sdp_mid, *ufrag, *pwd;
gboolean bundle_only;
GstCaps *caps;
+ GstStructure *extmap;
int i;
if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
g_free (direction);
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_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
return FALSE;
}
+ caps = gst_caps_make_writable (caps);
+
+ /* When an extmap is defined twice for the same ID, firefox complains and
+ * errors out (chrome is smart enough to accept strict duplicates).
+ *
+ * To work around this, we deduplicate extmap attributes, and also error
+ * out when a different extmap is defined for the same ID.
+ *
+ * _gather_extmap will strip out all extmap- fields, which will then be
+ * added upon adding the first format for the media.
+ */
+ extmap = _gather_extmap (caps, error);
+
+ if (!extmap) {
+ GST_ERROR_OBJECT (webrtc,
+ "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
+ gst_caps_unref (caps);
+ return FALSE;
+ }
+
+ caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
+ caps);
+
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstCaps *format = gst_caps_new_empty ();
- const GstStructure *s = gst_caps_get_structure (caps, i);
+ GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
+
+ if (i == 0) {
+ gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
+ }
- gst_caps_append_structure (format, gst_structure_copy (s));
+ gst_caps_append_structure (format, s);
GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
" to %u-th media", i, format, media_idx);
gst_caps_unref (format);
}
+ gst_clear_structure (&extmap);
+
{
const GstStructure *s = gst_caps_get_structure (caps, 0);
gint clockrate = -1;
reserved_pts = gather_reserved_pts (webrtc);
if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
-#ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
- && last_bundle && last_bundle && last_bundle[0]
-#else
&& last_bundle && last_bundle[0]
-#endif
&& _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
bundle_ufrag =
g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
g_assert (!g_list_find (seen_transceivers, trans));
if (wtrans->mline_locked && trans->mline != media_idx) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"Previous negotiatied transceiver %"
GST_PTR_FORMAT " with mid %s was in mline %d but transceiver"
" has locked mline %u", trans, trans->mid, media_idx,
if (g_hash_table_contains (all_mids, mid)) {
gst_sdp_media_free (media);
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_FAILED,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"Duplicate mid %s when creating offer", mid);
goto cancel_offer;
}
if (trans->mid) {
if (g_hash_table_contains (all_mids, trans->mid)) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"Duplicate mid %s when creating offer", trans->mid);
goto cancel_offer;
}
continue;
g_assert (wtrans->mline_locked);
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"Tranceiver %" GST_PTR_FORMAT " with mid %s has locked mline %d"
" but the whole offer only has %u sections", trans, trans->mid,
trans->mline, media_idx);
GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
if (!webrtc->pending_remote_description) {
- g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_INVALID_STATE,
+ g_set_error_literal (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INVALID_STATE,
"Asked to create an answer without a remote description");
return NULL;
}
guint bundle_media_index;
if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Bundle tag is %s but no media found matching", bundled[0]);
goto out;
}
if (0) {
rejected:
- GST_INFO_OBJECT (webrtc, "media %u rejected", i);
+ if (error && *error)
+ GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
+ else
+ GST_INFO_OBJECT (webrtc, "media %u rejected", i);
gst_sdp_media_free (media);
gst_sdp_media_copy (offer_media, &media);
gst_sdp_media_set_port_info (media, 0, 0);
+ /* Clear error here as it is not propagated to the caller and the media
+ * is just skipped, i.e. more iterations are going to happen. */
+ g_clear_error (error);
}
gst_sdp_message_add_media (ret, media);
gst_sdp_media_free (media);
if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
data, (GDestroyNotify) _free_create_sdp_data, promise)) {
GError *error =
- g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
+ g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
"Could not create offer. webrtcbin is closed");
GstStructure *s = gst_structure_new ("application/x-gst-promise",
"error", G_TYPE_ERROR, error, NULL);
if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
data, (GDestroyNotify) _free_create_sdp_data, promise)) {
GError *error =
- g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
+ g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
"Could not create answer. webrtcbin is closed.");
GstStructure *s = gst_structure_new ("application/x-gst-promise",
"error", G_TYPE_ERROR, error, NULL);
return ret;
}
+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);
+
+ if (trans->stream) {
+ ulpfec_pt =
+ transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
+ red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
+ }
+
+ if (ulpfec_pt || red_pt)
+ ret = gst_bin_new (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_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) {
+ GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
+
+ GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
+ rtp_trans->mline, red_pt);
+
+ gst_bin_add (GST_BIN (ret), redenc);
+ if (prev)
+ gst_element_link (prev, redenc);
+ else
+ sinkpad = gst_element_get_static_pad (redenc, "sink");
+
+ g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
+
+ prev = redenc;
+ }
+
+ if (sinkpad) {
+ GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
+ gst_object_unref (sinkpad);
+ gst_element_add_pad (ret, ghost);
+ }
+
+ 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);
+ }
+
+ return ret;
+}
+
#ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
static void
_insert_netsim_element_between (GstWebRTCBin * webrtc, GstElement * srcbin,
if (!gst_element_link_pads (netsim, "src", sinkbin, sinkpadname))
g_warn_if_reached ();
}
-
#endif
+
static GstPad *
_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
{
/*
* Not-bundle case:
*
- * ,--------------------------------------------webrtcbin-------------------------,
- * ; ;
- * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
- * ; ; send_rtp_src_%u o---o rtp_sink ; ;
- * ; ,---clocksync---, ; ; ; ; ;
- * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
- * ; sink_%u ; ; ; ; '---------------------' ;
- * o---------o sink src o---o send_rtp_sink_%u ; ;
- * ; '---------------' '--------------------' ;
- * '------------------------------------------------------------------------------'
+ * ,--------------------------------------------webrtcbin--------------------------------------------,
+ * ; ;
+ * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
+ * ; ; send_rtp_src_%u o---o rtp_sink ; ;
+ * ; ,---clocksync---, ; ; ; ; ;
+ * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
+ * ; sink_%u ; ; ,---fec encoder---, ; ; '---------------------' ;
+ * o---------o sink src o-o sink src o--o send_rtp_sink_%u ; ;
+ * ; '---------------' ,-----------------, '--------------------' ;
+ * '-------------------------------------------------------------------------------------------------'
*/
/*
* Bundle case:
- * ,-----------------------------------------------------webrtcbin--------------------------------,
- * ; ;
- * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
- * ; ; send_rtp_src_%u o---o rtp_sink ; ;
- * ; ; ; ; ; ;
- * ; sink_%u ,---clocksync---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
- * o----------o sink src o---o sink_%u ; ; ; '---------------------' ;
- * ; '---------------' ; ; ; ; ;
- * ; ; src o-o send_rtp_sink_%u ; ;
- * ; sink_%u ,---clocksync---, ; ; ; ; ;
- * o----------o sink src o---o sink%u ; '--------------------' ;
- * ; '---------------' '------------' ;
- * '----------------------------------------------------------------------------------------------'
+ * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
+ * ; ;
+ * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
+ * ; ; send_rtp_src_%u o---o rtp_sink ; ;
+ * ; ; ; ; ; ;
+ * ; sink_%u ,---clocksync---, ,---fec encoder---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
+ * o----------o sink src o-o sink src o--o sink_%u ; ; ; '---------------------' ;
+ * ; '---------------' ,-----------------, ; ; ; ; ;
+ * ; ; src o-o send_rtp_sink_%u ; ;
+ * ; sink_%u ,---clocksync---, ,---fec encoder---, ; ; ; ; ;
+ * o----------o sink src o-o sink src o--o sink%u ; '--------------------' ;
+ * ; '---------------' ,-----------------, '------------' ;
+ * '-----------------------------------------------------------------------------------------------------------------'
*/
GstPadTemplate *rtp_templ;
GstPad *rtp_sink, *sinkpad, *srcpad;
gchar *pad_name;
WebRTCTransceiver *trans;
GstElement *clocksync;
+ GstElement *fec_encoder;
g_return_val_if_fail (pad->trans != NULL, NULL);
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;
+
+ gst_bin_add (GST_BIN (webrtc), fec_encoder);
+ 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");
+ }
+
if (!webrtc->rtpfunnel) {
rtp_templ =
_find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
gst_structure_free (pt_map);
-#ifdef TIZEN_FEATURE_FIX_MEMORY_LEAK
g_free (rtx_pt);
-#endif
}
}
}
item.pt = pt;
+ item.media_idx = media_idx;
gst_caps_unref (outcaps);
g_array_append_val (stream->ptmap, item);
remote_setup = _get_dtls_setup_from_media (remote_media);
new_setup = _get_final_setup (local_setup, remote_setup);
if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Cannot intersect direction attributes for media %u", media_idx);
return;
}
remote_dir = _get_direction_from_media (remote_media);
new_dir = _get_final_direction (local_dir, remote_dir);
if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Cannot intersect dtls setup attributes for media %u", media_idx);
return;
}
if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
&& new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
&& prev_dir != new_dir) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"transceiver direction changes are not implemented. Media %u",
media_idx);
return;
remote_setup = _get_dtls_setup_from_media (remote_media);
new_setup = _get_final_setup (local_setup, remote_setup);
if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Cannot intersect dtls setup for media %u", media_idx);
return;
}
local_port = _get_sctp_port_from_media (local_media);
remote_port = _get_sctp_port_from_media (local_media);
if (local_port == -1 || remote_port == -1) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Could not find sctp port for media %u (local %i, remote %i)",
media_idx, local_port, remote_port);
return;
if (bundled) {
if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Bundle tag is %s but no media found matching", bundled[0]);
goto done;
}
webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"State mismatch. Could not find local transceiver by mline %u", i);
goto done;
} else {
continue;
if (rtp_trans->mline != i) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"m-line with mid %s is at position %d, but was locked to %d, "
"rejecting", rtp_trans->mid, i, rtp_trans->mline);
return FALSE;
if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"m-line %d was locked to audio, but SDP has %s media", i,
gst_sdp_media_get_media (media));
return FALSE;
if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
- g_set_error (error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+ g_set_error (error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_INTERNAL_FAILURE,
"m-line %d was locked to video, but SDP has %s media", i,
gst_sdp_media_get_media (media));
return FALSE;
if (bundled) {
if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
- g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error (&error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Bundle tag is %s but no matching media found", bundled[0]);
goto out;
}
if (!check_transceivers_not_removed (webrtc,
get_previous_description (webrtc, sd->source, sd->sdp->type),
sd->sdp)) {
- g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR,
- GST_WEBRTC_BIN_ERROR_BAD_SDP,
+ g_set_error_literal (&error, GST_WEBRTC_ERROR,
+ GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"m=lines removed from the SDP. Processing a completely new connection "
"is not currently supported.");
goto out;
(GstWebRTCBinFunc) _set_description_task, sd,
(GDestroyNotify) _free_set_description_data, promise)) {
GError *error =
- g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
+ g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
"Could not set remote description. webrtcbin is closed.");
GstStructure *s = gst_structure_new ("application/x-gst-promise",
"error", G_TYPE_ERROR, error, NULL);
(GstWebRTCBinFunc) _set_description_task, sd,
(GDestroyNotify) _free_set_description_data, promise)) {
GError *error =
- g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
- "Could not set remote description. webrtcbin is closed");
+ g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
+ "Could not set local description. webrtcbin is closed");
GstStructure *s = gst_structure_new ("application/x-gst-promise",
"error", G_TYPE_ERROR, error, NULL);
if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
stats, (GDestroyNotify) _free_get_stats, promise)) {
GError *error =
- g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
+ g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
"Could not retrieve statistics. webrtcbin is closed.");
GstStructure *s = gst_structure_new ("application/x-gst-promise",
"error", G_TYPE_ERROR, error, NULL);
{
TransportStream *stream;
gboolean have_rtx = FALSE;
-#ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
- GstStructure *pt_map = NULL;
-#endif
GstElement *ret = NULL;
stream = _find_transport_for_session (webrtc, session_id);
if (stream)
- have_rtx = transport_stream_get_pt (stream, "RTX") != 0;
+ have_rtx = transport_stream_get_pt (stream, "RTX", -1) != 0;
-#ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
- GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
- " with pt map %" GST_PTR_FORMAT, stream, pt_map);
-#else
- GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT, stream);
-#endif
+ GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT,
+ stream);
if (have_rtx) {
GstElement *rtx;
}
out:
-#ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
- if (pt_map)
- gst_structure_free (pt_map);
-
-#endif
return ret;
}
GstElement *prev = NULL;
GstPad *sinkpad = NULL;
TransportStream *stream;
- gint red_pt = 0;
gint rtx_pt = 0;
+ GValue red_pt_array = { 0, };
+ gboolean have_red_pt = FALSE;
+
+ g_value_init (&red_pt_array, GST_TYPE_ARRAY);
stream = _find_transport_for_session (webrtc, session_id);
if (stream) {
- red_pt = transport_stream_get_pt (stream, "RED");
- rtx_pt = transport_stream_get_pt (stream, "RTX");
+ 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);
}
GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
stream);
- if (red_pt || rtx_pt)
+ if (have_red_pt || rtx_pt)
ret = gst_bin_new (NULL);
if (rtx_pt) {
prev = gst_object_ref (stream->rtxreceive);
}
- if (red_pt) {
+ if (have_red_pt) {
GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
- GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
- red_pt, session_id);
+ GST_DEBUG_OBJECT (webrtc, "Creating RED decoder in session %u", session_id);
gst_bin_add (GST_BIN (ret), rtpreddec);
- g_object_set (rtpreddec, "pt", red_pt, NULL);
+ g_object_set_property (G_OBJECT (rtpreddec), "payloads", &red_pt_array);
if (prev)
gst_element_link (prev, rtpreddec);
}
out:
+ g_value_unset (&red_pt_array);
return ret;
error:
}
static GstElement *
-on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
- GstWebRTCBin * webrtc)
+on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
+ guint ssrc, guint pt, GstWebRTCBin * webrtc)
{
TransportStream *stream;
GstElement *ret = NULL;
- gint pt = 0;
+ gint fec_pt = 0;
GObject *internal_storage;
stream = _find_transport_for_session (webrtc, session_id);
* we detect it (by connecting to ptdemux:new-payload-type for
* example)
*/
- if (stream)
- pt = transport_stream_get_pt (stream, "ULPFEC");
-
- if (pt) {
- GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
- 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", pt, "storage", internal_storage, NULL);
- g_object_unref (internal_storage);
- }
-
- return ret;
-}
-
-static GstElement *
-on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
- GstWebRTCBin * webrtc)
-{
- GstElement *ret = NULL;
- GstElement *prev = NULL;
- TransportStream *stream;
- guint ulpfec_pt = 0;
- guint red_pt = 0;
- GstPad *sinkpad = NULL;
- GstWebRTCRTPTransceiver *trans;
-
- stream = _find_transport_for_session (webrtc, session_id);
- trans = _find_transceiver (webrtc, &session_id,
- (FindTransceiverFunc) transceiver_match_for_mline);
-
if (stream) {
- ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
- red_pt = transport_stream_get_pt (stream, "RED");
- }
-
- if (ulpfec_pt || red_pt)
- ret = gst_bin_new (NULL);
-
- if (ulpfec_pt) {
- GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
- GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
-
- GST_DEBUG_OBJECT (webrtc,
- "Creating ULPFEC encoder for session %d with pt %d", session_id,
- 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",
- WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
+ guint i;
+ for (i = 0; i < stream->ptmap->len; i++) {
+ PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
- 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);
+ if (item->pt == pt) {
+ fec_pt = transport_stream_get_pt (stream, "ULPFEC", item->media_idx);
+ break;
+ }
}
-
- prev = fecenc;
- }
-
- if (red_pt) {
- GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
-
- GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
- session_id, red_pt);
-
- gst_bin_add (GST_BIN (ret), redenc);
- if (prev)
- gst_element_link (prev, redenc);
- else
- sinkpad = gst_element_get_static_pad (redenc, "sink");
-
- g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
-
- prev = redenc;
}
- if (sinkpad) {
- GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
- gst_object_unref (sinkpad);
- gst_element_add_pad (ret, ghost);
- }
+ 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);
- 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);
+ g_object_set (ret, "pt", fec_pt, "storage", internal_storage, NULL);
+ g_object_unref (internal_storage);
}
return ret;
G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
g_signal_connect (rtpbin, "new-storage",
G_CALLBACK (on_rtpbin_new_storage), webrtc);
- g_signal_connect (rtpbin, "request-fec-decoder",
- G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
- g_signal_connect (rtpbin, "request-fec-encoder",
- G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
+ g_signal_connect (rtpbin, "request-fec-decoder-full",
+ G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
g_signal_connect (rtpbin, "on-bye-ssrc",
G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
g_signal_connect (rtpbin, "on-bye-timeout",
(GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
g_free (name);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void