Skip to main content

webrtc_sys/
jsep.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    error::Error,
17    fmt::{Display, Formatter},
18};
19
20use crate::impl_thread_safety;
21
22#[cxx::bridge(namespace = "livekit_ffi")]
23pub mod ffi {
24    #[derive(Debug)]
25    #[repr(i32)]
26    pub enum SdpType {
27        Offer,
28        PrAnswer,
29        Answer,
30        Rollback,
31    }
32
33    #[derive(Debug)]
34    pub struct SdpParseError {
35        pub line: String,
36        pub description: String,
37    }
38
39    extern "C++" {
40        include!("livekit/rtc_error.h");
41
42        type RtcError = crate::rtc_error::ffi::RtcError;
43    }
44
45    unsafe extern "C++" {
46        include!("livekit/jsep.h");
47
48        type IceCandidate;
49        type SessionDescription;
50
51        fn sdp_mid(self: &IceCandidate) -> String;
52        fn sdp_mline_index(self: &IceCandidate) -> i32;
53        fn candidate(self: &IceCandidate) -> String;
54        fn stringify(self: &IceCandidate) -> String;
55
56        fn sdp_type(self: &SessionDescription) -> SdpType;
57        fn stringify(self: &SessionDescription) -> String;
58        fn clone(self: &SessionDescription) -> UniquePtr<SessionDescription>;
59
60        fn create_ice_candidate(
61            sdp_mid: String,
62            sdp_mline_index: i32,
63            sdp: String,
64        ) -> Result<SharedPtr<IceCandidate>>;
65
66        fn create_session_description(
67            sdp_type: SdpType,
68            sdp: String,
69        ) -> Result<UniquePtr<SessionDescription>>;
70
71        fn _shared_ice_candidate() -> SharedPtr<IceCandidate>; // Ignore
72        fn _unique_session_description() -> UniquePtr<SessionDescription>; // Ignore
73    }
74}
75
76impl Error for ffi::SdpParseError {}
77
78impl Display for ffi::SdpParseError {
79    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
80        write!(f, "SdpParseError occurred {}: {}", self.line, self.description)
81    }
82}
83
84impl_thread_safety!(ffi::SessionDescription, Send + Sync);
85impl_thread_safety!(ffi::IceCandidate, Send + Sync);
86
87impl ffi::SdpParseError {
88    /// # Safety
89    /// The value must be correctly encoded
90    pub unsafe fn from(value: &str) -> Self {
91        // Parse the hex encoded error from c++
92        let line_length = u32::from_str_radix(&value[0..8], 16).unwrap() as usize + 8;
93        let line = String::from(&value[8..line_length]);
94        let description = String::from(&value[line_length..]);
95
96        Self { line, description }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    #[cxx::bridge(namespace = "livekit_ffi")]
103    pub mod ffi_tests {
104        unsafe extern "C++" {
105            include!("livekit/jsep.h");
106
107            fn serialize_sdp_parse_error_for_test() -> String;
108        }
109    }
110
111    use crate::jsep::ffi;
112
113    /// Tests that SdpParseError can correctly deserialize the hex-encoded
114    /// error format produced by C++ when SDP parsing fails.
115    #[test]
116    fn sdp_parse_error_deserialization() {
117        let serialized = ffi_tests::serialize_sdp_parse_error_for_test();
118        let err = unsafe { ffi::SdpParseError::from(&serialized) };
119
120        assert!(!err.line.is_empty(), "error line should not be empty");
121        assert!(!err.description.is_empty(), "error description should not be empty");
122    }
123}