diff options
Diffstat (limited to 'src/WebRTCLibDataChannel.cpp')
-rw-r--r-- | src/WebRTCLibDataChannel.cpp | 232 |
1 files changed, 124 insertions, 108 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; } |