Skip to content

Commit

Permalink
Merge pull request godotengine#65659 from Faless/rtc/4.x_expose_more
Browse files Browse the repository at this point in the history
[WebRTC] Expose more of the WebRTC API.
  • Loading branch information
Faless committed Sep 13, 2022
2 parents 22a09fe + dbff58e commit f6b36f5
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 83 deletions.
39 changes: 39 additions & 0 deletions modules/webrtc/doc_classes/WebRTCPeerConnection.xml
Expand Up @@ -67,6 +67,18 @@
Returns the connection state. See [enum ConnectionState].
</description>
</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">
<return type="int" enum="Error" />
<param index="0" name="configuration" type="Dictionary" default="{}" />
Expand Down Expand Up @@ -165,5 +177,32 @@
<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
The peer connection is closed (after calling [method close] for example).
</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>
</class>
10 changes: 10 additions & 0 deletions modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
Expand Up @@ -37,6 +37,16 @@
<description>
</description>
</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">
<return type="int" enum="Error" />
<param index="0" name="p_config" type="Dictionary" />
Expand Down
196 changes: 120 additions & 76 deletions modules/webrtc/library_godot_webrtc.js
Expand Up @@ -220,64 +220,123 @@ mergeInto(LibraryManager.library, GodotRTCDataChannel);
const GodotRTCPeerConnection = {
$GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
$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;
}

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) {
const ref = IDHandler.get(p_id);
if (!ref) {
if (!IDHandler.get(p_id)) {
return;
}
const type_str = GodotRuntime.allocString(session.type);
Expand All @@ -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',
Expand All @@ -331,14 +382,7 @@ const GodotRTCPeerConnection = {

godot_js_rtc_pc_destroy__sig: 'vi',
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',
Expand Down
13 changes: 13 additions & 0 deletions modules/webrtc/webrtc_peer_connection.cpp
Expand Up @@ -69,6 +69,8 @@ void WebRTCPeerConnection::_bind_methods() {
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_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("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
Expand All @@ -80,6 +82,17 @@ void WebRTCPeerConnection::_bind_methods() {
BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
BIND_ENUM_CONSTANT(STATE_FAILED);
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() {
Expand Down
19 changes: 19 additions & 0 deletions modules/webrtc/webrtc_peer_connection.h
Expand Up @@ -47,6 +47,21 @@ class WebRTCPeerConnection : public RefCounted {
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:
static StringName default_extension;

Expand All @@ -57,6 +72,8 @@ class WebRTCPeerConnection : public RefCounted {
static void set_default_extension(const StringName &p_name);

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 Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
Expand All @@ -74,5 +91,7 @@ class WebRTCPeerConnection : public RefCounted {
};

VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
VARIANT_ENUM_CAST(WebRTCPeerConnection::GatheringState);
VARIANT_ENUM_CAST(WebRTCPeerConnection::SignalingState);

#endif // WEBRTC_PEER_CONNECTION_H
2 changes: 2 additions & 0 deletions modules/webrtc/webrtc_peer_connection_extension.cpp
Expand Up @@ -32,6 +32,8 @@

void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_connection_state);
GDVIRTUAL_BIND(_get_gathering_state);
GDVIRTUAL_BIND(_get_signaling_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
GDVIRTUAL_BIND(_create_offer);
Expand Down
2 changes: 2 additions & 0 deletions modules/webrtc/webrtc_peer_connection_extension.h
Expand Up @@ -53,6 +53,8 @@ class WebRTCPeerConnectionExtension : public WebRTCPeerConnection {

/** GDExtension **/
EXBIND0RC(ConnectionState, get_connection_state);
EXBIND0RC(GatheringState, get_gathering_state);
EXBIND0RC(SignalingState, get_signaling_state);
EXBIND1R(Error, initialize, Dictionary);
EXBIND0R(Error, create_offer);
EXBIND2R(Error, set_remote_description, String, String);
Expand Down

0 comments on commit f6b36f5

Please sign in to comment.