webrtc version 2

This commit is contained in:
phamvannhat 2025-12-29 14:42:20 +07:00
parent 1194b48884
commit 72c09ad308
8 changed files with 292 additions and 152 deletions

View File

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

View File

@ -24,4 +24,6 @@ public:
private:
SignalingManager& sig_;
WebRTCTransport& rtc_;
bool offerSent_ = false;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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