Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build.
184 lines
5.8 KiB
C++
184 lines
5.8 KiB
C++
/*
|
|
* Copyright 2025 LiveKit, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "livekit/jsep.h"
|
|
|
|
#include <iomanip>
|
|
#include <memory>
|
|
|
|
#include "livekit/rtc_error.h"
|
|
#include "rtc_base/ref_counted_object.h"
|
|
#include "rust/cxx.h"
|
|
|
|
namespace livekit_ffi {
|
|
|
|
std::string serialize_sdp_error(webrtc::SdpParseError error) {
|
|
std::stringstream ss;
|
|
ss << std::hex << std::setfill('0');
|
|
ss << std::setw(8) << (uint32_t)error.line.length();
|
|
ss << std::dec << std::setw(1) << error.line;
|
|
ss << std::dec << std::setw(1) << error.description;
|
|
return ss.str();
|
|
}
|
|
|
|
IceCandidate::IceCandidate(
|
|
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate)
|
|
: ice_candidate_(std::move(ice_candidate)) {}
|
|
|
|
rust::String IceCandidate::sdp_mid() const {
|
|
return ice_candidate_->sdp_mid();
|
|
}
|
|
|
|
int IceCandidate::sdp_mline_index() const {
|
|
return ice_candidate_->sdp_mline_index();
|
|
}
|
|
|
|
rust::String IceCandidate::candidate() const {
|
|
return stringify();
|
|
}
|
|
|
|
rust::String IceCandidate::stringify() const {
|
|
std::string str;
|
|
ice_candidate_->ToString(&str);
|
|
return rust::String::lossy(str);
|
|
}
|
|
|
|
std::unique_ptr<webrtc::IceCandidateInterface> IceCandidate::release() {
|
|
return std::move(ice_candidate_);
|
|
}
|
|
|
|
std::shared_ptr<IceCandidate> create_ice_candidate(rust::String sdp_mid,
|
|
int sdp_mline_index,
|
|
rust::String sdp) {
|
|
webrtc::SdpParseError error;
|
|
auto ice_rtc = webrtc::CreateIceCandidate(sdp_mid.c_str(), sdp_mline_index,
|
|
sdp.c_str(), &error);
|
|
if (!ice_rtc) {
|
|
throw std::runtime_error(serialize_sdp_error(error));
|
|
}
|
|
|
|
return std::make_shared<IceCandidate>(
|
|
std::unique_ptr<webrtc::IceCandidateInterface>(ice_rtc));
|
|
}
|
|
|
|
SessionDescription::SessionDescription(
|
|
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description)
|
|
: session_description_(std::move(session_description)) {}
|
|
|
|
SdpType SessionDescription::sdp_type() const {
|
|
return static_cast<SdpType>(session_description_->GetType());
|
|
}
|
|
|
|
rust::String SessionDescription::stringify() const {
|
|
std::string str;
|
|
session_description_->ToString(&str);
|
|
return rust::String::lossy(str);
|
|
}
|
|
|
|
std::unique_ptr<SessionDescription> SessionDescription::clone() const {
|
|
return std::make_unique<SessionDescription>(session_description_->Clone());
|
|
}
|
|
|
|
std::unique_ptr<webrtc::SessionDescriptionInterface>
|
|
SessionDescription::release() {
|
|
return std::move(session_description_);
|
|
}
|
|
|
|
std::unique_ptr<SessionDescription> create_session_description(
|
|
SdpType type,
|
|
rust::String sdp) {
|
|
webrtc::SdpParseError error;
|
|
auto rtc_sdp = webrtc::CreateSessionDescription(
|
|
static_cast<webrtc::SdpType>(type), sdp.c_str(), &error);
|
|
if (!rtc_sdp) {
|
|
throw std::runtime_error(serialize_sdp_error(error));
|
|
}
|
|
|
|
return std::make_unique<SessionDescription>(std::move(rtc_sdp));
|
|
}
|
|
|
|
#ifdef LIVEKIT_TEST
|
|
rust::String serialize_sdp_parse_error_for_test() {
|
|
webrtc::SdpParseError error;
|
|
auto rtc_sdp = webrtc::CreateSessionDescription(
|
|
webrtc::SdpType::kOffer,
|
|
"v=0\n"
|
|
"o=- 6549709950142776241 2 IN IP4 127.0.0.1\n"
|
|
"s=-\n"
|
|
"t=0 0\n"
|
|
"======================== ERROR HERE\n"
|
|
"a=group:BUNDLE 0\n"
|
|
"a=extmap-allow-mixed\n"
|
|
"a=msid-semantic: WMS\n"
|
|
"m=application 9 UDP/DTLS/SCTP webrtc-datachannel\n"
|
|
"c=IN IP4 0.0.0.0\n"
|
|
"a=ice-ufrag:Tw7h\n"
|
|
"a=ice-pwd:6XOVUD6HpcB4c1M8EB8jXJE9\n"
|
|
"a=ice-options:trickle\n"
|
|
"a=fingerprint:sha-256 "
|
|
"4F:EC:23:59:5D:A5:E6:3E:3E:5D:8A:09:B6:FA:04:AA:19:99:49:67:BD:65:93:06:BB:EE:AC:D5:21:0F:57:D6\n"
|
|
"a=setup:actpass\n"
|
|
"a=mid:0\n"
|
|
"a=sctp-port:5000\n"
|
|
"a=max-message-size:262144\n",
|
|
&error);
|
|
if (rtc_sdp) {
|
|
return rust::String("");
|
|
}
|
|
return rust::String::lossy(serialize_sdp_error(error));
|
|
}
|
|
#endif
|
|
|
|
NativeCreateSdpObserver::NativeCreateSdpObserver(
|
|
rust::Box<PeerContext> ctx,
|
|
rust::Fn<void(rust::Box<PeerContext>, std::unique_ptr<SessionDescription>)>
|
|
on_success,
|
|
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_error)
|
|
: ctx_(std::move(ctx)), on_success_(on_success), on_error_(on_error) {}
|
|
|
|
void NativeCreateSdpObserver::OnSuccess(
|
|
webrtc::SessionDescriptionInterface* desc) {
|
|
// We have ownership of desc
|
|
on_success_(std::move(ctx_),
|
|
std::make_unique<SessionDescription>(
|
|
std::unique_ptr<webrtc::SessionDescriptionInterface>(desc)));
|
|
}
|
|
|
|
void NativeCreateSdpObserver::OnFailure(webrtc::RTCError error) {
|
|
on_error_(std::move(ctx_), to_error(error));
|
|
}
|
|
|
|
NativeSetLocalSdpObserver::NativeSetLocalSdpObserver(
|
|
rust::Box<PeerContext> ctx,
|
|
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete)
|
|
: ctx_(std::move(ctx)), on_complete_(on_complete) {}
|
|
|
|
void NativeSetLocalSdpObserver::OnSetLocalDescriptionComplete(
|
|
webrtc::RTCError error) {
|
|
on_complete_(std::move(ctx_), to_error(error));
|
|
}
|
|
|
|
NativeSetRemoteSdpObserver::NativeSetRemoteSdpObserver(
|
|
rust::Box<PeerContext> ctx,
|
|
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete)
|
|
: ctx_(std::move(ctx)), on_complete_(on_complete) {}
|
|
|
|
void NativeSetRemoteSdpObserver::OnSetRemoteDescriptionComplete(
|
|
webrtc::RTCError error) {
|
|
on_complete_(std::move(ctx_), to_error(error));
|
|
}
|
|
} // namespace livekit_ffi
|