175 lines
4.9 KiB
Rust
175 lines
4.9 KiB
Rust
use crate::output::{EncodedPacket, EncodedPacketType, OutputStream, OutputStreamError};
|
|
use anyhow::Result;
|
|
use log::{error, info};
|
|
use std::{
|
|
os::raw::{c_char, c_void},
|
|
slice,
|
|
time::Duration,
|
|
};
|
|
use tokio::runtime::Runtime;
|
|
|
|
pub struct RTCOutput {
|
|
stream: OutputStream,
|
|
runtime: Runtime,
|
|
}
|
|
/// cbindgen:prefix-with-name
|
|
#[repr(C)]
|
|
pub enum RTCOutputError {
|
|
ConnectFailed,
|
|
NetworkError,
|
|
}
|
|
|
|
impl From<OutputStreamError> for RTCOutputError {
|
|
fn from(ose: OutputStreamError) -> Self {
|
|
match ose {
|
|
OutputStreamError::ConnectFailed => RTCOutputError::ConnectFailed,
|
|
OutputStreamError::NetworkError => RTCOutputError::NetworkError,
|
|
OutputStreamError::WriteError(_) => RTCOutputError::NetworkError,
|
|
}
|
|
}
|
|
}
|
|
|
|
// You must call `char_rtc_output_free` on the returned value
|
|
#[no_mangle]
|
|
pub extern "C" fn char_rtc_output_new() -> *mut RTCOutput {
|
|
(|| -> Result<*mut RTCOutput> {
|
|
let runtime = tokio::runtime::Runtime::new()?;
|
|
let stream = runtime.block_on(OutputStream::new())?;
|
|
Ok(Box::into_raw(Box::new(RTCOutput { stream, runtime })))
|
|
})()
|
|
.unwrap_or_else(|e| {
|
|
error!("Unable to create whip output: {e:?}");
|
|
std::ptr::null_mut::<RTCOutput>()
|
|
})
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_free(output: *mut RTCOutput) {
|
|
info!("Freeing whip output");
|
|
if !output.is_null() {
|
|
drop(Box::from_raw(output));
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_bytes_sent(output: *const RTCOutput) -> u64 {
|
|
let output = output.as_ref().unwrap();
|
|
output.stream.bytes_sent()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_dropped_frames(output: *const RTCOutput) -> i32 {
|
|
let output = output.as_ref().unwrap();
|
|
output.stream.dropped_frames()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_congestion(output: *const RTCOutput) -> f64 {
|
|
let output = output.as_ref().unwrap();
|
|
output.stream.congestion()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_connect_time_ms(output: *const RTCOutput) -> i32 {
|
|
let output = output.as_ref().unwrap();
|
|
output.stream.connect_time().as_millis() as i32
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_connect(
|
|
output: *const RTCOutput,
|
|
url: *const c_char,
|
|
bearer_token: *const c_char,
|
|
) {
|
|
let output = output.as_ref().unwrap();
|
|
|
|
let url = std::ffi::CStr::from_ptr(url).to_str().unwrap().to_owned();
|
|
let bearer_token = if !bearer_token.is_null() {
|
|
Some(
|
|
std::ffi::CStr::from_ptr(bearer_token)
|
|
.to_str()
|
|
.unwrap()
|
|
.to_owned(),
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
output.runtime.spawn(async move {
|
|
output.stream.connect(&url, bearer_token.as_deref()).await;
|
|
});
|
|
}
|
|
|
|
// Once closed, you cannot call `char_rtc_output_connect` again
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_close(output: *const RTCOutput) {
|
|
let output = output.as_ref().unwrap();
|
|
|
|
info!("Closing whip output");
|
|
output
|
|
.runtime
|
|
.block_on(async {
|
|
info!("I'm on a thread!");
|
|
output.stream.close().await
|
|
})
|
|
.unwrap_or_else(|e| error!("Failed closing whip output: {e:?}"))
|
|
}
|
|
|
|
/// Write an audio or video packet to the whip output
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_write(
|
|
output: *const RTCOutput,
|
|
data: *const u8,
|
|
size: usize,
|
|
duration: u64,
|
|
is_audio: bool,
|
|
) -> bool {
|
|
let output = output.as_ref().unwrap();
|
|
|
|
let slice: &[u8] = slice::from_raw_parts(data, size);
|
|
let encoded_packet = EncodedPacket {
|
|
data: slice.to_owned(),
|
|
duration: Duration::from_micros(duration),
|
|
typ: if is_audio {
|
|
EncodedPacketType::Audio
|
|
} else {
|
|
EncodedPacketType::Video
|
|
},
|
|
};
|
|
|
|
output
|
|
.stream
|
|
.write(encoded_packet)
|
|
.map(|_| true)
|
|
.unwrap_or_else(|e| {
|
|
error!("Failed to write packets to whip output: {e:?}");
|
|
false
|
|
})
|
|
}
|
|
|
|
pub struct ErrorCallbackUserdata(*mut c_void);
|
|
unsafe impl Send for ErrorCallbackUserdata {}
|
|
unsafe impl Sync for ErrorCallbackUserdata {}
|
|
|
|
impl ErrorCallbackUserdata {
|
|
fn as_ptr(&self) -> *mut c_void {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
type RTCOutputErrorCallback = unsafe extern "C" fn(user_data: *mut c_void, error: RTCOutputError);
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn char_rtc_output_set_error_callback(
|
|
output: *const RTCOutput,
|
|
cb: RTCOutputErrorCallback,
|
|
user_data: *mut c_void,
|
|
) {
|
|
let output = output.as_ref().unwrap();
|
|
|
|
let user_data = ErrorCallbackUserdata(user_data);
|
|
output
|
|
.stream
|
|
.set_error_callback(Box::new(move |error| cb(user_data.as_ptr(), error.into())))
|
|
}
|