datachannel/
track.rs

1use std::ffi::{c_void, CStr, CString};
2use std::os::raw::c_char;
3use std::{ptr, slice};
4
5use datachannel_sys as sys;
6use webrtc_sdp::media_type::{parse_media_vector, SdpMedia};
7use webrtc_sdp::{parse_sdp_line, SdpLine};
8
9use crate::error::{check, Result};
10use crate::logger;
11
12#[derive(Debug, Clone, Copy)]
13#[cfg_attr(any(not(target_os = "windows"), target_env = "gnu"), repr(u32))]
14#[cfg_attr(all(target_os = "windows", not(target_env = "gnu")), repr(i32))]
15pub enum Direction {
16    Unknown = sys::rtcDirection_RTC_DIRECTION_UNKNOWN,
17    SendOnly = sys::rtcDirection_RTC_DIRECTION_SENDONLY,
18    RecvOnly = sys::rtcDirection_RTC_DIRECTION_RECVONLY,
19    SendRecv = sys::rtcDirection_RTC_DIRECTION_SENDRECV,
20    Inactive = sys::rtcDirection_RTC_DIRECTION_INACTIVE,
21}
22
23#[cfg(any(not(target_os = "windows"), target_env = "gnu"))]
24impl TryFrom<u32> for Direction {
25    type Error = ();
26
27    fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
28        match v {
29            x if x == Self::Unknown as u32 => Ok(Self::Unknown),
30            x if x == Self::SendOnly as u32 => Ok(Self::SendOnly),
31            x if x == Self::RecvOnly as u32 => Ok(Self::RecvOnly),
32            x if x == Self::SendRecv as u32 => Ok(Self::SendRecv),
33            x if x == Self::Inactive as u32 => Ok(Self::Inactive),
34            _ => Err(()),
35        }
36    }
37}
38
39#[cfg(all(target_os = "windows", not(target_env = "gnu")))]
40impl TryFrom<i32> for Direction {
41    type Error = ();
42
43    fn try_from(v: i32) -> std::result::Result<Self, Self::Error> {
44        match v {
45            x if x == Self::Unknown as i32 => Ok(Self::Unknown),
46            x if x == Self::SendOnly as i32 => Ok(Self::SendOnly),
47            x if x == Self::RecvOnly as i32 => Ok(Self::RecvOnly),
48            x if x == Self::SendRecv as i32 => Ok(Self::SendRecv),
49            x if x == Self::Inactive as i32 => Ok(Self::Inactive),
50            _ => Err(()),
51        }
52    }
53}
54
55#[derive(Debug, Clone, Copy)]
56#[cfg_attr(any(not(target_os = "windows"), target_env = "gnu"), repr(u32))]
57#[cfg_attr(all(target_os = "windows", not(target_env = "gnu")), repr(i32))]
58pub enum Codec {
59    H264 = sys::rtcCodec_RTC_CODEC_H264,
60    VP8 = sys::rtcCodec_RTC_CODEC_VP8,
61    VP9 = sys::rtcCodec_RTC_CODEC_VP9,
62    Opus = sys::rtcCodec_RTC_CODEC_OPUS,
63}
64
65#[derive(Debug, Clone)]
66pub struct TrackInit {
67    pub direction: Direction,
68    pub codec: Codec,
69    pub payload_type: i32,
70    pub ssrc: u32,
71    pub mid: CString,
72    pub name: Option<CString>,
73    pub msid: Option<CString>,
74    pub track_id: Option<CString>,
75    pub profile: Option<CString>,
76}
77
78impl TrackInit {
79    pub(crate) fn as_raw(&self) -> sys::rtcTrackInit {
80        sys::rtcTrackInit {
81            direction: self.direction as _,
82            codec: self.codec as _,
83            payloadType: self.payload_type,
84            ssrc: self.ssrc,
85            mid: self.mid.as_ptr(),
86            name: self
87                .name
88                .as_ref()
89                .map(|s| s.as_ptr())
90                .unwrap_or(std::ptr::null()),
91            msid: self
92                .msid
93                .as_ref()
94                .map(|s| s.as_ptr())
95                .unwrap_or(std::ptr::null()),
96            trackId: self
97                .track_id
98                .as_ref()
99                .map(|s| s.as_ptr())
100                .unwrap_or(std::ptr::null()),
101            profile: self
102                .profile
103                .as_ref()
104                .map(|s| s.as_ptr())
105                .unwrap_or(std::ptr::null()),
106        }
107    }
108}
109
110#[allow(unused_variables)]
111pub trait TrackHandler {
112    fn on_open(&mut self) {}
113    fn on_closed(&mut self) {}
114    fn on_error(&mut self, err: &str) {}
115    fn on_message(&mut self, msg: &[u8]) {}
116    fn on_available(&mut self) {}
117}
118
119pub struct RtcTrack<T> {
120    id: i32,
121    t_handler: T,
122}
123
124impl<T> RtcTrack<T>
125where
126    T: TrackHandler + Send,
127{
128    pub(crate) fn new(id: i32, t_handler: T) -> Result<Box<Self>> {
129        unsafe {
130            let mut rtc_t = Box::new(RtcTrack { id, t_handler });
131            let ptr = &mut *rtc_t;
132
133            sys::rtcSetUserPointer(id, ptr as *mut _ as *mut c_void);
134
135            check(sys::rtcSetOpenCallback(id, Some(RtcTrack::<T>::open_cb)))?;
136
137            check(sys::rtcSetClosedCallback(
138                id,
139                Some(RtcTrack::<T>::closed_cb),
140            ))?;
141
142            check(sys::rtcSetErrorCallback(id, Some(RtcTrack::<T>::error_cb)))?;
143
144            check(sys::rtcSetMessageCallback(
145                id,
146                Some(RtcTrack::<T>::message_cb),
147            ))?;
148
149            check(sys::rtcSetAvailableCallback(
150                id,
151                Some(RtcTrack::<T>::available_cb),
152            ))?;
153
154            Ok(rtc_t)
155        }
156    }
157
158    unsafe extern "C" fn open_cb(_: i32, ptr: *mut c_void) {
159        let rtc_t = &mut *(ptr as *mut RtcTrack<T>);
160        rtc_t.t_handler.on_open()
161    }
162
163    unsafe extern "C" fn closed_cb(_: i32, ptr: *mut c_void) {
164        let rtc_t = &mut *(ptr as *mut RtcTrack<T>);
165        rtc_t.t_handler.on_closed()
166    }
167
168    unsafe extern "C" fn error_cb(_: i32, err: *const c_char, ptr: *mut c_void) {
169        let rtc_t = &mut *(ptr as *mut RtcTrack<T>);
170        let err = CStr::from_ptr(err).to_string_lossy();
171        rtc_t.t_handler.on_error(&err)
172    }
173
174    unsafe extern "C" fn message_cb(_: i32, msg: *const c_char, size: i32, ptr: *mut c_void) {
175        let rtc_t = &mut *(ptr as *mut RtcTrack<T>);
176        let msg = if size < 0 {
177            CStr::from_ptr(msg).to_bytes()
178        } else {
179            slice::from_raw_parts(msg as *const u8, size as usize)
180        };
181        rtc_t.t_handler.on_message(msg)
182    }
183
184    unsafe extern "C" fn available_cb(_: i32, ptr: *mut c_void) {
185        let rtc_t = &mut *(ptr as *mut RtcTrack<T>);
186        rtc_t.t_handler.on_available()
187    }
188
189    pub fn send(&mut self, msg: &[u8]) -> Result<()> {
190        check(unsafe {
191            sys::rtcSendMessage(self.id, msg.as_ptr() as *const c_char, msg.len() as i32)
192        })
193        .map(|_| ())
194    }
195
196    pub fn description(&self) -> Option<Vec<SdpMedia>> {
197        let buf_size = check(unsafe {
198            sys::rtcGetTrackDescription(self.id, ptr::null_mut() as *mut c_char, 0)
199        })
200        .expect("Couldn't get buffer size") as usize;
201
202        let mut buf = vec![0; buf_size];
203        check(unsafe {
204            sys::rtcGetTrackDescription(self.id, buf.as_mut_ptr() as *mut c_char, buf_size as i32)
205        })
206        .map_err(|err| {
207            logger::warn!(
208                "Couldn't get description for RtcTrack id={} {:p}, {}",
209                self.id,
210                self,
211                err
212            );
213        })
214        .ok()
215        .and_then(|_| {
216            crate::ffi_string(&buf)
217                .map_err(|err| {
218                    logger::error!(
219                        "Couldn't get description for RtcTrack id={} {:p}, {}",
220                        self.id,
221                        self,
222                        err
223                    );
224                })
225                .ok()
226        })
227        .and_then(|description| {
228            description
229                .split('\n')
230                .enumerate()
231                .map(|(line_number, line)| parse_sdp_line(line, line_number))
232                .collect::<std::result::Result<Vec<SdpLine>, _>>()
233                .map_err(|err| logger::error!("Couldn't parse SdpLine: {}", err))
234                .ok()
235        })
236        .and_then(|mut sdp_lines| {
237            parse_media_vector(&mut sdp_lines)
238                .map_err(|err| logger::error!("Couldn't parse SdpMedia: {}", err))
239                .ok()
240        })
241    }
242
243    pub fn mid(&self) -> String {
244        let buf_size =
245            check(unsafe { sys::rtcGetTrackMid(self.id, ptr::null_mut() as *mut c_char, 0) })
246                .expect("Couldn't get buffer size") as usize;
247
248        let mut buf = vec![0; buf_size];
249        check(unsafe {
250            sys::rtcGetTrackMid(self.id, buf.as_mut_ptr() as *mut c_char, buf_size as i32)
251        })
252        .map_err(|err| {
253            logger::warn!(
254                "Couldn't get mid for RtcTrack id={} {:p}, {}",
255                self.id,
256                self,
257                err
258            );
259        })
260        .ok()
261        .and_then(|_| {
262            crate::ffi_string(&buf)
263                .map_err(|err| {
264                    logger::error!(
265                        "Couldn't get mid for RtcTrack id={} {:p}, {}",
266                        self.id,
267                        self,
268                        err
269                    );
270                })
271                .ok()
272        })
273        .unwrap_or_default()
274    }
275
276    pub fn direction(&self) -> Direction {
277        let mut direction = sys::rtcDirection_RTC_DIRECTION_UNKNOWN;
278        check(unsafe { sys::rtcGetTrackDirection(self.id, &mut direction) })
279            .expect("Couldn't get RtcTrack direction");
280        Direction::try_from(direction).unwrap_or(Direction::Unknown)
281    }
282}
283
284impl<T> Drop for RtcTrack<T> {
285    fn drop(&mut self) {
286        if let Err(err) = check(unsafe { sys::rtcDeleteTrack(self.id) }) {
287            logger::error!(
288                "Error while dropping RtcTrack id={} {:p}: {}",
289                self.id,
290                self,
291                err
292            );
293        }
294    }
295}