Browse Source

[WebRTC] Expose more of the WebRTC API.

Add get_gathering_state() returning the iceGatheringState of the
connection.

Add get_signaling_state() returning the signalingState of the
connection.

Improve JS library.
Fabio Alessandrelli 2 years ago
parent
commit
dbff58ebdd

+ 39 - 0
modules/webrtc/doc_classes/WebRTCPeerConnection.xml

@@ -67,6 +67,18 @@
 				Returns the connection state. See [enum ConnectionState].
 				Returns the connection state. See [enum ConnectionState].
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="get_gathering_state" qualifiers="const">
+			<return type="int" enum="WebRTCPeerConnection.GatheringState" />
+			<description>
+				Returns the ICE [enum GatheringState] of the connection. This lets you detect, for example, when collection of ICE candidates has finished.
+			</description>
+		</method>
+		<method name="get_signaling_state" qualifiers="const">
+			<return type="int" enum="WebRTCPeerConnection.SignalingState" />
+			<description>
+				Returns the [enum SignalingState] on the local end of the connection while connecting or reconnecting to another peer.
+			</description>
+		</method>
 		<method name="initialize">
 		<method name="initialize">
 			<return type="int" enum="Error" />
 			<return type="int" enum="Error" />
 			<param index="0" name="configuration" type="Dictionary" default="{}" />
 			<param index="0" name="configuration" type="Dictionary" default="{}" />
@@ -165,5 +177,32 @@
 		<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
 		<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
 			The peer connection is closed (after calling [method close] for example).
 			The peer connection is closed (after calling [method close] for example).
 		</constant>
 		</constant>
+		<constant name="GATHERING_STATE_NEW" value="0" enum="GatheringState">
+			The peer connection was just created and hasn't done any networking yet.
+		</constant>
+		<constant name="GATHERING_STATE_GATHERING" value="1" enum="GatheringState">
+			The ICE agent is in the process of gathering candidates for the connection.
+		</constant>
+		<constant name="GATHERING_STATE_COMPLETE" value="2" enum="GatheringState">
+			The ICE agent has finished gathering candidates. If something happens that requires collecting new candidates, such as a new interface being added or the addition of a new ICE server, the state will revert to gathering to gather those candidates.
+		</constant>
+		<constant name="SIGNALING_STATE_STABLE" value="0" enum="SignalingState">
+			There is no ongoing exchange of offer and answer underway. This may mean that the [WebRTCPeerConnection] is new ([constant STATE_NEW]) or that negotiation is complete and a connection has been established ([constant STATE_CONNECTED]).
+		</constant>
+		<constant name="SIGNALING_STATE_HAVE_LOCAL_OFFER" value="1" enum="SignalingState">
+			The local peer has called [method set_local_description], passing in SDP representing an offer (usually created by calling [method create_offer]), and the offer has been applied successfully.
+		</constant>
+		<constant name="SIGNALING_STATE_HAVE_REMOTE_OFFER" value="2" enum="SignalingState">
+			The remote peer has created an offer and used the signaling server to deliver it to the local peer, which has set the offer as the remote description by calling [method set_remote_description].
+		</constant>
+		<constant name="SIGNALING_STATE_HAVE_LOCAL_PRANSWER" value="3" enum="SignalingState">
+			The offer sent by the remote peer has been applied and an answer has been created and applied by calling [method set_local_description]. This provisional answer describes the supported media formats and so forth, but may not have a complete set of ICE candidates included. Further candidates will be delivered separately later.
+		</constant>
+		<constant name="SIGNALING_STATE_HAVE_REMOTE_PRANSWER" value="4" enum="SignalingState">
+			A provisional answer has been received and successfully applied in response to an offer previously sent and established by calling [method set_local_description].
+		</constant>
+		<constant name="SIGNALING_STATE_CLOSED" value="5" enum="SignalingState">
+			The [WebRTCPeerConnection] has been closed.
+		</constant>
 	</constants>
 	</constants>
 </class>
 </class>

+ 10 - 0
modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml

@@ -37,6 +37,16 @@
 			<description>
 			<description>
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_get_gathering_state" qualifiers="virtual const">
+			<return type="int" enum="WebRTCPeerConnection.GatheringState" />
+			<description>
+			</description>
+		</method>
+		<method name="_get_signaling_state" qualifiers="virtual const">
+			<return type="int" enum="WebRTCPeerConnection.SignalingState" />
+			<description>
+			</description>
+		</method>
 		<method name="_initialize" qualifiers="virtual">
 		<method name="_initialize" qualifiers="virtual">
 			<return type="int" enum="Error" />
 			<return type="int" enum="Error" />
 			<param index="0" name="p_config" type="Dictionary" />
 			<param index="0" name="p_config" type="Dictionary" />

+ 120 - 76
modules/webrtc/library_godot_webrtc.js

@@ -220,64 +220,123 @@ mergeInto(LibraryManager.library, GodotRTCDataChannel);
 const GodotRTCPeerConnection = {
 const GodotRTCPeerConnection = {
 	$GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
 	$GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
 	$GodotRTCPeerConnection: {
 	$GodotRTCPeerConnection: {
-		onstatechange: function (p_id, p_conn, callback, event) {
-			const ref = IDHandler.get(p_id);
-			if (!ref) {
-				return;
-			}
-			let state;
-			switch (p_conn.iceConnectionState) {
-			case 'new':
-				state = 0;
-				break;
-			case 'checking':
-				state = 1;
-				break;
-			case 'connected':
-			case 'completed':
-				state = 2;
-				break;
-			case 'disconnected':
-				state = 3;
-				break;
-			case 'failed':
-				state = 4;
-				break;
-			case 'closed':
-			default:
-				state = 5;
-				break;
-			}
-			callback(state);
+		// Enums
+		ConnectionState: {
+			'new': 0,
+			'connecting': 1,
+			'connected': 2,
+			'disconnected': 3,
+			'failed': 4,
+			'closed': 5,
 		},
 		},
 
 
-		onicecandidate: function (p_id, callback, event) {
-			const ref = IDHandler.get(p_id);
-			if (!ref || !event.candidate) {
-				return;
+		ConnectionStateCompat: {
+			// Using values from IceConnectionState for browsers that do not support ConnectionState (notably Firefox).
+			'new': 0,
+			'checking': 1,
+			'connected': 2,
+			'completed': 2,
+			'disconnected': 3,
+			'failed': 4,
+			'closed': 5,
+		},
+
+		IceGatheringState: {
+			'new': 0,
+			'gathering': 1,
+			'complete': 2,
+		},
+
+		SignalingState: {
+			'stable': 0,
+			'have-local-offer': 1,
+			'have-remote-offer': 2,
+			'have-local-pranswer': 3,
+			'have-remote-pranswer': 4,
+			'closed': 5,
+		},
+
+		// Callbacks
+		create: function (config, onConnectionChange, onSignalingChange, onIceGatheringChange, onIceCandidate, onDataChannel) {
+			let conn = null;
+			try {
+				conn = new RTCPeerConnection(config);
+			} catch (e) {
+				GodotRuntime.error(e);
+				return 0;
 			}
 			}
 
 
-			const c = event.candidate;
-			const candidate_str = GodotRuntime.allocString(c.candidate);
-			const mid_str = GodotRuntime.allocString(c.sdpMid);
-			callback(mid_str, c.sdpMLineIndex, candidate_str);
-			GodotRuntime.free(candidate_str);
-			GodotRuntime.free(mid_str);
+			const id = IDHandler.add(conn);
+
+			if ('connectionState' in conn && conn['connectionState'] !== undefined) {
+				// Use "connectionState" if supported
+				conn.onconnectionstatechange = function (event) {
+					if (!IDHandler.get(id)) {
+						return;
+					}
+					onConnectionChange(GodotRTCPeerConnection.ConnectionState[conn.connectionState] || 0);
+				};
+			} else {
+				// Fall back to using "iceConnectionState" when "connectionState" is not supported (notably Firefox).
+				conn.oniceconnectionstatechange = function (event) {
+					if (!IDHandler.get(id)) {
+						return;
+					}
+					onConnectionChange(GodotRTCPeerConnection.ConnectionStateCompat[conn.iceConnectionState] || 0);
+				};
+			}
+			conn.onicegatheringstatechange = function (event) {
+				if (!IDHandler.get(id)) {
+					return;
+				}
+				onIceGatheringChange(GodotRTCPeerConnection.IceGatheringState[conn.iceGatheringState] || 0);
+			};
+			conn.onsignalingstatechange = function (event) {
+				if (!IDHandler.get(id)) {
+					return;
+				}
+				onSignalingChange(GodotRTCPeerConnection.SignalingState[conn.signalingState] || 0);
+			};
+			conn.onicecandidate = function (event) {
+				if (!IDHandler.get(id)) {
+					return;
+				}
+				const c = event.candidate;
+				if (!c || !c.candidate) {
+					return;
+				}
+				const candidate_str = GodotRuntime.allocString(c.candidate);
+				const mid_str = GodotRuntime.allocString(c.sdpMid);
+				onIceCandidate(mid_str, c.sdpMLineIndex, candidate_str);
+				GodotRuntime.free(candidate_str);
+				GodotRuntime.free(mid_str);
+			};
+			conn.ondatachannel = function (event) {
+				if (!IDHandler.get(id)) {
+					return;
+				}
+				const cid = IDHandler.add(event.channel);
+				onDataChannel(cid);
+			};
+			return id;
 		},
 		},
 
 
-		ondatachannel: function (p_id, callback, event) {
-			const ref = IDHandler.get(p_id);
-			if (!ref) {
+		destroy: function (p_id) {
+			const conn = IDHandler.get(p_id);
+			if (!conn) {
 				return;
 				return;
 			}
 			}
-
-			const cid = IDHandler.add(event.channel);
-			callback(cid);
+			conn.onconnectionstatechange = null;
+			conn.oniceconnectionstatechange = null;
+			conn.onicegatheringstatechange = null;
+			conn.onsignalingstatechange = null;
+			conn.onicecandidate = null;
+			conn.ondatachannel = null;
+			IDHandler.remove(p_id);
 		},
 		},
 
 
 		onsession: function (p_id, callback, session) {
 		onsession: function (p_id, callback, session) {
-			const ref = IDHandler.get(p_id);
-			if (!ref) {
+			if (!IDHandler.get(p_id)) {
 				return;
 				return;
 			}
 			}
 			const type_str = GodotRuntime.allocString(session.type);
 			const type_str = GodotRuntime.allocString(session.type);
@@ -297,27 +356,19 @@ const GodotRTCPeerConnection = {
 		},
 		},
 	},
 	},
 
 
-	godot_js_rtc_pc_create__sig: 'iiiiii',
-	godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
-		const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
-		const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
-		const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref);
-
-		const config = JSON.parse(GodotRuntime.parseString(p_config));
-		let conn = null;
-		try {
-			conn = new RTCPeerConnection(config);
-		} catch (e) {
-			GodotRuntime.error(e);
-			return 0;
-		}
-
-		const base = GodotRTCPeerConnection;
-		const id = IDHandler.add(conn);
-		conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
-		conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
-		conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
-		return id;
+	godot_js_rtc_pc_create__sig: 'iiiiiiii',
+	godot_js_rtc_pc_create: function (p_config, p_ref, p_on_connection_state_change, p_on_ice_gathering_state_change, p_on_signaling_state_change, p_on_ice_candidate, p_on_datachannel) {
+		const wrap = function (p_func) {
+			return GodotRuntime.get_func(p_func).bind(null, p_ref);
+		};
+		return GodotRTCPeerConnection.create(
+			JSON.parse(GodotRuntime.parseString(p_config)),
+			wrap(p_on_connection_state_change),
+			wrap(p_on_signaling_state_change),
+			wrap(p_on_ice_gathering_state_change),
+			wrap(p_on_ice_candidate),
+			wrap(p_on_datachannel)
+		);
 	},
 	},
 
 
 	godot_js_rtc_pc_close__sig: 'vi',
 	godot_js_rtc_pc_close__sig: 'vi',
@@ -331,14 +382,7 @@ const GodotRTCPeerConnection = {
 
 
 	godot_js_rtc_pc_destroy__sig: 'vi',
 	godot_js_rtc_pc_destroy__sig: 'vi',
 	godot_js_rtc_pc_destroy: function (p_id) {
 	godot_js_rtc_pc_destroy: function (p_id) {
-		const ref = IDHandler.get(p_id);
-		if (!ref) {
-			return;
-		}
-		ref.oniceconnectionstatechange = null;
-		ref.onicecandidate = null;
-		ref.ondatachannel = null;
-		IDHandler.remove(p_id);
+		GodotRTCPeerConnection.destroy(p_id);
 	},
 	},
 
 
 	godot_js_rtc_pc_offer_create__sig: 'viiii',
 	godot_js_rtc_pc_offer_create__sig: 'viiii',

+ 13 - 0
modules/webrtc/webrtc_peer_connection.cpp

@@ -69,6 +69,8 @@ void WebRTCPeerConnection::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
 	ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
 
 
 	ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
 	ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
+	ClassDB::bind_method(D_METHOD("get_gathering_state"), &WebRTCPeerConnection::get_gathering_state);
+	ClassDB::bind_method(D_METHOD("get_signaling_state"), &WebRTCPeerConnection::get_signaling_state);
 
 
 	ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
 	ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
 	ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
 	ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
@@ -80,6 +82,17 @@ void WebRTCPeerConnection::_bind_methods() {
 	BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
 	BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
 	BIND_ENUM_CONSTANT(STATE_FAILED);
 	BIND_ENUM_CONSTANT(STATE_FAILED);
 	BIND_ENUM_CONSTANT(STATE_CLOSED);
 	BIND_ENUM_CONSTANT(STATE_CLOSED);
+
+	BIND_ENUM_CONSTANT(GATHERING_STATE_NEW);
+	BIND_ENUM_CONSTANT(GATHERING_STATE_GATHERING);
+	BIND_ENUM_CONSTANT(GATHERING_STATE_COMPLETE);
+
+	BIND_ENUM_CONSTANT(SIGNALING_STATE_STABLE);
+	BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_OFFER);
+	BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_OFFER);
+	BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_PRANSWER);
+	BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_PRANSWER);
+	BIND_ENUM_CONSTANT(SIGNALING_STATE_CLOSED);
 }
 }
 
 
 WebRTCPeerConnection::WebRTCPeerConnection() {
 WebRTCPeerConnection::WebRTCPeerConnection() {

+ 19 - 0
modules/webrtc/webrtc_peer_connection.h

@@ -47,6 +47,21 @@ public:
 		STATE_CLOSED
 		STATE_CLOSED
 	};
 	};
 
 
+	enum GatheringState {
+		GATHERING_STATE_NEW,
+		GATHERING_STATE_GATHERING,
+		GATHERING_STATE_COMPLETE,
+	};
+
+	enum SignalingState {
+		SIGNALING_STATE_STABLE,
+		SIGNALING_STATE_HAVE_LOCAL_OFFER,
+		SIGNALING_STATE_HAVE_REMOTE_OFFER,
+		SIGNALING_STATE_HAVE_LOCAL_PRANSWER,
+		SIGNALING_STATE_HAVE_REMOTE_PRANSWER,
+		SIGNALING_STATE_CLOSED,
+	};
+
 private:
 private:
 	static StringName default_extension;
 	static StringName default_extension;
 
 
@@ -57,6 +72,8 @@ public:
 	static void set_default_extension(const StringName &p_name);
 	static void set_default_extension(const StringName &p_name);
 
 
 	virtual ConnectionState get_connection_state() const = 0;
 	virtual ConnectionState get_connection_state() const = 0;
+	virtual GatheringState get_gathering_state() const = 0;
+	virtual SignalingState get_signaling_state() const = 0;
 
 
 	virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
 	virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
 	virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
 	virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
@@ -74,5 +91,7 @@ public:
 };
 };
 
 
 VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
 VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
+VARIANT_ENUM_CAST(WebRTCPeerConnection::GatheringState);
+VARIANT_ENUM_CAST(WebRTCPeerConnection::SignalingState);
 
 
 #endif // WEBRTC_PEER_CONNECTION_H
 #endif // WEBRTC_PEER_CONNECTION_H

+ 2 - 0
modules/webrtc/webrtc_peer_connection_extension.cpp

@@ -32,6 +32,8 @@
 
 
 void WebRTCPeerConnectionExtension::_bind_methods() {
 void WebRTCPeerConnectionExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_get_connection_state);
 	GDVIRTUAL_BIND(_get_connection_state);
+	GDVIRTUAL_BIND(_get_gathering_state);
+	GDVIRTUAL_BIND(_get_signaling_state);
 	GDVIRTUAL_BIND(_initialize, "p_config");
 	GDVIRTUAL_BIND(_initialize, "p_config");
 	GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
 	GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
 	GDVIRTUAL_BIND(_create_offer);
 	GDVIRTUAL_BIND(_create_offer);

+ 2 - 0
modules/webrtc/webrtc_peer_connection_extension.h

@@ -53,6 +53,8 @@ public:
 
 
 	/** GDExtension **/
 	/** GDExtension **/
 	EXBIND0RC(ConnectionState, get_connection_state);
 	EXBIND0RC(ConnectionState, get_connection_state);
+	EXBIND0RC(GatheringState, get_gathering_state);
+	EXBIND0RC(SignalingState, get_signaling_state);
 	EXBIND1R(Error, initialize, Dictionary);
 	EXBIND1R(Error, initialize, Dictionary);
 	EXBIND0R(Error, create_offer);
 	EXBIND0R(Error, create_offer);
 	EXBIND2R(Error, set_remote_description, String, String);
 	EXBIND2R(Error, set_remote_description, String, String);

+ 19 - 4
modules/webrtc/webrtc_peer_connection_js.cpp

@@ -51,6 +51,16 @@ void WebRTCPeerConnectionJS::_on_connection_state_changed(void *p_obj, int p_sta
 	peer->_conn_state = (ConnectionState)p_state;
 	peer->_conn_state = (ConnectionState)p_state;
 }
 }
 
 
+void WebRTCPeerConnectionJS::_on_gathering_state_changed(void *p_obj, int p_state) {
+	WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+	peer->_gathering_state = (GatheringState)p_state;
+}
+
+void WebRTCPeerConnectionJS::_on_signaling_state_changed(void *p_obj, int p_state) {
+	WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+	peer->_signaling_state = (SignalingState)p_state;
+}
+
 void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
 void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
 	ERR_PRINT("RTCPeerConnection error!");
 	ERR_PRINT("RTCPeerConnection error!");
 }
 }
@@ -100,7 +110,7 @@ Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
 	_conn_state = STATE_NEW;
 	_conn_state = STATE_NEW;
 
 
 	String config = Variant(p_config).to_json_string();
 	String config = Variant(p_config).to_json_string();
-	_js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_ice_candidate, &_on_data_channel);
+	_js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_gathering_state_changed, &_on_signaling_state_changed, &_on_ice_candidate, &_on_data_channel);
 	return _js_id ? OK : FAILED;
 	return _js_id ? OK : FAILED;
 }
 }
 
 
@@ -117,14 +127,19 @@ Error WebRTCPeerConnectionJS::poll() {
 	return OK;
 	return OK;
 }
 }
 
 
+WebRTCPeerConnection::GatheringState WebRTCPeerConnectionJS::get_gathering_state() const {
+	return _gathering_state;
+}
+
+WebRTCPeerConnection::SignalingState WebRTCPeerConnectionJS::get_signaling_state() const {
+	return _signaling_state;
+}
+
 WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
 WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
 	return _conn_state;
 	return _conn_state;
 }
 }
 
 
 WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
 WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
-	_conn_state = STATE_NEW;
-	_js_id = 0;
-
 	Dictionary config;
 	Dictionary config;
 	initialize(config);
 	initialize(config);
 }
 }

+ 11 - 3
modules/webrtc/webrtc_peer_connection_js.h

@@ -37,11 +37,13 @@
 
 
 extern "C" {
 extern "C" {
 typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
 typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnIceGatheringStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnSignalingStateChange)(void *p_obj, int p_state);
 typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
 typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
 typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
 typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
 typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
 typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
 typedef void (*RTCOnError)(void *p_obj);
 typedef void (*RTCOnError)(void *p_obj);
-extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
+extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_connection_state_change, RTCOnIceGatheringStateChange p_on_gathering_state_change, RTCOnSignalingStateChange p_on_signaling_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
 extern void godot_js_rtc_pc_close(int p_id);
 extern void godot_js_rtc_pc_close(int p_id);
 extern void godot_js_rtc_pc_destroy(int p_id);
 extern void godot_js_rtc_pc_destroy(int p_id);
 extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
 extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
@@ -55,10 +57,14 @@ class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
 	GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
 	GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
 
 
 private:
 private:
-	int _js_id;
-	ConnectionState _conn_state;
+	int _js_id = 0;
+	ConnectionState _conn_state = STATE_NEW;
+	GatheringState _gathering_state = GATHERING_STATE_NEW;
+	SignalingState _signaling_state = SIGNALING_STATE_STABLE;
 
 
 	static void _on_connection_state_changed(void *p_obj, int p_state);
 	static void _on_connection_state_changed(void *p_obj, int p_state);
+	static void _on_gathering_state_changed(void *p_obj, int p_state);
+	static void _on_signaling_state_changed(void *p_obj, int p_state);
 	static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
 	static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
 	static void _on_data_channel(void *p_obj, int p_channel);
 	static void _on_data_channel(void *p_obj, int p_channel);
 	static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
 	static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
@@ -66,6 +72,8 @@ private:
 
 
 public:
 public:
 	virtual ConnectionState get_connection_state() const override;
 	virtual ConnectionState get_connection_state() const override;
+	virtual GatheringState get_gathering_state() const override;
+	virtual SignalingState get_signaling_state() const override;
 
 
 	virtual Error initialize(Dictionary configuration = Dictionary()) override;
 	virtual Error initialize(Dictionary configuration = Dictionary()) override;
 	virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary()) override;
 	virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary()) override;