diff --git a/crates/teamtalk/src/types/entities/media_common.rs b/crates/teamtalk/src/types/entities/media_common.rs deleted file mode 100644 index ba79ddf..0000000 --- a/crates/teamtalk/src/types/entities/media_common.rs +++ /dev/null @@ -1,458 +0,0 @@ -use teamtalk_sys as ffi; - -/// Jitter control configuration. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, Default)] -pub struct JitterConfig { - pub fixed_delay_ms: i32, - pub use_adaptive: bool, - pub max_adaptive_delay_ms: i32, - pub active_adaptive_delay_ms: i32, -} - -impl From for JitterConfig { - fn from(c: ffi::JitterConfig) -> Self { - Self { - fixed_delay_ms: c.nFixedDelayMSec, - use_adaptive: c.bUseAdativeDejitter != 0, - max_adaptive_delay_ms: c.nMaxAdaptiveDelayMSec, - active_adaptive_delay_ms: c.nActiveAdaptiveDelayMSec, - } - } -} - -impl JitterConfig { - /// Creates a new jitter configuration. - #[must_use] - pub fn new( - fixed_delay_ms: i32, - use_adaptive: bool, - max_adaptive_delay_ms: i32, - active_adaptive_delay_ms: i32, - ) -> Self { - Self { - fixed_delay_ms, - use_adaptive, - max_adaptive_delay_ms, - active_adaptive_delay_ms, - } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::JitterConfig { - ffi::JitterConfig { - nFixedDelayMSec: self.fixed_delay_ms, - bUseAdativeDejitter: i32::from(self.use_adaptive), - nMaxAdaptiveDelayMSec: self.max_adaptive_delay_ms, - nActiveAdaptiveDelayMSec: self.active_adaptive_delay_ms, - } - } -} - -/// Video format configuration. -#[non_exhaustive] -#[derive(Debug, Clone, Copy)] -pub struct VideoFormat { - pub width: i32, - pub height: i32, - pub fps_numerator: i32, - pub fps_denominator: i32, - pub fourcc: ffi::FourCC, -} - -impl Default for VideoFormat { - fn default() -> Self { - Self { - width: 0, - height: 0, - fps_numerator: 0, - fps_denominator: 0, - fourcc: ffi::FourCC::FOURCC_NONE, - } - } -} - -impl From for VideoFormat { - fn from(f: ffi::VideoFormat) -> Self { - Self { - width: f.nWidth, - height: f.nHeight, - fps_numerator: f.nFPS_Numerator, - fps_denominator: f.nFPS_Denominator, - fourcc: f.picFourCC, - } - } -} - -impl VideoFormat { - /// Creates a new video format. - #[must_use] - pub fn new( - width: i32, - height: i32, - fps_numerator: i32, - fps_denominator: i32, - fourcc: ffi::FourCC, - ) -> Self { - Self { - width, - height, - fps_numerator, - fps_denominator, - fourcc, - } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::VideoFormat { - ffi::VideoFormat { - nWidth: self.width, - nHeight: self.height, - nFPS_Numerator: self.fps_numerator, - nFPS_Denominator: self.fps_denominator, - picFourCC: self.fourcc, - } - } -} - -/// Video codec configuration. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, Default)] -pub struct VideoCodec { - pub bitrate: i32, - pub deadline: u32, -} - -impl From for VideoCodec { - fn from(c: ffi::VideoCodec) -> Self { - Self { - bitrate: unsafe { - c.__bindgen_anon_1 - .webm_vp8 - .__bindgen_anon_1 - .nRcTargetBitrate - }, - deadline: unsafe { c.__bindgen_anon_1.webm_vp8.nEncodeDeadline }, - } - } -} - -impl VideoCodec { - /// Creates a new video codec configuration. - #[must_use] - pub fn new(bitrate: i32, deadline: u32) -> Self { - Self { bitrate, deadline } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::VideoCodec { - let mut raw = ffi::VideoCodec { - nCodec: ffi::Codec::WEBM_VP8_CODEC, - ..Default::default() - }; - raw.__bindgen_anon_1.webm_vp8.nEncodeDeadline = self.deadline; - raw.__bindgen_anon_1 - .webm_vp8 - .__bindgen_anon_1 - .nRcTargetBitrate = self.bitrate; - raw - } -} - -/// TLS encryption context settings. -#[non_exhaustive] -#[derive(Debug, Clone, Default)] -pub struct EncryptionContext { - pub cert_file: String, - pub key_file: String, - pub ca_file: String, - pub ca_dir: String, - pub verify_peer: bool, - pub verify_client_once: bool, - pub verify_depth: i32, -} - -impl EncryptionContext { - /// Creates a new encryption context. - pub fn new( - cert_file: impl Into, - key_file: impl Into, - ca_file: impl Into, - ca_dir: impl Into, - verify_peer: bool, - verify_client_once: bool, - verify_depth: i32, - ) -> Self { - Self { - cert_file: cert_file.into(), - key_file: key_file.into(), - ca_file: ca_file.into(), - ca_dir: ca_dir.into(), - verify_peer, - verify_client_once, - verify_depth, - } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::EncryptionContext { - let mut raw = ffi::EncryptionContext::default(); - let cert = crate::utils::ToTT::tt(&self.cert_file); - let key = crate::utils::ToTT::tt(&self.key_file); - let ca = crate::utils::ToTT::tt(&self.ca_file); - let cadir = crate::utils::ToTT::tt(&self.ca_dir); - unsafe { - let n_len = cert.len().min(511); - std::ptr::copy_nonoverlapping(cert.as_ptr(), raw.szCertificateFile.as_mut_ptr(), n_len); - let k_len = key.len().min(511); - std::ptr::copy_nonoverlapping(key.as_ptr(), raw.szPrivateKeyFile.as_mut_ptr(), k_len); - let ca_len = ca.len().min(511); - std::ptr::copy_nonoverlapping(ca.as_ptr(), raw.szCAFile.as_mut_ptr(), ca_len); - let cadir_len = cadir.len().min(511); - std::ptr::copy_nonoverlapping(cadir.as_ptr(), raw.szCADir.as_mut_ptr(), cadir_len); - } - raw.bVerifyPeer = i32::from(self.verify_peer); - raw.bVerifyClientOnce = i32::from(self.verify_client_once); - raw.nVerifyDepth = self.verify_depth; - raw - } -} - -/// Keep-alive configuration for client connections. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, Default)] -pub struct ClientKeepAlive { - pub lost_ms: i32, - pub tcp_interval_ms: i32, - pub udp_interval_ms: i32, - pub udp_rtx_ms: i32, - pub udp_connect_rtx_ms: i32, - pub udp_timeout_ms: i32, -} - -impl From for ClientKeepAlive { - fn from(c: ffi::ClientKeepAlive) -> Self { - Self { - lost_ms: c.nConnectionLostMSec, - tcp_interval_ms: c.nTcpKeepAliveIntervalMSec, - udp_interval_ms: c.nUdpKeepAliveIntervalMSec, - udp_rtx_ms: c.nUdpKeepAliveRTXMSec, - udp_connect_rtx_ms: c.nUdpConnectRTXMSec, - udp_timeout_ms: c.nUdpConnectTimeoutMSec, - } - } -} - -impl ClientKeepAlive { - /// Creates a new keep-alive configuration. - #[must_use] - pub fn new( - lost_ms: i32, - tcp_interval_ms: i32, - udp_interval_ms: i32, - udp_rtx_ms: i32, - udp_connect_rtx_ms: i32, - udp_timeout_ms: i32, - ) -> Self { - Self { - lost_ms, - tcp_interval_ms, - udp_interval_ms, - udp_rtx_ms, - udp_connect_rtx_ms, - udp_timeout_ms, - } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::ClientKeepAlive { - ffi::ClientKeepAlive { - nConnectionLostMSec: self.lost_ms, - nTcpKeepAliveIntervalMSec: self.tcp_interval_ms, - nUdpKeepAliveIntervalMSec: self.udp_interval_ms, - nUdpKeepAliveRTXMSec: self.udp_rtx_ms, - nUdpConnectRTXMSec: self.udp_connect_rtx_ms, - nUdpConnectTimeoutMSec: self.udp_timeout_ms, - } - } -} - -/// Abuse prevention configuration. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, Default)] -pub struct AbusePrevention { - pub commands_limit: i32, - pub commands_interval_ms: i32, -} - -impl From for AbusePrevention { - fn from(a: ffi::AbusePrevention) -> Self { - Self { - commands_limit: a.nCommandsLimit, - commands_interval_ms: a.nCommandsIntervalMSec, - } - } -} - -impl AbusePrevention { - /// Creates a new abuse prevention configuration. - #[must_use] - pub fn new(commands_limit: i32, commands_interval_ms: i32) -> Self { - Self { - commands_limit, - commands_interval_ms, - } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::AbusePrevention { - ffi::AbusePrevention { - nCommandsLimit: self.commands_limit, - nCommandsIntervalMSec: self.commands_interval_ms, - } - } -} - -/// Audio format description. -#[non_exhaustive] -#[derive(Debug, Clone, Copy)] -pub struct AudioFormat { - pub format: ffi::AudioFileFormat, - pub sample_rate: i32, - pub channels: i32, -} - -impl Default for AudioFormat { - fn default() -> Self { - Self { - format: ffi::AudioFileFormat::AFF_NONE, - sample_rate: 0, - channels: 0, - } - } -} - -impl From for AudioFormat { - fn from(f: ffi::AudioFormat) -> Self { - Self { - format: f.nAudioFmt, - sample_rate: f.nSampleRate, - channels: f.nChannels, - } - } -} - -impl AudioFormat { - /// Creates a new audio format. - #[must_use] - pub fn new(format: ffi::AudioFileFormat, sample_rate: i32, channels: i32) -> Self { - Self { - format, - sample_rate, - channels, - } - } - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::AudioFormat { - ffi::AudioFormat { - nAudioFmt: self.format, - nSampleRate: self.sample_rate, - nChannels: self.channels, - } - } -} - -/// Video frame metadata. -#[non_exhaustive] -pub struct VideoFrame { - pub width: i32, - pub height: i32, - pub stream_id: i32, - pub key_frame: bool, - pub buf: *mut std::ffi::c_void, - pub buf_len: i32, -} - -impl From for VideoFrame { - fn from(f: ffi::VideoFrame) -> Self { - Self { - width: f.nWidth, - height: f.nHeight, - stream_id: f.nStreamID, - key_frame: f.bKeyFrame != 0, - buf: f.frameBuffer, - buf_len: f.nFrameBufferSize, - } - } -} - -/// Audio input progress information. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, Default)] -pub struct AudioInputProgress { - pub stream_id: i32, - pub queue_ms: u32, - pub elapsed_ms: u32, -} - -impl From for AudioInputProgress { - fn from(p: ffi::AudioInputProgress) -> Self { - Self { - stream_id: p.nStreamID, - queue_ms: p.uQueueMSec, - elapsed_ms: p.uElapsedMSec, - } - } -} - -/// Desktop input packet. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, Default)] -pub struct DesktopInput { - pub mouse_pos_x: u16, - pub mouse_pos_y: u16, - pub key_code: u32, - pub key_state: ffi::DesktopKeyStates, -} - -impl From for DesktopInput { - fn from(input: ffi::DesktopInput) -> Self { - Self { - mouse_pos_x: input.uMousePosX, - mouse_pos_y: input.uMousePosY, - key_code: input.uKeyCode, - key_state: input.uKeyState, - } - } -} - -impl DesktopInput { - /// Converts to the raw `TeamTalk` struct. - #[must_use] - pub fn to_ffi(&self) -> ffi::DesktopInput { - ffi::DesktopInput { - uMousePosX: self.mouse_pos_x, - uMousePosY: self.mouse_pos_y, - uKeyCode: self.key_code, - uKeyState: self.key_state, - } - } -} - -/// SDK error message payload. -#[non_exhaustive] -#[derive(Debug, Clone, Default)] -pub struct ErrorMessage { - pub code: i32, - pub message: String, -} - -impl From for ErrorMessage { - fn from(e: ffi::ClientErrorMsg) -> Self { - Self { - code: e.nErrorNo, - message: crate::utils::strings::to_string(&e.szErrorMsg), - } - } -} diff --git a/crates/teamtalk/src/types/entities/media_common/audio.rs b/crates/teamtalk/src/types/entities/media_common/audio.rs new file mode 100644 index 0000000..e1681e8 --- /dev/null +++ b/crates/teamtalk/src/types/entities/media_common/audio.rs @@ -0,0 +1,131 @@ +//! Audio-related configuration, format, and input-progress types. + +use teamtalk_sys as ffi; + +/// Jitter control configuration. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, Default)] +pub struct JitterConfig { + /// Fixed jitter delay in milliseconds. + pub fixed_delay_ms: i32, + /// Whether the adaptive dejitter algorithm is enabled. + pub use_adaptive: bool, + /// Maximum adaptive delay in milliseconds. + pub max_adaptive_delay_ms: i32, + /// Currently active adaptive delay in milliseconds. + pub active_adaptive_delay_ms: i32, +} + +impl From for JitterConfig { + fn from(c: ffi::JitterConfig) -> Self { + Self { + fixed_delay_ms: c.nFixedDelayMSec, + use_adaptive: c.bUseAdativeDejitter != 0, + max_adaptive_delay_ms: c.nMaxAdaptiveDelayMSec, + active_adaptive_delay_ms: c.nActiveAdaptiveDelayMSec, + } + } +} + +impl JitterConfig { + /// Creates a new jitter configuration. + #[must_use] + pub fn new( + fixed_delay_ms: i32, + use_adaptive: bool, + max_adaptive_delay_ms: i32, + active_adaptive_delay_ms: i32, + ) -> Self { + Self { + fixed_delay_ms, + use_adaptive, + max_adaptive_delay_ms, + active_adaptive_delay_ms, + } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::JitterConfig { + ffi::JitterConfig { + nFixedDelayMSec: self.fixed_delay_ms, + bUseAdativeDejitter: i32::from(self.use_adaptive), + nMaxAdaptiveDelayMSec: self.max_adaptive_delay_ms, + nActiveAdaptiveDelayMSec: self.active_adaptive_delay_ms, + } + } +} + +/// Audio format description. +#[non_exhaustive] +#[derive(Debug, Clone, Copy)] +pub struct AudioFormat { + /// Audio file format container. + pub format: ffi::AudioFileFormat, + /// Sample rate in Hz. + pub sample_rate: i32, + /// Number of channels. + pub channels: i32, +} + +impl Default for AudioFormat { + fn default() -> Self { + Self { + format: ffi::AudioFileFormat::AFF_NONE, + sample_rate: 0, + channels: 0, + } + } +} + +impl From for AudioFormat { + fn from(f: ffi::AudioFormat) -> Self { + Self { + format: f.nAudioFmt, + sample_rate: f.nSampleRate, + channels: f.nChannels, + } + } +} + +impl AudioFormat { + /// Creates a new audio format. + #[must_use] + pub fn new(format: ffi::AudioFileFormat, sample_rate: i32, channels: i32) -> Self { + Self { + format, + sample_rate, + channels, + } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::AudioFormat { + ffi::AudioFormat { + nAudioFmt: self.format, + nSampleRate: self.sample_rate, + nChannels: self.channels, + } + } +} + +/// Audio input progress information. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, Default)] +pub struct AudioInputProgress { + /// Stream id associated with the progress report. + pub stream_id: i32, + /// Queued audio duration in milliseconds. + pub queue_ms: u32, + /// Elapsed audio duration in milliseconds. + pub elapsed_ms: u32, +} + +impl From for AudioInputProgress { + fn from(p: ffi::AudioInputProgress) -> Self { + Self { + stream_id: p.nStreamID, + queue_ms: p.uQueueMSec, + elapsed_ms: p.uElapsedMSec, + } + } +} diff --git a/crates/teamtalk/src/types/entities/media_common/desktop.rs b/crates/teamtalk/src/types/entities/media_common/desktop.rs new file mode 100644 index 0000000..400985c --- /dev/null +++ b/crates/teamtalk/src/types/entities/media_common/desktop.rs @@ -0,0 +1,41 @@ +//! Desktop sharing input packet types. + +use teamtalk_sys as ffi; + +/// Desktop input packet. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, Default)] +pub struct DesktopInput { + /// Mouse x-position in pixels. + pub mouse_pos_x: u16, + /// Mouse y-position in pixels. + pub mouse_pos_y: u16, + /// Virtual key code. + pub key_code: u32, + /// Key state flags. + pub key_state: ffi::DesktopKeyStates, +} + +impl From for DesktopInput { + fn from(input: ffi::DesktopInput) -> Self { + Self { + mouse_pos_x: input.uMousePosX, + mouse_pos_y: input.uMousePosY, + key_code: input.uKeyCode, + key_state: input.uKeyState, + } + } +} + +impl DesktopInput { + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::DesktopInput { + ffi::DesktopInput { + uMousePosX: self.mouse_pos_x, + uMousePosY: self.mouse_pos_y, + uKeyCode: self.key_code, + uKeyState: self.key_state, + } + } +} diff --git a/crates/teamtalk/src/types/entities/media_common/error.rs b/crates/teamtalk/src/types/entities/media_common/error.rs new file mode 100644 index 0000000..bc34346 --- /dev/null +++ b/crates/teamtalk/src/types/entities/media_common/error.rs @@ -0,0 +1,22 @@ +//! SDK error message payload. + +use teamtalk_sys as ffi; + +/// SDK error message payload. +#[non_exhaustive] +#[derive(Debug, Clone, Default)] +pub struct ErrorMessage { + /// Numeric SDK error code. + pub code: i32, + /// Human-readable SDK error message. + pub message: String, +} + +impl From for ErrorMessage { + fn from(e: ffi::ClientErrorMsg) -> Self { + Self { + code: e.nErrorNo, + message: crate::utils::strings::to_string(&e.szErrorMsg), + } + } +} diff --git a/crates/teamtalk/src/types/entities/media_common/mod.rs b/crates/teamtalk/src/types/entities/media_common/mod.rs new file mode 100644 index 0000000..9fa169e --- /dev/null +++ b/crates/teamtalk/src/types/entities/media_common/mod.rs @@ -0,0 +1,33 @@ +//! Low-level media/transport configuration and payload types split by +//! domain. +//! +//! The module was previously a single 458-line file bundling jitter +//! control, audio/video formats, TLS encryption context, keep-alive, +//! abuse prevention, desktop input, and SDK error payloads in one +//! place. It is now directory-first and split by domain per the +//! AGENTS.md guideline: +//! +//! * `audio.rs` - [`JitterConfig`], [`AudioFormat`], +//! [`AudioInputProgress`]. +//! * `video.rs` - [`VideoFormat`], [`VideoCodec`], [`VideoFrame`]. +//! * `transport.rs` - [`EncryptionContext`], [`ClientKeepAlive`], +//! [`AbusePrevention`]. +//! * `desktop.rs` - [`DesktopInput`]. +//! * `error.rs` - [`ErrorMessage`]. +//! +//! All items are re-exported so existing call sites that use the +//! glob re-export from `types::entities` (see +//! `types/entities/mod.rs`, `pub use media_common::*;`) keep +//! working unchanged. + +mod audio; +mod desktop; +mod error; +mod transport; +mod video; + +pub use audio::{AudioFormat, AudioInputProgress, JitterConfig}; +pub use desktop::DesktopInput; +pub use error::ErrorMessage; +pub use transport::{AbusePrevention, ClientKeepAlive, EncryptionContext}; +pub use video::{VideoCodec, VideoFormat, VideoFrame}; diff --git a/crates/teamtalk/src/types/entities/media_common/transport.rs b/crates/teamtalk/src/types/entities/media_common/transport.rs new file mode 100644 index 0000000..f68c2cf --- /dev/null +++ b/crates/teamtalk/src/types/entities/media_common/transport.rs @@ -0,0 +1,173 @@ +//! Transport-layer and security configuration types (TLS, keep-alive, +//! abuse prevention). + +use teamtalk_sys as ffi; + +/// TLS encryption context settings. +#[non_exhaustive] +#[derive(Debug, Clone, Default)] +pub struct EncryptionContext { + /// Path to the certificate file. + pub cert_file: String, + /// Path to the private key file. + pub key_file: String, + /// Path to the CA certificate bundle file. + pub ca_file: String, + /// Path to the CA certificate directory. + pub ca_dir: String, + /// Whether to verify the peer certificate. + pub verify_peer: bool, + /// Whether to verify the client certificate once per session. + pub verify_client_once: bool, + /// Maximum certificate chain depth to verify. + pub verify_depth: i32, +} + +impl EncryptionContext { + /// Creates a new encryption context. + pub fn new( + cert_file: impl Into, + key_file: impl Into, + ca_file: impl Into, + ca_dir: impl Into, + verify_peer: bool, + verify_client_once: bool, + verify_depth: i32, + ) -> Self { + Self { + cert_file: cert_file.into(), + key_file: key_file.into(), + ca_file: ca_file.into(), + ca_dir: ca_dir.into(), + verify_peer, + verify_client_once, + verify_depth, + } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::EncryptionContext { + let mut raw = ffi::EncryptionContext::default(); + let cert = crate::utils::ToTT::tt(&self.cert_file); + let key = crate::utils::ToTT::tt(&self.key_file); + let ca = crate::utils::ToTT::tt(&self.ca_file); + let cadir = crate::utils::ToTT::tt(&self.ca_dir); + unsafe { + let n_len = cert.len().min(511); + std::ptr::copy_nonoverlapping(cert.as_ptr(), raw.szCertificateFile.as_mut_ptr(), n_len); + let k_len = key.len().min(511); + std::ptr::copy_nonoverlapping(key.as_ptr(), raw.szPrivateKeyFile.as_mut_ptr(), k_len); + let ca_len = ca.len().min(511); + std::ptr::copy_nonoverlapping(ca.as_ptr(), raw.szCAFile.as_mut_ptr(), ca_len); + let cadir_len = cadir.len().min(511); + std::ptr::copy_nonoverlapping(cadir.as_ptr(), raw.szCADir.as_mut_ptr(), cadir_len); + } + raw.bVerifyPeer = i32::from(self.verify_peer); + raw.bVerifyClientOnce = i32::from(self.verify_client_once); + raw.nVerifyDepth = self.verify_depth; + raw + } +} + +/// Keep-alive configuration for client connections. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, Default)] +pub struct ClientKeepAlive { + /// Connection-lost threshold in milliseconds. + pub lost_ms: i32, + /// TCP keep-alive interval in milliseconds. + pub tcp_interval_ms: i32, + /// UDP keep-alive interval in milliseconds. + pub udp_interval_ms: i32, + /// UDP keep-alive retransmit interval in milliseconds. + pub udp_rtx_ms: i32, + /// UDP connect retransmit interval in milliseconds. + pub udp_connect_rtx_ms: i32, + /// UDP connect timeout in milliseconds. + pub udp_timeout_ms: i32, +} + +impl From for ClientKeepAlive { + fn from(c: ffi::ClientKeepAlive) -> Self { + Self { + lost_ms: c.nConnectionLostMSec, + tcp_interval_ms: c.nTcpKeepAliveIntervalMSec, + udp_interval_ms: c.nUdpKeepAliveIntervalMSec, + udp_rtx_ms: c.nUdpKeepAliveRTXMSec, + udp_connect_rtx_ms: c.nUdpConnectRTXMSec, + udp_timeout_ms: c.nUdpConnectTimeoutMSec, + } + } +} + +impl ClientKeepAlive { + /// Creates a new keep-alive configuration. + #[must_use] + pub fn new( + lost_ms: i32, + tcp_interval_ms: i32, + udp_interval_ms: i32, + udp_rtx_ms: i32, + udp_connect_rtx_ms: i32, + udp_timeout_ms: i32, + ) -> Self { + Self { + lost_ms, + tcp_interval_ms, + udp_interval_ms, + udp_rtx_ms, + udp_connect_rtx_ms, + udp_timeout_ms, + } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::ClientKeepAlive { + ffi::ClientKeepAlive { + nConnectionLostMSec: self.lost_ms, + nTcpKeepAliveIntervalMSec: self.tcp_interval_ms, + nUdpKeepAliveIntervalMSec: self.udp_interval_ms, + nUdpKeepAliveRTXMSec: self.udp_rtx_ms, + nUdpConnectRTXMSec: self.udp_connect_rtx_ms, + nUdpConnectTimeoutMSec: self.udp_timeout_ms, + } + } +} + +/// Abuse prevention configuration. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, Default)] +pub struct AbusePrevention { + /// Maximum number of commands permitted within the interval. + pub commands_limit: i32, + /// Sliding window for the command rate limit, in milliseconds. + pub commands_interval_ms: i32, +} + +impl From for AbusePrevention { + fn from(a: ffi::AbusePrevention) -> Self { + Self { + commands_limit: a.nCommandsLimit, + commands_interval_ms: a.nCommandsIntervalMSec, + } + } +} + +impl AbusePrevention { + /// Creates a new abuse prevention configuration. + #[must_use] + pub fn new(commands_limit: i32, commands_interval_ms: i32) -> Self { + Self { + commands_limit, + commands_interval_ms, + } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::AbusePrevention { + ffi::AbusePrevention { + nCommandsLimit: self.commands_limit, + nCommandsIntervalMSec: self.commands_interval_ms, + } + } +} diff --git a/crates/teamtalk/src/types/entities/media_common/video.rs b/crates/teamtalk/src/types/entities/media_common/video.rs new file mode 100644 index 0000000..c2a1e4d --- /dev/null +++ b/crates/teamtalk/src/types/entities/media_common/video.rs @@ -0,0 +1,150 @@ +//! Video-related configuration and frame metadata types. + +use teamtalk_sys as ffi; + +/// Video format configuration. +#[non_exhaustive] +#[derive(Debug, Clone, Copy)] +pub struct VideoFormat { + /// Frame width in pixels. + pub width: i32, + /// Frame height in pixels. + pub height: i32, + /// Frame rate numerator. + pub fps_numerator: i32, + /// Frame rate denominator. + pub fps_denominator: i32, + /// Picture format FourCC identifier. + pub fourcc: ffi::FourCC, +} + +impl Default for VideoFormat { + fn default() -> Self { + Self { + width: 0, + height: 0, + fps_numerator: 0, + fps_denominator: 0, + fourcc: ffi::FourCC::FOURCC_NONE, + } + } +} + +impl From for VideoFormat { + fn from(f: ffi::VideoFormat) -> Self { + Self { + width: f.nWidth, + height: f.nHeight, + fps_numerator: f.nFPS_Numerator, + fps_denominator: f.nFPS_Denominator, + fourcc: f.picFourCC, + } + } +} + +impl VideoFormat { + /// Creates a new video format. + #[must_use] + pub fn new( + width: i32, + height: i32, + fps_numerator: i32, + fps_denominator: i32, + fourcc: ffi::FourCC, + ) -> Self { + Self { + width, + height, + fps_numerator, + fps_denominator, + fourcc, + } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::VideoFormat { + ffi::VideoFormat { + nWidth: self.width, + nHeight: self.height, + nFPS_Numerator: self.fps_numerator, + nFPS_Denominator: self.fps_denominator, + picFourCC: self.fourcc, + } + } +} + +/// Video codec configuration. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, Default)] +pub struct VideoCodec { + /// Target bitrate for the WebM VP8 encoder. + pub bitrate: i32, + /// Encode deadline hint for the WebM VP8 encoder. + pub deadline: u32, +} + +impl From for VideoCodec { + fn from(c: ffi::VideoCodec) -> Self { + Self { + bitrate: unsafe { + c.__bindgen_anon_1 + .webm_vp8 + .__bindgen_anon_1 + .nRcTargetBitrate + }, + deadline: unsafe { c.__bindgen_anon_1.webm_vp8.nEncodeDeadline }, + } + } +} + +impl VideoCodec { + /// Creates a new video codec configuration. + #[must_use] + pub fn new(bitrate: i32, deadline: u32) -> Self { + Self { bitrate, deadline } + } + /// Converts to the raw `TeamTalk` struct. + #[must_use] + pub fn to_ffi(&self) -> ffi::VideoCodec { + let mut raw = ffi::VideoCodec { + nCodec: ffi::Codec::WEBM_VP8_CODEC, + ..Default::default() + }; + raw.__bindgen_anon_1.webm_vp8.nEncodeDeadline = self.deadline; + raw.__bindgen_anon_1 + .webm_vp8 + .__bindgen_anon_1 + .nRcTargetBitrate = self.bitrate; + raw + } +} + +/// Video frame metadata. +#[non_exhaustive] +pub struct VideoFrame { + /// Frame width in pixels. + pub width: i32, + /// Frame height in pixels. + pub height: i32, + /// Stream id associated with the frame. + pub stream_id: i32, + /// Whether the frame is a keyframe. + pub key_frame: bool, + /// Pointer to the decoded frame buffer. + pub buf: *mut std::ffi::c_void, + /// Length of the decoded frame buffer in bytes. + pub buf_len: i32, +} + +impl From for VideoFrame { + fn from(f: ffi::VideoFrame) -> Self { + Self { + width: f.nWidth, + height: f.nHeight, + stream_id: f.nStreamID, + key_frame: f.bKeyFrame != 0, + buf: f.frameBuffer, + buf_len: f.nFrameBufferSize, + } + } +}