/* GStreamer * Copyright (C) 2017 Matthew Waters * * 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 #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; gulong on_new_selected_pair_id; gulong on_component_state_changed_id; }; #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); NiceAgent *agent; GstWebRTCICE *webrtc_ice = g_weak_ref_get (&nice->stream->ice_weak); if (webrtc_ice) { g_object_get (webrtc_ice, "agent", &agent, NULL); if (nice->priv->on_component_state_changed_id != 0) { g_signal_handler_disconnect (agent, nice->priv->on_component_state_changed_id); } if (nice->priv->on_new_selected_pair_id != 0) { g_signal_handler_disconnect (agent, nice->priv->on_new_selected_pair_id); } g_object_unref (agent); gst_object_unref (webrtc_ice); } 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; GstWebRTCICE *webrtc_ice = g_weak_ref_get (&nice->stream->ice_weak); g_assert (webrtc_ice != NULL); g_object_get (webrtc_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); gst_object_unref (webrtc_ice); 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); gst_object_unref (webrtc_ice); } static void _on_new_selected_pair (NiceAgent * agent, guint stream_id, NiceComponentType component, NiceCandidate * lcandidate, NiceCandidate * rcandidate, GWeakRef * nice_weak) { GstWebRTCNiceTransport *nice = g_weak_ref_get (nice_weak); GstWebRTCICETransport *ice; GstWebRTCICEComponent comp = _nice_component_to_gst (component); guint our_stream_id; if (!nice) return; ice = GST_WEBRTC_ICE_TRANSPORT (nice); g_object_get (nice->stream, "stream-id", &our_stream_id, NULL); if (stream_id != our_stream_id) goto cleanup; if (comp != ice->component) goto cleanup; gst_webrtc_ice_transport_selected_pair_change (ice); cleanup: gst_object_unref (nice); } static void _on_component_state_changed (NiceAgent * agent, guint stream_id, NiceComponentType component, NiceComponentState state, GWeakRef * nice_weak) { GstWebRTCNiceTransport *nice = g_weak_ref_get (nice_weak); GstWebRTCICETransport *ice; GstWebRTCICEComponent comp = _nice_component_to_gst (component); guint our_stream_id; if (!nice) return; ice = GST_WEBRTC_ICE_TRANSPORT (nice); g_object_get (nice->stream, "stream-id", &our_stream_id, NULL); if (stream_id != our_stream_id) goto cleanup; if (comp != ice->component) goto cleanup; 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)); cleanup: gst_object_unref (nice); } static GWeakRef * weak_new (GstWebRTCNiceTransport * nice) { GWeakRef *weak = g_new0 (GWeakRef, 1); g_weak_ref_init (weak, nice); return weak; } static void weak_free (GWeakRef * weak) { g_weak_ref_clear (weak); g_free (weak); } 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; GstWebRTCICE *webrtc_ice = g_weak_ref_get (&nice->stream->ice_weak); g_assert (webrtc_ice != NULL); g_object_get (nice->stream, "stream-id", &our_stream_id, NULL); g_object_get (webrtc_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; nice->priv->on_component_state_changed_id = g_signal_connect_data (agent, "component-state-changed", G_CALLBACK (_on_component_state_changed), weak_new (nice), (GClosureNotify) weak_free, (GConnectFlags) 0); nice->priv->on_new_selected_pair_id = g_signal_connect_data (agent, "new-selected-pair-full", G_CALLBACK (_on_new_selected_pair), weak_new (nice), (GClosureNotify) weak_free, (GConnectFlags) 0); 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); gst_object_unref (webrtc_ice); 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); }