use std::error::Error; use std::fmt::{Debug, Display, Formatter}; use std::mem::ManuallyDrop; use std::str::FromStr; use cxx::UniquePtr; use crate::rtc_error::ffi::RTCError; #[cxx::bridge(namespace = "livekit")] pub mod ffi { #[derive(Debug)] #[repr(i32)] pub enum SdpType { Offer, PrAnswer, Answer, Rollback, } #[derive(Debug)] pub struct SdpParseError { pub line: String, pub description: String, } extern "Rust" { type CreateSdpObserverWrapper; fn on_success( self: &CreateSdpObserverWrapper, session_description: UniquePtr, ); fn on_failure(self: &CreateSdpObserverWrapper, error: RTCError); type SetLocalSdpObserverWrapper; fn on_set_local_description_complete(self: &SetLocalSdpObserverWrapper, error: RTCError); type SetRemoteSdpObserverWrapper; fn on_set_remote_description_complete(self: &SetRemoteSdpObserverWrapper, error: RTCError); } unsafe extern "C++" { include!("webrtc-sys/src/rtc_error.rs.h"); include!("livekit/jsep.h"); type RTCError = crate::rtc_error::ffi::RTCError; type IceCandidate; type SessionDescription; type NativeCreateSdpObserverHandle; type NativeSetLocalSdpObserverHandle; type NativeSetRemoteSdpObserverHandle; fn sdp_mid(self: &IceCandidate) -> String; fn sdp_mline_index(self: &IceCandidate) -> i32; fn candidate(self: &IceCandidate) -> String; fn stringify(self: &IceCandidate) -> String; fn stringify(self: &SessionDescription) -> String; fn clone(self: &SessionDescription) -> UniquePtr; fn create_native_create_sdp_observer( observer: Box, ) -> UniquePtr; fn create_native_set_local_sdp_observer( observer: Box, ) -> UniquePtr; fn create_native_set_remote_sdp_observer( observer: Box, ) -> UniquePtr; fn create_ice_candidate( sdp_mid: String, sdp_mline_index: i32, sdp: String, ) -> Result>; fn create_session_description( sdp_type: SdpType, sdp: String, ) -> Result>; fn _unique_ice_candidate() -> UniquePtr; // Ignore fn _unique_session_description() -> UniquePtr; // Ignore } } impl Error for ffi::SdpParseError {} impl Display for ffi::SdpParseError { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!( f, "SdpParseError occurred {}: {}", self.line, self.description ) } } unsafe impl Send for ffi::SessionDescription {} unsafe impl Sync for ffi::SessionDescription {} unsafe impl Send for ffi::IceCandidate {} unsafe impl Sync for ffi::IceCandidate {} impl ffi::SdpParseError { /// # Safety /// The value must be correctly encoded pub unsafe fn from(value: &str) -> Self { // Parse the hex encoded error from c++ let line_length = u32::from_str_radix(&value[0..8], 16).unwrap() as usize + 8; let line = String::from(&value[8..line_length]); let description = String::from(&value[line_length..]); Self { line, description } } } impl FromStr for ffi::SdpType { type Err = (); fn from_str(s: &str) -> Result { match s { "offer" => Ok(ffi::SdpType::Offer), "pranswer" => Ok(ffi::SdpType::PrAnswer), "answer" => Ok(ffi::SdpType::Answer), "rollback" => Ok(ffi::SdpType::Rollback), _ => Err(()), } } } // CreateSdpObserver pub struct CreateSdpObserverWrapper { pub on_success: ManuallyDrop) + Send>>, pub on_failure: ManuallyDrop>, } impl CreateSdpObserverWrapper { fn on_success(&self, session_description: UniquePtr) { unsafe { std::ptr::read(&*self.on_success)(session_description); } } fn on_failure(&self, error: RTCError) { unsafe { std::ptr::read(&*self.on_failure)(error); } } } // SetLocalSdpObserver pub struct SetLocalSdpObserverWrapper(pub ManuallyDrop>); impl SetLocalSdpObserverWrapper { fn on_set_local_description_complete(&self, error: RTCError) { unsafe { std::ptr::read(&*self.0)(error); } } } // SetRemoteSdpObserver pub struct SetRemoteSdpObserverWrapper(pub ManuallyDrop>); impl SetRemoteSdpObserverWrapper { fn on_set_remote_description_complete(&self, error: RTCError) { unsafe { std::ptr::read(&*self.0)(error); } } } #[cfg(test)] mod tests { use log::info; use crate::jsep::ffi; #[test] fn throw_error() { let sdp_string = "v=0 o=- 6549709950142776241 2 IN IP4 127.0.0.1 s=- t=0 0 ======================== ERROR HERE a=group:BUNDLE 0 a=extmap-allow-mixed a=msid-semantic: WMS m=application 9 UDP/DTLS/SCTP webrtc-datachannel c=IN IP4 0.0.0.0 a=ice-ufrag:Tw7h a=ice-pwd:6XOVUD6HpcB4c1M8EB8jXJE9 a=ice-options:trickle 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 a=setup:actpass a=mid:0 a=sctp-port:5000 a=max-message-size:262144 "; let sdp = ffi::create_session_description(ffi::SdpType::Offer, sdp_string.to_string()); let err = unsafe { ffi::SdpParseError::from(sdp.err().unwrap().what()) }; info!("parse err: {:?}", err) } }