Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
f0f60ebdfc | |||
56bc156be4 | |||
6b13875150 |
5 changed files with 318 additions and 2 deletions
|
@ -7,8 +7,9 @@ edition = "2021"
|
|||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
base64 = { version = "0.13", default-features = false, features = ["alloc"] }
|
||||
bitstream-io = "1.3"
|
||||
deku = { version = "0.13", default-features = false, features = ["alloc"] }
|
||||
crc = "3.0"
|
||||
thiserror = "1"
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
|
@ -19,3 +20,4 @@ ascii = "1"
|
|||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
assert-json-diff = "2"
|
||||
hexlit = "0.5"
|
||||
|
|
286
src/data.rs
Normal file
286
src/data.rs
Normal 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]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use deku::prelude::*;
|
||||
use crate::{BytesWritten, ClockTimeExt};
|
||||
use ascii::AsciiString;
|
||||
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))]
|
||||
#[repr(u8)]
|
||||
#[deku(type="u8", bits="2")]
|
||||
pub enum DeviceRestrictions {
|
||||
/// This Segment is restricted for a class of devices defined by an out of band message that
|
||||
/// describes which devices are excluded.
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::time::Duration;
|
|||
use thiserror::Error;
|
||||
|
||||
mod commands;
|
||||
mod data;
|
||||
mod descriptors;
|
||||
mod info;
|
||||
mod time;
|
||||
|
|
25
src/time.rs
25
src/time.rs
|
@ -80,3 +80,28 @@ where
|
|||
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")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue