/* 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" #include <gio/gnetworking.h> #define GST_CAT_DEFAULT gst_webrtc_nice_transport_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); enum { SIGNAL_0, LAST_SIGNAL, }; enum { PROP_0, PROP_STREAM, PROP_SEND_BUFFER_SIZE, PROP_RECEIVE_BUFFER_SIZE }; //static guint gst_webrtc_nice_transport_signals[LAST_SIGNAL] = { 0 }; struct _GstWebRTCNiceTransportPrivate { gboolean running; gint send_buffer_size; gint receive_buffer_size; }; #define gst_webrtc_nice_transport_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstWebRTCNiceTransport, gst_webrtc_nice_transport, GST_TYPE_WEBRTC_ICE_TRANSPORT, G_ADD_PRIVATE (GstWebRTCNiceTransport) GST_DEBUG_CATEGORY_INIT (gst_webrtc_nice_transport_debug, "webrtcnicetransport", 0, "webrtcnicetransport"); ); 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; case PROP_SEND_BUFFER_SIZE: nice->priv->send_buffer_size = g_value_get_int (value); gst_webrtc_nice_transport_update_buffer_size (nice); break; case PROP_RECEIVE_BUFFER_SIZE: nice->priv->receive_buffer_size = g_value_get_int (value); gst_webrtc_nice_transport_update_buffer_size (nice); 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; case PROP_SEND_BUFFER_SIZE: g_value_set_int (value, nice->priv->send_buffer_size); break; case PROP_RECEIVE_BUFFER_SIZE: g_value_set_int (value, nice->priv->receive_buffer_size); 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); } void gst_webrtc_nice_transport_update_buffer_size (GstWebRTCNiceTransport * nice) { NiceAgent *agent = NULL; GPtrArray *sockets; guint i; g_object_get (nice->stream->ice, "agent", &agent, NULL); g_assert (agent != NULL); sockets = nice_agent_get_sockets (agent, nice->stream->stream_id, 1); if (sockets == NULL) { g_object_unref (agent); return; } for (i = 0; i < sockets->len; i++) { GSocket *gsocket = g_ptr_array_index (sockets, i); #ifdef SO_SNDBUF if (nice->priv->send_buffer_size != 0) { GError *gerror = NULL; if (!g_socket_set_option (gsocket, SOL_SOCKET, SO_SNDBUF, nice->priv->send_buffer_size, &gerror)) GST_WARNING_OBJECT (nice, "Could not set send buffer size : %s", gerror->message); g_clear_error (&gerror); } #endif #ifdef SO_RCVBUF if (nice->priv->receive_buffer_size != 0) { GError *gerror = NULL; if (!g_socket_set_option (gsocket, SOL_SOCKET, SO_RCVBUF, nice->priv->receive_buffer_size, &gerror)) GST_WARNING_OBJECT (nice, "Could not set send receive size : %s", gerror->message); g_clear_error (&gerror); } #endif } g_ptr_array_unref (sockets); g_object_unref (agent); } 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, "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; 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)); /** * GstWebRTCNiceTransport:send-buffer-size: * * Size of the kernel send buffer in bytes, 0=default * * Since: 1.20 */ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEND_BUFFER_SIZE, g_param_spec_int ("send-buffer-size", "Send Buffer Size", "Size of the kernel send buffer in bytes, 0=default", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GstWebRTCNiceTransport:receive-buffer-size: * * Size of the kernel receive buffer in bytes, 0=default * * Since: 1.20 */ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RECEIVE_BUFFER_SIZE, g_param_spec_int ("receive-buffer-size", "Receive Buffer Size", "Size of the kernel receive buffer in bytes, 0=default", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void gst_webrtc_nice_transport_init (GstWebRTCNiceTransport * nice) { nice->priv = gst_webrtc_nice_transport_get_instance_private (nice); } GstWebRTCNiceTransport * gst_webrtc_nice_transport_new (GstWebRTCICEStream * stream, GstWebRTCICEComponent component) { return g_object_new (GST_TYPE_WEBRTC_NICE_TRANSPORT, "stream", stream, "component", component, NULL); }