gst-meet/lib-gst-meet-c/src/lib.rs

296 lines
7.2 KiB
Rust

use std::{
ffi::{c_void, CStr, CString},
os::raw::c_char,
ptr,
sync::{
atomic::{AtomicPtr, Ordering},
Arc,
},
};
use anyhow::Result;
use glib::{
ffi::GMainContext,
translate::{from_glib, from_glib_full, ToGlibPtr},
};
use lib_gst_meet::JitsiConferenceConfig;
pub use lib_gst_meet::{init_tracing, Authentication, Connection, JitsiConference, MediaType};
use tokio::runtime::Runtime;
pub struct Context {
runtime: Runtime,
}
#[repr(C)]
pub struct ConferenceConfig {
pub muc: *const c_char,
pub focus: *const c_char,
pub nick: *const c_char,
pub region: *const c_char,
pub video_codec: *const c_char,
}
#[repr(C)]
pub struct Participant {
pub jid: *const c_char,
pub muc_jid: *const c_char,
pub nick: *const c_char,
}
trait ResultExt<T> {
fn ok_raw_or_log(self) -> *mut T;
}
impl<T> ResultExt<T> for Result<T> {
fn ok_raw_or_log(self) -> *mut T {
match self {
Ok(o) => Box::into_raw(Box::new(o)),
Err(e) => {
eprintln!("lib-gst-meet: {:?}", e);
ptr::null_mut()
},
}
}
}
#[no_mangle]
pub extern "C" fn gstmeet_init() -> *mut Context {
Runtime::new()
.map(|runtime| Context { runtime })
.map_err(|e| e.into())
.ok_raw_or_log()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_init_tracing(level: *const c_char) {
let level = CStr::from_ptr(level)
.to_string_lossy()
.parse()
.expect("invalid tracing level");
init_tracing(level);
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_deinit(context: *mut Context) {
Box::from_raw(context);
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_connection_new(
context: *mut Context,
websocket_url: *const c_char,
xmpp_domain: *const c_char,
tls_insecure: bool,
) -> *mut Connection {
let websocket_url = CStr::from_ptr(websocket_url);
let xmpp_domain = CStr::from_ptr(xmpp_domain);
(*context)
.runtime
.block_on(Connection::new(
&websocket_url.to_string_lossy(),
&xmpp_domain.to_string_lossy(),
Authentication::Anonymous,
tls_insecure,
))
.map(|(connection, background)| {
(*context).runtime.spawn(background);
connection
})
.ok_raw_or_log()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_connection_free(connection: *mut Connection) {
Box::from_raw(connection);
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_connection_connect(
context: *mut Context,
connection: *mut Connection,
) -> bool {
(*context)
.runtime
.block_on((*connection).connect())
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_connection_join_conference(
context: *mut Context,
connection: *mut Connection,
glib_main_context: *mut GMainContext,
config: *const ConferenceConfig,
) -> *mut JitsiConference {
let muc = match CStr::from_ptr((*config).muc).to_string_lossy().parse() {
Ok(jid) => jid,
Err(e) => {
eprintln!("lib-gst-meet: invalid MUC JID: {:?}", e);
return ptr::null_mut();
},
};
let focus = match CStr::from_ptr((*config).focus).to_string_lossy().parse() {
Ok(jid) => jid,
Err(e) => {
eprintln!("lib-gst-meet: invalid focus JID: {:?}", e);
return ptr::null_mut();
},
};
let region = if (*config).region.is_null() {
None
}
else {
Some(
CStr::from_ptr((*config).region)
.to_string_lossy()
.to_string(),
)
};
let config = JitsiConferenceConfig {
muc,
focus,
nick: CStr::from_ptr((*config).nick).to_string_lossy().to_string(),
region,
video_codec: CStr::from_ptr((*config).video_codec)
.to_string_lossy()
.to_string(),
extra_muc_features: vec![],
// TODO
start_bitrate: 800,
stereo: false,
#[cfg(feature = "log-rtp")]
log_rtp: false,
};
(*context)
.runtime
.block_on(JitsiConference::join(
(*connection).clone(),
from_glib_full(glib_main_context),
config,
))
.ok_raw_or_log()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_leave(
context: *mut Context,
conference: *mut JitsiConference,
) -> bool {
(*context)
.runtime
.block_on(Box::from_raw(conference).leave())
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_set_muted(
context: *mut Context,
conference: *mut JitsiConference,
media_type: MediaType,
muted: bool,
) -> bool {
(*context)
.runtime
.block_on((*conference).set_muted(media_type, muted))
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_pipeline(
context: *mut Context,
conference: *mut JitsiConference,
) -> *mut gstreamer::ffi::GstPipeline {
(*context)
.runtime
.block_on((*conference).pipeline())
.map(|pipeline| pipeline.to_glib_full())
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.unwrap_or(ptr::null_mut())
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_audio_sink_element(
context: *mut Context,
conference: *mut JitsiConference,
) -> *mut gstreamer::ffi::GstElement {
(*context)
.runtime
.block_on((*conference).audio_sink_element())
.map(|pipeline| pipeline.to_glib_full())
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.unwrap_or(ptr::null_mut())
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_video_sink_element(
context: *mut Context,
conference: *mut JitsiConference,
) -> *mut gstreamer::ffi::GstElement {
(*context)
.runtime
.block_on((*conference).video_sink_element())
.map(|pipeline| pipeline.to_glib_full())
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.unwrap_or(ptr::null_mut())
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_on_participant(
context: *mut Context,
conference: *mut JitsiConference,
f: unsafe extern "C" fn(
*mut JitsiConference,
Participant,
*mut c_void,
) -> *mut gstreamer::ffi::GstBin,
ctx: *mut c_void,
) {
let ctx = Arc::new(AtomicPtr::new(ctx));
(*context).runtime.block_on(
(*conference).on_participant(move |conference, participant| {
let ctx = ctx.clone();
Box::pin(async move {
let participant = Participant {
jid: participant
.jid
.map(
|jid| Ok::<_, anyhow::Error>(CString::new(jid.to_string())?.into_raw() as *const _),
)
.transpose()?
.unwrap_or_else(ptr::null),
muc_jid: CString::new(participant.muc_jid.to_string())?.into_raw() as *const _,
nick: participant
.nick
.map(|nick| Ok::<_, anyhow::Error>(CString::new(nick)?.into_raw() as *const _))
.transpose()?
.unwrap_or_else(ptr::null),
};
f(
Box::into_raw(Box::new(conference)),
participant,
ctx.load(Ordering::Relaxed),
);
Ok(())
})
}),
);
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_conference_set_pipeline_state(
context: *mut Context,
conference: *mut JitsiConference,
state: gstreamer::ffi::GstState,
) -> bool {
(*context)
.runtime
.block_on((*conference).set_pipeline_state(from_glib(state)))
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
.is_ok()
}