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
This commit is contained in:
Matthew Waters 2017-01-31 20:56:59 +11:00
parent 94a7bf9ede
commit 1894293d63
69 changed files with 12704 additions and 7 deletions

5
.gitignore vendored
View file

@ -46,6 +46,8 @@ Makefile
tmp-orc.c
gst*orc.h
/gst-libs/gst/*/*-enumtypes.[ch]
/tests/check/orc
/tests/check/media/
/tests/check/libs/player
@ -71,6 +73,9 @@ gst*orc.h
/tests/examples/opencv/gstfacedetect_test
/tests/examples/playout
/tests/examples/waylandsink/gtkwaylandsink
/tests/examples/webrtc/webrtc
/tests/examples/webrtc/webrtcbidirectional
/tests/examples/webrtc/webrtcswap
Build
*.user

View file

@ -2425,6 +2425,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,
@ -2501,6 +2511,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)
@ -2676,6 +2687,7 @@ gst-libs/gst/codecparsers/Makefile
gst-libs/gst/mpegts/Makefile
gst-libs/gst/uridownloader/Makefile
gst-libs/gst/wayland/Makefile
gst-libs/gst/webrtc/Makefile
gst-libs/gst/player/Makefile
gst-libs/gst/video/Makefile
gst-libs/gst/audio/Makefile
@ -2724,6 +2736,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
@ -2793,6 +2806,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
@ -2813,6 +2827,8 @@ pkgconfig/gstreamer-player.pc
pkgconfig/gstreamer-player-uninstalled.pc
pkgconfig/gstreamer-wayland.pc
pkgconfig/gstreamer-wayland-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

View file

@ -61,6 +61,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

View file

@ -73,6 +73,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" />

View file

@ -1065,3 +1065,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>

View file

@ -7,6 +7,7 @@
#include <gst/insertbin/gstinsertbin.h>
#include <gst/mpegts/mpegts.h>
#include <gst/player/player.h>
#include <gst/webrtc/webrtc.h>
gst_audio_aggregator_get_type
gst_audio_aggregator_pad_get_type
@ -49,3 +50,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

View file

@ -406,6 +406,12 @@ else
WEBRTCDSP_DIR=
endif
if USE_WEBRTC
WEBRTC_DIR=webrtc
else
WEBRTC_DIR=
endif
if USE_TTML
TTML_DIR=ttml
else
@ -482,7 +488,8 @@ SUBDIRS=\
$(DTLS_DIR) \
$(VULKAN_DIR) \
$(WEBRTCDSP_DIR) \
$(TTML_DIR)
$(TTML_DIR) \
$(WEBRTC_DIR)
DIST_SUBDIRS = \
assrender \
@ -551,6 +558,7 @@ DIST_SUBDIRS = \
dtls \
vulkan \
webrtcdsp \
ttml
ttml \
webrtc
include $(top_srcdir)/common/parallel-subdirs.mak

View file

@ -65,6 +65,7 @@ subdir('voaacenc')
subdir('vulkan')
subdir('wayland')
subdir('webrtcdsp')
subdir('webrtc')
subdir('webp')
subdir('x265')
subdir('zbar')

53
ext/webrtc/Makefile.am Normal file
View file

@ -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

58
ext/webrtc/fwd.h Normal file
View file

@ -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__ */

39
ext/webrtc/gstwebrtc.c Normal file
View file

@ -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)

3530
ext/webrtc/gstwebrtcbin.c Normal file

File diff suppressed because it is too large Load diff

154
ext/webrtc/gstwebrtcbin.h Normal file
View file

@ -0,0 +1,154 @@
/* 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 __GST_WEBRTC_BIN_H__
#define __GST_WEBRTC_BIN_H__
#include <gst/sdp/sdp.h>
#include "fwd.h"
#include "gstwebrtcice.h"
G_BEGIN_DECLS
#define GST_WEBRTC_BIN_ERROR gst_webrtc_bin_error_quark ()
GQuark gst_webrtc_bin_error_quark (void);
typedef enum
{
GST_WEBRTC_BIN_ERROR_FAILED,
GST_WEBRTC_BIN_ERROR_INVALID_SYNTAX,
GST_WEBRTC_BIN_ERROR_INVALID_MODIFICATION,
GST_WEBRTC_BIN_ERROR_INVALID_STATE,
GST_WEBRTC_BIN_ERROR_BAD_SDP,
GST_WEBRTC_BIN_ERROR_FINGERPRINT,
} GstWebRTCJSEPSDPError;
GType gst_webrtc_bin_pad_get_type(void);
#define GST_TYPE_WEBRTC_BIN_PAD (gst_webrtc_bin_pad_get_type())
#define GST_WEBRTC_BIN_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_BIN_PAD,GstWebRTCBinPad))
#define GST_IS_WEBRTC_BIN_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_BIN_PAD))
#define GST_WEBRTC_BIN_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_BIN_PAD,GstWebRTCBinPadClass))
#define GST_IS_WEBRTC_BIN_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_BIN_PAD))
#define GST_WEBRTC_BIN_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_BIN_PAD,GstWebRTCBinPadClass))
typedef struct _GstWebRTCBinPad GstWebRTCBinPad;
typedef struct _GstWebRTCBinPadClass GstWebRTCBinPadClass;
struct _GstWebRTCBinPad
{
GstGhostPad parent;
guint mlineindex;
GstWebRTCRTPTransceiver *trans;
};
struct _GstWebRTCBinPadClass
{
GstGhostPadClass parent_class;
};
GType gst_webrtc_bin_get_type(void);
#define GST_TYPE_WEBRTC_BIN (gst_webrtc_bin_get_type())
#define GST_WEBRTC_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_BIN,GstWebRTCBin))
#define GST_IS_WEBRTC_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_BIN))
#define GST_WEBRTC_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_BIN,GstWebRTCBinClass))
#define GST_IS_WEBRTC_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_BIN))
#define GST_WEBRTC_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_BIN,GstWebRTCBinClass))
struct _GstWebRTCBin
{
GstBin parent;
GstElement *rtpbin;
GstWebRTCSignalingState signaling_state;
GstWebRTCICEGatheringState ice_gathering_state;
GstWebRTCICEConnectionState ice_connection_state;
GstWebRTCPeerConnectionState peer_connection_state;
GstWebRTCSessionDescription *current_local_description;
GstWebRTCSessionDescription *pending_local_description;
GstWebRTCSessionDescription *current_remote_description;
GstWebRTCSessionDescription *pending_remote_description;
GstWebRTCBinPrivate *priv;
};
struct _GstWebRTCBinClass
{
GstBinClass parent_class;
};
struct _GstWebRTCBinPrivate
{
guint max_sink_pad_serial;
gboolean bundle;
GArray *transceivers;
GArray *session_mid_map;
GArray *transports;
GstWebRTCICE *ice;
GArray *ice_stream_map;
GArray *pending_ice_candidates;
/* peerconnection variables */
gboolean is_closed;
gboolean need_negotiation;
gpointer sctp_transport; /* FIXME */
/* peerconnection helper thread for promises */
GMainContext *main_context;
GMainLoop *loop;
GThread *thread;
GMutex pc_lock;
GCond pc_cond;
gboolean running;
gboolean async_pending;
GList *pending_pads;
/* count of the number of media streams we've offered for uniqueness */
/* FIXME: overflow? */
guint media_counter;
GstStructure *stats;
};
typedef void (*GstWebRTCBinFunc) (GstWebRTCBin * webrtc, gpointer data);
typedef struct
{
GstWebRTCBin *webrtc;
GstWebRTCBinFunc op;
gpointer data;
GDestroyNotify notify;
// GstPromise *promise; /* FIXME */
} GstWebRTCBinTask;
void gst_webrtc_bin_enqueue_task (GstWebRTCBin * pc,
GstWebRTCBinFunc func,
gpointer data,
GDestroyNotify notify);
G_END_DECLS
#endif /* __GST_WEBRTC_BIN_H__ */

887
ext/webrtc/gstwebrtcice.c Normal file
View file

@ -0,0 +1,887 @@
/* 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 "gstwebrtcice.h"
/* libnice */
#include <agent.h>
#include "icestream.h"
#include "nicetransport.h"
/* XXX:
*
* - are locally generated remote candidates meant to be readded to libnice?
*/
#define GST_CAT_DEFAULT gst_webrtc_ice_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_ice_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCICE, gst_webrtc_ice,
GST_TYPE_OBJECT,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_ice_debug, "webrtcice", 0, "webrtcice");
);
GQuark
gst_webrtc_ice_error_quark (void)
{
return g_quark_from_static_string ("gst-webrtc-ice-error-quark");
}
enum
{
SIGNAL_0,
ON_ICE_CANDIDATE_SIGNAL,
ON_ICE_GATHERING_STATE_CHANGE_SIGNAL,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_ICE_GATHERING_STATE,
PROP_STUN_SERVER,
PROP_TURN_SERVER,
PROP_CONTROLLER,
PROP_AGENT,
};
static guint gst_webrtc_ice_signals[LAST_SIGNAL] = { 0 };
struct _GstWebRTCICEPrivate
{
NiceAgent *nice_agent;
GArray *nice_stream_map;
GThread *thread;
GMainContext *main_context;
GMainLoop *loop;
GMutex lock;
GCond cond;
};
static gboolean
_unlock_pc_thread (GMutex * lock)
{
g_mutex_unlock (lock);
return G_SOURCE_REMOVE;
}
static gpointer
_gst_nice_thread (GstWebRTCICE * ice)
{
g_mutex_lock (&ice->priv->lock);
ice->priv->main_context = g_main_context_new ();
ice->priv->loop = g_main_loop_new (ice->priv->main_context, FALSE);
g_cond_broadcast (&ice->priv->cond);
g_main_context_invoke (ice->priv->main_context,
(GSourceFunc) _unlock_pc_thread, &ice->priv->lock);
g_main_loop_run (ice->priv->loop);
g_mutex_lock (&ice->priv->lock);
g_main_context_unref (ice->priv->main_context);
ice->priv->main_context = NULL;
g_main_loop_unref (ice->priv->loop);
ice->priv->loop = NULL;
g_cond_broadcast (&ice->priv->cond);
g_mutex_unlock (&ice->priv->lock);
return NULL;
}
static void
_start_thread (GstWebRTCICE * ice)
{
g_mutex_lock (&ice->priv->lock);
ice->priv->thread = g_thread_new ("gst-nice-ops",
(GThreadFunc) _gst_nice_thread, ice);
while (!ice->priv->loop)
g_cond_wait (&ice->priv->cond, &ice->priv->lock);
g_mutex_unlock (&ice->priv->lock);
}
static void
_stop_thread (GstWebRTCICE * ice)
{
g_mutex_lock (&ice->priv->lock);
g_main_loop_quit (ice->priv->loop);
while (ice->priv->loop)
g_cond_wait (&ice->priv->cond, &ice->priv->lock);
g_mutex_unlock (&ice->priv->lock);
g_thread_unref (ice->priv->thread);
}
#if 0
static NiceComponentType
_webrtc_component_to_nice (GstWebRTCICEComponent comp)
{
switch (comp) {
case GST_WEBRTC_ICE_COMPONENT_RTP:
return NICE_COMPONENT_TYPE_RTP;
case GST_WEBRTC_ICE_COMPONENT_RTCP:
return NICE_COMPONENT_TYPE_RTCP;
default:
g_assert_not_reached ();
return 0;
}
}
static GstWebRTCICEComponent
_nice_component_to_webrtc (NiceComponentType comp)
{
switch (comp) {
case NICE_COMPONENT_TYPE_RTP:
return GST_WEBRTC_ICE_COMPONENT_RTP;
case NICE_COMPONENT_TYPE_RTCP:
return GST_WEBRTC_ICE_COMPONENT_RTCP;
default:
g_assert_not_reached ();
return 0;
}
}
#endif
struct NiceStreamItem
{
guint session_id;
guint nice_stream_id;
GstWebRTCICEStream *stream;
};
/* TRUE to continue, FALSE to stop */
typedef gboolean (*NiceStreamItemForeachFunc) (struct NiceStreamItem * item,
gpointer user_data);
static void
_nice_stream_item_foreach (GstWebRTCICE * ice, NiceStreamItemForeachFunc func,
gpointer data)
{
int i, len;
len = ice->priv->nice_stream_map->len;
for (i = 0; i < len; i++) {
struct NiceStreamItem *item =
&g_array_index (ice->priv->nice_stream_map, struct NiceStreamItem,
i);
if (!func (item, data))
break;
}
}
/* TRUE for match, FALSE otherwise */
typedef gboolean (*NiceStreamItemFindFunc) (struct NiceStreamItem * item,
gpointer user_data);
struct nice_find
{
NiceStreamItemFindFunc func;
gpointer data;
struct NiceStreamItem *ret;
};
static gboolean
_find_nice_item (struct NiceStreamItem *item, gpointer user_data)
{
struct nice_find *f = user_data;
if (f->func (item, f->data)) {
f->ret = item;
return FALSE;
}
return TRUE;
}
static struct NiceStreamItem *
_nice_stream_item_find (GstWebRTCICE * ice, NiceStreamItemFindFunc func,
gpointer data)
{
struct nice_find f;
f.func = func;
f.data = data;
f.ret = NULL;
_nice_stream_item_foreach (ice, _find_nice_item, &f);
return f.ret;
}
#define NICE_MATCH_INIT { -1, -1, NULL }
static gboolean
_match (struct NiceStreamItem *item, struct NiceStreamItem *m)
{
if (m->session_id != -1 && m->session_id != item->session_id)
return FALSE;
if (m->nice_stream_id != -1 && m->nice_stream_id != item->nice_stream_id)
return FALSE;
if (m->stream != NULL && m->stream != item->stream)
return FALSE;
return TRUE;
}
static struct NiceStreamItem *
_find_item (GstWebRTCICE * ice, guint session_id, guint nice_stream_id,
GstWebRTCICEStream * stream)
{
struct NiceStreamItem m = NICE_MATCH_INIT;
m.session_id = session_id;
m.nice_stream_id = nice_stream_id;
m.stream = stream;
return _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
}
static struct NiceStreamItem *
_create_nice_stream_item (GstWebRTCICE * ice, guint session_id)
{
struct NiceStreamItem item;
item.session_id = session_id;
item.nice_stream_id = nice_agent_add_stream (ice->priv->nice_agent, 2);
item.stream = gst_webrtc_ice_stream_new (ice, item.nice_stream_id);
g_array_append_val (ice->priv->nice_stream_map, item);
return _find_item (ice, item.session_id, item.nice_stream_id, item.stream);
}
static void
_parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass)
{
const gchar *colon;
if (!userinfo) {
*user = NULL;
*pass = NULL;
return;
}
colon = g_strstr_len (userinfo, -1, ":");
if (!colon) {
*user = g_strdup (userinfo);
*pass = NULL;
return;
}
*user = g_strndup (userinfo, colon - userinfo);
*pass = g_strdup (&colon[1]);
}
GstWebRTCICEStream *
gst_webrtc_ice_add_stream (GstWebRTCICE * ice, guint session_id)
{
struct NiceStreamItem m = NICE_MATCH_INIT;
struct NiceStreamItem *item;
m.session_id = session_id;
item = _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
if (item) {
GST_ERROR_OBJECT (ice, "stream already added with session_id=%u",
session_id);
return 0;
}
item = _create_nice_stream_item (ice, session_id);
if (ice->turn_server) {
gboolean ret;
gchar *user, *pass;
const gchar *userinfo, *transport, *scheme;
NiceRelayType relays[4] = { 0, };
int i, relay_n = 0;
scheme = gst_uri_get_scheme (ice->turn_server);
transport = gst_uri_get_query_value (ice->turn_server, "transport");
userinfo = gst_uri_get_userinfo (ice->turn_server);
_parse_userinfo (userinfo, &user, &pass);
if (g_strcmp0 (scheme, "turns") == 0) {
relays[relay_n++] = NICE_RELAY_TYPE_TURN_TLS;
} else if (g_strcmp0 (scheme, "turn") == 0) {
if (!transport || g_strcmp0 (transport, "udp") == 0)
relays[relay_n++] = NICE_RELAY_TYPE_TURN_UDP;
if (!transport || g_strcmp0 (transport, "tcp") == 0)
relays[relay_n++] = NICE_RELAY_TYPE_TURN_TCP;
}
g_assert (relay_n < G_N_ELEMENTS (relays));
for (i = 0; i < relay_n; i++) {
ret = nice_agent_set_relay_info (ice->priv->nice_agent,
item->nice_stream_id, NICE_COMPONENT_TYPE_RTP,
gst_uri_get_host (ice->turn_server),
gst_uri_get_port (ice->turn_server), user, pass, relays[i]);
if (!ret) {
gchar *uri = gst_uri_to_string (ice->turn_server);
GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
g_free (uri);
break;
}
ret = nice_agent_set_relay_info (ice->priv->nice_agent,
item->nice_stream_id, NICE_COMPONENT_TYPE_RTCP,
gst_uri_get_host (ice->turn_server),
gst_uri_get_port (ice->turn_server), user, pass, relays[i]);
if (!ret) {
gchar *uri = gst_uri_to_string (ice->turn_server);
GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
g_free (uri);
break;
}
}
g_free (user);
g_free (pass);
}
return item->stream;
}
static void
_on_new_candidate (NiceAgent * agent, NiceCandidate * candidate,
GstWebRTCICE * ice)
{
struct NiceStreamItem *item;
gchar *attr;
item = _find_item (ice, -1, candidate->stream_id, NULL);
if (!item) {
GST_WARNING_OBJECT (ice, "received signal for non-existent stream %u",
candidate->stream_id);
return;
}
if (!candidate->username || !candidate->password) {
gboolean got_credentials;
gchar *ufrag, *password;
got_credentials = nice_agent_get_local_credentials (ice->priv->nice_agent,
candidate->stream_id, &ufrag, &password);
g_warn_if_fail (got_credentials);
if (!candidate->username)
candidate->username = ufrag;
else
g_free (ufrag);
if (!candidate->password)
candidate->password = password;
else
g_free (password);
}
attr = nice_agent_generate_local_candidate_sdp (agent, candidate);
g_signal_emit (ice, gst_webrtc_ice_signals[ON_ICE_CANDIDATE_SIGNAL],
0, item->session_id, attr);
g_free (attr);
}
GstWebRTCICETransport *
gst_webrtc_ice_find_transport (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
GstWebRTCICEComponent component)
{
struct NiceStreamItem *item;
item = _find_item (ice, -1, -1, stream);
g_return_val_if_fail (item != NULL, NULL);
return gst_webrtc_ice_stream_find_transport (item->stream, component);
}
#if 0
/* TODO don't rely on libnice to (de)serialize candidates */
static NiceCandidateType
_candidate_type_from_string (const gchar * s)
{
if (g_strcmp0 (s, "host") == 0) {
return NICE_CANDIDATE_TYPE_HOST;
} else if (g_strcmp0 (s, "srflx") == 0) {
return NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
} else if (g_strcmp0 (s, "prflx") == 0) { /* FIXME: is the right string? */
return NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
} else if (g_strcmp0 (s, "relay") == 0) {
return NICE_CANDIDATE_TYPE_RELAY;
} else {
g_assert_not_reached ();
return 0;
}
}
static const gchar *
_candidate_type_to_string (NiceCandidateType type)
{
switch (type) {
case NICE_CANDIDATE_TYPE_HOST:
return "host";
case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
return "srflx";
case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
return "prflx";
case NICE_CANDIDATE_TYPE_RELAY:
return "relay";
default:
g_assert_not_reached ();
return NULL;
}
}
static NiceCandidateTransport
_candidate_transport_from_string (const gchar * s)
{
if (g_strcmp0 (s, "UDP") == 0) {
return NICE_CANDIDATE_TRANSPORT_UDP;
} else if (g_strcmp0 (s, "TCP tcptype") == 0) {
return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
} else if (g_strcmp0 (s, "tcp-passive") == 0) { /* FIXME: is the right string? */
return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
} else if (g_strcmp0 (s, "tcp-so") == 0) {
return NICE_CANDIDATE_TRANSPORT_TCP_SO;
} else {
g_assert_not_reached ();
return 0;
}
}
static const gchar *
_candidate_type_to_string (NiceCandidateType type)
{
switch (type) {
case NICE_CANDIDATE_TYPE_HOST:
return "host";
case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
return "srflx";
case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
return "prflx";
case NICE_CANDIDATE_TYPE_RELAY:
return "relay";
default:
g_assert_not_reached ();
return NULL;
}
}
#endif
/* must start with "a=candidate:" */
void
gst_webrtc_ice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
const gchar * candidate)
{
struct NiceStreamItem *item;
NiceCandidate *cand;
GSList *candidates = NULL;
item = _find_item (ice, -1, -1, stream);
g_return_if_fail (item != NULL);
cand =
nice_agent_parse_remote_candidate_sdp (ice->priv->nice_agent,
item->nice_stream_id, candidate);
if (!cand) {
GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'", candidate);
return;
}
candidates = g_slist_append (candidates, cand);
nice_agent_set_remote_candidates (ice->priv->nice_agent, item->nice_stream_id,
cand->component_id, candidates);
g_slist_free (candidates);
nice_candidate_free (cand);
}
gboolean
gst_webrtc_ice_set_remote_credentials (GstWebRTCICE * ice,
GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
{
struct NiceStreamItem *item;
g_return_val_if_fail (ufrag != NULL, FALSE);
g_return_val_if_fail (pwd != NULL, FALSE);
item = _find_item (ice, -1, -1, stream);
g_return_val_if_fail (item != NULL, FALSE);
GST_DEBUG_OBJECT (ice, "Setting remote ICE credentials on "
"ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
nice_agent_set_remote_credentials (ice->priv->nice_agent,
item->nice_stream_id, ufrag, pwd);
return TRUE;
}
gboolean
gst_webrtc_ice_set_local_credentials (GstWebRTCICE * ice,
GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
{
struct NiceStreamItem *item;
g_return_val_if_fail (ufrag != NULL, FALSE);
g_return_val_if_fail (pwd != NULL, FALSE);
item = _find_item (ice, -1, -1, stream);
g_return_val_if_fail (item != NULL, FALSE);
GST_DEBUG_OBJECT (ice, "Setting local ICE credentials on "
"ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
nice_agent_set_local_credentials (ice->priv->nice_agent, item->nice_stream_id,
ufrag, pwd);
return TRUE;
}
gboolean
gst_webrtc_ice_gather_candidates (GstWebRTCICE * ice,
GstWebRTCICEStream * stream)
{
struct NiceStreamItem *item;
item = _find_item (ice, -1, -1, stream);
g_return_val_if_fail (item != NULL, FALSE);
GST_DEBUG_OBJECT (ice, "gather candidates for stream %u",
item->nice_stream_id);
return gst_webrtc_ice_stream_gather_candidates (stream);
}
static void
_clear_ice_stream (struct NiceStreamItem *item)
{
if (!item)
return;
if (item->stream) {
g_signal_handlers_disconnect_by_data (item->stream->ice->priv->nice_agent,
item->stream);
gst_object_unref (item->stream);
}
}
static gchar *
_resolve_host (const gchar * host)
{
GResolver *resolver = g_resolver_get_default ();
GError *error = NULL;
GInetAddress *addr;
GList *addresses;
if (!(addresses = g_resolver_lookup_by_name (resolver, host, NULL, &error))) {
GST_ERROR ("%s", error->message);
g_clear_error (&error);
return NULL;
}
/* XXX: only the first address is used */
addr = addresses->data;
return g_inet_address_to_string (addr);
}
static void
_set_turn_server (GstWebRTCICE * ice, const gchar * s)
{
GstUri *uri = gst_uri_from_string (s);
const gchar *userinfo, *host, *scheme;
GList *keys = NULL, *l;
gchar *ip = NULL, *user = NULL, *pass = NULL;
gboolean turn_tls = FALSE;
guint port;
GST_DEBUG_OBJECT (ice, "setting turn server, %s", s);
if (!uri) {
GST_ERROR_OBJECT (ice, "Could not parse turn server '%s'", s);
return;
}
scheme = gst_uri_get_scheme (uri);
if (g_strcmp0 (scheme, "turn") == 0) {
} else if (g_strcmp0 (scheme, "turns") == 0) {
turn_tls = TRUE;
} else {
GST_ERROR_OBJECT (ice, "unknown scheme '%s'", scheme);
goto out;
}
keys = gst_uri_get_query_keys (uri);
for (l = keys; l; l = l->next) {
gchar *key = l->data;
if (g_strcmp0 (key, "transport") == 0) {
const gchar *transport = gst_uri_get_query_value (uri, "transport");
if (!transport) {
} else if (g_strcmp0 (transport, "udp") == 0) {
} else if (g_strcmp0 (transport, "tcp") == 0) {
} else {
GST_ERROR_OBJECT (ice, "unknown transport value, '%s'", transport);
goto out;
}
} else {
GST_ERROR_OBJECT (ice, "unknown query key, '%s'", key);
goto out;
}
}
/* TODO: Implement error checking similar to the stun server below */
userinfo = gst_uri_get_userinfo (uri);
_parse_userinfo (userinfo, &user, &pass);
if (!user) {
GST_ERROR_OBJECT (ice, "No username specified in '%s'", s);
goto out;
}
if (!pass) {
GST_ERROR_OBJECT (ice, "No password specified in '%s'", s);
goto out;
}
host = gst_uri_get_host (uri);
if (!host) {
GST_ERROR_OBJECT (ice, "Turn server has no host");
goto out;
}
ip = _resolve_host (host);
if (!ip) {
GST_ERROR_OBJECT (ice, "Failed to resolve turn server '%s'", host);
goto out;
}
port = gst_uri_get_port (uri);
if (port == GST_URI_NO_PORT) {
if (turn_tls) {
gst_uri_set_port (uri, 5349);
} else {
gst_uri_set_port (uri, 3478);
}
}
/* Set the resolved IP as the host since that's what libnice wants */
gst_uri_set_host (uri, ip);
if (ice->turn_server)
gst_uri_unref (ice->turn_server);
ice->turn_server = uri;
out:
g_list_free (keys);
g_free (ip);
g_free (user);
g_free (pass);
}
static void
gst_webrtc_ice_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
switch (prop_id) {
case PROP_STUN_SERVER:{
const gchar *s = g_value_get_string (value);
GstUri *uri = gst_uri_from_string (s);
const gchar *msg = "must be of the form stun://<host>:<port>";
const gchar *host;
gchar *ip;
guint port;
GST_DEBUG_OBJECT (ice, "setting stun server, %s", s);
if (!uri) {
GST_ERROR_OBJECT (ice, "Couldn't parse stun server '%s', %s", s, msg);
return;
}
host = gst_uri_get_host (uri);
if (!host) {
GST_ERROR_OBJECT (ice, "Stun server '%s' has no host, %s", s, msg);
return;
}
port = gst_uri_get_port (uri);
if (port == GST_URI_NO_PORT) {
GST_INFO_OBJECT (ice, "Stun server '%s' has no port, assuming 3478", s);
port = 3478;
gst_uri_set_port (uri, port);
}
ip = _resolve_host (host);
if (!ip) {
GST_ERROR_OBJECT (ice, "Failed to resolve stun server '%s'", host);
return;
}
if (ice->stun_server)
gst_uri_unref (ice->stun_server);
ice->stun_server = uri;
g_object_set (ice->priv->nice_agent, "stun-server", ip,
"stun-server-port", port, NULL);
g_free (ip);
break;
}
case PROP_TURN_SERVER:{
_set_turn_server (ice, g_value_get_string (value));
break;
}
case PROP_CONTROLLER:
g_object_set_property (G_OBJECT (ice->priv->nice_agent),
"controlling-mode", value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_ice_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
switch (prop_id) {
case PROP_STUN_SERVER:
if (ice->stun_server)
g_value_take_string (value, gst_uri_to_string (ice->stun_server));
else
g_value_take_string (value, NULL);
break;
case PROP_TURN_SERVER:
if (ice->turn_server)
g_value_take_string (value, gst_uri_to_string (ice->turn_server));
else
g_value_take_string (value, NULL);
break;
case PROP_CONTROLLER:
g_object_get_property (G_OBJECT (ice->priv->nice_agent),
"controlling-mode", value);
break;
case PROP_AGENT:
g_value_set_object (value, ice->priv->nice_agent);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_ice_finalize (GObject * object)
{
GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
g_signal_handlers_disconnect_by_data (ice->priv->nice_agent, ice);
_stop_thread (ice);
if (ice->turn_server)
gst_uri_unref (ice->turn_server);
if (ice->stun_server)
gst_uri_unref (ice->stun_server);
g_mutex_clear (&ice->priv->lock);
g_cond_clear (&ice->priv->cond);
g_array_free (ice->priv->nice_stream_map, TRUE);
g_object_unref (ice->priv->nice_agent);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_webrtc_ice_class_init (GstWebRTCICEClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
g_type_class_add_private (klass, sizeof (GstWebRTCICEPrivate));
gobject_class->get_property = gst_webrtc_ice_get_property;
gobject_class->set_property = gst_webrtc_ice_set_property;
gobject_class->finalize = gst_webrtc_ice_finalize;
g_object_class_install_property (gobject_class,
PROP_STUN_SERVER,
g_param_spec_string ("stun-server", "STUN Server",
"The STUN server of the form stun://hostname:port",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_TURN_SERVER,
g_param_spec_string ("turn-server", "TURN Server",
"The TURN server of the form turn(s)://username:password@host:port",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_CONTROLLER,
g_param_spec_boolean ("controller", "ICE controller",
"Whether the ICE agent is the controller or controlled. "
"In WebRTC, the initial offerrer is the ICE controller.", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_AGENT,
g_param_spec_object ("agent", "ICE agent",
"ICE agent in use by this object", NICE_TYPE_AGENT,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GstWebRTCICE::on-ice-candidate:
* @object: the #GstWebRtcBin
* @candidate: the ICE candidate
*/
gst_webrtc_ice_signals[ON_ICE_CANDIDATE_SIGNAL] =
g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
}
static void
gst_webrtc_ice_init (GstWebRTCICE * ice)
{
ice->priv =
G_TYPE_INSTANCE_GET_PRIVATE ((ice), GST_TYPE_WEBRTC_ICE,
GstWebRTCICEPrivate);
g_mutex_init (&ice->priv->lock);
g_cond_init (&ice->priv->cond);
_start_thread (ice);
ice->priv->nice_agent = nice_agent_new (ice->priv->main_context,
NICE_COMPATIBILITY_RFC5245);
g_signal_connect (ice->priv->nice_agent, "new-candidate-full",
G_CALLBACK (_on_new_candidate), ice);
ice->priv->nice_stream_map =
g_array_new (FALSE, TRUE, sizeof (struct NiceStreamItem));
g_array_set_clear_func (ice->priv->nice_stream_map,
(GDestroyNotify) _clear_ice_stream);
}
GstWebRTCICE *
gst_webrtc_ice_new (void)
{
return g_object_new (GST_TYPE_WEBRTC_ICE, NULL);
}

83
ext/webrtc/gstwebrtcice.h Normal file
View file

@ -0,0 +1,83 @@
/* 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 __GST_WEBRTC_ICE_H__
#define __GST_WEBRTC_ICE_H__
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc.h>
#include "fwd.h"
G_BEGIN_DECLS
#define GST_WEBRTC_ICE_ERROR gst_webrtc_ice_error_quark ()
GQuark gst_webrtc_ice_error_quark (void);
GType gst_webrtc_ice_get_type(void);
#define GST_TYPE_WEBRTC_ICE (gst_webrtc_ice_get_type())
#define GST_WEBRTC_ICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_ICE,GstWebRTCICE))
#define GST_IS_WEBRTC_ICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_ICE))
#define GST_WEBRTC_ICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_ICE,GstWebRTCICEClass))
#define GST_IS_WEBRTC_ICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_ICE))
#define GST_WEBRTC_ICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_ICE,GstWebRTCICEClass))
struct _GstWebRTCICE
{
GstObject parent;
GstWebRTCICEGatheringState ice_gathering_state;
GstWebRTCICEConnectionState ice_connection_state;
GstUri *stun_server;
GstUri *turn_server;
GstWebRTCICEPrivate *priv;
};
struct _GstWebRTCICEClass
{
GstObjectClass parent_class;
};
GstWebRTCICE * gst_webrtc_ice_new (void);
GstWebRTCICEStream * gst_webrtc_ice_add_stream (GstWebRTCICE * ice,
guint session_id);
GstWebRTCICETransport * gst_webrtc_ice_find_transport (GstWebRTCICE * ice,
GstWebRTCICEStream * stream,
GstWebRTCICEComponent component);
gboolean gst_webrtc_ice_gather_candidates (GstWebRTCICE * ice,
GstWebRTCICEStream * stream);
/* FIXME: GstStructure-ize the candidate */
void gst_webrtc_ice_add_candidate (GstWebRTCICE * ice,
GstWebRTCICEStream * stream,
const gchar * candidate);
gboolean gst_webrtc_ice_set_local_credentials (GstWebRTCICE * ice,
GstWebRTCICEStream * stream,
gchar * ufrag,
gchar * pwd);
gboolean gst_webrtc_ice_set_remote_credentials (GstWebRTCICE * ice,
GstWebRTCICEStream * stream,
gchar * ufrag,
gchar * pwd);
G_END_DECLS
#endif /* __GST_WEBRTC_ICE_H__ */

549
ext/webrtc/gstwebrtcstats.c Normal file
View file

@ -0,0 +1,549 @@
/* 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
/* for GValueArray... */
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "gstwebrtcstats.h"
#include "gstwebrtcbin.h"
#include "transportstream.h"
#include "transportreceivebin.h"
#include "utils.h"
#include "webrtctransceiver.h"
#define GST_CAT_DEFAULT gst_webrtc_stats_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void
_init_debug (void)
{
static gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (gst_webrtc_stats_debug, "webrtcice", 0,
"webrtcice");
g_once_init_leave (&_init, 1);
}
}
static double
monotonic_time_as_double_milliseconds (void)
{
return g_get_monotonic_time () / 1000.0;
}
static void
_set_base_stats (GstStructure * s, GstWebRTCStatsType type, double ts,
const char *id)
{
gchar *name = _enum_value_to_string (GST_TYPE_WEBRTC_STATS_TYPE,
type);
g_return_if_fail (name != NULL);
gst_structure_set_name (s, name);
gst_structure_set (s, "type", GST_TYPE_WEBRTC_STATS_TYPE, type, "timestamp",
G_TYPE_DOUBLE, ts, "id", G_TYPE_STRING, id, NULL);
g_free (name);
}
static GstStructure *
_get_peer_connection_stats (GstWebRTCBin * webrtc)
{
GstStructure *s = gst_structure_new_empty ("unused");
/* FIXME: datachannel */
gst_structure_set (s, "data-channels-opened", G_TYPE_UINT, 0,
"data-channels-closed", G_TYPE_UINT, 0, "data-channels-requested",
G_TYPE_UINT, 0, "data-channels-accepted", G_TYPE_UINT, 0, NULL);
return s;
}
#define CLOCK_RATE_VALUE_TO_SECONDS(v,r) ((double) v / (double) clock_rate)
/* https://www.w3.org/TR/webrtc-stats/#inboundrtpstats-dict*
https://www.w3.org/TR/webrtc-stats/#outboundrtpstats-dict* */
static void
_get_stats_from_rtp_source_stats (GstWebRTCBin * webrtc,
const GstStructure * source_stats, const gchar * codec_id,
const gchar * transport_id, GstStructure * s)
{
GstStructure *in, *out, *r_in, *r_out;
gchar *in_id, *out_id, *r_in_id, *r_out_id;
guint ssrc, fir, pli, nack, jitter;
int lost, clock_rate;
guint64 packets, bytes;
gboolean have_rb = FALSE, sent_rb = FALSE;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
gst_structure_get_uint (source_stats, "ssrc", &ssrc);
gst_structure_get (source_stats, "have-rb", G_TYPE_BOOLEAN, &have_rb,
"sent_rb", G_TYPE_BOOLEAN, &sent_rb, "clock-rate", G_TYPE_INT,
&clock_rate, NULL);
in_id = g_strdup_printf ("rtp-inbound-stream-stats_%u", ssrc);
out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc);
r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc);
r_out_id = g_strdup_printf ("rtp-remote-outbound-stream-stats_%u", ssrc);
in = gst_structure_new_empty (in_id);
_set_base_stats (in, GST_WEBRTC_STATS_INBOUND_RTP, ts, in_id);
/* RTCStreamStats */
gst_structure_set (in, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (in, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (in, "transport-id", G_TYPE_STRING, transport_id, NULL);
if (gst_structure_get_uint (source_stats, "recv-fir-count", &fir))
gst_structure_set (in, "fir-count", G_TYPE_UINT, fir, NULL);
if (gst_structure_get_uint (source_stats, "recv-pli-count", &pli))
gst_structure_set (in, "pli-count", G_TYPE_UINT, pli, NULL);
if (gst_structure_get_uint (source_stats, "recv-nack-count", &nack))
gst_structure_set (in, "nack-count", G_TYPE_UINT, nack, NULL);
/* XXX: mediaType, trackId, sliCount, qpSum */
/* RTCReceivedRTPStreamStats */
if (gst_structure_get_uint64 (source_stats, "packets-received", &packets))
gst_structure_set (in, "packets-received", G_TYPE_UINT64, packets, NULL);
if (gst_structure_get_uint64 (source_stats, "octets-received", &bytes))
gst_structure_set (in, "bytes-received", G_TYPE_UINT64, bytes, NULL);
if (gst_structure_get_int (source_stats, "packets-lost", &lost))
gst_structure_set (in, "packets-lost", G_TYPE_INT, lost, NULL);
if (gst_structure_get_uint (source_stats, "jitter", &jitter))
gst_structure_set (in, "jitter", G_TYPE_DOUBLE,
CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
/*
RTCReceivedRTPStreamStats
double fractionLost;
unsigned long packetsDiscarded;
unsigned long packetsFailedDecryption;
unsigned long packetsRepaired;
unsigned long burstPacketsLost;
unsigned long burstPacketsDiscarded;
unsigned long burstLossCount;
unsigned long burstDiscardCount;
double burstLossRate;
double burstDiscardRate;
double gapLossRate;
double gapDiscardRate;
*/
/* RTCInboundRTPStreamStats */
gst_structure_set (in, "remote-id", G_TYPE_STRING, r_out_id, NULL);
/* XXX: framesDecoded, lastPacketReceivedTimestamp */
r_in = gst_structure_new_empty (r_in_id);
_set_base_stats (r_in, GST_WEBRTC_STATS_REMOTE_INBOUND_RTP, ts, r_in_id);
/* RTCStreamStats */
gst_structure_set (r_in, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (r_in, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (r_in, "transport-id", G_TYPE_STRING, transport_id, NULL);
/* XXX: mediaType, trackId, sliCount, qpSum */
/* RTCReceivedRTPStreamStats */
if (sent_rb) {
if (gst_structure_get_uint (source_stats, "sent-rb-jitter", &jitter))
gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE,
CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
if (gst_structure_get_int (source_stats, "sent-rb-packetslost", &lost))
gst_structure_set (r_in, "packets-lost", G_TYPE_INT, lost, NULL);
/* packetsReceived, bytesReceived */
} else {
/* default values */
gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE, 0.0, "packets-lost",
G_TYPE_INT, 0, NULL);
}
/* XXX: RTCReceivedRTPStreamStats
double fractionLost;
unsigned long packetsDiscarded;
unsigned long packetsFailedDecryption;
unsigned long packetsRepaired;
unsigned long burstPacketsLost;
unsigned long burstPacketsDiscarded;
unsigned long burstLossCount;
unsigned long burstDiscardCount;
double burstLossRate;
double burstDiscardRate;
double gapLossRate;
double gapDiscardRate;
*/
/* RTCRemoteInboundRTPStreamStats */
gst_structure_set (r_in, "local-id", G_TYPE_STRING, out_id, NULL);
if (have_rb) {
guint32 rtt;
if (gst_structure_get_uint (source_stats, "rb-round-trip", &rtt)) {
/* 16.16 fixed point to double */
double val =
(double) ((rtt & 0xffff0000) >> 16) + ((rtt & 0xffff) / 65536.0);
gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, val, NULL);
}
} else {
/* default values */
gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, 0.0, NULL);
}
/* XXX: framesDecoded, lastPacketReceivedTimestamp */
out = gst_structure_new_empty (out_id);
_set_base_stats (out, GST_WEBRTC_STATS_OUTBOUND_RTP, ts, out_id);
/* RTCStreamStats */
gst_structure_set (out, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (out, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (out, "transport-id", G_TYPE_STRING, transport_id, NULL);
if (gst_structure_get_uint (source_stats, "sent-fir-count", &fir))
gst_structure_set (out, "fir-count", G_TYPE_UINT, fir, NULL);
if (gst_structure_get_uint (source_stats, "sent-pli-count", &pli))
gst_structure_set (out, "pli-count", G_TYPE_UINT, pli, NULL);
if (gst_structure_get_uint (source_stats, "sent-nack-count", &nack))
gst_structure_set (out, "nack-count", G_TYPE_UINT, nack, NULL);
/* XXX: mediaType, trackId, sliCount, qpSum */
/* RTCSentRTPStreamStats */
if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes))
gst_structure_set (out, "bytes-sent", G_TYPE_UINT64, bytes, NULL);
if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets))
gst_structure_set (out, "packets-sent", G_TYPE_UINT64, packets, NULL);
/* XXX:
unsigned long packetsDiscardedOnSend;
unsigned long long bytesDiscardedOnSend;
*/
/* RTCOutboundRTPStreamStats */
gst_structure_set (out, "remote-id", G_TYPE_STRING, r_in_id, NULL);
/* XXX:
DOMHighResTimeStamp lastPacketSentTimestamp;
double targetBitrate;
unsigned long framesEncoded;
double totalEncodeTime;
double averageRTCPInterval;
*/
r_out = gst_structure_new_empty (r_out_id);
_set_base_stats (r_out, GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP, ts, r_out_id);
/* RTCStreamStats */
gst_structure_set (r_out, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (r_out, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (r_out, "transport-id", G_TYPE_STRING, transport_id, NULL);
/* XXX: mediaType, trackId, sliCount, qpSum */
/* RTCSentRTPStreamStats */
/* if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes))
gst_structure_set (r_out, "bytes-sent", G_TYPE_UINT64, bytes, NULL);
if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets))
gst_structure_set (r_out, "packets-sent", G_TYPE_UINT64, packets, NULL);*/
/* XXX:
unsigned long packetsDiscardedOnSend;
unsigned long long bytesDiscardedOnSend;
*/
gst_structure_set (r_out, "local-id", G_TYPE_STRING, in_id, NULL);
gst_structure_set (s, in_id, GST_TYPE_STRUCTURE, in, NULL);
gst_structure_set (s, out_id, GST_TYPE_STRUCTURE, out, NULL);
gst_structure_set (s, r_in_id, GST_TYPE_STRUCTURE, r_in, NULL);
gst_structure_set (s, r_out_id, GST_TYPE_STRUCTURE, r_out, NULL);
gst_structure_free (in);
gst_structure_free (out);
gst_structure_free (r_in);
gst_structure_free (r_out);
g_free (in_id);
g_free (out_id);
g_free (r_in_id);
g_free (r_out_id);
}
/* https://www.w3.org/TR/webrtc-stats/#candidatepair-dict* */
static gchar *
_get_stats_from_ice_transport (GstWebRTCBin * webrtc,
GstWebRTCICETransport * transport, GstStructure * s)
{
GstStructure *stats;
gchar *id;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
id = g_strdup_printf ("ice-candidate-pair_%s", GST_OBJECT_NAME (transport));
stats = gst_structure_new_empty (id);
_set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
/* XXX: RTCIceCandidatePairStats
DOMString transportId;
DOMString localCandidateId;
DOMString remoteCandidateId;
RTCStatsIceCandidatePairState state;
unsigned long long priority;
boolean nominated;
unsigned long packetsSent;
unsigned long packetsReceived;
unsigned long long bytesSent;
unsigned long long bytesReceived;
DOMHighResTimeStamp lastPacketSentTimestamp;
DOMHighResTimeStamp lastPacketReceivedTimestamp;
DOMHighResTimeStamp firstRequestTimestamp;
DOMHighResTimeStamp lastRequestTimestamp;
DOMHighResTimeStamp lastResponseTimestamp;
double totalRoundTripTime;
double currentRoundTripTime;
double availableOutgoingBitrate;
double availableIncomingBitrate;
unsigned long circuitBreakerTriggerCount;
unsigned long long requestsReceived;
unsigned long long requestsSent;
unsigned long long responsesReceived;
unsigned long long responsesSent;
unsigned long long retransmissionsReceived;
unsigned long long retransmissionsSent;
unsigned long long consentRequestsSent;
DOMHighResTimeStamp consentExpiredTimestamp;
*/
/* XXX: RTCIceCandidateStats
DOMString transportId;
boolean isRemote;
RTCNetworkType networkType;
DOMString ip;
long port;
DOMString protocol;
RTCIceCandidateType candidateType;
long priority;
DOMString url;
DOMString relayProtocol;
boolean deleted = false;
};
*/
gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
gst_structure_free (stats);
return id;
}
/* https://www.w3.org/TR/webrtc-stats/#dom-rtctransportstats */
static gchar *
_get_stats_from_dtls_transport (GstWebRTCBin * webrtc,
GstWebRTCDTLSTransport * transport, GstStructure * s)
{
GstStructure *stats;
gchar *id;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
id = g_strdup_printf ("transport-stats_%s", GST_OBJECT_NAME (transport));
stats = gst_structure_new_empty (id);
_set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
/* XXX: RTCTransportStats
unsigned long packetsSent;
unsigned long packetsReceived;
unsigned long long bytesSent;
unsigned long long bytesReceived;
DOMString rtcpTransportStatsId;
RTCIceRole iceRole;
RTCDtlsTransportState dtlsState;
DOMString selectedCandidatePairId;
DOMString localCertificateId;
DOMString remoteCertificateId;
*/
/* XXX: RTCCertificateStats
DOMString fingerprint;
DOMString fingerprintAlgorithm;
DOMString base64Certificate;
DOMString issuerCertificateId;
*/
/* XXX: RTCIceCandidateStats
DOMString transportId;
boolean isRemote;
DOMString ip;
long port;
DOMString protocol;
RTCIceCandidateType candidateType;
long priority;
DOMString url;
boolean deleted = false;
*/
gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
gst_structure_free (stats);
_get_stats_from_ice_transport (webrtc, transport->transport, s);
return id;
}
static void
_get_stats_from_transport_channel (GstWebRTCBin * webrtc,
TransportStream * stream, const gchar * codec_id, GstStructure * s)
{
GstWebRTCDTLSTransport *transport;
GObject *rtp_session;
GstStructure *rtp_stats;
GValueArray *source_stats;
gchar *transport_id;
double ts;
int i;
gst_structure_get_double (s, "timestamp", &ts);
transport = stream->transport;
if (!transport)
transport = stream->transport;
if (!transport)
return;
g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
stream->session_id, &rtp_session);
g_object_get (rtp_session, "stats", &rtp_stats, NULL);
gst_structure_get (rtp_stats, "source-stats", G_TYPE_VALUE_ARRAY,
&source_stats, NULL);
GST_DEBUG_OBJECT (webrtc, "retrieving rtp stream stats from transport %"
GST_PTR_FORMAT " rtp session %" GST_PTR_FORMAT " with %u rtp sources, "
"transport %" GST_PTR_FORMAT, stream, rtp_session, source_stats->n_values,
transport);
transport_id = _get_stats_from_dtls_transport (webrtc, transport, s);
/* construct stats objects */
for (i = 0; i < source_stats->n_values; i++) {
const GstStructure *stats;
const GValue *val = g_value_array_get_nth (source_stats, i);
gboolean internal;
stats = gst_value_get_structure (val);
/* skip internal sources */
gst_structure_get (stats, "internal", G_TYPE_BOOLEAN, &internal, NULL);
if (internal)
continue;
_get_stats_from_rtp_source_stats (webrtc, stats, codec_id, transport_id, s);
}
g_object_unref (rtp_session);
gst_structure_free (rtp_stats);
g_value_array_free (source_stats);
g_free (transport_id);
}
/* https://www.w3.org/TR/webrtc-stats/#codec-dict* */
static gchar *
_get_codec_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad,
GstStructure * s)
{
GstStructure *stats;
GstCaps *caps;
gchar *id;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
stats = gst_structure_new_empty ("unused");
id = g_strdup_printf ("codec-stats-%s", GST_OBJECT_NAME (pad));
_set_base_stats (stats, GST_WEBRTC_STATS_CODEC, ts, id);
caps = gst_pad_get_current_caps (pad);
if (caps && gst_caps_is_fixed (caps)) {
GstStructure *caps_s = gst_caps_get_structure (caps, 0);
gint pt, clock_rate;
if (gst_structure_get_int (caps_s, "payload", &pt))
gst_structure_set (stats, "payload-type", G_TYPE_UINT, pt, NULL);
if (gst_structure_get_int (caps_s, "clock-rate", &clock_rate))
gst_structure_set (stats, "clock-rate", G_TYPE_UINT, clock_rate, NULL);
/* FIXME: codecType, mimeType, channels, sdpFmtpLine, implementation, transportId */
}
if (caps)
gst_caps_unref (caps);
gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
gst_structure_free (stats);
return id;
}
static gboolean
_get_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad, GstStructure * s)
{
GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
gchar *codec_id;
codec_id = _get_codec_stats_from_pad (webrtc, pad, s);
if (wpad->trans) {
WebRTCTransceiver *trans;
trans = WEBRTC_TRANSCEIVER (wpad->trans);
if (trans->stream)
_get_stats_from_transport_channel (webrtc, trans->stream, codec_id, s);
}
g_free (codec_id);
return TRUE;
}
void
gst_webrtc_bin_update_stats (GstWebRTCBin * webrtc)
{
GstStructure *s = gst_structure_new_empty ("application/x-webrtc-stats");
double ts = monotonic_time_as_double_milliseconds ();
GstStructure *pc_stats;
_init_debug ();
gst_structure_set (s, "timestamp", G_TYPE_DOUBLE, ts, NULL);
/* FIXME: better unique IDs */
/* FIXME: rate limitting stat updates? */
/* FIXME: all stats need to be kept forever */
GST_DEBUG_OBJECT (webrtc, "updating stats at time %f", ts);
if ((pc_stats = _get_peer_connection_stats (webrtc))) {
const gchar *id = "peer-connection-stats";
_set_base_stats (pc_stats, GST_WEBRTC_STATS_PEER_CONNECTION, ts, id);
gst_structure_set (s, id, GST_TYPE_STRUCTURE, pc_stats, NULL);
gst_structure_free (pc_stats);
}
gst_element_foreach_pad (GST_ELEMENT (webrtc),
(GstElementForeachPadFunc) _get_stats_from_pad, s);
gst_structure_remove_field (s, "timestamp");
if (webrtc->priv->stats)
gst_structure_free (webrtc->priv->stats);
webrtc->priv->stats = s;
}

View file

@ -0,0 +1,35 @@
/* 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 __GST_WEBRTC_STATS_H__
#define __GST_WEBRTC_STATS_H__
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc.h>
#include "fwd.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
void gst_webrtc_bin_update_stats (GstWebRTCBin * webrtc);
G_END_DECLS
#endif /* __GST_WEBRTC_STATS_H__ */

239
ext/webrtc/icestream.c Normal file
View file

@ -0,0 +1,239 @@
/* 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 "icestream.h"
#include "nicetransport.h"
#define GST_CAT_DEFAULT gst_webrtc_ice_stream_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_ice_stream_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCICEStream, gst_webrtc_ice_stream,
GST_TYPE_OBJECT,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_ice_stream_debug,
"webrtcicestream", 0, "webrtcicestream"););
enum
{
SIGNAL_0,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_ICE,
PROP_STREAM_ID,
};
//static guint gst_webrtc_ice_stream_signals[LAST_SIGNAL] = { 0 };
struct _GstWebRTCICEStreamPrivate
{
gboolean gathered;
GList *transports;
};
static void
gst_webrtc_ice_stream_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCICEStream *stream = GST_WEBRTC_ICE_STREAM (object);
switch (prop_id) {
case PROP_ICE:
/* XXX: weak-ref this? */
stream->ice = g_value_get_object (value);
break;
case PROP_STREAM_ID:
stream->stream_id = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_ice_stream_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCICEStream *stream = GST_WEBRTC_ICE_STREAM (object);
switch (prop_id) {
case PROP_ICE:
g_value_set_object (value, stream->ice);
break;
case PROP_STREAM_ID:
g_value_set_uint (value, stream->stream_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_ice_stream_finalize (GObject * object)
{
GstWebRTCICEStream *stream = GST_WEBRTC_ICE_STREAM (object);
g_list_free (stream->priv->transports);
stream->priv->transports = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
_on_candidate_gathering_done (NiceAgent * agent, guint stream_id,
GstWebRTCICEStream * ice)
{
GList *l;
if (stream_id != ice->stream_id)
return;
GST_DEBUG_OBJECT (ice, "%u gathering done", stream_id);
ice->priv->gathered = TRUE;
for (l = ice->priv->transports; l; l = l->next) {
GstWebRTCICETransport *ice = l->data;
gst_webrtc_ice_transport_gathering_state_change (ice,
GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE);
}
}
GstWebRTCICETransport *
gst_webrtc_ice_stream_find_transport (GstWebRTCICEStream * stream,
GstWebRTCICEComponent component)
{
GstWebRTCICEComponent trans_comp;
GstWebRTCICETransport *ret;
GList *l;
g_return_val_if_fail (GST_IS_WEBRTC_ICE_STREAM (stream), NULL);
for (l = stream->priv->transports; l; l = l->next) {
GstWebRTCICETransport *trans = l->data;
g_object_get (trans, "component", &trans_comp, NULL);
if (component == trans_comp)
return gst_object_ref (trans);
}
ret =
GST_WEBRTC_ICE_TRANSPORT (gst_webrtc_nice_transport_new (stream,
component));
stream->priv->transports = g_list_prepend (stream->priv->transports, ret);
return ret;
}
static void
gst_webrtc_ice_stream_constructed (GObject * object)
{
GstWebRTCICEStream *stream = GST_WEBRTC_ICE_STREAM (object);
NiceAgent *agent;
g_object_get (stream->ice, "agent", &agent, NULL);
g_signal_connect (agent, "candidate-gathering-done",
G_CALLBACK (_on_candidate_gathering_done), stream);
g_object_unref (agent);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
gboolean
gst_webrtc_ice_stream_gather_candidates (GstWebRTCICEStream * stream)
{
NiceAgent *agent;
GList *l;
g_return_val_if_fail (GST_IS_WEBRTC_ICE_STREAM (stream), FALSE);
GST_DEBUG_OBJECT (stream, "start gathering candidates");
if (stream->priv->gathered)
return TRUE;
for (l = stream->priv->transports; l; l = l->next) {
GstWebRTCICETransport *trans = l->data;
gst_webrtc_ice_transport_gathering_state_change (trans,
GST_WEBRTC_ICE_GATHERING_STATE_GATHERING);
}
g_object_get (stream->ice, "agent", &agent, NULL);
if (!nice_agent_gather_candidates (agent, stream->stream_id)) {
g_object_unref (agent);
return FALSE;
}
g_object_unref (agent);
return TRUE;
}
static void
gst_webrtc_ice_stream_class_init (GstWebRTCICEStreamClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
g_type_class_add_private (klass, sizeof (GstWebRTCICEStreamPrivate));
gobject_class->constructed = gst_webrtc_ice_stream_constructed;
gobject_class->get_property = gst_webrtc_ice_stream_get_property;
gobject_class->set_property = gst_webrtc_ice_stream_set_property;
gobject_class->finalize = gst_webrtc_ice_stream_finalize;
g_object_class_install_property (gobject_class,
PROP_ICE,
g_param_spec_object ("ice",
"ICE", "ICE agent associated with this stream",
GST_TYPE_WEBRTC_ICE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_STREAM_ID,
g_param_spec_uint ("stream-id",
"ICE stream id", "ICE stream id associated with this stream",
0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_ice_stream_init (GstWebRTCICEStream * ice)
{
ice->priv =
G_TYPE_INSTANCE_GET_PRIVATE ((ice), GST_TYPE_WEBRTC_ICE_STREAM,
GstWebRTCICEStreamPrivate);
}
GstWebRTCICEStream *
gst_webrtc_ice_stream_new (GstWebRTCICE * ice, guint stream_id)
{
return g_object_new (GST_TYPE_WEBRTC_ICE_STREAM, "ice", ice,
"stream-id", stream_id, NULL);
}

63
ext/webrtc/icestream.h Normal file
View file

@ -0,0 +1,63 @@
/* 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 __GST_WEBRTC_ICE_STREAM_H__
#define __GST_WEBRTC_ICE_STREAM_H__
#include <gst/gst.h>
/* libice */
#include <agent.h>
#include <gst/webrtc/webrtc.h>
#include "gstwebrtcice.h"
G_BEGIN_DECLS
GType gst_webrtc_ice_stream_get_type(void);
#define GST_TYPE_WEBRTC_ICE_STREAM (gst_webrtc_ice_stream_get_type())
#define GST_WEBRTC_ICE_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_ICE_STREAM,GstWebRTCICEStream))
#define GST_IS_WEBRTC_ICE_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_ICE_STREAM))
#define GST_WEBRTC_ICE_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_ICE_STREAM,GstWebRTCICEStreamClass))
#define GST_IS_WEBRTC_ICE_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_ICE_STREAM))
#define GST_WEBRTC_ICE_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_ICE_STREAM,GstWebRTCICEStreamClass))
struct _GstWebRTCICEStream
{
GstObject parent;
GstWebRTCICE *ice;
guint stream_id;
GstWebRTCICEStreamPrivate *priv;
};
struct _GstWebRTCICEStreamClass
{
GstObjectClass parent_class;
};
GstWebRTCICEStream * gst_webrtc_ice_stream_new (GstWebRTCICE * ice,
guint stream_id);
GstWebRTCICETransport * gst_webrtc_ice_stream_find_transport (GstWebRTCICEStream * stream,
GstWebRTCICEComponent component);
gboolean gst_webrtc_ice_stream_gather_candidates (GstWebRTCICEStream * ice);
G_END_DECLS
#endif /* __GST_WEBRTC_ICE_STREAM_H__ */

27
ext/webrtc/meson.build Normal file
View file

@ -0,0 +1,27 @@
webrtc_sources = [
'gstwebrtc.c',
'gstwebrtcice.c',
'gstwebrtcstats.c',
'icestream.c',
'nicetransport.c',
'gstwebrtcbin.c',
'transportreceivebin.c',
'transportsendbin.c',
'transportstream.c',
'utils.c',
'webrtcsdp.c',
'webrtctransceiver.c',
]
libnice_dep = dependency('nice', version : '>=0.1.14', required : false)
if libnice_dep.found()
library('gstwebrtc',
webrtc_sources,
c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
include_directories : [configinc],
dependencies : [libnice_dep, gstbase_dep, gstsdp_dep, gstwebrtc_dep],
install : true,
install_dir : plugins_install_dir,
)
endif

268
ext/webrtc/nicetransport.c Normal file
View file

@ -0,0 +1,268 @@
/* 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 "nicetransport.h"
#include "icestream.h"
#define GST_CAT_DEFAULT gst_webrtc_nice_transport_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_nice_transport_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCNiceTransport, gst_webrtc_nice_transport,
GST_TYPE_WEBRTC_ICE_TRANSPORT,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_nice_transport_debug,
"webrtcnicetransport", 0, "webrtcnicetransport");
);
enum
{
SIGNAL_0,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_STREAM,
};
//static guint gst_webrtc_nice_transport_signals[LAST_SIGNAL] = { 0 };
struct _GstWebRTCNiceTransportPrivate
{
gboolean running;
};
static NiceComponentType
_gst_component_to_nice (GstWebRTCICEComponent component)
{
switch (component) {
case GST_WEBRTC_ICE_COMPONENT_RTP:
return NICE_COMPONENT_TYPE_RTP;
case GST_WEBRTC_ICE_COMPONENT_RTCP:
return NICE_COMPONENT_TYPE_RTCP;
default:
g_assert_not_reached ();
return 0;
}
}
static GstWebRTCICEComponent
_nice_component_to_gst (NiceComponentType component)
{
switch (component) {
case NICE_COMPONENT_TYPE_RTP:
return GST_WEBRTC_ICE_COMPONENT_RTP;
case NICE_COMPONENT_TYPE_RTCP:
return GST_WEBRTC_ICE_COMPONENT_RTCP;
default:
g_assert_not_reached ();
return 0;
}
}
static GstWebRTCICEConnectionState
_nice_component_state_to_gst (NiceComponentState state)
{
switch (state) {
case NICE_COMPONENT_STATE_DISCONNECTED:
return GST_WEBRTC_ICE_CONNECTION_STATE_DISCONNECTED;
case NICE_COMPONENT_STATE_GATHERING:
return GST_WEBRTC_ICE_CONNECTION_STATE_NEW;
case NICE_COMPONENT_STATE_CONNECTING:
return GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING;
case NICE_COMPONENT_STATE_CONNECTED:
return GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED;
case NICE_COMPONENT_STATE_READY:
return GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED;
case NICE_COMPONENT_STATE_FAILED:
return GST_WEBRTC_ICE_CONNECTION_STATE_FAILED;
default:
g_assert_not_reached ();
return 0;
}
}
static void
gst_webrtc_nice_transport_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCNiceTransport *nice = GST_WEBRTC_NICE_TRANSPORT (object);
switch (prop_id) {
case PROP_STREAM:
if (nice->stream)
gst_object_unref (nice->stream);
nice->stream = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_nice_transport_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCNiceTransport *nice = GST_WEBRTC_NICE_TRANSPORT (object);
switch (prop_id) {
case PROP_STREAM:
g_value_set_object (value, nice->stream);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_nice_transport_finalize (GObject * object)
{
GstWebRTCNiceTransport *nice = GST_WEBRTC_NICE_TRANSPORT (object);
gst_object_unref (nice->stream);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
_on_new_selected_pair (NiceAgent * agent, guint stream_id,
NiceComponentType component, NiceCandidate * lcandidate,
NiceCandidate * rcandidate, GstWebRTCNiceTransport * nice)
{
GstWebRTCICETransport *ice = GST_WEBRTC_ICE_TRANSPORT (nice);
GstWebRTCICEComponent comp = _nice_component_to_gst (component);
guint our_stream_id;
g_object_get (nice->stream, "stream-id", &our_stream_id, NULL);
if (stream_id != our_stream_id)
return;
if (comp != ice->component)
return;
gst_webrtc_ice_transport_selected_pair_change (ice);
}
static void
_on_component_state_changed (NiceAgent * agent, guint stream_id,
NiceComponentType component, NiceComponentState state,
GstWebRTCNiceTransport * nice)
{
GstWebRTCICETransport *ice = GST_WEBRTC_ICE_TRANSPORT (nice);
GstWebRTCICEComponent comp = _nice_component_to_gst (component);
guint our_stream_id;
g_object_get (nice->stream, "stream-id", &our_stream_id, NULL);
if (stream_id != our_stream_id)
return;
if (comp != ice->component)
return;
GST_DEBUG_OBJECT (ice, "%u %u %s", stream_id, component,
nice_component_state_to_string (state));
gst_webrtc_ice_transport_connection_state_change (ice,
_nice_component_state_to_gst (state));
}
static void
gst_webrtc_nice_transport_constructed (GObject * object)
{
GstWebRTCNiceTransport *nice = GST_WEBRTC_NICE_TRANSPORT (object);
GstWebRTCICETransport *ice = GST_WEBRTC_ICE_TRANSPORT (object);
NiceComponentType component = _gst_component_to_nice (ice->component);
gboolean controlling_mode;
guint our_stream_id;
NiceAgent *agent;
g_object_get (nice->stream, "stream-id", &our_stream_id, NULL);
g_object_get (nice->stream->ice, "agent", &agent, NULL);
g_object_get (agent, "controlling-mode", &controlling_mode, NULL);
ice->role =
controlling_mode ? GST_WEBRTC_ICE_ROLE_CONTROLLING :
GST_WEBRTC_ICE_ROLE_CONTROLLED;
g_signal_connect (agent, "component-state-changed",
G_CALLBACK (_on_component_state_changed), nice);
g_signal_connect (agent, "new-selected-pair-full",
G_CALLBACK (_on_new_selected_pair), nice);
ice->src = gst_element_factory_make ("nicesrc", NULL);
if (ice->src) {
g_object_set (ice->src, "agent", agent, "stream", our_stream_id,
"component", component, NULL);
}
ice->sink = gst_element_factory_make ("nicesink", NULL);
if (ice->sink) {
g_object_set (ice->sink, "agent", agent, "stream", our_stream_id,
"component", component, "async", FALSE, "enable-last-sample", FALSE,
NULL);
if (ice->component == GST_WEBRTC_ICE_COMPONENT_RTCP)
g_object_set (ice->sink, "sync", FALSE, NULL);
}
g_object_unref (agent);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
gst_webrtc_nice_transport_class_init (GstWebRTCNiceTransportClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
g_type_class_add_private (klass, sizeof (GstWebRTCNiceTransportPrivate));
gobject_class->constructed = gst_webrtc_nice_transport_constructed;
gobject_class->get_property = gst_webrtc_nice_transport_get_property;
gobject_class->set_property = gst_webrtc_nice_transport_set_property;
gobject_class->finalize = gst_webrtc_nice_transport_finalize;
g_object_class_install_property (gobject_class,
PROP_STREAM,
g_param_spec_object ("stream",
"WebRTC ICE Stream", "ICE stream associated with this transport",
GST_TYPE_WEBRTC_ICE_STREAM,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_nice_transport_init (GstWebRTCNiceTransport * nice)
{
nice->priv =
G_TYPE_INSTANCE_GET_PRIVATE ((nice), GST_TYPE_WEBRTC_NICE_TRANSPORT,
GstWebRTCNiceTransportPrivate);
}
GstWebRTCNiceTransport *
gst_webrtc_nice_transport_new (GstWebRTCICEStream * stream,
GstWebRTCICEComponent component)
{
return g_object_new (GST_TYPE_WEBRTC_NICE_TRANSPORT, "stream", stream,
"component", component, NULL);
}

View file

@ -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 __GST_WEBRTC_NICE_TRANSPORT_H__
#define __GST_WEBRTC_NICE_TRANSPORT_H__
#include <gst/gst.h>
/* libnice */
#include <agent.h>
#include <gst/webrtc/webrtc.h>
#include "gstwebrtcice.h"
G_BEGIN_DECLS
GType gst_webrtc_nice_transport_get_type(void);
#define GST_TYPE_WEBRTC_NICE_TRANSPORT (gst_webrtc_nice_transport_get_type())
#define GST_WEBRTC_NICE_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_NICE_TRANSPORT,GstWebRTCNiceTransport))
#define GST_IS_WEBRTC_NICE_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_NICE_TRANSPORT))
#define GST_WEBRTC_NICE_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_NICE_TRANSPORT,GstWebRTCNiceTransportClass))
#define GST_IS_WEBRTC_NICE_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_NICE_TRANSPORT))
#define GST_WEBRTC_NICE_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_NICE_TRANSPORT,GstWebRTCNiceTransportClass))
struct _GstWebRTCNiceTransport
{
GstWebRTCICETransport parent;
GstWebRTCICEStream *stream;
GstWebRTCNiceTransportPrivate *priv;
};
struct _GstWebRTCNiceTransportClass
{
GstWebRTCICETransportClass parent_class;
};
GstWebRTCNiceTransport * gst_webrtc_nice_transport_new (GstWebRTCICEStream * stream,
GstWebRTCICEComponent component);
G_END_DECLS
#endif /* __GST_WEBRTC_NICE_TRANSPORT_H__ */

View file

@ -0,0 +1,376 @@
/* 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 "transportreceivebin.h"
#include "utils.h"
/*
* ,----------------------------transport_receive_%u-----------------------------,
* ; (rtp) ;
* ; ,---nicesrc----, ,-capsfilter-, ,----dtlssrtpdec----, ,--funnel--, ;
* ; ; src o--o sink src o--o sink rtp_src o------o sink_0 ; ;
* ; '--------------' '------------' ; ; ; src o--o rtp_src
* ; ; rtcp_src o-, ,--o sink_1 ; ;
* ; '-------------------' ; ; '----------' ;
* ; ; ; ,--funnel--, ;
* ; '-+--o sink_0 ; ;
* ; ,-' ; src o--o rtcp_src
* ; (rtcp) ; ,-o sink_1 ; ;
* ; ,---nicesrc----, ,-capsfilter-, ,----dtlssrtpdec----, ; ; '----------' ;
* ; ; src o--o sink src o--o sink rtp_src o-' ; ;
* ; '--------------' '------------' ; ; ; ;
* ; ; rtcp_src o----' ;
* ; '-------------------' ;
* '-----------------------------------------------------------------------------'
*
* Do we really wnat to be *that* permissive in what we accept?
*
* FIXME: When and how do we want to clear the possibly stored buffers?
*/
#define GST_CAT_DEFAULT gst_webrtc_transport_receive_bin_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define transport_receive_bin_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (TransportReceiveBin, transport_receive_bin,
GST_TYPE_BIN,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_transport_receive_bin_debug,
"webrtctransportreceivebin", 0, "webrtctransportreceivebin");
);
static GstStaticPadTemplate rtp_sink_template =
GST_STATIC_PAD_TEMPLATE ("rtp_src",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp"));
static GstStaticPadTemplate rtcp_sink_template =
GST_STATIC_PAD_TEMPLATE ("rtcp_src",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp"));
enum
{
PROP_0,
PROP_STREAM,
};
static const gchar *
_receive_state_to_string (ReceiveState state)
{
switch (state) {
case RECEIVE_STATE_BLOCK:
return "block";
case RECEIVE_STATE_DROP:
return "drop";
case RECEIVE_STATE_PASS:
return "pass";
default:
return "Unknown";
}
}
static GstPadProbeReturn
pad_block (GstPad * pad, GstPadProbeInfo * info, TransportReceiveBin * receive)
{
GstPadProbeReturn ret;
g_mutex_lock (&receive->pad_block_lock);
while (receive->receive_state == RECEIVE_STATE_BLOCK) {
g_cond_wait (&receive->pad_block_cond, &receive->pad_block_lock);
GST_DEBUG_OBJECT (pad, "probe waited. new state %s",
_receive_state_to_string (receive->receive_state));
}
ret = GST_PAD_PROBE_PASS;
if (receive->receive_state == RECEIVE_STATE_DROP) {
ret = GST_PAD_PROBE_DROP;
} else if (receive->receive_state == RECEIVE_STATE_PASS) {
ret = GST_PAD_PROBE_OK;
}
g_mutex_unlock (&receive->pad_block_lock);
return ret;
}
void
transport_receive_bin_set_receive_state (TransportReceiveBin * receive,
ReceiveState state)
{
g_mutex_lock (&receive->pad_block_lock);
receive->receive_state = state;
GST_DEBUG_OBJECT (receive, "changing receive state to %s",
_receive_state_to_string (state));
g_cond_signal (&receive->pad_block_cond);
g_mutex_unlock (&receive->pad_block_lock);
}
static void
transport_receive_bin_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
TransportReceiveBin *receive = TRANSPORT_RECEIVE_BIN (object);
GST_OBJECT_LOCK (receive);
switch (prop_id) {
case PROP_STREAM:
/* XXX: weak-ref this? */
receive->stream = TRANSPORT_STREAM (g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (receive);
}
static void
transport_receive_bin_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
TransportReceiveBin *receive = TRANSPORT_RECEIVE_BIN (object);
GST_OBJECT_LOCK (receive);
switch (prop_id) {
case PROP_STREAM:
g_value_set_object (value, receive->stream);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (receive);
}
static void
transport_receive_bin_finalize (GObject * object)
{
TransportReceiveBin *receive = TRANSPORT_RECEIVE_BIN (object);
g_mutex_clear (&receive->pad_block_lock);
g_cond_clear (&receive->pad_block_cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstStateChangeReturn
transport_receive_bin_change_state (GstElement * element,
GstStateChange transition)
{
TransportReceiveBin *receive = TRANSPORT_RECEIVE_BIN (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GST_DEBUG ("changing state: %s => %s",
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:{
GstElement *elem;
receive->rtp_block =
_create_pad_block (GST_ELEMENT (receive), receive->rtp_src, 0, NULL,
NULL);
receive->rtp_block->block_id =
gst_pad_add_probe (receive->rtp_src, GST_PAD_PROBE_TYPE_ALL_BOTH,
(GstPadProbeCallback) pad_block, receive, NULL);
/* XXX: because nice needs the nicesrc internal main loop running in order
* correctly STUN... */
/* FIXME: this races with the pad exposure later and may get not-linked */
elem = receive->stream->transport->transport->src;
gst_element_set_locked_state (elem, TRUE);
gst_element_set_state (elem, GST_STATE_PLAYING);
elem = receive->stream->rtcp_transport->transport->src;
gst_element_set_locked_state (elem, TRUE);
gst_element_set_state (elem, GST_STATE_PLAYING);
break;
}
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:{
GstElement *elem;
elem = receive->stream->transport->transport->src;
gst_element_set_locked_state (elem, FALSE);
gst_element_set_state (elem, GST_STATE_NULL);
elem = receive->stream->rtcp_transport->transport->src;
gst_element_set_locked_state (elem, FALSE);
gst_element_set_state (elem, GST_STATE_NULL);
if (receive->rtp_block)
_free_pad_block (receive->rtp_block);
receive->rtp_block = NULL;
break;
}
default:
break;
}
return ret;
}
static void
rtp_queue_overrun (GstElement * queue, TransportReceiveBin * receive)
{
GST_WARNING_OBJECT (receive, "Internal receive queue overrun. Dropping data");
}
static void
transport_receive_bin_constructed (GObject * object)
{
TransportReceiveBin *receive = TRANSPORT_RECEIVE_BIN (object);
GstWebRTCDTLSTransport *transport;
GstPad *ghost, *pad;
GstElement *capsfilter, *funnel, *queue;
GstCaps *caps;
g_return_if_fail (receive->stream);
/* link ice src, dtlsrtp together for rtp */
transport = receive->stream->transport;
gst_bin_add (GST_BIN (receive), GST_ELEMENT (transport->dtlssrtpdec));
capsfilter = gst_element_factory_make ("capsfilter", NULL);
caps = gst_caps_new_empty_simple ("application/x-rtp");
g_object_set (capsfilter, "caps", caps, NULL);
gst_caps_unref (caps);
gst_bin_add (GST_BIN (receive), GST_ELEMENT (capsfilter));
if (!gst_element_link_pads (capsfilter, "src", transport->dtlssrtpdec,
"sink"))
g_warn_if_reached ();
gst_bin_add (GST_BIN (receive), GST_ELEMENT (transport->transport->src));
if (!gst_element_link_pads (GST_ELEMENT (transport->transport->src), "src",
GST_ELEMENT (capsfilter), "sink"))
g_warn_if_reached ();
/* link ice src, dtlsrtp together for rtcp */
transport = receive->stream->rtcp_transport;
gst_bin_add (GST_BIN (receive), GST_ELEMENT (transport->dtlssrtpdec));
capsfilter = gst_element_factory_make ("capsfilter", NULL);
caps = gst_caps_new_empty_simple ("application/x-rtcp");
g_object_set (capsfilter, "caps", caps, NULL);
gst_caps_unref (caps);
gst_bin_add (GST_BIN (receive), GST_ELEMENT (capsfilter));
if (!gst_element_link_pads (capsfilter, "src", transport->dtlssrtpdec,
"sink"))
g_warn_if_reached ();
gst_bin_add (GST_BIN (receive), GST_ELEMENT (transport->transport->src));
if (!gst_element_link_pads (GST_ELEMENT (transport->transport->src), "src",
GST_ELEMENT (capsfilter), "sink"))
g_warn_if_reached ();
/* create funnel for rtp_src */
funnel = gst_element_factory_make ("funnel", NULL);
gst_bin_add (GST_BIN (receive), funnel);
if (!gst_element_link_pads (receive->stream->transport->dtlssrtpdec,
"rtp_src", funnel, "sink_0"))
g_warn_if_reached ();
if (!gst_element_link_pads (receive->stream->rtcp_transport->dtlssrtpdec,
"rtp_src", funnel, "sink_1"))
g_warn_if_reached ();
queue = gst_element_factory_make ("queue", NULL);
/* FIXME: make this configurable? */
g_object_set (queue, "leaky", 2, "max-size-time", (guint64) 0,
"max-size-buffers", 0, "max-size-bytes", 5 * 1024 * 1024, NULL);
g_signal_connect (queue, "overrun", G_CALLBACK (rtp_queue_overrun), receive);
gst_bin_add (GST_BIN (receive), queue);
if (!gst_element_link_pads (funnel, "src", queue, "sink"))
g_warn_if_reached ();
pad = gst_element_get_static_pad (queue, "src");
receive->rtp_src = gst_ghost_pad_new ("rtp_src", pad);
gst_element_add_pad (GST_ELEMENT (receive), receive->rtp_src);
gst_object_unref (pad);
/* create funnel for rtcp_src */
funnel = gst_element_factory_make ("funnel", NULL);
gst_bin_add (GST_BIN (receive), funnel);
if (!gst_element_link_pads (receive->stream->transport->dtlssrtpdec,
"rtcp_src", funnel, "sink_0"))
g_warn_if_reached ();
if (!gst_element_link_pads (receive->stream->rtcp_transport->dtlssrtpdec,
"rtcp_src", funnel, "sink_1"))
g_warn_if_reached ();
pad = gst_element_get_static_pad (funnel, "src");
ghost = gst_ghost_pad_new ("rtcp_src", pad);
gst_element_add_pad (GST_ELEMENT (receive), ghost);
gst_object_unref (pad);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
transport_receive_bin_class_init (TransportReceiveBinClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = (GstElementClass *) klass;
element_class->change_state = transport_receive_bin_change_state;
gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
gst_element_class_add_static_pad_template (element_class,
&rtcp_sink_template);
gst_element_class_set_metadata (element_class, "WebRTC Transport Receive Bin",
"Filter/Network/WebRTC", "A bin for webrtc connections",
"Matthew Waters <matthew@centricular.com>");
gobject_class->constructed = transport_receive_bin_constructed;
gobject_class->get_property = transport_receive_bin_get_property;
gobject_class->set_property = transport_receive_bin_set_property;
gobject_class->finalize = transport_receive_bin_finalize;
g_object_class_install_property (gobject_class,
PROP_STREAM,
g_param_spec_object ("stream", "Stream",
"The TransportStream for this receiveing bin",
transport_stream_get_type (),
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static void
transport_receive_bin_init (TransportReceiveBin * receive)
{
g_mutex_init (&receive->pad_block_lock);
g_cond_init (&receive->pad_block_cond);
}

View file

@ -0,0 +1,65 @@
/* 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 __TRANSPORT_RECEIVE_BIN_H__
#define __TRANSPORT_RECEIVE_BIN_H__
#include <gst/gst.h>
#include "transportstream.h"
G_BEGIN_DECLS
GType transport_receive_bin_get_type(void);
#define GST_TYPE_WEBRTC_TRANSPORT_RECEIVE_BIN (transport_receive_bin_get_type())
#define TRANSPORT_RECEIVE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_TRANSPORT_RECEIVE_BIN,TransportReceiveBin))
#define TRANSPORT_RECEIVE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_TRANSPORT_RECEIVE_BIN,TransportReceiveBinClass))
#define TRANSPORT_RECEIVE_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_TRANSPORT_RECEIVE_BIN,TransportReceiveBinClass))
typedef enum
{
RECEIVE_STATE_BLOCK = 1,
RECEIVE_STATE_DROP,
RECEIVE_STATE_PASS,
} ReceiveState;
struct _TransportReceiveBin
{
GstBin parent;
TransportStream *stream; /* parent transport stream */
gboolean rtcp_mux;
GstPad *rtp_src;
struct pad_block *rtp_block;
GMutex pad_block_lock;
GCond pad_block_cond;
ReceiveState receive_state;
};
struct _TransportReceiveBinClass
{
GstBinClass parent_class;
};
void transport_receive_bin_set_receive_state (TransportReceiveBin * receive,
ReceiveState state);
G_END_DECLS
#endif /* __TRANSPORT_RECEIVE_BIN_H__ */

View file

@ -0,0 +1,471 @@
/* 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 "transportsendbin.h"
#include "utils.h"
/*
* ,------------------------transport_send_%u-------------------------,
* ; ,-----dtlssrtpenc---, ;
* rtp_sink o--------------------------o rtp_sink_0 ; ,---nicesink---, ;
* ; ; src o--o sink ; ;
* ; ,--outputselector--, ,-o rtcp_sink_0 ; '--------------' ;
* ; ; src_0 o-' '-------------------' ;
* rtcp_sink ;---o sink ; ,----dtlssrtpenc----, ,---nicesink---, ;
* ; ; src_1 o---o rtcp_sink_0 src o--o sink ; ;
* ; '------------------' '-------------------' '--------------' ;
* '------------------------------------------------------------------'
*
* outputselecter is used to switch between rtcp-mux and no rtcp-mux
*
* FIXME: Do we need a valve drop=TRUE for the no RTCP case?
*/
#define GST_CAT_DEFAULT gst_webrtc_transport_send_bin_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define transport_send_bin_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (TransportSendBin, transport_send_bin, GST_TYPE_BIN,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_transport_send_bin_debug,
"webrtctransportsendbin", 0, "webrtctransportsendbin"););
static GstStaticPadTemplate rtp_sink_template =
GST_STATIC_PAD_TEMPLATE ("rtp_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp"));
static GstStaticPadTemplate rtcp_sink_template =
GST_STATIC_PAD_TEMPLATE ("rtcp_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp"));
enum
{
PROP_0,
PROP_STREAM,
PROP_RTCP_MUX,
};
static void
_set_rtcp_mux (TransportSendBin * send, gboolean rtcp_mux)
{
GstPad *active_pad;
if (rtcp_mux)
active_pad = gst_element_get_static_pad (send->outputselector, "src_0");
else
active_pad = gst_element_get_static_pad (send->outputselector, "src_1");
send->rtcp_mux = rtcp_mux;
GST_OBJECT_UNLOCK (send);
g_object_set (send->outputselector, "active-pad", active_pad, NULL);
gst_object_unref (active_pad);
GST_OBJECT_LOCK (send);
}
static void
transport_send_bin_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
TransportSendBin *send = TRANSPORT_SEND_BIN (object);
GST_OBJECT_LOCK (send);
switch (prop_id) {
case PROP_STREAM:
/* XXX: weak-ref this? */
send->stream = TRANSPORT_STREAM (g_value_get_object (value));
break;
case PROP_RTCP_MUX:
_set_rtcp_mux (send, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (send);
}
static void
transport_send_bin_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
TransportSendBin *send = TRANSPORT_SEND_BIN (object);
GST_OBJECT_LOCK (send);
switch (prop_id) {
case PROP_STREAM:
g_value_set_object (value, send->stream);
break;
case PROP_RTCP_MUX:
g_value_set_boolean (value, send->rtcp_mux);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (send);
}
static GstPadProbeReturn
pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
{
GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
return GST_PAD_PROBE_OK;
}
static GstStateChangeReturn
transport_send_bin_change_state (GstElement * element,
GstStateChange transition)
{
TransportSendBin *send = TRANSPORT_SEND_BIN (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:{
/* XXX: don't change state until the client-ness has been chosen
* arguably the element should be able to deal with this itself or
* we should only add it once/if we get the encoding keys */
gst_element_set_locked_state (send->stream->transport->dtlssrtpenc, TRUE);
gst_element_set_locked_state (send->stream->rtcp_transport->dtlssrtpenc,
TRUE);
break;
}
case GST_STATE_CHANGE_READY_TO_PAUSED:{
GstElement *elem;
GstPad *pad;
/* unblock the encoder once the key is set, this should also be automatic */
elem = send->stream->transport->dtlssrtpenc;
pad = gst_element_get_static_pad (elem, "rtp_sink_0");
send->rtp_block = _create_pad_block (elem, pad, 0, NULL, NULL);
send->rtp_block->block_id =
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
NULL);
gst_object_unref (pad);
/* unblock the encoder once the key is set, this should also be automatic */
pad = gst_element_get_static_pad (elem, "rtcp_sink_0");
send->rtcp_mux_block = _create_pad_block (elem, pad, 0, NULL, NULL);
send->rtcp_mux_block->block_id =
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
NULL);
gst_object_unref (pad);
elem = send->stream->rtcp_transport->dtlssrtpenc;
/* unblock the encoder once the key is set, this should also be automatic */
pad = gst_element_get_static_pad (elem, "rtcp_sink_0");
send->rtcp_block = _create_pad_block (elem, pad, 0, NULL, NULL);
send->rtcp_block->block_id =
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
NULL);
gst_object_unref (pad);
/* unblock ice sink once a connection is made, this should also be automatic */
elem = send->stream->transport->transport->sink;
pad = gst_element_get_static_pad (elem, "sink");
send->rtp_nice_block = _create_pad_block (elem, pad, 0, NULL, NULL);
send->rtp_nice_block->block_id =
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
NULL);
gst_object_unref (pad);
/* unblock ice sink once a connection is made, this should also be automatic */
elem = send->stream->rtcp_transport->transport->sink;
pad = gst_element_get_static_pad (elem, "sink");
send->rtcp_nice_block = _create_pad_block (elem, pad, 0, NULL, NULL);
send->rtcp_nice_block->block_id =
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
NULL);
gst_object_unref (pad);
break;
}
case GST_STATE_CHANGE_PAUSED_TO_READY:
{
/* Release pad blocks */
if (send->rtp_block && send->rtp_block->block_id) {
gst_pad_set_active (send->rtp_block->pad, FALSE);
gst_pad_remove_probe (send->rtp_block->pad, send->rtp_block->block_id);
send->rtp_block->block_id = 0;
}
if (send->rtcp_mux_block && send->rtcp_mux_block->block_id) {
gst_pad_set_active (send->rtcp_mux_block->pad, FALSE);
gst_pad_remove_probe (send->rtcp_mux_block->pad,
send->rtcp_mux_block->block_id);
send->rtcp_mux_block->block_id = 0;
}
if (send->rtcp_block && send->rtcp_block->block_id) {
gst_pad_set_active (send->rtcp_block->pad, FALSE);
gst_pad_remove_probe (send->rtcp_block->pad,
send->rtcp_block->block_id);
send->rtcp_block->block_id = 0;
}
if (send->rtp_nice_block && send->rtp_nice_block->block_id) {
gst_pad_set_active (send->rtp_nice_block->pad, FALSE);
gst_pad_remove_probe (send->rtp_nice_block->pad,
send->rtp_nice_block->block_id);
send->rtp_nice_block->block_id = 0;
}
if (send->rtcp_nice_block && send->rtcp_nice_block->block_id) {
gst_pad_set_active (send->rtcp_nice_block->pad, FALSE);
gst_pad_remove_probe (send->rtcp_nice_block->pad,
send->rtcp_nice_block->block_id);
send->rtcp_nice_block->block_id = 0;
}
break;
}
case GST_STATE_CHANGE_READY_TO_NULL:{
GstElement *elem;
if (send->rtp_block)
_free_pad_block (send->rtp_block);
send->rtp_block = NULL;
if (send->rtcp_mux_block)
_free_pad_block (send->rtcp_mux_block);
send->rtcp_mux_block = NULL;
elem = send->stream->transport->dtlssrtpenc;
gst_element_set_locked_state (elem, FALSE);
if (send->rtcp_block)
_free_pad_block (send->rtcp_block);
send->rtcp_block = NULL;
elem = send->stream->rtcp_transport->dtlssrtpenc;
gst_element_set_locked_state (elem, FALSE);
if (send->rtp_nice_block)
_free_pad_block (send->rtp_nice_block);
send->rtp_nice_block = NULL;
if (send->rtcp_nice_block)
_free_pad_block (send->rtcp_nice_block);
send->rtcp_nice_block = NULL;
break;
}
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
return ret;
}
static void
_on_dtls_enc_key_set (GstElement * element, TransportSendBin * send)
{
if (element == send->stream->transport->dtlssrtpenc) {
GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
send->rtp_block->pad);
_free_pad_block (send->rtp_block);
send->rtp_block = NULL;
GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
send->rtcp_mux_block->pad);
_free_pad_block (send->rtcp_mux_block);
send->rtcp_mux_block = NULL;
} else if (element == send->stream->rtcp_transport->dtlssrtpenc) {
GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
send->rtcp_block->pad);
_free_pad_block (send->rtcp_block);
send->rtcp_block = NULL;
}
}
static void
_on_notify_ice_connection_state (GstWebRTCICETransport * transport,
GParamSpec * pspec, TransportSendBin * send)
{
GstWebRTCICEConnectionState state;
g_object_get (transport, "state", &state, NULL);
if (state == GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED ||
state == GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED) {
GST_OBJECT_LOCK (send);
if (transport == send->stream->transport->transport) {
if (send->rtp_nice_block) {
GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
send->rtp_nice_block->pad);
_free_pad_block (send->rtp_nice_block);
}
send->rtp_nice_block = NULL;
} else if (transport == send->stream->rtcp_transport->transport) {
if (send->rtcp_nice_block) {
GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
send->rtcp_nice_block->pad);
_free_pad_block (send->rtcp_nice_block);
}
send->rtcp_nice_block = NULL;
}
GST_OBJECT_UNLOCK (send);
}
}
static void
transport_send_bin_constructed (GObject * object)
{
TransportSendBin *send = TRANSPORT_SEND_BIN (object);
GstWebRTCDTLSTransport *transport;
GstPadTemplate *templ;
GstPad *ghost, *pad;
g_return_if_fail (send->stream);
g_object_bind_property (send, "rtcp-mux", send->stream, "rtcp-mux",
G_BINDING_BIDIRECTIONAL);
transport = send->stream->transport;
templ = _find_pad_template (transport->dtlssrtpenc,
GST_PAD_SINK, GST_PAD_REQUEST, "rtp_sink_%d");
pad = gst_element_request_pad (transport->dtlssrtpenc, templ, "rtp_sink_0",
NULL);
/* unblock the encoder once the key is set */
g_signal_connect (transport->dtlssrtpenc, "on-key-set",
G_CALLBACK (_on_dtls_enc_key_set), send);
gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->dtlssrtpenc));
/* unblock ice sink once it signals a connection */
g_signal_connect (transport->transport, "notify::state",
G_CALLBACK (_on_notify_ice_connection_state), send);
gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->transport->sink));
if (!gst_element_link_pads (GST_ELEMENT (transport->dtlssrtpenc), "src",
GST_ELEMENT (transport->transport->sink), "sink"))
g_warn_if_reached ();
send->outputselector = gst_element_factory_make ("output-selector", NULL);
gst_bin_add (GST_BIN (send), send->outputselector);
if (!gst_element_link_pads (GST_ELEMENT (send->outputselector), "src_0",
GST_ELEMENT (transport->dtlssrtpenc), "rtcp_sink_0"))
g_warn_if_reached ();
ghost = gst_ghost_pad_new ("rtp_sink", pad);
gst_element_add_pad (GST_ELEMENT (send), ghost);
gst_object_unref (pad);
transport = send->stream->rtcp_transport;
templ = _find_pad_template (transport->dtlssrtpenc,
GST_PAD_SINK, GST_PAD_REQUEST, "rtcp_sink_%d");
/* unblock the encoder once the key is set */
g_signal_connect (transport->dtlssrtpenc, "on-key-set",
G_CALLBACK (_on_dtls_enc_key_set), send);
gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->dtlssrtpenc));
/* unblock ice sink once it signals a connection */
g_signal_connect (transport->transport, "notify::state",
G_CALLBACK (_on_notify_ice_connection_state), send);
gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->transport->sink));
if (!gst_element_link_pads (GST_ELEMENT (transport->dtlssrtpenc), "src",
GST_ELEMENT (transport->transport->sink), "sink"))
g_warn_if_reached ();
if (!gst_element_link_pads (GST_ELEMENT (send->outputselector), "src_1",
GST_ELEMENT (transport->dtlssrtpenc), "rtcp_sink_0"))
g_warn_if_reached ();
pad = gst_element_get_static_pad (send->outputselector, "sink");
ghost = gst_ghost_pad_new ("rtcp_sink", pad);
gst_element_add_pad (GST_ELEMENT (send), ghost);
gst_object_unref (pad);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
transport_send_bin_dispose (GObject * object)
{
TransportSendBin *send = TRANSPORT_SEND_BIN (object);
if (send->stream) {
g_signal_handlers_disconnect_by_data (send->stream->transport->transport,
send);
g_signal_handlers_disconnect_by_data (send->stream->
rtcp_transport->transport, send);
}
send->stream = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
transport_send_bin_class_init (TransportSendBinClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = (GstElementClass *) klass;
element_class->change_state = transport_send_bin_change_state;
gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
gst_element_class_add_static_pad_template (element_class,
&rtcp_sink_template);
gst_element_class_set_metadata (element_class, "WebRTC Transport Send Bin",
"Filter/Network/WebRTC", "A bin for webrtc connections",
"Matthew Waters <matthew@centricular.com>");
gobject_class->constructed = transport_send_bin_constructed;
gobject_class->dispose = transport_send_bin_dispose;
gobject_class->get_property = transport_send_bin_get_property;
gobject_class->set_property = transport_send_bin_set_property;
g_object_class_install_property (gobject_class,
PROP_STREAM,
g_param_spec_object ("stream", "Stream",
"The TransportStream for this sending bin",
transport_stream_get_type (),
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_RTCP_MUX,
g_param_spec_boolean ("rtcp-mux", "RTCP Mux",
"Whether RTCP packets are muxed with RTP packets",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
transport_send_bin_init (TransportSendBin * send)
{
}

View file

@ -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 __TRANSPORT_SEND_BIN_H__
#define __TRANSPORT_SEND_BIN_H__
#include <gst/gst.h>
#include "transportstream.h"
#include "utils.h"
G_BEGIN_DECLS
GType transport_send_bin_get_type(void);
#define GST_TYPE_WEBRTC_TRANSPORT_SEND_BIN (transport_send_bin_get_type())
#define TRANSPORT_SEND_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_TRANSPORT_SEND_BIN,TransportSendBin))
#define TRANSPORT_SEND_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_TRANSPORT_SEND_BIN,TransportSendBinClass))
#define TRANSPORT_SEND_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_TRANSPORT_SEND_BIN,TransportSendBinClass))
struct _TransportSendBin
{
GstBin parent;
TransportStream *stream; /* parent transport stream */
gboolean rtcp_mux;
GstElement *outputselector;
struct pad_block *rtp_block;
struct pad_block *rtcp_mux_block;
struct pad_block *rtcp_block;
struct pad_block *rtp_nice_block;
struct pad_block *rtcp_nice_block;
};
struct _TransportSendBinClass
{
GstBinClass parent_class;
};
G_END_DECLS
#endif /* __TRANSPORT_SEND_BIN_H__ */

View file

@ -0,0 +1,252 @@
/* 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 "transportstream.h"
#include "transportsendbin.h"
#include "transportreceivebin.h"
#include "gstwebrtcice.h"
#include "gstwebrtcbin.h"
#include "utils.h"
#define transport_stream_parent_class parent_class
G_DEFINE_TYPE (TransportStream, transport_stream, GST_TYPE_OBJECT);
enum
{
PROP_0,
PROP_WEBRTC,
PROP_SESSION_ID,
PROP_RTCP_MUX,
PROP_DTLS_CLIENT,
};
static void
transport_stream_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
TransportStream *stream = TRANSPORT_STREAM (object);
switch (prop_id) {
case PROP_WEBRTC:
gst_object_set_parent (GST_OBJECT (stream), g_value_get_object (value));
break;
}
GST_OBJECT_LOCK (stream);
switch (prop_id) {
case PROP_WEBRTC:
break;
case PROP_SESSION_ID:
stream->session_id = g_value_get_uint (value);
break;
case PROP_RTCP_MUX:
stream->rtcp_mux = g_value_get_boolean (value);
break;
case PROP_DTLS_CLIENT:
stream->dtls_client = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (stream);
}
static void
transport_stream_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
TransportStream *stream = TRANSPORT_STREAM (object);
GST_OBJECT_LOCK (stream);
switch (prop_id) {
case PROP_SESSION_ID:
g_value_set_uint (value, stream->session_id);
break;
case PROP_RTCP_MUX:
g_value_set_boolean (value, stream->rtcp_mux);
break;
case PROP_DTLS_CLIENT:
g_value_set_boolean (value, stream->dtls_client);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (stream);
}
static void
transport_stream_dispose (GObject * object)
{
TransportStream *stream = TRANSPORT_STREAM (object);
if (stream->send_bin)
gst_object_unref (stream->send_bin);
stream->send_bin = NULL;
if (stream->receive_bin)
gst_object_unref (stream->receive_bin);
stream->receive_bin = NULL;
if (stream->transport)
gst_object_unref (stream->transport);
stream->transport = NULL;
if (stream->rtcp_transport)
gst_object_unref (stream->rtcp_transport);
stream->rtcp_transport = NULL;
GST_OBJECT_PARENT (object) = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
transport_stream_finalize (GObject * object)
{
TransportStream *stream = TRANSPORT_STREAM (object);
g_array_free (stream->ptmap, TRUE);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
transport_stream_constructed (GObject * object)
{
TransportStream *stream = TRANSPORT_STREAM (object);
GstWebRTCBin *webrtc;
GstWebRTCICETransport *ice_trans;
stream->transport = gst_webrtc_dtls_transport_new (stream->session_id, FALSE);
stream->rtcp_transport =
gst_webrtc_dtls_transport_new (stream->session_id, TRUE);
webrtc = GST_WEBRTC_BIN (gst_object_get_parent (GST_OBJECT (object)));
g_object_bind_property (stream->transport, "client", stream, "dtls-client",
G_BINDING_BIDIRECTIONAL);
g_object_bind_property (stream->rtcp_transport, "client", stream,
"dtls-client", G_BINDING_BIDIRECTIONAL);
g_object_bind_property (stream->transport, "certificate",
stream->rtcp_transport, "certificate", G_BINDING_BIDIRECTIONAL);
/* Need to go full Java and have a transport manager?
* Or make the caller set the ICE transport up? */
stream->stream = _find_ice_stream_for_session (webrtc, stream->session_id);
if (stream->stream == NULL) {
stream->stream = gst_webrtc_ice_add_stream (webrtc->priv->ice,
stream->session_id);
_add_ice_stream_item (webrtc, stream->session_id, stream->stream);
}
ice_trans =
gst_webrtc_ice_find_transport (webrtc->priv->ice, stream->stream,
GST_WEBRTC_ICE_COMPONENT_RTP);
gst_webrtc_dtls_transport_set_transport (stream->transport, ice_trans);
gst_object_unref (ice_trans);
ice_trans =
gst_webrtc_ice_find_transport (webrtc->priv->ice, stream->stream,
GST_WEBRTC_ICE_COMPONENT_RTCP);
gst_webrtc_dtls_transport_set_transport (stream->rtcp_transport, ice_trans);
gst_object_unref (ice_trans);
stream->send_bin = g_object_new (transport_send_bin_get_type (), "stream",
stream, NULL);
gst_object_ref_sink (stream->send_bin);
stream->receive_bin = g_object_new (transport_receive_bin_get_type (),
"stream", stream, NULL);
gst_object_ref_sink (stream->receive_bin);
gst_object_unref (webrtc);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
transport_stream_class_init (TransportStreamClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->constructed = transport_stream_constructed;
gobject_class->get_property = transport_stream_get_property;
gobject_class->set_property = transport_stream_set_property;
gobject_class->dispose = transport_stream_dispose;
gobject_class->finalize = transport_stream_finalize;
/* some acrobatics are required to set the parent before _constructed()
* has been called */
g_object_class_install_property (gobject_class,
PROP_WEBRTC,
g_param_spec_object ("webrtc", "Parent webrtcbin",
"Parent webrtcbin",
GST_TYPE_WEBRTC_BIN,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_SESSION_ID,
g_param_spec_uint ("session-id", "Session ID",
"Session ID used for this transport",
0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_RTCP_MUX,
g_param_spec_boolean ("rtcp-mux", "RTCP Mux",
"Whether RTCP packets are muxed with RTP packets",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_DTLS_CLIENT,
g_param_spec_boolean ("dtls-client", "DTLS client",
"Whether we take the client role in DTLS negotiation",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
clear_ptmap_item (PtMapItem * item)
{
if (item->caps)
gst_caps_unref (item->caps);
}
static void
transport_stream_init (TransportStream * stream)
{
stream->ptmap = g_array_new (FALSE, TRUE, sizeof (PtMapItem));
g_array_set_clear_func (stream->ptmap, (GDestroyNotify) clear_ptmap_item);
}
TransportStream *
transport_stream_new (GstWebRTCBin * webrtc, guint session_id)
{
TransportStream *stream;
stream = g_object_new (transport_stream_get_type (), "webrtc", webrtc,
"session-id", session_id, NULL);
return stream;
}

View file

@ -0,0 +1,69 @@
/* 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 __TRANSPORT_STREAM_H__
#define __TRANSPORT_STREAM_H__
#include "fwd.h"
#include <gst/webrtc/rtptransceiver.h>
G_BEGIN_DECLS
GType transport_stream_get_type(void);
#define GST_TYPE_WEBRTC_TRANSPORT_STREAM (transport_stream_get_type())
#define TRANSPORT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_TRANSPORT_STREAM,TransportStream))
#define TRANSPORT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_TRANSPORT_STREAM,TransportStreamClass))
#define TRANSPORT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_TRANSPORT_STREAM,TransportStreamClass))
typedef struct
{
guint8 pt;
GstCaps *caps;
} PtMapItem;
struct _TransportStream
{
GstObject parent;
guint session_id; /* session_id */
gboolean rtcp;
gboolean rtcp_mux;
gboolean rtcp_rsize;
gboolean dtls_client;
TransportSendBin *send_bin; /* bin containing all the sending transport elements */
TransportReceiveBin *receive_bin; /* bin containing all the receiving transport elements */
GstWebRTCICEStream *stream;
GstWebRTCDTLSTransport *transport;
GstWebRTCDTLSTransport *rtcp_transport;
GArray *ptmap; /* array of PtMapItem's */
};
struct _TransportStreamClass
{
GstObjectClass parent_class;
};
TransportStream * transport_stream_new (GstWebRTCBin * webrtc,
guint session_id);
G_END_DECLS
#endif /* __TRANSPORT_STREAM_H__ */

138
ext/webrtc/utils.c Normal file
View file

@ -0,0 +1,138 @@
/* 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 "utils.h"
#include "gstwebrtcbin.h"
GstPadTemplate *
_find_pad_template (GstElement * element, GstPadDirection direction,
GstPadPresence presence, const gchar * name)
{
GstElementClass *element_class = GST_ELEMENT_GET_CLASS (element);
const GList *l = gst_element_class_get_pad_template_list (element_class);
GstPadTemplate *templ = NULL;
for (; l; l = l->next) {
templ = l->data;
if (templ->direction != direction)
continue;
if (templ->presence != presence)
continue;
if (g_strcmp0 (templ->name_template, name) == 0) {
return templ;
}
}
return NULL;
}
GstSDPMessage *
_get_latest_sdp (GstWebRTCBin * webrtc)
{
if (webrtc->current_local_description &&
webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
return webrtc->current_local_description->sdp;
}
if (webrtc->current_remote_description &&
webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
return webrtc->current_remote_description->sdp;
}
if (webrtc->current_local_description &&
webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
return webrtc->current_local_description->sdp;
}
if (webrtc->current_remote_description &&
webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
return webrtc->current_remote_description->sdp;
}
return NULL;
}
struct pad_block *
_create_pad_block (GstElement * element, GstPad * pad, gulong block_id,
gpointer user_data, GDestroyNotify notify)
{
struct pad_block *ret = g_new0 (struct pad_block, 1);
ret->element = gst_object_ref (element);
ret->pad = gst_object_ref (pad);
ret->block_id = block_id;
ret->user_data = user_data;
ret->notify = notify;
return ret;
}
void
_free_pad_block (struct pad_block *block)
{
if (!block)
return;
if (block->block_id)
gst_pad_remove_probe (block->pad, block->block_id);
gst_object_unref (block->element);
gst_object_unref (block->pad);
if (block->notify)
block->notify (block->user_data);
g_free (block);
}
gchar *
_enum_value_to_string (GType type, guint value)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
gchar *str = NULL;
enum_class = g_type_class_ref (type);
enum_value = g_enum_get_value (enum_class, value);
if (enum_value)
str = g_strdup (enum_value->value_nick);
g_type_class_unref (enum_class);
return str;
}
const gchar *
_g_checksum_to_webrtc_string (GChecksumType type)
{
switch (type) {
case G_CHECKSUM_SHA1:
return "sha-1";
case G_CHECKSUM_SHA256:
return "sha-256";
#ifdef G_CHECKSUM_SHA384
case G_CHECKSUM_SHA384:
return "sha-384";
#endif
case G_CHECKSUM_SHA512:
return "sha-512";
default:
g_warning ("unknown GChecksumType!");
return NULL;
}
}

65
ext/webrtc/utils.h Normal file
View file

@ -0,0 +1,65 @@
/* 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_UTILS_H__
#define __WEBRTC_UTILS_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc.h>
#include "fwd.h"
G_BEGIN_DECLS
GstPadTemplate * _find_pad_template (GstElement * element,
GstPadDirection direction,
GstPadPresence presence,
const gchar * name);
GstSDPMessage * _get_latest_sdp (GstWebRTCBin * webrtc);
GstWebRTCICEStream * _find_ice_stream_for_session (GstWebRTCBin * webrtc,
guint session_id);
void _add_ice_stream_item (GstWebRTCBin * webrtc,
guint session_id,
GstWebRTCICEStream * stream);
struct pad_block
{
GstElement *element;
GstPad *pad;
gulong block_id;
gpointer user_data;
GDestroyNotify notify;
};
void _free_pad_block (struct pad_block *block);
struct pad_block * _create_pad_block (GstElement * element,
GstPad * pad,
gulong block_id,
gpointer user_data,
GDestroyNotify notify);
G_GNUC_INTERNAL
gchar * _enum_value_to_string (GType type, guint value);
G_GNUC_INTERNAL
const gchar * _g_checksum_to_webrtc_string (GChecksumType type);
G_END_DECLS
#endif /* __WEBRTC_UTILS_H__ */

716
ext/webrtc/webrtcsdp.c Normal file
View file

@ -0,0 +1,716 @@
/* 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 "webrtcsdp.h"
#include "utils.h"
#include "gstwebrtcbin.h"
#include <string.h>
#define IS_EMPTY_SDP_ATTRIBUTE(val) (val == NULL || g_strcmp0(val, "") == 0)
const gchar *
_sdp_source_to_string (SDPSource source)
{
switch (source) {
case SDP_LOCAL:
return "local";
case SDP_REMOTE:
return "remote";
default:
return "none";
}
}
static gboolean
_check_valid_state_for_sdp_change (GstWebRTCBin * webrtc, SDPSource source,
GstWebRTCSDPType type, GError ** error)
{
GstWebRTCSignalingState state = webrtc->signaling_state;
#define STATE(val) GST_WEBRTC_SIGNALING_STATE_ ## val
#define TYPE(val) GST_WEBRTC_SDP_TYPE_ ## val
if (source == SDP_LOCAL && type == TYPE (OFFER) && state == STATE (STABLE))
return TRUE;
if (source == SDP_LOCAL && type == TYPE (OFFER)
&& state == STATE (HAVE_LOCAL_OFFER))
return TRUE;
if (source == SDP_LOCAL && type == TYPE (ANSWER)
&& state == STATE (HAVE_REMOTE_OFFER))
return TRUE;
if (source == SDP_LOCAL && type == TYPE (PRANSWER)
&& state == STATE (HAVE_REMOTE_OFFER))
return TRUE;
if (source == SDP_LOCAL && type == TYPE (PRANSWER)
&& state == STATE (HAVE_LOCAL_PRANSWER))
return TRUE;
if (source == SDP_REMOTE && type == TYPE (OFFER) && state == STATE (STABLE))
return TRUE;
if (source == SDP_REMOTE && type == TYPE (OFFER)
&& state == STATE (HAVE_REMOTE_OFFER))
return TRUE;
if (source == SDP_REMOTE && type == TYPE (ANSWER)
&& state == STATE (HAVE_LOCAL_OFFER))
return TRUE;
if (source == SDP_REMOTE && type == TYPE (PRANSWER)
&& state == STATE (HAVE_LOCAL_OFFER))
return TRUE;
if (source == SDP_REMOTE && type == TYPE (PRANSWER)
&& state == STATE (HAVE_REMOTE_PRANSWER))
return TRUE;
{
gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
webrtc->signaling_state);
gchar *type_str = _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, type);
g_set_error (error, GST_WEBRTC_BIN_ERROR,
GST_WEBRTC_BIN_ERROR_INVALID_STATE,
"Not in the correct state (%s) for setting %s %s description", state,
_sdp_source_to_string (source), type_str);
g_free (state);
g_free (type_str);
}
return FALSE;
#undef STATE
#undef TYPE
}
static gboolean
_check_sdp_crypto (GstWebRTCBin * webrtc, SDPSource source,
GstWebRTCSessionDescription * sdp, GError ** error)
{
const gchar *message_fingerprint, *fingerprint;
const GstSDPKey *key;
int i;
key = gst_sdp_message_get_key (sdp->sdp);
if (!IS_EMPTY_SDP_ATTRIBUTE (key->data)) {
g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
GST_WEBRTC_BIN_ERROR_BAD_SDP, "sdp contains a k line");
return FALSE;
}
message_fingerprint = fingerprint =
gst_sdp_message_get_attribute_val (sdp->sdp, "fingerprint");
for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
const gchar *media_fingerprint =
gst_sdp_media_get_attribute_val (media, "fingerprint");
if (!IS_EMPTY_SDP_ATTRIBUTE (message_fingerprint)
&& !IS_EMPTY_SDP_ATTRIBUTE (media_fingerprint)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR,
GST_WEBRTC_BIN_ERROR_FINGERPRINT,
"No fingerprint lines in sdp for media %u", i);
return FALSE;
}
if (IS_EMPTY_SDP_ATTRIBUTE (fingerprint)) {
fingerprint = media_fingerprint;
}
if (!IS_EMPTY_SDP_ATTRIBUTE (media_fingerprint)
&& g_strcmp0 (fingerprint, media_fingerprint) != 0) {
g_set_error (error, GST_WEBRTC_BIN_ERROR,
GST_WEBRTC_BIN_ERROR_FINGERPRINT,
"Fingerprint in media %u differs from %s fingerprint. "
"\'%s\' != \'%s\'", i, message_fingerprint ? "global" : "previous",
fingerprint, media_fingerprint);
return FALSE;
}
}
return TRUE;
}
#if 0
static gboolean
_session_has_attribute_key (const GstSDPMessage * msg, const gchar * key)
{
int i;
for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) {
const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
if (g_strcmp0 (attr->key, key) == 0)
return TRUE;
}
return FALSE;
}
static gboolean
_session_has_attribute_key_value (const GstSDPMessage * msg, const gchar * key,
const gchar * value)
{
int i;
for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) {
const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
if (g_strcmp0 (attr->key, key) == 0 && g_strcmp0 (attr->value, value) == 0)
return TRUE;
}
return FALSE;
}
static gboolean
_check_trickle_ice (GstSDPMessage * msg, GError ** error)
{
if (!_session_has_attribute_key_value (msg, "ice-options", "trickle")) {
g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
GST_WEBRTC_BIN_ERROR_BAD_SDP,
"No required \'a=ice-options:trickle\' line in sdp");
}
return TRUE;
}
#endif
gboolean
_media_has_attribute_key (const GstSDPMedia * media, const gchar * key)
{
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, key) == 0)
return TRUE;
}
return FALSE;
}
static gboolean
_media_has_mid (const GstSDPMedia * media, guint media_idx, GError ** error)
{
const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
if (IS_EMPTY_SDP_ATTRIBUTE (mid)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u is missing or contains an empty \'mid\' attribute",
media_idx);
return FALSE;
}
return TRUE;
}
static const gchar *
_media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx)
{
const gchar *ice_ufrag;
ice_ufrag = gst_sdp_message_get_attribute_val (msg, "ice-ufrag");
if (IS_EMPTY_SDP_ATTRIBUTE (ice_ufrag)) {
const GstSDPMedia *media = gst_sdp_message_get_media (msg, media_idx);
ice_ufrag = gst_sdp_media_get_attribute_val (media, "ice-ufrag");
if (IS_EMPTY_SDP_ATTRIBUTE (ice_ufrag))
return NULL;
}
return ice_ufrag;
}
static const gchar *
_media_get_ice_pwd (const GstSDPMessage * msg, guint media_idx)
{
const gchar *ice_pwd;
ice_pwd = gst_sdp_message_get_attribute_val (msg, "ice-pwd");
if (IS_EMPTY_SDP_ATTRIBUTE (ice_pwd)) {
const GstSDPMedia *media = gst_sdp_message_get_media (msg, media_idx);
ice_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
if (IS_EMPTY_SDP_ATTRIBUTE (ice_pwd))
return NULL;
}
return ice_pwd;
}
static gboolean
_media_has_setup (const GstSDPMedia * media, guint media_idx, GError ** error)
{
static const gchar *valid_setups[] = { "actpass", "active", "passive", NULL };
const gchar *setup = gst_sdp_media_get_attribute_val (media, "setup");
if (IS_EMPTY_SDP_ATTRIBUTE (setup)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u is missing or contains an empty \'setup\' attribute",
media_idx);
return FALSE;
}
if (!g_strv_contains (valid_setups, setup)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u contains unknown \'setup\' attribute, \'%s\'", media_idx,
setup);
return FALSE;
}
return TRUE;
}
#if 0
static gboolean
_media_has_dtls_id (const GstSDPMedia * media, guint media_idx, GError ** error)
{
const gchar *dtls_id = gst_sdp_media_get_attribute_val (media, "ice-pwd");
if (IS_EMPTY_SDP_ATTRIBUTE (dtls_id)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u is missing or contains an empty \'dtls-id\' attribute",
media_idx);
return FALSE;
}
return TRUE;
}
#endif
gboolean
validate_sdp (GstWebRTCBin * webrtc, SDPSource source,
GstWebRTCSessionDescription * sdp, GError ** error)
{
#if 0
const gchar *group, *bundle_ice_ufrag = NULL, *bundle_ice_pwd = NULL;
gchar **group_members = NULL;
gboolean is_bundle = FALSE;
#endif
int i;
if (!_check_valid_state_for_sdp_change (webrtc, source, sdp->type, error))
return FALSE;
if (!_check_sdp_crypto (webrtc, source, sdp, error))
return FALSE;
/* not explicitly required
if (ICE && !_check_trickle_ice (sdp->sdp))
return FALSE;
group = gst_sdp_message_get_attribute_val (sdp->sdp, "group");
is_bundle = g_str_has_prefix (group, "BUNDLE");
if (is_bundle)
group_members = g_strsplit (&group[6], " ", -1);*/
for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
#if 0
const gchar *mid;
gboolean media_in_bundle = FALSE, first_media_in_bundle = FALSE;
gboolean bundle_only = FALSE;
#endif
if (!_media_has_mid (media, i, error))
goto fail;
#if 0
mid = gst_sdp_media_get_attribute_val (media, "mid");
media_in_bundle = is_bundle && g_strv_contains (group_members, mid);
if (media_in_bundle)
bundle_only =
gst_sdp_media_get_attribute_val (media, "bundle-only") != NULL;
first_media_in_bundle = media_in_bundle
&& g_strcmp0 (mid, group_members[0]) == 0;
#endif
if (!_media_get_ice_ufrag (sdp->sdp, i)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u is missing or contains an empty \'ice-ufrag\' attribute",
i);
goto fail;
}
if (!_media_get_ice_pwd (sdp->sdp, i)) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u is missing or contains an empty \'ice-pwd\' attribute", i);
goto fail;
}
if (!_media_has_setup (media, i, error))
goto fail;
#if 0
/* check paramaters in bundle are the same */
if (media_in_bundle) {
const gchar *ice_ufrag =
gst_sdp_media_get_attribute_val (media, "ice-ufrag");
const gchar *ice_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
if (!bundle_ice_ufrag)
bundle_ice_ufrag = ice_ufrag;
else if (!g_strcmp0 (bundle_ice_ufrag, ice_ufrag) != 0) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u has different ice-ufrag values in bundle. "
"%s != %s", i, bundle_ice_ufrag, ice_ufrag);
goto fail;
}
if (!bundle_ice_pwd) {
bundle_ice_pwd = ice_pwd;
} else if (g_strcmp0 (bundle_ice_pwd, ice_pwd) == 0) {
g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
"media %u has different ice-ufrag values in bundle. "
"%s != %s", i, bundle_ice_ufrag, ice_ufrag);
goto fail;
}
}
#endif
}
// g_strv_free (group_members);
return TRUE;
fail:
// g_strv_free (group_members);
return FALSE;
}
GstWebRTCRTPTransceiverDirection
_get_direction_from_media (const GstSDPMedia * media)
{
GstWebRTCRTPTransceiverDirection new_dir =
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
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, "sendonly") == 0) {
if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
GST_ERROR ("Multiple direction attributes");
return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
}
new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
} else if (g_strcmp0 (attr->key, "sendrecv") == 0) {
if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
GST_ERROR ("Multiple direction attributes");
return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
}
new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
} else if (g_strcmp0 (attr->key, "recvonly") == 0) {
if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
GST_ERROR ("Multiple direction attributes");
return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
}
new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
} else if (g_strcmp0 (attr->key, "inactive") == 0) {
if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
GST_ERROR ("Multiple direction attributes");
return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
}
new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE;
}
}
return new_dir;
}
#define DIR(val) GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_ ## val
GstWebRTCRTPTransceiverDirection
_intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
GstWebRTCRTPTransceiverDirection answer)
{
if (offer == DIR (SENDONLY) && answer == DIR (SENDRECV))
return DIR (RECVONLY);
if (offer == DIR (SENDONLY) && answer == DIR (RECVONLY))
return DIR (RECVONLY);
if (offer == DIR (RECVONLY) && answer == DIR (SENDRECV))
return DIR (SENDONLY);
if (offer == DIR (RECVONLY) && answer == DIR (SENDONLY))
return DIR (SENDONLY);
if (offer == DIR (SENDRECV) && answer == DIR (SENDRECV))
return DIR (SENDRECV);
if (offer == DIR (SENDRECV) && answer == DIR (SENDONLY))
return DIR (SENDONLY);
if (offer == DIR (SENDRECV) && answer == DIR (RECVONLY))
return DIR (RECVONLY);
return DIR (NONE);
}
void
_media_replace_direction (GstSDPMedia * media,
GstWebRTCRTPTransceiverDirection direction)
{
gchar *dir_str;
int i;
dir_str =
_enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
direction);
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, "sendonly") == 0
|| g_strcmp0 (attr->key, "sendrecv") == 0
|| g_strcmp0 (attr->key, "recvonly") == 0) {
GstSDPAttribute new_attr = { 0, };
GST_TRACE ("replace %s with %s", attr->key, dir_str);
gst_sdp_attribute_set (&new_attr, dir_str, "");
gst_sdp_media_replace_attribute (media, i, &new_attr);
return;
}
}
GST_TRACE ("add %s", dir_str);
gst_sdp_media_add_attribute (media, dir_str, "");
g_free (dir_str);
}
GstWebRTCRTPTransceiverDirection
_get_final_direction (GstWebRTCRTPTransceiverDirection local_dir,
GstWebRTCRTPTransceiverDirection remote_dir)
{
GstWebRTCRTPTransceiverDirection new_dir;
new_dir = DIR (NONE);
switch (local_dir) {
case DIR (INACTIVE):
new_dir = DIR (INACTIVE);
break;
case DIR (SENDONLY):
if (remote_dir == DIR (SENDONLY)) {
GST_ERROR ("remote SDP has the same directionality. "
"This is not legal.");
return DIR (NONE);
} else if (remote_dir == DIR (INACTIVE)) {
new_dir = DIR (INACTIVE);
} else {
new_dir = DIR (SENDONLY);
}
break;
case DIR (RECVONLY):
if (remote_dir == DIR (RECVONLY)) {
GST_ERROR ("remote SDP has the same directionality. "
"This is not legal.");
return DIR (NONE);
} else if (remote_dir == DIR (INACTIVE)) {
new_dir = DIR (INACTIVE);
} else {
new_dir = DIR (RECVONLY);
}
break;
case DIR (SENDRECV):
if (remote_dir == DIR (INACTIVE)) {
new_dir = DIR (INACTIVE);
} else if (remote_dir == DIR (SENDONLY)) {
new_dir = DIR (RECVONLY);
} else if (remote_dir == DIR (RECVONLY)) {
new_dir = DIR (SENDONLY);
} else if (remote_dir == DIR (SENDRECV)) {
new_dir = DIR (SENDRECV);
}
break;
default:
g_assert_not_reached ();
break;
}
if (new_dir == DIR (NONE)) {
GST_ERROR ("Abnormal situation!");
return DIR (NONE);
}
return new_dir;
}
#undef DIR
#define SETUP(val) GST_WEBRTC_DTLS_SETUP_ ## val
GstWebRTCDTLSSetup
_get_dtls_setup_from_media (const GstSDPMedia * media)
{
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, "setup") == 0) {
if (g_strcmp0 (attr->value, "actpass") == 0) {
return SETUP (ACTPASS);
} else if (g_strcmp0 (attr->value, "active") == 0) {
return SETUP (ACTIVE);
} else if (g_strcmp0 (attr->value, "passive") == 0) {
return SETUP (PASSIVE);
} else {
GST_ERROR ("unknown setup value %s", attr->value);
return SETUP (NONE);
}
}
}
GST_LOG ("no setup attribute in media");
return SETUP (NONE);
}
GstWebRTCDTLSSetup
_intersect_dtls_setup (GstWebRTCDTLSSetup offer)
{
switch (offer) {
case SETUP (NONE): /* default is active */
case SETUP (ACTPASS):
case SETUP (PASSIVE):
return SETUP (ACTIVE);
case SETUP (ACTIVE):
return SETUP (PASSIVE);
default:
return SETUP (NONE);
}
}
void
_media_replace_setup (GstSDPMedia * media, GstWebRTCDTLSSetup setup)
{
gchar *setup_str;
int i;
setup_str = _enum_value_to_string (GST_TYPE_WEBRTC_DTLS_SETUP, setup);
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, "setup") == 0) {
GstSDPAttribute new_attr = { 0, };
GST_TRACE ("replace setup:%s with setup:%s", attr->value, setup_str);
gst_sdp_attribute_set (&new_attr, "setup", setup_str);
gst_sdp_media_replace_attribute (media, i, &new_attr);
return;
}
}
GST_TRACE ("add setup:%s", setup_str);
gst_sdp_media_add_attribute (media, "setup", setup_str);
g_free (setup_str);
}
GstWebRTCDTLSSetup
_get_final_setup (GstWebRTCDTLSSetup local_setup,
GstWebRTCDTLSSetup remote_setup)
{
GstWebRTCDTLSSetup new_setup;
new_setup = SETUP (NONE);
switch (local_setup) {
case SETUP (NONE):
/* someone's done a bad job of mangling the SDP. or bugs */
g_critical ("Received a locally generated sdp without a parseable "
"\'a=setup\' line. This indicates a bug somewhere. Bailing");
return SETUP (NONE);
case SETUP (ACTIVE):
if (remote_setup == SETUP (ACTIVE)) {
GST_ERROR ("remote SDP has the same "
"\'a=setup:active\' attribute. This is not legal");
return SETUP (NONE);
}
new_setup = SETUP (ACTIVE);
break;
case SETUP (PASSIVE):
if (remote_setup == SETUP (PASSIVE)) {
GST_ERROR ("remote SDP has the same "
"\'a=setup:passive\' attribute. This is not legal");
return SETUP (NONE);
}
new_setup = SETUP (PASSIVE);
break;
case SETUP (ACTPASS):
if (remote_setup == SETUP (ACTPASS)) {
GST_ERROR ("remote SDP has the same "
"\'a=setup:actpass\' attribute. This is not legal");
return SETUP (NONE);
}
if (remote_setup == SETUP (ACTIVE))
new_setup = SETUP (PASSIVE);
else if (remote_setup == SETUP (PASSIVE))
new_setup = SETUP (ACTIVE);
else if (remote_setup == SETUP (NONE)) {
/* XXX: what to do here? */
GST_WARNING ("unspecified situation. local: "
"\'a=setup:actpass\' remote: none/unparseable");
new_setup = SETUP (ACTIVE);
}
break;
default:
g_assert_not_reached ();
return SETUP (NONE);
}
if (new_setup == SETUP (NONE)) {
GST_ERROR ("Abnormal situation!");
return SETUP (NONE);
}
return new_setup;
}
#undef SETUP
gchar *
_generate_fingerprint_from_certificate (gchar * certificate,
GChecksumType checksum_type)
{
gchar **lines, *line;
guchar *tmp, *decoded, *digest;
GChecksum *checksum;
GString *fingerprint;
gsize decoded_length, digest_size;
gint state = 0;
guint save = 0;
int i;
g_return_val_if_fail (certificate != NULL, NULL);
/* 1. decode the certificate removing newlines and the certificate header
* and footer */
decoded = tmp = g_new0 (guchar, (strlen (certificate) / 4) * 3 + 3);
lines = g_strsplit (certificate, "\n", 0);
for (i = 0, line = lines[i]; line; line = lines[++i]) {
if (line[0] && !g_str_has_prefix (line, "-----"))
tmp += g_base64_decode_step (line, strlen (line), tmp, &state, &save);
}
g_strfreev (lines);
decoded_length = tmp - decoded;
/* 2. compute a checksum of the decoded certificate */
checksum = g_checksum_new (checksum_type);
digest_size = g_checksum_type_get_length (checksum_type);
digest = g_new (guint8, digest_size);
g_checksum_update (checksum, decoded, decoded_length);
g_checksum_get_digest (checksum, digest, &digest_size);
g_free (decoded);
/* 3. hex encode the checksum separated with ':'s */
fingerprint = g_string_new (NULL);
for (i = 0; i < digest_size; i++) {
if (i)
g_string_append (fingerprint, ":");
g_string_append_printf (fingerprint, "%02X", digest[i]);
}
g_free (digest);
g_checksum_free (checksum);
return g_string_free (fingerprint, FALSE);
}
#define DEFAULT_ICE_UFRAG_LEN 32
#define DEFAULT_ICE_PASSWORD_LEN 32
static const gchar *ice_credential_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/";
void
_generate_ice_credentials (gchar ** ufrag, gchar ** password)
{
int i;
*ufrag = g_malloc0 (DEFAULT_ICE_UFRAG_LEN + 1);
for (i = 0; i < DEFAULT_ICE_UFRAG_LEN; i++)
(*ufrag)[i] =
ice_credential_chars[g_random_int_range (0,
strlen (ice_credential_chars))];
*password = g_malloc0 (DEFAULT_ICE_PASSWORD_LEN + 1);
for (i = 0; i < DEFAULT_ICE_PASSWORD_LEN; i++)
(*password)[i] =
ice_credential_chars[g_random_int_range (0,
strlen (ice_credential_chars))];
}

80
ext/webrtc/webrtcsdp.h Normal file
View file

@ -0,0 +1,80 @@
/* 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_SDP_H__
#define __WEBRTC_SDP_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc.h>
#include "fwd.h"
G_BEGIN_DECLS
typedef enum
{
SDP_NONE,
SDP_LOCAL,
SDP_REMOTE,
} SDPSource;
G_GNUC_INTERNAL
const gchar * _sdp_source_to_string (SDPSource source);
G_GNUC_INTERNAL
gboolean validate_sdp (GstWebRTCBin * webrtc,
SDPSource source,
GstWebRTCSessionDescription * sdp,
GError ** error);
G_GNUC_INTERNAL
GstWebRTCRTPTransceiverDirection _get_direction_from_media (const GstSDPMedia * media);
G_GNUC_INTERNAL
GstWebRTCRTPTransceiverDirection _intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
GstWebRTCRTPTransceiverDirection answer);
G_GNUC_INTERNAL
void _media_replace_direction (GstSDPMedia * media,
GstWebRTCRTPTransceiverDirection direction);
G_GNUC_INTERNAL
GstWebRTCRTPTransceiverDirection _get_final_direction (GstWebRTCRTPTransceiverDirection local_dir,
GstWebRTCRTPTransceiverDirection remote_dir);
G_GNUC_INTERNAL
GstWebRTCDTLSSetup _get_dtls_setup_from_media (const GstSDPMedia * media);
G_GNUC_INTERNAL
GstWebRTCDTLSSetup _intersect_dtls_setup (GstWebRTCDTLSSetup offer);
G_GNUC_INTERNAL
void _media_replace_setup (GstSDPMedia * media,
GstWebRTCDTLSSetup setup);
G_GNUC_INTERNAL
GstWebRTCDTLSSetup _get_final_setup (GstWebRTCDTLSSetup local_setup,
GstWebRTCDTLSSetup remote_setup);
G_GNUC_INTERNAL
gchar * _generate_fingerprint_from_certificate (gchar * certificate,
GChecksumType checksum_type);
G_GNUC_INTERNAL
void _generate_ice_credentials (gchar ** ufrag,
gchar ** password);
G_GNUC_INTERNAL
gboolean _media_has_attribute_key (const GstSDPMedia * media,
const gchar * key);
#endif /* __WEBRTC_UTILS_H__ */

View file

@ -0,0 +1,149 @@
/* 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 "utils.h"
#include "webrtctransceiver.h"
#define webrtc_transceiver_parent_class parent_class
G_DEFINE_TYPE (WebRTCTransceiver, webrtc_transceiver,
GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
enum
{
PROP_0,
PROP_WEBRTC,
};
void
webrtc_transceiver_set_transport (WebRTCTransceiver * trans,
TransportStream * stream)
{
GstWebRTCRTPTransceiver *rtp_trans;
g_return_if_fail (WEBRTC_IS_TRANSCEIVER (trans));
rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
gst_object_replace ((GstObject **) & trans->stream, (GstObject *) stream);
if (rtp_trans->sender)
gst_object_replace ((GstObject **) & rtp_trans->sender->transport,
(GstObject *) stream->transport);
if (rtp_trans->receiver)
gst_object_replace ((GstObject **) & rtp_trans->receiver->transport,
(GstObject *) stream->transport);
if (rtp_trans->sender)
gst_object_replace ((GstObject **) & rtp_trans->sender->rtcp_transport,
(GstObject *) stream->rtcp_transport);
if (rtp_trans->receiver)
gst_object_replace ((GstObject **) & rtp_trans->receiver->rtcp_transport,
(GstObject *) stream->rtcp_transport);
}
static void
webrtc_transceiver_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (object);
switch (prop_id) {
case PROP_WEBRTC:
gst_object_set_parent (GST_OBJECT (trans), g_value_get_object (value));
break;
}
GST_OBJECT_LOCK (trans);
switch (prop_id) {
case PROP_WEBRTC:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (trans);
}
static void
webrtc_transceiver_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (object);
GST_OBJECT_LOCK (trans);
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (trans);
}
static void
webrtc_transceiver_finalize (GObject * object)
{
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (object);
if (trans->stream)
gst_object_unref (trans->stream);
trans->stream = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
webrtc_transceiver_class_init (WebRTCTransceiverClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = webrtc_transceiver_get_property;
gobject_class->set_property = webrtc_transceiver_set_property;
gobject_class->finalize = webrtc_transceiver_finalize;
/* some acrobatics are required to set the parent before _constructed()
* has been called */
g_object_class_install_property (gobject_class,
PROP_WEBRTC,
g_param_spec_object ("webrtc", "Parent webrtcbin",
"Parent webrtcbin",
GST_TYPE_WEBRTC_BIN,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static void
webrtc_transceiver_init (WebRTCTransceiver * trans)
{
}
WebRTCTransceiver *
webrtc_transceiver_new (GstWebRTCBin * webrtc, GstWebRTCRTPSender * sender,
GstWebRTCRTPReceiver * receiver)
{
WebRTCTransceiver *trans;
trans = g_object_new (webrtc_transceiver_get_type (), "sender", sender,
"receiver", receiver, "webrtc", webrtc, NULL);
return trans;
}

View file

@ -0,0 +1,57 @@
/* 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_TRANSCEIVER_H__
#define __WEBRTC_TRANSCEIVER_H__
#include "fwd.h"
#include <gst/webrtc/rtptransceiver.h>
#include "transportstream.h"
G_BEGIN_DECLS
GType webrtc_transceiver_get_type(void);
#define WEBRTC_TYPE_TRANSCEIVER (webrtc_transceiver_get_type())
#define WEBRTC_TRANSCEIVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),WEBRTC_TYPE_TRANSCEIVER,WebRTCTransceiver))
#define WEBRTC_IS_TRANSCEIVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),WEBRTC_TYPE_TRANSCEIVER))
#define WEBRTC_TRANSCEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,WEBRTC_TYPE_TRANSCEIVER,WebRTCTransceiverClass))
#define WEBRTC_TRANSCEIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,WEBRTC_TYPE_TRANSCEIVER,WebRTCTransceiverClass))
struct _WebRTCTransceiver
{
GstWebRTCRTPTransceiver parent;
TransportStream *stream;
};
struct _WebRTCTransceiverClass
{
GstWebRTCRTPTransceiverClass parent_class;
};
WebRTCTransceiver * webrtc_transceiver_new (GstWebRTCBin * webrtc,
GstWebRTCRTPSender * sender,
GstWebRTCRTPReceiver * receiver);
void webrtc_transceiver_set_transport (WebRTCTransceiver * trans,
TransportStream * stream);
G_END_DECLS
#endif /* __WEBRTC_TRANSCEIVER_H__ */

View file

@ -7,12 +7,12 @@ OPENCV_DIR=opencv
endif
SUBDIRS = uridownloader adaptivedemux interfaces basecamerabinsrc codecparsers \
insertbin mpegts video audio player isoff $(WAYLAND_DIR) \
insertbin mpegts video audio player isoff webrtc $(WAYLAND_DIR) \
$(OPENCV_DIR)
noinst_HEADERS = gst-i18n-plugin.h gettext.h glib-compat-private.h
DIST_SUBDIRS = uridownloader adaptivedemux interfaces basecamerabinsrc \
codecparsers insertbin mpegts wayland opencv video audio player isoff
codecparsers insertbin mpegts wayland opencv video audio player isoff webrtc
adaptivedemux: uridownloader

View file

@ -12,3 +12,4 @@ subdir('opencv')
subdir('player')
subdir('video')
subdir('wayland')
subdir('webrtc')

View file

@ -0,0 +1,54 @@
lib_LTLIBRARIES = libgstwebrtc-@GST_API_VERSION@.la
glib_enum_headers = dtlstransport.h icetransport.h rtptransceiver.h webrtc_fwd.h
glib_enum_define = GST_WEBRTC
glib_gen_prefix = gst_webrtc
glib_gen_basename = webrtc
glib_gen_decl_banner=GST_EXPORT
built_sources = webrtc-enumtypes.c
built_headers = webrtc-enumtypes.h
BUILT_SOURCES = $(built_sources) $(built_headers)
CLEANFILES = $(BUILT_SOURCES)
libgstwebrtc_@GST_API_VERSION@_la_SOURCES = \
dtlstransport.c \
icetransport.c \
rtcsessiondescription.c \
rtpreceiver.c \
rtpsender.c \
rtptransceiver.c
nodist_libgstwebrtc_@GST_API_VERSION@_la_SOURCES = $(built_sources)
libgstwebrtc_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/webrtc
libgstwebrtc_@GST_API_VERSION@include_HEADERS = \
dtlstransport.h \
icetransport.h \
rtcsessiondescription.h \
rtpreceiver.h \
rtpsender.h \
rtptransceiver.h \
webrtc_fwd.h \
webrtc.h
nodist_libgstwebrtc_@GST_API_VERSION@include_HEADERS = $(built_headers)
libgstwebrtc_@GST_API_VERSION@_la_CFLAGS = \
-I$(top_builddir)/gst-libs \
-I$(top_srcdir)/gst-libs \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(GST_SDP_CFLAGS)
libgstwebrtc_@GST_API_VERSION@_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
$(GST_SDP_LIBS)
libgstwebrtc_@GST_API_VERSION@_la_LDFLAGS = \
$(GST_LIB_LDFLAGS) \
$(GST_ALL_LDFLAGS) \
$(GST_LT_LDFLAGS)
include $(top_srcdir)/common/gst-glib-gen.mak

View file

@ -0,0 +1,238 @@
/* 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.
*/
/**
* SECTION:gstwebrtc-dtlstransport
* @short_description: RTCDtlsTransport object
* @title: GstWebRTCDTLSTransport
* @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPReceiver, #GstWebRTCICETransport
*
* <ulink url="https://www.w3.org/TR/webrtc/#rtcdtlstransport">https://www.w3.org/TR/webrtc/#rtcdtlstransport</ulink>
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "dtlstransport.h"
#define GST_CAT_DEFAULT gst_webrtc_dtls_transport_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_dtls_transport_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCDTLSTransport, gst_webrtc_dtls_transport,
GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_dtls_transport_debug,
"dtlstransport", 0, "dtlstransport");
);
enum
{
SIGNAL_0,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_SESSION_ID,
PROP_TRANSPORT,
PROP_STATE,
PROP_CLIENT,
PROP_CERTIFICATE,
PROP_REMOTE_CERTIFICATE,
PROP_RTCP,
};
void
gst_webrtc_dtls_transport_set_transport (GstWebRTCDTLSTransport * transport,
GstWebRTCICETransport * ice)
{
g_return_if_fail (GST_IS_WEBRTC_DTLS_TRANSPORT (transport));
g_return_if_fail (GST_IS_WEBRTC_ICE_TRANSPORT (ice));
gst_object_replace ((GstObject **) & transport->transport, GST_OBJECT (ice));
}
static void
gst_webrtc_dtls_transport_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCDTLSTransport *webrtc = GST_WEBRTC_DTLS_TRANSPORT (object);
switch (prop_id) {
case PROP_SESSION_ID:
webrtc->session_id = g_value_get_uint (value);
break;
case PROP_CLIENT:
g_object_set_property (G_OBJECT (webrtc->dtlssrtpenc), "is-client",
value);
gst_element_set_locked_state (webrtc->dtlssrtpenc, FALSE);
gst_element_sync_state_with_parent (webrtc->dtlssrtpenc);
break;
case PROP_CERTIFICATE:
g_object_set_property (G_OBJECT (webrtc->dtlssrtpdec), "pem", value);
break;
case PROP_RTCP:
webrtc->is_rtcp = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_dtls_transport_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCDTLSTransport *webrtc = GST_WEBRTC_DTLS_TRANSPORT (object);
switch (prop_id) {
case PROP_SESSION_ID:
g_value_set_uint (value, webrtc->session_id);
break;
case PROP_TRANSPORT:
g_value_set_object (value, webrtc->transport);
break;
case PROP_STATE:
g_value_set_enum (value, webrtc->state);
break;
case PROP_CLIENT:
g_object_get_property (G_OBJECT (webrtc->dtlssrtpenc), "is-client",
value);
break;
case PROP_CERTIFICATE:
g_object_get_property (G_OBJECT (webrtc->dtlssrtpdec), "pem", value);
break;
case PROP_REMOTE_CERTIFICATE:
g_object_get_property (G_OBJECT (webrtc->dtlssrtpdec), "peer-pem", value);
break;
case PROP_RTCP:
g_value_set_boolean (value, webrtc->is_rtcp);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_dtls_transport_finalize (GObject * object)
{
GstWebRTCDTLSTransport *webrtc = GST_WEBRTC_DTLS_TRANSPORT (object);
if (webrtc->transport) {
gst_object_unref (webrtc->transport);
}
webrtc->transport = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_webrtc_dtls_transport_constructed (GObject * object)
{
GstWebRTCDTLSTransport *webrtc = GST_WEBRTC_DTLS_TRANSPORT (object);
gchar *connection_id;
/* XXX: this may collide with another connection_id however this is only a
* problem if multiple dtls element sets are being used within the same
* process */
connection_id = g_strdup_printf ("%s_%u_%u", webrtc->is_rtcp ? "rtcp" : "rtp",
webrtc->session_id, g_random_int ());
webrtc->dtlssrtpenc = gst_element_factory_make ("dtlssrtpenc", NULL);
g_object_set (webrtc->dtlssrtpenc, "connection-id", connection_id,
"is-client", webrtc->client, NULL);
webrtc->dtlssrtpdec = gst_element_factory_make ("dtlssrtpdec", NULL);
g_object_set (webrtc->dtlssrtpdec, "connection-id", connection_id, NULL);
g_free (connection_id);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
gst_webrtc_dtls_transport_class_init (GstWebRTCDTLSTransportClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->constructed = gst_webrtc_dtls_transport_constructed;
gobject_class->get_property = gst_webrtc_dtls_transport_get_property;
gobject_class->set_property = gst_webrtc_dtls_transport_set_property;
gobject_class->finalize = gst_webrtc_dtls_transport_finalize;
g_object_class_install_property (gobject_class,
PROP_SESSION_ID,
g_param_spec_uint ("session-id", "Session ID",
"Unique session ID", 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_TRANSPORT,
g_param_spec_object ("transport", "ICE transport",
"ICE transport used by this dtls transport",
GST_TYPE_WEBRTC_ICE_TRANSPORT,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/* FIXME: implement */
g_object_class_install_property (gobject_class,
PROP_STATE,
g_param_spec_enum ("state", "DTLS state",
"State of the DTLS transport",
GST_TYPE_WEBRTC_DTLS_TRANSPORT_STATE,
GST_WEBRTC_DTLS_TRANSPORT_STATE_NEW,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_CLIENT,
g_param_spec_boolean ("client", "DTLS client",
"Are we the client in the DTLS handshake?", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_CERTIFICATE,
g_param_spec_string ("certificate", "DTLS certificate",
"DTLS certificate", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_REMOTE_CERTIFICATE,
g_param_spec_string ("remote-certificate", "Remote DTLS certificate",
"Remote DTLS certificate", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_RTCP,
g_param_spec_boolean ("rtcp", "RTCP",
"The transport is being used solely for RTCP", FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_dtls_transport_init (GstWebRTCDTLSTransport * webrtc)
{
}
GstWebRTCDTLSTransport *
gst_webrtc_dtls_transport_new (guint session_id, gboolean is_rtcp)
{
return g_object_new (GST_TYPE_WEBRTC_DTLS_TRANSPORT, "session-id", session_id,
"rtcp", is_rtcp, NULL);
}

View file

@ -0,0 +1,70 @@
/* 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 __GST_WEBRTC_DTLS_TRANSPORT_H__
#define __GST_WEBRTC_DTLS_TRANSPORT_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc_fwd.h>
#include <gst/webrtc/icetransport.h>
G_BEGIN_DECLS
GST_EXPORT
GType gst_webrtc_dtls_transport_get_type(void);
#define GST_TYPE_WEBRTC_DTLS_TRANSPORT (gst_webrtc_dtls_transport_get_type())
#define GST_WEBRTC_DTLS_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_DTLS_TRANSPORT,GstWebRTCDTLSTransport))
#define GST_IS_WEBRTC_DTLS_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_DTLS_TRANSPORT))
#define GST_WEBRTC_DTLS_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_DTLS_TRANSPORT,GstWebRTCDTLSTransportClass))
#define GST_IS_WEBRTC_DTLS_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_DTLS_TRANSPORT))
#define GST_WEBRTC_DTLS_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_DTLS_TRANSPORT,GstWebRTCDTLSTransportClass))
struct _GstWebRTCDTLSTransport
{
GstObject parent;
GstWebRTCICETransport *transport;
GstWebRTCDTLSTransportState state;
gboolean is_rtcp;
gboolean client;
guint session_id;
GstElement *dtlssrtpenc;
GstElement *dtlssrtpdec;
gpointer _padding[GST_PADDING];
};
struct _GstWebRTCDTLSTransportClass
{
GstBinClass parent_class;
gpointer _padding[GST_PADDING];
};
GST_EXPORT
GstWebRTCDTLSTransport * gst_webrtc_dtls_transport_new (guint session_id, gboolean rtcp);
GST_EXPORT
void gst_webrtc_dtls_transport_set_transport (GstWebRTCDTLSTransport * transport,
GstWebRTCICETransport * ice);
G_END_DECLS
#endif /* __GST_WEBRTC_DTLS_TRANSPORT_H__ */

View file

@ -0,0 +1,204 @@
/* 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.
*/
/**
* SECTION:gstwebrtc-icetransport
* @short_description: RTCIceTransport object
* @title: GstWebRTCICETransport
* @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPReceiver, #GstWebRTCDTLSTransport
*
* <ulink url="https://www.w3.org/TR/webrtc/#rtcicetransport">https://www.w3.org/TR/webrtc/#rtcicetransport</ulink>
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "icetransport.h"
#include "webrtc-enumtypes.h"
#define GST_CAT_DEFAULT gst_webrtc_ice_transport_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_ice_transport_parent_class parent_class
/* We would inherit from GstBin however when combined with the dtls transport,
* this causes loops in the graph. */
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCICETransport,
gst_webrtc_ice_transport, GST_TYPE_OBJECT,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_ice_transport_debug,
"webrtcicetransport", 0, "webrtcicetransport"););
enum
{
SIGNAL_0,
ON_SELECTED_CANDIDATE_PAIR_CHANGE_SIGNAL,
ON_NEW_CANDIDATE_SIGNAL,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_COMPONENT,
PROP_STATE,
PROP_GATHERING_STATE,
};
static guint gst_webrtc_ice_transport_signals[LAST_SIGNAL] = { 0 };
void
gst_webrtc_ice_transport_connection_state_change (GstWebRTCICETransport * ice,
GstWebRTCICEConnectionState new_state)
{
ice->state = new_state;
g_object_notify (G_OBJECT (ice), "state");
}
void
gst_webrtc_ice_transport_gathering_state_change (GstWebRTCICETransport * ice,
GstWebRTCICEGatheringState new_state)
{
ice->gathering_state = new_state;
g_object_notify (G_OBJECT (ice), "gathering-state");
}
void
gst_webrtc_ice_transport_selected_pair_change (GstWebRTCICETransport * ice)
{
g_signal_emit (ice,
gst_webrtc_ice_transport_signals
[ON_SELECTED_CANDIDATE_PAIR_CHANGE_SIGNAL], 0);
}
void
gst_webrtc_ice_transport_new_candidate (GstWebRTCICETransport * ice,
guint stream_id, GstWebRTCICEComponent component, gchar * attr)
{
g_signal_emit (ice, gst_webrtc_ice_transport_signals[ON_NEW_CANDIDATE_SIGNAL],
stream_id, component, attr);
}
static void
gst_webrtc_ice_transport_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCICETransport *webrtc = GST_WEBRTC_ICE_TRANSPORT (object);
switch (prop_id) {
case PROP_COMPONENT:
webrtc->component = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_ice_transport_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCICETransport *webrtc = GST_WEBRTC_ICE_TRANSPORT (object);
switch (prop_id) {
case PROP_COMPONENT:
g_value_set_enum (value, webrtc->component);
break;
case PROP_STATE:
g_value_set_enum (value, webrtc->state);
break;
case PROP_GATHERING_STATE:
g_value_set_enum (value, webrtc->gathering_state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_ice_transport_finalize (GObject * object)
{
// GstWebRTCICETransport *webrtc = GST_WEBRTC_ICE_TRANSPORT (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_webrtc_ice_transport_constructed (GObject * object)
{
// GstWebRTCICETransport *webrtc = GST_WEBRTC_ICE_TRANSPORT (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
gst_webrtc_ice_transport_class_init (GstWebRTCICETransportClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->constructed = gst_webrtc_ice_transport_constructed;
gobject_class->get_property = gst_webrtc_ice_transport_get_property;
gobject_class->set_property = gst_webrtc_ice_transport_set_property;
gobject_class->finalize = gst_webrtc_ice_transport_finalize;
g_object_class_install_property (gobject_class,
PROP_COMPONENT,
g_param_spec_enum ("component",
"ICE component", "The ICE component of this transport",
GST_TYPE_WEBRTC_ICE_COMPONENT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_STATE,
g_param_spec_enum ("state",
"ICE connection state", "The ICE connection state of this transport",
GST_TYPE_WEBRTC_ICE_CONNECTION_STATE, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_GATHERING_STATE,
g_param_spec_enum ("gathering-state",
"ICE gathering state", "The ICE gathering state of this transport",
GST_TYPE_WEBRTC_ICE_GATHERING_STATE, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GstWebRTC::on-selected_candidate-pair-change:
* @object: the #GstWebRTCICETransport
*/
gst_webrtc_ice_transport_signals[ON_SELECTED_CANDIDATE_PAIR_CHANGE_SIGNAL] =
g_signal_new ("on-selected-candidate-pair-change",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_generic, G_TYPE_NONE, 0);
/**
* GstWebRTC::on-new-candidate:
* @object: the #GstWebRTCICETransport
*/
gst_webrtc_ice_transport_signals[ON_NEW_CANDIDATE_SIGNAL] =
g_signal_new ("on-new-candidate",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING);
}
static void
gst_webrtc_ice_transport_init (GstWebRTCICETransport * webrtc)
{
}

View file

@ -0,0 +1,76 @@
/* 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 __GST_WEBRTC_ICE_TRANSPORT_H__
#define __GST_WEBRTC_ICE_TRANSPORT_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc_fwd.h>
G_BEGIN_DECLS
GST_EXPORT
GType gst_webrtc_ice_transport_get_type(void);
#define GST_TYPE_WEBRTC_ICE_TRANSPORT (gst_webrtc_ice_transport_get_type())
#define GST_WEBRTC_ICE_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_ICE_TRANSPORT,GstWebRTCICETransport))
#define GST_IS_WEBRTC_ICE_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_ICE_TRANSPORT))
#define GST_WEBRTC_ICE_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_ICE_TRANSPORT,GstWebRTCICETransportClass))
#define GST_IS_WEBRTC_ICE_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_ICE_TRANSPORT))
#define GST_WEBRTC_ICE_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_ICE_TRANSPORT,GstWebRTCICETransportClass))
struct _GstWebRTCICETransport
{
GstObject parent;
GstWebRTCIceRole role;
GstWebRTCICEComponent component;
GstWebRTCICEConnectionState state;
GstWebRTCICEGatheringState gathering_state;
/* Filled by subclasses */
GstElement *src;
GstElement *sink;
gpointer _padding[GST_PADDING];
};
struct _GstWebRTCICETransportClass
{
GstBinClass parent_class;
gboolean (*gather_candidates) (GstWebRTCICETransport * transport);
gpointer _padding[GST_PADDING];
};
GST_EXPORT
void gst_webrtc_ice_transport_connection_state_change (GstWebRTCICETransport * ice,
GstWebRTCICEConnectionState new_state);
GST_EXPORT
void gst_webrtc_ice_transport_gathering_state_change (GstWebRTCICETransport * ice,
GstWebRTCICEGatheringState new_state);
GST_EXPORT
void gst_webrtc_ice_transport_selected_pair_change (GstWebRTCICETransport * ice);
GST_EXPORT
void gst_webrtc_ice_transport_new_candidate (GstWebRTCICETransport * ice, guint stream_id, GstWebRTCICEComponent component, gchar * attr);
G_END_DECLS
#endif /* __GST_WEBRTC_ICE_TRANSPORT_H__ */

View file

@ -0,0 +1,59 @@
webrtc_sources = [
'dtlstransport.c',
'icetransport.c',
'rtcsessiondescription.c',
'rtpreceiver.c',
'rtpsender.c',
'rtptransceiver.c',
]
webrtc_headers = [
'dtlstransport.h',
'icetransport.h',
'rtcsessiondescription.h',
'rtpreceiver.h',
'rtpsender.h',
'rtptransceiver.h',
'webrtc_fwd.h',
'webrtc.h',
]
webrtc_enumtypes_headers = [
'dtlstransport.h',
'icetransport.h',
'rtptransceiver.h',
'webrtc_fwd.h',
]
mkenums = find_program('webrtc_mkenum.py')
gstwebrtc_h = custom_target('gstwebrtcenum_h',
output : 'webrtc-enumtypes.h',
input : webrtc_enumtypes_headers,
install : true,
install_dir : 'include/gstreamer-1.0/gst/webrtc/',
command : [mkenums, glib_mkenums, '@OUTPUT@', '@INPUT@'])
gstwebrtc_c = custom_target('gstwebrtcenum_c',
output : 'webrtc-enumtypes.c',
input : webrtc_enumtypes_headers,
depends : [gstwebrtc_h],
command : [mkenums, glib_mkenums, '@OUTPUT@', '@INPUT@'])
webrtc_gen_sources = [gstwebrtc_h]
gstwebrtc_dependencies = [gstbase_dep, gstpbutils_dep, gstsdp_dep]
gstwebrtc = library('gstwebrtc-' + api_version,
webrtc_sources, gstwebrtc_c, gstwebrtc_h,
c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
include_directories : [configinc, libsinc],
version : libversion,
soversion : soversion,
install : true,
dependencies : gstwebrtc_dependencies,
)
install_headers(webrtc_headers, subdir : 'gstreamer-1.0/gst/webrtc')
gstwebrtc_dep = declare_dependency(link_with: gstwebrtc,
include_directories : libsinc,
dependencies: gstwebrtc_dependencies)

View file

@ -0,0 +1,123 @@
/* 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.
*/
/**
* SECTION:gstwebrtc-sessiondescription
* @short_description: RTCSessionDescription object
* @title: GstWebRTCSessionDescription
*
* <ulink url="https://www.w3.org/TR/webrtc/#rtcsessiondescription-class">https://www.w3.org/TR/webrtc/#rtcsessiondescription-class</ulink>
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "rtcsessiondescription.h"
#define GST_CAT_DEFAULT gst_webrtc_peerconnection_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
/**
* gst_webrtc_sdp_type_to_string:
* @type: a #GstWebRTCSDPType
*
* Returns: the string representation of @type or "unknown" when @type is not
* recognized.
*/
const gchar *
gst_webrtc_sdp_type_to_string (GstWebRTCSDPType type)
{
switch (type) {
case GST_WEBRTC_SDP_TYPE_OFFER:
return "offer";
case GST_WEBRTC_SDP_TYPE_PRANSWER:
return "pranswer";
case GST_WEBRTC_SDP_TYPE_ANSWER:
return "answer";
case GST_WEBRTC_SDP_TYPE_ROLLBACK:
return "rollback";
default:
return "unknown";
}
}
/**
* gst_webrtc_session_description_copy:
* @src: (transfer none): a #GstWebRTCSessionDescription
*
* Returns: (transfer full): a new copy of @src
*/
GstWebRTCSessionDescription *
gst_webrtc_session_description_copy (const GstWebRTCSessionDescription * src)
{
GstWebRTCSessionDescription *ret;
if (!src)
return NULL;
ret = g_new0 (GstWebRTCSessionDescription, 1);
ret->type = src->type;
gst_sdp_message_copy (src->sdp, &ret->sdp);
return ret;
}
/**
* gst_webrtc_session_description_free:
* @desc: (transfer full): a #GstWebRTCSessionDescription
*
* Free @desc and all associated resources
*/
void
gst_webrtc_session_description_free (GstWebRTCSessionDescription * desc)
{
g_return_if_fail (desc != NULL);
gst_sdp_message_free (desc->sdp);
g_free (desc);
}
/**
* gst_webrtc_session_description_new:
* @type: a #GstWebRTCSDPType
* @sdp: a #GstSDPMessage
*
* Returns: (transfer full): a new #GstWebRTCSessionDescription from @type
* and @sdp
*/
GstWebRTCSessionDescription *
gst_webrtc_session_description_new (GstWebRTCSDPType type, GstSDPMessage * sdp)
{
GstWebRTCSessionDescription *ret;
ret = g_new0 (GstWebRTCSessionDescription, 1);
ret->type = type;
ret->sdp = sdp;
return ret;
}
G_DEFINE_BOXED_TYPE_WITH_CODE (GstWebRTCSessionDescription,
gst_webrtc_session_description, gst_webrtc_session_description_copy,
gst_webrtc_session_description_free,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_peerconnection_debug,
"webrtcsessiondescription", 0, "webrtcsessiondescription"));

View file

@ -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 __GST_WEBRTC_SESSION_DESCRIPTION_H__
#define __GST_WEBRTC_SESSION_DESCRIPTION_H__
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc_fwd.h>
G_BEGIN_DECLS
GST_EXPORT
const gchar * gst_webrtc_sdp_type_to_string (GstWebRTCSDPType type);
#define GST_TYPE_WEBRTC_SESSION_DESCRIPTION (gst_webrtc_session_description_get_type())
GST_EXPORT
GType gst_webrtc_session_description_get_type (void);
/**
* GstWebRTCSessionDescription:
* type: the #GstWebRTCSDPType of the description
* sdp: the #GstSDPMessage of the description
*
* See <ulink url="https://www.w3.org/TR/webrtc/#rtcsessiondescription-class">https://www.w3.org/TR/webrtc/#rtcsessiondescription-class</ulink>
*/
struct _GstWebRTCSessionDescription
{
GstWebRTCSDPType type;
GstSDPMessage *sdp;
};
GST_EXPORT
GstWebRTCSessionDescription * gst_webrtc_session_description_new (GstWebRTCSDPType type, GstSDPMessage *sdp);
GST_EXPORT
GstWebRTCSessionDescription * gst_webrtc_session_description_copy (const GstWebRTCSessionDescription * src);
GST_EXPORT
void gst_webrtc_session_description_free (GstWebRTCSessionDescription * desc);
G_END_DECLS
#endif /* __GST_WEBRTC_PEERCONNECTION_H__ */

View file

@ -0,0 +1,135 @@
/* 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.
*/
/**
* SECTION:gstwebrtc-receiver
* @short_description: RTCRtpReceiver object
* @title: GstWebRTCRTPReceiver
* @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPTransceiver
*
* <ulink url="https://www.w3.org/TR/webrtc/#rtcrtpreceiver-interface">https://www.w3.org/TR/webrtc/#rtcrtpreceiver-interface</ulink>
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "rtpreceiver.h"
#define GST_CAT_DEFAULT gst_webrtc_rtp_receiver_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_rtp_receiver_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCRTPReceiver, gst_webrtc_rtp_receiver,
GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_rtp_receiver_debug,
"webrtcreceiver", 0, "webrtcreceiver"););
enum
{
SIGNAL_0,
LAST_SIGNAL,
};
enum
{
PROP_0,
};
//static guint gst_webrtc_rtp_receiver_signals[LAST_SIGNAL] = { 0 };
void
gst_webrtc_rtp_receiver_set_transport (GstWebRTCRTPReceiver * receiver,
GstWebRTCDTLSTransport * transport)
{
g_return_if_fail (GST_IS_WEBRTC_RTP_RECEIVER (receiver));
g_return_if_fail (GST_IS_WEBRTC_DTLS_TRANSPORT (transport));
gst_object_replace ((GstObject **) & receiver->transport,
GST_OBJECT (transport));
}
void
gst_webrtc_rtp_receiver_set_rtcp_transport (GstWebRTCRTPReceiver * receiver,
GstWebRTCDTLSTransport * transport)
{
g_return_if_fail (GST_IS_WEBRTC_RTP_RECEIVER (receiver));
g_return_if_fail (GST_IS_WEBRTC_DTLS_TRANSPORT (transport));
gst_object_replace ((GstObject **) & receiver->rtcp_transport,
GST_OBJECT (transport));
}
static void
gst_webrtc_rtp_receiver_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_rtp_receiver_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_rtp_receiver_finalize (GObject * object)
{
GstWebRTCRTPReceiver *webrtc = GST_WEBRTC_RTP_RECEIVER (object);
if (webrtc->transport)
gst_object_unref (webrtc->transport);
webrtc->transport = NULL;
if (webrtc->rtcp_transport)
gst_object_unref (webrtc->rtcp_transport);
webrtc->rtcp_transport = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_webrtc_rtp_receiver_class_init (GstWebRTCRTPReceiverClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = gst_webrtc_rtp_receiver_get_property;
gobject_class->set_property = gst_webrtc_rtp_receiver_set_property;
gobject_class->finalize = gst_webrtc_rtp_receiver_finalize;
}
static void
gst_webrtc_rtp_receiver_init (GstWebRTCRTPReceiver * webrtc)
{
}
GstWebRTCRTPReceiver *
gst_webrtc_rtp_receiver_new (void)
{
return g_object_new (GST_TYPE_WEBRTC_RTP_RECEIVER, NULL);
}

View file

@ -0,0 +1,76 @@
/* 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 __GST_WEBRTC_RTP_RECEIVER_H__
#define __GST_WEBRTC_RTP_RECEIVER_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc_fwd.h>
#include <gst/webrtc/dtlstransport.h>
G_BEGIN_DECLS
GST_EXPORT
GType gst_webrtc_rtp_receiver_get_type(void);
#define GST_TYPE_WEBRTC_RTP_RECEIVER (gst_webrtc_rtp_receiver_get_type())
#define GST_WEBRTC_RTP_RECEIVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_RTP_RECEIVER,GstWebRTCRTPReceiver))
#define GST_IS_WEBRTC_RTP_RECEIVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_RTP_RECEIVER))
#define GST_WEBRTC_RTP_RECEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_RTP_RECEIVER,GstWebRTCRTPReceiverClass))
#define GST_IS_WEBRTC_RTP_RECEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_RTP_RECEIVER))
#define GST_WEBRTC_RTP_RECEIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_RTP_RECEIVER,GstWebRTCRTPReceiverClass))
typedef struct _GstWebRTCRTPReceiver GstWebRTCRTPReceiver;
typedef struct _GstWebRTCRTPReceiverClass GstWebRTCRTPReceiverClass;
struct _GstWebRTCRTPReceiver
{
GstObject parent;
/* The MediStreamTrack is represented by the stream and is output into @transport/@rtcp_transport as necessary */
GstWebRTCDTLSTransport *transport;
GstWebRTCDTLSTransport *rtcp_transport;
gpointer _padding[GST_PADDING];
};
struct _GstWebRTCRTPReceiverClass
{
GstObjectClass parent_class;
gpointer _padding[GST_PADDING];
};
GST_EXPORT
GstWebRTCRTPReceiver * gst_webrtc_rtp_receiver_new (void);
GST_EXPORT
GstStructure * gst_webrtc_rtp_receiver_get_parameters (GstWebRTCRTPReceiver * receiver, gchar * kind);
/* FIXME: promise? */
GST_EXPORT
gboolean gst_webrtc_rtp_receiver_set_parameters (GstWebRTCRTPReceiver * receiver,
GstStructure * parameters);
GST_EXPORT
void gst_webrtc_rtp_receiver_set_transport (GstWebRTCRTPReceiver * receiver,
GstWebRTCDTLSTransport * transport);
GST_EXPORT
void gst_webrtc_rtp_receiver_set_rtcp_transport (GstWebRTCRTPReceiver * receiver,
GstWebRTCDTLSTransport * transport);
G_END_DECLS
#endif /* __GST_WEBRTC_RTP_RECEIVER_H__ */

View file

@ -0,0 +1,141 @@
/* 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.
*/
/**
* SECTION:gstwebrtc-sender
* @short_description: RTCRtpSender object
* @title: GstWebRTCRTPSender
* @see_also: #GstWebRTCRTPReceiver, #GstWebRTCRTPTransceiver
*
* <ulink url="https://www.w3.org/TR/webrtc/#rtcrtpsender-interface">https://www.w3.org/TR/webrtc/#rtcrtpsender-interface</ulink>
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "rtpsender.h"
#include "rtptransceiver.h"
#define GST_CAT_DEFAULT gst_webrtc_rtp_sender_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_rtp_sender_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCRTPSender, gst_webrtc_rtp_sender,
GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_rtp_sender_debug,
"webrtcsender", 0, "webrtcsender");
);
enum
{
SIGNAL_0,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_MID,
PROP_SENDER,
PROP_STOPPED,
PROP_DIRECTION,
};
//static guint gst_webrtc_rtp_sender_signals[LAST_SIGNAL] = { 0 };
void
gst_webrtc_rtp_sender_set_transport (GstWebRTCRTPSender * sender,
GstWebRTCDTLSTransport * transport)
{
g_return_if_fail (GST_IS_WEBRTC_RTP_SENDER (sender));
g_return_if_fail (GST_IS_WEBRTC_DTLS_TRANSPORT (transport));
gst_object_replace ((GstObject **) & sender->transport,
GST_OBJECT (transport));
}
void
gst_webrtc_rtp_sender_set_rtcp_transport (GstWebRTCRTPSender * sender,
GstWebRTCDTLSTransport * transport)
{
g_return_if_fail (GST_IS_WEBRTC_RTP_SENDER (sender));
g_return_if_fail (GST_IS_WEBRTC_DTLS_TRANSPORT (transport));
gst_object_replace ((GstObject **) & sender->rtcp_transport,
GST_OBJECT (transport));
}
static void
gst_webrtc_rtp_sender_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_rtp_sender_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_rtp_sender_finalize (GObject * object)
{
GstWebRTCRTPSender *webrtc = GST_WEBRTC_RTP_SENDER (object);
if (webrtc->transport)
gst_object_unref (webrtc->transport);
webrtc->transport = NULL;
if (webrtc->rtcp_transport)
gst_object_unref (webrtc->rtcp_transport);
webrtc->rtcp_transport = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_webrtc_rtp_sender_class_init (GstWebRTCRTPSenderClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = gst_webrtc_rtp_sender_get_property;
gobject_class->set_property = gst_webrtc_rtp_sender_set_property;
gobject_class->finalize = gst_webrtc_rtp_sender_finalize;
}
static void
gst_webrtc_rtp_sender_init (GstWebRTCRTPSender * webrtc)
{
}
GstWebRTCRTPSender *
gst_webrtc_rtp_sender_new (GArray * send_encodings /* FIXME */ )
{
return g_object_new (GST_TYPE_WEBRTC_RTP_SENDER, NULL);
}

View file

@ -0,0 +1,77 @@
/* 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 __GST_WEBRTC_RTP_SENDER_H__
#define __GST_WEBRTC_RTP_SENDER_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc_fwd.h>
#include <gst/webrtc/dtlstransport.h>
G_BEGIN_DECLS
GST_EXPORT
GType gst_webrtc_rtp_sender_get_type(void);
#define GST_TYPE_WEBRTC_RTP_SENDER (gst_webrtc_rtp_sender_get_type())
#define GST_WEBRTC_RTP_SENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_RTP_SENDER,GstWebRTCRTPSender))
#define GST_IS_WEBRTC_RTP_SENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_RTP_SENDER))
#define GST_WEBRTC_RTP_SENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_RTP_SENDER,GstWebRTCRTPSenderClass))
#define GST_IS_WEBRTC_RTP_SENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_RTP_SENDER))
#define GST_WEBRTC_RTP_SENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_RTP_SENDER,GstWebRTCRTPSenderClass))
struct _GstWebRTCRTPSender
{
GstObject parent;
/* The MediStreamTrack is represented by the stream and is output into @transport/@rtcp_transport as necessary */
GstWebRTCDTLSTransport *transport;
GstWebRTCDTLSTransport *rtcp_transport;
GArray *send_encodings;
gpointer _padding[GST_PADDING];
};
struct _GstWebRTCRTPSenderClass
{
GstObjectClass parent_class;
gpointer _padding[GST_PADDING];
};
GST_EXPORT
GstWebRTCRTPSender * gst_webrtc_rtp_sender_new (GArray * send_encodings);
GST_EXPORT
GstStructure * gst_webrtc_rtp_sender_get_parameters (GstWebRTCRTPSender * sender, gchar * kind);
/* FIXME: promise? */
GST_EXPORT
gboolean gst_webrtc_rtp_sender_set_parameters (GstWebRTCRTPSender * sender,
GstStructure * parameters);
GST_EXPORT
void gst_webrtc_rtp_sender_set_transport (GstWebRTCRTPSender * sender,
GstWebRTCDTLSTransport * transport);
GST_EXPORT
void gst_webrtc_rtp_sender_set_rtcp_transport (GstWebRTCRTPSender * sender,
GstWebRTCDTLSTransport * transport);
G_END_DECLS
#endif /* __GST_WEBRTC_RTP_SENDER_H__ */

View file

@ -0,0 +1,186 @@
/* 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.
*/
/**
* SECTION:gstwebrtc-transceiver
* @short_description: RTCRtpTransceiver object
* @title: GstWebRTCRTPTransceiver
* @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPReceiver
*
* <ulink url="https://www.w3.org/TR/webrtc/#rtcrtptransceiver-interface">https://www.w3.org/TR/webrtc/#rtcrtptransceiver-interface</ulink>
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "rtptransceiver.h"
#define GST_CAT_DEFAULT gst_webrtc_rtp_transceiver_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define gst_webrtc_rtp_transceiver_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCRTPTransceiver,
gst_webrtc_rtp_transceiver, GST_TYPE_OBJECT,
GST_DEBUG_CATEGORY_INIT (gst_webrtc_rtp_transceiver_debug,
"webrtctransceiver", 0, "webrtctransceiver");
);
enum
{
SIGNAL_0,
LAST_SIGNAL,
};
enum
{
PROP_0,
PROP_MID,
PROP_SENDER,
PROP_RECEIVER,
PROP_STOPPED, // FIXME
PROP_DIRECTION, // FIXME
PROP_MLINE,
};
//static guint gst_webrtc_rtp_transceiver_signals[LAST_SIGNAL] = { 0 };
static void
gst_webrtc_rtp_transceiver_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCRTPTransceiver *webrtc = GST_WEBRTC_RTP_TRANSCEIVER (object);
switch (prop_id) {
case PROP_SENDER:
webrtc->sender = g_value_dup_object (value);
break;
case PROP_RECEIVER:
webrtc->receiver = g_value_dup_object (value);
break;
case PROP_MLINE:
webrtc->mline = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_rtp_transceiver_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCRTPTransceiver *webrtc = GST_WEBRTC_RTP_TRANSCEIVER (object);
switch (prop_id) {
case PROP_SENDER:
g_value_set_object (value, webrtc->sender);
break;
case PROP_RECEIVER:
g_value_set_object (value, webrtc->receiver);
break;
case PROP_MLINE:
g_value_set_uint (value, webrtc->mline);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_rtp_transceiver_constructed (GObject * object)
{
GstWebRTCRTPTransceiver *webrtc = GST_WEBRTC_RTP_TRANSCEIVER (object);
gst_object_set_parent (GST_OBJECT (webrtc->sender), GST_OBJECT (webrtc));
gst_object_set_parent (GST_OBJECT (webrtc->receiver), GST_OBJECT (webrtc));
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
gst_webrtc_rtp_transceiver_dispose (GObject * object)
{
GstWebRTCRTPTransceiver *webrtc = GST_WEBRTC_RTP_TRANSCEIVER (object);
if (webrtc->sender) {
GST_OBJECT_PARENT (webrtc->sender) = NULL;
gst_object_unref (webrtc->sender);
}
webrtc->sender = NULL;
if (webrtc->receiver) {
GST_OBJECT_PARENT (webrtc->receiver) = NULL;
gst_object_unref (webrtc->receiver);
}
webrtc->receiver = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_webrtc_rtp_transceiver_finalize (GObject * object)
{
GstWebRTCRTPTransceiver *webrtc = GST_WEBRTC_RTP_TRANSCEIVER (object);
g_free (webrtc->mid);
if (webrtc->codec_preferences)
gst_caps_unref (webrtc->codec_preferences);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_webrtc_rtp_transceiver_class_init (GstWebRTCRTPTransceiverClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = gst_webrtc_rtp_transceiver_get_property;
gobject_class->set_property = gst_webrtc_rtp_transceiver_set_property;
gobject_class->constructed = gst_webrtc_rtp_transceiver_constructed;
gobject_class->dispose = gst_webrtc_rtp_transceiver_dispose;
gobject_class->finalize = gst_webrtc_rtp_transceiver_finalize;
g_object_class_install_property (gobject_class,
PROP_SENDER,
g_param_spec_object ("sender", "Sender",
"The RTP sender for this transceiver",
GST_TYPE_WEBRTC_RTP_SENDER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_RECEIVER,
g_param_spec_object ("receiver", "Receiver",
"The RTP receiver for this transceiver",
GST_TYPE_WEBRTC_RTP_RECEIVER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_MLINE,
g_param_spec_uint ("mlineindex", "Media Line Index",
"Index in the SDP of the Media",
0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_rtp_transceiver_init (GstWebRTCRTPTransceiver * webrtc)
{
}

View file

@ -0,0 +1,69 @@
/* 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 __GST_WEBRTC_RTP_TRANSCEIVER_H__
#define __GST_WEBRTC_RTP_TRANSCEIVER_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc_fwd.h>
#include <gst/webrtc/rtpsender.h>
#include <gst/webrtc/rtpreceiver.h>
G_BEGIN_DECLS
GST_EXPORT
GType gst_webrtc_rtp_transceiver_get_type(void);
#define GST_TYPE_WEBRTC_RTP_TRANSCEIVER (gst_webrtc_rtp_transceiver_get_type())
#define GST_WEBRTC_RTP_TRANSCEIVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_RTP_TRANSCEIVER,GstWebRTCRTPTransceiver))
#define GST_IS_WEBRTC_RTP_TRANSCEIVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_RTP_TRANSCEIVER))
#define GST_WEBRTC_RTP_TRANSCEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_RTP_TRANSCEIVER,GstWebRTCRTPTransceiverClass))
#define GST_IS_WEBRTC_RTP_TRANSCEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_RTP_TRANSCEIVER))
#define GST_WEBRTC_RTP_TRANSCEIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_RTP_TRANSCEIVER,GstWebRTCRTPTransceiverClass))
struct _GstWebRTCRTPTransceiver
{
GstObject parent;
guint mline;
gchar *mid;
gboolean stopped;
GstWebRTCRTPSender *sender;
GstWebRTCRTPReceiver *receiver;
GstWebRTCRTPTransceiverDirection direction;
GstWebRTCRTPTransceiverDirection current_direction;
GstCaps *codec_preferences;
gpointer _padding[GST_PADDING];
};
struct _GstWebRTCRTPTransceiverClass
{
GstObjectClass parent_class;
gpointer _padding[GST_PADDING];
};
GST_EXPORT
void gst_webrtc_rtp_transceiver_stop (GstWebRTCRTPTransceiver * transceiver);
G_END_DECLS
#endif /* __GST_WEBRTC_RTP_TRANSCEIVER_H__ */

View file

@ -0,0 +1,33 @@
/* 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 __GST_WEBRTC_WEBRTC_H__
#define __GST_WEBRTC_WEBRTC_H__
#include <gst/gst.h>
#include <gst/webrtc/webrtc_fwd.h>
#include <gst/webrtc/webrtc-enumtypes.h>
#include <gst/webrtc/dtlstransport.h>
#include <gst/webrtc/icetransport.h>
#include <gst/webrtc/rtcsessiondescription.h>
#include <gst/webrtc/rtpreceiver.h>
#include <gst/webrtc/rtpsender.h>
#include <gst/webrtc/rtptransceiver.h>
#endif /* __GST_WEBRTC_WEBRTC_H__ */

View file

@ -0,0 +1,251 @@
/* 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 __GST_WEBRTC_FWD_H__
#define __GST_WEBRTC_FWD_H__
#ifndef GST_USE_UNSTABLE_API
#warning "The WebRTC library from gst-plugins-bad is unstable API and may change in future."
#warning "You can define GST_USE_UNSTABLE_API to avoid this warning."
#endif
#include <gst/gst.h>
#include <gst/webrtc/webrtc-enumtypes.h>
typedef struct _GstWebRTCDTLSTransport GstWebRTCDTLSTransport;
typedef struct _GstWebRTCDTLSTransportClass GstWebRTCDTLSTransportClass;
typedef struct _GstWebRTCICETransport GstWebRTCICETransport;
typedef struct _GstWebRTCICETransportClass GstWebRTCICETransportClass;
typedef struct _GstWebRTCRTPReceiver GstWebRTCRTPReceiver;
typedef struct _GstWebRTCRTPReceiverClass GstWebRTCRTPReceiverClass;
typedef struct _GstWebRTCRTPSender GstWebRTCRTPSender;
typedef struct _GstWebRTCRTPSenderClass GstWebRTCRTPSenderClass;
typedef struct _GstWebRTCSessionDescription GstWebRTCSessionDescription;
typedef struct _GstWebRTCRTPTransceiver GstWebRTCRTPTransceiver;
typedef struct _GstWebRTCRTPTransceiverClass GstWebRTCRTPTransceiverClass;
/**
* GstWebRTCDTLSTransportState:
* GST_WEBRTC_DTLS_TRANSPORT_STATE_NEW: new
* GST_WEBRTC_DTLS_TRANSPORT_STATE_CLOSED: closed
* GST_WEBRTC_DTLS_TRANSPORT_STATE_FAILED: failed
* GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTING: connecting
* GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED: connected
*/
typedef enum /*< underscore_name=gst_webrtc_dtls_transport_state >*/
{
GST_WEBRTC_DTLS_TRANSPORT_STATE_NEW,
GST_WEBRTC_DTLS_TRANSPORT_STATE_CLOSED,
GST_WEBRTC_DTLS_TRANSPORT_STATE_FAILED,
GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTING,
GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED,
} GstWebRTCDTLSTransportState;
/**
* GstWebRTCICEGatheringState:
* GST_WEBRTC_ICE_GATHERING_STATE_NEW: new
* GST_WEBRTC_ICE_GATHERING_STATE_GATHERING: gathering
* GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE: complete
*
* See <ulink url="http://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate">http://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate</ulink>
*/
typedef enum /*< underscore_name=gst_webrtc_ice_gathering_state >*/
{
GST_WEBRTC_ICE_GATHERING_STATE_NEW,
GST_WEBRTC_ICE_GATHERING_STATE_GATHERING,
GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE,
} GstWebRTCICEGatheringState; /*< underscore_name=gst_webrtc_ice_gathering_state >*/
/**
* GstWebRTCICEConnectionState:
* GST_WEBRTC_ICE_CONNECTION_STATE_NEW: new
* GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING: checking
* GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED: connected
* GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED: completed
* GST_WEBRTC_ICE_CONNECTION_STATE_FAILED: failed
* GST_WEBRTC_ICE_CONNECTION_STATE_DISCONNECTED: disconnected
* GST_WEBRTC_ICE_CONNECTION_STATE_CLOSED: closed
*
* See <ulink url="http://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate">http://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate</ulink>
*/
typedef enum /*< underscore_name=gst_webrtc_ice_connection_state >*/
{
GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING,
GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED,
GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED,
GST_WEBRTC_ICE_CONNECTION_STATE_FAILED,
GST_WEBRTC_ICE_CONNECTION_STATE_DISCONNECTED,
GST_WEBRTC_ICE_CONNECTION_STATE_CLOSED,
} GstWebRTCICEConnectionState;
/**
* GstWebRTCSignalingState:
* GST_WEBRTC_SIGNALING_STATE_STABLE: stable
* GST_WEBRTC_SIGNALING_STATE_CLOSED: closed
* GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER: have-local-offer
* GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER: have-remote-offer
* GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER: have-local-pranswer
* GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER: have-remote-pranswer
*
* See <ulink url="http://w3c.github.io/webrtc-pc/#dom-rtcsignalingstate">http://w3c.github.io/webrtc-pc/#dom-rtcsignalingstate</ulink>
*/
typedef enum /*< underscore_name=gst_webrtc_signaling_state >*/
{
GST_WEBRTC_SIGNALING_STATE_STABLE,
GST_WEBRTC_SIGNALING_STATE_CLOSED,
GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER,
GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER,
GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER,
GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER,
} GstWebRTCSignalingState;
/**
* GstWebRTCPeerConnectionState:
* GST_WEBRTC_PEER_CONNECTION_STATE_NEW: new
* GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTING: connecting
* GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTED: connected
* GST_WEBRTC_PEER_CONNECTION_STATE_DISCONNECTED: disconnected
* GST_WEBRTC_PEER_CONNECTION_STATE_FAILED: failed
* GST_WEBRTC_PEER_CONNECTION_STATE_CLOSED: closed
*
* See <ulink url="http://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectionstate">http://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectionstate</ulink>
*/
typedef enum /*< underscore_name=gst_webrtc_peer_connection_state >*/
{
GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTING,
GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTED,
GST_WEBRTC_PEER_CONNECTION_STATE_DISCONNECTED,
GST_WEBRTC_PEER_CONNECTION_STATE_FAILED,
GST_WEBRTC_PEER_CONNECTION_STATE_CLOSED,
} GstWebRTCPeerConnectionState;
/**
* GstWebRTCIceRole:
* GST_WEBRTC_ICE_ROLE_CONTROLLED: controlled
* GST_WEBRTC_ICE_ROLE_CONTROLLING: controlling
*/
typedef enum /*< underscore_name=gst_webrtc_ice_role >*/
{
GST_WEBRTC_ICE_ROLE_CONTROLLED,
GST_WEBRTC_ICE_ROLE_CONTROLLING,
} GstWebRTCIceRole;
/**
* GstWebRTCIceComponent:
* GST_WEBRTC_ICE_COMPONENT_RTP,
* GST_WEBRTC_ICE_COMPONENT_RTCP,
*/
typedef enum /*< underscore_name=gst_webrtc_ice_component >*/
{
GST_WEBRTC_ICE_COMPONENT_RTP,
GST_WEBRTC_ICE_COMPONENT_RTCP,
} GstWebRTCICEComponent;
/**
* GstWebRTCSDPType:
* GST_WEBRTC_SDP_TYPE_OFFER: offer
* GST_WEBRTC_SDP_TYPE_PRANSWER: pranswer
* GST_WEBRTC_SDP_TYPE_ANSWER: answer
* GST_WEBRTC_SDP_TYPE_ROLLBACK: rollback
*
* See <ulink url="http://w3c.github.io/webrtc-pc/#rtcsdptype">http://w3c.github.io/webrtc-pc/#rtcsdptype</ulink>
*/
typedef enum /*< underscore_name=gst_webrtc_sdp_type >*/
{
GST_WEBRTC_SDP_TYPE_OFFER = 1,
GST_WEBRTC_SDP_TYPE_PRANSWER,
GST_WEBRTC_SDP_TYPE_ANSWER,
GST_WEBRTC_SDP_TYPE_ROLLBACK,
} GstWebRTCSDPType;
/**
* GstWebRTCRtpTransceiverDirection:
* GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE: none
* GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE: inactive
* GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY: sendonly
* GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY: recvonly
* GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV: sendrecv
*/
typedef enum /*< underscore_name=gst_webrtc_rtp_transceiver_direction >*/
{
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE,
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY,
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY,
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV,
} GstWebRTCRTPTransceiverDirection;
/**
* GstWebRTCDTLSSetup:
* GST_WEBRTC_DTLS_SETUP_NONE: none
* GST_WEBRTC_DTLS_SETUP_ACTPASS: actpass
* GST_WEBRTC_DTLS_SETUP_ACTIVE: sendonly
* GST_WEBRTC_DTLS_SETUP_PASSIVE: recvonly
*/
typedef enum /*< underscore_name=gst_webrtc_dtls_setup >*/
{
GST_WEBRTC_DTLS_SETUP_NONE,
GST_WEBRTC_DTLS_SETUP_ACTPASS,
GST_WEBRTC_DTLS_SETUP_ACTIVE,
GST_WEBRTC_DTLS_SETUP_PASSIVE,
} GstWebRTCDTLSSetup;
/**
* GstWebRTCStatsType:
* GST_WEBRTC_STATS_CODEC: codec
* GST_WEBRTC_STATS_INBOUND_RTP: inbound-rtp
* GST_WEBRTC_STATS_OUTBOUND_RTP: outbound-rtp
* GST_WEBRTC_STATS_REMOTE_INBOUND_RTP: remote-inbound-rtp
* GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP: remote-outbound-rtp
* GST_WEBRTC_STATS_CSRC: csrc
* GST_WEBRTC_STATS_PEER_CONNECTION: peer-connectiion
* GST_WEBRTC_STATS_DATA_CHANNEL: data-channel
* GST_WEBRTC_STATS_STREAM: stream
* GST_WEBRTC_STATS_TRANSPORT: transport
* GST_WEBRTC_STATS_CANDIDATE_PAIR: candidate-pair
* GST_WEBRTC_STATS_LOCAL_CANDIDATE: local-candidate
* GST_WEBRTC_STATS_REMOTE_CANDIDATE: remote-candidate
* GST_WEBRTC_STATS_CERTIFICATE: certificate
*/
typedef enum /*< underscore_name=gst_webrtc_stats_type >*/
{
GST_WEBRTC_STATS_CODEC = 1,
GST_WEBRTC_STATS_INBOUND_RTP,
GST_WEBRTC_STATS_OUTBOUND_RTP,
GST_WEBRTC_STATS_REMOTE_INBOUND_RTP,
GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP,
GST_WEBRTC_STATS_CSRC,
GST_WEBRTC_STATS_PEER_CONNECTION,
GST_WEBRTC_STATS_DATA_CHANNEL,
GST_WEBRTC_STATS_STREAM,
GST_WEBRTC_STATS_TRANSPORT,
GST_WEBRTC_STATS_CANDIDATE_PAIR,
GST_WEBRTC_STATS_LOCAL_CANDIDATE,
GST_WEBRTC_STATS_REMOTE_CANDIDATE,
GST_WEBRTC_STATS_CERTIFICATE,
} GstWebRTCStatsType;
#endif /* __GST_WEBRTC_FWD_H__ */

View file

@ -0,0 +1,55 @@
#!/usr/bin/env python3
# This is in its own file rather than inside meson.build
# because a) mixing the two is ugly and b) trying to
# make special characters such as \n go through all
# backends is a fool's errand.
import sys, os, shutil, subprocess
h_array = ['--fhead',
"#ifndef __GST_WEBRTC_ENUM_TYPES_H__\n#define __GST_WEBRTC_ENUM_TYPES_H__\n\n#include <gst/gst.h>\n\nG_BEGIN_DECLS\n",
'--fprod',
"\n/* enumerations from \"@filename@\" */\n",
'--vhead',
"GST_EXPORT GType @enum_name@_get_type (void);\n#define GST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n",
'--ftail',
"G_END_DECLS\n\n#endif /* __GST_WEBRTC_ENUM_TYPES_H__ */"
]
c_array = ['--fhead',
"#include \"webrtc-enumtypes.h\"\n\n#include \"webrtc.h\"",
'--fprod',
"\n/* enumerations from \"@filename@\" */",
'--vhead',
"GType\n@enum_name@_get_type (void)\n{\n static volatile gsize g_define_type_id__volatile = 0;\n if (g_once_init_enter (&g_define_type_id__volatile)) {\n static const G@Type@Value values[] = {",
'--vprod',
" { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" },",
'--vtail',
" { 0, NULL, NULL }\n };\n GType g_define_type_id = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);\n }\n return g_define_type_id__volatile;\n}\n"
]
cmd = []
argn = 1
# Find the full command needed to run glib-mkenums
# On UNIX-like, this is just the full path to glib-mkenums
# On Windows, this is the full path to interpreter + full path to glib-mkenums
for arg in sys.argv[1:]:
cmd.append(arg)
argn += 1
if arg.endswith('glib-mkenums'):
break
ofilename = sys.argv[argn]
headers = sys.argv[argn + 1:]
if ofilename.endswith('.h'):
arg_array = h_array
else:
arg_array = c_array
cmd_array = cmd + arg_array + headers
pc = subprocess.Popen(cmd_array, stdout=subprocess.PIPE)
(stdo, _) = pc.communicate()
if pc.returncode != 0:
sys.exit(pc.returncode)
open(ofilename, 'wb').write(stdo)

View file

@ -6,6 +6,7 @@ pcverfiles = \
gstreamer-insertbin-@GST_API_VERSION@.pc \
gstreamer-mpegts-@GST_API_VERSION@.pc \
gstreamer-player-@GST_API_VERSION@.pc \
gstreamer-webrtc-@GST_API_VERSION@.pc \
gstreamer-bad-audio-@GST_API_VERSION@.pc \
gstreamer-bad-video-@GST_API_VERSION@.pc
@ -15,6 +16,7 @@ pcverfiles_uninstalled = \
gstreamer-insertbin-@GST_API_VERSION@-uninstalled.pc \
gstreamer-mpegts-@GST_API_VERSION@-uninstalled.pc \
gstreamer-player-@GST_API_VERSION@-uninstalled.pc \
gstreamer-webrtc-@GST_API_VERSION@-uninstalled.pc \
gstreamer-bad-audio-@GST_API_VERSION@-uninstalled.pc \
gstreamer-bad-video-@GST_API_VERSION@-uninstalled.pc
@ -39,6 +41,7 @@ cp_verbose_0 = @echo " CP $@";
-e "s|[@]mpegtslibdir[@]|$(abs_top_builddir)/gst-libs/gst/mpegts/.libs|" \
-e "s|[@]playerlibdir[@]|$(abs_top_builddir)/gst-libs/gst/player/.libs|" \
-e "s|[@]waylandlibdir[@]|$(abs_top_builddir)/gst-libs/gst/wayland/.libs|" \
-e "s|[@]webrtclibdir[@]|$(abs_top_builddir)/gst-libs/gst/webrtc/.libs|" \
-e "s|[@]basecamerabinsrclibdir[@]|$(abs_top_builddir)/gst-libs/gst/basecamerabinsrc/.libs|" \
-e "s|[@]photographylibdir[@]|$(abs_top_builddir)/gst-libs/gst/interfaces/.libs|" \
$< > $@.tmp && mv $@.tmp $@
@ -53,6 +56,7 @@ pcinfiles = \
gstreamer-insertbin.pc.in gstreamer-insertbin-uninstalled.pc.in \
gstreamer-mpegts.pc.in gstreamer-mpegts-uninstalled.pc.in \
gstreamer-player.pc.in gstreamer-player-uninstalled.pc.in \
gstreamer-webrtc.pc.in gstreamer-webrtc-uninstalled.pc.in \
gstreamer-bad-audio.pc.in gstreamer-bad-audio-uninstalled.pc.in \
gstreamer-bad-video.pc.in gstreamer-bad-video-uninstalled.pc.in

View file

@ -10,5 +10,5 @@ Name: GStreamer Bad Plugin libraries, Uninstalled
Description: Streaming media framework, bad plugins libraries, uninstalled
Version: @VERSION@
Requires: gstreamer-@GST_API_VERSION@
Libs: -L@audiolibdir@ -L@basecamerabinsrclibdir@ -L@codecparserslibdir@ -L@insertbinlibdir@ -L@photographylibdir@ -L@mpegtslibdir@ -L@playerlibdir@ -L@videolibdir@ -L@waylandlibdir@
Libs: -L@audiolibdir@ -L@basecamerabinsrclibdir@ -L@codecparserslibdir@ -L@insertbinlibdir@ -L@photographylibdir@ -L@mpegtslibdir@ -L@playerlibdir@ -L@videolibdir@ -L@waylandlibdir@ -L@webrtclibdir@
Cflags: -I@abs_top_srcdir@/gst-libs -I@abs_top_builddir@/gst-libs

View file

@ -0,0 +1,12 @@
prefix=
exec_prefix=
libdir=@webrtclibdir@
includedir=@abs_top_srcdir@/gst-libs
Name: GStreamer WebRTC, Uninstalled
Description: GStreamer WebRTC support, uninstalled
Requires: gstreamer-@GST_API_VERSION@ gstreamer-base-@GST_API_VERSION@
Version: @VERSION@
Libs: -L${libdir} -lgstwebrtc-@GST_API_VERSION@
Cflags: -I@abs_top_srcdir@/gst-libs -I@abs_top_builddir@/gst-libs

View file

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@/gstreamer-@GST_API_VERSION@
Name: GStreamer WebRTC
Description: GStreamer WebRTC support
Requires: gstreamer-@GST_API_VERSION@ gstreamer-base-@GST_API_VERSION@
Version: @VERSION@
Libs: -L${libdir} -lgstwebrtc-@GST_API_VERSION@
Cflags: -I${includedir}

View file

@ -18,6 +18,7 @@ pkgconf.set('mpegtslibdir', join_paths(meson.build_root(), gstmpegts.outdir()))
pkgconf.set('playerlibdir', join_paths(meson.build_root(), gstplayer.outdir()))
pkgconf.set('basecamerabinsrclibdir', join_paths(meson.build_root(), gstbasecamerabin.outdir()))
pkgconf.set('photographylibdir', join_paths(meson.build_root(), gstphotography.outdir()))
pkgconf.set('webrtclibdir', join_paths(meson.build_root(), gstwebrtc.outdir()))
pkg_install_dir = '@0@/pkgconfig'.format(get_option('libdir'))
@ -29,6 +30,7 @@ pkg_libs = [
'mpegts',
'player',
'plugins-bad',
'webrtc',
]
#if use_wayland

View file

@ -204,6 +204,12 @@ else
check_ipcpipeline=
endif
if USE_WEBRTC
check_webrtc = elements/webrtcbin
else
check_webrtc=
endif
VALGRIND_TO_FIX = \
elements/mpeg2enc \
elements/mplex \
@ -284,6 +290,7 @@ check_PROGRAMS = \
$(check_hlsdemux) \
$(check_srtp) \
$(check_player) \
$(check_webrtc) \
$(EXPERIMENTAL_CHECKS)
noinst_HEADERS = elements/mxfdemux.h libs/isoff.h
@ -562,6 +569,13 @@ orc/compositor.c: $(top_srcdir)/gst/compositor/compositororc.orc
$(MKDIR_P) orc/
$(ORCC) --test -o $@ $<
elements_webrtcbin_LDADD = \
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la \
$(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_SDP_LIBS) $(LDADD)
elements_webrtcbin_CFLAGS = \
$(GST_PLUGINS_BASE_CLAGS) $(GST_PLUGINS_BAD_CFLAGS) $(GST_SDP_CFLAGS) \
$(GST_BASE_CFLAGS) $(CFLAGS) $(AM_CFLAGS)
distclean-local-orc:
rm -rf orc

View file

@ -65,5 +65,6 @@ videorecordingbin
viewfinderbin
voaacenc
voamrwbenc
webrtcbin
x265enc
zbar

File diff suppressed because it is too large Load diff

View file

@ -55,6 +55,7 @@ base_tests = [
[['elements/videoframe-audiolevel.c']],
[['elements/viewfinderbin.c']],
[['elements/voaacenc.c'], not voaac_dep.found(), [voaac_dep]],
[['elements/webrtcbin.c'], not libnice_dep.found(), [gstwebrtc_dep]],
[['elements/x265enc.c'], not x265_dep.found(), [x265_dep]],
[['elements/zbar.c'], not zbar_dep.found(), [zbar_dep]],
[['libs/h264parser.c'], false, [gstcodecparsers_dep]],

View file

@ -52,6 +52,12 @@ else
IPCPIPELINE_DIR=
endif
if USE_WEBRTC
WEBRTC_DIR=webrtc
else
WEBRTC_DIR=
endif
noinst_PROGRAMS = playout
playout_SOURCES = playout.c
@ -60,8 +66,8 @@ playout_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_LIB
SUBDIRS= codecparsers mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) \
$(GTK3_DIR) $(AVSAMPLE_DIR) $(WAYLAND_DIR) $(MATRIXMIX_DIR) \
$(IPCPIPELINE_DIR)
$(IPCPIPELINE_DIR) $(WEBRTC_DIR)
DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 gtk \
avsamplesink waylandsink audiomixmatrix ipcpipeline
avsamplesink waylandsink audiomixmatrix ipcpipeline webrtc
include $(top_srcdir)/common/parallel-subdirs.mak

View file

@ -13,6 +13,7 @@ subdir('mpegts')
#subdir('qt')
#subdir('uvch264')
#subdir('waylandsink')
subdir('webrtc')
executable('playout',
'playout.c',

View file

@ -0,0 +1,41 @@
noinst_PROGRAMS = webrtc webrtcbidirectional webrtcswap
webrtc_SOURCES = webrtc.c
webrtc_CFLAGS=\
-I$(top_srcdir)/gst-libs \
-I$(top_builddir)/gst-libs \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(GST_SDP_CFLAGS)
webrtc_LDADD=\
$(GST_PLUGINS_BASE_LIBS) \
$(GST_LIBS) \
$(GST_SDP_LIBS) \
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
webrtcbidirectional_SOURCES = webrtcbidirectional.c
webrtcbidirectional_CFLAGS=\
-I$(top_srcdir)/gst-libs \
-I$(top_builddir)/gst-libs \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(GST_SDP_CFLAGS)
webrtcbidirectional_LDADD=\
$(GST_PLUGINS_BASE_LIBS) \
$(GST_LIBS) \
$(GST_SDP_LIBS) \
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
webrtcswap_SOURCES = webrtcswap.c
webrtcswap_CFLAGS=\
-I$(top_srcdir)/gst-libs \
-I$(top_builddir)/gst-libs \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(GST_SDP_CFLAGS)
webrtcswap_LDADD=\
$(GST_PLUGINS_BASE_LIBS) \
$(GST_LIBS) \
$(GST_SDP_LIBS) \
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la

View file

@ -0,0 +1,15 @@
examples = ['webrtc', 'webrtcbidirectional', 'webrtcswap']
foreach example : examples
exe_name = example
src_file = '@0@.c'.format(example)
executable(exe_name,
src_file,
install: true,
include_directories : [configinc],
dependencies : [glib_dep, gst_dep, gstwebrtc_dep],
c_args : ['-DHAVE_CONFIG_H=1', '-DGST_USE_UNSTABLE_API'],
)
endforeach

View file

@ -0,0 +1,187 @@
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc.h>
#include <string.h>
static GMainLoop *loop;
static GstElement *pipe1, *webrtc1, *webrtc2;
static GstBus *bus1;
static gboolean
_bus_watch (GstBus * bus, GstMessage * msg, GstElement * pipe)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_STATE_CHANGED:
if (GST_ELEMENT (msg->src) == pipe) {
GstState old, new, pending;
gst_message_parse_state_changed (msg, &old, &new, &pending);
{
gchar *dump_name = g_strconcat ("state_changed-",
gst_element_state_get_name (old), "_",
gst_element_state_get_name (new), NULL);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (msg->src),
GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
g_free (dump_name);
}
}
break;
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg_info = NULL;
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "error");
gst_message_parse_error (msg, &err, &dbg_info);
g_printerr ("ERROR from element %s: %s\n",
GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free (err);
g_free (dbg_info);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "eos");
g_print ("EOS received\n");
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static void
_webrtc_pad_added (GstElement * webrtc, GstPad * new_pad, GstElement * pipe)
{
GstElement *out;
GstPad *sink;
if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
return;
out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
"videoconvert ! queue ! xvimagesink sync=false", TRUE, NULL);
gst_bin_add (GST_BIN (pipe), out);
gst_element_sync_state_with_parent (out);
sink = out->sinkpads->data;
gst_pad_link (new_pad, sink);
}
static void
_on_answer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *answer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "answer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (answer->sdp);
g_print ("Created answer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
gst_webrtc_session_description_free (answer);
}
static void
_on_offer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *offer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "offer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (offer->sdp);
g_print ("Created offer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
gst_webrtc_session_description_free (offer);
}
static void
_on_negotiation_needed (GstElement * element, gpointer user_data)
{
GstPromise *promise;
promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
}
static void
_on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
GstElement * other)
{
g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
}
int
main (int argc, char *argv[])
{
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
pipe1 =
gst_parse_launch
("videotestsrc ! video/x-raw,framerate=1/1 ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! "
"webrtcbin name=send webrtcbin name=recv", NULL);
bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "send");
g_signal_connect (webrtc1, "on-negotiation-needed",
G_CALLBACK (_on_negotiation_needed), NULL);
webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "recv");
g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
g_signal_connect (webrtc1, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc2);
g_signal_connect (webrtc2, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc1);
g_print ("Starting pipeline\n");
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
g_main_loop_run (loop);
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
g_print ("Pipeline stopped\n");
gst_object_unref (webrtc1);
gst_object_unref (webrtc2);
gst_bus_remove_watch (bus1);
gst_object_unref (bus1);
gst_object_unref (pipe1);
gst_deinit ();
return 0;
}

View file

@ -0,0 +1,197 @@
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc.h>
#include <string.h>
static GMainLoop *loop;
static GstElement *pipe1, *webrtc1, *webrtc2;
static GstBus *bus1;
static gboolean
_bus_watch (GstBus * bus, GstMessage * msg, GstElement * pipe)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_STATE_CHANGED:
if (GST_ELEMENT (msg->src) == pipe) {
GstState old, new, pending;
gst_message_parse_state_changed (msg, &old, &new, &pending);
{
gchar *dump_name = g_strconcat ("state_changed-",
gst_element_state_get_name (old), "_",
gst_element_state_get_name (new), NULL);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (msg->src),
GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
g_free (dump_name);
}
}
break;
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg_info = NULL;
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "error");
gst_message_parse_error (msg, &err, &dbg_info);
g_printerr ("ERROR from element %s: %s\n",
GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free (err);
g_free (dbg_info);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "eos");
g_print ("EOS received\n");
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static void
_webrtc_pad_added (GstElement * webrtc, GstPad * new_pad, GstElement * pipe)
{
GstElement *out;
GstPad *sink;
if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
return;
out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
"videoconvert ! queue ! xvimagesink", TRUE, NULL);
gst_bin_add (GST_BIN (pipe), out);
gst_element_sync_state_with_parent (out);
sink = out->sinkpads->data;
gst_pad_link (new_pad, sink);
}
static void
_on_answer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *answer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "answer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (answer->sdp);
g_print ("Created answer:\n%s\n", desc);
g_free (desc);
/* this is one way to tell webrtcbin that we don't want to be notified when
* this task is complete: set a NULL promise */
g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
/* this is another way to tell webrtcbin that we don't want to be notified
* when this task is complete: interrupt the promise */
promise = gst_promise_new ();
g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
gst_promise_interrupt (promise);
gst_promise_unref (promise);
gst_webrtc_session_description_free (answer);
}
static void
_on_offer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *offer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "offer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (offer->sdp);
g_print ("Created offer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
gst_webrtc_session_description_free (offer);
}
static void
_on_negotiation_needed (GstElement * element, gpointer user_data)
{
GstPromise *promise;
promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
}
static void
_on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
GstElement * other)
{
g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
}
int
main (int argc, char *argv[])
{
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
pipe1 =
gst_parse_launch ("videotestsrc ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! "
"webrtcbin name=smpte videotestsrc pattern=ball ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! webrtcbin name=ball",
NULL);
bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "smpte");
g_signal_connect (webrtc1, "on-negotiation-needed",
G_CALLBACK (_on_negotiation_needed), NULL);
g_signal_connect (webrtc1, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "ball");
g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
g_signal_connect (webrtc1, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc2);
g_signal_connect (webrtc2, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc1);
g_print ("Starting pipeline\n");
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
g_main_loop_run (loop);
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
g_print ("Pipeline stopped\n");
gst_object_unref (webrtc1);
gst_object_unref (webrtc2);
gst_bus_remove_watch (bus1);
gst_object_unref (bus1);
gst_object_unref (pipe1);
gst_deinit ();
return 0;
}

View file

@ -0,0 +1,215 @@
#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc.h>
#include <string.h>
static GMainLoop *loop;
static GstElement *pipe1, *webrtc1, *webrtc2;
static GstBus *bus1;
static gboolean
_bus_watch (GstBus * bus, GstMessage * msg, GstElement * pipe)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_STATE_CHANGED:
if (GST_ELEMENT (msg->src) == pipe) {
GstState old, new, pending;
gst_message_parse_state_changed (msg, &old, &new, &pending);
{
gchar *dump_name = g_strconcat ("state_changed-",
gst_element_state_get_name (old), "_",
gst_element_state_get_name (new), NULL);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (msg->src),
GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
g_free (dump_name);
}
}
break;
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg_info = NULL;
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "error");
gst_message_parse_error (msg, &err, &dbg_info);
g_printerr ("ERROR from element %s: %s\n",
GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free (err);
g_free (dbg_info);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "eos");
g_print ("EOS received\n");
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static void
_webrtc_pad_added (GstElement * webrtc, GstPad * new_pad, GstElement * pipe)
{
GstElement *out = NULL;
GstPad *sink = NULL;
GstCaps *caps;
GstStructure *s;
const gchar *encoding_name;
if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
return;
caps = gst_pad_get_current_caps (new_pad);
if (!caps)
caps = gst_pad_query_caps (new_pad, NULL);
GST_ERROR_OBJECT (new_pad, "caps %" GST_PTR_FORMAT, caps);
g_assert (gst_caps_is_fixed (caps));
s = gst_caps_get_structure (caps, 0);
encoding_name = gst_structure_get_string (s, "encoding-name");
if (g_strcmp0 (encoding_name, "VP8") == 0) {
out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
"videoconvert ! queue ! xvimagesink sync=false", TRUE, NULL);
} else if (g_strcmp0 (encoding_name, "OPUS") == 0) {
out = gst_parse_bin_from_description ("rtpopusdepay ! opusdec ! "
"audioconvert ! audioresample ! audiorate ! queue ! autoaudiosink",
TRUE, NULL);
} else {
g_critical ("Unknown encoding name %s", encoding_name);
g_assert_not_reached ();
}
gst_bin_add (GST_BIN (pipe), out);
gst_element_sync_state_with_parent (out);
sink = out->sinkpads->data;
gst_pad_link (new_pad, sink);
gst_caps_unref (caps);
}
static void
_on_answer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *answer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "answer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (answer->sdp);
g_print ("Created answer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
gst_webrtc_session_description_free (answer);
}
static void
_on_offer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *offer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "offer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (offer->sdp);
g_print ("Created offer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
gst_webrtc_session_description_free (offer);
}
static void
_on_negotiation_needed (GstElement * element, gpointer user_data)
{
GstPromise *promise;
promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
}
static void
_on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
GstElement * other)
{
g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
}
int
main (int argc, char *argv[])
{
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
pipe1 =
gst_parse_launch ("webrtcbin name=smpte webrtcbin name=ball "
"videotestsrc pattern=smpte ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! smpte.sink_0 "
"audiotestsrc ! opusenc ! rtpopuspay ! queue ! "
"application/x-rtp,media=audio,payload=97,encoding-name=OPUS ! smpte.sink_1 "
"videotestsrc pattern=ball ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! ball.sink_1 "
"audiotestsrc wave=saw ! opusenc ! rtpopuspay ! queue ! "
"application/x-rtp,media=audio,payload=97,encoding-name=OPUS ! ball.sink_0 ",
NULL);
bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "smpte");
g_signal_connect (webrtc1, "on-negotiation-needed",
G_CALLBACK (_on_negotiation_needed), NULL);
g_signal_connect (webrtc1, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "ball");
g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
g_signal_connect (webrtc1, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc2);
g_signal_connect (webrtc2, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc1);
g_print ("Starting pipeline\n");
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
g_main_loop_run (loop);
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
g_print ("Pipeline stopped\n");
gst_object_unref (webrtc1);
gst_object_unref (webrtc2);
gst_bus_remove_watch (bus1);
gst_object_unref (bus1);
gst_object_unref (pipe1);
gst_deinit ();
return 0;
}