Skip to content

Commit 5a86e47

Browse files
committed
chore: release 0.7.29
- Updated version number to 0.7.29. - Enhanced NativeWebRtcPeer class to support shared_from_this for better memory management and callback handling.
1 parent a9c2a1f commit 5a86e47

2 files changed

Lines changed: 161 additions & 120 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.7.28
1+
0.7.29

src/daemon/remote_webrtc.cpp

Lines changed: 160 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ void EnsureLibDatachannelLoggerInstalled() {
304304
});
305305
}
306306

307-
class NativeWebRtcPeer final : public WebRtcPeer {
307+
class NativeWebRtcPeer final
308+
: public WebRtcPeer,
309+
public std::enable_shared_from_this<NativeWebRtcPeer> {
308310
public:
309311
explicit NativeWebRtcPeer(RemoteFrameReader frame_reader, PixelFormat preferred_video_format)
310312
: frame_reader_(std::move(frame_reader)),
@@ -333,124 +335,148 @@ class NativeWebRtcPeer final : public WebRtcPeer {
333335
}
334336
config.disableAutoNegotiation = true;
335337
peer_ = std::make_shared<rtc::PeerConnection>(std::move(config));
336-
peer_->onStateChange([this](rtc::PeerConnection::State state) {
337-
const int code = static_cast<int>(state);
338-
// Connecting/connected fire on every session and reconnect; only
339-
// the terminal-ish transitions (disconnected/failed/closed) are
340-
// worth logging at INFO. The rest goes to verbose.
341-
if (code >= 3) {
342-
std::fprintf(stdout,
343-
"[INFO] remote_webrtc: peer state %s\n",
344-
PeerStateName(code));
345-
std::fflush(stdout);
346-
} else {
347-
VerboseLog("[INFO] remote_webrtc: peer state %s\n",
348-
PeerStateName(code));
349-
}
350-
if (state == rtc::PeerConnection::State::Connected) {
351-
StartVideoPump();
352-
StartAudioPump();
353-
}
354-
// Surface terminal transitions to the embedder so it can drop
355-
// the per-session bookkeeping and tell the browser the
356-
// desktop is gone. We deliberately ignore Disconnected
357-
// because libdatachannel will either climb back to Connected
358-
// or escalate to Failed on its own ICE consent timer; firing
359-
// on Disconnected would tear sessions down for transient
360-
// wifi blips. `peer_closed_dispatched_` keeps us idempotent
361-
// because Failed -> Closed often fires both edges.
362-
if (state == rtc::PeerConnection::State::Failed ||
363-
state == rtc::PeerConnection::State::Closed) {
364-
PeerClosedHandler handler;
365-
{
366-
std::lock_guard<std::mutex> lock(mu_);
367-
if (peer_closed_dispatched_) return;
368-
peer_closed_dispatched_ = true;
369-
handler = peer_closed_handler_;
370-
}
371-
if (handler) {
372-
const std::string reason =
373-
state == rtc::PeerConnection::State::Failed ? "peer_failed" : "peer_closed";
374-
try {
375-
handler(reason);
376-
} catch (const std::exception& e) {
377-
std::fprintf(stdout,
378-
"[ERROR] remote_webrtc: peer closed handler threw: %s\n",
379-
e.what());
380-
std::fflush(stdout);
381-
}
382-
}
383-
}
338+
}
339+
340+
void InstallCallbacks() {
341+
std::weak_ptr<NativeWebRtcPeer> weak = shared_from_this();
342+
peer_->onStateChange([weak](rtc::PeerConnection::State state) {
343+
if (auto self = weak.lock()) self->HandleStateChange(state);
384344
});
385-
peer_->onLocalDescription([this](rtc::Description description) {
386-
std::lock_guard<std::mutex> lock(mu_);
387-
local_type_ = description.typeString();
388-
local_sdp_ = StripLipSyncGroups(std::string(description));
389-
description_ready_ = true;
390-
cv_.notify_all();
345+
peer_->onLocalDescription([weak](rtc::Description description) {
346+
if (auto self = weak.lock()) self->HandleLocalDescription(std::move(description));
391347
});
392-
peer_->onLocalCandidate([this](rtc::Candidate candidate) {
393-
nlohmann::json entry = {
394-
{"candidate", std::string(candidate)},
395-
{"sdpMid", candidate.mid()},
396-
};
397-
LocalIceCandidateHandler handler;
348+
peer_->onLocalCandidate([weak](rtc::Candidate candidate) {
349+
if (auto self = weak.lock()) self->HandleLocalCandidate(std::move(candidate));
350+
});
351+
peer_->onGatheringStateChange([weak](rtc::PeerConnection::GatheringState state) {
352+
if (auto self = weak.lock()) self->HandleGatheringStateChange(state);
353+
});
354+
peer_->onDataChannel([weak](std::shared_ptr<rtc::DataChannel> channel) {
355+
if (auto self = weak.lock()) self->AttachDataChannel(std::move(channel));
356+
});
357+
peer_->onTrack([weak](std::shared_ptr<rtc::Track> track) {
358+
if (auto self = weak.lock()) self->HandleTrack(std::move(track));
359+
});
360+
}
361+
362+
void HandleStateChange(rtc::PeerConnection::State state) {
363+
const int code = static_cast<int>(state);
364+
// Connecting/connected fire on every session and reconnect; only
365+
// the terminal-ish transitions (disconnected/failed/closed) are
366+
// worth logging at INFO. The rest goes to verbose.
367+
if (code >= 3) {
368+
std::fprintf(stdout,
369+
"[INFO] remote_webrtc: peer state %s\n",
370+
PeerStateName(code));
371+
std::fflush(stdout);
372+
} else {
373+
VerboseLog("[INFO] remote_webrtc: peer state %s\n",
374+
PeerStateName(code));
375+
}
376+
if (state == rtc::PeerConnection::State::Connected) {
377+
StartVideoPump();
378+
StartAudioPump();
379+
}
380+
// Surface terminal transitions to the embedder so it can drop
381+
// the per-session bookkeeping and tell the browser the
382+
// desktop is gone. We deliberately ignore Disconnected
383+
// because libdatachannel will either climb back to Connected
384+
// or escalate to Failed on its own ICE consent timer; firing
385+
// on Disconnected would tear sessions down for transient
386+
// wifi blips. `peer_closed_dispatched_` keeps us idempotent
387+
// because Failed -> Closed often fires both edges.
388+
if (state == rtc::PeerConnection::State::Failed ||
389+
state == rtc::PeerConnection::State::Closed) {
390+
PeerClosedHandler handler;
398391
{
399392
std::lock_guard<std::mutex> lock(mu_);
400-
candidates_.push_back(entry);
401-
handler = local_ice_handler_;
393+
if (peer_closed_dispatched_) return;
394+
peer_closed_dispatched_ = true;
395+
handler = peer_closed_handler_;
402396
}
403-
// Trickle every host-side candidate to the embedder so the
404-
// browser can start probing as soon as gathering produces
405-
// host / srflx entries, instead of having to wait for the
406-
// initial answer's `candidates[]` (which we cap at a short
407-
// gathering window so STUN-blackholed networks don't stall
408-
// session creation).
409-
//
410-
// Trickle frames may legitimately reach the browser before
411-
// the answer SDP - libdatachannel's worker can fire
412-
// onLocalCandidate before our caller has had a chance to
413-
// SendJson the answer envelope. That's the standard
414-
// signaling race every WebRTC client is expected to
415-
// handle by queueing addIceCandidate calls until
416-
// setRemoteDescription resolves; the browser-side
417-
// RemoteDesktopPanel does exactly that, so we fire as
418-
// soon as we have a candidate.
419397
if (handler) {
398+
const std::string reason =
399+
state == rtc::PeerConnection::State::Failed ? "peer_failed" : "peer_closed";
420400
try {
421-
handler(std::move(entry));
401+
handler(reason);
422402
} catch (const std::exception& e) {
423403
std::fprintf(stdout,
424-
"[ERROR] remote_webrtc: local ice handler threw: %s\n",
404+
"[ERROR] remote_webrtc: peer closed handler threw: %s\n",
425405
e.what());
426406
std::fflush(stdout);
427407
}
428408
}
429-
});
430-
peer_->onGatheringStateChange([this](rtc::PeerConnection::GatheringState state) {
431-
if (state == rtc::PeerConnection::GatheringState::Complete) {
432-
std::lock_guard<std::mutex> lock(mu_);
433-
gathering_complete_ = true;
434-
cv_.notify_all();
435-
}
436-
});
437-
peer_->onDataChannel([this](std::shared_ptr<rtc::DataChannel> channel) {
438-
AttachDataChannel(std::move(channel));
439-
});
440-
peer_->onTrack([this](std::shared_ptr<rtc::Track> track) {
441-
if (!track) return;
442-
const auto description = track->description();
443-
VerboseLog("[INFO] remote_webrtc: negotiated track type=%s mid=%s\n",
444-
description.type().c_str(),
445-
description.mid().c_str());
446-
if (description.type() == "video") {
447-
ConfigureVideoTrack(std::move(track));
448-
} else if (description.type() == "audio") {
449-
ConfigureAudioTrack(std::move(track));
450-
} else {
451-
track->close();
409+
}
410+
}
411+
412+
void HandleLocalDescription(rtc::Description description) {
413+
std::lock_guard<std::mutex> lock(mu_);
414+
local_type_ = description.typeString();
415+
local_sdp_ = StripLipSyncGroups(std::string(description));
416+
description_ready_ = true;
417+
cv_.notify_all();
418+
}
419+
420+
void HandleLocalCandidate(rtc::Candidate candidate) {
421+
nlohmann::json entry = {
422+
{"candidate", std::string(candidate)},
423+
{"sdpMid", candidate.mid()},
424+
};
425+
LocalIceCandidateHandler handler;
426+
{
427+
std::lock_guard<std::mutex> lock(mu_);
428+
candidates_.push_back(entry);
429+
handler = local_ice_handler_;
430+
}
431+
// Trickle every host-side candidate to the embedder so the
432+
// browser can start probing as soon as gathering produces
433+
// host / srflx entries, instead of having to wait for the
434+
// initial answer's `candidates[]` (which we cap at a short
435+
// gathering window so STUN-blackholed networks don't stall
436+
// session creation).
437+
//
438+
// Trickle frames may legitimately reach the browser before
439+
// the answer SDP - libdatachannel's worker can fire
440+
// onLocalCandidate before our caller has had a chance to
441+
// SendJson the answer envelope. That's the standard
442+
// signaling race every WebRTC client is expected to
443+
// handle by queueing addIceCandidate calls until
444+
// setRemoteDescription resolves; the browser-side
445+
// RemoteDesktopPanel does exactly that, so we fire as
446+
// soon as we have a candidate.
447+
if (handler) {
448+
try {
449+
handler(std::move(entry));
450+
} catch (const std::exception& e) {
451+
std::fprintf(stdout,
452+
"[ERROR] remote_webrtc: local ice handler threw: %s\n",
453+
e.what());
454+
std::fflush(stdout);
452455
}
453-
});
456+
}
457+
}
458+
459+
void HandleGatheringStateChange(rtc::PeerConnection::GatheringState state) {
460+
if (state == rtc::PeerConnection::GatheringState::Complete) {
461+
std::lock_guard<std::mutex> lock(mu_);
462+
gathering_complete_ = true;
463+
cv_.notify_all();
464+
}
465+
}
466+
467+
void HandleTrack(std::shared_ptr<rtc::Track> track) {
468+
if (!track) return;
469+
const auto description = track->description();
470+
VerboseLog("[INFO] remote_webrtc: negotiated track type=%s mid=%s\n",
471+
description.type().c_str(),
472+
description.mid().c_str());
473+
if (description.type() == "video") {
474+
ConfigureVideoTrack(std::move(track));
475+
} else if (description.type() == "audio") {
476+
ConfigureAudioTrack(std::move(track));
477+
} else {
478+
track->close();
479+
}
454480
}
455481

456482
~NativeWebRtcPeer() override {
@@ -669,22 +695,27 @@ class NativeWebRtcPeer final : public WebRtcPeer {
669695
// every session, so this is 4 lines per reconnect — verbose only.
670696
VerboseLog("[INFO] remote_webrtc: data channel attached label=%s\n",
671697
label.c_str());
672-
std::weak_ptr<rtc::DataChannel> weak = channel;
698+
std::weak_ptr<NativeWebRtcPeer> self_weak = weak_from_this();
699+
std::weak_ptr<rtc::DataChannel> channel_weak = channel;
673700
channel->onMessage(
674-
[this, weak, label](rtc::binary /*data*/) {
701+
[self_weak, channel_weak, label](rtc::binary /*data*/) {
702+
if (!self_weak.lock() || channel_weak.expired()) return;
675703
std::fprintf(stdout,
676704
"[WARN] remote_webrtc: dropping binary frame on dc=%s "
677705
"(only JSON text messages are supported)\n",
678706
label.c_str());
679707
std::fflush(stdout);
680708
},
681-
[this, label](std::string text) {
682-
DispatchDataChannelText(label, std::move(text));
709+
[self_weak, label](std::string text) {
710+
if (auto self = self_weak.lock()) {
711+
self->DispatchDataChannelText(label, std::move(text));
712+
}
683713
});
684-
channel->onOpen([this, label] {
685-
DispatchDataChannelOpen(label);
714+
channel->onOpen([self_weak, label] {
715+
if (auto self = self_weak.lock()) self->DispatchDataChannelOpen(label);
686716
});
687-
channel->onClosed([this, label] {
717+
channel->onClosed([self_weak, label] {
718+
if (!self_weak.lock()) return;
688719
VerboseLog("[INFO] remote_webrtc: data channel closed label=%s\n",
689720
label.c_str());
690721
});
@@ -874,8 +905,11 @@ class NativeWebRtcPeer final : public WebRtcPeer {
874905
// estimation via LSR/DLSR is gone for video; ICE candidate-pair RTT
875906
// (browser getStats) is unaffected.
876907
packetizer->addToChain(std::make_shared<rtc::RtcpNackResponder>());
877-
packetizer->addToChain(std::make_shared<rtc::PliHandler>([this]() {
878-
force_keyframe_requested_ = true;
908+
std::weak_ptr<NativeWebRtcPeer> weak = weak_from_this();
909+
packetizer->addToChain(std::make_shared<rtc::PliHandler>([weak]() {
910+
auto self = weak.lock();
911+
if (!self) return;
912+
self->force_keyframe_requested_ = true;
879913
// Receivers fire PLI on every key frame loss / freeze recovery,
880914
// sometimes several times per second. Default to verbose; even
881915
// verbose users get rate-limited info aggregated below by the
@@ -888,9 +922,11 @@ class NativeWebRtcPeer final : public WebRtcPeer {
888922
video_track_->mid().c_str(),
889923
color_space_id,
890924
playout_delay_id);
891-
video_track_->onOpen([this]() {
925+
video_track_->onOpen([weak]() {
926+
auto self = weak.lock();
927+
if (!self) return;
892928
VerboseLog("[INFO] remote_webrtc: video track opened\n");
893-
StartVideoPump();
929+
self->StartVideoPump();
894930
});
895931
}
896932

@@ -909,9 +945,12 @@ class NativeWebRtcPeer final : public WebRtcPeer {
909945
VerboseLog("[INFO] remote_webrtc: audio payload type=%u mid=%s\n",
910946
payload_type,
911947
audio_track_->mid().c_str());
912-
audio_track_->onOpen([this]() {
948+
std::weak_ptr<NativeWebRtcPeer> weak = weak_from_this();
949+
audio_track_->onOpen([weak]() {
950+
auto self = weak.lock();
951+
if (!self) return;
913952
VerboseLog("[INFO] remote_webrtc: audio track opened\n");
914-
StartAudioPump();
953+
self->StartAudioPump();
915954
});
916955
}
917956

@@ -1546,9 +1585,11 @@ class NativeWebRtcPeer final : public WebRtcPeer {
15461585
std::shared_ptr<WebRtcPeer> CreateWebRtcPeer(
15471586
RemoteFrameReader frame_reader,
15481587
PixelFormat preferred_video_format) {
1549-
return std::make_shared<NativeWebRtcPeer>(
1588+
auto peer = std::make_shared<NativeWebRtcPeer>(
15501589
std::move(frame_reader),
15511590
preferred_video_format);
1591+
peer->InstallCallbacks();
1592+
return peer;
15521593
}
15531594

15541595
bool NativeWebRtcAvailable() {

0 commit comments

Comments
 (0)