Compare commits
4 Commits
cd614c0956
...
2ec4c231c2
Author | SHA1 | Date |
---|---|---|
|
2ec4c231c2 | |
|
a419a78a0a | |
|
5fd5b6f764 | |
|
be7817cb10 |
|
@ -1,14 +1,24 @@
|
||||||
pub struct AtemPacket {
|
use core::{fmt::Display, str};
|
||||||
length: u16,
|
|
||||||
flags: u8,
|
// TODO: we don't need itertools once https://github.com/rust-lang/rust/issues/79524 lands
|
||||||
session_id: u16,
|
use itertools::Itertools;
|
||||||
remote_packet_id: u16,
|
|
||||||
body: Vec<u8>,
|
/// The "hello" packet to start communication with the ATEM
|
||||||
|
pub const COMMAND_CONNECT_HELLO: [u8; 20] = [
|
||||||
|
0x10, 0x14, 0x53, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct AtemPacket<T: AsRef<[u8]>> {
|
||||||
|
buf: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum AtemPacketErr {
|
pub enum AtemPacketErr {
|
||||||
TooShort(String),
|
/// The packet was too short
|
||||||
LengthDiffers(String),
|
TooShort { got: usize },
|
||||||
|
/// The packet's stated and actual lengths were different
|
||||||
|
LengthDiffers { expected: u16, got: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -32,64 +42,122 @@ impl From<PacketFlag> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtemPacket {
|
impl<T: AsRef<[u8]>> AtemPacket<T> {
|
||||||
|
pub fn new_checked(buf: T) -> Result<Self, AtemPacketErr> {
|
||||||
|
let len = buf.as_ref().len();
|
||||||
|
if len < 12 {
|
||||||
|
return Err(AtemPacketErr::TooShort {
|
||||||
|
got: buf.as_ref().len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let p = Self { buf };
|
||||||
|
if p.length() as usize != len {
|
||||||
|
return Err(AtemPacketErr::LengthDiffers {
|
||||||
|
expected: p.length(),
|
||||||
|
got: len,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(p)
|
||||||
|
}
|
||||||
pub fn length(&self) -> u16 {
|
pub fn length(&self) -> u16 {
|
||||||
self.length
|
u16::from_be_bytes(self.buf.as_ref()[0..=1].try_into().unwrap()) & 0x07ff
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flags(&self) -> u8 {
|
pub fn flags(&self) -> u8 {
|
||||||
self.flags
|
self.buf.as_ref()[0] >> 3
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_id(&self) -> u16 {
|
pub fn session_id(&self) -> u16 {
|
||||||
self.session_id
|
u16::from_be_bytes(self.buf.as_ref()[2..=3].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ack_number(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.buf.as_ref()[4..=5].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remote_sequence_number(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.buf.as_ref()[9..=10].try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote_packet_id(&self) -> u16 {
|
pub fn remote_packet_id(&self) -> u16 {
|
||||||
self.remote_packet_id
|
u16::from_be_bytes(self.buf.as_ref()[10..=11].try_into().unwrap())
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body(&self) -> Vec<u8> {
|
|
||||||
self.body.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if this packet has the given [`PacketFlag`]
|
||||||
pub fn has_flag(&self, flag: PacketFlag) -> bool {
|
pub fn has_flag(&self, flag: PacketFlag) -> bool {
|
||||||
self.flags & u8::from(flag) > 0
|
self.flags() & u8::from(flag) > 0
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for AtemPacket {
|
|
||||||
type Error = AtemPacketErr;
|
|
||||||
|
|
||||||
fn try_from(buffer: &[u8]) -> Result<Self, Self::Error> {
|
|
||||||
if buffer.len() < 12 {
|
|
||||||
return Err(AtemPacketErr::TooShort(format!(
|
|
||||||
"Invalid packet from ATEM {:x?}",
|
|
||||||
buffer
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = u16::from_be_bytes(buffer[0..2].try_into().unwrap()) & 0x07ff;
|
/// Get an iterator over the `Field`s in this packet.
|
||||||
if length as usize != buffer.len() {
|
///
|
||||||
return Err(AtemPacketErr::LengthDiffers(format!(
|
/// Returns None if this is a packet without fields.
|
||||||
"Length of message differs, expected {} got {}",
|
pub fn fields(&self) -> Option<Fields> {
|
||||||
length,
|
// TODO: do we only ever get newsessionid during the handshake (i.e. not in a packet with fields)?
|
||||||
buffer.len()
|
if self.has_flag(PacketFlag::NewSessionId) {
|
||||||
)));
|
None
|
||||||
}
|
} else {
|
||||||
|
Some(Fields {
|
||||||
let flags = buffer[0] >> 3;
|
data: self.body(),
|
||||||
let session_id = u16::from_be_bytes(buffer[2..4].try_into().unwrap());
|
offset: 0,
|
||||||
let remote_packet_id = u16::from_be_bytes(buffer[10..12].try_into().unwrap());
|
|
||||||
|
|
||||||
let body = buffer[12..].to_vec();
|
|
||||||
|
|
||||||
Ok(AtemPacket {
|
|
||||||
length,
|
|
||||||
flags,
|
|
||||||
session_id,
|
|
||||||
remote_packet_id,
|
|
||||||
body,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body(&self) -> &[u8] {
|
||||||
|
&self.buf.as_ref()[12..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<T: AsRef<[u8]>> Display for AtemPacket<T> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"sid: {}, len: {}, fields: ({})",
|
||||||
|
self.session_id(),
|
||||||
|
self.length(),
|
||||||
|
self.fields()
|
||||||
|
.and_then(|f| Some(
|
||||||
|
f.map(|f| str::from_utf8(f.r#type).unwrap())
|
||||||
|
.intersperse(", ")
|
||||||
|
.collect::<String>()
|
||||||
|
))
|
||||||
|
.unwrap_or("none".into())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ATEM protocol field - a 4-ascii character type plus variable length data
|
||||||
|
pub struct Field<'a> {
|
||||||
|
r#type: &'a [u8; 4],
|
||||||
|
data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Created by [`AtemPacket::fields`]
|
||||||
|
pub struct Fields<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Fields<'a> {
|
||||||
|
type Item = Field<'a>;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let remain = self.data.len() - self.offset;
|
||||||
|
if remain == 0 {
|
||||||
|
return None;
|
||||||
|
} else if remain < 8 {
|
||||||
|
// TODO: is 8 indeed the minimum size for something here? (i.e. no field data)
|
||||||
|
panic!("Oh no");
|
||||||
|
}
|
||||||
|
|
||||||
|
let length =
|
||||||
|
u16::from_be_bytes(self.data[self.offset..=self.offset + 1].try_into().unwrap());
|
||||||
|
// TODO: sanity check length
|
||||||
|
let r#type: &[u8; 4] = self.data[self.offset + 4..=self.offset + 7]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let data = &self.data[self.offset + 8..self.offset + (length as usize)];
|
||||||
|
self.offset += (length as usize);
|
||||||
|
Some(Field { r#type, data })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use core::net::SocketAddr;
|
||||||
use std::{io, sync::Arc, thread::yield_now};
|
use std::{io, sync::Arc, thread::yield_now};
|
||||||
|
|
||||||
use tokio::{sync::RwLock, task::JoinHandle};
|
use tokio::{sync::RwLock, task::JoinHandle};
|
||||||
|
@ -17,8 +18,8 @@ pub enum AtemSocketConnectionError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtemSocket {
|
impl AtemSocket {
|
||||||
pub fn new() -> Self {
|
pub fn new<A: Into<SocketAddr>>(addr: A) -> Self {
|
||||||
let socket = AtemSocketInner::new();
|
let socket = AtemSocketInner::new(addr.into());
|
||||||
let socket = Arc::new(RwLock::new(socket));
|
let socket = Arc::new(RwLock::new(socket));
|
||||||
|
|
||||||
let socket_clone = Arc::clone(&socket);
|
let socket_clone = Arc::clone(&socket);
|
||||||
|
@ -37,12 +38,8 @@ impl AtemSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect(
|
pub async fn connect(&mut self) -> Result<(), AtemSocketConnectionError> {
|
||||||
&mut self,
|
self.socket.write().await.connect().await?;
|
||||||
address: String,
|
|
||||||
port: u16,
|
|
||||||
) -> Result<(), AtemSocketConnectionError> {
|
|
||||||
self.socket.write().await.connect(address, port).await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -60,9 +57,3 @@ impl AtemSocket {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AtemSocket {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
use core::net::SocketAddr;
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
net::SocketAddr,
|
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::debug;
|
use log::{debug, trace};
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
|
|
||||||
use crate::atem_lib::atem_util;
|
use crate::atem_lib::atem_packet::{AtemPacket, PacketFlag, COMMAND_CONNECT_HELLO};
|
||||||
|
|
||||||
const IN_FLIGHT_TIMEOUT: u64 = 60;
|
const IN_FLIGHT_TIMEOUT: u64 = 60;
|
||||||
const CONNECTION_TIMEOUT: u64 = 5000;
|
const CONNECTION_TIMEOUT: u64 = 5000;
|
||||||
|
@ -39,27 +39,6 @@ impl Into<u8> for ConnectionState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum PacketFlag {
|
|
||||||
AckRequest,
|
|
||||||
NewSessionId,
|
|
||||||
IsRetransmit,
|
|
||||||
RetransmitRequest,
|
|
||||||
AckReply,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PacketFlag> for u8 {
|
|
||||||
fn from(flag: PacketFlag) -> Self {
|
|
||||||
match flag {
|
|
||||||
PacketFlag::AckRequest => 0x01,
|
|
||||||
PacketFlag::NewSessionId => 0x02,
|
|
||||||
PacketFlag::IsRetransmit => 0x04,
|
|
||||||
PacketFlag::RetransmitRequest => 0x08,
|
|
||||||
PacketFlag::AckReply => 0x10,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct InFlightPacket {
|
struct InFlightPacket {
|
||||||
packet_id: u16,
|
packet_id: u16,
|
||||||
|
@ -89,8 +68,7 @@ pub struct AtemSocketInner {
|
||||||
session_id: u16,
|
session_id: u16,
|
||||||
|
|
||||||
socket: Option<UdpSocket>,
|
socket: Option<UdpSocket>,
|
||||||
address: String,
|
address: SocketAddr,
|
||||||
port: u16,
|
|
||||||
|
|
||||||
last_received_at: SystemTime,
|
last_received_at: SystemTime,
|
||||||
last_received_packed_id: u16,
|
last_received_packed_id: u16,
|
||||||
|
@ -113,7 +91,7 @@ enum AtemSocketWriteError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtemSocketInner {
|
impl AtemSocketInner {
|
||||||
pub fn new() -> Self {
|
pub fn new(addr: SocketAddr) -> Self {
|
||||||
AtemSocketInner {
|
AtemSocketInner {
|
||||||
connection_state: ConnectionState::Closed,
|
connection_state: ConnectionState::Closed,
|
||||||
reconnect_timer: None,
|
reconnect_timer: None,
|
||||||
|
@ -123,8 +101,7 @@ impl AtemSocketInner {
|
||||||
session_id: 0,
|
session_id: 0,
|
||||||
|
|
||||||
socket: None,
|
socket: None,
|
||||||
address: "0.0.0.0".to_string(),
|
address: addr,
|
||||||
port: 0,
|
|
||||||
|
|
||||||
last_received_at: SystemTime::now(),
|
last_received_at: SystemTime::now(),
|
||||||
last_received_packed_id: 0,
|
last_received_packed_id: 0,
|
||||||
|
@ -134,15 +111,9 @@ impl AtemSocketInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect(&mut self, address: String, port: u16) -> Result<(), io::Error> {
|
pub async fn connect(&mut self) -> Result<(), io::Error> {
|
||||||
self.address = address.clone();
|
|
||||||
self.port = port;
|
|
||||||
|
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").await?;
|
let socket = UdpSocket::bind("0.0.0.0:0").await?;
|
||||||
let remote_addr = format!("{}:{}", address, port)
|
socket.connect(self.address).await?;
|
||||||
.parse::<SocketAddr>()
|
|
||||||
.unwrap();
|
|
||||||
socket.connect(remote_addr).await?;
|
|
||||||
self.socket = Some(socket);
|
self.socket = Some(socket);
|
||||||
|
|
||||||
self.start_timers();
|
self.start_timers();
|
||||||
|
@ -152,7 +123,7 @@ impl AtemSocketInner {
|
||||||
self.in_flight = vec![];
|
self.in_flight = vec![];
|
||||||
debug!("Reconnect");
|
debug!("Reconnect");
|
||||||
|
|
||||||
self.send_packet(&atem_util::COMMAND_CONNECT_HELLO).await;
|
self.send_packet(&COMMAND_CONNECT_HELLO).await;
|
||||||
self.connection_state = ConnectionState::SynSent;
|
self.connection_state = ConnectionState::SynSent;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -218,7 +189,7 @@ impl AtemSocketInner {
|
||||||
|
|
||||||
async fn restart_connection(&mut self) {
|
async fn restart_connection(&mut self) {
|
||||||
self.disconnect();
|
self.disconnect();
|
||||||
self.connect(self.address.clone(), self.port).await.ok();
|
self.connect().await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn tick(&mut self) {
|
pub async fn tick(&mut self) {
|
||||||
|
@ -277,30 +248,23 @@ impl AtemSocketInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recieved_packet(&mut self, packet: &[u8]) {
|
async fn recieved_packet(&mut self, packet: &[u8]) {
|
||||||
debug!("RECV {:x?}", packet);
|
trace!("RX Raw: {:x?}", packet);
|
||||||
|
|
||||||
if packet.len() < 12 {
|
let checked = match AtemPacket::new_checked(packet) {
|
||||||
debug!("Invalid packet from ATEM {:x?}", packet);
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Invalid packet ({:?}): {:x?}", e, packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
debug!("RX: {}", checked);
|
||||||
|
|
||||||
self.last_received_at = SystemTime::now();
|
self.last_received_at = SystemTime::now();
|
||||||
let length = u16::from_be_bytes(packet[0..2].try_into().unwrap()) & 0x07ff;
|
|
||||||
|
|
||||||
if length as usize != packet.len() {
|
self.session_id = checked.session_id();
|
||||||
debug!(
|
let remote_packet_id = checked.remote_packet_id();
|
||||||
"Length of message differs, expected {} got {}",
|
|
||||||
length,
|
|
||||||
packet.len()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let flags = packet[0] >> 3;
|
if checked.flags() & u8::from(PacketFlag::NewSessionId) > 0 {
|
||||||
self.session_id = u16::from_be_bytes(packet[2..4].try_into().unwrap());
|
|
||||||
let remote_packet_id = u16::from_be_bytes(packet[10..12].try_into().unwrap());
|
|
||||||
|
|
||||||
if flags & u8::from(PacketFlag::NewSessionId) > 0 {
|
|
||||||
debug!("New session");
|
debug!("New session");
|
||||||
self.connection_state = ConnectionState::Established;
|
self.connection_state = ConnectionState::Established;
|
||||||
self.last_received_packed_id = remote_packet_id;
|
self.last_received_packed_id = remote_packet_id;
|
||||||
|
@ -309,20 +273,20 @@ impl AtemSocketInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.connection_state == ConnectionState::Established {
|
if self.connection_state == ConnectionState::Established {
|
||||||
if flags & u8::from(PacketFlag::RetransmitRequest) > 0 {
|
if checked.has_flag(PacketFlag::RetransmitRequest) {
|
||||||
let from_packet_id = u16::from_be_bytes(packet[6..8].try_into().unwrap());
|
let from_packet_id = u16::from_be_bytes(packet[6..8].try_into().unwrap());
|
||||||
debug!("Retransmit request: {:x?}", from_packet_id);
|
debug!("Retransmit request: {:x?}", from_packet_id);
|
||||||
|
|
||||||
self.retransmit_from(from_packet_id).await;
|
self.retransmit_from(from_packet_id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags & u8::from(PacketFlag::AckRequest) > 0 {
|
if checked.has_flag(PacketFlag::AckRequest) {
|
||||||
if remote_packet_id == (self.last_received_packed_id + 1) % MAX_PACKET_ID {
|
if remote_packet_id == (self.last_received_packed_id + 1) % MAX_PACKET_ID {
|
||||||
self.last_received_packed_id = remote_packet_id;
|
self.last_received_packed_id = remote_packet_id;
|
||||||
self.send_or_queue_ack().await;
|
self.send_or_queue_ack().await;
|
||||||
|
|
||||||
if length > 12 {
|
if checked.length() > 12 {
|
||||||
self.on_command_received(&packet[12..], remote_packet_id);
|
self.on_command_received(checked.body(), remote_packet_id);
|
||||||
}
|
}
|
||||||
} else if self
|
} else if self
|
||||||
.is_packet_covered_by_ack(self.last_received_packed_id, remote_packet_id)
|
.is_packet_covered_by_ack(self.last_received_packed_id, remote_packet_id)
|
||||||
|
@ -331,12 +295,12 @@ impl AtemSocketInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags & u8::from(PacketFlag::IsRetransmit) > 0 {
|
if checked.has_flag(PacketFlag::IsRetransmit) {
|
||||||
debug!("ATEM retransmitted packet {:x?}", remote_packet_id);
|
debug!("ATEM retransmitted packet {:x?}", remote_packet_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags & u8::from(PacketFlag::AckReply) > 0 {
|
if checked.has_flag(PacketFlag::AckReply) {
|
||||||
let ack_packet_id = u16::from_be_bytes(packet[4..6].try_into().unwrap());
|
let ack_packet_id = checked.ack_number();
|
||||||
let mut acked_commands: Vec<AckedPacket> = vec![];
|
let mut acked_commands: Vec<AckedPacket> = vec![];
|
||||||
|
|
||||||
self.in_flight = self
|
self.in_flight = self
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
pub const COMMAND_CONNECT_HELLO: [u8; 20] = [
|
|
||||||
0x10, 0x14, 0x53, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
];
|
|
|
@ -1,4 +1,3 @@
|
||||||
mod atem_packet;
|
pub mod atem_packet;
|
||||||
pub mod atem_socket;
|
pub mod atem_socket;
|
||||||
mod atem_socket_inner;
|
mod atem_socket_inner;
|
||||||
pub mod atem_util;
|
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
pub struct ColorGeneratorState {
|
pub struct ColorGeneratorState {
|
||||||
pub hue: u64,
|
pub hue: u64,
|
||||||
pub saturation: u64,
|
pub saturation: u64,
|
||||||
pub luma: u64
|
pub luma: u64,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::enums::{ExternalPortType, FairlightAnalogInputLevel, FairlightAudioMixOption, FairlightAudioSourceType, FairlightInputConfiguration, FairlightInputType};
|
use crate::enums::{
|
||||||
|
ExternalPortType, FairlightAnalogInputLevel, FairlightAudioMixOption, FairlightAudioSourceType,
|
||||||
|
FairlightInputConfiguration, FairlightInputType,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct FairlightAudioDynamicsState {
|
pub struct FairlightAudioDynamicsState {
|
||||||
|
@ -8,7 +11,7 @@ pub struct FairlightAudioDynamicsState {
|
||||||
|
|
||||||
pub limiter: Option<FairlightAudioLimiterState>,
|
pub limiter: Option<FairlightAudioLimiterState>,
|
||||||
pub compressor: Option<FairlightAudioCompressorState>,
|
pub compressor: Option<FairlightAudioCompressorState>,
|
||||||
pub expander: Option<FairlightAudioExpanderState>
|
pub expander: Option<FairlightAudioExpanderState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -17,7 +20,7 @@ pub struct FairlightAudioLimiterState {
|
||||||
pub threshold: u64,
|
pub threshold: u64,
|
||||||
pub attack: u64,
|
pub attack: u64,
|
||||||
pub hold: u64,
|
pub hold: u64,
|
||||||
pub release: u64
|
pub release: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -27,7 +30,7 @@ pub struct FairlightAudioCompressorState {
|
||||||
pub ratio: u64,
|
pub ratio: u64,
|
||||||
pub attack: u64,
|
pub attack: u64,
|
||||||
pub hold: u64,
|
pub hold: u64,
|
||||||
pub release: u64
|
pub release: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -39,7 +42,7 @@ pub struct FairlightAudioExpanderState {
|
||||||
pub ratio: u64,
|
pub ratio: u64,
|
||||||
pub attack: u64,
|
pub attack: u64,
|
||||||
pub hold: u64,
|
pub hold: u64,
|
||||||
pub release: u64
|
pub release: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -55,14 +58,14 @@ pub struct FairlightAudioEqualizerBandState {
|
||||||
pub frequency: u64,
|
pub frequency: u64,
|
||||||
|
|
||||||
pub gain: u64,
|
pub gain: u64,
|
||||||
pub q_factor: u64
|
pub q_factor: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct FairlightAudioMasterChannelPropertiesState {
|
pub struct FairlightAudioMasterChannelPropertiesState {
|
||||||
// Gain in decibel, -Infinity to +6dB
|
// Gain in decibel, -Infinity to +6dB
|
||||||
pub fader_gain: u64,
|
pub fader_gain: u64,
|
||||||
pub follow_fade_to_black: bool
|
pub follow_fade_to_black: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -78,21 +81,21 @@ pub struct FairlightAudioMonitorChannel {
|
||||||
pub gain: u64,
|
pub gain: u64,
|
||||||
pub input_master_gain: u64,
|
pub input_master_gain: u64,
|
||||||
pub input_talkback_gain: u64,
|
pub input_talkback_gain: u64,
|
||||||
pub input_sidetone_gain: u64
|
pub input_sidetone_gain: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct FairlightAudioSource {
|
pub struct FairlightAudioSource {
|
||||||
pub properties: Option<FairlightAudioSourcePropertiesState>,
|
pub properties: Option<FairlightAudioSourcePropertiesState>,
|
||||||
pub equalizer: Option<FairlightAudioEqualizerState>,
|
pub equalizer: Option<FairlightAudioEqualizerState>,
|
||||||
pub dynamics: Option<FairlightAudioDynamicsState>
|
pub dynamics: Option<FairlightAudioDynamicsState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct FairlightAudioEqualizerState {
|
pub struct FairlightAudioEqualizerState {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub gain: u64,
|
pub gain: u64,
|
||||||
bands: Vec<FairlightAudioEqualizerBandState>
|
bands: Vec<FairlightAudioEqualizerBandState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -110,13 +113,13 @@ pub struct FairlightAudioSourcePropertiesState {
|
||||||
pub fader_gain: u64,
|
pub fader_gain: u64,
|
||||||
|
|
||||||
supported_mix_options: Vec<FairlightAudioMixOption>,
|
supported_mix_options: Vec<FairlightAudioMixOption>,
|
||||||
pub mix_option: FairlightAudioMixOption
|
pub mix_option: FairlightAudioMixOption,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct FairlightAudioInput {
|
pub struct FairlightAudioInput {
|
||||||
pub properties: Option<FairlightAudioInputProperties>,
|
pub properties: Option<FairlightAudioInputProperties>,
|
||||||
pub sources: HashMap<String, FairlightAudioSource>
|
pub sources: HashMap<String, FairlightAudioSource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -128,7 +131,7 @@ pub struct FairlightAudioInputProperties {
|
||||||
pub active_configuration: FairlightInputConfiguration,
|
pub active_configuration: FairlightInputConfiguration,
|
||||||
|
|
||||||
supported_input_levels: Vec<FairlightAnalogInputLevel>,
|
supported_input_levels: Vec<FairlightAnalogInputLevel>,
|
||||||
pub activeInputLevel: FairlightAnalogInputLevel
|
pub activeInputLevel: FairlightAnalogInputLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -137,5 +140,5 @@ pub struct AtemFairlightAudioState {
|
||||||
pub master: Option<FairlightAudioMasterChannel>,
|
pub master: Option<FairlightAudioMasterChannel>,
|
||||||
pub monitor: Option<FairlightAudioMonitorChannel>,
|
pub monitor: Option<FairlightAudioMonitorChannel>,
|
||||||
|
|
||||||
pub audio_follow_video_crossfade_transition_enabled: Option<bool>
|
pub audio_follow_video_crossfade_transition_enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,5 @@ pub struct InputChannel {
|
||||||
pub external_port_type: ExternalPortType,
|
pub external_port_type: ExternalPortType,
|
||||||
internal_port_type: InternalPortType,
|
internal_port_type: InternalPortType,
|
||||||
source_availability: SourceAvailability,
|
source_availability: SourceAvailability,
|
||||||
me_availability: MeAvailability
|
me_availability: MeAvailability,
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub struct RecordingState {
|
||||||
|
|
||||||
pub duration: Option<Timecode>,
|
pub duration: Option<Timecode>,
|
||||||
|
|
||||||
pub disks: HashMap<u64, RecordingDiskProperties>
|
pub disks: HashMap<u64, RecordingDiskProperties>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -19,7 +19,7 @@ pub struct RecordingDiskProperties {
|
||||||
pub disk_id: u64,
|
pub disk_id: u64,
|
||||||
pub volume_name: String,
|
pub volume_name: String,
|
||||||
pub recording_time_available: u64,
|
pub recording_time_available: u64,
|
||||||
pub status: RecordingDiskStatus
|
pub status: RecordingDiskStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -27,7 +27,7 @@ pub struct RecordingStateStatus {
|
||||||
pub state: RecordingStatus,
|
pub state: RecordingStatus,
|
||||||
pub error: RecordingError,
|
pub error: RecordingError,
|
||||||
|
|
||||||
pub recording_time_available: u64
|
pub recording_time_available: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
|
@ -37,5 +37,5 @@ pub struct RecordingStateProperties {
|
||||||
pub working_set_1_disk_id: u64,
|
pub working_set_1_disk_id: u64,
|
||||||
pub working_set_2_disk_id: u64,
|
pub working_set_2_disk_id: u64,
|
||||||
|
|
||||||
pub record_in_all_cameras: bool
|
pub record_in_all_cameras: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,19 @@ pub struct StreamingState {
|
||||||
pub stats: Option<StreamingStateStats>,
|
pub stats: Option<StreamingStateStats>,
|
||||||
pub service: StreamingServiceProperties,
|
pub service: StreamingServiceProperties,
|
||||||
|
|
||||||
pub duration: Option<Timecode>
|
pub duration: Option<Timecode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct StreamingStateStatus {
|
pub struct StreamingStateStatus {
|
||||||
state: StreamingStatus,
|
state: StreamingStatus,
|
||||||
error: StreamingError
|
error: StreamingError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Getters, new)]
|
#[derive(Getters, new)]
|
||||||
pub struct StreamingStateStats {
|
pub struct StreamingStateStats {
|
||||||
cache_used: u64,
|
cache_used: u64,
|
||||||
encoding_bitrate: u64
|
encoding_bitrate: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StreamingServiceProperties {
|
pub struct StreamingServiceProperties {
|
||||||
|
@ -28,5 +28,5 @@ pub struct StreamingServiceProperties {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub key: String,
|
pub key: String,
|
||||||
|
|
||||||
bitrates: (u64, u64)
|
bitrates: (u64, u64),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::time::Duration;
|
use std::{env::args, net::Ipv4Addr, time::Duration};
|
||||||
|
|
||||||
use atem_connection_rs::{
|
use atem_connection_rs::{
|
||||||
atem_lib::atem_socket::AtemSocket,
|
atem_lib::atem_socket::AtemSocket,
|
||||||
|
@ -18,8 +18,11 @@ async fn main() {
|
||||||
let switch_to_source_1 = ProgramInput::new(0, 1);
|
let switch_to_source_1 = ProgramInput::new(0, 1);
|
||||||
let switch_to_source_2 = ProgramInput::new(0, 2);
|
let switch_to_source_2 = ProgramInput::new(0, 2);
|
||||||
|
|
||||||
let mut atem = AtemSocket::new();
|
let mut atem = AtemSocket::new((
|
||||||
atem.connect("127.0.0.1".to_string(), 9910).await.ok();
|
args().skip(1).next().unwrap().parse::<Ipv4Addr>().unwrap(),
|
||||||
|
9910,
|
||||||
|
));
|
||||||
|
atem.connect().await.ok();
|
||||||
|
|
||||||
let mut tracking_id = 0;
|
let mut tracking_id = 0;
|
||||||
loop {
|
loop {
|
||||||
|
|
150
flake.lock
150
flake.lock
|
@ -1,70 +1,15 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"devshell": {
|
"naersk": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705332421,
|
"lastModified": 1745925850,
|
||||||
"narHash": "sha256-USpGLPme1IuqG78JNqSaRabilwkCyHmVWY0M9vYyqEA=",
|
"narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "devshell",
|
|
||||||
"rev": "83cb93d6d063ad290beee669f4badf9914cc16ec",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "devshell",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1701680307,
|
|
||||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils_2": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681202837,
|
|
||||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"naersk": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1698420672,
|
|
||||||
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
|
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
|
"rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -75,11 +20,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1704161960,
|
"lastModified": 1749401433,
|
||||||
"narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=",
|
"narHash": "sha256-HXIQzULIG/MEUW2Q/Ss47oE3QrjxvpUX7gUl4Xp6lnc=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "63143ac2c9186be6d9da6035fa22620018c85932",
|
"rev": "08fcb0dcb59df0344652b38ea6326a2d8271baff",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -91,12 +36,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705883077,
|
"lastModified": 1733412085,
|
||||||
"narHash": "sha256-ByzHHX3KxpU1+V0erFy8jpujTufimh6KaS/Iv3AciHk=",
|
"narHash": "sha256-FillH0qdWDt/nlO6ED7h4cmN+G9uXwGjwmCnHs0QVYM=",
|
||||||
"owner": "NixOS",
|
"path": "/nix/store/5wvrlpfrwlmg24ywkybvidyzcvki6bwx-source",
|
||||||
"repo": "nixpkgs",
|
"rev": "4dc2fc4e62dbf62b84132fe526356fbac7b03541",
|
||||||
"rev": "5f5210aa20e343b7e35f40c033000db0ef80d7b9",
|
"type": "path"
|
||||||
"type": "github"
|
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
|
@ -105,25 +49,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705883077,
|
"lastModified": 1744536153,
|
||||||
"narHash": "sha256-ByzHHX3KxpU1+V0erFy8jpujTufimh6KaS/Iv3AciHk=",
|
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5f5210aa20e343b7e35f40c033000db0ef80d7b9",
|
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"id": "nixpkgs",
|
|
||||||
"type": "indirect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_4": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681358109,
|
|
||||||
"narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -135,24 +65,22 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"devshell": "devshell",
|
|
||||||
"naersk": "naersk",
|
"naersk": "naersk",
|
||||||
"nixpkgs": "nixpkgs_3",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"rust-overlay": "rust-overlay",
|
"rust-overlay": "rust-overlay",
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-overlay": {
|
"rust-overlay": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_2",
|
"nixpkgs": "nixpkgs_3"
|
||||||
"nixpkgs": "nixpkgs_4"
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705976279,
|
"lastModified": 1749436897,
|
||||||
"narHash": "sha256-Zx97bJ3+O8IP70uJPD//rRsr8bcxICISMTZUT/L9eFk=",
|
"narHash": "sha256-OkDtaCGQQVwVFz5HWfbmrMJR99sFIMXHCHEYXzUJEJY=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "f889dc31ef97835834bdc3662394ebdb3c96b974",
|
"rev": "e7876c387e35dc834838aff254d8e74cf5bd4f19",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -176,46 +104,16 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems_3": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"utils": {
|
"utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_3"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705309234,
|
"lastModified": 1731533236,
|
||||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
14
flake.nix
14
flake.nix
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
utils.url = "github:numtide/flake-utils";
|
utils.url = "github:numtide/flake-utils";
|
||||||
devshell.url = "github:numtide/devshell";
|
|
||||||
naersk.url = "github:nix-community/naersk";
|
naersk.url = "github:nix-community/naersk";
|
||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
};
|
};
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
utils,
|
utils,
|
||||||
naersk,
|
naersk,
|
||||||
devshell,
|
|
||||||
rust-overlay,
|
rust-overlay,
|
||||||
}:
|
}:
|
||||||
utils.lib.eachDefaultSystem (system: let
|
utils.lib.eachDefaultSystem (system: let
|
||||||
|
@ -35,16 +33,10 @@
|
||||||
|
|
||||||
apps.default = utils.lib.mkApp {drv = packages.default;};
|
apps.default = utils.lib.mkApp {drv = packages.default;};
|
||||||
|
|
||||||
# Provide a dev env with rust and rust-analyzer
|
devShells.default = pkgs.mkShell {
|
||||||
devShells.default = let
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
overlays = [devshell.overlays.default];
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.devshell.mkShell {
|
|
||||||
packages = with pkgs; [(rust.override {extensions = ["rust-src"];}) rust-analyzer gcc];
|
packages = with pkgs; [(rust.override {extensions = ["rust-src"];}) rust-analyzer gcc];
|
||||||
|
|
||||||
};
|
};
|
||||||
formatter = pkgs.alejandra;
|
formatter = pkgs.nixfmt-rfc-style;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue