/* * 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 #include #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 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 IceCandidate::release() { return std::move(ice_candidate_); } std::shared_ptr 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( std::unique_ptr(ice_rtc)); } SessionDescription::SessionDescription( std::unique_ptr session_description) : session_description_(std::move(session_description)) {} SdpType SessionDescription::sdp_type() const { return static_cast(session_description_->GetType()); } rust::String SessionDescription::stringify() const { std::string str; session_description_->ToString(&str); return rust::String::lossy(str); } std::unique_ptr SessionDescription::clone() const { return std::make_unique(session_description_->Clone()); } std::unique_ptr SessionDescription::release() { return std::move(session_description_); } std::unique_ptr create_session_description( SdpType type, rust::String sdp) { webrtc::SdpParseError error; auto rtc_sdp = webrtc::CreateSessionDescription( static_cast(type), sdp.c_str(), &error); if (!rtc_sdp) { throw std::runtime_error(serialize_sdp_error(error)); } return std::make_unique(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 ctx, rust::Fn, std::unique_ptr)> on_success, rust::Fn, 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( std::unique_ptr(desc))); } void NativeCreateSdpObserver::OnFailure(webrtc::RTCError error) { on_error_(std::move(ctx_), to_error(error)); } NativeSetLocalSdpObserver::NativeSetLocalSdpObserver( rust::Box ctx, rust::Fn, 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 ctx, rust::Fn, 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