Segmentation descriptor with upid types
This commit is contained in:
parent
1f1ee312c0
commit
fa917dafce
6 changed files with 284 additions and 126 deletions
|
@ -14,6 +14,7 @@ thiserror = "1"
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
ascii = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
|
@ -22,18 +22,15 @@ Implemented parts of the standard are:
|
||||||
- [x] Splice Null
|
- [x] Splice Null
|
||||||
- [ ] Splice Insert
|
- [ ] Splice Insert
|
||||||
- [ ] Splice Schedule
|
- [ ] Splice Schedule
|
||||||
- [ ] Time Signal
|
- [x] Time Signal
|
||||||
- [ ] Bandwidth Reservation
|
- [ ] Bandwidth Reservation
|
||||||
- [ ] Splice Time
|
- [ ] Splice Time
|
||||||
- Splice Descriptors:
|
- Splice Descriptors:
|
||||||
- [ ] Avail
|
- [ ] Avail
|
||||||
- [ ] DTMF
|
- [ ] DTMF
|
||||||
- [ ] Segmentation Descriptor
|
- [ ] Segmentation Descriptor
|
||||||
- [ ] Cablelabs
|
|
||||||
- [ ] MPU
|
- [ ] MPU
|
||||||
- [ ] MID
|
- [ ] MID
|
||||||
- [ ] ADS
|
|
||||||
- [ ] SCR
|
|
||||||
- Encryption Information section
|
- Encryption Information section
|
||||||
- Encryption Algorithms:
|
- Encryption Algorithms:
|
||||||
- [ ] DES – ECB mode
|
- [ ] DES – ECB mode
|
||||||
|
|
|
@ -42,6 +42,12 @@ impl TransportPacketWrite for SpliceDescriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SegmentationDescriptor> for SpliceDescriptor {
|
||||||
|
fn from(segmentation: SegmentationDescriptor) -> Self {
|
||||||
|
SpliceDescriptor::Segmentation(segmentation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
enum SpliceDescriptorTag {
|
enum SpliceDescriptorTag {
|
||||||
Avail,
|
Avail,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{CueError, TransportPacketWrite};
|
use crate::{BytesWritten, CueError, TransportPacketWrite};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use bitstream_io::{BigEndian, BitWrite, BitWriter};
|
use ascii::AsciiString;
|
||||||
|
use bitstream_io::{BigEndian, BitRecorder, BitWrite, BitWriter};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -10,7 +11,7 @@ use crate::descriptors::{SpliceDescriptorExt, SpliceDescriptorTag};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||||
pub struct SegmentationDescriptor {
|
pub struct SegmentationDescriptor {
|
||||||
segmentation_event_id: u32,
|
segmentation_event_id: u32,
|
||||||
|
@ -32,6 +33,76 @@ pub struct SegmentationDescriptor {
|
||||||
sub_segments_expected: u8,
|
sub_segments_expected: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SegmentationDescriptor {
|
||||||
|
pub fn set_segmentation_event_id(&mut self, segmentation_event_id: u32) {
|
||||||
|
self.segmentation_event_id = segmentation_event_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segmentation_event_cancel_indicator(
|
||||||
|
&mut self,
|
||||||
|
segmentation_event_cancel_indicator: bool,
|
||||||
|
) {
|
||||||
|
self.segmentation_event_cancel_indicator = segmentation_event_cancel_indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_program_segmentation_flag(&mut self, program_segmentation_flag: bool) {
|
||||||
|
self.program_segmentation_flag = program_segmentation_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segmentation_duration_flag(&mut self, segmentation_duration_flag: bool) {
|
||||||
|
self.segmentation_duration_flag = segmentation_duration_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_delivery_not_restricted_flag(&mut self, delivery_not_restricted_flag: bool) {
|
||||||
|
self.delivery_not_restricted_flag = delivery_not_restricted_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_web_delivery_allowed_flag(&mut self, web_delivery_allowed_flag: bool) {
|
||||||
|
self.web_delivery_allowed_flag = web_delivery_allowed_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_no_regional_blackout_flag(&mut self, no_regional_blackout_flag: bool) {
|
||||||
|
self.no_regional_blackout_flag = no_regional_blackout_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_archive_allowed_flag(&mut self, archive_allowed_flag: bool) {
|
||||||
|
self.archive_allowed_flag = archive_allowed_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_device_restrictions(&mut self, device_restrictions: DeviceRestrictions) {
|
||||||
|
self.device_restrictions = device_restrictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segmentation_duration(&mut self, segmentation_duration: u64) {
|
||||||
|
self.set_segmentation_duration_flag(true);
|
||||||
|
self.segmentation_duration = segmentation_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segmentation_upid(&mut self, segmentation_upid: SegmentationUpid) {
|
||||||
|
self.segmentation_upid = segmentation_upid;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segmentation_type(&mut self, segmentation_type: SegmentationType) {
|
||||||
|
self.segmentation_type = segmentation_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segment_num(&mut self, segment_num: u8) {
|
||||||
|
self.segment_num = segment_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_segments_expected(&mut self, segments_expected: u8) {
|
||||||
|
self.segments_expected = segments_expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sub_segment_num(&mut self, sub_segment_num: u8) {
|
||||||
|
self.sub_segment_num = sub_segment_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sub_segments_expected(&mut self, sub_segments_expected: u8) {
|
||||||
|
self.sub_segments_expected = sub_segments_expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TransportPacketWrite for SegmentationDescriptor {
|
impl TransportPacketWrite for SegmentationDescriptor {
|
||||||
fn write_to<W>(&self, buffer: &mut W) -> anyhow::Result<()>
|
fn write_to<W>(&self, buffer: &mut W) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
|
@ -39,66 +110,63 @@ impl TransportPacketWrite for SegmentationDescriptor {
|
||||||
{
|
{
|
||||||
use SegmentationFieldSyntax::*;
|
use SegmentationFieldSyntax::*;
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut recorder: BitRecorder<u32, BigEndian> = BitRecorder::new();
|
||||||
let mut internal_buffer = BitWriter::endian(&mut data, BigEndian);
|
recorder.write(32, self.identifier())?;
|
||||||
internal_buffer.write(32, self.identifier())?;
|
recorder.write(32, self.segmentation_event_id)?;
|
||||||
internal_buffer.write(32, self.segmentation_event_id)?;
|
recorder.write_bit(self.segmentation_event_cancel_indicator)?;
|
||||||
internal_buffer.write_bit(self.segmentation_event_cancel_indicator)?;
|
recorder.write(7, 0x7f)?;
|
||||||
internal_buffer.write(7, 0x7f)?;
|
|
||||||
if !self.segmentation_event_cancel_indicator {
|
if !self.segmentation_event_cancel_indicator {
|
||||||
internal_buffer.write_bit(self.program_segmentation_flag)?;
|
recorder.write_bit(self.program_segmentation_flag)?;
|
||||||
internal_buffer.write_bit(self.segmentation_duration_flag)?;
|
recorder.write_bit(self.segmentation_duration_flag)?;
|
||||||
internal_buffer.write_bit(self.delivery_not_restricted_flag)?;
|
recorder.write_bit(self.delivery_not_restricted_flag)?;
|
||||||
if !self.delivery_not_restricted_flag {
|
if !self.delivery_not_restricted_flag {
|
||||||
internal_buffer.write_bit(self.web_delivery_allowed_flag)?;
|
recorder.write_bit(self.web_delivery_allowed_flag)?;
|
||||||
internal_buffer.write_bit(self.no_regional_blackout_flag)?;
|
recorder.write_bit(self.no_regional_blackout_flag)?;
|
||||||
internal_buffer.write_bit(self.archive_allowed_flag)?;
|
recorder.write_bit(self.archive_allowed_flag)?;
|
||||||
internal_buffer.write(2, self.device_restrictions as u8)?;
|
recorder.write(2, self.device_restrictions as u8)?;
|
||||||
} else {
|
} else {
|
||||||
internal_buffer.write(5, 0x1f)?;
|
recorder.write(5, 0x1f)?;
|
||||||
}
|
}
|
||||||
if !self.program_segmentation_flag {
|
if !self.program_segmentation_flag {
|
||||||
internal_buffer.write(8, self.components.len() as u8)?;
|
recorder.write(8, self.components.len() as u8)?;
|
||||||
for component in &self.components {
|
for component in &self.components {
|
||||||
component.write_to(&mut internal_buffer)?;
|
component.write_to(&mut recorder)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.segmentation_duration_flag {
|
if self.segmentation_duration_flag {
|
||||||
internal_buffer.write(40, self.segmentation_duration)?;
|
recorder.write(40, self.segmentation_duration)?;
|
||||||
}
|
}
|
||||||
internal_buffer.write(8, u8::from(self.segmentation_upid.segmentation_upid_type()))?;
|
recorder.write(8, u8::from(self.segmentation_upid.segmentation_upid_type()))?;
|
||||||
self.segmentation_upid.write_to(&mut internal_buffer)?;
|
self.segmentation_upid.write_to(&mut recorder)?;
|
||||||
internal_buffer.write(8, self.segmentation_type.id())?;
|
recorder.write(8, self.segmentation_type.id())?;
|
||||||
|
|
||||||
let s = self.segmentation_type.syntax();
|
let s = self.segmentation_type.syntax();
|
||||||
match s.segment_num {
|
match s.segment_num {
|
||||||
Fixed(n) => internal_buffer.write(8, n)?,
|
Fixed(n) => recorder.write(8, n)?,
|
||||||
NonZero | ZeroOrNonZero => internal_buffer.write(8, self.segment_num)?, // needs to check for non-zero
|
NonZero | ZeroOrNonZero => recorder.write(8, self.segment_num)?, // needs to check for non-zero
|
||||||
NotUsed => internal_buffer.write(8, 0u8)?,
|
NotUsed => recorder.write(8, 0u8)?,
|
||||||
}
|
}
|
||||||
match s.segments_expected {
|
match s.segments_expected {
|
||||||
Fixed(n) => internal_buffer.write(8, n)?,
|
Fixed(n) => recorder.write(8, n)?,
|
||||||
NonZero | ZeroOrNonZero => internal_buffer.write(8, self.segments_expected)?, // needs to check for non-zero
|
NonZero | ZeroOrNonZero => recorder.write(8, self.segments_expected)?, // needs to check for non-zero
|
||||||
NotUsed => internal_buffer.write(8, 0u8)?,
|
NotUsed => recorder.write(8, 0u8)?,
|
||||||
}
|
}
|
||||||
match s.sub_segment_num {
|
match s.sub_segment_num {
|
||||||
Fixed(n) => internal_buffer.write(8, n)?,
|
Fixed(n) => recorder.write(8, n)?,
|
||||||
NonZero | ZeroOrNonZero => internal_buffer.write(8, self.sub_segment_num)?, // needs to check for non-zero
|
NonZero | ZeroOrNonZero => recorder.write(8, self.sub_segment_num)?, // needs to check for non-zero
|
||||||
NotUsed => {}
|
NotUsed => {}
|
||||||
}
|
}
|
||||||
match s.sub_segments_expected {
|
match s.sub_segments_expected {
|
||||||
Fixed(n) => internal_buffer.write(8, n)?,
|
Fixed(n) => recorder.write(8, n)?,
|
||||||
NonZero | ZeroOrNonZero => internal_buffer.write(8, self.sub_segments_expected)?, // needs to check for non-zero
|
NonZero | ZeroOrNonZero => recorder.write(8, self.sub_segments_expected)?, // needs to check for non-zero
|
||||||
NotUsed => {}
|
NotUsed => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal_buffer.flush()?;
|
|
||||||
|
|
||||||
let mut buffer = BitWriter::endian(buffer, BigEndian);
|
let mut buffer = BitWriter::endian(buffer, BigEndian);
|
||||||
buffer.write(8, self.splice_descriptor_tag())?;
|
buffer.write(8, self.splice_descriptor_tag())?;
|
||||||
buffer.write(8, data.len() as u8)?;
|
buffer.write(8, recorder.bytes_written() as u8)?;
|
||||||
buffer.write_bytes(data.as_slice())?;
|
recorder.playback(&mut buffer)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -113,7 +181,7 @@ impl SpliceDescriptorExt for SegmentationDescriptor {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
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.
|
||||||
RestrictGroup0 = 0b00,
|
RestrictGroup0 = 0b00,
|
||||||
|
@ -130,6 +198,12 @@ enum DeviceRestrictions {
|
||||||
None = 0b11,
|
None = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for DeviceRestrictions {
|
||||||
|
fn default() -> Self {
|
||||||
|
DeviceRestrictions::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum SegmentationUpidType {
|
pub enum SegmentationUpidType {
|
||||||
|
@ -154,6 +228,12 @@ pub enum SegmentationUpidType {
|
||||||
Reserved(u8),
|
Reserved(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SegmentationUpidType {
|
||||||
|
fn default() -> Self {
|
||||||
|
SegmentationUpidType::NotUsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<SegmentationUpidType> for u8 {
|
impl From<SegmentationUpidType> for u8 {
|
||||||
fn from(s: SegmentationUpidType) -> Self {
|
fn from(s: SegmentationUpidType) -> Self {
|
||||||
use SegmentationUpidType::*;
|
use SegmentationUpidType::*;
|
||||||
|
@ -213,83 +293,103 @@ impl Display for SegmentationUpidType {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum SegmentationUpid {
|
pub enum SegmentationUpid {
|
||||||
NotUsed,
|
NotUsed,
|
||||||
UserDefinedDeprecated,
|
UserDefinedDeprecated(AsciiString),
|
||||||
ISCI,
|
ISCI(AsciiString),
|
||||||
AdID,
|
AdID(AsciiString),
|
||||||
UMID,
|
UMID(AsciiString),
|
||||||
ISANDeprecated,
|
ISANDeprecated(u64),
|
||||||
ISAN,
|
ISAN(u128),
|
||||||
TID,
|
TID(AsciiString),
|
||||||
AiringID(u64),
|
AiringID(u64),
|
||||||
ADI,
|
ADI(AsciiString),
|
||||||
EIDR,
|
EIDR(u128),
|
||||||
ATSCContentIdentifier,
|
ATSCContentIdentifier(AsciiString),
|
||||||
MPU,
|
MPU,
|
||||||
MID,
|
MID,
|
||||||
ADSInformation,
|
ADSInformation(AsciiString),
|
||||||
URI,
|
URI(AsciiString),
|
||||||
UUID,
|
UUID(u128),
|
||||||
SCR,
|
SCR(AsciiString),
|
||||||
Reserved(u8),
|
Reserved(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SegmentationUpid {
|
||||||
|
fn default() -> Self {
|
||||||
|
SegmentationUpid::NotUsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SegmentationUpid {
|
impl SegmentationUpid {
|
||||||
pub fn segmentation_upid_type(&self) -> SegmentationUpidType {
|
pub fn segmentation_upid_type(&self) -> SegmentationUpidType {
|
||||||
use SegmentationUpid::*;
|
use SegmentationUpid::*;
|
||||||
match self {
|
match self {
|
||||||
NotUsed => SegmentationUpidType::NotUsed,
|
NotUsed => SegmentationUpidType::NotUsed,
|
||||||
UserDefinedDeprecated => SegmentationUpidType::UserDefinedDeprecated,
|
UserDefinedDeprecated(_) => SegmentationUpidType::UserDefinedDeprecated,
|
||||||
ISCI => SegmentationUpidType::ISCI,
|
ISCI(_) => SegmentationUpidType::ISCI,
|
||||||
AdID => SegmentationUpidType::AdID,
|
AdID(_) => SegmentationUpidType::AdID,
|
||||||
UMID => SegmentationUpidType::UMID,
|
UMID(_) => SegmentationUpidType::UMID,
|
||||||
ISANDeprecated => SegmentationUpidType::ISANDeprecated,
|
ISANDeprecated(_) => SegmentationUpidType::ISANDeprecated,
|
||||||
ISAN => SegmentationUpidType::ISAN,
|
ISAN(_) => SegmentationUpidType::ISAN,
|
||||||
TID => SegmentationUpidType::TID,
|
TID(_) => SegmentationUpidType::TID,
|
||||||
AiringID(_) => SegmentationUpidType::AiringID,
|
AiringID(_) => SegmentationUpidType::AiringID,
|
||||||
ADI => SegmentationUpidType::ADI,
|
ADI(_) => SegmentationUpidType::ADI,
|
||||||
EIDR => SegmentationUpidType::EIDR,
|
EIDR(_) => SegmentationUpidType::EIDR,
|
||||||
ATSCContentIdentifier => SegmentationUpidType::ATSCContentIdentifier,
|
ATSCContentIdentifier(_) => SegmentationUpidType::ATSCContentIdentifier,
|
||||||
MPU => SegmentationUpidType::MPU,
|
MPU => SegmentationUpidType::MPU,
|
||||||
MID => SegmentationUpidType::MID,
|
MID => SegmentationUpidType::MID,
|
||||||
ADSInformation => SegmentationUpidType::ADSInformation,
|
ADSInformation(_) => SegmentationUpidType::ADSInformation,
|
||||||
URI => SegmentationUpidType::URI,
|
URI(_) => SegmentationUpidType::URI,
|
||||||
UUID => SegmentationUpidType::UUID,
|
UUID(_) => SegmentationUpidType::UUID,
|
||||||
SCR => SegmentationUpidType::SCR,
|
SCR(_) => SegmentationUpidType::SCR,
|
||||||
Reserved(r) => SegmentationUpidType::Reserved(*r),
|
Reserved(r) => SegmentationUpidType::Reserved(*r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to<W>(&self, out: &mut BitWriter<W, BigEndian>) -> io::Result<()>
|
fn write_to(&self, out: &mut BitRecorder<u32, BigEndian>) -> anyhow::Result<()> {
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
use SegmentationUpid::*;
|
use SegmentationUpid::*;
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut recorder = BitRecorder::<u32, BigEndian>::new();
|
||||||
let mut buffer = BitWriter::endian(&mut data, BigEndian);
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
// URI(uri) => {
|
AiringID(v) | ISANDeprecated(v) => {
|
||||||
// let raw_value = CStr::from_bytes_with_nul("https://link.com".as_bytes()).unwrap();
|
// 8 byes is 64 bits
|
||||||
// buffer.write_bytes(raw_value.to_bytes())?;
|
recorder.write(64, *v)?
|
||||||
// }
|
|
||||||
AiringID(aid) => {
|
|
||||||
// 8 bytes is 64 bits
|
|
||||||
buffer.write(64, *aid)?;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
ISAN(value) | EIDR(value) | UUID(value) => {
|
||||||
|
recorder.write_bytes(value.to_be_bytes().as_slice())?
|
||||||
|
}
|
||||||
|
UserDefinedDeprecated(value)
|
||||||
|
| ADI(value)
|
||||||
|
| ATSCContentIdentifier(value)
|
||||||
|
| ADSInformation(value)
|
||||||
|
| URI(value)
|
||||||
|
| SCR(value) => recorder.write_bytes(value.as_bytes())?,
|
||||||
|
ISCI(v) => {
|
||||||
|
let buf = v.as_bytes().iter().take(8).copied().collect::<Vec<_>>();
|
||||||
|
recorder.write_bytes(buf.as_slice())?;
|
||||||
|
}
|
||||||
|
AdID(v) | TID(v) => {
|
||||||
|
let buf = v.as_bytes().iter().take(12).copied().collect::<Vec<_>>();
|
||||||
|
recorder.write_bytes(buf.as_slice())?;
|
||||||
|
}
|
||||||
|
UMID(v) => {
|
||||||
|
let buf = v.as_bytes().iter().take(32).copied().collect::<Vec<_>>();
|
||||||
|
recorder.write_bytes(buf.as_slice())?;
|
||||||
|
}
|
||||||
|
MPU => todo!("Needs to implement MPU() record"),
|
||||||
|
MID => todo!("Needs to implement MID() record"),
|
||||||
|
NotUsed => {}
|
||||||
|
Reserved(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.flush()?;
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
// All variants with variable length use the same write logic
|
NotUsed | Reserved(_) => {
|
||||||
UserDefinedDeprecated | URI | AiringID(_) => {
|
out.write(8, 0u8)?;
|
||||||
out.write(8, data.len() as u8)?;
|
|
||||||
out.write_bytes(data.as_slice())?;
|
|
||||||
}
|
}
|
||||||
|
// All variants with any contained value use the same logic
|
||||||
_ => {
|
_ => {
|
||||||
out.write(8, 0x0)?;
|
out.write(8, recorder.bytes_written() as u8)?;
|
||||||
|
recorder.playback(out)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,13 +405,10 @@ struct Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component {
|
impl Component {
|
||||||
fn write_to<W>(&self, buffer: &mut BitWriter<W, BigEndian>) -> io::Result<()>
|
fn write_to(&self, recorder: &mut BitRecorder<u32, BigEndian>) -> io::Result<()> {
|
||||||
where
|
recorder.write(8, self.component_tag)?;
|
||||||
W: io::Write,
|
recorder.write(7, 0x7f)?;
|
||||||
{
|
recorder.write(33, self.pts_offset)
|
||||||
buffer.write(8, self.component_tag)?;
|
|
||||||
buffer.write(7, 0x7f)?;
|
|
||||||
buffer.write(33, self.pts_offset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,6 +464,12 @@ pub enum SegmentationType {
|
||||||
NetworkEnd,
|
NetworkEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SegmentationType {
|
||||||
|
fn default() -> Self {
|
||||||
|
SegmentationType::NotIndicated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SegmentationType {
|
impl SegmentationType {
|
||||||
fn id(&self) -> u8 {
|
fn id(&self) -> u8 {
|
||||||
use SegmentationType::*;
|
use SegmentationType::*;
|
||||||
|
@ -787,12 +890,15 @@ mod tests {
|
||||||
fn write_segmentation_upid_airing_id() -> Result<()> {
|
fn write_segmentation_upid_airing_id() -> Result<()> {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let mut buffer = BitWriter::endian(&mut data, BigEndian);
|
let mut buffer = BitWriter::endian(&mut data, BigEndian);
|
||||||
|
let mut recorder = BitRecorder::<u32, BigEndian>::new();
|
||||||
|
|
||||||
let segmentation_upid = SegmentationUpid::AiringID(0x2ca0a18a);
|
let segmentation_upid = SegmentationUpid::AiringID(0x2ca0a18a);
|
||||||
segmentation_upid.write_to(&mut buffer)?;
|
segmentation_upid.write_to(&mut recorder)?;
|
||||||
|
|
||||||
|
recorder.playback(&mut buffer)?;
|
||||||
|
|
||||||
// length (1 byte) + data (8 bytes)
|
// length (1 byte) + data (8 bytes)
|
||||||
assert_eq!(data.len(), 9);
|
assert_eq!(recorder.bytes_written(), 9);
|
||||||
|
|
||||||
let hex = hex::encode(data[1..].to_vec());
|
let hex = hex::encode(data[1..].to_vec());
|
||||||
assert_eq!(hex, "000000002ca0a18a".to_string());
|
assert_eq!(hex, "000000002ca0a18a".to_string());
|
||||||
|
@ -803,26 +909,20 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn write_segmentation_descriptor() -> Result<()> {
|
fn write_segmentation_descriptor() -> Result<()> {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let segmentation_descriptor = SegmentationDescriptor {
|
let mut descriptor = SegmentationDescriptor::default();
|
||||||
segmentation_event_id: 0x4800008e,
|
descriptor.set_segmentation_event_id(0x4800008e);
|
||||||
segmentation_event_cancel_indicator: false,
|
descriptor.set_program_segmentation_flag(true);
|
||||||
program_segmentation_flag: true,
|
descriptor.set_segmentation_duration_flag(true);
|
||||||
segmentation_duration_flag: true,
|
descriptor.set_no_regional_blackout_flag(true);
|
||||||
delivery_not_restricted_flag: false,
|
descriptor.set_archive_allowed_flag(true);
|
||||||
web_delivery_allowed_flag: false,
|
descriptor.set_segmentation_duration(27630000);
|
||||||
no_regional_blackout_flag: true,
|
descriptor.set_segmentation_upid(SegmentationUpid::AiringID(0x2ca0a18a));
|
||||||
archive_allowed_flag: true,
|
descriptor.set_segmentation_type(SegmentationType::ProviderPlacementOpportunityStart);
|
||||||
device_restrictions: DeviceRestrictions::None,
|
descriptor.set_segment_num(2);
|
||||||
components: vec![],
|
descriptor.set_sub_segment_num(154);
|
||||||
segmentation_duration: 27630000,
|
descriptor.set_sub_segments_expected(201);
|
||||||
segmentation_upid: SegmentationUpid::AiringID(0x2ca0a18a),
|
|
||||||
segmentation_type: SegmentationType::ProviderPlacementOpportunityStart,
|
descriptor.write_to(&mut data)?;
|
||||||
segment_num: 2,
|
|
||||||
segments_expected: 0,
|
|
||||||
sub_segment_num: 154,
|
|
||||||
sub_segments_expected: 201,
|
|
||||||
};
|
|
||||||
segmentation_descriptor.write_to(&mut data)?;
|
|
||||||
|
|
||||||
let hex = hex::encode(data.as_slice());
|
let hex = hex::encode(data.as_slice());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
54
src/info.rs
54
src/info.rs
|
@ -340,7 +340,7 @@ mod serde_serialization {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::commands::*;
|
use crate::commands::*;
|
||||||
use crate::descriptors::SegmentationDescriptor;
|
use crate::descriptors::{SegmentationDescriptor, SegmentationType, SegmentationUpid};
|
||||||
use crate::ClockTimeExt;
|
use crate::ClockTimeExt;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assert_json_diff::assert_json_eq;
|
use assert_json_diff::assert_json_eq;
|
||||||
|
@ -374,11 +374,25 @@ mod tests {
|
||||||
fn compliance_spec_14_1_example_time_signal_as_base64() -> Result<()> {
|
fn compliance_spec_14_1_example_time_signal_as_base64() -> Result<()> {
|
||||||
let mut splice = SpliceInfoSection::new(TimeSignal::from_ticks(0x072bd0050));
|
let mut splice = SpliceInfoSection::new(TimeSignal::from_ticks(0x072bd0050));
|
||||||
splice.set_cw_index(0xff);
|
splice.set_cw_index(0xff);
|
||||||
// splice.add_descriptor(SegmentationDescriptor::new().into());
|
|
||||||
|
let mut descriptor = SegmentationDescriptor::default();
|
||||||
|
descriptor.set_segmentation_event_id(0x4800008e);
|
||||||
|
descriptor.set_program_segmentation_flag(true);
|
||||||
|
descriptor.set_segmentation_duration_flag(true);
|
||||||
|
descriptor.set_no_regional_blackout_flag(true);
|
||||||
|
descriptor.set_archive_allowed_flag(true);
|
||||||
|
descriptor.set_segmentation_duration(27630000);
|
||||||
|
descriptor.set_segmentation_upid(SegmentationUpid::AiringID(0x2ca0a18a));
|
||||||
|
descriptor.set_segmentation_type(SegmentationType::ProviderPlacementOpportunityStart);
|
||||||
|
descriptor.set_segment_num(2);
|
||||||
|
descriptor.set_sub_segment_num(154);
|
||||||
|
descriptor.set_sub_segments_expected(201);
|
||||||
|
|
||||||
|
splice.add_descriptor(descriptor.into());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
splice.into_encoded()?.to_base64(),
|
splice.into_encoded()?.to_base64(),
|
||||||
"/DA0AAAAAAAA///wBQb+cr0AUAAeAhxDVUVJSAAAjn/PAAGlmbAICAAAAAAsoKGKNAIAmsnRfg=="
|
"/DA2AAAAAAAA///wBQb+cr0AUAAgAh5DVUVJSAAAjn/PAAGlmbAICAAAAAAsoKGKNAIAmsm2waDx"
|
||||||
.to_string()
|
.to_string()
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -388,11 +402,25 @@ mod tests {
|
||||||
fn compliance_spec_14_1_example_time_signal_as_hex() -> Result<()> {
|
fn compliance_spec_14_1_example_time_signal_as_hex() -> Result<()> {
|
||||||
let mut splice = SpliceInfoSection::new(TimeSignal::from_ticks(0x072bd0050));
|
let mut splice = SpliceInfoSection::new(TimeSignal::from_ticks(0x072bd0050));
|
||||||
splice.set_cw_index(0xff);
|
splice.set_cw_index(0xff);
|
||||||
// splice.add_descriptor(SegmentationDescriptor::new().into());
|
|
||||||
|
let mut descriptor = SegmentationDescriptor::default();
|
||||||
|
descriptor.set_segmentation_event_id(0x4800008e);
|
||||||
|
descriptor.set_program_segmentation_flag(true);
|
||||||
|
descriptor.set_segmentation_duration_flag(true);
|
||||||
|
descriptor.set_no_regional_blackout_flag(true);
|
||||||
|
descriptor.set_archive_allowed_flag(true);
|
||||||
|
descriptor.set_segmentation_duration(27630000);
|
||||||
|
descriptor.set_segmentation_upid(SegmentationUpid::AiringID(0x2ca0a18a));
|
||||||
|
descriptor.set_segmentation_type(SegmentationType::ProviderPlacementOpportunityStart);
|
||||||
|
descriptor.set_segment_num(2);
|
||||||
|
descriptor.set_sub_segment_num(154);
|
||||||
|
descriptor.set_sub_segments_expected(201);
|
||||||
|
|
||||||
|
splice.add_descriptor(descriptor.into());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
splice.into_encoded()?.to_hex(),
|
splice.into_encoded()?.to_hex(),
|
||||||
"0xfc3034000000000000fffff00506fe72bd0050001e021c435545494800008e7fcf0001a599b00808000000002ca0a18a3402009ac9d17e".to_string()
|
"0xfc3036000000000000fffff00506fe72bd00500020021e435545494800008e7fcf0001a599b00808000000002ca0a18a3402009ac9b6c1a0f1".to_string()
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -402,7 +430,21 @@ mod tests {
|
||||||
fn compliance_spec_14_1_example_time_signal_as_json() -> Result<()> {
|
fn compliance_spec_14_1_example_time_signal_as_json() -> Result<()> {
|
||||||
let mut splice = SpliceInfoSection::new(TimeSignal::from_ticks(0x072bd0050));
|
let mut splice = SpliceInfoSection::new(TimeSignal::from_ticks(0x072bd0050));
|
||||||
splice.set_cw_index(0xff);
|
splice.set_cw_index(0xff);
|
||||||
// splice.add_descriptor(SegmentationDescriptor::new().into());
|
|
||||||
|
let mut descriptor = SegmentationDescriptor::default();
|
||||||
|
descriptor.set_segmentation_event_id(0x4800008e);
|
||||||
|
descriptor.set_program_segmentation_flag(true);
|
||||||
|
descriptor.set_segmentation_duration_flag(true);
|
||||||
|
descriptor.set_no_regional_blackout_flag(true);
|
||||||
|
descriptor.set_archive_allowed_flag(true);
|
||||||
|
descriptor.set_segmentation_duration(27630000);
|
||||||
|
descriptor.set_segmentation_upid(SegmentationUpid::AiringID(0x2ca0a18a));
|
||||||
|
descriptor.set_segmentation_type(SegmentationType::ProviderPlacementOpportunityStart);
|
||||||
|
descriptor.set_segment_num(2);
|
||||||
|
descriptor.set_sub_segment_num(154);
|
||||||
|
descriptor.set_sub_segments_expected(201);
|
||||||
|
|
||||||
|
splice.add_descriptor(descriptor.into());
|
||||||
|
|
||||||
assert_json_eq!(
|
assert_json_eq!(
|
||||||
serde_json::to_value(&splice.into_encoded()?)?,
|
serde_json::to_value(&splice.into_encoded()?)?,
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use bitstream_io::{BigEndian, BitRecorder};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -40,6 +41,17 @@ fn ticks_to_secs(value: u64) -> f64 {
|
||||||
(value as f64 / 90_000.0 * 1_000_000.0).ceil() as f64 / 1_000_000.0
|
(value as f64 / 90_000.0 * 1_000_000.0).ceil() as f64 / 1_000_000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait BytesWritten {
|
||||||
|
fn bytes_written(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytesWritten for BitRecorder<u32, BigEndian> {
|
||||||
|
#[inline]
|
||||||
|
fn bytes_written(&self) -> u32 {
|
||||||
|
self.written() / 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
fn serialize_time<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize_time<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|
Loading…
Reference in a new issue