use std::net::Ipv4Addr;
use crate::types::{
AmpZoneSettings, V2ipAudioFormat, VideoWallOp, VideoWallWindow, VolumeMuteStatus,
VIDEO_WALL_CLEARED,
};
use super::enums::{EdidProfile, RcAction, RcKey};
use super::opcode::audio_sub;
use super::uid::DeviceUid;
pub(crate) fn append_fixed_str(dst: &mut Vec<u8>, value: &str, size: usize) {
let taken = value.as_bytes().get(..size).unwrap_or(value.as_bytes());
dst.extend_from_slice(taken);
dst.resize(dst.len() + (size - taken.len()), 0);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct StreamAddr {
pub(crate) ip: Ipv4Addr,
pub(crate) port: u16,
}
impl Default for StreamAddr {
fn default() -> Self {
Self {
ip: Ipv4Addr::UNSPECIFIED,
port: 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct V2ipStreams {
pub(crate) video: StreamAddr,
pub(crate) audio: StreamAddr,
pub(crate) anc: StreamAddr,
}
pub(crate) fn append_stream_addr(dst: &mut Vec<u8>, addr: StreamAddr) {
dst.extend_from_slice(&addr.ip.octets());
dst.extend_from_slice(&addr.port.to_le_bytes());
dst.extend_from_slice(&[0, 0]);
}
pub(crate) fn build_v2ip_manual_source_switch(
target: DeviceUid,
streams: V2ipStreams,
format: Option<V2ipAudioFormat>,
) -> Vec<u8> {
let mut p = Vec::with_capacity(48);
p.extend_from_slice(target.as_bytes());
append_stream_addr(&mut p, streams.video);
append_stream_addr(&mut p, streams.audio);
append_stream_addr(&mut p, streams.anc);
if let Some(f) = format {
p.extend_from_slice(&f.wire());
}
p
}
pub(crate) fn build_set_volume(target: DeviceUid, port: u16, volume: VolumeMuteStatus) -> Vec<u8> {
let mut p = Vec::with_capacity(24);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&port.to_le_bytes());
p.extend_from_slice(&volume.wire());
p.extend_from_slice(&[0, 0, 0]);
p
}
pub(crate) fn audio_cmd_header(sub_opcode: u16, target: DeviceUid) -> Vec<u8> {
let mut h = Vec::with_capacity(20);
h.extend_from_slice(&sub_opcode.to_le_bytes());
h.extend_from_slice(&[0, 0]);
h.extend_from_slice(target.as_bytes());
h
}
pub(crate) fn audio_param(endpoint_id: u16, param: u32) -> [u8; 8] {
let e = endpoint_id.to_le_bytes();
let v = param.to_le_bytes();
[e[0], e[1], 0, 0, v[0], v[1], v[2], v[3]]
}
pub(crate) fn mv_cmd_payload(target: DeviceUid, sub_opcode: u8, args: &[u8]) -> Vec<u8> {
let mut p = Vec::with_capacity(24 + args.len());
p.extend_from_slice(target.as_bytes());
p.push(sub_opcode);
p.extend_from_slice(&[0; 7]);
p.extend_from_slice(args);
p
}
pub(crate) fn build_amp_zone_settings(
target: DeviceUid,
zone: u16,
s: &AmpZoneSettings,
) -> Vec<u8> {
let mut p = Vec::with_capacity(56);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&zone.to_le_bytes());
p.extend_from_slice(&[s.gain_left, s.gain_right, s.volume_min, s.volume_max]);
p.extend_from_slice(&[0, 0]);
p.extend_from_slice(&s.delay_left.to_le_bytes());
p.extend_from_slice(&s.delay_right.to_le_bytes());
p.extend_from_slice(&[s.bass, s.treble, s.bridged, s.power_mode, s.power_level]);
p.extend_from_slice(&[0, 0, 0]);
p.extend_from_slice(&s.power_timeout.to_le_bytes());
p.extend_from_slice(&s.eq_left);
p.extend_from_slice(&s.eq_right);
p.extend_from_slice(&[0, 0]);
p
}
pub(crate) fn build_hello(
protocol: u16,
app_name: &str,
serial: &str,
version: &str,
features: u32,
) -> Vec<u8> {
let mut p = Vec::with_capacity(54);
p.extend_from_slice(&protocol.to_le_bytes());
append_fixed_str(&mut p, app_name, 16);
append_fixed_str(&mut p, serial, 16);
append_fixed_str(&mut p, version, 16);
p.extend_from_slice(&features.to_le_bytes());
p
}
pub(crate) fn build_set_bay_name(target: DeviceUid, port: u16, name: &str) -> Vec<u8> {
let mut p = Vec::with_capacity(34);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&port.to_le_bytes());
append_fixed_str(&mut p, name, 16);
p
}
pub(crate) fn build_target_only(target: DeviceUid) -> Vec<u8> {
target.as_bytes().to_vec()
}
pub(crate) fn build_edid_profile(target: DeviceUid, profile: EdidProfile) -> Vec<u8> {
let mut p = Vec::with_capacity(24);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&profile.to_wire().to_le_bytes());
p.extend_from_slice(&[0; 6]);
p
}
pub(crate) fn build_rc_action(target: DeviceUid, port: u16, action: RcAction) -> Vec<u8> {
let mut p = Vec::with_capacity(20);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&port.to_le_bytes());
p.extend_from_slice(&action.to_wire().to_le_bytes());
p
}
pub(crate) fn build_rc_key(target: DeviceUid, port: u16, key: RcKey) -> Vec<u8> {
let mut p = Vec::with_capacity(20);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&port.to_le_bytes());
p.extend_from_slice(&key.to_wire().to_le_bytes());
p
}
fn build_target_and_flag(target: DeviceUid, flag: bool) -> Vec<u8> {
let mut p = Vec::with_capacity(17);
p.extend_from_slice(target.as_bytes());
p.push(u8::from(flag));
p
}
pub(crate) fn build_video_wall(
target: DeviceUid,
window: VideoWallWindow,
op: VideoWallOp,
) -> Vec<u8> {
let mut p = Vec::with_capacity(32);
p.extend_from_slice(target.as_bytes());
let geometry = if op == VideoWallOp::REVERT {
VIDEO_WALL_CLEARED
} else {
window
};
for value in [
geometry.pos_x,
geometry.pos_y,
geometry.width,
geometry.height,
geometry.raster_w,
geometry.raster_h,
] {
p.extend_from_slice(&value.to_le_bytes());
}
p.push(op.to_wire());
p.extend_from_slice(&[0, 0, 0]);
p
}
pub(crate) fn build_stats_request(target: DeviceUid, subscribe: bool) -> Vec<u8> {
build_target_and_flag(target, subscribe)
}
pub(crate) fn build_edid_request(target: DeviceUid, output: bool) -> Vec<u8> {
build_target_and_flag(target, output)
}
pub(crate) fn build_v2ip_source_switch(
sink: DeviceUid,
video_ip: Ipv4Addr,
audio_ip: Ipv4Addr,
) -> Vec<u8> {
let mut p = Vec::with_capacity(24);
p.extend_from_slice(sink.as_bytes());
p.extend_from_slice(&video_ip.octets());
p.extend_from_slice(&audio_ip.octets());
p
}
pub(crate) fn build_bay_hide(target: DeviceUid, port: u16, hidden: bool) -> Vec<u8> {
let mut p = Vec::with_capacity(24);
p.extend_from_slice(target.as_bytes());
p.extend_from_slice(&port.to_le_bytes());
p.push(u8::from(hidden));
p.extend_from_slice(&[0; 5]);
p
}
pub(crate) fn build_audio_select_input(
sink: DeviceUid,
sink_endpoint: u16,
source: DeviceUid,
source_endpoint: u16,
) -> Vec<u8> {
let mut p = audio_cmd_header(audio_sub::SELECT_INPUT, sink);
p.extend_from_slice(sink.as_bytes());
p.extend_from_slice(source.as_bytes());
p.extend_from_slice(&sink_endpoint.to_le_bytes());
p.extend_from_slice(&source_endpoint.to_le_bytes());
p
}