added initial draft of lib-gst-meet-c. added shell.nix for deps

This commit is contained in:
Jasper Hugo 2021-08-14 20:06:39 +07:00
parent 2634c68ad6
commit 9af6db6c02
8 changed files with 320 additions and 1 deletions

11
Cargo.lock generated
View File

@ -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"

View File

@ -2,6 +2,7 @@
members = [
"gst-meet",
"lib-gst-meet",
"lib-gst-meet-c",
"nice-gst-meet",
"nice-gst-meet-sys",
]
]

18
lib-gst-meet-c/Cargo.toml Normal file
View File

@ -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"]

View File

@ -0,0 +1,3 @@
language = "C"
include_guard = "gstmeet_h"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"

65
lib-gst-meet-c/gstmeet.h Normal file
View File

@ -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 */

211
lib-gst-meet-c/src/lib.rs Normal file
View File

@ -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)))
}
})));
}

View File

@ -6,6 +6,7 @@ pub struct Source {
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[repr(C)]
pub enum MediaType {
Video,
Audio,

9
shell.nix Normal file
View File

@ -0,0 +1,9 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
name = "gst-meet";
buildInputs = with pkgs; [
glib
pkg-config
];
}