diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | SConstruct | 4 | ||||
-rw-r--r-- | src/GodotCreateSessionDescriptionObserver.cpp | 16 | ||||
-rw-r--r-- | src/GodotDataChannelObserver.cpp | 19 | ||||
-rw-r--r-- | src/GodotPeerConnectionObserver.cpp | 46 | ||||
-rw-r--r-- | src/GodotSetSessionDescriptionObserver.cpp | 11 | ||||
-rw-r--r-- | src/WebRTCPeer.cpp | 161 | ||||
-rw-r--r-- | src/WebRTCPeer.hpp | 84 | ||||
-rw-r--r-- | src/init.cpp | 4 | ||||
-rw-r--r-- | src/net/WebRTCPeerNative.cpp | 30 | ||||
-rw-r--r-- | src/net/WebRTCPeerNative.hpp | 6 |
11 files changed, 339 insertions, 43 deletions
@@ -3,3 +3,4 @@ bin/* *.a *.lib .sconsign.dblite +*.obj @@ -110,12 +110,12 @@ if target_platform == "linux": elif target_platform == "windows": # Mostly VisualStudio if env["CC"] == "cl": - env.Append(CCFLAGS=["/DWEBRTC_WIN", "/D_WINSOCKAPI_", "/DNOMINMAX", "/DRTC_UNUSED=", "/DNO_RETURN="]) + env.Append(CCFLAGS=["/DWEBRTC_WIN", "/DWIN32_LEAN_AND_MEAN", "/DNOMINMAX", "/DRTC_UNUSED=", "/DNO_RETURN="]) env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in ["secur32", lib_name]]) env.Append(LIBPATH=[lib_path]) # Mostly "gcc" else: - env.Append(CCFLAGS=["-DWEBRTC_WIN", "-D_WINSOCKAPI_", "-DNOMINMAX", "-DRTC_UNUSED=", "-DNO_RETURN="]) + env.Append(CCFLAGS=["-DWINVER=0x0603", "-D_WIN32_WINNT=0x0603", "-DWEBRTC_WIN", "-DWIN32_LEAN_AND_MEAN", "-DNOMINMAX", "-DRTC_UNUSED=", "-DNO_RETURN="]) env.Append(LIBS=["secur32", lib_name]) env.Append(LIBPATH=[lib_path]) diff --git a/src/GodotCreateSessionDescriptionObserver.cpp b/src/GodotCreateSessionDescriptionObserver.cpp new file mode 100644 index 0000000..1f4b661 --- /dev/null +++ b/src/GodotCreateSessionDescriptionObserver.cpp @@ -0,0 +1,16 @@ +#include "WebRTCPeer.hpp" + +using namespace godot_webrtc; + +WebRTCPeer::GodotCSDO::GodotCSDO(WebRTCPeer *parent) { + this->parent = parent; +} + +void WebRTCPeer::GodotCSDO::OnSuccess(webrtc::SessionDescriptionInterface *desc) { + // serialize this offer and send it to the remote peer: + std::string sdp; // sdp = session description protocol + desc->ToString(&sdp); + parent->queue_signal("offer_created", 2, desc->type().c_str(), sdp.c_str()); +}; + +void WebRTCPeer::GodotCSDO::OnFailure(const std::string &error){}; diff --git a/src/GodotDataChannelObserver.cpp b/src/GodotDataChannelObserver.cpp new file mode 100644 index 0000000..081c868 --- /dev/null +++ b/src/GodotDataChannelObserver.cpp @@ -0,0 +1,19 @@ +#include "WebRTCPeer.hpp" + +using namespace godot_webrtc; + +WebRTCPeer::GodotDCO::GodotDCO(WebRTCPeer *parent) { + this->parent = parent; +} + +void WebRTCPeer::GodotDCO::OnMessage(const webrtc::DataBuffer &buffer) { + const uint8_t *data = buffer.data.data<uint8_t>(); + uint8_t *memory_controlled_buffer = new uint8_t[buffer.data.size()]; + + std::copy(data, data + buffer.data.size(), memory_controlled_buffer); + parent->queue_packet(memory_controlled_buffer, buffer.data.size()); +}; + +void WebRTCPeer::GodotDCO::OnStateChange(){}; + +void WebRTCPeer::GodotDCO::OnBufferedAmountChange(uint64_t previous_amount){}; diff --git a/src/GodotPeerConnectionObserver.cpp b/src/GodotPeerConnectionObserver.cpp new file mode 100644 index 0000000..572b6d7 --- /dev/null +++ b/src/GodotPeerConnectionObserver.cpp @@ -0,0 +1,46 @@ +#include "WebRTCPeer.hpp" + +using namespace godot_webrtc; + +WebRTCPeer::GodotPCO::GodotPCO(WebRTCPeer *parent) { + this->parent = parent; +} + +void WebRTCPeer::GodotPCO::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) { +} + +void WebRTCPeer::GodotPCO::OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) { +} + +void WebRTCPeer::GodotPCO::OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) { +} + +void WebRTCPeer::GodotPCO::OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) { +} + +void WebRTCPeer::GodotPCO::OnRenegotiationNeeded() { +} + +void WebRTCPeer::GodotPCO::OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) { +} + +void WebRTCPeer::GodotPCO::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) { +} + +void WebRTCPeer::GodotPCO::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) { + // Serialize the candidate and send it to the remote peer: + + 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("new_ice_candidate", + 3, + candidateSdpMidName, + candidateSdpMlineIndexName, + candidateSdpName); +} diff --git a/src/GodotSetSessionDescriptionObserver.cpp b/src/GodotSetSessionDescriptionObserver.cpp new file mode 100644 index 0000000..93b9b8d --- /dev/null +++ b/src/GodotSetSessionDescriptionObserver.cpp @@ -0,0 +1,11 @@ +#include "WebRTCPeer.hpp" + +using namespace godot_webrtc; + +WebRTCPeer::GodotSSDO::GodotSSDO(WebRTCPeer *parent) { + this->parent = parent; +} + +void WebRTCPeer::GodotSSDO::OnSuccess(){}; + +void WebRTCPeer::GodotSSDO::OnFailure(const std::string &error){}; diff --git a/src/WebRTCPeer.cpp b/src/WebRTCPeer.cpp index f7572ad..00455c0 100644 --- a/src/WebRTCPeer.cpp +++ b/src/WebRTCPeer.cpp @@ -3,7 +3,6 @@ using namespace godot_webrtc; void WebRTCPeer::set_write_mode(godot_int mode) { - } godot_int WebRTCPeer::get_write_mode() const { @@ -19,59 +18,185 @@ godot_int WebRTCPeer::get_connection_state() const { } godot_error WebRTCPeer::create_offer() { - return GODOT_FAILED; + peer_connection->CreateOffer( + ptr_csdo, // CreateSessionDescriptionObserver* observer, + nullptr // webrtc::PeerConnectionInterface::RTCOfferAnswerOptions() // const MediaConstraintsInterface* constraints + ); + return GODOT_OK; } godot_error WebRTCPeer::set_remote_description(const char *type, const char *sdp) { - return GODOT_FAILED; + godot_error err = set_description(type, sdp, false); //false meaning !isLocal because it is remote + peer_connection->CreateAnswer(ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); + return err; } godot_error WebRTCPeer::set_local_description(const char *type, const char *sdp) { - return GODOT_FAILED; + return set_description(type, sdp, true); // isLocal == true } godot_error WebRTCPeer::add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) { - return GODOT_FAILED; + webrtc::SdpParseError *error = nullptr; + webrtc::IceCandidateInterface *candidate = webrtc::CreateIceCandidate( + sdpMidName, + sdpMlineIndexName, + sdpName, + error); + + // @TODO do something if there's an error (if error, or if !candidate) + if (error || !candidate) + std::cout << "ERROR with creating ICE candidate (" << error << ")\n"; + + if (!peer_connection->AddIceCandidate(candidate)) + ERR_PRINT("Error with adding ICE candidate"); + return GODOT_OK; } godot_error WebRTCPeer::poll() { - return GODOT_FAILED; + std::function<void()> signal; + while (!signal_queue.empty()) { + mutex_signal_queue->lock(); + signal = signal_queue.front(); + signal_queue.pop(); + mutex_signal_queue->unlock(); + + signal(); + } + return GODOT_OK; } godot_error WebRTCPeer::get_packet(const uint8_t **r_buffer, int &r_len) { - printf("Get packet"); - r_len = 0; + if (packet_queue_size == 0) + return GODOT_ERR_UNAVAILABLE; + mutex_packet_queue->lock(); + uint8_t *current_packet = packet_queue.front(); + *r_buffer = current_packet; + r_len = packet_sizes_queue.front(); + + packet_queue.pop(); + packet_sizes_queue.pop(); + mutex_packet_queue->unlock(); + + --packet_queue_size; return GODOT_OK; } godot_error WebRTCPeer::put_packet(const uint8_t *p_buffer, int p_len) { - printf("Put packet"); - return GODOT_OK; + webrtc::DataBuffer webrtc_buffer(rtc::CopyOnWriteBuffer(p_buffer, p_len), true); + data_channel->Send(webrtc_buffer); + return GODOT_OK; // @TODO properly return any Error we may get. } godot_int WebRTCPeer::get_available_packet_count() const { - printf("Get packet count"); - return 2; + return packet_queue_size; } godot_int WebRTCPeer::get_max_packet_size() const { - printf("Get max packet size"); - return 1024; + return 1200; } -void WebRTCPeer::_register_methods() { } +void WebRTCPeer::_register_methods() { +} void WebRTCPeer::_init() { - printf("Binding PacketPeer interface"); register_interface(&interface); + + // initialize variables: + mutex_signal_queue = new std::mutex; + mutex_packet_queue = new std::mutex; + packet_queue_size = 0; + + // create a PeerConnectionFactoryInterface: + signaling_thread = new rtc::Thread; + signaling_thread->Start(); + pc_factory = webrtc::CreateModularPeerConnectionFactory( + nullptr, // rtc::Thread* network_thread, + nullptr, // rtc::Thread* worker_thread, + signaling_thread, + nullptr, // std::unique_ptr<cricket::MediaEngineInterface> media_engine, + nullptr, // std::unique_ptr<CallFactoryInterface> call_factory, + nullptr // std::unique_ptr<RtcEventLogFactoryInterface> event_log_factory + ); + if (pc_factory.get() == nullptr) { // PeerConnectionFactory couldn't be created. Fail the method call. + godot_print_error("PeerConnectionFactory could not be created", "_init", "WebRTCPeer.cpp", 80); + // return GODOT_FAILED; + } + + // create PeerConnection configuration and add the ice servers: + webrtc::PeerConnectionInterface::RTCConfiguration configuration; + //webrtc::PeerConnectionInterface::IceServer ice_server; + + //ice_server.uri = "stun:stun.l.google.com:19302"; // @FIXME allow user to input ice servers + //configuration.servers.push_back(ice_server); + + // create a PeerConnection object: + peer_connection = pc_factory->CreatePeerConnection(configuration, nullptr, nullptr, &pco); + if (peer_connection.get() == nullptr) { // PeerConnection couldn't be created. Fail the method call. + godot_print_error("PeerConnection could not be created", "_init", "WebRTCPeer.cpp", 101); + // return GODOT_FAILED; + } + + // create a DataChannel + webrtc::DataChannelInit data_channel_config; + data_channel_config.negotiated = true; // True if the channel has been externally negotiated + data_channel_config.id = 0; + + data_channel = peer_connection->CreateDataChannel("channel", &data_channel_config); + // @TODO (NONESSENTIAL) create data_channel check. fail function call if data_channel isn't created + data_channel->RegisterObserver(&dco); } -WebRTCPeer::WebRTCPeer() { +WebRTCPeer::WebRTCPeer() : + dco(this), + pco(this), + ptr_csdo(new rtc::RefCountedObject<GodotCSDO>(this)), + ptr_ssdo(new rtc::RefCountedObject<GodotSSDO>(this)) { } WebRTCPeer::~WebRTCPeer() { if (_owner) { - printf("Unbinding PacketPeer interface"); register_interface(NULL); } + delete mutex_signal_queue; + delete mutex_packet_queue; +} + +void WebRTCPeer::queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1, const godot::Variant &p_arg2, const godot::Variant &p_arg3) { + mutex_signal_queue->lock(); + signal_queue.push( + [this, p_name, p_argc, p_arg1, p_arg2, p_arg3] { + if (p_argc == 2) + emit_signal(p_name, p_arg1, p_arg2); + else + emit_signal(p_name, p_arg1, p_arg2, p_arg3); + }); + mutex_signal_queue->unlock(); +} + +void WebRTCPeer::queue_packet(uint8_t *buffer, int buffer_size) { + mutex_packet_queue->lock(); + packet_queue.push(buffer); + packet_sizes_queue.push(buffer_size); + ++packet_queue_size; + mutex_packet_queue->unlock(); +} + +godot_error WebRTCPeer::set_description(const char *type, const char *sdp, bool isLocal) { + // webrtc::SdpType type = (isOffer) ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer; + godot::String string_sdp = sdp; + + webrtc::SdpType sdptype = (godot::String(type) == godot::String("offer")) ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer; + std::unique_ptr<webrtc::SessionDescriptionInterface> desc = + webrtc::CreateSessionDescription(sdptype, sdp); + + if (isLocal) { + peer_connection->SetLocalDescription( + ptr_ssdo, // @TODO (NONESSENTIAL, OPTIONAL) replace this with DummySetSessionDescriptionObserver::Create() + desc.release()); + } else { + peer_connection->SetRemoteDescription( + ptr_ssdo, // @TODO (NONESSENTIAL, OPTIONAL) replace this with DummySetSessionDescriptionObserver::Create() + desc.release()); + } + return GODOT_OK; } diff --git a/src/WebRTCPeer.hpp b/src/WebRTCPeer.hpp index 72560bc..45a90ec 100644 --- a/src/WebRTCPeer.hpp +++ b/src/WebRTCPeer.hpp @@ -1,9 +1,18 @@ #ifndef WEBRTC_PEER_H #define WEBRTC_PEER_H -#include <Godot.hpp> +#include "api/peerconnectioninterface.h" // interface for all things needed from WebRTC +#include "media/base/mediaengine.h" // needed for CreateModularPeerConnectionFactory +#ifdef interface +#pragma message("\n 'interface' is defined \n") +#undef interface +#endif // interface + +#include <functional> // std::function +#include <mutex> // mutex @TODO replace std::mutex with Godot mutex #include "net/WebRTCPeerNative.hpp" +#include <Godot.hpp> namespace godot_webrtc { @@ -34,8 +43,79 @@ public: WebRTCPeer(); ~WebRTCPeer(); + + /* helper functions */ + + 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_signal(godot::StringName p_name, Variant_ARG_LIST); + void queue_packet(uint8_t *, int); + godot_error set_description(const char *type, const char *sdp, bool isLocal); + + /** DataChannelObserver callback functions **/ + class GodotDCO : public webrtc::DataChannelObserver { + public: + WebRTCPeer *parent; + + GodotDCO(WebRTCPeer *parent); + void OnMessage(const webrtc::DataBuffer &buffer) override; + void OnStateChange() override; // UNUSED + void OnBufferedAmountChange(uint64_t previous_amount) override; // UNUSED + }; + + /** PeerConnectionObserver callback functions **/ + class GodotPCO : public webrtc::PeerConnectionObserver { + public: + WebRTCPeer *parent; + + GodotPCO(WebRTCPeer *parent); + 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; + void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override; + }; + + /** CreateSessionDescriptionObserver callback functions **/ + class GodotCSDO : public webrtc::CreateSessionDescriptionObserver { + public: + WebRTCPeer *parent; + + GodotCSDO(WebRTCPeer *parent); + void OnSuccess(webrtc::SessionDescriptionInterface *desc) override; + void OnFailure(const std::string &error) override; + }; + + /** SetSessionDescriptionObserver callback functions **/ + class GodotSSDO : public webrtc::SetSessionDescriptionObserver { + public: + WebRTCPeer *parent; + + GodotSSDO(WebRTCPeer *parent); + void OnSuccess() override; + void OnFailure(const std::string &error) override; + }; + + GodotDCO dco; + GodotPCO pco; + rtc::scoped_refptr<GodotSSDO> ptr_ssdo; + rtc::scoped_refptr<GodotCSDO> ptr_csdo; + + std::mutex *mutex_signal_queue; + std::mutex *mutex_packet_queue; + int packet_queue_size; + std::queue<uint8_t *> packet_queue; + std::queue<int> packet_sizes_queue; + std::queue<std::function<void()> > signal_queue; + + rtc::Thread *signaling_thread; + rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory; + rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection; + rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel; }; -} +} // namespace godot_webrtc #endif // WEBRTC_PEER_H diff --git a/src/init.cpp b/src/init.cpp index 60aaf45..e8e1d0c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,6 +1,6 @@ -#include <gdnative_api_struct.gen.h> -#include "net/WebRTCPeerNative.hpp" #include "WebRTCPeer.hpp" +#include "net/WebRTCPeerNative.hpp" +#include <gdnative_api_struct.gen.h> /* Godot export stuff */ extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { diff --git a/src/net/WebRTCPeerNative.cpp b/src/net/WebRTCPeerNative.cpp index be9040c..d2abcaa 100644 --- a/src/net/WebRTCPeerNative.cpp +++ b/src/net/WebRTCPeerNative.cpp @@ -7,16 +7,14 @@ void WebRTCPeerNative::register_interface(const godot_net_webrtc_peer *p_interfa _net_api->godot_net_bind_webrtc_peer(_owner, p_interface); } -void WebRTCPeerNative::_register_methods() { } +void WebRTCPeerNative::_register_methods() {} void WebRTCPeerNative::_init() { - printf("Binding PacketPeer interface"); register_interface(&interface); } WebRTCPeerNative::~WebRTCPeerNative() { if (_owner) { - printf("Unbinding PacketPeer interface"); register_interface(NULL); } } @@ -27,53 +25,53 @@ WebRTCPeerNative::~WebRTCPeerNative() { * and you could use void *user for any kind of state struct pointer you have. */ godot_error get_packet_wp(void *user, const uint8_t **r_buffer, int &r_len) { - return ((WebRTCPeerNative *) user)->get_packet(r_buffer, r_len); + return ((WebRTCPeerNative *)user)->get_packet(r_buffer, r_len); } godot_error put_packet_wp(void *user, const uint8_t *p_buffer, int p_len) { - return ((WebRTCPeerNative *) user)->put_packet(p_buffer, p_len); + return ((WebRTCPeerNative *)user)->put_packet(p_buffer, p_len); } godot_int get_available_packet_count_wp(const void *user) { - return ((WebRTCPeerNative *) user)->get_available_packet_count(); + return ((WebRTCPeerNative *)user)->get_available_packet_count(); } godot_int get_max_packet_size_wp(const void *user) { - return ((WebRTCPeerNative *) user)->get_max_packet_size(); + return ((WebRTCPeerNative *)user)->get_max_packet_size(); } void set_write_mode_wp(void *user, godot_int write_mode) { - ((WebRTCPeerNative *) user)->set_write_mode(write_mode); + ((WebRTCPeerNative *)user)->set_write_mode(write_mode); } godot_int get_write_mode_wp(const void *user) { - return ((WebRTCPeerNative *) user)->get_write_mode(); + return ((WebRTCPeerNative *)user)->get_write_mode(); } bool was_string_packet_wp(const void *user) { - return ((WebRTCPeerNative *) user)->was_string_packet(); + return ((WebRTCPeerNative *)user)->was_string_packet(); } godot_int get_connection_state_wp(const void *user) { - return ((WebRTCPeerNative *) user)->get_connection_state(); + return ((WebRTCPeerNative *)user)->get_connection_state(); } godot_error create_offer_wp(void *user) { - return ((WebRTCPeerNative *) user)->create_offer(); + return ((WebRTCPeerNative *)user)->create_offer(); } godot_error set_remote_description_wp(void *user, const char *type, const char *sdp) { - return ((WebRTCPeerNative *) user)->set_remote_description(type, sdp); + return ((WebRTCPeerNative *)user)->set_remote_description(type, sdp); } godot_error set_local_description_wp(void *user, const char *type, const char *sdp) { - return ((WebRTCPeerNative *) user)->set_local_description(type, sdp); + return ((WebRTCPeerNative *)user)->set_local_description(type, sdp); } godot_error add_ice_candidate_wp(void *user, const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) { - return ((WebRTCPeerNative *) user)->add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName); + return ((WebRTCPeerNative *)user)->add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName); } godot_error poll_wp(void *user) { - return ((WebRTCPeerNative *) user)->poll(); + return ((WebRTCPeerNative *)user)->poll(); } diff --git a/src/net/WebRTCPeerNative.hpp b/src/net/WebRTCPeerNative.hpp index fe2a8a7..e82f295 100644 --- a/src/net/WebRTCPeerNative.hpp +++ b/src/net/WebRTCPeerNative.hpp @@ -29,7 +29,7 @@ class WebRTCPeerNative : public godot::WebRTCPeerGDNative { protected: godot_net_webrtc_peer interface = { - {3, 1}, + { 3, 1 }, this, &get_packet_wp, @@ -63,8 +63,8 @@ public: virtual godot_int get_connection_state() const = 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 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; |