diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/WebRTCLibDataChannel.cpp | 232 | ||||
-rw-r--r-- | src/WebRTCLibDataChannel.hpp | 98 | ||||
-rw-r--r-- | src/WebRTCLibPeerConnection.cpp | 382 | ||||
-rw-r--r-- | src/WebRTCLibPeerConnection.hpp | 111 | ||||
-rw-r--r-- | src/init_gdextension.cpp | 72 | ||||
-rw-r--r-- | src/init_gdnative.cpp (renamed from src/init.cpp) | 10 | ||||
-rw-r--r-- | src/net/WebRTCDataChannelNative.cpp | 38 | ||||
-rw-r--r-- | src/net/WebRTCDataChannelNative.hpp | 40 | ||||
-rw-r--r-- | src/net/WebRTCPeerConnectionNative.cpp | 24 | ||||
-rw-r--r-- | src/net/WebRTCPeerConnectionNative.hpp | 22 |
10 files changed, 546 insertions, 483 deletions
diff --git a/src/WebRTCLibDataChannel.cpp b/src/WebRTCLibDataChannel.cpp index bab9c79..5c4ad5d 100644 --- a/src/WebRTCLibDataChannel.cpp +++ b/src/WebRTCLibDataChannel.cpp @@ -30,138 +30,155 @@ #include "WebRTCLibDataChannel.hpp" +#ifdef GDNATIVE_WEBRTC #include "GDNativeLibrary.hpp" #include "NativeScript.hpp" +#define ERR_UNAVAILABLE GODOT_ERR_UNAVAILABLE +#define FAILED GODOT_FAILED +#define ERR_INVALID_PARAMETER GODOT_ERR_INVALID_PARAMETER +#define OK GODOT_OK +#endif -using namespace godot_webrtc; - -// Channel observer -WebRTCLibDataChannel::ChannelObserver::ChannelObserver(WebRTCLibDataChannel *parent) { - this->parent = parent; -} - -void WebRTCLibDataChannel::ChannelObserver::OnMessage(const webrtc::DataBuffer &buffer) { - parent->queue_packet(buffer.data.data<uint8_t>(), buffer.data.size()); -} - -void WebRTCLibDataChannel::ChannelObserver::OnStateChange() { -} +#include <stdio.h> +#include <string.h> +#include <cstring> -void WebRTCLibDataChannel::ChannelObserver::OnBufferedAmountChange(uint64_t previous_amount) { -} +using namespace godot; +using namespace godot_webrtc; // DataChannel -WebRTCLibDataChannel *WebRTCLibDataChannel::new_data_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel) { +WebRTCLibDataChannel *WebRTCLibDataChannel::new_data_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated) { // Invalid channel result in NULL return - ERR_FAIL_COND_V(p_channel.get() == nullptr, NULL); + ERR_FAIL_COND_V(!p_channel, nullptr); +#ifdef GDNATIVE_WEBRTC // Instance a WebRTCDataChannelGDNative object - godot::WebRTCDataChannelGDNative *out = godot::WebRTCDataChannelGDNative::_new(); - // Set our implementation as it's script - godot::NativeScript *script = godot::NativeScript::_new(); - script->set_library(godot::detail::get_wrapper<godot::GDNativeLibrary>((godot_object *)godot::gdnlib)); + WebRTCDataChannelGDNative *native = WebRTCDataChannelGDNative::_new(); + // Set our implementation as its script + NativeScript *script = NativeScript::_new(); + script->set_library(detail::get_wrapper<GDNativeLibrary>((godot_object *)gdnlib)); script->set_class_name("WebRTCLibDataChannel"); - out->set_script(script); - - // Bind the data channel to the ScriptInstance userdata (our script) - WebRTCLibDataChannel *tmp = out->cast_to<WebRTCLibDataChannel>(out); - tmp->bind_channel(p_channel); - - return tmp; + native->set_script(script); + WebRTCLibDataChannel *out = native->cast_to<WebRTCLibDataChannel>(native); +#else + WebRTCLibDataChannel *out = memnew(WebRTCLibDataChannel); +#endif + // Bind the library data channel to our object. + out->bind_channel(p_channel, p_negotiated); + return out; } -void WebRTCLibDataChannel::bind_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel) { - ERR_FAIL_COND(p_channel.get() == nullptr); +void WebRTCLibDataChannel::bind_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated) { + ERR_FAIL_COND(!p_channel); channel = p_channel; - label = p_channel->label(); - protocol = p_channel->protocol(); - channel->RegisterObserver(&observer); -} - -void WebRTCLibDataChannel::queue_packet(const uint8_t *data, uint32_t size) { + negotiated = p_negotiated; + + // Binding this should be fine as long as we call close when going out of scope. + p_channel->onMessage([this](auto message) { + if (std::holds_alternative<rtc::string>(message)) { + rtc::string str = std::get<rtc::string>(message); + queue_packet(reinterpret_cast<const uint8_t *>(str.c_str()), str.size(), true); + } else if (std::holds_alternative<rtc::binary>(message)) { + rtc::binary bin = std::get<rtc::binary>(message); + queue_packet(reinterpret_cast<const uint8_t *>(&bin[0]), bin.size(), false); + } else { + ERR_PRINT("Message parsing bug. Unknown message type."); + } + }); + p_channel->onOpen([this]() { + channel_state = STATE_OPEN; + }); + p_channel->onClosed([this]() { + channel_state = STATE_CLOSED; + }); + p_channel->onError([](auto error) { + ERR_PRINT("Channel Error: " + String(std::string(error).c_str())); + }); +} + +void WebRTCLibDataChannel::queue_packet(const uint8_t *data, uint32_t size, bool p_is_string) { mutex->lock(); - godot::PoolByteArray packet; + std::vector<uint8_t> packet; packet.resize(size); - { - godot::PoolByteArray::Write w = packet.write(); - memcpy(w.ptr(), data, size); - } - packet_queue.push(packet); + memcpy(&packet[0], data, size); + packet_queue.push(QueuedPacket(packet, p_is_string)); mutex->unlock(); } -void WebRTCLibDataChannel::set_write_mode(godot_int mode) { +void WebRTCLibDataChannel::_set_write_mode(int64_t p_mode) { + ERR_FAIL_COND(p_mode != WRITE_MODE_TEXT && p_mode != WRITE_MODE_BINARY); + write_mode = (WriteMode)p_mode; } -godot_int WebRTCLibDataChannel::get_write_mode() const { - return 0; +int64_t WebRTCLibDataChannel::_get_write_mode() const { + return write_mode; } -bool WebRTCLibDataChannel::was_string_packet() const { - return false; +bool WebRTCLibDataChannel::_was_string_packet() const { + return current_packet.second; } -WebRTCLibDataChannel::ChannelState WebRTCLibDataChannel::get_ready_state() const { - ERR_FAIL_COND_V(channel.get() == nullptr, STATE_CLOSED); - return (ChannelState)channel->state(); +int64_t WebRTCLibDataChannel::_get_ready_state() const { + ERR_FAIL_COND_V(!channel, STATE_CLOSED); + return channel_state; } -const char *WebRTCLibDataChannel::get_label() const { - ERR_FAIL_COND_V(channel.get() == nullptr, ""); - return label.c_str(); +String WebRTCLibDataChannel::_get_label() const { + ERR_FAIL_COND_V(!channel, ""); + return channel->label().c_str(); } -bool WebRTCLibDataChannel::is_ordered() const { - ERR_FAIL_COND_V(channel.get() == nullptr, false); - return channel->ordered(); +bool WebRTCLibDataChannel::_is_ordered() const { + ERR_FAIL_COND_V(!channel, false); + return channel->reliability().unordered == false; } -int WebRTCLibDataChannel::get_id() const { - ERR_FAIL_COND_V(channel.get() == nullptr, -1); - return channel->id(); +int64_t WebRTCLibDataChannel::_get_id() const { + ERR_FAIL_COND_V(!channel, -1); + return channel->id().value_or(-1); } -int WebRTCLibDataChannel::get_max_packet_life_time() const { - ERR_FAIL_COND_V(channel.get() == nullptr, 0); - return channel->maxRetransmitTime(); +int64_t WebRTCLibDataChannel::_get_max_packet_life_time() const { + ERR_FAIL_COND_V(!channel, 0); + return channel->reliability().type == rtc::Reliability::Type::Timed ? std::get<std::chrono::milliseconds>(channel->reliability().rexmit).count() : -1; } -int WebRTCLibDataChannel::get_max_retransmits() const { - ERR_FAIL_COND_V(channel.get() == nullptr, 0); - return channel->maxRetransmits(); +int64_t WebRTCLibDataChannel::_get_max_retransmits() const { + ERR_FAIL_COND_V(!channel, 0); + return channel->reliability().type == rtc::Reliability::Type::Rexmit ? std::get<int>(channel->reliability().rexmit) : -1; } -const char *WebRTCLibDataChannel::get_protocol() const { - ERR_FAIL_COND_V(channel.get() == nullptr, ""); - return protocol.c_str(); +String WebRTCLibDataChannel::_get_protocol() const { + ERR_FAIL_COND_V(!channel, ""); + return channel->protocol().c_str(); } -bool WebRTCLibDataChannel::is_negotiated() const { - ERR_FAIL_COND_V(channel.get() == nullptr, false); - return channel->negotiated(); +bool WebRTCLibDataChannel::_is_negotiated() const { + ERR_FAIL_COND_V(!channel, false); + return negotiated; } -int WebRTCLibDataChannel::get_buffered_amount() const { - ERR_FAIL_COND_V(channel.get() == nullptr, 0); - return channel->buffered_amount(); +int64_t WebRTCLibDataChannel::_get_buffered_amount() const { + ERR_FAIL_COND_V(!channel, 0); + return channel->bufferedAmount(); } -godot_error WebRTCLibDataChannel::poll() { - return GODOT_OK; +int64_t WebRTCLibDataChannel::_poll() { + return OK; } -void WebRTCLibDataChannel::close() { - if (channel.get() != nullptr) { - channel->Close(); - channel->UnregisterObserver(); +void WebRTCLibDataChannel::_close() try { + if (channel) { + channel->close(); } +} catch (...) { } -godot_error WebRTCLibDataChannel::get_packet(const uint8_t **r_buffer, int *r_len) { - ERR_FAIL_COND_V(packet_queue.empty(), GODOT_ERR_UNAVAILABLE); +int64_t WebRTCLibDataChannel::_get_packet(const uint8_t **r_buffer, int32_t *r_len) { + ERR_FAIL_COND_V(packet_queue.empty(), ERR_UNAVAILABLE); mutex->lock(); @@ -169,47 +186,46 @@ godot_error WebRTCLibDataChannel::get_packet(const uint8_t **r_buffer, int *r_le current_packet = packet_queue.front(); packet_queue.pop(); // Set out buffer and size (buffer will be gone at next get_packet or close) - *r_buffer = current_packet.read().ptr(); - *r_len = current_packet.size(); + *r_buffer = ¤t_packet.first[0]; + *r_len = current_packet.first.size(); mutex->unlock(); - return GODOT_OK; + return 0; } -godot_error WebRTCLibDataChannel::put_packet(const uint8_t *p_buffer, int p_len) { - ERR_FAIL_COND_V(channel.get() == nullptr, GODOT_ERR_UNAVAILABLE); - - webrtc::DataBuffer webrtc_buffer(rtc::CopyOnWriteBuffer(p_buffer, p_len), true); - ERR_FAIL_COND_V(!channel->Send(webrtc_buffer), GODOT_FAILED); - - return GODOT_OK; +int64_t WebRTCLibDataChannel::_put_packet(const uint8_t *p_buffer, int64_t p_len) try { + ERR_FAIL_COND_V(!channel, FAILED); + ERR_FAIL_COND_V(channel->isClosed(), FAILED); + if (write_mode == WRITE_MODE_TEXT) { + std::string str(p_len, '\x00'); + std::strncpy(str.data(), (const char *)p_buffer, p_len); + channel->send(str); + } else if (write_mode == WRITE_MODE_BINARY) { + channel->send(reinterpret_cast<const std::byte *>(p_buffer), p_len); + } else { + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + return OK; +} catch (const std::exception &e) { + ERR_PRINT(e.what()); + ERR_FAIL_V(FAILED); } -godot_int WebRTCLibDataChannel::get_available_packet_count() const { +int64_t WebRTCLibDataChannel::_get_available_packet_count() const { return packet_queue.size(); } -godot_int WebRTCLibDataChannel::get_max_packet_size() const { - return 1200; -} - -void WebRTCLibDataChannel::_register_methods() { +int64_t WebRTCLibDataChannel::_get_max_packet_size() const { + return 16384; // See RFC-8831 section 6.6: https://datatracker.ietf.org/doc/rfc8831/ } -void WebRTCLibDataChannel::_init() { - register_interface(&interface); -} - -WebRTCLibDataChannel::WebRTCLibDataChannel() : - observer(this) { +WebRTCLibDataChannel::WebRTCLibDataChannel() { mutex = new std::mutex; } WebRTCLibDataChannel::~WebRTCLibDataChannel() { - close(); - if (_owner) { - register_interface(NULL); - } + _close(); + channel = nullptr; delete mutex; } diff --git a/src/WebRTCLibDataChannel.hpp b/src/WebRTCLibDataChannel.hpp index 63c7fdd..b79eeaa 100644 --- a/src/WebRTCLibDataChannel.hpp +++ b/src/WebRTCLibDataChannel.hpp @@ -31,72 +31,72 @@ #ifndef WEBRTC_DATA_CHANNEL_H #define WEBRTC_DATA_CHANNEL_H +#ifdef GDNATIVE_WEBRTC #include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks -#include "api/peer_connection_interface.h" // interface for all things needed from WebRTC -#include "media/base/media_engine.h" // needed for CreateModularPeerConnectionFactory - -#include "PoolArrays.hpp" #include "net/WebRTCDataChannelNative.hpp" +#define WebRTCDataChannelExtension WebRTCDataChannelNative +#if !defined(GDCLASS) +#define GDCLASS(arg1, arg2) GODOT_CLASS(arg1, arg2) +#endif +#else +#include <godot_cpp/classes/web_rtc_data_channel_extension.hpp> +#endif + #include <mutex> +#include <queue> +#include <utility> + +#include "rtc/rtc.hpp" namespace godot_webrtc { -class WebRTCLibDataChannel : public WebRTCDataChannelNative { - GODOT_CLASS(WebRTCLibDataChannel, WebRTCDataChannelNative); +class WebRTCLibDataChannel : public godot::WebRTCDataChannelExtension { + GDCLASS(WebRTCLibDataChannel, WebRTCDataChannelExtension); private: - class ChannelObserver : public webrtc::DataChannelObserver { - public: - WebRTCLibDataChannel *parent; + using QueuedPacket = std::pair<std::vector<uint8_t>, bool>; + std::mutex *mutex; + std::queue<QueuedPacket> packet_queue; + QueuedPacket current_packet; + std::shared_ptr<rtc::DataChannel> channel = nullptr; - ChannelObserver(WebRTCLibDataChannel *parent); - void OnMessage(const webrtc::DataBuffer &buffer) override; - void OnStateChange() override; // UNUSED - void OnBufferedAmountChange(uint64_t previous_amount) override; // UNUSED - }; + WriteMode write_mode = WRITE_MODE_TEXT; + ChannelState channel_state = STATE_CONNECTING; + bool negotiated = false; - ChannelObserver observer; - rtc::scoped_refptr<webrtc::DataChannelInterface> channel; + void queue_packet(const uint8_t *data, uint32_t size, bool p_is_string); + void bind_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated); - std::mutex *mutex; - std::queue<godot::PoolByteArray> packet_queue; - godot::PoolByteArray current_packet; - std::string label; - std::string protocol; +protected: + static void _bind_methods() {} public: - static WebRTCLibDataChannel *new_data_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel); - static void _register_methods(); + static WebRTCLibDataChannel *new_data_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated); - void _init(); - - void bind_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel); - void queue_packet(const uint8_t *data, uint32_t size); + /* PacketPeer */ + virtual int64_t _get_packet(const uint8_t **r_buffer, int32_t *r_len) override; + virtual int64_t _put_packet(const uint8_t *p_buffer, int64_t p_len) override; + virtual int64_t _get_available_packet_count() const override; + virtual int64_t _get_max_packet_size() const override; /* WebRTCDataChannel */ - void set_write_mode(godot_int mode); - godot_int get_write_mode() const; - bool was_string_packet() const; - - ChannelState get_ready_state() const; - const char *get_label() const; - bool is_ordered() const; - int get_id() const; - int get_max_packet_life_time() const; - int get_max_retransmits() const; - const char *get_protocol() const; - bool is_negotiated() const; - int get_buffered_amount() const; - - godot_error poll(); - void close(); - - /* PacketPeer */ - virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len); - virtual godot_error put_packet(const uint8_t *p_buffer, int p_len); - virtual godot_int get_available_packet_count() const; - virtual godot_int get_max_packet_size() const; + int64_t _poll() override; + void _close() override; + + void _set_write_mode(int64_t p_mode) override; + int64_t _get_write_mode() const override; + bool _was_string_packet() const override; + + int64_t _get_ready_state() const override; + godot::String _get_label() const override; + bool _is_ordered() const override; + int64_t _get_id() const override; + int64_t _get_max_packet_life_time() const override; + int64_t _get_max_retransmits() const override; + godot::String _get_protocol() const override; + bool _is_negotiated() const override; + int64_t _get_buffered_amount() const override; WebRTCLibDataChannel(); ~WebRTCLibDataChannel(); diff --git a/src/WebRTCLibPeerConnection.cpp b/src/WebRTCLibPeerConnection.cpp index d090654..cda018a 100644 --- a/src/WebRTCLibPeerConnection.cpp +++ b/src/WebRTCLibPeerConnection.cpp @@ -29,214 +29,201 @@ /*************************************************************************/ #include "WebRTCLibPeerConnection.hpp" -#include "WebRTCDataChannel.hpp" -#include "WebRTCDataChannelGDNative.hpp" #include "WebRTCLibDataChannel.hpp" +using namespace godot; using namespace godot_webrtc; -std::unique_ptr<rtc::Thread> WebRTCLibPeerConnection::signaling_thread = nullptr; - -// PeerConnectionObserver -void WebRTCLibPeerConnection::GodotPCO::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) { - godot::Dictionary candidateSDP; - godot::String candidateSdpMidName = candidate->sdp_mid().c_str(); - int candidateSdpMlineIndexName = candidate->sdp_mline_index(); - std::string sdp; - candidate->ToString(&sdp); - godot::String candidateSdpName = sdp.c_str(); - parent->queue_signal("ice_candidate_created", 3, candidateSdpMidName, candidateSdpMlineIndexName, candidateSdpName); -} +#ifdef GDNATIVE_WEBRTC +struct CastableError { + godot::Error err_enum; + int64_t err_int; -// SetSessionDescriptionObserver -void WebRTCLibPeerConnection::GodotSSDO::OnSuccess() { - if (make_offer) { - make_offer = false; - parent->peer_connection->CreateAnswer(parent->ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); + operator int64_t() { return err_int; } + operator godot::Error() { return err_enum; } + CastableError(godot::Error p_enum, int64_t p_int) { + err_enum = p_enum; + err_int = p_int; } -} - -// CreateSessionDescriptionObserver -void WebRTCLibPeerConnection::GodotCSDO::OnSuccess(webrtc::SessionDescriptionInterface *desc) { - // serialize this offer and send it to the remote peer: - std::string sdp; - desc->ToString(&sdp); - parent->queue_signal("session_description_created", 2, desc->type().c_str(), sdp.c_str()); -} +}; +#define MKERR(m_err) CastableError(godot::Error::m_err, GODOT_##m_err) +#define OK MKERR(OK) +#define FAILED MKERR(FAILED) +#define ERR_UNCONFIGURED MKERR(ERR_UNCONFIGURED) +#define ERR_UNAVAILABLE MKERR(ERR_UNAVAILABLE) +#define ERR_INVALID_PARAMETER MKERR(ERR_INVALID_PARAMETER) +#define ERR_BUG MKERR(ERR_BUG) +#endif void WebRTCLibPeerConnection::initialize_signaling() { - if (signaling_thread.get() == nullptr) { - signaling_thread = rtc::Thread::Create(); - } - signaling_thread->Start(); +#ifdef DEBUG_ENABLED + rtc::InitLogger(rtc::LogLevel::Debug); +#endif } void WebRTCLibPeerConnection::deinitialize_signaling() { - if (signaling_thread.get() != nullptr) { - signaling_thread->Stop(); - } } -godot_error _parse_ice_server(webrtc::PeerConnectionInterface::RTCConfiguration &r_config, godot::Dictionary p_server) { - godot::Variant v; - webrtc::PeerConnectionInterface::IceServer ice_server; - godot::String url; - - ERR_FAIL_COND_V(!p_server.has("urls"), GODOT_ERR_INVALID_PARAMETER); +Error WebRTCLibPeerConnection::_parse_ice_server(rtc::Configuration &r_config, Dictionary p_server) { + ERR_FAIL_COND_V(!p_server.has("urls"), ERR_INVALID_PARAMETER); // Parse mandatory URL - v = p_server["urls"]; - if (v.get_type() == godot::Variant::STRING) { - url = v; - ice_server.urls.push_back(url.utf8().get_data()); - } else if (v.get_type() == godot::Variant::ARRAY) { - godot::Array names = v; - for (int j = 0; j < names.size(); j++) { - v = names[j]; - ERR_FAIL_COND_V(v.get_type() != godot::Variant::STRING, GODOT_ERR_INVALID_PARAMETER); - url = v; - ice_server.urls.push_back(url.utf8().get_data()); - } + Array urls; + Variant urls_var = p_server["urls"]; + if (urls_var.get_type() == Variant::STRING) { + urls.push_back(urls_var); + } else if (urls_var.get_type() == Variant::ARRAY) { + urls = urls_var; } else { - ERR_FAIL_V(GODOT_ERR_INVALID_PARAMETER); + ERR_FAIL_V(ERR_INVALID_PARAMETER); } // Parse credentials (only meaningful for TURN, only support password) - if (p_server.has("username") && (v = p_server["username"]) && v.get_type() == godot::Variant::STRING) { - ice_server.username = (v.operator godot::String()).utf8().get_data(); + String username; + String credential; + if (p_server.has("username") && p_server["username"].get_type() == Variant::STRING) { + username = p_server["username"]; } - if (p_server.has("credential") && (v = p_server["credential"]) && v.get_type() == godot::Variant::STRING) { - ice_server.password = (v.operator godot::String()).utf8().get_data(); + if (p_server.has("credential") && p_server["credential"].get_type() == Variant::STRING) { + credential = p_server["credential"]; } - - r_config.servers.push_back(ice_server); - return GODOT_OK; + for (int i = 0; i < urls.size(); i++) { + rtc::IceServer srv(urls[i].operator String().utf8().get_data()); + srv.username = username.utf8().get_data(); + srv.password = credential.utf8().get_data(); + r_config.iceServers.push_back(srv); + } + return OK; } -godot_error _parse_channel_config(webrtc::DataChannelInit &r_config, godot::Dictionary p_dict) { - godot::Variant v; -#define _SET_N(PROP, PNAME, TYPE) \ - if (p_dict.has(#PROP)) { \ - v = p_dict[#PROP]; \ - if (v.get_type() == godot::Variant::TYPE) \ - r_config.PNAME = v; \ +Error WebRTCLibPeerConnection::_parse_channel_config(rtc::DataChannelInit &r_config, const Dictionary &p_dict) { + Variant nil; + Variant v; + if (p_dict.has("negotiated")) { + r_config.negotiated = p_dict["negotiated"].operator bool(); } -#define _SET(PROP, TYPE) _SET_N(PROP, PROP, TYPE) - _SET(negotiated, BOOL); - _SET(id, INT); - _SET_N(maxPacketLifeTime, maxRetransmitTime, INT); - _SET(maxRetransmits, INT); - _SET(ordered, BOOL); -#undef _SET - if (p_dict.has("protocol") && (v = p_dict["protocol"]) && v.get_type() == godot::Variant::STRING) { - r_config.protocol = v.operator godot::String().utf8().get_data(); + if (p_dict.has("id")) { + r_config.id = uint16_t(p_dict["id"].operator int32_t()); } - - // ID makes sense only when negotiated is true (and must be set in that case) - ERR_FAIL_COND_V(r_config.negotiated ? r_config.id == -1 : r_config.id != -1, GODOT_ERR_INVALID_PARAMETER); - // Only one of maxRetransmits and maxRetransmitTime can be set on a channel. - ERR_FAIL_COND_V(r_config.maxRetransmits && r_config.maxRetransmitTime, GODOT_ERR_INVALID_PARAMETER); - return GODOT_OK; + // If negotiated it must have an ID, and ID only makes sense when negotiated. + ERR_FAIL_COND_V(r_config.negotiated != r_config.id.has_value(), ERR_INVALID_PARAMETER); + // Channels cannot be both time-constrained and retry-constrained. + ERR_FAIL_COND_V(p_dict.has("maxPacketLifeTime") && p_dict.has("maxRetransmits"), ERR_INVALID_PARAMETER); + if (p_dict.has("maxPacketLifeTime")) { + r_config.reliability.type = rtc::Reliability::Type::Timed; + r_config.reliability.rexmit = std::chrono::milliseconds(p_dict["maxPacketLifeTime"].operator int32_t()); + } else if (p_dict.has("maxRetransmits")) { + r_config.reliability.type = rtc::Reliability::Type::Rexmit; + r_config.reliability.rexmit = p_dict["maxRetransmits"].operator int32_t(); + } + if (p_dict.has("ordered") && p_dict["ordered"].operator bool() == false) { + r_config.reliability.unordered = true; + } + if (p_dict.has("protocol")) { + r_config.protocol = p_dict["protocol"].operator String().utf8().get_data(); + } + return OK; } -WebRTCLibPeerConnection::ConnectionState WebRTCLibPeerConnection::get_connection_state() const { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, STATE_CLOSED); +int64_t WebRTCLibPeerConnection::_get_connection_state() const { + ERR_FAIL_COND_V(peer_connection == nullptr, STATE_CLOSED); - webrtc::PeerConnectionInterface::IceConnectionState state = peer_connection->ice_connection_state(); + rtc::PeerConnection::State state = peer_connection->state(); switch (state) { - case webrtc::PeerConnectionInterface::kIceConnectionNew: + case rtc::PeerConnection::State::New: return STATE_NEW; - case webrtc::PeerConnectionInterface::kIceConnectionChecking: + case rtc::PeerConnection::State::Connecting: return STATE_CONNECTING; - case webrtc::PeerConnectionInterface::kIceConnectionConnected: + case rtc::PeerConnection::State::Connected: return STATE_CONNECTED; - case webrtc::PeerConnectionInterface::kIceConnectionCompleted: - return STATE_CONNECTED; - case webrtc::PeerConnectionInterface::kIceConnectionFailed: - return STATE_FAILED; - case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: + case rtc::PeerConnection::State::Disconnected: return STATE_DISCONNECTED; - case webrtc::PeerConnectionInterface::kIceConnectionClosed: - return STATE_CLOSED; + case rtc::PeerConnection::State::Failed: + return STATE_FAILED; default: return STATE_CLOSED; } } -godot_error WebRTCLibPeerConnection::initialize(const godot_dictionary *p_config) { - webrtc::PeerConnectionInterface::RTCConfiguration config; - godot::Dictionary d = *(godot::Dictionary *)p_config; - godot::Variant v; - if (d.has("iceServers") && (v = d["iceServers"]) && v.get_type() == godot::Variant::ARRAY) { - godot::Array servers = v; +int64_t WebRTCLibPeerConnection::_initialize(const Dictionary &p_config) { + rtc::Configuration config = {}; + if (p_config.has("iceServers") && p_config["iceServers"].get_type() == Variant::ARRAY) { + Array servers = p_config["iceServers"]; for (int i = 0; i < servers.size(); i++) { - v = servers[i]; - ERR_FAIL_COND_V(v.get_type() != godot::Variant::DICTIONARY, GODOT_ERR_INVALID_PARAMETER); - godot_error err; - godot::Dictionary server = v; - err = _parse_ice_server(config, server); - ERR_FAIL_COND_V(err != GODOT_OK, err); + ERR_FAIL_COND_V(servers[i].get_type() != Variant::DICTIONARY, ERR_INVALID_PARAMETER); + Dictionary server = servers[i]; + Error err = _parse_ice_server(config, server); + ERR_FAIL_COND_V(err != OK, FAILED); } } - return _create_pc(config); + return (int64_t)_create_pc(config); } -godot_object *WebRTCLibPeerConnection::create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config) { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, NULL); +Object *WebRTCLibPeerConnection::_create_data_channel(const String &p_channel, const Dictionary &p_channel_config) try { + ERR_FAIL_COND_V(!peer_connection, nullptr); // Read config from dictionary - webrtc::DataChannelInit config; - godot::Dictionary d = *(godot::Dictionary *)p_channel_config; - godot_error err = _parse_channel_config(config, d); - ERR_FAIL_COND_V(err != GODOT_OK, NULL); - - WebRTCLibDataChannel *wrapper = WebRTCLibDataChannel::new_data_channel(peer_connection->CreateDataChannel(p_channel, &config)); - ERR_FAIL_COND_V(wrapper == NULL, NULL); - return wrapper->_owner; + rtc::DataChannelInit config; + + Error err = _parse_channel_config(config, p_channel_config); + ERR_FAIL_COND_V(err != OK, nullptr); + + std::shared_ptr<rtc::DataChannel> ch = peer_connection->createDataChannel(p_channel.utf8().get_data(), config); + ERR_FAIL_COND_V(ch == nullptr, nullptr); + + WebRTCLibDataChannel *wrapper = WebRTCLibDataChannel::new_data_channel(ch, ch->id().has_value()); + ERR_FAIL_COND_V(wrapper == nullptr, nullptr); + return wrapper; +} catch (const std::exception &e) { + ERR_PRINT(e.what()); + ERR_FAIL_V(nullptr); } -godot_error WebRTCLibPeerConnection::create_offer() { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); - peer_connection->CreateOffer(ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); - return GODOT_OK; +int64_t WebRTCLibPeerConnection::_create_offer() try { + ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(_get_connection_state() != STATE_NEW, FAILED); + peer_connection->setLocalDescription(rtc::Description::Type::Offer); + return OK; +} catch (const std::exception &e) { + ERR_PRINT(e.what()); + ERR_FAIL_V(FAILED); } -#define _MAKE_DESC(TYPE, SDP) webrtc::CreateSessionDescription((godot::String(TYPE) == godot::String("offer") ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer), SDP) -godot_error WebRTCLibPeerConnection::set_remote_description(const char *type, const char *sdp) { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); - std::unique_ptr<webrtc::SessionDescriptionInterface> desc = _MAKE_DESC(type, sdp); - if (desc->GetType() == webrtc::SdpType::kOffer) { - ptr_ssdo->make_offer = true; +int64_t WebRTCLibPeerConnection::_set_remote_description(const String &p_type, const String &p_sdp) try { + ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED); + std::string sdp(p_sdp.utf8().get_data()); + std::string type(p_type.utf8().get_data()); + rtc::Description desc(sdp, type); + peer_connection->setRemoteDescription(desc); + // Automatically create the answer. + if (p_type == String("offer")) { + peer_connection->setLocalDescription(rtc::Description::Type::Answer); } - peer_connection->SetRemoteDescription(ptr_ssdo, desc.release()); - return GODOT_OK; + return OK; +} catch (const std::exception &e) { + ERR_PRINT(e.what()); + ERR_FAIL_V(FAILED); } -godot_error WebRTCLibPeerConnection::set_local_description(const char *type, const char *sdp) { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); - std::unique_ptr<webrtc::SessionDescriptionInterface> desc = _MAKE_DESC(type, sdp); - peer_connection->SetLocalDescription(ptr_ssdo, desc.release()); - return GODOT_OK; +int64_t WebRTCLibPeerConnection::_set_local_description(const String &p_type, const String &p_sdp) { + ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED); + // XXX Library quirk. It doesn't seem possible to create offers/answers without setting the local description. + // Ignore this call for now to avoid crash (it's already set automatically!). + //peer_connection->setLocalDescription(p_type == String("offer") ? rtc::Description::Type::Offer : rtc::Description::Type::Answer); + return OK; } -#undef _MAKE_DESC - -godot_error WebRTCLibPeerConnection::add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); - - webrtc::SdpParseError *error = nullptr; - webrtc::IceCandidateInterface *candidate = webrtc::CreateIceCandidate( - sdpMidName, - sdpMlineIndexName, - sdpName, - error); - - ERR_FAIL_COND_V(error || !candidate, GODOT_ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!peer_connection->AddIceCandidate(candidate), GODOT_FAILED); - return GODOT_OK; +int64_t WebRTCLibPeerConnection::_add_ice_candidate(const String &sdpMidName, int64_t sdpMlineIndexName, const String &sdpName) try { + ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED); + rtc::Candidate candidate(sdpName.utf8().get_data(), sdpMidName.utf8().get_data()); + peer_connection->addRemoteCandidate(candidate); + return OK; +} catch (const std::exception &e) { + ERR_PRINT(e.what()); + ERR_FAIL_V(FAILED); } -godot_error WebRTCLibPeerConnection::poll() { - ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); +int64_t WebRTCLibPeerConnection::_poll() { + ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED); while (!signal_queue.empty()) { mutex_signal_queue->lock(); @@ -245,68 +232,83 @@ godot_error WebRTCLibPeerConnection::poll() { mutex_signal_queue->unlock(); signal.emit(this); } - return GODOT_OK; + return OK; } -void WebRTCLibPeerConnection::close() { - if (peer_connection.get() != nullptr) { - peer_connection->Close(); +void WebRTCLibPeerConnection::_close() { + if (peer_connection != nullptr) { + try { + peer_connection->close(); + } catch (...) { + } } - peer_connection = nullptr; + while (!signal_queue.empty()) { signal_queue.pop(); } } -void WebRTCLibPeerConnection::_register_methods() { -} - void WebRTCLibPeerConnection::_init() { +#ifdef GDNATIVE_WEBRTC register_interface(&interface); - - // initialize variables: +#endif mutex_signal_queue = new std::mutex; - // create a PeerConnectionFactoryInterface: - webrtc::PeerConnectionFactoryDependencies deps; - - ERR_FAIL_COND(signaling_thread.get() == nullptr); - deps.signaling_thread = signaling_thread.get(); - pc_factory = webrtc::CreateModularPeerConnectionFactory(std::move(deps)); - - // Create peer connection with default configuration. - webrtc::PeerConnectionInterface::RTCConfiguration config; - _create_pc(config); + _initialize(Dictionary()); } -godot_error WebRTCLibPeerConnection::_create_pc(webrtc::PeerConnectionInterface::RTCConfiguration &config) { - ERR_FAIL_COND_V(pc_factory.get() == nullptr, GODOT_ERR_BUG); - peer_connection = nullptr; - peer_connection = pc_factory->CreatePeerConnection(config, nullptr, nullptr, &pco); - if (peer_connection.get() == nullptr) { // PeerConnection couldn't be created. Fail the method call. - ERR_PRINT("PeerConnection could not be created"); - return GODOT_FAILED; - } - return GODOT_OK; +Error WebRTCLibPeerConnection::_create_pc(rtc::Configuration &r_config) try { + // Prevents libdatachannel from automatically creating offers. + r_config.disableAutoNegotiation = true; + + peer_connection = std::make_shared<rtc::PeerConnection>(r_config); + ERR_FAIL_COND_V(!peer_connection, FAILED); + + // Binding this should be fine as long as we call close when going out of scope. + peer_connection->onLocalDescription([this](rtc::Description description) { + String type = description.type() == rtc::Description::Type::Offer ? "offer" : "answer"; + queue_signal("session_description_created", 2, type, String(std::string(description).c_str())); + }); + peer_connection->onLocalCandidate([this](rtc::Candidate candidate) { + queue_signal("ice_candidate_created", 3, String(candidate.mid().c_str()), 0, String(candidate.candidate().c_str())); + }); + peer_connection->onDataChannel([this](std::shared_ptr<rtc::DataChannel> channel) { + queue_signal("data_channel_received", 1, WebRTCLibDataChannel::new_data_channel(channel, false)); + }); + /* + peer_connection->onStateChange([](rtc::PeerConnection::State state) { + std::cout << "[State: " << state << "]" << std::endl; + }); + + peer_connection->onGatheringStateChange([](rtc::PeerConnection::GatheringState state) { + std::cout << "[Gathering State: " << state << "]" << std::endl; + }); + */ + return OK; +} catch (const std::exception &e) { + ERR_PRINT(e.what()); + ERR_FAIL_V(FAILED); } -WebRTCLibPeerConnection::WebRTCLibPeerConnection() : - pco(this), - ptr_csdo(new rtc::RefCountedObject<GodotCSDO>(this)), - ptr_ssdo(new rtc::RefCountedObject<GodotSSDO>(this)) { +WebRTCLibPeerConnection::WebRTCLibPeerConnection() { +#ifndef GDNATIVE_WEBRTC + _init(); +#endif } WebRTCLibPeerConnection::~WebRTCLibPeerConnection() { +#ifdef GDNATIVE_WEBRTC if (_owner) { - register_interface(NULL); + register_interface(nullptr); } - close(); +#endif + _close(); delete mutex_signal_queue; } -void WebRTCLibPeerConnection::queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1, const godot::Variant &p_arg2, const godot::Variant &p_arg3) { +void WebRTCLibPeerConnection::queue_signal(String p_name, int p_argc, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) { mutex_signal_queue->lock(); - const godot::Variant argv[3] = { p_arg1, p_arg2, p_arg3 }; + const Variant argv[3] = { p_arg1, p_arg2, p_arg3 }; signal_queue.push(Signal(p_name, p_argc, argv)); mutex_signal_queue->unlock(); } diff --git a/src/WebRTCLibPeerConnection.hpp b/src/WebRTCLibPeerConnection.hpp index a265bc1..1a759b2 100644 --- a/src/WebRTCLibPeerConnection.hpp +++ b/src/WebRTCLibPeerConnection.hpp @@ -31,99 +31,61 @@ #ifndef WEBRTC_PEER_H #define WEBRTC_PEER_H +#ifdef GDNATIVE_WEBRTC #include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks -#include "api/peer_connection_interface.h" // interface for all things needed from WebRTC -#include "media/base/media_engine.h" // needed for CreateModularPeerConnectionFactory -#include <mutex> - #include "net/WebRTCPeerConnectionNative.hpp" +#define WebRTCPeerConnectionExtension WebRTCPeerConnectionNative +#if !defined(GDCLASS) +#define GDCLASS(arg1, arg2) GODOT_CLASS(arg1, arg2) +#endif +#else +#include <godot_cpp/classes/web_rtc_peer_connection_extension.hpp> +#endif + +#include "rtc/rtc.hpp" + +#include <mutex> +#include <queue> namespace godot_webrtc { -class WebRTCLibPeerConnection : public WebRTCPeerConnectionNative { - GODOT_CLASS(WebRTCLibPeerConnection, WebRTCPeerConnectionNative); +class WebRTCLibPeerConnection : public godot::WebRTCPeerConnectionExtension { + GDCLASS(WebRTCLibPeerConnection, WebRTCPeerConnectionExtension); private: - godot_error _create_pc(webrtc::PeerConnectionInterface::RTCConfiguration &config); + std::shared_ptr<rtc::PeerConnection> peer_connection = nullptr; + godot::Array candidates; + + godot::Error _create_pc(rtc::Configuration &r_config); + godot::Error _parse_ice_server(rtc::Configuration &r_config, godot::Dictionary p_server); + godot::Error _parse_channel_config(rtc::DataChannelInit &r_config, const godot::Dictionary &p_dict); - static std::unique_ptr<rtc::Thread> signaling_thread; +protected: + static void _bind_methods() {} public: - static void _register_methods(); + static void _register_methods() {} static void initialize_signaling(); static void deinitialize_signaling(); void _init(); - ConnectionState get_connection_state() const; + int64_t _get_connection_state() const override; - godot_error initialize(const godot_dictionary *p_config); - godot_object *create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config); - godot_error create_offer(); - godot_error set_remote_description(const char *type, const char *sdp); - godot_error set_local_description(const char *type, const char *sdp); - godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName); - godot_error poll(); - void close(); + int64_t _initialize(const godot::Dictionary &p_config) override; + godot::Object *_create_data_channel(const godot::String &p_channel, const godot::Dictionary &p_channel_config) override; + int64_t _create_offer() override; + int64_t _set_remote_description(const godot::String &type, const godot::String &sdp) override; + int64_t _set_local_description(const godot::String &type, const godot::String &sdp) override; + int64_t _add_ice_candidate(const godot::String &sdpMidName, int64_t sdpMlineIndexName, const godot::String &sdpName) override; + int64_t _poll() override; + void _close() override; WebRTCLibPeerConnection(); ~WebRTCLibPeerConnection(); - /* helper functions */ private: - void queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1 = godot::Variant(), const godot::Variant &p_arg2 = godot::Variant(), const godot::Variant &p_arg3 = godot::Variant()); - void queue_packet(uint8_t *, int); - - /** PeerConnectionObserver callback functions **/ - class GodotPCO : public webrtc::PeerConnectionObserver { - public: - WebRTCLibPeerConnection *parent; - - GodotPCO(WebRTCLibPeerConnection *p_parent) { - parent = p_parent; - } - void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override; - - void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {} - void OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {} - void OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {} - void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) override {} - void OnRenegotiationNeeded() override {} - void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {} - void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override {} - }; - - /** CreateSessionDescriptionObserver callback functions **/ - class GodotCSDO : public webrtc::CreateSessionDescriptionObserver { - public: - WebRTCLibPeerConnection *parent = nullptr; - - GodotCSDO(WebRTCLibPeerConnection *p_parent) { - parent = p_parent; - } - void OnSuccess(webrtc::SessionDescriptionInterface *desc) override; - void OnFailure(webrtc::RTCError error) override { - ERR_PRINT(godot::String(error.message())); - } - }; - - /** SetSessionDescriptionObserver callback functions **/ - class GodotSSDO : public webrtc::SetSessionDescriptionObserver { - public: - WebRTCLibPeerConnection *parent = nullptr; - bool make_offer = false; - - GodotSSDO(WebRTCLibPeerConnection *p_parent) { - parent = p_parent; - } - void OnSuccess() override; - void OnFailure(webrtc::RTCError error) override { - make_offer = false; - ERR_PRINT(godot::String(error.message())); - } - }; - class Signal { godot::String method; godot::Variant argv[3]; @@ -151,15 +113,10 @@ private: } }; - GodotPCO pco; - rtc::scoped_refptr<GodotSSDO> ptr_ssdo; - rtc::scoped_refptr<GodotCSDO> ptr_csdo; - std::mutex *mutex_signal_queue = nullptr; std::queue<Signal> signal_queue; - rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory; - rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection; + void queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1 = godot::Variant(), const godot::Variant &p_arg2 = godot::Variant(), const godot::Variant &p_arg3 = godot::Variant()); }; } // namespace godot_webrtc diff --git a/src/init_gdextension.cpp b/src/init_gdextension.cpp new file mode 100644 index 0000000..c923881 --- /dev/null +++ b/src/init_gdextension.cpp @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* init_gdextension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include <godot/gdnative_interface.h> + +#include <godot_cpp/core/class_db.hpp> +#include <godot_cpp/core/defs.hpp> +#include <godot_cpp/godot.hpp> + +#include "WebRTCLibDataChannel.hpp" +#include "WebRTCLibPeerConnection.hpp" + +using namespace godot; +using namespace godot_webrtc; + +void register_webrtc_extension_types(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + WebRTCLibPeerConnection::initialize_signaling(); + godot::ClassDB::register_class<WebRTCLibDataChannel>(); + godot::ClassDB::register_class<WebRTCLibPeerConnection>(); + WebRTCPeerConnection::set_default_extension("WebRTCLibPeerConnection"); +} + +void unregister_webrtc_extension_types(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + WebRTCLibPeerConnection::deinitialize_signaling(); +} + +extern "C" { +GDNativeBool GDN_EXPORT webrtc_extension_init(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization) { + GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); + + init_obj.register_initializer(register_webrtc_extension_types); + init_obj.register_terminator(unregister_webrtc_extension_types); + init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + + return init_obj.init(); +} +} diff --git a/src/init.cpp b/src/init_gdnative.cpp index a6066c3..77669af 100644 --- a/src/init.cpp +++ b/src/init_gdnative.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* init.cpp */ +/* init_gdnative.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -78,7 +78,7 @@ godot_net_webrtc_library library = { }; extern "C" void GDN_EXPORT godot_gdnative_singleton() { - if (WebRTCPeerConnectionNative::_net_api) { + if (godot::WebRTCPeerConnectionNative::_net_api) { ERR_FAIL_COND(!godot::gdnlib); _singleton_lib = godot::gdnlib; ERR_FAIL_COND(!godot::api); @@ -92,7 +92,7 @@ extern "C" void GDN_EXPORT godot_gdnative_singleton() { _set_library_mb = godot::api->godot_method_bind_get_method("NativeScript", "set_library"); ERR_FAIL_COND(!_set_library_mb); // If registration is successful _singleton will be set to true - _singleton = WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(&library) == GODOT_OK; + _singleton = godot::WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(&library) == GODOT_OK; if (!_singleton) ERR_PRINT("Failed initializing webrtc singleton library"); } @@ -111,7 +111,7 @@ extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { break; if (net_api->next->version.major == 3 && net_api->next->version.minor == 2) { - WebRTCPeerConnectionNative::_net_api = (const godot_gdnative_ext_net_3_2_api_struct *)net_api->next; + godot::WebRTCPeerConnectionNative::_net_api = (const godot_gdnative_ext_net_3_2_api_struct *)net_api->next; } } @@ -121,7 +121,7 @@ extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { if (_singleton) { // If we are the active singleton, unregister - WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(NULL); + godot::WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(NULL); } godot_webrtc::WebRTCLibPeerConnection::deinitialize_signaling(); godot::Godot::gdnative_terminate(o); diff --git a/src/net/WebRTCDataChannelNative.cpp b/src/net/WebRTCDataChannelNative.cpp index 4e6b114..4639021 100644 --- a/src/net/WebRTCDataChannelNative.cpp +++ b/src/net/WebRTCDataChannelNative.cpp @@ -31,6 +31,8 @@ #include "WebRTCDataChannelNative.hpp" #include "net/WebRTCPeerConnectionNative.hpp" +using namespace godot; + void WebRTCDataChannelNative::register_interface(const godot_net_webrtc_data_channel *p_interface) { ERR_FAIL_COND(!WebRTCPeerConnectionNative::_net_api); WebRTCPeerConnectionNative::_net_api->godot_net_bind_webrtc_data_channel(_owner, p_interface); @@ -54,73 +56,73 @@ WebRTCDataChannelNative::~WebRTCDataChannelNative() { * and you could use void *user for any kind of state struct pointer you have. */ godot_error get_packet_wdc(void *user, const uint8_t **r_buffer, int *r_len) { - return ((WebRTCDataChannelNative *)user)->get_packet(r_buffer, r_len); + return (godot_error)(((WebRTCDataChannelNative *)user)->_get_packet(r_buffer, r_len)); } godot_error put_packet_wdc(void *user, const uint8_t *p_buffer, int p_len) { - return ((WebRTCDataChannelNative *)user)->put_packet(p_buffer, p_len); + return (godot_error)(((WebRTCDataChannelNative *)user)->_put_packet(p_buffer, p_len)); } godot_int get_available_packet_count_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_available_packet_count(); + return ((WebRTCDataChannelNative *)user)->_get_available_packet_count(); } godot_int get_max_packet_size_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_max_packet_size(); + return ((WebRTCDataChannelNative *)user)->_get_max_packet_size(); } void set_write_mode_wdc(void *user, godot_int write_mode) { - ((WebRTCDataChannelNative *)user)->set_write_mode(write_mode); + ((WebRTCDataChannelNative *)user)->_set_write_mode(write_mode); } godot_int get_write_mode_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_write_mode(); + return ((WebRTCDataChannelNative *)user)->_get_write_mode(); } bool was_string_packet_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->was_string_packet(); + return ((WebRTCDataChannelNative *)user)->_was_string_packet(); } godot_int get_ready_state_wdc(const void *user) { - return (godot_int)(((WebRTCDataChannelNative *)user)->get_ready_state()); + return (godot_int)(((WebRTCDataChannelNative *)user)->_get_ready_state()); } const char *get_label_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_label(); + return ((WebRTCDataChannelNative *)user)->_get_label().utf8().get_data(); } bool is_ordered_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->is_ordered(); + return ((WebRTCDataChannelNative *)user)->_is_ordered(); } int get_id_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_id(); + return ((WebRTCDataChannelNative *)user)->_get_id(); } int get_max_packet_life_time_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_max_packet_life_time(); + return ((WebRTCDataChannelNative *)user)->_get_max_packet_life_time(); } int get_max_retransmits_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_max_retransmits(); + return ((WebRTCDataChannelNative *)user)->_get_max_retransmits(); } const char *get_protocol_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_protocol(); + return ((WebRTCDataChannelNative *)user)->_get_protocol().utf8().get_data(); } bool is_negotiated_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->is_negotiated(); + return ((WebRTCDataChannelNative *)user)->_is_negotiated(); } int get_buffered_amount_wdc(const void *user) { - return ((WebRTCDataChannelNative *)user)->get_buffered_amount(); + return ((WebRTCDataChannelNative *)user)->_get_buffered_amount(); } godot_error poll_wdc(void *user) { - return ((WebRTCDataChannelNative *)user)->poll(); + return (godot_error)(((WebRTCDataChannelNative *)user)->_poll()); } void close_wdc(void *user) { - ((WebRTCDataChannelNative *)user)->close(); + ((WebRTCDataChannelNative *)user)->_close(); } diff --git a/src/net/WebRTCDataChannelNative.hpp b/src/net/WebRTCDataChannelNative.hpp index 20ef38e..a8ab697 100644 --- a/src/net/WebRTCDataChannelNative.hpp +++ b/src/net/WebRTCDataChannelNative.hpp @@ -69,6 +69,8 @@ typedef struct { } #endif +namespace godot { + class WebRTCDataChannelNative : public godot::WebRTCDataChannelGDNative { GODOT_CLASS(WebRTCDataChannelNative, godot::WebRTCDataChannelGDNative); @@ -110,30 +112,32 @@ public: void _init(); void register_interface(const godot_net_webrtc_data_channel *interface); - virtual void set_write_mode(godot_int mode) = 0; - virtual godot_int get_write_mode() const = 0; - virtual bool was_string_packet() const = 0; + virtual void _set_write_mode(int64_t mode) = 0; + virtual int64_t _get_write_mode() const = 0; + virtual bool _was_string_packet() const = 0; - virtual ChannelState get_ready_state() const = 0; - virtual const char *get_label() const = 0; - virtual bool is_ordered() const = 0; - virtual int get_id() const = 0; - virtual int get_max_packet_life_time() const = 0; - virtual int get_max_retransmits() const = 0; - virtual const char *get_protocol() const = 0; - virtual bool is_negotiated() const = 0; - virtual int get_buffered_amount() const = 0; + virtual int64_t _get_ready_state() const = 0; + virtual godot::String _get_label() const = 0; + virtual bool _is_ordered() const = 0; + virtual int64_t _get_id() const = 0; + virtual int64_t _get_max_packet_life_time() const = 0; + virtual int64_t _get_max_retransmits() const = 0; + virtual godot::String _get_protocol() const = 0; + virtual bool _is_negotiated() const = 0; + virtual int64_t _get_buffered_amount() const = 0; - virtual godot_error poll() = 0; - virtual void close() = 0; + virtual int64_t _poll() = 0; + virtual void _close() = 0; /* PacketPeer */ - virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len) = 0; - virtual godot_error put_packet(const uint8_t *p_buffer, int p_len) = 0; - virtual godot_int get_available_packet_count() const = 0; - virtual godot_int get_max_packet_size() const = 0; + virtual int64_t _get_packet(const uint8_t **r_buffer, int32_t *r_len) = 0; + virtual int64_t _put_packet(const uint8_t *p_buffer, int64_t p_len) = 0; + virtual int64_t _get_available_packet_count() const = 0; + virtual int64_t _get_max_packet_size() const = 0; ~WebRTCDataChannelNative(); }; +}; // namespace godot + #endif // WEBRTC_DATA_CHANNEL_NATIVE diff --git a/src/net/WebRTCPeerConnectionNative.cpp b/src/net/WebRTCPeerConnectionNative.cpp index c8c7587..508004f 100644 --- a/src/net/WebRTCPeerConnectionNative.cpp +++ b/src/net/WebRTCPeerConnectionNative.cpp @@ -30,6 +30,8 @@ #include "WebRTCPeerConnectionNative.hpp" +using namespace godot; + const godot_gdnative_ext_net_3_2_api_struct *WebRTCPeerConnectionNative::_net_api = NULL; void WebRTCPeerConnectionNative::register_interface(const godot_net_webrtc_peer_connection *p_interface) { @@ -55,19 +57,23 @@ WebRTCPeerConnectionNative::~WebRTCPeerConnectionNative() { * and you could use void *user for any kind of state struct pointer you have. */ godot_int get_connection_state_wp(const void *user) { - return (godot_int)((WebRTCPeerConnectionNative *)user)->get_connection_state(); + return (godot_int)((WebRTCPeerConnectionNative *)user)->_get_connection_state(); } godot_error initialize_wp(void *user, const godot_dictionary *p_config) { - return ((WebRTCPeerConnectionNative *)user)->initialize(p_config); + return (godot_error)(((WebRTCPeerConnectionNative *)user)->_initialize(*(Dictionary *)p_config)); } godot_object *create_data_channel_wp(void *user, const char *p_channel, const godot_dictionary *p_channel_config) { - return ((WebRTCPeerConnectionNative *)user)->create_data_channel(p_channel, p_channel_config); + Object *ptr = ((WebRTCPeerConnectionNative *)user)->_create_data_channel(p_channel, *(Dictionary *)p_channel_config); + if (ptr) { + return ptr->_owner; + } + return nullptr; } godot_error create_offer_wp(void *user) { - return ((WebRTCPeerConnectionNative *)user)->create_offer(); + return (godot_error)(((WebRTCPeerConnectionNative *)user)->_create_offer()); } godot_error create_answer_wp(void *user) { @@ -75,21 +81,21 @@ godot_error create_answer_wp(void *user) { } godot_error set_remote_description_wp(void *user, const char *type, const char *sdp) { - return ((WebRTCPeerConnectionNative *)user)->set_remote_description(type, sdp); + return (godot_error)(((WebRTCPeerConnectionNative *)user)->_set_remote_description(type, sdp)); } godot_error set_local_description_wp(void *user, const char *type, const char *sdp) { - return ((WebRTCPeerConnectionNative *)user)->set_local_description(type, sdp); + return (godot_error)(((WebRTCPeerConnectionNative *)user)->_set_local_description(type, sdp)); } godot_error add_ice_candidate_wp(void *user, const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) { - return ((WebRTCPeerConnectionNative *)user)->add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName); + return (godot_error)(((WebRTCPeerConnectionNative *)user)->_add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName)); } godot_error poll_wp(void *user) { - return ((WebRTCPeerConnectionNative *)user)->poll(); + return (godot_error)((WebRTCPeerConnectionNative *)user)->_poll(); } void close_wp(void *user) { - ((WebRTCPeerConnectionNative *)user)->close(); + ((WebRTCPeerConnectionNative *)user)->_close(); } diff --git a/src/net/WebRTCPeerConnectionNative.hpp b/src/net/WebRTCPeerConnectionNative.hpp index fb75a46..1613b53 100644 --- a/src/net/WebRTCPeerConnectionNative.hpp +++ b/src/net/WebRTCPeerConnectionNative.hpp @@ -50,6 +50,8 @@ godot_error add_ice_candidate_wp(void *, const char *, int, const char *); godot_error poll_wp(void *); void close_wp(void *); +namespace godot { + class WebRTCPeerConnectionNative : public godot::WebRTCPeerConnectionGDNative { GODOT_CLASS(WebRTCPeerConnectionNative, godot::WebRTCPeerConnectionGDNative); @@ -79,18 +81,20 @@ public: void _init(); void register_interface(const godot_net_webrtc_peer_connection *interface); - virtual ConnectionState get_connection_state() const = 0; + virtual int64_t _get_connection_state() const = 0; - virtual godot_error initialize(const godot_dictionary *p_config) = 0; - virtual godot_object *create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config) = 0; - virtual godot_error create_offer() = 0; - virtual godot_error set_remote_description(const char *type, const char *sdp) = 0; - virtual godot_error set_local_description(const char *type, const char *sdp) = 0; - virtual godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) = 0; - virtual godot_error poll() = 0; - virtual void close() = 0; + virtual int64_t _initialize(const godot::Dictionary &p_config) = 0; + virtual godot::Object *_create_data_channel(const godot::String &p_channel, const godot::Dictionary &p_channel_config) = 0; + virtual int64_t _create_offer() = 0; + virtual int64_t _set_remote_description(const godot::String &type, const godot::String &sdp) = 0; + virtual int64_t _set_local_description(const godot::String &type, const godot::String &sdp) = 0; + virtual int64_t _add_ice_candidate(const godot::String &sdpMidName, int64_t sdpMlineIndexName, const godot::String &sdpName) = 0; + virtual int64_t _poll() = 0; + virtual void _close() = 0; ~WebRTCPeerConnectionNative(); }; +}; // namespace godot + #endif // WEBRTC_PEER_NATIVE |