added initial draft of lib-gst-meet-c. added shell.nix for deps
This commit is contained in:
parent
2634c68ad6
commit
9af6db6c02
|
@ -667,6 +667,17 @@ dependencies = [
|
|||
"xmpp-parsers-gst-meet",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lib-gst-meet-c"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glib",
|
||||
"gstreamer",
|
||||
"lib-gst-meet",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.99"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
members = [
|
||||
"gst-meet",
|
||||
"lib-gst-meet",
|
||||
"lib-gst-meet-c",
|
||||
"nice-gst-meet",
|
||||
"nice-gst-meet-sys",
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "lib-gst-meet-c"
|
||||
description = "Connect GStreamer pipelines to Jitsi Meet conferences (C bindings)"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
authors = ["Jasper Hugo <jasper@avstack.io>"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1", default-features = false }
|
||||
glib = { version = "0.14", default-features = false }
|
||||
gstreamer = { version = "0.17", default-features = false }
|
||||
lib-gst-meet = { version = "0.1", path = "../lib-gst-meet", default-features = false }
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
|
||||
|
||||
[lib]
|
||||
name = "gstmeet"
|
||||
crate_type = ["staticlib", "cdylib"]
|
|
@ -0,0 +1,3 @@
|
|||
language = "C"
|
||||
include_guard = "gstmeet_h"
|
||||
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef gstmeet_h
|
||||
#define gstmeet_h
|
||||
|
||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct Context Context;
|
||||
|
||||
typedef struct ConferenceConfig {
|
||||
const char *muc;
|
||||
const char *focus;
|
||||
const char *nick;
|
||||
const char *region;
|
||||
const char *video_codec;
|
||||
} ConferenceConfig;
|
||||
|
||||
typedef struct Participant {
|
||||
const char *jid;
|
||||
const char *muc_jid;
|
||||
const char *nick;
|
||||
} Participant;
|
||||
|
||||
struct Context *gstmeet_init(void);
|
||||
|
||||
void gstmeet_deinit(struct Context *context);
|
||||
|
||||
JitsiConnection *gstmeet_connection_new(struct Context *context,
|
||||
const char *websocket_url,
|
||||
const char *xmpp_domain);
|
||||
|
||||
void gstmeet_connection_free(JitsiConnection *connection);
|
||||
|
||||
bool gstmeet_connection_connect(struct Context *context, JitsiConnection *connection);
|
||||
|
||||
JitsiConference *gstmeet_connection_join_conference(struct Context *context,
|
||||
JitsiConnection *connection,
|
||||
GMainContext *glib_main_context,
|
||||
const struct ConferenceConfig *config);
|
||||
|
||||
bool gstmeet_conference_connected(struct Context *context, JitsiConference *conference);
|
||||
|
||||
bool gstmeet_conference_leave(struct Context *context, JitsiConference *conference);
|
||||
|
||||
bool gstmeet_conference_set_muted(struct Context *context,
|
||||
JitsiConference *conference,
|
||||
MediaType media_type,
|
||||
bool muted);
|
||||
|
||||
GstPipeline *gstmeet_conference_pipeline(struct Context *context, JitsiConference *conference);
|
||||
|
||||
GstElement *gstmeet_conference_audio_sink_element(struct Context *context,
|
||||
JitsiConference *conference);
|
||||
|
||||
GstElement *gstmeet_conference_video_sink_element(struct Context *context,
|
||||
JitsiConference *conference);
|
||||
|
||||
void gstmeet_conference_on_participant(struct Context *context,
|
||||
JitsiConference *conference,
|
||||
GstBin *(*f)(struct Participant));
|
||||
|
||||
#endif /* gstmeet_h */
|
|
@ -0,0 +1,211 @@
|
|||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
os::raw::c_char,
|
||||
ptr,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use glib::{ffi::GMainContext, translate::{from_glib_full, ToGlibPtr}};
|
||||
pub use lib_gst_meet::{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_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_connected(context: *mut Context, conference: *mut JitsiConference) -> bool {
|
||||
(*context)
|
||||
.runtime
|
||||
.block_on((*conference).connected())
|
||||
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
#[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).connected())
|
||||
.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(Participant) -> *mut gstreamer::ffi::GstBin,
|
||||
) {
|
||||
(*context)
|
||||
.runtime
|
||||
.block_on((*conference).on_participant(move |participant| 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),
|
||||
};
|
||||
let maybe_bin = f(participant);
|
||||
if maybe_bin.is_null() {
|
||||
Ok(None)
|
||||
}
|
||||
else {
|
||||
Ok(Some(from_glib_full(maybe_bin)))
|
||||
}
|
||||
})));
|
||||
}
|
|
@ -6,6 +6,7 @@ pub struct Source {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub enum MediaType {
|
||||
Video,
|
||||
Audio,
|
||||
|
|
Loading…
Reference in New Issue