use std::ffi::{CStr, CString};
use ntgcalls::{
ntg_audio_description_struct, ntg_call_info_struct, ntg_media_description_struct,
ntg_media_source_enum, ntg_video_description_struct,
};
use crate::{
enums::{
ConnectionKind, ConnectionState, MediaSegmentQuality, MediaSource, StreamDevice,
StreamStatus,
},
utils::IntoCString,
};
#[derive(Debug, Default, Clone)]
pub struct MediaDescription {
pub microphone: Option<AudioDescription>,
pub speaker: Option<AudioDescription>,
pub camera: Option<VideoDescription>,
pub screen: Option<VideoDescription>,
}
#[derive(Debug, Clone)]
pub struct AudioDescription {
pub media_source: MediaSource,
input: CString,
pub sample_rate: u32,
pub channel_count: u8,
pub keep_open: bool,
}
impl AudioDescription {
pub fn new<S: IntoCString>(
media_source: MediaSource,
input: S,
sample_rate: u32,
channel_count: u8,
keep_open: bool,
) -> Self {
Self {
media_source,
input: input.into_c_string(),
sample_rate,
channel_count,
keep_open,
}
}
pub(crate) fn to_ffi(&self) -> ntg_audio_description_struct {
ntg_audio_description_struct {
mediaSource: self.media_source as ntg_media_source_enum,
input: self.input.as_ptr() as *mut _,
sampleRate: self.sample_rate,
channelCount: self.channel_count,
keepOpen: self.keep_open,
}
}
}
#[derive(Debug, Clone)]
pub struct VideoDescription {
pub media_source: MediaSource,
input: CString,
pub width: i16,
pub height: i16,
pub fps: u8,
pub keep_open: bool,
}
impl VideoDescription {
pub fn new<S: IntoCString>(
media_source: MediaSource,
input: S,
width: i16,
height: i16,
fps: u8,
keep_open: bool,
) -> Self {
Self {
media_source,
input: input.into_c_string(),
width,
height,
fps,
keep_open,
}
}
pub(crate) fn to_ffi(&self) -> ntg_video_description_struct {
ntg_video_description_struct {
mediaSource: self.media_source as ntg_media_source_enum,
input: self.input.as_ptr() as *mut _,
width: self.width,
height: self.height,
fps: self.fps,
keepOpen: self.keep_open,
}
}
}
pub(crate) struct FfiMediaDesc {
pub microphone: Option<ntg_audio_description_struct>,
pub speaker: Option<ntg_audio_description_struct>,
pub camera: Option<ntg_video_description_struct>,
pub screen: Option<ntg_video_description_struct>,
}
impl FfiMediaDesc {
pub fn new(desc: &MediaDescription) -> Self {
Self {
microphone: desc.microphone.as_ref().map(AudioDescription::to_ffi),
speaker: desc.speaker.as_ref().map(AudioDescription::to_ffi),
camera: desc.camera.as_ref().map(VideoDescription::to_ffi),
screen: desc.screen.as_ref().map(VideoDescription::to_ffi),
}
}
pub fn as_ffi(&mut self) -> ntg_media_description_struct {
ntg_media_description_struct {
microphone: self
.microphone
.as_mut()
.map_or(std::ptr::null_mut(), |a| a as *mut _),
speaker: self
.speaker
.as_mut()
.map_or(std::ptr::null_mut(), |a| a as *mut _),
camera: self
.camera
.as_mut()
.map_or(std::ptr::null_mut(), |v| v as *mut _),
screen: self
.screen
.as_mut()
.map_or(std::ptr::null_mut(), |v| v as *mut _),
}
}
}
#[derive(Debug, Clone)]
pub struct CallInfo {
pub chat_id: i64,
pub capture: StreamStatus,
pub playback: StreamStatus,
}
impl From<ntg_call_info_struct> for CallInfo {
fn from(s: ntg_call_info_struct) -> Self {
Self {
chat_id: s.chatId,
capture: unsafe { std::mem::transmute::<i32, crate::enums::StreamStatus>(s.capture) },
playback: unsafe { std::mem::transmute::<i32, crate::enums::StreamStatus>(s.playback) },
}
}
}
#[derive(Debug, Clone)]
pub struct MediaState {
pub muted: bool,
pub video_paused: bool,
pub video_stopped: bool,
pub presentation_paused: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct NetworkInfo {
pub kind: ConnectionKind,
pub state: ConnectionState,
}
#[derive(Debug, Clone)]
pub struct RtcServer {
pub id: u64,
pub ipv4: String,
pub ipv6: String,
pub username: String,
pub password: String,
pub port: u16,
pub turn: bool,
pub stun: bool,
pub tcp: bool,
pub peer_tag: Vec<u8>,
}
pub(crate) struct FfiRtcServer {
pub ffi: ntgcalls::ntg_rtc_server_struct,
_ipv4: CString,
_ipv6: CString,
_username: CString,
_password: CString,
_peer_tag: Vec<u8>,
}
impl FfiRtcServer {
pub fn new(s: &RtcServer) -> Self {
let ipv4 = CString::new(s.ipv4.as_str()).unwrap_or_default();
let ipv6 = CString::new(s.ipv6.as_str()).unwrap_or_default();
let username = CString::new(s.username.as_str()).unwrap_or_default();
let password = CString::new(s.password.as_str()).unwrap_or_default();
let mut peer_tag = s.peer_tag.clone();
let ffi = ntgcalls::ntg_rtc_server_struct {
id: s.id,
ipv4: ipv4.as_ptr() as *mut _,
ipv6: ipv6.as_ptr() as *mut _,
username: username.as_ptr() as *mut _,
password: password.as_ptr() as *mut _,
port: s.port,
turn: s.turn,
stun: s.stun,
tcp: s.tcp,
peerTag: if peer_tag.is_empty() {
std::ptr::null_mut()
} else {
peer_tag.as_mut_ptr()
},
peerTagSize: peer_tag.len() as i32,
};
Self {
ffi,
_ipv4: ipv4,
_ipv6: ipv6,
_username: username,
_password: password,
_peer_tag: peer_tag,
}
}
}
#[derive(Debug, Clone)]
pub struct DhConfig {
pub g: i32,
pub p: Vec<u8>,
pub random: Vec<u8>,
}
pub(crate) struct FfiDhConfig {
pub ffi: ntgcalls::ntg_dh_config_struct,
_p: Vec<u8>,
_random: Vec<u8>,
}
impl FfiDhConfig {
pub fn new(c: &DhConfig) -> Self {
let p = c.p.clone();
let random = c.random.clone();
let ffi = ntgcalls::ntg_dh_config_struct {
g: c.g,
p: p.as_ptr(),
sizeP: p.len() as i32,
random: random.as_ptr(),
sizeRandom: random.len() as i32,
};
Self {
ffi,
_p: p,
_random: random,
}
}
}
#[derive(Debug, Clone)]
pub struct AuthParams {
pub g_a_or_b: Vec<u8>,
pub key_fingerprint: i64,
}
impl AuthParams {
pub(crate) fn from_ffi(raw: ntgcalls::ntg_auth_params_struct) -> Self {
let g_a_or_b = if raw.g_a_or_b.is_null() || raw.sizeGAB <= 0 {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(raw.g_a_or_b, raw.sizeGAB as usize).to_vec() }
};
Self {
g_a_or_b,
key_fingerprint: raw.key_fingerprint,
}
}
}
#[derive(Debug, Clone)]
pub struct SsrcGroup {
pub semantics: String,
pub ssrcs: Vec<u32>,
}
pub(crate) struct FfiSsrcGroup {
pub ffi: ntgcalls::ntg_ssrc_group_struct,
_semantics: CString,
_ssrcs: Vec<u32>,
}
impl FfiSsrcGroup {
pub fn new(g: &SsrcGroup) -> Self {
let sem = CString::new(g.semantics.as_str()).unwrap_or_default();
let mut ssrcs = g.ssrcs.clone();
let ffi = ntgcalls::ntg_ssrc_group_struct {
semantics: sem.as_ptr() as *mut _,
ssrcs: if ssrcs.is_empty() {
std::ptr::null_mut()
} else {
ssrcs.as_mut_ptr()
},
sizeSsrcs: ssrcs.len() as i32,
};
Self {
ffi,
_semantics: sem,
_ssrcs: ssrcs,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FrameData {
pub absolute_capture_timestamp_ms: i64,
pub width: u16,
pub height: u16,
pub rotation: u16,
}
impl FrameData {
pub(crate) fn to_ffi(self) -> ntgcalls::ntg_frame_data_struct {
ntgcalls::ntg_frame_data_struct {
absoluteCaptureTimestampMs: self.absolute_capture_timestamp_ms,
width: self.width,
height: self.height,
rotation: self.rotation,
}
}
}
#[derive(Debug, Clone)]
pub struct DeviceInfo {
pub name: String,
pub metadata: String,
}
impl DeviceInfo {
unsafe fn from_ffi(raw: &ntgcalls::ntg_device_info_struct) -> Self {
let name = if raw.name.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(raw.name) }
.to_string_lossy()
.into_owned()
};
let metadata = if raw.metadata.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(raw.metadata) }
.to_string_lossy()
.into_owned()
};
Self { name, metadata }
}
}
#[derive(Debug, Clone)]
pub struct MediaDevices {
pub microphones: Vec<DeviceInfo>,
pub speakers: Vec<DeviceInfo>,
pub cameras: Vec<DeviceInfo>,
pub screens: Vec<DeviceInfo>,
}
impl MediaDevices {
pub(crate) unsafe fn from_ffi(raw: ntgcalls::ntg_media_devices_struct) -> Self {
fn read_devices(ptr: *mut ntgcalls::ntg_device_info_struct, size: i32) -> Vec<DeviceInfo> {
if ptr.is_null() || size <= 0 {
return Vec::new();
}
unsafe {
std::slice::from_raw_parts(ptr, size as usize)
.iter()
.map(|d| DeviceInfo::from_ffi(d))
.collect()
}
}
Self {
microphones: read_devices(raw.microphone, raw.sizeMicrophone),
speakers: read_devices(raw.speaker, raw.sizeSpeaker),
cameras: read_devices(raw.camera, raw.sizeCamera),
screens: read_devices(raw.screen, raw.sizeScreen),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RemoteSource {
pub ssrc: u32,
pub state: StreamStatus,
pub device: StreamDevice,
}
#[derive(Debug, Clone, Copy)]
pub struct SegmentPartRequest {
pub segment_id: i64,
pub part_id: i32,
pub limit: i32,
pub timestamp: i64,
pub quality_update: bool,
pub channel_id: i32,
pub quality: MediaSegmentQuality,
}