Import GSoC work to WebRTC GDNative implementation

This commit is contained in:
Brandon Makin
2018-08-12 22:47:37 -07:00
committed by Fabio Alessandrelli
parent a44a5af115
commit 34fd1f8caa
11 changed files with 339 additions and 43 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ bin/*
*.a
*.lib
.sconsign.dblite
*.obj

View File

@@ -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])

View File

@@ -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){};

View File

@@ -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){};

View File

@@ -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);
}

View File

@@ -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){};

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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;