Compare commits

..

3 commits

Author SHA1 Message Date
f0f60ebdfc
Roadblock on Deku usage
Here we hit a road block on using Deku for the SCTE35 spec. The Upid
type is more complex in terms of serialization which makes it very
complicated to use Deku.

Besides the problem above. It seems like using Deku will make
maintainance more complicated due to the externsive use of magic macros.
Which also does impact development. I still believe Deku is a great
project to be used in more straight forward binary formats.
2022-05-12 09:08:28 +02:00
56bc156be4
Define Splice Descriptor using Deku
Implements the generic Splice Descriptor definition using the Deku
declarative library macros.

I've implemented only the generic variant of the Splice Descriptors to
check how it would look like using Deku.
2022-05-11 23:17:43 +02:00
6b13875150
Use Deku crate for serialization
Using the Deku library we can implement a `no_std` compatible read and
write structs/enums. We don't need to write manually encoding code and
rely completely on the Deku derive macros to do the right job.

I've implemented the SpliceTime part of the spec as a proof of concept
of the use of Deku. It worked great for this small structure.
2022-05-10 23:05:48 +02:00
5 changed files with 318 additions and 2 deletions

View file

@ -7,8 +7,9 @@ edition = "2021"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
base64 = "0.13" base64 = { version = "0.13", default-features = false, features = ["alloc"] }
bitstream-io = "1.3" bitstream-io = "1.3"
deku = { version = "0.13", default-features = false, features = ["alloc"] }
crc = "3.0" crc = "3.0"
thiserror = "1" thiserror = "1"
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
@ -19,3 +20,4 @@ ascii = "1"
[dev-dependencies] [dev-dependencies]
serde_json = "1" serde_json = "1"
assert-json-diff = "2" assert-json-diff = "2"
hexlit = "0.5"

286
src/data.rs Normal file
View file

@ -0,0 +1,286 @@
use ascii::AsciiString;
use deku::prelude::*;
use crate::{DeviceRestrictions};
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "big", type = "u8", bits = "1")]
pub(crate) enum SpliceTime {
#[deku(id = "1")]
TimeSpecified {
#[deku(bits = "6", assert_eq = "0x3f", update = "0x3f")]
_reserved: u8,
#[deku(bits = "33")]
pts_time: u64,
},
#[deku(id = "0")]
NoTimeSpecified {
#[deku(bits = "7", assert_eq = "0x7f", update = "0x7f")]
_reserved: u8,
},
}
impl Default for SpliceTime {
fn default() -> Self {
Self::NoTimeSpecified { _reserved: 0x7f }
}
}
impl SpliceTime {
fn new(pts_time: u64) -> Self {
Self::TimeSpecified {
_reserved: 0x3f,
pts_time,
}
}
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
#[deku(type = "u8")]
pub(crate) enum SpliceDescriptor {
#[deku(id = "0x02")]
SegmentationDescriptor(SegmentationDescriptor),
#[deku(id_pat = "_")]
Template(GenericDescriptor),
}
impl SpliceDescriptor {
pub(crate) fn update(&mut self) -> Result<(), deku::DekuError> {
use SpliceDescriptor::*;
match self {
Template(s) => s.update(),
SegmentationDescriptor(s) => s.update(),
}
}
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub(crate) struct GenericDescriptor {
id: u8,
#[deku(update = "self.private_bytes.len() + 2")]
descriptor_length: u8,
identifier: u32,
#[deku(count = "descriptor_length - 2")]
private_bytes: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
pub(crate) struct SegmentationDescriptor {
descriptor_length: u8, // TODO: need to calculate by hand the size based in `self.*`
identifier: u32,
segmentation_event_id: u32,
#[deku(bits = "1")]
segmentation_event_cancel_indicator: bool,
#[deku(bits = "7", assert_eq = "0x7f", update = "0x7f")]
_reserved: u8,
#[deku(
skip,
cond = "*segmentation_event_cancel_indicator == false",
default = "None"
)]
segmentation: Option<Segmentation>,
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
pub(crate) struct Segmentation {
#[deku(bits = "1")]
program_segmentation_flag: bool,
#[deku(bits = "1")]
segmentation_duration_flag: bool,
#[deku(bits = "1")]
delivery_not_restricted_flag: bool,
#[deku(cond = "*delivery_not_restricted_flag == false")]
delivery_restriction: Option<DeliveryRestriction>,
#[deku(cond = "*delivery_not_restricted_flag", bits = "5")]
_reserved1: Option<u8>,
#[deku(cond = "*program_segmentation_flag == false")]
program_components: Option<ProgramComponents>,
#[deku(cond = "*segmentation_duration_flag", bits = "40")]
segmentation_duration: u64,
segmentation_upid: SegmentationUpid,
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
#[deku(type = "u8")]
#[non_exhaustive]
pub enum SegmentationUpid {
#[deku(id = "0x00")]
NotUsed,
#[deku(id = "0x01")]
UserDefinedDeprecated(AsciiString),
#[deku(id = "0x02")]
ISCI(AsciiString),
#[deku(id = "0x03")]
AdID(AsciiString),
#[deku(id = "0x04")]
UMID(AsciiString),
#[deku(id = "0x05")]
ISANDeprecated(u64),
#[deku(id = "0x06")]
ISAN(u128),
#[deku(id = "0x07")]
TID(AsciiString),
#[deku(id = "0x08")]
AiringID(u64),
#[deku(id = "0x09")]
ADI(AsciiString),
#[deku(id = "0x0a")]
EIDR(u128),
#[deku(id = "0x0b")]
ATSCContentIdentifier(AsciiString),
#[deku(id = "0x0c")]
MPU,
#[deku(id = "0x0d")]
MID,
#[deku(id = "0x0e")]
ADSInformation(AsciiString),
#[deku(id = "0x0f")]
URI(AsciiString),
#[deku(id = "0x10")]
UUID(u128),
#[deku(id = "0x11")]
SCR(AsciiString),
#[deku(id_pat = "_")]
Reserved(u8),
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
pub(crate) struct ProgramComponents {
#[deku(update = "self.components.len()")]
component_count: u8,
#[deku(count = "component_count")]
components: Vec<SegmentationDescriptorComponent>
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
pub(crate) struct SegmentationDescriptorComponent {
component_tag: u8,
#[deku(bits = "7")]
_reserved: u8,
#[deku(bits = "33")]
pts_offset: u64,
}
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
pub(crate) struct DeliveryRestriction {
#[deku(bits = "1")]
web_delivery_allowed_flag: bool,
#[deku(bits = "1")]
no_regional_blackout_flag: bool,
#[deku(bits = "1")]
archive_allowed_flag: bool,
device_restrictions: DeviceRestrictions,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_splice_time() {
let st = SpliceTime::default();
let data: Vec<u8> = st.try_into().unwrap();
assert_eq!(hex::encode(data.as_slice()), "7f");
// Check update is defined for missing value in field
let mut st = SpliceTime::NoTimeSpecified {
_reserved: 0
};
st.update().unwrap();
let data: Vec<u8> = st.try_into().unwrap();
assert_eq!(hex::encode(data.as_slice()), "7f");
}
#[test]
fn write_splice_time_with_pts_time() {
let st = SpliceTime::new(0x072bd0050);
let encoded: Vec<u8> = st.try_into().unwrap();
assert_eq!(hex::encode(encoded.as_slice()), "fe72bd0050");
}
#[test]
fn read_splice_time_with_pts_time() {
let data = hex::decode("fe72bd0050").unwrap();
let st = SpliceTime::try_from(data.as_slice()).unwrap();
assert_eq!(st, SpliceTime::new(0x072bd0050));
}
#[test]
fn write_generic_descriptor() {
let mut sd = SpliceDescriptor::Template(GenericDescriptor {
id: 0xff,
descriptor_length: 0,
identifier: 0x43554549,
private_bytes: vec![0x01],
});
sd.update().unwrap();
let data: Vec<u8> = sd.try_into().unwrap();
assert_eq!(hex::encode(data.as_slice()), "ff034355454901");
}
#[test]
fn read_generic_descriptor() {
let data = hex::decode("ff034355454901").unwrap();
let sd = SpliceDescriptor::try_from(data.as_slice()).unwrap();
assert_eq!(
sd,
SpliceDescriptor::Template(GenericDescriptor {
id: 0xff,
descriptor_length: 3,
identifier: 0x43554549,
private_bytes: vec![0x01]
})
);
}
}

View file

@ -1,3 +1,4 @@
use deku::prelude::*;
use crate::{BytesWritten, ClockTimeExt}; use crate::{BytesWritten, ClockTimeExt};
use ascii::AsciiString; use ascii::AsciiString;
use bitstream_io::{BigEndian, BitRecorder, BitWrite, BitWriter}; use bitstream_io::{BigEndian, BitRecorder, BitWrite, BitWriter};
@ -252,9 +253,10 @@ impl SpliceDescriptorExt for SegmentationDescriptor {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, DekuRead, DekuWrite)]
#[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", derive(Serialize))]
#[repr(u8)] #[repr(u8)]
#[deku(type="u8", bits="2")]
pub enum DeviceRestrictions { pub enum DeviceRestrictions {
/// This Segment is restricted for a class of devices defined by an out of band message that /// This Segment is restricted for a class of devices defined by an out of band message that
/// describes which devices are excluded. /// describes which devices are excluded.

View file

@ -4,6 +4,7 @@ use std::time::Duration;
use thiserror::Error; use thiserror::Error;
mod commands; mod commands;
mod data;
mod descriptors; mod descriptors;
mod info; mod info;
mod time; mod time;

View file

@ -80,3 +80,28 @@ where
t t
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn encode_time_signal() {
let mut st = SpliceTime::default();
let mut data = Vec::new();
st.write_to(&mut data).unwrap();
assert_eq!(hex::encode(data.as_slice()), "7f")
}
#[test]
fn encode_time_signal_with_time() {
let mut st = SpliceTime::from(0x072bd0050);
let mut data = Vec::new();
st.write_to(&mut data).unwrap();
assert_eq!(hex::encode(data.as_slice()), "fe72bd0050")
}
}