use std::ffi::c_char;
use std::net::Ipv4Addr;
use mx_remote::{
AmpDolbySettings, AudioEndpoint, DeviceV2ipDetails, DeviceV2ipSink, FirmwareVersion,
MultiviewerStatus, NetworkPortStatus, RcSettings, StreamKind, TopologyEntry, UtpCableStatus,
V2ipDecoderDetail, V2ipDeviceStats, V2ipRxStats, V2ipStreamSource, V2ipStreamSources,
V2ipTilingConfig, V2ipTxStats, VctStatus, MULTIVIEWER_INPUTS,
};
use crate::abi::{fail, guard, mxr_result_t, mxr_uid_t, put_str};
use crate::control::mxr_audio_format_t;
use crate::info::{copy_into, not_heard_from, null_out, MXR_NAME_LEN, MXR_VERSION_LEN};
use crate::remote::{mxr_remote_t, with, MXR_IP_STRING_LEN};
pub const MXR_MULTIVIEWER_INPUTS: usize = 4;
const _: () = assert!(MXR_MULTIVIEWER_INPUTS == MULTIVIEWER_INPUTS);
pub const MXR_UTP_PAIRS: usize = 4;
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum mxr_stream_kind_t {
MXR_STREAM_VIDEO = 0,
MXR_STREAM_AUDIO = 1,
MXR_STREAM_ANC = 2,
MXR_STREAM_ARC = 3,
}
impl From<StreamKind> for mxr_stream_kind_t {
fn from(kind: StreamKind) -> Self {
match kind {
StreamKind::Video => Self::MXR_STREAM_VIDEO,
StreamKind::Audio => Self::MXR_STREAM_AUDIO,
StreamKind::Anc => Self::MXR_STREAM_ANC,
StreamKind::Arc => Self::MXR_STREAM_ARC,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_stream_source_t {
pub kind: mxr_stream_kind_t,
pub ip: [c_char; MXR_IP_STRING_LEN],
pub port: u16,
pub valid: bool,
}
impl From<V2ipStreamSource> for mxr_stream_source_t {
fn from(s: V2ipStreamSource) -> Self {
let mut out = Self {
kind: s.kind.into(),
ip: [0; MXR_IP_STRING_LEN],
port: s.port,
valid: s.is_valid(),
};
put_str(&mut out.ip, &s.ip.to_string());
out
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_stream_sources_t {
pub uid: mxr_uid_t,
pub video: mxr_stream_source_t,
pub audio: mxr_stream_source_t,
pub anc: mxr_stream_source_t,
pub has_arc: bool,
pub arc: mxr_stream_source_t,
}
impl From<V2ipStreamSources> for mxr_stream_sources_t {
fn from(s: V2ipStreamSources) -> Self {
Self {
uid: s.uid.into(),
video: s.video.into(),
audio: s.audio.into(),
anc: s.anc.into(),
has_arc: s.arc.is_some(),
arc: s.arc.unwrap_or_default().into(),
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_details_t {
pub video: mxr_stream_source_t,
pub audio: mxr_stream_source_t,
pub anc: mxr_stream_source_t,
pub arc: mxr_stream_source_t,
pub tx_rate: i16,
pub dscp_video: i16,
pub dscp_audio: i16,
pub dscp_anc: i16,
pub scaling_mode: u16,
pub scaling_refresh: u16,
pub scaling_flags: u8,
}
pub const MXR_SCALING_FLAG_MODE_VALID: u8 = 1 << 0;
pub const MXR_SCALING_FLAG_OPTIONS_VALID: u8 = 1 << 1;
pub const MXR_SCALING_FLAG_AUTO_SCALING: u8 = 1 << 7;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_sink_t {
pub addresses: mxr_stream_sources_t,
pub has_audio_format: bool,
pub audio_format: mxr_audio_format_t,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_tx_stats_t {
pub video: u32,
pub audio: u32,
pub anc: u32,
pub stream_down: u32,
pub overflow: u32,
}
impl From<V2ipTxStats> for mxr_v2ip_tx_stats_t {
fn from(s: V2ipTxStats) -> Self {
Self {
video: s.video,
audio: s.audio,
anc: s.anc,
stream_down: s.stream_down,
overflow: s.overflow,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_rx_stats_t {
pub video_total: u32,
pub video_dropped: u32,
pub video_seq_errors: u32,
pub wdt_timeout: u32,
pub audio_total: u32,
pub audio_dropped: u32,
pub audio_seq_errors: u32,
pub anc_total: u32,
pub anc_dropped: u32,
pub anc_seq_errors: u32,
pub decoder_state: u8,
}
impl From<V2ipRxStats> for mxr_v2ip_rx_stats_t {
fn from(s: V2ipRxStats) -> Self {
Self {
video_total: s.video_total,
video_dropped: s.video_dropped,
video_seq_errors: s.video_seq_errors,
wdt_timeout: s.wdt_timeout,
audio_total: s.audio_total,
audio_dropped: s.audio_dropped,
audio_seq_errors: s.audio_seq_errors,
anc_total: s.anc_total,
anc_dropped: s.anc_dropped,
anc_seq_errors: s.anc_seq_errors,
decoder_state: s.decoder_state.to_wire(),
}
}
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum mxr_v2ip_decoder_detail_t {
MXR_V2IP_DECODER_ABSENT = 0,
MXR_V2IP_DECODER_NEVER_ANSWERED = 1,
MXR_V2IP_DECODER_ANSWERED = 2,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_decoder_t {
pub detail: mxr_v2ip_decoder_detail_t,
pub reason: u8,
pub blocking: bool,
pub width: u16,
pub height: u16,
pub format: u16,
pub updates: u16,
pub flags: u32,
pub blocked_count: u32,
}
impl From<V2ipDecoderDetail> for mxr_v2ip_decoder_t {
fn from(detail: V2ipDecoderDetail) -> Self {
let empty = Self {
detail: mxr_v2ip_decoder_detail_t::MXR_V2IP_DECODER_ABSENT,
reason: 0,
blocking: false,
width: 0,
height: 0,
format: 0,
updates: 0,
flags: 0,
blocked_count: 0,
};
match detail {
V2ipDecoderDetail::Absent => empty,
V2ipDecoderDetail::NeverAnswered => Self {
detail: mxr_v2ip_decoder_detail_t::MXR_V2IP_DECODER_NEVER_ANSWERED,
..empty
},
V2ipDecoderDetail::Answered(r) => Self {
detail: mxr_v2ip_decoder_detail_t::MXR_V2IP_DECODER_ANSWERED,
reason: r.reason.to_wire(),
blocking: r.blocking,
width: r.width,
height: r.height,
format: r.format.to_wire(),
updates: r.updates,
flags: r.flags,
blocked_count: r.blocked_count,
},
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_stats_t {
pub tx: mxr_v2ip_tx_stats_t,
pub tx_per_minute: mxr_v2ip_tx_stats_t,
pub rx: mxr_v2ip_rx_stats_t,
pub rx_per_minute: mxr_v2ip_rx_stats_t,
pub decoder: mxr_v2ip_decoder_t,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_tiling_config_t {
pub target: mxr_uid_t,
pub pos_x: u16,
pub pos_y: u16,
pub width: u16,
pub height: u16,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_multiviewer_status_t {
pub uid: mxr_uid_t,
pub mappings: [mxr_uid_t; MXR_MULTIVIEWER_INPUTS],
pub mcu_version: [c_char; MXR_NAME_LEN],
pub scaler_version: [c_char; MXR_NAME_LEN],
pub hw_view_mode: u8,
pub view_mode: u8,
pub pip_position: u8,
pub pip_size: u8,
pub output_mode: u8,
pub hdcp_mode: u8,
pub output_itc: u8,
pub edid_template: u8,
pub aspect_ratio: u8,
pub auto_switch: u8,
pub audio_source: u8,
pub has_audio_volume: bool,
pub audio_volume: u8,
pub audio_muted: u8,
pub video_sources: [u8; MXR_MULTIVIEWER_INPUTS],
pub remote_control: u8,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_audio_endpoint_t {
pub id: u8,
pub features: u32,
pub has_address: bool,
pub address: mxr_stream_source_t,
pub parent: i16,
pub child_count: usize,
pub has_inputs_available: bool,
pub inputs_available: u32,
pub has_inputs_routed: bool,
pub inputs_routed: u32,
pub linked_device: mxr_uid_t,
pub linked_endpoint: i16,
}
impl From<&AudioEndpoint> for mxr_audio_endpoint_t {
fn from(e: &AudioEndpoint) -> Self {
Self {
id: e.id,
features: e.features.bits(),
has_address: e.address.is_some(),
address: e.address.unwrap_or_default().into(),
parent: e.parent.map_or(-1, i16::from),
child_count: e.children.len(),
has_inputs_available: e.inputs_available.is_some(),
inputs_available: e.inputs_available.unwrap_or(0),
has_inputs_routed: e.inputs_routed.is_some(),
inputs_routed: e.inputs_routed.unwrap_or(0),
linked_device: e.linked_device.into(),
linked_endpoint: e.linked_endpoint.map_or(-1, i16::from),
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_cable_status_t {
pub polarity: bool,
pub pair: u8,
pub skew: u32,
pub length: u32,
}
impl From<UtpCableStatus> for mxr_cable_status_t {
fn from(c: UtpCableStatus) -> Self {
Self {
polarity: c.polarity,
pair: c.pair,
skew: c.skew,
length: c.length,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_network_port_t {
pub port: u16,
pub name: [c_char; MXR_NAME_LEN],
pub link_speed: u8,
pub link_full_duplex: bool,
pub ip: [c_char; MXR_IP_STRING_LEN],
pub querier: [c_char; MXR_IP_STRING_LEN],
pub has_mac_address: bool,
pub mac_address: [u8; 6],
pub has_errors: bool,
pub in_error: bool,
pub in_fcs_error: bool,
pub in_collision: bool,
pub out_deferred: bool,
pub out_excessive: bool,
pub polarity_error: bool,
pub skew_warning: bool,
pub length_warning: bool,
pub has_vct_status: bool,
pub vct_warning: [bool; MXR_UTP_PAIRS],
pub cable_status_count: usize,
pub cable_status: [mxr_cable_status_t; MXR_UTP_PAIRS],
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_topology_entry_t {
pub uid: mxr_uid_t,
pub mask: u32,
}
impl From<TopologyEntry> for mxr_topology_entry_t {
fn from(e: TopologyEntry) -> Self {
Self {
uid: e.uid.into(),
mask: e.mask,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_firmware_version_t {
pub firmware_type: u8,
pub timestamp: u32,
pub hash: u32,
pub version: [c_char; MXR_VERSION_LEN],
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_dolby_settings_t {
pub mode: u8,
pub pcm_upmix: bool,
pub dolby_detected: bool,
pub pcm_upmix_active: bool,
}
impl From<AmpDolbySettings> for mxr_dolby_settings_t {
fn from(s: AmpDolbySettings) -> Self {
Self {
mode: s.mode,
pcm_upmix: s.pcm_upmix,
dolby_detected: s.dolby_detected,
pcm_upmix_active: s.pcm_upmix_active,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_rc_settings_t {
pub target: mxr_uid_t,
pub rc_target: u8,
pub ip: [c_char; MXR_IP_STRING_LEN],
pub cec_enabled: bool,
pub cec_auto_on: bool,
pub forward_rc: bool,
pub forward_ir: bool,
pub rc_status: u8,
pub status_name: [c_char; MXR_NAME_LEN],
}
fn put_ip(dst: &mut [c_char], ip: Option<Ipv4Addr>) {
put_str(dst, &ip.map(|ip| ip.to_string()).unwrap_or_default());
}
unsafe fn fill<T>(
r: &mxr_remote_t,
uid: mxr_uid_t,
out: *mut T,
what: &str,
value: Option<T>,
) -> mxr_result_t {
if out.is_null() {
return null_out(what);
}
match value {
Some(value) => {
unsafe { *out = value };
mxr_result_t::MXR_OK
}
None => not_reported(r, uid, what),
}
}
fn not_reported(r: &mxr_remote_t, uid: mxr_uid_t, what: &str) -> mxr_result_t {
if r.remote.device(uid.into()).is_none() {
return not_heard_from(uid);
}
fail(
mxr_result_t::MXR_ERR_NOT_REPORTED,
&format!("the device has reported no {what}"),
)
}
#[no_mangle]
pub unsafe extern "C" fn mxr_v2ip_stats(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_v2ip_stats_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value = r
.remote
.v2ip_stats(uid.into())
.map(|s: V2ipDeviceStats| mxr_v2ip_stats_t {
tx: s.tx.into(),
tx_per_minute: s.tx_per_minute.into(),
rx: s.rx.into(),
rx_per_minute: s.rx_per_minute.into(),
decoder: s.decoder.into(),
});
unsafe { fill(r, uid, out, "V2IP statistics", value) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_v2ip_details(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_v2ip_details_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value = r
.remote
.v2ip_details(uid.into())
.map(|d: DeviceV2ipDetails| mxr_v2ip_details_t {
video: d.video.into(),
audio: d.audio.into(),
anc: d.anc.into(),
arc: d.arc.into(),
tx_rate: d.tx_rate.map_or(-1, i16::from),
dscp_video: d.dscp.video.map_or(-1, i16::from),
dscp_audio: d.dscp.audio.map_or(-1, i16::from),
dscp_anc: d.dscp.anc.map_or(-1, i16::from),
scaling_mode: d.scaling.mode.to_wire(),
scaling_refresh: d.scaling.refresh,
scaling_flags: d.scaling.flags,
});
unsafe { fill(r, uid, out, "V2IP encoder configuration", value) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_v2ip_sink(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_v2ip_sink_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value = r
.remote
.v2ip_sink(uid.into())
.map(|s: DeviceV2ipSink| mxr_v2ip_sink_t {
addresses: s.addresses.into(),
has_audio_format: s.audio_fmt.is_some(),
audio_format: {
let f = s.audio_fmt.unwrap_or_default();
mxr_audio_format_t {
sample_rate: f.sample_rate,
channels: f.channels,
}
},
});
unsafe { fill(r, uid, out, "V2IP sink route", value) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_v2ip_tiling(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_tiling_config_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value =
r.remote
.v2ip_tiling(uid.into())
.map(|t: V2ipTilingConfig| mxr_tiling_config_t {
target: t.target.into(),
pos_x: t.pos_x,
pos_y: t.pos_y,
width: t.width,
height: t.height,
});
unsafe { fill(r, uid, out, "window", value) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_multiviewer_status(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_multiviewer_status_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value = r.remote.multiviewer_status(uid.into()).map(multiviewer_of);
unsafe { fill(r, uid, out, "multiviewer status", value) }
})
}
fn multiviewer_of(s: MultiviewerStatus) -> mxr_multiviewer_status_t {
let mut out = mxr_multiviewer_status_t {
uid: s.uid.into(),
mappings: [mxr_uid_t::default(); MXR_MULTIVIEWER_INPUTS],
mcu_version: [0; MXR_NAME_LEN],
scaler_version: [0; MXR_NAME_LEN],
hw_view_mode: s.hw_view_mode,
view_mode: s.view_mode.to_wire(),
pip_position: s.pip_position.to_wire(),
pip_size: s.pip_size.to_wire(),
output_mode: s.output_mode.to_wire(),
hdcp_mode: s.hdcp_mode.to_wire(),
output_itc: s.output_itc.to_wire(),
edid_template: s.edid_template.to_wire(),
aspect_ratio: s.aspect_ratio.to_wire(),
auto_switch: s.auto_switch.to_wire(),
audio_source: s.audio_source.to_wire(),
has_audio_volume: s.audio_volume.is_some(),
audio_volume: s.audio_volume.unwrap_or(0),
audio_muted: s.audio_muted.to_wire(),
video_sources: [0; MXR_MULTIVIEWER_INPUTS],
remote_control: s.remote_control.to_wire(),
};
for (slot, uid) in out.mappings.iter_mut().zip(s.mappings) {
*slot = uid.into();
}
for (slot, source) in out.video_sources.iter_mut().zip(s.video_sources) {
*slot = source.to_wire();
}
put_str(&mut out.mcu_version, &s.mcu_version);
put_str(&mut out.scaler_version, &s.scaler_version);
out
}
#[no_mangle]
pub unsafe extern "C" fn mxr_dolby_settings(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_dolby_settings_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value = r
.remote
.dolby_settings(uid.into())
.map(mxr_dolby_settings_t::from);
unsafe { fill(r, uid, out, "Dolby settings", value) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_rc_settings(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_rc_settings_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let value = r.remote.rc_settings(uid.into()).map(rc_settings_of);
unsafe { fill(r, uid, out, "remote-control configuration", value) }
})
}
fn rc_settings_of(s: RcSettings) -> mxr_rc_settings_t {
let mut out = mxr_rc_settings_t {
target: s.target.into(),
rc_target: s.rc_target,
ip: [0; MXR_IP_STRING_LEN],
cec_enabled: s.cec_enabled,
cec_auto_on: s.cec_auto_on,
forward_rc: s.forward_rc,
forward_ir: s.forward_ir,
rc_status: s.rc_status,
status_name: [0; MXR_NAME_LEN],
};
put_ip(&mut out.ip, s.ip);
put_str(&mut out.status_name, &s.status_name);
out
}
#[no_mangle]
pub unsafe extern "C" fn mxr_v2ip_sources(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_stream_sources_t,
cap: usize,
) -> usize {
guard(0, || {
let Some(r) = (unsafe { remote.as_ref() }) else {
return no_handle();
};
let Some(sources) = r.remote.v2ip_sources(uid.into()) else {
not_reported(r, uid, "V2IP stream sources");
return 0;
};
unsafe { copy_into(&sources, out, cap) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_network_status(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_network_port_t,
cap: usize,
) -> usize {
guard(0, || {
let Some(r) = (unsafe { remote.as_ref() }) else {
return no_handle();
};
let ports: Vec<mxr_network_port_t> = r
.remote
.network_status(uid.into())
.iter()
.map(port_of)
.collect();
unsafe { copy_into(&ports, out, cap) }
})
}
fn port_of(p: &NetworkPortStatus) -> mxr_network_port_t {
let errors = p.errors.unwrap_or_default();
let mut out = mxr_network_port_t {
port: p.port,
name: [0; MXR_NAME_LEN],
link_speed: p.link_speed.to_wire(),
link_full_duplex: p.link_full_duplex,
ip: [0; MXR_IP_STRING_LEN],
querier: [0; MXR_IP_STRING_LEN],
has_mac_address: p.mac_address.is_some(),
mac_address: p.mac_address.unwrap_or_default().0,
has_errors: p.errors.is_some(),
in_error: errors.in_error,
in_fcs_error: errors.in_fcs_error,
in_collision: errors.in_collision,
out_deferred: errors.out_deferred,
out_excessive: errors.out_excessive,
polarity_error: errors.polarity_error,
skew_warning: errors.skew_warning,
length_warning: errors.length_warning,
has_vct_status: p.vct_status.is_some(),
vct_warning: [false; MXR_UTP_PAIRS],
cable_status_count: p.cable_status.len().min(MXR_UTP_PAIRS),
cable_status: [mxr_cable_status_t {
polarity: false,
pair: 0,
skew: 0,
length: 0,
}; MXR_UTP_PAIRS],
};
put_str(&mut out.name, &p.name);
put_ip(&mut out.ip, p.ip);
put_ip(&mut out.querier, p.querier);
if let Some(vct) = p.vct_status {
for (slot, status) in out.vct_warning.iter_mut().zip(vct) {
*slot = status == VctStatus::Warning;
}
}
for (slot, cable) in out.cable_status.iter_mut().zip(&p.cable_status) {
*slot = (*cable).into();
}
out
}
#[no_mangle]
pub unsafe extern "C" fn mxr_topology(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_topology_entry_t,
cap: usize,
) -> usize {
guard(0, || {
let Some(r) = (unsafe { remote.as_ref() }) else {
return no_handle();
};
let topology = r.remote.topology(uid.into());
unsafe { copy_into(&topology, out, cap) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_device_firmware(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_firmware_version_t,
cap: usize,
) -> usize {
guard(0, || {
let Some(r) = (unsafe { remote.as_ref() }) else {
return no_handle();
};
let versions: Vec<mxr_firmware_version_t> = r
.remote
.firmware(uid.into())
.iter()
.map(|(_, v)| firmware_of(v))
.collect();
unsafe { copy_into(&versions, out, cap) }
})
}
fn firmware_of(v: &FirmwareVersion) -> mxr_firmware_version_t {
let mut out = mxr_firmware_version_t {
firmware_type: v.firmware_type.to_wire(),
timestamp: v.timestamp,
hash: v.hash,
version: [0; MXR_VERSION_LEN],
};
put_str(&mut out.version, &v.version);
out
}
#[no_mangle]
pub unsafe extern "C" fn mxr_audio_endpoints(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
out: *mut mxr_audio_endpoint_t,
cap: usize,
) -> usize {
guard(0, || {
let Some(r) = (unsafe { remote.as_ref() }) else {
return no_handle();
};
let Some(endpoints) = r.remote.audio_endpoints(uid.into()) else {
not_reported(r, uid, "audio endpoints");
return 0;
};
let list: Vec<mxr_audio_endpoint_t> = endpoints.list().map(Into::into).collect();
unsafe { copy_into(&list, out, cap) }
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_audio_endpoint_children(
remote: *const mxr_remote_t,
uid: mxr_uid_t,
endpoint: u8,
out: *mut u8,
cap: usize,
) -> usize {
guard(0, || {
let Some(r) = (unsafe { remote.as_ref() }) else {
return no_handle();
};
let children = match r.remote.audio_endpoints(uid.into()) {
Some(endpoints) => match endpoints.get(endpoint) {
Some(e) => e.children.clone(),
None => {
fail(
mxr_result_t::MXR_ERR_NOT_FOUND,
&format!("the device has no audio endpoint {endpoint}"),
);
return 0;
}
},
None => {
not_reported(r, uid, "audio endpoints");
return 0;
}
};
unsafe { copy_into(&children, out, cap) }
})
}
fn no_handle() -> usize {
fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the client handle is null",
);
0
}