Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions necromancer/examples/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ async fn main() -> Result<()> {

info!("ME {me}: Program {pgm:?}, Preview {pre:?}");
}
if state.topology.auxs > 0 {
for aux in 0..state.topology.auxs {
let src = state.get_aux_source(aux).unwrap_or_default();
info!("AUX {aux}: {src:?}");
}
}
info!(
"Supported DVE transition styles (scale={:?}, rotate={:?}): {:?}",
state.dve_can_scale_up, state.dve_can_rotate, state.dve_supported_transition_styles,
Expand All @@ -67,6 +73,10 @@ async fn main() -> Result<()> {
info!("Preview sources: {:?}", state.get_preview_sources());
}

if update.contains(StateUpdate::AUX_SOURCE) {
info!("AUX sources: {:?}", state.get_aux_sources());
}

if update.contains(StateUpdate::FADE_TO_BLACK_RATE) {
info!("Fade to black rates: {:?}", state.get_fade_to_black_rates());
}
Expand Down
18 changes: 17 additions & 1 deletion necromancer/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
protocol::{
atom::{
Atom, Auto, Cut, CutToBlack, FadeToBlackAuto, FileTransferChunkParams, FileType,
FinishFileDownload, MediaPlayerSourceID, MediaPoolLock, Payload,
FinishFileDownload, MediaPlayerSourceID, MediaPoolLock, Payload, ChangeAuxSource,
SetColourGeneratorParams, SetMediaPlayerSource, SetPreviewInput, SetProgramInput,
SetupFileDownload, SetupFileUpload, TimecodeRequest, TransferChunk, CAPTURE_STILL,
CLEAR_MEDIA_POOL, CLEAR_STARTUP_SETTINGS, RESTORE_STARTUP_SETTINGS,
Expand Down Expand Up @@ -354,6 +354,22 @@ impl AtemController {
self.send(vec![cmd]).await
}

/// Sets the current source for a given AUX bus.
pub async fn set_aux_source(&self, aux_bus: u8, video_source: VideoSource) -> Result<(), Error> {
let state = self.get_state().await;
if aux_bus >= state.topology.auxs {
error!(
"aux bus #{aux_bus} does not exist, switcher has {} aux bus(es)",
state.topology.auxs
);
return Err(Error::ParameterOutOfRange);
}
drop(state);

let cmd = Atom::new(ChangeAuxSource { aux_bus, video_source });
self.send(vec![cmd]).await
}

pub async fn cut_black(&self, me: u8, black: bool) -> Result<(), Error> {
let cmd = Atom::new(CutToBlack { me, black });
self.send(vec![cmd]).await
Expand Down
38 changes: 38 additions & 0 deletions necromancer/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ bitflags! {
const FAIRLIGHT_INPUT_SOURCE_PROPS = 1 << 19;
const FAIRLIGHT_FREQUENCY_RANGES = 1 << 20;
const DVE_CAPABILITIES = 1 << 21;
const AUX_SOURCE = 1 << 22;

const PREVIEW_OR_PROGRAM_SOURCE = Self::PREVIEW_SOURCE.bits() | Self::PROGRAM_SOURCE.bits();
const UNSUPPORTED_COMMAND = 1 << 31;
Expand Down Expand Up @@ -75,6 +76,8 @@ pub struct AtemState {
me_capabilities: [MixEffectBlockCapabilities; MAX_MES],
program_source: [VideoSource; MAX_MES],
preview_source: [VideoSource; MAX_MES],
/// Current source for each AUX bus.
pub aux_sources: Vec<VideoSource>,
/// Transition position for each ME.
pub transition_position: HashMap<u8, TransitionPosition>,
/// Current tally state for each source.
Expand Down Expand Up @@ -197,6 +200,10 @@ impl AtemState {
self.topology.media_players,
);
}
if self.aux_sources.len() != usize::from(self.topology.auxs) {
self.aux_sources
.resize(usize::from(self.topology.auxs), VideoSource::default());
}
updated_fields |= StateUpdate::TOPOLOGY;
}

Expand Down Expand Up @@ -314,6 +321,20 @@ impl AtemState {
updated_fields |= StateUpdate::MEDIA_PLAYER_SOURCE;
}

Payload::AuxSource(aux) => {
debug!(?aux, "updated aux source");
let idx = usize::from(aux.aux_bus);
if idx >= usize::from(self.topology.auxs) {
continue;
}
if self.aux_sources.len() <= idx {
self.aux_sources
.resize(usize::from(self.topology.auxs), VideoSource::default());
}
self.aux_sources[idx] = aux.video_source;
updated_fields |= StateUpdate::AUX_SOURCE;
}

Payload::ColourGeneratorParams(colv) => {
debug!(?colv, "updated colour generator params");
if colv.id >= MAX_COLOUR_GENERATORS {
Expand Down Expand Up @@ -423,6 +444,22 @@ impl AtemState {
&self.preview_source[0..self.topology.mes as usize]
}

/// Get the current source for a given AUX bus.
///
/// Returns `None` if the bus index is invalid for this switcher's topology.
pub fn get_aux_source(&self, bus: u8) -> Option<VideoSource> {
if bus >= self.topology.auxs {
return None;
}
let idx = usize::from(bus);
self.aux_sources.get(idx).copied()
}

/// Get the current sources for all AUX buses.
pub fn get_aux_sources(&self) -> &[VideoSource] {
&self.aux_sources
}

pub const fn get_fade_to_black_status(&self, me: u8) -> Option<FadeToBlackStatus> {
if me >= self.topology.mes {
return None;
Expand Down Expand Up @@ -500,6 +537,7 @@ impl std::fmt::Debug for AtemState {
"preview_source",
&&self.preview_source[..MAX_MES.min(self.topology.mes as usize)],
)
.field("aux_sources", &self.aux_sources)
.field("transition_position", &self.transition_position)
.field("tally_by_source", &self.tally_by_source)
.field("supported_video_modes", &self.supported_video_modes)
Expand Down
50 changes: 42 additions & 8 deletions necromancer_protocol/src/atom/aux_.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,42 @@
//! # Aux; 0/2 atoms
//!
//! ## Unimplemented atoms (2)
//!
//! FourCC | Atom name | Length
//! ------ | --------- | ------
//! `AuxS` | `AuxSource` | 0xc
//! `CAuS` | `ChangeAuxSource` | 0xc
//! Aux (auxiliary) video outputs.

use crate::structs::VideoSource;
use binrw::binrw;

/// `AuxS`: current aux source (`AuxSource`)
///
/// Sent by the switcher to report the current source on an AUX bus.
///
/// ## Packet format
///
/// * `u8`: aux bus ID
/// * 1 byte padding
/// * `u16`: video source
#[binrw]
#[brw(big)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AuxSource {
#[brw(pad_after = 1)]
pub aux_bus: u8,
pub video_source: VideoSource,
}

/// `CAuS`: change aux source (`ChangeAuxSource`)
///
/// Sent by a client to change the source on an AUX bus.
///
/// sofie-atem-connection serialises this as:
///
/// * `u8`: setting mask (always `0x01`)
/// * `u8`: aux bus ID
/// * `u16`: video source
#[binrw]
#[brw(big)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChangeAuxSource {
#[br(temp)]
#[bw(calc = 0x01)]
_setting_mask: u8,
pub aux_bus: u8,
pub video_source: VideoSource,
}
3 changes: 3 additions & 0 deletions necromancer_protocol/src/atom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ use binrw::{binrw, helpers::until_eof, io::TakeSeekExt};
use std::{fmt::Debug, io::SeekFrom};

pub use self::{
aux::{AuxSource, ChangeAuxSource},
camera::{CameraCommand, CameraControl},
colour::{ColourGeneratorParams, SetColourGeneratorParams},
fairlight::{
Expand Down Expand Up @@ -246,6 +247,7 @@ atom_payloads!(
b"_ver" => Version,
b"_VMC" => SupportedVideoModes,
b"AMBP" => FairlightAudioMixerMasterOutEqualiserBandProperties,
b"AuxS" => AuxSource,
b"Capt" => CaptureStill,
b"CCdP" => CameraControl,
b"CClV" => SetColourGeneratorParams,
Expand All @@ -254,6 +256,7 @@ atom_payloads!(
b"ColV" => ColourGeneratorParams,
b"CPgI" => SetProgramInput,
b"CPvI" => SetPreviewInput,
b"CAuS" => ChangeAuxSource,
b"CTCC" => SetTimecodeConfig,
b"CVdM" => SetVideoMode,
b"DAut" => Auto,
Expand Down