281 lines
10 KiB
C++
281 lines
10 KiB
C++
//
|
|
// Created by WolverinDEV on 24/10/2020.
|
|
//
|
|
|
|
#include "./lib.h"
|
|
#include "./imports.h"
|
|
#include "../client/SpeakingClient.h"
|
|
#include "../client/voice/VoiceClient.h"
|
|
#include <Definitions.h>
|
|
|
|
using namespace ts;
|
|
using namespace ts::server;
|
|
using namespace ts::rtc;
|
|
|
|
struct LibCallbackData {
|
|
ClientId client_id;
|
|
std::weak_ptr<SpeakingClient> weak_ref;
|
|
};
|
|
|
|
void librtc_callback_free_client_data(const void* data) {
|
|
delete (LibCallbackData*) data;
|
|
}
|
|
|
|
void librtc_callback_client_stream_assignment(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
|
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
|
|
|
auto target_client = callback_data->weak_ref.lock();
|
|
if(!target_client) { return; }
|
|
|
|
if(source_data) {
|
|
auto source_client = source_data->weak_ref.lock();
|
|
if(!source_client) { return; }
|
|
|
|
ts::command_builder notify{"notifyrtcstreamassignment"};
|
|
notify.put_unchecked(0, "streamid", stream_id);
|
|
notify.put_unchecked(0, "sclid", source_client->getClientId());
|
|
notify.put_unchecked(0, "scluid", source_client->getUid());
|
|
notify.put_unchecked(0, "scldbid", source_client->getClientDatabaseId());
|
|
notify.put_unchecked(0, "sclname", source_client->getDisplayName());
|
|
target_client->sendCommand(notify);
|
|
} else {
|
|
ts::command_builder notify{"notifyrtcstreamassignment"};
|
|
notify.put_unchecked(0, "streamid", stream_id);
|
|
notify.put_unchecked(0, "sclid", 0);
|
|
target_client->sendCommand(notify);
|
|
}
|
|
}
|
|
|
|
void librtc_callback_client_offer_generated(const void* callback_data_ptr, const char* offer, size_t offer_length) {
|
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
|
auto target_client = callback_data->weak_ref.lock();
|
|
if(!target_client) { return; }
|
|
|
|
ts::command_builder notify{"notifyrtcsessiondescription"};
|
|
notify.put_unchecked(0, "mode", "offer");
|
|
notify.put_unchecked(0, "sdp", std::string_view{offer, offer_length});
|
|
target_client->sendCommand(notify);
|
|
}
|
|
|
|
void librtc_callback_client_audio_start(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
|
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
|
|
|
auto target_client = callback_data->weak_ref.lock();
|
|
if(!target_client) { return; }
|
|
|
|
auto source_client = source_data->weak_ref.lock();
|
|
if(!source_client) { return; }
|
|
|
|
ts::command_builder notify{"notifyrtcstateaudio"};
|
|
notify.put_unchecked(0, "streamid", stream_id);
|
|
notify.put_unchecked(0, "sclid", source_client->getClientId());
|
|
notify.put_unchecked(0, "scluid", source_client->getUid());
|
|
notify.put_unchecked(0, "scldbid", source_client->getClientDatabaseId());
|
|
notify.put_unchecked(0, "sclname", source_client->getDisplayName());
|
|
notify.put_unchecked(0, "state", "1");
|
|
target_client->sendCommand(notify);
|
|
}
|
|
|
|
void librtc_callback_client_audio_stop(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
|
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
|
|
|
auto target_client = callback_data->weak_ref.lock();
|
|
if(!target_client) { return; }
|
|
|
|
auto source_client = source_data->weak_ref.lock();
|
|
if(!source_client) { return; }
|
|
|
|
ts::command_builder notify{"notifyrtcstateaudio"};
|
|
notify.put_unchecked(0, "streamid", stream_id);
|
|
notify.put_unchecked(0, "sclid", source_client->getClientId());
|
|
notify.put_unchecked(0, "state", "0");
|
|
target_client->sendCommand(notify);
|
|
}
|
|
|
|
void librtc_callback_client_audio_sender_data(const void* callback_data_ptr, const void* source_data_ptr, uint8_t mode, uint16_t seq_no, uint8_t codec, const void* data, uint32_t length) {
|
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
|
|
|
/* Target client must be a voice client. The web client does not uses the native audio client */
|
|
auto target_client = std::dynamic_pointer_cast<VoiceClient>(callback_data->weak_ref.lock());
|
|
if(!target_client) { return; }
|
|
|
|
auto source_client = source_data->weak_ref.lock();
|
|
if(!source_client) { return; }
|
|
|
|
if(mode == 0) {
|
|
if(!target_client->shouldReceiveVoice(source_client)) {
|
|
return;
|
|
}
|
|
|
|
/* TODO: Somehow set the head (compressed) flag for beginning voice packets? */
|
|
auto packet = protocol::allocate_outgoing_packet(length + 5);
|
|
packet->type_and_flags = protocol::PacketType::VOICE;
|
|
|
|
*((uint16_t*) packet->payload + 0) = htons(seq_no);
|
|
*((uint16_t*) packet->payload + 1) = htons(source_data->client_id);
|
|
packet->payload[4] = codec;
|
|
if(data) {
|
|
memcpy(packet->payload + 5, data, length);
|
|
} else {
|
|
assert(length == 0);
|
|
}
|
|
|
|
target_client->getConnection()->send_packet(packet);
|
|
} else {
|
|
if(!target_client->shouldReceiveVoiceWhisper(source_client)) {
|
|
return;
|
|
}
|
|
|
|
/* FIXME: TODO! */
|
|
}
|
|
}
|
|
|
|
static NativeCallbacks native_callbacks{
|
|
.version = 1,
|
|
|
|
.free_client_data = librtc_callback_free_client_data,
|
|
|
|
.client_stream_assignment = librtc_callback_client_stream_assignment,
|
|
.client_offer_generated = librtc_callback_client_offer_generated,
|
|
|
|
.client_stream_start = librtc_callback_client_audio_start,
|
|
.client_stream_stop = librtc_callback_client_audio_stop,
|
|
|
|
.client_audio_sender_data = librtc_callback_client_audio_sender_data
|
|
};
|
|
|
|
std::string_view rtc::version() {
|
|
return std::string_view{librtc_version()};
|
|
}
|
|
|
|
bool rtc::initialize(std::string &error) {
|
|
auto error_ptr = librtc_init(&native_callbacks, sizeof native_callbacks);
|
|
if(!error_ptr) { return true; }
|
|
|
|
error = std::string{error_ptr};
|
|
librtc_free_str(error_ptr);
|
|
return false;
|
|
}
|
|
|
|
Server::Server() {
|
|
this->server_ptr = librtc_create_server();
|
|
}
|
|
|
|
Server::~Server() {
|
|
librtc_destroy_server(this->server_ptr);
|
|
}
|
|
|
|
RTCClientId Server::create_rtp_client(const std::shared_ptr<server::SpeakingClient> &client) {
|
|
auto data = new LibCallbackData{
|
|
.client_id = client->getClientId(),
|
|
.weak_ref = client
|
|
};
|
|
return librtc_create_rtp_client(this->server_ptr, data);
|
|
}
|
|
|
|
RTCClientId Server::create_native_client(const std::shared_ptr<server::SpeakingClient> &client) {
|
|
auto data = new LibCallbackData{
|
|
.client_id = client->getClientId(),
|
|
.weak_ref = client
|
|
};
|
|
return librtc_create_native_client(this->server_ptr, data);
|
|
}
|
|
|
|
void Server::destroy_client(RTCClientId client_id) {
|
|
librtc_destroy_client(this->server_ptr, client_id);
|
|
}
|
|
|
|
void Server::reset_rtp_session(RTCClientId client_id) {
|
|
librtc_reset_rtp_session(this->server_ptr, client_id);
|
|
}
|
|
|
|
bool Server::apply_remote_description(std::string &error, RTCClientId client_id, uint32_t mode, const std::string_view &description) {
|
|
auto error_ptr = librtc_apply_remote_description(this->server_ptr, client_id, mode, description.data());
|
|
if(!error_ptr) { return true; }
|
|
|
|
error = std::string{error_ptr};
|
|
librtc_free_str(error_ptr);
|
|
return false;
|
|
}
|
|
|
|
bool Server::generate_local_description(RTCClientId client, std::string &result) {
|
|
char* description_ptr;
|
|
auto error_ptr = librtc_generate_local_description(this->server_ptr, client, &description_ptr);
|
|
if(error_ptr) {
|
|
result = std::string{error_ptr};
|
|
librtc_free_str(error_ptr);
|
|
return false;
|
|
} else {
|
|
result = std::string{description_ptr};
|
|
librtc_free_str(description_ptr);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool Server::add_ice_candidate(std::string &error, RTCClientId client_id, uint32_t media_line, const std::string_view &description) {
|
|
auto error_ptr = librtc_add_ice_candidate(this->server_ptr, client_id, media_line, description.length() == 0 ? nullptr : description.data());
|
|
if(!error_ptr) { return true; }
|
|
|
|
error = std::string{error_ptr};
|
|
librtc_free_str(error_ptr);
|
|
return false;
|
|
}
|
|
|
|
void Server::ice_candidates_finished(RTCClientId) {
|
|
/* Nothing really to do here */
|
|
}
|
|
|
|
uint32_t Server::create_channel() {
|
|
return librtc_create_channel(this->server_ptr);
|
|
}
|
|
|
|
ChannelAssignResult Server::assign_channel(uint32_t client_id, uint32_t channel_id) {
|
|
auto result = librtc_assign_channel(this->server_ptr, client_id, channel_id);
|
|
switch(result) {
|
|
case 0x00: return ChannelAssignResult::Success;
|
|
case 0x01: return ChannelAssignResult::ClientUnknown;
|
|
case 0x02: return ChannelAssignResult::TargetChannelUnknown;
|
|
default: return ChannelAssignResult::UnknownError;
|
|
}
|
|
}
|
|
|
|
BroadcastStartResult Server::start_broadcast(uint32_t client_id, uint8_t btype, uint32_t track_id) {
|
|
auto result = librtc_client_broadcast(this->server_ptr, client_id, btype, track_id);
|
|
switch(result) {
|
|
case 0x00: return BroadcastStartResult::Success;
|
|
case 0x01: return BroadcastStartResult::InvalidClient;
|
|
case 0x02: return BroadcastStartResult::ClientHasNoChannel;
|
|
case 0x03: return BroadcastStartResult::InvalidBroadcastType;
|
|
case 0x04: return BroadcastStartResult::InvalidStreamId;
|
|
default: return BroadcastStartResult::UnknownError;
|
|
}
|
|
}
|
|
|
|
std::optional<NativeAudioSourceSupplier> Server::create_audio_source_supplier_sender(uint32_t client_id) {
|
|
auto result = librtc_create_audio_source_supplier(this->server_ptr, client_id);
|
|
if(!result) { return std::nullopt; }
|
|
|
|
return std::make_optional<NativeAudioSourceSupplier>(result);
|
|
}
|
|
|
|
void Server::destroy_channel(uint32_t channel_id) {
|
|
librtc_destroy_channel(this->server_ptr, channel_id);
|
|
}
|
|
|
|
NativeAudioSourceSupplier::NativeAudioSourceSupplier(void *ptr) : sender_ptr{ptr} {}
|
|
NativeAudioSourceSupplier::NativeAudioSourceSupplier(NativeAudioSourceSupplier &&other) noexcept : sender_ptr{other.sender_ptr} {
|
|
other.sender_ptr = nullptr;
|
|
}
|
|
NativeAudioSourceSupplier::~NativeAudioSourceSupplier() noexcept {
|
|
if(this->sender_ptr) {
|
|
librtc_destroy_audio_source_supplier(this->sender_ptr);
|
|
}
|
|
}
|
|
|
|
void NativeAudioSourceSupplier::send_audio(uint16_t seq_no, bool marked, uint32_t timestamp, uint8_t codec, const std::string_view &data) {
|
|
librtc_audio_source_supply(this->sender_ptr, seq_no, marked, timestamp, codec, data.empty() ? nullptr : data.data(), data.length());
|
|
} |