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

225 lines
6.5 KiB
Rust

use std::{
ffi::{CStr, CString, c_void},
os::raw::c_char,
ptr,
sync::{Arc, atomic::{AtomicPtr, Ordering}},
};
use anyhow::Result;
use glib::{ffi::GMainContext, translate::{from_glib, from_glib_full, ToGlibPtr}};
pub use lib_gst_meet::{init_tracing, JitsiConference, JitsiConnection, MediaType};
use lib_gst_meet::JitsiConferenceConfig;
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,
) -> *mut JitsiConnection {
let websocket_url = CStr::from_ptr(websocket_url);
let xmpp_domain = CStr::from_ptr(xmpp_domain);
(*context)
.runtime
.block_on(JitsiConnection::new(&websocket_url.to_string_lossy(), &xmpp_domain.to_string_lossy()))
.map(|(connection, background)| {
(*context).runtime.spawn(background);
connection
})
.ok_raw_or_log()
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_connection_free(connection: *mut JitsiConnection) {
Box::from_raw(connection);
}
#[no_mangle]
pub unsafe extern "C" fn gstmeet_connection_connect(context: *mut Context, connection: *mut JitsiConnection) -> 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 JitsiConnection,
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 config = JitsiConferenceConfig {
muc,
focus,
nick: CStr::from_ptr((*config).nick).to_string_lossy().to_string(),
region: CStr::from_ptr((*config).region).to_string_lossy().to_string(),
video_codec: CStr::from_ptr((*config).video_codec).to_string_lossy().to_string(),
};
(*context)
.runtime
.block_on((*connection).join_conference(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: CString::new(participant.jid.to_string())?.into_raw() as *const _,
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()
}