Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save astarasikov/0f7a9854befd93c8ff4b742fd3760449 to your computer and use it in GitHub Desktop.
Save astarasikov/0f7a9854befd93c8ff4b742fd3760449 to your computer and use it in GitHub Desktop.
GstWebRTC backport for OpenEmbedded/Angstrom
From e465847873fdb54de7bc834c2d3d78639b98427a Mon Sep 17 00:00:00 2001
From: Matthew Waters <matthew@centricular.com>
Date: Tue, 31 Jan 2017 20:56:59 +1100
Subject: [PATCH] webrtcbin: an element that handles the transport aspects of
webrtc connections
SDP's are generated and consumed according to the W3C PeerConnection API
available from https://www.w3.org/TR/webrtc/
The SDP is either created initially from the connected
sink pads/attached transceivers as in the case of generating an offer or
intersected with the connected sink pads/attached transceivers as in
the case for creating an answer. In both cases, the rtp payloaded streams
sent by the peer are exposed as separate src pads.
The implementation supports trickle ICE, RTCP muxing, reduced size RTCP.
With contributions from:
Nirbheek Chauhan <nirbheek@centricular.com>
Mathieu Duponchelle <mathieu@centricular.com>
Edward Hervey <edward@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=792523
---
configure.ac | 16 +
docs/libs/Makefile.am | 1 +
docs/libs/gst-plugins-bad-libs-docs.sgml | 10 +
docs/libs/gst-plugins-bad-libs-sections.txt | 101 +
docs/libs/gst-plugins-bad-libs.types | 20 +
ext/Makefile.am | 12 +-
ext/meson.build | 1 +
ext/webrtc/Makefile.am | 53 +
ext/webrtc/fwd.h | 58 +
ext/webrtc/gstwebrtc.c | 39 +
ext/webrtc/gstwebrtcbin.c | 3532 +++++++++++++++++++++
ext/webrtc/gstwebrtcbin.h | 154 +
ext/webrtc/gstwebrtcice.c | 887 ++++++
ext/webrtc/gstwebrtcice.h | 83 +
ext/webrtc/gstwebrtcstats.c | 549 ++++
ext/webrtc/gstwebrtcstats.h | 35 +
ext/webrtc/icestream.c | 239 ++
ext/webrtc/icestream.h | 63 +
ext/webrtc/meson.build | 27 +
ext/webrtc/nicetransport.c | 268 ++
ext/webrtc/nicetransport.h | 58 +
ext/webrtc/transportreceivebin.c | 376 +++
ext/webrtc/transportreceivebin.h | 65 +
ext/webrtc/transportsendbin.c | 471 +++
ext/webrtc/transportsendbin.h | 58 +
ext/webrtc/transportstream.c | 252 ++
ext/webrtc/transportstream.h | 69 +
ext/webrtc/utils.c | 138 +
ext/webrtc/utils.h | 65 +
ext/webrtc/webrtcsdp.c | 716 +++++
ext/webrtc/webrtcsdp.h | 80 +
ext/webrtc/webrtctransceiver.c | 149 +
ext/webrtc/webrtctransceiver.h | 57 +
gst-libs/gst/Makefile.am | 5 +-
gst-libs/gst/meson.build | 1 +
gst-libs/gst/webrtc/Makefile.am | 54 +
gst-libs/gst/webrtc/dtlstransport.c | 238 ++
gst-libs/gst/webrtc/dtlstransport.h | 70 +
gst-libs/gst/webrtc/icetransport.c | 204 ++
gst-libs/gst/webrtc/icetransport.h | 76 +
gst-libs/gst/webrtc/meson.build | 59 +
gst-libs/gst/webrtc/rtcsessiondescription.c | 123 +
gst-libs/gst/webrtc/rtcsessiondescription.h | 58 +
gst-libs/gst/webrtc/rtpreceiver.c | 135 +
gst-libs/gst/webrtc/rtpreceiver.h | 76 +
gst-libs/gst/webrtc/rtpsender.c | 141 +
gst-libs/gst/webrtc/rtpsender.h | 77 +
gst-libs/gst/webrtc/rtptransceiver.c | 186 ++
gst-libs/gst/webrtc/rtptransceiver.h | 69 +
gst-libs/gst/webrtc/webrtc.h | 33 +
gst-libs/gst/webrtc/webrtc_fwd.h | 251 ++
gst-libs/gst/webrtc/webrtc_mkenum.py | 55 +
pkgconfig/Makefile.am | 4 +
pkgconfig/gstreamer-plugins-bad-uninstalled.pc.in | 2 +-
pkgconfig/gstreamer-webrtc-uninstalled.pc.in | 12 +
pkgconfig/gstreamer-webrtc.pc.in | 12 +
pkgconfig/meson.build | 2 +
tests/check/Makefile.am | 14 +
tests/check/elements/webrtcbin.c | 1382 ++++++++
tests/check/meson.build | 130 +
tests/examples/Makefile.am | 8 +-
tests/examples/meson.build | 23 +
tests/examples/webrtc/Makefile.am | 41 +
tests/examples/webrtc/meson.build | 15 +
tests/examples/webrtc/webrtc.c | 187 ++
tests/examples/webrtc/webrtcbidirectional.c | 197 ++
tests/examples/webrtc/webrtcswap.c | 215 ++
67 files changed, 12850 insertions(+), 7 deletions(-)
create mode 100644 ext/webrtc/Makefile.am
create mode 100644 ext/webrtc/fwd.h
create mode 100644 ext/webrtc/gstwebrtc.c
create mode 100644 ext/webrtc/gstwebrtcbin.c
create mode 100644 ext/webrtc/gstwebrtcbin.h
create mode 100644 ext/webrtc/gstwebrtcice.c
create mode 100644 ext/webrtc/gstwebrtcice.h
create mode 100644 ext/webrtc/gstwebrtcstats.c
create mode 100644 ext/webrtc/gstwebrtcstats.h
create mode 100644 ext/webrtc/icestream.c
create mode 100644 ext/webrtc/icestream.h
create mode 100644 ext/webrtc/meson.build
create mode 100644 ext/webrtc/nicetransport.c
create mode 100644 ext/webrtc/nicetransport.h
create mode 100644 ext/webrtc/transportreceivebin.c
create mode 100644 ext/webrtc/transportreceivebin.h
create mode 100644 ext/webrtc/transportsendbin.c
create mode 100644 ext/webrtc/transportsendbin.h
create mode 100644 ext/webrtc/transportstream.c
create mode 100644 ext/webrtc/transportstream.h
create mode 100644 ext/webrtc/utils.c
create mode 100644 ext/webrtc/utils.h
create mode 100644 ext/webrtc/webrtcsdp.c
create mode 100644 ext/webrtc/webrtcsdp.h
create mode 100644 ext/webrtc/webrtctransceiver.c
create mode 100644 ext/webrtc/webrtctransceiver.h
create mode 100644 gst-libs/gst/webrtc/Makefile.am
create mode 100644 gst-libs/gst/webrtc/dtlstransport.c
create mode 100644 gst-libs/gst/webrtc/dtlstransport.h
create mode 100644 gst-libs/gst/webrtc/icetransport.c
create mode 100644 gst-libs/gst/webrtc/icetransport.h
create mode 100644 gst-libs/gst/webrtc/meson.build
create mode 100644 gst-libs/gst/webrtc/rtcsessiondescription.c
create mode 100644 gst-libs/gst/webrtc/rtcsessiondescription.h
create mode 100644 gst-libs/gst/webrtc/rtpreceiver.c
create mode 100644 gst-libs/gst/webrtc/rtpreceiver.h
create mode 100644 gst-libs/gst/webrtc/rtpsender.c
create mode 100644 gst-libs/gst/webrtc/rtpsender.h
create mode 100644 gst-libs/gst/webrtc/rtptransceiver.c
create mode 100644 gst-libs/gst/webrtc/rtptransceiver.h
create mode 100644 gst-libs/gst/webrtc/webrtc.h
create mode 100644 gst-libs/gst/webrtc/webrtc_fwd.h
create mode 100755 gst-libs/gst/webrtc/webrtc_mkenum.py
create mode 100644 pkgconfig/gstreamer-webrtc-uninstalled.pc.in
create mode 100644 pkgconfig/gstreamer-webrtc.pc.in
create mode 100644 tests/check/elements/webrtcbin.c
create mode 100644 tests/check/meson.build
create mode 100644 tests/examples/meson.build
create mode 100644 tests/examples/webrtc/Makefile.am
create mode 100644 tests/examples/webrtc/meson.build
create mode 100644 tests/examples/webrtc/webrtc.c
create mode 100644 tests/examples/webrtc/webrtcbidirectional.c
create mode 100644 tests/examples/webrtc/webrtcswap.c
diff --git a/configure.ac b/configure.ac
index 30e26b873..08c07529c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3344,6 +3344,16 @@ AG_GST_CHECK_FEATURE(WEBRTCDSP, [WebRTC Audio Processing], webrtcdsp, [
AC_LANG_POP([C++])
])
+dnl *** WebRTC ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_WEBRTC, true)
+AG_GST_CHECK_FEATURE(WEBRTC, [WebRTC], webrtc, [
+ AG_GST_PKG_CHECK_MODULES(GST_SDP, gstreamer-sdp-1.0)
+ PKG_CHECK_MODULES(NICE, nice >= 0.1, [
+ HAVE_WEBRTC="yes" ], [
+ HAVE_WEBRTC="no"
+ ])
+])
+
else
dnl not building plugins with external dependencies,
@@ -3418,6 +3428,7 @@ AM_CONDITIONAL(USE_RTMP, false)
AM_CONDITIONAL(USE_TELETEXTDEC, false)
AM_CONDITIONAL(USE_UVCH264, false)
AM_CONDITIONAL(USE_WEBP, false)
+AM_CONDITIONAL(USE_WEBRTC, false)
AM_CONDITIONAL(USE_WEBRTCDSP, false)
AM_CONDITIONAL(USE_OPENH264, false)
AM_CONDITIONAL(USE_X265, false)
@@ -3594,6 +3605,7 @@ gst-libs/gst/mpegts/Makefile
gst-libs/gst/uridownloader/Makefile
gst-libs/gst/wayland/Makefile
gst-libs/gst/base/Makefile
+gst-libs/gst/webrtc/Makefile
gst-libs/gst/player/Makefile
gst-libs/gst/video/Makefile
gst-libs/gst/audio/Makefile
@@ -3656,6 +3668,7 @@ tests/examples/mxf/Makefile
tests/examples/opencv/Makefile
tests/examples/uvch264/Makefile
tests/examples/waylandsink/Makefile
+tests/examples/webrtc/Makefile
tests/icles/Makefile
ext/voamrwbenc/Makefile
ext/voaacenc/Makefile
@@ -3721,6 +3734,7 @@ ext/webp/Makefile
ext/x265/Makefile
ext/zbar/Makefile
ext/dtls/Makefile
+ext/webrtc/Makefile
ext/webrtcdsp/Makefile
ext/ttml/Makefile
po/Makefile.in
@@ -3745,6 +3759,8 @@ pkgconfig/gstreamer-wayland.pc
pkgconfig/gstreamer-wayland-uninstalled.pc
pkgconfig/gstreamer-bad-base.pc
pkgconfig/gstreamer-bad-base-uninstalled.pc
+pkgconfig/gstreamer-webrtc.pc
+pkgconfig/gstreamer-webrtc-uninstalled.pc
pkgconfig/gstreamer-bad-video.pc
pkgconfig/gstreamer-bad-video-uninstalled.pc
pkgconfig/gstreamer-bad-audio.pc
diff --git a/docs/libs/Makefile.am b/docs/libs/Makefile.am
index f294bd008..0ba5d1856 100644
--- a/docs/libs/Makefile.am
+++ b/docs/libs/Makefile.am
@@ -65,6 +65,7 @@ GTKDOC_LIBS = \
$(top_builddir)/gst-libs/gst/insertbin/libgstinsertbin-@GST_API_VERSION@.la \
$(top_builddir)/gst-libs/gst/mpegts/libgstmpegts-@GST_API_VERSION@.la \
$(top_builddir)/gst-libs/gst/player/libgstplayer-@GST_API_VERSION@.la \
+ $(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la \
$(GST_BASE_LIBS)
# If you need to override some of the declarations, place them in this file
diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml
index 872846b72..707803229 100644
--- a/docs/libs/gst-plugins-bad-libs-docs.sgml
+++ b/docs/libs/gst-plugins-bad-libs-docs.sgml
@@ -137,6 +137,16 @@
<xi:include href="xml/gstplayer-visualization.xml"/>
</chapter>
+ <chapter id="webrtc">
+ <title>WebRTC Library</title>
+ <xi:include href="xml/gstwebrtc-dtlstransport.xml"/>
+ <xi:include href="xml/gstwebrtc-icetransport.xml"/>
+ <xi:include href="xml/gstwebrtc-receiver.xml"/>
+ <xi:include href="xml/gstwebrtc-sender.xml"/>
+ <xi:include href="xml/gstwebrtc-sessiondescription.xml"/>
+ <xi:include href="xml/gstwebrtc-transceiver.xml"/>
+ </chapter>
+
<chapter>
<title>Interfaces</title>
<xi:include href="xml/gstphotography.xml" />
diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt
index 3f0faa1d5..52d4e5b2b 100644
--- a/docs/libs/gst-plugins-bad-libs-sections.txt
+++ b/docs/libs/gst-plugins-bad-libs-sections.txt
@@ -2179,3 +2179,104 @@ GstPlayerSubtitleInfoClass
gst_player_subtitle_info_get_type
</SECTION>
+
+<SECTION>
+<FILE>gstwebrtc-dtlstransport</FILE>
+GstWebRTCDTLSTransportState
+
+gst_webrtc_dtls_transport_new
+
+<SUBSECTION Standard>
+GST_TYPE_WEBRTC_DTLS_TRANSPORT
+gst_webrtc_dtls_transport_get_type
+GstWebRTCDTLSTransport
+GST_WEBRTC_DTLS_TRANSPORT
+GST_IS_WEBRTC_DTLS_TRANSPORT
+GstWebRTCDTLSTransportClass
+GST_WEBRTC_DTLS_TRANSPORT_CLASS
+GST_WEBRTC_DTLS_TRANSPORT_GET_CLASS
+GST_IS_WEBRTC_DTLS_TRANSPORT_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gstwebrtc-icetransport</FILE>
+GstWebRTCIceRole
+GstWebRTCICEConnectionState
+GstWebRTCICEGatheringState
+
+
+
+<SUBSECTION Standard>
+GST_TYPE_WEBRTC_ICE_TRANSPORT
+gst_webrtc_ice_transport_get_type
+GstWebRTCICETransport
+GST_WEBRTC_ICE_TRANSPORT
+GST_IS_WEBRTC_ICE_TRANSPORT
+GstWebRTCICETransportClass
+GST_WEBRTC_ICE_TRANSPORT_CLASS
+GST_WEBRTC_ICE_TRANSPORT_GET_CLASS
+GST_IS_WEBRTC_ICE_TRANSPORT_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gstwebrtc-receiver</FILE>
+gst_webrtc_rtp_receiver_new
+gst_webrtc_rtp_receiver_get_parameters
+gst_webrtc_rtp_receiver_set_parameters
+gst_webrtc_rtp_receiver_set_rtcp_transport
+gst_webrtc_rtp_receiver_set_transport
+<SUBSECTION Standard>
+GST_TYPE_WEBRTC_RTP_RECEIVER
+gst_webrtc_rtp_receiver_get_type
+GstWebRTCRTPReceiver
+GST_WEBRTC_RTP_RECEIVER
+GST_IS_WEBRTC_RTP_RECEIVER
+GstWebRTCRTPReceiverClass
+GST_WEBRTC_RTP_RECEIVER_CLASS
+GST_WEBRTC_RTP_RECEIVER_GET_CLASS
+GST_IS_WEBRTC_RTP_RECEIVER_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gstwebrtc-sender</FILE>
+gst_webrtc_rtp_sender_new
+gst_webrtc_rtp_sender_get_parameters
+gst_webrtc_rtp_sender_set_parameters
+gst_webrtc_rtp_sender_set_rtcp_transport
+gst_webrtc_rtp_sender_set_transport
+<SUBSECTION Standard>
+GST_TYPE_WEBRTC_RTP_SENDER
+gst_webrtc_rtp_sender_get_type
+GstWebRTCRTPSender
+GST_WEBRTC_RTP_SENDER
+GST_IS_WEBRTC_RTP_SENDER
+GstWebRTCRTPSenderClass
+GST_WEBRTC_RTP_SENDER_CLASS
+GST_WEBRTC_RTP_SENDER_GET_CLASS
+GST_IS_WEBRTC_RTP_SENDER_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gstwebrtc-sessiondescription</FILE>
+GstWebRTCSessionDescription
+gst_webrtc_session_description_new
+gst_webrtc_session_description_copy
+gst_webrtc_session_description_free
+<SUBSECTION Standard>
+gst_webrtc_session_description_get_type
+GST_TYPE_WEBRTC_SESSION_DESCRIPTION
+</SECTION>
+
+<SECTION>
+<FILE>gstwebrtc-transceiver</FILE>
+<SUBSECTION Standard>
+GST_TYPE_WEBRTC_RTP_TRANSCEIVER
+gst_webrtc_rtp_transceiver_get_type
+GstWebRTCRTPTransceiver
+GST_WEBRTC_RTP_TRANSCEIVER
+GST_IS_WEBRTC_RTP_TRANSCEIVER
+GstWebRTCRTPTransceiverClass
+GST_WEBRTC_RTP_TRANSCEIVER_CLASS
+GST_WEBRTC_RTP_TRANSCEIVER_GET_CLASS
+GST_IS_WEBRTC_RTP_TRANSCEIVER_CLASS
+</SECTION>
diff --git a/docs/libs/gst-plugins-bad-libs.types b/docs/libs/gst-plugins-bad-libs.types
index 6d5a82b35..4b2e2baff 100644
--- a/docs/libs/gst-plugins-bad-libs.types
+++ b/docs/libs/gst-plugins-bad-libs.types
@@ -9,6 +9,7 @@
#include <gst/mpegts/mpegts.h>
#include <gst/gl/gl.h>
#include <gst/player/player.h>
+#include <gst/webrtc/webrtc.h>
gst_aggregator_get_type
gst_aggregator_pad_get_type
@@ -77,3 +78,22 @@ gst_player_video_overlay_video_renderer_get_type
gst_player_video_renderer_get_type
gst_player_visualization_get_type
+
+gst_webrtc_dtls_setup_get_type
+gst_webrtc_dtls_transport_get_type
+gst_webrtc_dtls_transport_state_get_type
+gst_webrtc_ice_component_get_type
+gst_webrtc_ice_connection_state_get_type
+gst_webrtc_ice_gathering_state_get_type
+gst_webrtc_ice_role_get_type
+gst_webrtc_sdp_type_get_type
+gst_webrtc_ice_transport_get_type
+gst_webrtc_peer_connection_state_get_type
+gst_webrtc_rtp_receiver_get_type
+gst_webrtc_rtp_sender_get_type
+gst_webrtc_session_description_get_type
+gst_webrtc_signaling_state_get_type
+gst_webrtc_rtp_transceiver_direction_get_type
+gst_webrtc_rtp_transceiver_get_type
+gst_webrtc_stats_type_get_type
+
diff --git a/ext/Makefile.am b/ext/Makefile.am
index 534b9ac7b..187e54a8d 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -382,6 +382,12 @@ else
WEBRTCDSP_DIR=
endif
+if USE_WEBRTC
+WEBRTC_DIR=webrtc
+else
+WEBRTC_DIR=
+endif
+
if USE_TTML
TTML_DIR=ttml
else
@@ -454,7 +460,8 @@ SUBDIRS=\
$(DTLS_DIR) \
$(VULKAN_DIR) \
$(WEBRTCDSP_DIR) \
- $(TTML_DIR)
+ $(TTML_DIR) \
+ $(WEBRTC_DIR)
DIST_SUBDIRS = \
assrender \
@@ -519,6 +526,7 @@ DIST_SUBDIRS = \
dtls \
vulkan \
webrtcdsp \
- ttml
+ ttml \
+ webrtc
include $(top_srcdir)/common/parallel-subdirs.mak
diff --git a/ext/meson.build b/ext/meson.build
index f6ec86421..ff2d27176 100644
--- a/ext/meson.build
+++ b/ext/meson.build
@@ -63,6 +63,7 @@ subdir('voaacenc')
subdir('vulkan')
subdir('wayland')
subdir('webrtcdsp')
+subdir('webrtc')
subdir('webp')
subdir('x265')
subdir('zbar')
diff --git a/ext/webrtc/Makefile.am b/ext/webrtc/Makefile.am
new file mode 100644
index 000000000..5f9a71488
--- /dev/null
+++ b/ext/webrtc/Makefile.am
@@ -0,0 +1,53 @@
+plugin_LTLIBRARIES = libgstwebrtc.la
+
+noinst_HEADERS = \
+ fwd.h \
+ gstwebrtcbin.h \
+ gstwebrtcice.h \
+ gstwebrtcstats.h \
+ icestream.h \
+ nicetransport.h \
+ transportstream.h \
+ transportsendbin.h \
+ transportreceivebin.h \
+ utils.h \
+ webrtcsdp.h \
+ webrtctransceiver.h
+
+libgstwebrtc_la_SOURCES = \
+ gstwebrtc.c \
+ gstwebrtcbin.c \
+ gstwebrtcice.c \
+ gstwebrtcstats.c \
+ icestream.c \
+ nicetransport.c \
+ transportstream.c \
+ transportsendbin.c \
+ transportreceivebin.c \
+ utils.c \
+ webrtcsdp.c \
+ webrtctransceiver.c
+
+libgstwebrtc_la_SOURCES += $(BUILT_SOURCES)
+noinst_HEADERS += $(built_headers)
+
+libgstwebrtc_la_CFLAGS = \
+ -I$(top_builddir)/gst-libs \
+ -I$(top_srcdir)/gst-libs \
+ $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_BASE_CFLAGS) \
+ $(GST_CFLAGS) \
+ $(GST_SDP_CFLAGS) \
+ $(NICE_CFLAGS)
+libgstwebrtc_la_LIBADD = \
+ $(GST_PLUGINS_BASE_LIBS) \
+ $(GST_BASE_LIBS) \
+ $(GST_LIBS) \
+ $(GST_SDP_LIBS) \
+ $(NICE_LIBS) \
+ $(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
+
+libgstwebrtc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstwebrtc_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+
+include $(top_srcdir)/common/gst-glib-gen.mak
diff --git a/ext/webrtc/fwd.h b/ext/webrtc/fwd.h
new file mode 100644
index 000000000..903145fbf
--- /dev/null
+++ b/ext/webrtc/fwd.h
@@ -0,0 +1,58 @@
+/* GStreamer
+ * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __WEBRTC_FWD_H__
+#define __WEBRTC_FWD_H__
+
+#include <gst/gst.h>
+#include <gst/webrtc/webrtc.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstWebRTCBin GstWebRTCBin;
+typedef struct _GstWebRTCBinClass GstWebRTCBinClass;
+typedef struct _GstWebRTCBinPrivate GstWebRTCBinPrivate;
+
+typedef struct _GstWebRTCICE GstWebRTCICE;
+typedef struct _GstWebRTCICEClass GstWebRTCICEClass;
+typedef struct _GstWebRTCICEPrivate GstWebRTCICEPrivate;
+
+typedef struct _GstWebRTCICEStream GstWebRTCICEStream;
+typedef struct _GstWebRTCICEStreamClass GstWebRTCICEStreamClass;
+typedef struct _GstWebRTCICEStreamPrivate GstWebRTCICEStreamPrivate;
+
+typedef struct _GstWebRTCNiceTransport GstWebRTCNiceTransport;
+typedef struct _GstWebRTCNiceTransportClass GstWebRTCNiceTransportClass;
+typedef struct _GstWebRTCNiceTransportPrivate GstWebRTCNiceTransportPrivate;
+
+typedef struct _TransportStream TransportStream;
+typedef struct _TransportStreamClass TransportStreamClass;
+
+typedef struct _TransportSendBin TransportSendBin;
+typedef struct _TransportSendBinClass TransportSendBinClass;
+
+typedef struct _TransportReceiveBin TransportReceiveBin;
+typedef struct _TransportReceiveBinClass TransportReceiveBinClass;
+
+typedef struct _WebRTCTransceiver WebRTCTransceiver;
+typedef struct _WebRTCTransceiverClass WebRTCTransceiverClass;
+
+G_END_DECLS
+
+#endif /* __WEBRTC_FWD_H__ */
diff --git a/ext/webrtc/gstwebrtc.c b/ext/webrtc/gstwebrtc.c
new file mode 100644
index 000000000..27dc642b4
--- /dev/null
+++ b/ext/webrtc/gstwebrtc.c
@@ -0,0 +1,39 @@
+/* GStreamer
+ * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstwebrtcbin.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "webrtcbin", GST_RANK_PRIMARY,
+ GST_TYPE_WEBRTC_BIN))
+ return FALSE;
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ webrtc,
+ "WebRTC plugins",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/webrtc/gstwebrtcbin.c b/ext/webrtc/gstwebrtcbin.c
new file mode 100644
index 000000000..8d2b376fe
--- /dev/null
+++ b/ext/webrtc/gstwebrtcbin.c
@@ -0,0 +1,3532 @@
+/* GStreamer
+ * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstwebrtcbin.h"
+#include "gstwebrtcstats.h"
+#include "transportstream.h"
+#include "transportreceivebin.h"
+#include "utils.h"
+#include "webrtcsdp.h"
+#include "webrtctransceiver.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define RANDOM_SESSION_ID \
+ ((((((guint64) g_random_int()) << 32) | \
+ (guint64) g_random_int ())) & \
+ G_GUINT64_CONSTANT (0x7fffffffffffffff))
+
+#define PC_GET_LOCK(w) (&w->priv->pc_lock)
+#define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
+#define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
+
+#define PC_GET_COND(w) (&w->priv->pc_cond)
+#define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
+#define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
+#define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
+
+/*
+ * This webrtcbin implements the majority of the W3's peerconnection API and
+ * implementation guide where possible. Generating offers, answers and setting
+ * local and remote SDP's are all supported. To start with, only the media
+ * interface has been implemented (no datachannel yet).
+ *
+ * Each input/output pad is equivalent to a Track in W3 parlance which are
+ * added/removed from the bin. The number of requested sink pads is the number
+ * of streams that will be sent to the receiver and will be associated with a
+ * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
+ *
+ * On the receiving side, RTPTransceiver's are created in response to setting
+ * a remote description. Output pads for the receiving streams in the set
+ * description are also created.
+ */
+
+/*
+ * TODO:
+ * assert sending payload type matches the stream
+ * reconfiguration (of anything)
+ * LS groups
+ * bundling
+ * setting custom DTLS certificates
+ * data channel
+ *
+ * seperate session id's from mlineindex properly
+ * how to deal with replacing a input/output track/stream
+ */
+
+#define GST_CAT_DEFAULT gst_webrtc_bin_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+GQuark
+gst_webrtc_bin_error_quark (void)
+{
+ return g_quark_from_static_string ("gst-webrtc-bin-error-quark");
+}
+
+G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
+
+static void
+gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_webrtc_bin_pad_finalize (GObject * object)
+{
+ GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
+
+ if (pad->trans)
+ gst_object_unref (pad->trans);
+ pad->trans = NULL;
+
+ G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
+}
+
+static void
+gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->get_property = gst_webrtc_bin_pad_get_property;
+ gobject_class->set_property = gst_webrtc_bin_pad_set_property;
+ gobject_class->finalize = gst_webrtc_bin_pad_finalize;
+}
+
+static GstCaps *
+_transport_stream_get_caps_for_pt (TransportStream * stream, guint pt)
+{
+ guint i, len;
+
+ len = stream->ptmap->len;
+ for (i = 0; i < len; i++) {
+ PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
+ if (item->pt == pt)
+ return item->caps;
+ }
+ return NULL;
+}
+
+static void
+gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
+{
+}
+
+static GstWebRTCBinPad *
+gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
+{
+ GstWebRTCBinPad *pad =
+ g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
+ direction, NULL);
+
+ if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
+ gst_object_unref (pad);
+ return NULL;
+ }
+
+ GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
+ direction == GST_PAD_SRC ? "src" : "sink");
+ return pad;
+}
+
+#define gst_webrtc_bin_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
+ GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
+ "webrtcbin element"););
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+enum
+{
+ SIGNAL_0,
+ CREATE_OFFER_SIGNAL,
+ CREATE_ANSWER_SIGNAL,
+ SET_LOCAL_DESCRIPTION_SIGNAL,
+ SET_REMOTE_DESCRIPTION_SIGNAL,
+ ADD_ICE_CANDIDATE_SIGNAL,
+ ON_NEGOTIATION_NEEDED_SIGNAL,
+ ON_ICE_CANDIDATE_SIGNAL,
+ GET_STATS_SIGNAL,
+ ADD_TRANSCEIVER_SIGNAL,
+ GET_TRANSCEIVERS_SIGNAL,
+ LAST_SIGNAL,
+};
+
+enum
+{
+ PROP_0,
+ PROP_CONNECTION_STATE,
+ PROP_SIGNALING_STATE,
+ PROP_ICE_GATHERING_STATE,
+ PROP_ICE_CONNECTION_STATE,
+ PROP_LOCAL_DESCRIPTION,
+ PROP_CURRENT_LOCAL_DESCRIPTION,
+ PROP_PENDING_LOCAL_DESCRIPTION,
+ PROP_REMOTE_DESCRIPTION,
+ PROP_CURRENT_REMOTE_DESCRIPTION,
+ PROP_PENDING_REMOTE_DESCRIPTION,
+ PROP_STUN_SERVER,
+ PROP_TURN_SERVER,
+};
+
+static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
+
+static GstWebRTCDTLSTransport *
+_transceiver_get_transport (GstWebRTCRTPTransceiver * trans)
+{
+ if (trans->sender) {
+ return trans->sender->transport;
+ } else if (trans->receiver) {
+ return trans->receiver->transport;
+ }
+
+ return NULL;
+}
+
+static GstWebRTCDTLSTransport *
+_transceiver_get_rtcp_transport (GstWebRTCRTPTransceiver * trans)
+{
+ if (trans->sender) {
+ return trans->sender->rtcp_transport;
+ } else if (trans->receiver) {
+ return trans->receiver->rtcp_transport;
+ }
+
+ return NULL;
+}
+
+typedef struct
+{
+ guint session_id;
+ GstWebRTCICEStream *stream;
+} IceStreamItem;
+
+/* FIXME: locking? */
+GstWebRTCICEStream *
+_find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
+{
+ int i;
+
+ for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
+ IceStreamItem *item =
+ &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
+
+ if (item->session_id == session_id) {
+ GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
+ "session %u", item->stream, session_id);
+ return item->stream;
+ }
+ }
+
+ GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
+ session_id);
+ return NULL;
+}
+
+void
+_add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
+ GstWebRTCICEStream * stream)
+{
+ IceStreamItem item = { session_id, stream };
+
+ GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
+ "session %u", stream, session_id);
+ g_array_append_val (webrtc->priv->ice_stream_map, item);
+}
+
+typedef struct
+{
+ guint session_id;
+ gchar *mid;
+} SessionMidItem;
+
+static void
+clear_session_mid_item (SessionMidItem * item)
+{
+ g_free (item->mid);
+}
+
+typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
+ gconstpointer data);
+
+static GstWebRTCRTPTransceiver *
+_find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
+ FindTransceiverFunc func)
+{
+ int i;
+
+ for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+ GstWebRTCRTPTransceiver *transceiver =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ i);
+
+ if (func (transceiver, data))
+ return transceiver;
+ }
+
+ return NULL;
+}
+
+static gboolean
+match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
+{
+ return g_strcmp0 (trans->mid, mid) == 0;
+}
+
+static gboolean
+transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
+{
+ return trans->mline == *mline;
+}
+
+static GstWebRTCRTPTransceiver *
+_find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
+{
+ GstWebRTCRTPTransceiver *trans;
+
+ trans = _find_transceiver (webrtc, &mlineindex,
+ (FindTransceiverFunc) transceiver_match_for_mline);
+
+ GST_TRACE_OBJECT (webrtc,
+ "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
+ mlineindex);
+
+ return trans;
+}
+
+typedef gboolean (*FindTransportFunc) (TransportStream * p1,
+ gconstpointer data);
+
+static TransportStream *
+_find_transport (GstWebRTCBin * webrtc, gconstpointer data,
+ FindTransportFunc func)
+{
+ int i;
+
+ for (i = 0; i < webrtc->priv->transports->len; i++) {
+ TransportStream *stream =
+ g_array_index (webrtc->priv->transports, TransportStream *,
+ i);
+
+ if (func (stream, data))
+ return stream;
+ }
+
+ return NULL;
+}
+
+static gboolean
+match_stream_for_session (TransportStream * trans, guint * session)
+{
+ return trans->session_id == *session;
+}
+
+static TransportStream *
+_find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
+{
+ TransportStream *stream;
+
+ stream = _find_transport (webrtc, &session_id,
+ (FindTransportFunc) match_stream_for_session);
+
+ GST_TRACE_OBJECT (webrtc,
+ "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
+
+ return stream;
+}
+
+typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
+
+static GstWebRTCBinPad *
+_find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
+{
+ GstElement *element = GST_ELEMENT (webrtc);
+ GList *l;
+
+ GST_OBJECT_LOCK (webrtc);
+ l = element->pads;
+ for (; l; l = g_list_next (l)) {
+ if (!GST_IS_WEBRTC_BIN_PAD (l->data))
+ continue;
+ if (func (l->data, data)) {
+ gst_object_ref (l->data);
+ GST_OBJECT_UNLOCK (webrtc);
+ return l->data;
+ }
+ }
+
+ l = webrtc->priv->pending_pads;
+ for (; l; l = g_list_next (l)) {
+ if (!GST_IS_WEBRTC_BIN_PAD (l->data))
+ continue;
+ if (func (l->data, data)) {
+ gst_object_ref (l->data);
+ GST_OBJECT_UNLOCK (webrtc);
+ return l->data;
+ }
+ }
+ GST_OBJECT_UNLOCK (webrtc);
+
+ return NULL;
+}
+
+static void
+_add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+{
+ GST_OBJECT_LOCK (webrtc);
+ webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
+ GST_OBJECT_UNLOCK (webrtc);
+}
+
+static void
+_remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+{
+ GST_OBJECT_LOCK (webrtc);
+ webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
+ GST_OBJECT_UNLOCK (webrtc);
+}
+
+static void
+_add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+{
+ _remove_pending_pad (webrtc, pad);
+
+ if (webrtc->priv->running)
+ gst_pad_set_active (GST_PAD (pad), TRUE);
+ gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
+}
+
+static void
+_remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+{
+ _remove_pending_pad (webrtc, pad);
+
+ gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
+}
+
+typedef struct
+{
+ GstPadDirection direction;
+ guint mlineindex;
+} MLineMatch;
+
+static gboolean
+pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
+{
+ return GST_PAD_DIRECTION (pad) == match->direction
+ && pad->mlineindex == match->mlineindex;
+}
+
+static GstWebRTCBinPad *
+_find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
+ guint mlineindex)
+{
+ MLineMatch m = { direction, mlineindex };
+
+ return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
+}
+
+typedef struct
+{
+ GstPadDirection direction;
+ GstWebRTCRTPTransceiver *trans;
+} TransMatch;
+
+static gboolean
+pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
+{
+ return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
+}
+
+static GstWebRTCBinPad *
+_find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
+ GstWebRTCRTPTransceiver * trans)
+{
+ TransMatch m = { direction, trans };
+
+ return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
+}
+
+#if 0
+static gboolean
+match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
+{
+ return pad->ssrc == *ssrc;
+}
+
+static gboolean
+match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
+{
+ return pad == other;
+}
+#endif
+
+static gboolean
+_unlock_pc_thread (GMutex * lock)
+{
+ g_mutex_unlock (lock);
+ return G_SOURCE_REMOVE;
+}
+
+static gpointer
+_gst_pc_thread (GstWebRTCBin * webrtc)
+{
+ PC_LOCK (webrtc);
+ webrtc->priv->main_context = g_main_context_new ();
+ webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
+
+ PC_COND_BROADCAST (webrtc);
+ g_main_context_invoke (webrtc->priv->main_context,
+ (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
+
+ /* Having the thread be the thread default GMainContext will break the
+ * required queue-like ordering (from W3's peerconnection spec) of re-entrant
+ * tasks */
+ g_main_loop_run (webrtc->priv->loop);
+
+ PC_LOCK (webrtc);
+ g_main_context_unref (webrtc->priv->main_context);
+ webrtc->priv->main_context = NULL;
+ g_main_loop_unref (webrtc->priv->loop);
+ webrtc->priv->loop = NULL;
+ PC_COND_BROADCAST (webrtc);
+ PC_UNLOCK (webrtc);
+
+ return NULL;
+}
+
+static void
+_start_thread (GstWebRTCBin * webrtc)
+{
+ PC_LOCK (webrtc);
+ webrtc->priv->thread = g_thread_new ("gst-pc-ops",
+ (GThreadFunc) _gst_pc_thread, webrtc);
+
+ while (!webrtc->priv->loop)
+ PC_COND_WAIT (webrtc);
+ webrtc->priv->is_closed = FALSE;
+ PC_UNLOCK (webrtc);
+}
+
+static void
+_stop_thread (GstWebRTCBin * webrtc)
+{
+ PC_LOCK (webrtc);
+ webrtc->priv->is_closed = TRUE;
+ g_main_loop_quit (webrtc->priv->loop);
+ while (webrtc->priv->loop)
+ PC_COND_WAIT (webrtc);
+ PC_UNLOCK (webrtc);
+
+ g_thread_unref (webrtc->priv->thread);
+}
+
+static gboolean
+_execute_op (GstWebRTCBinTask * op)
+{
+ PC_LOCK (op->webrtc);
+ if (op->webrtc->priv->is_closed) {
+ GST_DEBUG_OBJECT (op->webrtc,
+ "Peerconnection is closed, aborting execution");
+ goto out;
+ }
+
+ op->op (op->webrtc, op->data);
+
+out:
+ PC_UNLOCK (op->webrtc);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+_free_op (GstWebRTCBinTask * op)
+{
+ if (op->notify)
+ op->notify (op->data);
+ g_free (op);
+}
+
+void
+gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
+ gpointer data, GDestroyNotify notify)
+{
+ GstWebRTCBinTask *op;
+ GSource *source;
+
+ g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc));
+
+ if (webrtc->priv->is_closed) {
+ GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
+ if (notify)
+ notify (data);
+ return;
+ }
+ op = g_new0 (GstWebRTCBinTask, 1);
+ op->webrtc = webrtc;
+ op->op = func;
+ op->data = data;
+ op->notify = notify;
+
+ source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (source, (GSourceFunc) _execute_op, op,
+ (GDestroyNotify) _free_op);
+ g_source_attach (source, webrtc->priv->main_context);
+ g_source_unref (source);
+}
+
+/* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
+static GstWebRTCICEConnectionState
+_collate_ice_connection_states (GstWebRTCBin * webrtc)
+{
+#define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
+ GstWebRTCICEConnectionState any_state = 0;
+ gboolean all_closed = TRUE;
+ int i;
+
+ for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+ GstWebRTCRTPTransceiver *rtp_trans =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ i);
+ WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
+ TransportStream *stream = trans->stream;
+ GstWebRTCICETransport *transport, *rtcp_transport;
+ GstWebRTCICEConnectionState ice_state;
+ gboolean rtcp_mux = FALSE;
+
+ if (rtp_trans->stopped)
+ continue;
+ if (!rtp_trans->mid)
+ continue;
+
+ g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
+
+ transport = _transceiver_get_transport (rtp_trans)->transport;
+
+ /* get transport state */
+ g_object_get (transport, "state", &ice_state, NULL);
+ any_state |= (1 << ice_state);
+ if (ice_state != STATE (CLOSED))
+ all_closed = FALSE;
+
+ rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans)->transport;
+
+ if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) {
+ g_object_get (rtcp_transport, "state", &ice_state, NULL);
+ any_state |= (1 << ice_state);
+ if (ice_state != STATE (CLOSED))
+ all_closed = FALSE;
+ }
+ }
+
+ GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
+
+ if (webrtc->priv->is_closed) {
+ GST_TRACE_OBJECT (webrtc, "returning closed");
+ return STATE (CLOSED);
+ }
+ /* Any of the RTCIceTransport s are in the failed state. */
+ if (any_state & (1 << STATE (FAILED))) {
+ GST_TRACE_OBJECT (webrtc, "returning failed");
+ return STATE (FAILED);
+ }
+ /* Any of the RTCIceTransport s are in the disconnected state and
+ * none of them are in the failed state. */
+ if (any_state & (1 << STATE (DISCONNECTED))) {
+ GST_TRACE_OBJECT (webrtc, "returning disconnected");
+ return STATE (DISCONNECTED);
+ }
+ /* Any of the RTCIceTransport's are in the checking state and none of them
+ * are in the failed or disconnected state. */
+ if (any_state & (1 << STATE (CHECKING))) {
+ GST_TRACE_OBJECT (webrtc, "returning checking");
+ return STATE (CHECKING);
+ }
+ /* Any of the RTCIceTransport s are in the new state and none of them are
+ * in the checking, failed or disconnected state, or all RTCIceTransport's
+ * are in the closed state. */
+ if ((any_state & (1 << STATE (NEW))) || all_closed) {
+ GST_TRACE_OBJECT (webrtc, "returning new");
+ return STATE (NEW);
+ }
+ /* All RTCIceTransport s are in the connected, completed or closed state
+ * and at least one of them is in the connected state. */
+ if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 <<
+ STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) {
+ GST_TRACE_OBJECT (webrtc, "returning connected");
+ return STATE (CONNECTED);
+ }
+ /* All RTCIceTransport s are in the completed or closed state and at least
+ * one of them is in the completed state. */
+ if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED))
+ && any_state & (1 << STATE (COMPLETED))) {
+ GST_TRACE_OBJECT (webrtc, "returning connected");
+ return STATE (CONNECTED);
+ }
+
+ GST_FIXME ("unspecified situation, returning new");
+ return STATE (NEW);
+#undef STATE
+}
+
+/* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
+static GstWebRTCICEGatheringState
+_collate_ice_gathering_states (GstWebRTCBin * webrtc)
+{
+#define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
+ GstWebRTCICEGatheringState any_state = 0;
+ gboolean all_completed = webrtc->priv->transceivers->len > 0;
+ int i;
+
+ for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+ GstWebRTCRTPTransceiver *rtp_trans =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ i);
+ WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
+ TransportStream *stream = trans->stream;
+ GstWebRTCICETransport *transport, *rtcp_transport;
+ GstWebRTCICEGatheringState ice_state;
+ gboolean rtcp_mux = FALSE;
+
+ if (rtp_trans->stopped)
+ continue;
+ if (!rtp_trans->mid)
+ continue;
+
+ g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
+
+ transport = _transceiver_get_transport (rtp_trans)->transport;
+
+ /* get gathering state */
+ g_object_get (transport, "gathering-state", &ice_state, NULL);
+ any_state |= (1 << ice_state);
+ if (ice_state != STATE (COMPLETE))
+ all_completed = FALSE;
+
+ rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans)->transport;
+
+ if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
+ g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL);
+ any_state |= (1 << ice_state);
+ if (ice_state != STATE (COMPLETE))
+ all_completed = FALSE;
+ }
+ }
+
+ GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
+
+ /* Any of the RTCIceTransport s are in the gathering state. */
+ if (any_state & (1 << STATE (GATHERING))) {
+ GST_TRACE_OBJECT (webrtc, "returning gathering");
+ return STATE (GATHERING);
+ }
+ /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
+ * the completed gathering state. */
+ if (all_completed) {
+ GST_TRACE_OBJECT (webrtc, "returning complete");
+ return STATE (COMPLETE);
+ }
+
+ /* Any of the RTCIceTransport s are in the new gathering state and none
+ * of the transports are in the gathering state, or there are no transports. */
+ GST_TRACE_OBJECT (webrtc, "returning new");
+ return STATE (NEW);
+#undef STATE
+}
+
+/* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
+static GstWebRTCPeerConnectionState
+_collate_peer_connection_states (GstWebRTCBin * webrtc)
+{
+#define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
+#define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
+#define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
+ GstWebRTCICEConnectionState any_ice_state = 0;
+ GstWebRTCDTLSTransportState any_dtls_state = 0;
+ int i;
+
+ for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+ GstWebRTCRTPTransceiver *rtp_trans =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ i);
+ WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
+ TransportStream *stream = trans->stream;
+ GstWebRTCDTLSTransport *transport, *rtcp_transport;
+ GstWebRTCICEGatheringState ice_state;
+ GstWebRTCDTLSTransportState dtls_state;
+ gboolean rtcp_mux = FALSE;
+
+ if (rtp_trans->stopped)
+ continue;
+ if (!rtp_trans->mid)
+ continue;
+
+ g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
+ transport = _transceiver_get_transport (rtp_trans);
+
+ /* get transport state */
+ g_object_get (transport, "state", &dtls_state, NULL);
+ any_dtls_state |= (1 << dtls_state);
+ g_object_get (transport->transport, "state", &ice_state, NULL);
+ any_ice_state |= (1 << ice_state);
+
+ rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans);
+
+ if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
+ g_object_get (rtcp_transport, "state", &dtls_state, NULL);
+ any_dtls_state |= (1 << dtls_state);
+ g_object_get (rtcp_transport->transport, "state", &ice_state, NULL);
+ any_ice_state |= (1 << ice_state);
+ }
+ }
+
+ GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
+ "state: 0x%x", any_ice_state, any_dtls_state);
+
+ /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
+ if (webrtc->priv->is_closed) {
+ GST_TRACE_OBJECT (webrtc, "returning closed");
+ return STATE (CLOSED);
+ }
+
+ /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
+ if (any_ice_state & (1 << ICE_STATE (FAILED))) {
+ GST_TRACE_OBJECT (webrtc, "returning failed");
+ return STATE (FAILED);
+ }
+ if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
+ GST_TRACE_OBJECT (webrtc, "returning failed");
+ return STATE (FAILED);
+ }
+
+ /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting
+ * or checking state and none of them is in the failed state. */
+ if (any_ice_state & (1 << ICE_STATE (CHECKING))) {
+ GST_TRACE_OBJECT (webrtc, "returning connecting");
+ return STATE (CONNECTING);
+ }
+ if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) {
+ GST_TRACE_OBJECT (webrtc, "returning connecting");
+ return STATE (CONNECTING);
+ }
+
+ /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
+ * state and none of them are in the failed or connecting or checking state. */
+ if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
+ GST_TRACE_OBJECT (webrtc, "returning disconnected");
+ return STATE (DISCONNECTED);
+ }
+
+ /* All RTCIceTransport's and RTCDtlsTransport's are in the connected,
+ * completed or closed state and at least of them is in the connected or
+ * completed state. */
+ if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 <<
+ ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED)))
+ && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 <<
+ DTLS_STATE (CLOSED)))
+ && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 <<
+ ICE_STATE (COMPLETED))
+ || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) {
+ GST_TRACE_OBJECT (webrtc, "returning connected");
+ return STATE (CONNECTED);
+ }
+
+ /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state
+ * and none of the transports are in the connecting, checking, failed or
+ * disconnected state, or all transports are in the closed state. */
+ if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) {
+ GST_TRACE_OBJECT (webrtc, "returning new");
+ return STATE (NEW);
+ }
+ if ((any_ice_state & (1 << ICE_STATE (NEW))
+ || any_dtls_state & (1 << DTLS_STATE (NEW)))
+ && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED)
+ | (1 << ICE_STATE (DISCONNECTED))))
+ && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 <<
+ DTLS_STATE (FAILED)))) {
+ GST_TRACE_OBJECT (webrtc, "returning new");
+ return STATE (NEW);
+ }
+
+ GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new");
+ return STATE (NEW);
+#undef DTLS_STATE
+#undef ICE_STATE
+#undef STATE
+}
+
+static void
+_update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
+{
+ GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
+ GstWebRTCICEGatheringState new_state;
+
+ new_state = _collate_ice_gathering_states (webrtc);
+
+ if (new_state != webrtc->ice_gathering_state) {
+ gchar *old_s, *new_s;
+
+ old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
+ old_state);
+ new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
+ new_state);
+ GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
+ old_s, old_state, new_s, new_state);
+ g_free (old_s);
+ g_free (new_s);
+
+ webrtc->ice_gathering_state = new_state;
+ PC_UNLOCK (webrtc);
+ g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
+ PC_LOCK (webrtc);
+ }
+}
+
+static void
+_update_ice_gathering_state (GstWebRTCBin * webrtc)
+{
+ gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
+ NULL);
+}
+
+static void
+_update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
+{
+ GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
+ GstWebRTCICEConnectionState new_state;
+
+ new_state = _collate_ice_connection_states (webrtc);
+
+ if (new_state != old_state) {
+ gchar *old_s, *new_s;
+
+ old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
+ old_state);
+ new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
+ new_state);
+ GST_INFO_OBJECT (webrtc,
+ "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
+ new_s, new_state);
+ g_free (old_s);
+ g_free (new_s);
+
+ webrtc->ice_connection_state = new_state;
+ PC_UNLOCK (webrtc);
+ g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
+ PC_LOCK (webrtc);
+ }
+}
+
+static void
+_update_ice_connection_state (GstWebRTCBin * webrtc)
+{
+ gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
+ NULL);
+}
+
+static void
+_update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
+{
+ GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
+ GstWebRTCPeerConnectionState new_state;
+
+ new_state = _collate_peer_connection_states (webrtc);
+
+ if (new_state != old_state) {
+ gchar *old_s, *new_s;
+
+ old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
+ old_state);
+ new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
+ new_state);
+ GST_INFO_OBJECT (webrtc,
+ "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
+ new_s, new_state);
+ g_free (old_s);
+ g_free (new_s);
+
+ webrtc->peer_connection_state = new_state;
+ PC_UNLOCK (webrtc);
+ g_object_notify (G_OBJECT (webrtc), "connection-state");
+ PC_LOCK (webrtc);
+ }
+}
+
+static void
+_update_peer_connection_state (GstWebRTCBin * webrtc)
+{
+ gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
+ NULL, NULL);
+}
+
+/* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
+static gboolean
+_check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
+{
+ int i;
+
+ GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
+
+ /* If any implementation-specific negotiation is required, as described at
+ * the start of this section, return "true".
+ * FIXME */
+ /* FIXME: emit when input caps/format changes? */
+
+ /* If connection has created any RTCDataChannel's, and no m= section has
+ * been negotiated yet for data, return "true".
+ * FIXME */
+
+ if (!webrtc->current_local_description) {
+ GST_LOG_OBJECT (webrtc, "no local description set");
+ return TRUE;
+ }
+
+ if (!webrtc->current_remote_description) {
+ GST_LOG_OBJECT (webrtc, "no remote description set");
+ return TRUE;
+ }
+
+ for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+ GstWebRTCRTPTransceiver *trans;
+
+ trans =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ i);
+
+ if (trans->stopped) {
+ /* FIXME: If t is stopped and is associated with an m= section according to
+ * [JSEP] (section 3.4.1.), but the associated m= section is not yet
+ * rejected in connection's currentLocalDescription or
+ * currentRemoteDescription , return "true". */
+ GST_FIXME_OBJECT (webrtc,
+ "check if the transceiver is rejected in descriptions");
+ } else {
+ const GstSDPMedia *media;
+ GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
+
+ if (trans->mline == -1) {
+ GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT,
+ i, trans);
+ return TRUE;
+ }
+ /* internal inconsistency */
+ g_assert (trans->mline <
+ gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
+ g_assert (trans->mline <
+ gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
+
+ /* FIXME: msid handling
+ * If t's direction is "sendrecv" or "sendonly", and the associated m=
+ * section in connection's currentLocalDescription doesn't contain an
+ * "a=msid" line, return "true". */
+
+ media =
+ gst_sdp_message_get_media (webrtc->current_local_description->sdp,
+ trans->mline);
+ local_dir = _get_direction_from_media (media);
+
+ media =
+ gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
+ trans->mline);
+ remote_dir = _get_direction_from_media (media);
+
+ if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
+ /* If connection's currentLocalDescription if of type "offer", and
+ * the direction of the associated m= section in neither the offer
+ * nor answer matches t's direction, return "true". */
+
+ if (local_dir != trans->direction && remote_dir != trans->direction) {
+ GST_LOG_OBJECT (webrtc,
+ "transceiver direction doesn't match description");
+ return TRUE;
+ }
+ } else if (webrtc->current_local_description->type ==
+ GST_WEBRTC_SDP_TYPE_ANSWER) {
+ GstWebRTCRTPTransceiverDirection intersect_dir;
+
+ /* If connection's currentLocalDescription if of type "answer", and
+ * the direction of the associated m= section in the answer does not
+ * match t's direction intersected with the offered direction (as
+ * described in [JSEP] (section 5.3.1.)), return "true". */
+
+ /* remote is the offer, local is the answer */
+ intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
+
+ if (intersect_dir != trans->direction) {
+ GST_LOG_OBJECT (webrtc,
+ "transceiver direction doesn't match description");
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ GST_LOG_OBJECT (webrtc, "no negotiation needed");
+ return FALSE;
+}
+
+static void
+_check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
+{
+ if (webrtc->priv->need_negotiation) {
+ GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
+ PC_UNLOCK (webrtc);
+ g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
+ 0);
+ PC_LOCK (webrtc);
+ }
+}
+
+/* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
+static void
+_update_need_negotiation (GstWebRTCBin * webrtc)
+{
+ /* If connection's [[isClosed]] slot is true, abort these steps. */
+ if (webrtc->priv->is_closed)
+ return;
+ /* If connection's signaling state is not "stable", abort these steps. */
+ if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
+ return;
+
+ /* If the result of checking if negotiation is needed is "false", clear the
+ * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
+ * to false, and abort these steps. */
+ if (!_check_if_negotiation_is_needed (webrtc)) {
+ webrtc->priv->need_negotiation = FALSE;
+ return;
+ }
+ /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
+ if (webrtc->priv->need_negotiation)
+ return;
+ /* Set connection's [[needNegotiation]] slot to true. */
+ webrtc->priv->need_negotiation = TRUE;
+ /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
+ * true, fire a simple event named negotiationneeded at connection. */
+ gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
+ NULL);
+}
+
+static GstCaps *
+_find_codec_preferences (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * trans,
+ GstPadDirection direction, guint media_idx)
+{
+ GstCaps *ret = NULL;
+
+ GST_LOG_OBJECT (webrtc, "retreiving codec preferences from %" GST_PTR_FORMAT,
+ trans);
+
+ if (trans->codec_preferences) {
+ GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
+ trans->codec_preferences);
+ ret = gst_caps_ref (trans->codec_preferences);
+ } else {
+ GstWebRTCBinPad *pad = _find_pad_for_mline (webrtc, direction, media_idx);
+ if (pad) {
+ GstCaps *caps = gst_pad_get_current_caps (GST_PAD (pad));
+ if (caps) {
+ GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
+ caps);
+ } else {
+ if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
+ GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
+ caps);
+ }
+ if (caps)
+ ret = caps;
+ gst_object_unref (pad);
+ }
+ }
+
+ return ret;
+}
+
+static GstCaps *
+_add_supported_attributes_to_caps (const GstCaps * caps)
+{
+ GstCaps *ret;
+ int i;
+
+ ret = gst_caps_make_writable (caps);
+
+ for (i = 0; i < gst_caps_get_size (ret); i++) {
+ GstStructure *s = gst_caps_get_structure (ret, i);
+
+ if (!gst_structure_has_field (s, "rtcp-fb-nack"))
+ gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
+ if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
+ gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
+ /* FIXME: is this needed? */
+ /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
+ gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
+
+ /* FIXME: codec-specific paramters? */
+ }
+
+ return ret;
+}
+
+static void
+_on_ice_transport_notify_state (GstWebRTCICETransport * transport,
+ GParamSpec * pspec, GstWebRTCBin * webrtc)
+{
+ _update_ice_connection_state (webrtc);
+ _update_peer_connection_state (webrtc);
+}
+
+static void
+_on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
+ GParamSpec * pspec, GstWebRTCBin * webrtc)
+{
+ _update_ice_gathering_state (webrtc);
+}
+
+static void
+_on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
+ GParamSpec * pspec, GstWebRTCBin * webrtc)
+{
+ _update_peer_connection_state (webrtc);
+}
+
+static WebRTCTransceiver *
+_create_webrtc_transceiver (GstWebRTCBin * webrtc)
+{
+ WebRTCTransceiver *trans;
+ GstWebRTCRTPTransceiver *rtp_trans;
+ GstWebRTCRTPSender *sender;
+ GstWebRTCRTPReceiver *receiver;
+
+ sender = gst_webrtc_rtp_sender_new (NULL);
+ receiver = gst_webrtc_rtp_receiver_new ();
+ trans = webrtc_transceiver_new (webrtc, sender, receiver);
+ rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
+ rtp_trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
+ rtp_trans->mline = -1;
+
+ g_array_append_val (webrtc->priv->transceivers, trans);
+
+ gst_object_unref (sender);
+ gst_object_unref (receiver);
+
+ return trans;
+}
+
+static TransportStream *
+_create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
+{
+ GstWebRTCDTLSTransport *transport;
+ TransportStream *ret;
+ gchar *pad_name;
+
+ /* FIXME: how to parametrize the sender and the receiver */
+ ret = transport_stream_new (webrtc, session_id);
+ transport = ret->transport;
+
+ g_signal_connect (G_OBJECT (transport->transport), "notify::state",
+ G_CALLBACK (_on_ice_transport_notify_state), webrtc);
+ g_signal_connect (G_OBJECT (transport->transport),
+ "notify::gathering-state",
+ G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
+ g_signal_connect (G_OBJECT (transport), "notify::state",
+ G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
+
+ if ((transport = ret->rtcp_transport)) {
+ g_signal_connect (G_OBJECT (transport->transport),
+ "notify::state", G_CALLBACK (_on_ice_transport_notify_state), webrtc);
+ g_signal_connect (G_OBJECT (transport->transport),
+ "notify::gathering-state",
+ G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
+ g_signal_connect (G_OBJECT (transport), "notify::state",
+ G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
+ }
+
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
+
+ pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
+ if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
+ GST_ELEMENT (webrtc->rtpbin), pad_name))
+ g_warn_if_reached ();
+ g_free (pad_name);
+
+ pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
+ if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
+ GST_ELEMENT (ret->send_bin), "rtcp_sink"))
+ g_warn_if_reached ();
+ g_free (pad_name);
+
+ g_array_append_val (webrtc->priv->transports, ret);
+
+ GST_TRACE_OBJECT (webrtc,
+ "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
+
+ gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
+ gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
+
+ return ret;
+}
+
+/* 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, GstWebRTCSDPType type, guint media_idx)
+{
+ /* TODO:
+ * rtp header extensions
+ * ice attributes
+ * rtx
+ * fec
+ * msid-semantics
+ * msid
+ * dtls fingerprints
+ * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
+ */
+ gchar *direction, *sdp_mid;
+ GstCaps *caps;
+ int i;
+
+ /* "An m= section is generated for each RtpTransceiver that has been added
+ * to the Bin, excluding any stopped RtpTransceivers." */
+ if (trans->stopped)
+ return FALSE;
+ if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
+ || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
+ return FALSE;
+
+ gst_sdp_media_set_port_info (media, 9, 0);
+ gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
+ gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
+
+ direction =
+ _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
+ trans->direction);
+ gst_sdp_media_add_attribute (media, direction, "");
+ g_free (direction);
+ /* FIXME: negotiate this */
+ gst_sdp_media_add_attribute (media, "rtcp-mux", "");
+ gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
+
+ if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
+ caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
+ caps = _add_supported_attributes_to_caps (caps);
+ } else if (type == GST_WEBRTC_SDP_TYPE_ANSWER) {
+ caps = _find_codec_preferences (webrtc, trans, GST_PAD_SRC, media_idx);
+ /* FIXME: add rtcp-fb paramaters */
+ } else {
+ g_assert_not_reached ();
+ }
+
+ 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;
+ }
+
+ 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);
+
+ gst_caps_append_structure (format, gst_structure_copy (s));
+
+ GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
+ " to %u-th media", i, format, media_idx);
+
+ /* this only looks at the first structure so we loop over the given caps
+ * and add each structure inside it piecemeal */
+ gst_sdp_media_set_media_from_caps (format, media);
+
+ gst_caps_unref (format);
+ }
+
+ /* Some identifier; we also add the media name to it so it's identifiable */
+ sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
+ webrtc->priv->media_counter++);
+ gst_sdp_media_add_attribute (media, "mid", sdp_mid);
+ g_free (sdp_mid);
+
+ if (trans->sender) {
+ gchar *cert, *fingerprint, *val;
+
+ if (!trans->sender->transport) {
+ TransportStream *item;
+ /* FIXME: bundle */
+ item = _find_transport_for_session (webrtc, media_idx);
+ if (!item)
+ item = _create_transport_channel (webrtc, media_idx);
+ webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
+ }
+
+ g_object_get (trans->sender->transport, "certificate", &cert, NULL);
+
+ fingerprint =
+ _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
+ g_free (cert);
+ val =
+ g_strdup_printf ("%s %s",
+ _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
+ g_free (fingerprint);
+
+ gst_sdp_media_add_attribute (media, "fingerprint", val);
+ g_free (val);
+ }
+
+ gst_caps_unref (caps);
+
+ return TRUE;
+}
+
+static GstSDPMessage *
+_create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
+{
+ GstSDPMessage *ret;
+ int i;
+
+ gst_sdp_message_new (&ret);
+
+ gst_sdp_message_set_version (ret, "0");
+ {
+ /* FIXME: session id and version need special handling depending on the state we're in */
+ gchar *sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
+ gst_sdp_message_set_origin (ret, "-", sess_id, "0", "IN", "IP4", "0.0.0.0");
+ g_free (sess_id);
+ }
+ gst_sdp_message_set_session_name (ret, "-");
+ gst_sdp_message_add_time (ret, "0", "0", NULL);
+ gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
+
+ /* for each rtp transceiver */
+ for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+ GstWebRTCRTPTransceiver *trans;
+ GstSDPMedia media = { 0, };
+ gchar *ufrag, *pwd;
+
+ trans =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ i);
+
+ gst_sdp_media_init (&media);
+ /* mandated by JSEP */
+ gst_sdp_media_add_attribute (&media, "setup", "actpass");
+
+ /* FIXME: only needed when restarting ICE */
+ _generate_ice_credentials (&ufrag, &pwd);
+ gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag);
+ gst_sdp_media_add_attribute (&media, "ice-pwd", pwd);
+ g_free (ufrag);
+ g_free (pwd);
+
+ if (sdp_media_from_transceiver (webrtc, &media, trans,
+ GST_WEBRTC_SDP_TYPE_OFFER, i))
+ gst_sdp_message_add_media (ret, &media);
+ else
+ gst_sdp_media_uninit (&media);
+ }
+
+ /* FIXME: pre-emptively setup receiving elements when needed */
+
+ /* XXX: only true for the initial offerer */
+ g_object_set (webrtc->priv->ice, "controller", TRUE, NULL);
+
+ return ret;
+}
+
+static GstSDPMessage *
+_create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
+{
+ GstSDPMessage *ret = NULL;
+ const GstWebRTCSessionDescription *pending_remote =
+ webrtc->pending_remote_description;
+ int i;
+
+ if (!webrtc->pending_remote_description) {
+ GST_ERROR_OBJECT (webrtc,
+ "Asked to create an answer without a remote description");
+ return NULL;
+ }
+
+ gst_sdp_message_new (&ret);
+
+ /* FIXME: session id and version need special handling depending on the state we're in */
+ gst_sdp_message_set_version (ret, "0");
+ {
+ const GstSDPOrigin *offer_origin =
+ gst_sdp_message_get_origin (pending_remote->sdp);
+ gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id, "0", "IN",
+ "IP4", "0.0.0.0");
+ }
+ gst_sdp_message_set_session_name (ret, "-");
+
+ for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
+ const GstSDPAttribute *attr =
+ gst_sdp_message_get_attribute (pending_remote->sdp, i);
+
+ if (g_strcmp0 (attr->key, "ice-options") == 0) {
+ gst_sdp_message_add_attribute (ret, attr->key, attr->value);
+ }
+ }
+
+ for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
+ /* FIXME:
+ * bundle policy
+ */
+ GstSDPMedia *media = NULL;
+ GstSDPMedia *offer_media;
+ GstWebRTCRTPTransceiver *rtp_trans = NULL;
+ WebRTCTransceiver *trans = NULL;
+ GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
+ GstWebRTCDTLSSetup offer_setup, answer_setup;
+ GstCaps *offer_caps, *answer_caps = NULL;
+ gchar *cert;
+ int j;
+
+ gst_sdp_media_new (&media);
+ gst_sdp_media_set_port_info (media, 9, 0);
+ gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
+ gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
+
+ {
+ /* FIXME: only needed when restarting ICE */
+ gchar *ufrag, *pwd;
+ _generate_ice_credentials (&ufrag, &pwd);
+ gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
+ gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
+ g_free (ufrag);
+ g_free (pwd);
+ }
+
+ offer_media =
+ (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
+ for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
+ const GstSDPAttribute *attr =
+ gst_sdp_media_get_attribute (offer_media, j);
+
+ if (g_strcmp0 (attr->key, "mid") == 0
+ || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
+ gst_sdp_media_add_attribute (media, attr->key, attr->value);
+ /* FIXME: handle anything we want to keep */
+ }
+ }
+
+ offer_caps = gst_caps_new_empty ();
+ for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) {
+ guint pt = atoi (gst_sdp_media_get_format (offer_media, j));
+ GstCaps *caps;
+ int k;
+
+ caps = gst_sdp_media_get_caps_from_media (offer_media, pt);
+
+ /* gst_sdp_media_get_caps_from_media() produces caps with name
+ * "application/x-unknown" which will fail intersection with
+ * "application/x-rtp" caps so mangle the returns caps to have the
+ * correct name here */
+ for (k = 0; k < gst_caps_get_size (caps); k++) {
+ GstStructure *s = gst_caps_get_structure (caps, k);
+ gst_structure_set_name (s, "application/x-rtp");
+ }
+
+ gst_caps_append (offer_caps, caps);
+ }
+
+ for (j = 0; j < webrtc->priv->transceivers->len; j++) {
+ GstCaps *trans_caps;
+
+ rtp_trans =
+ g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
+ j);
+ trans_caps = _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
+
+ GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
+ " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
+
+ /* FIXME: technically this is a little overreaching as some fields we
+ * we can deal with not having and/or we may have unrecognized fields
+ * that we cannot actually support */
+ if (trans_caps) {
+ answer_caps = gst_caps_intersect (offer_caps, trans_caps);
+ if (answer_caps && !gst_caps_is_empty (answer_caps)) {
+ GST_LOG_OBJECT (webrtc,
+ "found compatible transceiver %" GST_PTR_FORMAT
+ " for offer media %u", trans, i);
+ if (trans_caps)
+ gst_caps_unref (trans_caps);
+ break;
+ } else {
+ if (answer_caps) {
+ gst_caps_unref (answer_caps);
+ answer_caps = NULL;
+ }
+ if (trans_caps)
+ gst_caps_unref (trans_caps);
+ rtp_trans = NULL;
+ }
+ } else {
+ rtp_trans = NULL;
+ }
+ }
+
+ if (rtp_trans) {
+ answer_dir = rtp_trans->direction;
+ g_assert (answer_caps != NULL);
+ } else {
+ /* if no transceiver, then we only receive that stream and respond with
+ * the exact same caps */
+ /* FIXME: how to validate that subsequent elements can actually receive
+ * this payload/format */
+ answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
+ answer_caps = gst_caps_ref (offer_caps);
+ }
+ /* respond with the requested caps */
+ if (answer_caps) {
+ gst_sdp_media_set_media_from_caps (answer_caps, media);
+ gst_caps_unref (answer_caps);
+ answer_caps = NULL;
+ }
+ if (!rtp_trans) {
+ trans = _create_webrtc_transceiver (webrtc);
+ rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
+ rtp_trans->direction = answer_dir;
+ rtp_trans->mline = i;
+ } else {
+ trans = WEBRTC_TRANSCEIVER (rtp_trans);
+ }
+
+ /* set the new media direction */
+ offer_dir = _get_direction_from_media (offer_media);
+ answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
+ if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
+ GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
+ "transceiver direction");
+ goto rejected;
+ }
+ _media_replace_direction (media, answer_dir);
+
+ /* set the a=setup: attribute */
+ offer_setup = _get_dtls_setup_from_media (offer_media);
+ answer_setup = _intersect_dtls_setup (offer_setup);
+ if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
+ GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
+ "transceiver direction");
+ goto rejected;
+ }
+ _media_replace_setup (media, answer_setup);
+
+ /* FIXME: bundle! */
+ if (!trans->stream) {
+ TransportStream *item = _find_transport_for_session (webrtc, i);
+ if (!item)
+ item = _create_transport_channel (webrtc, i);
+ webrtc_transceiver_set_transport (trans, item);
+ }
+ /* set the a=fingerprint: for this transport */
+ g_object_get (trans->stream->transport, "certificate", &cert, NULL);
+
+ {
+ gchar *fingerprint, *val;
+
+ fingerprint =
+ _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
+ g_free (cert);
+ val =
+ g_strdup_printf ("%s %s",
+ _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
+ g_free (fingerprint);
+
+ gst_sdp_media_add_attribute (media, "fingerprint", val);
+ g_free (val);
+ }
+
+ if (0) {
+ rejected:
+ 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);
+ }
+ gst_sdp_message_add_media (ret, media);
+ gst_sdp_media_free (media);
+
+ gst_caps_unref (offer_caps);
+ }
+
+ /* FIXME: can we add not matched transceivers? */
+
+ /* XXX: only true for the initial offerer */
+ g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
+
+ return ret;
+}
+
+struct create_sdp
+{
+ GstStructure *options;
+ GstPromise *promise;
+ GstWebRTCSDPType type;
+};
+
+static void
+_create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
+{
+ GstWebRTCSessionDescription *desc = NULL;
+ GstSDPMessage *sdp = NULL;
+ GstStructure *s = NULL;
+
+ GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
+ gst_webrtc_sdp_type_to_string (data->type), data->options);
+
+ if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
+ sdp = _create_offer_task (webrtc, data->options);
+ else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
+ sdp = _create_answer_task (webrtc, data->options);
+ else {
+ g_assert_not_reached ();
+ goto out;
+ }
+
+ if (sdp) {
+ desc = gst_webrtc_session_description_new (data->type, sdp);
+ s = gst_structure_new ("application/x-gst-promise",
+ gst_webrtc_sdp_type_to_string (data->type),
+ GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
+ }
+
+out:
+ PC_UNLOCK (webrtc);
+ gst_promise_reply (data->promise, s);
+ PC_LOCK (webrtc);
+
+ if (desc)
+ gst_webrtc_session_description_free (desc);
+}
+
+static void
+_free_create_sdp_data (struct create_sdp *data)
+{
+ if (data->options)
+ gst_structure_free (data->options);
+ gst_promise_unref (data->promise);
+ g_free (data);
+}
+
+static void
+gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
+ const GstStructure * options, GstPromise * promise)
+{
+ struct create_sdp *data = g_new0 (struct create_sdp, 1);
+
+ if (options)
+ data->options = gst_structure_copy (options);
+ data->promise = gst_promise_ref (promise);
+ data->type = GST_WEBRTC_SDP_TYPE_OFFER;
+
+ gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
+ data, (GDestroyNotify) _free_create_sdp_data);
+}
+
+static void
+gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
+ const GstStructure * options, GstPromise * promise)
+{
+ struct create_sdp *data = g_new0 (struct create_sdp, 1);
+
+ if (options)
+ data->options = gst_structure_copy (options);
+ data->promise = gst_promise_ref (promise);
+ data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
+
+ gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
+ data, (GDestroyNotify) _free_create_sdp_data);
+}
+
+static GstWebRTCBinPad *
+_create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
+ guint media_idx)
+{
+ GstWebRTCBinPad *pad;
+ gchar *pad_name;
+
+ pad_name =
+ g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
+ media_idx);
+ pad = gst_webrtc_bin_pad_new (pad_name, direction);
+ g_free (pad_name);
+ pad->mlineindex = media_idx;
+
+ return pad;
+}
+
+static GstWebRTCRTPTransceiver *
+_find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
+ const GstSDPMessage * sdp, guint media_idx)
+{
+ const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
+ GstWebRTCRTPTransceiver *ret = NULL;
+ int i;
+
+ for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
+ const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
+
+ if (g_strcmp0 (attr->key, "mid") == 0) {
+ if ((ret =
+ _find_transceiver (webrtc, attr->value,
+ (FindTransceiverFunc) match_for_mid)))
+ goto out;
+ }
+ }
+
+ ret = _find_transceiver (webrtc, &media_idx,
+ (FindTransceiverFunc) transceiver_match_for_mline);
+
+out:
+ GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
+ return ret;
+}
+
+static GstPad *
+_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+{
+/*
+ * ,-------------------------webrtcbin-------------------------,
+ * ; ;
+ * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
+ * ; ; send_rtp_src_%u o---o rtp_sink ; ;
+ * ; ; ; ; ; ;
+ * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
+ * ; sink_%u ; ; '---------------------' ;
+ * o----------o send_rtp_sink_%u ; ;
+ * ; '--------------------' ;
+ * '--------------------- -------------------------------------'
+ */
+ GstPadTemplate *rtp_templ;
+ GstPad *rtp_sink;
+ gchar *pad_name;
+ WebRTCTransceiver *trans;
+
+ g_return_val_if_fail (pad->trans != NULL, NULL);
+
+ GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
+
+ rtp_templ =
+ _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
+ "send_rtp_sink_%u");
+ g_assert (rtp_templ);
+
+ pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
+ rtp_sink =
+ gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
+ g_free (pad_name);
+ gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
+ gst_object_unref (rtp_sink);
+
+ trans = WEBRTC_TRANSCEIVER (pad->trans);
+ if (!trans->stream) {
+ TransportStream *item;
+ /* FIXME: bundle */
+ item = _find_transport_for_session (webrtc, pad->mlineindex);
+ if (!item)
+ item = _create_transport_channel (webrtc, pad->mlineindex);
+ webrtc_transceiver_set_transport (trans, item);
+ }
+
+ pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
+ if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
+ GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
+ g_warn_if_reached ();
+ g_free (pad_name);
+
+ gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
+
+ return GST_PAD (pad);
+}
+
+/* output pads are receiving elements */
+static GstWebRTCBinPad *
+_connect_output_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+{
+/*
+ * ,------------------------webrtcbin------------------------,
+ * ; ,---------rtpbin---------, ;
+ * ; ,-transport_receive_%u--, ; ; ;
+ * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
+ * ; ; ; ; ; ;
+ * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
+ * ; '-----------------------' ; ; ; src_%u
+ * ; ; recv_rtp_src_%u_%u_%u o--o
+ * ; '------------------------' ;
+ * '---------------------------------------------------------'
+ */
+ gchar *pad_name;
+ WebRTCTransceiver *trans;
+
+ g_return_val_if_fail (pad->trans != NULL, NULL);
+
+ GST_INFO_OBJECT (pad, "linking output stream %u", pad->mlineindex);
+
+ trans = WEBRTC_TRANSCEIVER (pad->trans);
+ if (!trans->stream) {
+ TransportStream *item;
+ /* FIXME: bundle */
+ item = _find_transport_for_session (webrtc, pad->mlineindex);
+ if (!item)
+ item = _create_transport_channel (webrtc, pad->mlineindex);
+ webrtc_transceiver_set_transport (trans, item);
+ }
+
+ pad_name = g_strdup_printf ("recv_rtp_sink_%u", pad->mlineindex);
+ if (!gst_element_link_pads (GST_ELEMENT (trans->stream->receive_bin),
+ "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
+ g_warn_if_reached ();
+ g_free (pad_name);
+
+ gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->receive_bin));
+
+ return pad;
+}
+
+typedef struct
+{
+ guint mlineindex;
+ gchar *candidate;
+} IceCandidateItem;
+
+static void
+_clear_ice_candidate_item (IceCandidateItem ** item)
+{
+ g_free ((*item)->candidate);
+ g_free (*item);
+}
+
+static void
+_add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item)
+{
+ GstWebRTCICEStream *stream;
+
+ stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
+ if (stream == NULL) {
+ GST_WARNING_OBJECT (webrtc, "Unknown mline %u, ignoring", item->mlineindex);
+ return;
+ }
+
+ GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",