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")]
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    use log::info;
103
104    use crate::jsep::ffi;
105
106    #[test]
107    fn throw_error() {
108        let sdp_string = "v=0
109o=- 6549709950142776241 2 IN IP4 127.0.0.1
110s=-
111t=0 0
112======================== ERROR HERE
113a=group:BUNDLE 0
114a=extmap-allow-mixed
115a=msid-semantic: WMS
116m=application 9 UDP/DTLS/SCTP webrtc-datachannel
117c=IN IP4 0.0.0.0
118a=ice-ufrag:Tw7h
119a=ice-pwd:6XOVUD6HpcB4c1M8EB8jXJE9
120a=ice-options:trickle
121a=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
122a=setup:actpass
123a=mid:0
124a=sctp-port:5000
125a=max-message-size:262144
126";
127
128        let sdp = ffi::create_session_description(ffi::SdpType::Offer, sdp_string.to_string());
129        let err = unsafe { ffi::SdpParseError::from(sdp.err().unwrap().what()) };
130        info!("parse err: {:?}", err)
131    }
132}