webrtc version 2
This commit is contained in:
parent
1194b48884
commit
72c09ad308
|
|
@ -4,30 +4,33 @@
|
|||
PeerClient::PeerClient(SignalingManager& sig, WebRTCTransport& rtc)
|
||||
: sig_(sig), rtc_(rtc)
|
||||
{
|
||||
/* ---------- Send OFFER ---------- */
|
||||
rtc_.onLocalSdp([&](std::string sdp) {
|
||||
sig_.sendSdp(sdp);
|
||||
std::cout << "[Client] Sent OFFER\n";
|
||||
// ---------- Local SDP ----------
|
||||
rtc_.onLocalSdp([this](std::string sdp){
|
||||
if(!offerSent_){
|
||||
sig_.sendSdp(sdp);
|
||||
offerSent_ = true;
|
||||
std::cout << "[Client] Sent OFFER\n";
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- Send local ICE ---------- */
|
||||
rtc_.onLocalIce([&](std::string cand, std::string mid) {
|
||||
sig_.sendIce(cand, mid);
|
||||
std::cout << "[Client] Sent ICE: " << cand << "\n";
|
||||
// ---------- Local ICE ----------
|
||||
rtc_.onLocalIce([this](std::string cand, std::string mid){
|
||||
if(!offerSent_){
|
||||
// buffer ICE nếu OFFER chưa gửi
|
||||
static std::vector<std::pair<std::string,std::string>> iceBuffer;
|
||||
iceBuffer.emplace_back(cand, mid);
|
||||
} else {
|
||||
sig_.sendIce(cand, mid);
|
||||
std::cout << "[Client] Sent ICE: " << cand << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- DC open ---------- */
|
||||
rtc_.onDcOpen([]() {
|
||||
std::cout << "[Client] DC OPEN\n";
|
||||
});
|
||||
|
||||
/* ---------- Receive signaling ---------- */
|
||||
sig_.onReceive([&](const SignalingMsg& m) {
|
||||
if(m.type == SigType::SDP) {
|
||||
// ---------- Remote signaling ----------
|
||||
sig_.onReceive([this](const SignalingMsg& m){
|
||||
if(m.type == SigType::SDP){
|
||||
rtc_.setRemoteAnswer(m.payload1);
|
||||
std::cout << "[Client] Got ANSWER\n";
|
||||
}
|
||||
else if(m.type == SigType::ICE) {
|
||||
} else if(m.type == SigType::ICE){
|
||||
rtc_.addRemoteIce(m.payload1, m.payload2);
|
||||
std::cout << "[Client] Got remote ICE\n";
|
||||
}
|
||||
|
|
@ -37,5 +40,5 @@ PeerClient::PeerClient(SignalingManager& sig, WebRTCTransport& rtc)
|
|||
void PeerClient::start()
|
||||
{
|
||||
sig_.start();
|
||||
rtc_.createOffer();
|
||||
rtc_.createOffer(); // trigger onLocalSdp -> gửi OFFER
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,6 @@ public:
|
|||
private:
|
||||
SignalingManager& sig_;
|
||||
WebRTCTransport& rtc_;
|
||||
|
||||
bool offerSent_ = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,37 +1,50 @@
|
|||
#include "WebRTCTransport.hpp"
|
||||
#include <iostream>
|
||||
|
||||
WebRTCTransport::WebRTCTransport()
|
||||
{
|
||||
rtc::Configuration cfg;
|
||||
cfg.iceServers.emplace_back(
|
||||
"stun:stun.l.google.com:19302");
|
||||
cfg.iceServers.emplace_back("stun:stun.l.google.com:19302");
|
||||
|
||||
pc_ = std::make_shared<rtc::PeerConnection>(cfg);
|
||||
dc_ = pc_->createDataChannel("dc");
|
||||
|
||||
dc_->onMessage([this](rtc::message_variant msg){
|
||||
if(const auto* s = std::get_if<std::string>(&msg)){
|
||||
std::cout << "[WebRTC] DC message received: " << *s << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebRTCTransport::onLocalSdp(
|
||||
std::function<void(std::string)> cb)
|
||||
void WebRTCTransport::onLocalSdp(std::function<void(std::string)> cb)
|
||||
{
|
||||
pc_->onLocalDescription(
|
||||
[cb](rtc::Description d) {
|
||||
cb(std::string(d));
|
||||
});
|
||||
pc_->onLocalDescription([cb](rtc::Description d){
|
||||
cb(std::string(d));
|
||||
});
|
||||
}
|
||||
|
||||
void WebRTCTransport::onLocalIce(
|
||||
std::function<void(std::string,std::string)> cb)
|
||||
void WebRTCTransport::onLocalIce(std::function<void(std::string,std::string)> cb)
|
||||
{
|
||||
pc_->onLocalCandidate(
|
||||
[cb](rtc::Candidate c) {
|
||||
cb(c.candidate(), c.mid());
|
||||
});
|
||||
pc_->onLocalCandidate([cb](rtc::Candidate c){
|
||||
cb(c.candidate(), c.mid());
|
||||
});
|
||||
}
|
||||
|
||||
void WebRTCTransport::onDcOpen(
|
||||
std::function<void()> cb)
|
||||
void WebRTCTransport::onDcOpen(std::function<void()> cb)
|
||||
{
|
||||
dc_->onOpen(cb);
|
||||
dc_->onOpen([this, cb]{
|
||||
dcOpen_ = true; // update trạng thái
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
void WebRTCTransport::onDcMessage(std::function<void(const std::string&)> cb)
|
||||
{
|
||||
dc_->onMessage([cb](rtc::message_variant msg){
|
||||
if(const auto* s = std::get_if<std::string>(&msg)){
|
||||
cb(*s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebRTCTransport::createOffer()
|
||||
|
|
@ -39,19 +52,33 @@ void WebRTCTransport::createOffer()
|
|||
pc_->setLocalDescription();
|
||||
}
|
||||
|
||||
void WebRTCTransport::setRemoteAnswer(
|
||||
const std::string& sdp)
|
||||
void WebRTCTransport::createAnswer()
|
||||
{
|
||||
pc_->setRemoteDescription(
|
||||
rtc::Description(
|
||||
sdp,
|
||||
rtc::Description::Type::Answer));
|
||||
// tạo Answer dựa trên remote Offer
|
||||
pc_->setLocalDescription(rtc::Description::Type::Answer);
|
||||
}
|
||||
|
||||
void WebRTCTransport::addRemoteIce(
|
||||
const std::string& cand,
|
||||
const std::string& mid)
|
||||
|
||||
void WebRTCTransport::setRemoteOffer(const std::string& sdp)
|
||||
{
|
||||
pc_->addRemoteCandidate(
|
||||
rtc::Candidate(cand, mid));
|
||||
pc_->setRemoteDescription(rtc::Description(sdp, rtc::Description::Type::Offer));
|
||||
}
|
||||
|
||||
void WebRTCTransport::setRemoteAnswer(const std::string& sdp)
|
||||
{
|
||||
pc_->setRemoteDescription(rtc::Description(sdp, rtc::Description::Type::Answer));
|
||||
}
|
||||
|
||||
void WebRTCTransport::addRemoteIce(const std::string& cand, const std::string& mid)
|
||||
{
|
||||
pc_->addRemoteCandidate(rtc::Candidate(cand, mid));
|
||||
}
|
||||
|
||||
void WebRTCTransport::sendMessage(const std::string& msg)
|
||||
{
|
||||
if(dcOpen_){
|
||||
dc_->send(msg);
|
||||
std::cout << "[WebRTC] DC message sent: " << msg << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,29 @@
|
|||
#pragma once
|
||||
#include <rtc/rtc.hpp>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class WebRTCTransport {
|
||||
class WebRTCTransport
|
||||
{
|
||||
public:
|
||||
WebRTCTransport();
|
||||
|
||||
void createOffer();
|
||||
void setRemoteAnswer(const std::string& sdp);
|
||||
void addRemoteIce(const std::string& cand,
|
||||
const std::string& mid);
|
||||
|
||||
void onLocalSdp(std::function<void(std::string)> cb);
|
||||
void onLocalIce(std::function<void(std::string,std::string)> cb);
|
||||
void onDcOpen(std::function<void()> cb);
|
||||
void onDcMessage(std::function<void(const std::string&)> cb);
|
||||
|
||||
void createOffer();
|
||||
void createAnswer();
|
||||
void setRemoteOffer(const std::string& sdp);
|
||||
void setRemoteAnswer(const std::string& sdp);
|
||||
void addRemoteIce(const std::string& cand, const std::string& mid);
|
||||
|
||||
void sendMessage(const std::string& msg);
|
||||
|
||||
private:
|
||||
std::shared_ptr<rtc::PeerConnection> pc_;
|
||||
std::shared_ptr<rtc::DataChannel> dc_;
|
||||
bool dcOpen_ = false; // track trạng thái DataChannel
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,80 +1,72 @@
|
|||
#include "PeerServer.hpp"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
PeerServer::PeerServer(SignalingManager& sig, WebRTCTransport& rtc)
|
||||
: sig_(sig), rtc_(rtc)
|
||||
PeerServer::PeerServer(SignalingManager& sig,
|
||||
WebRTCTransport& rtc)
|
||||
: sig_(sig),
|
||||
rtc_(rtc),
|
||||
gotOffer_(false),
|
||||
sdpDone_(false)
|
||||
{
|
||||
/* ---------- DataChannel ---------- */
|
||||
rtc_.onDcOpen([]
|
||||
{
|
||||
/* ===== DATA CHANNEL ===== */
|
||||
rtc_.onDcOpen([this]{
|
||||
std::cout << "[Server] DC OPEN\n";
|
||||
|
||||
std::thread([this]{
|
||||
int i = 0;
|
||||
while (true) {
|
||||
rtc_.sendMessage(
|
||||
"Hello client #" + std::to_string(i++));
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::seconds(1));
|
||||
}
|
||||
}).detach();
|
||||
});
|
||||
|
||||
/* ---------- Local SDP ---------- */
|
||||
rtc_.onLocalSdp([this](std::string sdp)
|
||||
{
|
||||
if(!remoteSet_) {
|
||||
// chưa nhận OFFER → buffer ANSWER
|
||||
pendingAnswer_ = sdp;
|
||||
rtc_.onDcMessage([](const std::string& msg){
|
||||
std::cout << "[Server] DC received: " << msg << "\n";
|
||||
});
|
||||
|
||||
/* ===== LOCAL SDP (ANSWER) ===== */
|
||||
rtc_.onLocalSdp([this](std::string sdp){
|
||||
if (sdpDone_) {
|
||||
std::cout << "[Server] Ignore duplicate ANSWER\n";
|
||||
return;
|
||||
}
|
||||
if(answerSent_) return;
|
||||
answerSent_ = true;
|
||||
|
||||
sig_.sendSdp(sdp);
|
||||
std::cout << "[Server] Sent ANSWER\n";
|
||||
sdpDone_ = true;
|
||||
|
||||
// std::cout << "[Server] Sent ANSWER\n";
|
||||
});
|
||||
|
||||
/* ---------- Local ICE ---------- */
|
||||
rtc_.onLocalIce([this](std::string cand, std::string mid)
|
||||
{
|
||||
/* ===== LOCAL ICE ===== */
|
||||
rtc_.onLocalIce([this](std::string cand, std::string mid){
|
||||
sig_.sendIce(cand, mid);
|
||||
std::cout << "[Server] Sent ICE: " << cand << "\n";
|
||||
});
|
||||
|
||||
/* ---------- Receive signaling ---------- */
|
||||
sig_.onReceive([this](const SignalingMsg& m)
|
||||
{
|
||||
if(m.type == SigType::SDP)
|
||||
{
|
||||
if(remoteSet_) {
|
||||
/* ===== SIGNALING RECEIVE ===== */
|
||||
sig_.onReceive([this](const SignalingMsg& m){
|
||||
if (m.type == SigType::SDP) {
|
||||
|
||||
if (gotOffer_) {
|
||||
std::cout << "[Server] Ignore duplicate OFFER\n";
|
||||
return;
|
||||
}
|
||||
|
||||
rtc_.setRemoteOffer(m.payload1);
|
||||
remoteSet_ = true;
|
||||
|
||||
std::cout << "[Server] Got OFFER\n";
|
||||
gotOffer_ = true;
|
||||
|
||||
// flush buffered ICE
|
||||
for(auto& c : iceBuffer_)
|
||||
rtc_.addRemoteIce(c.first, c.second);
|
||||
iceBuffer_.clear();
|
||||
rtc_.setRemoteOffer(m.payload1);
|
||||
rtc_.createAnswer();
|
||||
|
||||
// gửi ANSWER đã buffer hoặc tạo mới
|
||||
if(!pendingAnswer_.empty())
|
||||
{
|
||||
sig_.sendSdp(pendingAnswer_);
|
||||
answerSent_ = true;
|
||||
pendingAnswer_.clear();
|
||||
std::cout << "[Server] Sent ANSWER\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
rtc_.createAnswer();
|
||||
}
|
||||
}
|
||||
else if(m.type == SigType::ICE)
|
||||
{
|
||||
if(!remoteSet_)
|
||||
{
|
||||
iceBuffer_.emplace_back(m.payload1, m.payload2);
|
||||
}
|
||||
else
|
||||
{
|
||||
rtc_.addRemoteIce(m.payload1, m.payload2);
|
||||
std::cout << "[Server] Got remote ICE: " << m.payload1 << "\n";
|
||||
} else if (m.type == SigType::ICE) {
|
||||
|
||||
}
|
||||
rtc_.addRemoteIce(m.payload1, m.payload2);
|
||||
std::cout << "[Server] Got remote ICE\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "WebRTCTransport.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class PeerServer
|
||||
{
|
||||
|
|
@ -15,9 +14,7 @@ private:
|
|||
SignalingManager& sig_;
|
||||
WebRTCTransport& rtc_;
|
||||
|
||||
bool remoteSet_ = false;
|
||||
bool answerSent_ = false;
|
||||
|
||||
std::string pendingAnswer_; // buffer ANSWER nếu onLocalSdp trigger sớm
|
||||
bool gotOffer_ = false;
|
||||
bool sdpDone_ = false;
|
||||
std::vector<std::pair<std::string,std::string>> iceBuffer_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,71 +1,159 @@
|
|||
#include "WebRTCTransport.hpp"
|
||||
#include <iostream>
|
||||
|
||||
WebRTCTransport::WebRTCTransport()
|
||||
: dcOpen_(false),
|
||||
haveRemoteOffer_(false),
|
||||
answerCreated_(false),
|
||||
localAnswerSent_(false)
|
||||
{
|
||||
rtc::Configuration cfg;
|
||||
cfg.iceServers.emplace_back(
|
||||
"stun:stun.l.google.com:19302");
|
||||
cfg.iceServers.emplace_back("stun:stun.l.google.com:19302");
|
||||
|
||||
pc_ = std::make_shared<rtc::PeerConnection>(cfg);
|
||||
dc_ = pc_->createDataChannel("dc");
|
||||
|
||||
/* ===== LOCAL ICE ===== */
|
||||
pc_->onLocalCandidate([this](rtc::Candidate c){
|
||||
if (onLocalIceCb_) {
|
||||
onLocalIceCb_(c.candidate(), c.mid());
|
||||
}
|
||||
});
|
||||
|
||||
/* ===== DATA CHANNEL ===== */
|
||||
pc_->onDataChannel([this](std::shared_ptr<rtc::DataChannel> dc){
|
||||
dc_ = dc;
|
||||
|
||||
dc_->onOpen([this]{
|
||||
dcOpen_ = true;
|
||||
if (onDcOpenCb_) onDcOpenCb_();
|
||||
});
|
||||
|
||||
dc_->onMessage([this](rtc::message_variant msg){
|
||||
if (const auto* s = std::get_if<std::string>(&msg)) {
|
||||
if (onDcMessageCb_) onDcMessageCb_(*s);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ===== CALLBACK REG ===== */
|
||||
|
||||
void WebRTCTransport::onLocalSdp(
|
||||
std::function<void(std::string)> cb)
|
||||
{
|
||||
pc_->onLocalDescription(
|
||||
[cb](rtc::Description d) {
|
||||
cb(std::string(d));
|
||||
});
|
||||
onLocalSdpCb_ = cb;
|
||||
|
||||
pc_->onLocalDescription([this](rtc::Description d){
|
||||
if (!onLocalSdpCb_) return;
|
||||
|
||||
if (d.type() != rtc::Description::Type::Answer)
|
||||
return;
|
||||
|
||||
if (localAnswerSent_) {
|
||||
std::cout << "[Transport] Ignore duplicate local ANSWER\n";
|
||||
return;
|
||||
}
|
||||
|
||||
localAnswerSent_ = true;
|
||||
onLocalSdpCb_(std::string(d));
|
||||
});
|
||||
}
|
||||
|
||||
void WebRTCTransport::onLocalIce(
|
||||
std::function<void(std::string,std::string)> cb)
|
||||
{
|
||||
pc_->onLocalCandidate(
|
||||
[cb](rtc::Candidate c) {
|
||||
cb(c.candidate(), c.mid());
|
||||
});
|
||||
onLocalIceCb_ = cb;
|
||||
}
|
||||
|
||||
void WebRTCTransport::onDcOpen(
|
||||
std::function<void()> cb)
|
||||
void WebRTCTransport::onDcOpen(std::function<void()> cb)
|
||||
{
|
||||
dc_->onOpen(cb);
|
||||
onDcOpenCb_ = cb;
|
||||
}
|
||||
|
||||
void WebRTCTransport::createOffer()
|
||||
void WebRTCTransport::onDcMessage(
|
||||
std::function<void(const std::string&)> cb)
|
||||
{
|
||||
pc_->setLocalDescription();
|
||||
onDcMessageCb_ = cb;
|
||||
}
|
||||
|
||||
void WebRTCTransport::setRemoteAnswer(
|
||||
const std::string& sdp)
|
||||
/* ===== SDP ===== */
|
||||
|
||||
void WebRTCTransport::setRemoteOffer(const std::string& sdp)
|
||||
{
|
||||
if (haveRemoteOffer_) {
|
||||
std::cout << "[Transport] Duplicate OFFER ignored\n";
|
||||
return;
|
||||
}
|
||||
|
||||
pc_->setRemoteDescription(
|
||||
rtc::Description(sdp,
|
||||
rtc::Description::Type::Offer));
|
||||
|
||||
haveRemoteOffer_ = true;
|
||||
|
||||
/* ===== FLUSH ICE BUFFER ===== */
|
||||
if (!iceBuffer_.empty()) {
|
||||
std::cout << "[Transport] Flush ICE buffer: "
|
||||
<< iceBuffer_.size()
|
||||
<< " candidate(s)\n";
|
||||
|
||||
for (auto& c : iceBuffer_) {
|
||||
std::cout << "[Transport] + "
|
||||
<< c.candidate()
|
||||
<< "\n";
|
||||
pc_->addRemoteCandidate(c);
|
||||
}
|
||||
|
||||
iceBuffer_.clear();
|
||||
} else {
|
||||
std::cout << "[Transport] ICE buffer empty\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WebRTCTransport::createAnswer()
|
||||
{
|
||||
auto state = pc_->signalingState();
|
||||
|
||||
if (state != rtc::PeerConnection::SignalingState::HaveRemoteOffer) {
|
||||
std::cout << "[Transport] Cannot create ANSWER, state = "
|
||||
<< static_cast<int>(state) << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "[Transport] Create ANSWER\n";
|
||||
pc_->setLocalDescription(rtc::Description::Type::Answer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WebRTCTransport::setRemoteAnswer(const std::string& sdp)
|
||||
{
|
||||
pc_->setRemoteDescription(
|
||||
rtc::Description(
|
||||
sdp,
|
||||
rtc::Description(sdp,
|
||||
rtc::Description::Type::Answer));
|
||||
}
|
||||
|
||||
/* ===== ICE ===== */
|
||||
|
||||
void WebRTCTransport::addRemoteIce(
|
||||
const std::string& cand,
|
||||
const std::string& mid)
|
||||
{
|
||||
pc_->addRemoteCandidate(
|
||||
rtc::Candidate(cand, mid));
|
||||
rtc::Candidate c(cand, mid);
|
||||
|
||||
if (haveRemoteOffer_) {
|
||||
pc_->addRemoteCandidate(c);
|
||||
} else {
|
||||
iceBuffer_.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCTransport::setRemoteOffer(
|
||||
const std::string& sdp)
|
||||
{
|
||||
pc_->setRemoteDescription(
|
||||
rtc::Description(
|
||||
sdp,
|
||||
rtc::Description::Type::Offer));
|
||||
}
|
||||
/* ===== DATA ===== */
|
||||
|
||||
void WebRTCTransport::createAnswer()
|
||||
void WebRTCTransport::sendMessage(const std::string& msg)
|
||||
{
|
||||
pc_->setLocalDescription();
|
||||
if (dcOpen_ && dc_) {
|
||||
dc_->send(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,51 @@
|
|||
#pragma once
|
||||
#include <rtc/rtc.hpp>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class WebRTCTransport {
|
||||
class WebRTCTransport
|
||||
{
|
||||
public:
|
||||
WebRTCTransport();
|
||||
|
||||
void createOffer();
|
||||
void setRemoteAnswer(const std::string& sdp);
|
||||
void addRemoteIce(const std::string& cand,
|
||||
const std::string& mid);
|
||||
|
||||
void onLocalSdp(std::function<void(std::string)> cb);
|
||||
void onLocalIce(std::function<void(std::string,std::string)> cb);
|
||||
void onDcOpen(std::function<void()> cb);
|
||||
void onDcMessage(std::function<void(const std::string&)> cb);
|
||||
|
||||
/* WebRTCTransport.hpp */
|
||||
void setRemoteOffer(const std::string& sdp);
|
||||
void createOffer();
|
||||
void createAnswer();
|
||||
void setRemoteOffer(const std::string& sdp);
|
||||
void setRemoteAnswer(const std::string& sdp);
|
||||
void addRemoteIce(const std::string& cand, const std::string& mid);
|
||||
|
||||
void sendMessage(const std::string& msg);
|
||||
|
||||
private:
|
||||
std::shared_ptr<rtc::PeerConnection> pc_;
|
||||
std::shared_ptr<rtc::DataChannel> dc_;
|
||||
|
||||
// ===== state =====
|
||||
bool dcOpen_ = false;
|
||||
|
||||
// SDP state
|
||||
bool haveRemoteOffer_ = false; // set sau setRemoteOffer()
|
||||
bool answerCreated_ = false; // set sau createAnswer()
|
||||
|
||||
bool localAnswerSent_ = false;
|
||||
|
||||
|
||||
// ===== ICE buffer =====
|
||||
std::vector<rtc::Candidate> iceBuffer_;
|
||||
|
||||
// ===== callbacks =====
|
||||
std::function<void(std::string)> onLocalSdpCb_;
|
||||
std::function<void(std::string,std::string)> onLocalIceCb_;
|
||||
std::function<void()> onDcOpenCb_;
|
||||
std::function<void(const std::string&)> onDcMessageCb_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue