use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::operations::{AnyOperation, Operation};
use crate::time::Timestamp;
use broadcast_common::{Parse, Serialize};
const HEADER_LEN: usize = 10; const RESERVED: u16 = 0xFFFF;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct MultipleOperationMessage<'a> {
pub message_size: u16,
pub protocol_version: u8,
pub as_index: u8,
pub message_number: u8,
pub dpi_pid_index: u16,
pub scte35_protocol_version: u8,
pub timestamp: Timestamp,
pub operations: Vec<Operation<'a>>,
}
impl<'a> MultipleOperationMessage<'a> {
#[must_use]
pub fn new(
protocol_version: u8,
as_index: u8,
message_number: u8,
dpi_pid_index: u16,
scte35_protocol_version: u8,
timestamp: Timestamp,
operations: Vec<Operation<'a>>,
) -> Self {
let mut message_size = HEADER_LEN as u16;
message_size += timestamp.serialized_len() as u16;
message_size += 1; for op in &operations {
message_size += 4; message_size += op.body_len() as u16;
}
Self {
message_size,
protocol_version,
as_index,
message_number,
dpi_pid_index,
scte35_protocol_version,
timestamp,
operations,
}
}
}
impl<'a> Parse<'a> for MultipleOperationMessage<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < HEADER_LEN {
return Err(Error::BufferTooShort {
need: HEADER_LEN,
have: bytes.len(),
what: "multiple_operation_message header",
});
}
let reserved = u16::from_be_bytes([bytes[0], bytes[1]]);
if reserved != RESERVED {
return Err(Error::ReservedSet {
field: "reserved",
expected: RESERVED,
got: reserved,
});
}
let message_size = u16::from_be_bytes([bytes[2], bytes[3]]);
let protocol_version = bytes[4];
let as_index = bytes[5];
let message_number = bytes[6];
let dpi_pid_index = u16::from_be_bytes([bytes[7], bytes[8]]);
let scte35_protocol_version = bytes[9];
let mut pos = HEADER_LEN;
let timestamp = Timestamp::parse(&bytes[pos..])?;
pos += timestamp.serialized_len();
if bytes.len() < pos + 1 {
return Err(Error::BufferTooShort {
need: pos + 1,
have: bytes.len(),
what: "num_ops",
});
}
let num_ops = bytes[pos] as usize;
pos += 1;
let mut operations = Vec::with_capacity(num_ops);
for i in 0..num_ops {
if bytes.len() < pos + 4 {
return Err(Error::BufferTooShort {
need: pos + 4,
have: bytes.len(),
what: "operation opID+data_length",
});
}
let op_id = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
let data_length = u16::from_be_bytes([bytes[pos + 2], bytes[pos + 3]]) as usize;
pos += 4;
if bytes.len() < pos + data_length {
return Err(Error::BufferTooShort {
need: pos + data_length,
have: bytes.len(),
what: "operation data",
});
}
let body = &bytes[pos..pos + data_length];
pos += data_length;
let any_op = AnyOperation::dispatch(op_id, body)?;
operations.push(Operation {
op_id,
data: any_op,
});
let _ = i;
}
let expected_size = pos as u16;
if message_size != expected_size {
return Err(Error::LengthOverflow {
declared: message_size as usize,
available: bytes.len(),
what: "multiple_operation_message messageSize mismatch",
});
}
Ok(Self {
message_size,
protocol_version,
as_index,
message_number,
dpi_pid_index,
scte35_protocol_version,
timestamp,
operations,
})
}
}
impl Serialize for MultipleOperationMessage<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
self.message_size as usize
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
buf[0..2].copy_from_slice(&RESERVED.to_be_bytes());
buf[2..4].copy_from_slice(&self.message_size.to_be_bytes());
buf[4] = self.protocol_version;
buf[5] = self.as_index;
buf[6] = self.message_number;
buf[7..9].copy_from_slice(&self.dpi_pid_index.to_be_bytes());
buf[9] = self.scte35_protocol_version;
let mut pos = HEADER_LEN;
self.timestamp.serialize_into(&mut buf[pos..])?;
pos += self.timestamp.serialized_len();
buf[pos] = self.operations.len() as u8;
pos += 1;
for op in &self.operations {
buf[pos..pos + 2].copy_from_slice(&op.op_id.to_be_bytes());
let data_len = op.body_len() as u16;
buf[pos + 2..pos + 4].copy_from_slice(&data_len.to_be_bytes());
pos += 4;
op.data
.serialize_body_into(&mut buf[pos..pos + data_len as usize])?;
pos += data_len as usize;
}
Ok(need)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::operations::{
insert_descriptor::InsertDescriptor,
splice_request::{SpliceInsertType, SpliceRequest},
time_signal_request::TimeSignalRequest,
};
use alloc::vec;
#[test]
fn round_trip_single_splice_request() {
let ops = vec![Operation {
op_id: 0x0101,
data: AnyOperation::SpliceRequest(SpliceRequest {
splice_insert_type: SpliceInsertType::SpliceStartNormal,
splice_event_id: 0x0000_0042,
unique_program_id: 1,
pre_roll_time: 5000,
break_duration: 300,
avail_num: 0,
avails_expected: 0,
auto_return_flag: 1,
not_an_entry_flag: 0,
}),
}];
let msg = MultipleOperationMessage::new(0, 1, 42, 0, 0, Timestamp::None, ops);
let bytes = msg.to_bytes();
let back = MultipleOperationMessage::parse(&bytes).unwrap();
assert_eq!(msg, back);
}
#[test]
fn round_trip_multiple_operations() {
let ops = vec![
Operation {
op_id: 0x0101,
data: AnyOperation::SpliceRequest(SpliceRequest {
splice_insert_type: SpliceInsertType::SpliceStartNormal,
splice_event_id: 1,
unique_program_id: 1,
pre_roll_time: 1000,
break_duration: 300,
avail_num: 0,
avails_expected: 0,
auto_return_flag: 0,
not_an_entry_flag: 0,
}),
},
Operation {
op_id: 0x0108,
data: AnyOperation::InsertDescriptor(InsertDescriptor {
descriptor_count: 1,
descriptor_images: alloc::vec![&[0xAB, 0x02, 0x01, 0x02][..]],
}),
},
Operation {
op_id: 0x0104,
data: AnyOperation::TimeSignalRequest(TimeSignalRequest {
pre_roll_time: 2000,
}),
},
];
let msg = MultipleOperationMessage::new(0, 1, 42, 0, 0, Timestamp::None, ops);
let bytes = msg.to_bytes();
let back = MultipleOperationMessage::parse(&bytes).unwrap();
assert_eq!(msg, back);
assert_eq!(back.operations.len(), 3);
}
#[test]
fn mutate_field_changes_output() {
let ops = vec![Operation {
op_id: 0x0101,
data: AnyOperation::SpliceRequest(SpliceRequest {
splice_insert_type: SpliceInsertType::SpliceStartNormal,
splice_event_id: 1,
unique_program_id: 1,
pre_roll_time: 1000,
break_duration: 300,
avail_num: 0,
avails_expected: 0,
auto_return_flag: 0,
not_an_entry_flag: 0,
}),
}];
let msg = MultipleOperationMessage::new(0, 1, 42, 0, 0, Timestamp::None, ops);
let bytes = msg.to_bytes();
let mut msg2 = msg.clone();
msg2.message_number = 99;
assert_ne!(msg2.to_bytes(), bytes);
}
}