Segmentation descriptor with upid types

This commit is contained in:
Rafael Caricio 2022-05-03 13:40:39 +02:00
parent 1f1ee312c0
commit fa917dafce
Signed by: rafaelcaricio
GPG key ID: 3C86DBCE8E93C947
6 changed files with 284 additions and 126 deletions

View file

@ -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"

View file

@ -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

View file

@ -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,

View file

@ -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!(

View file

@ -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()?)?,

View file

@ -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