Serialize TimeSignal with additional fields

Move all code related to the "serde" feature to a separated module. Add
missing getter methods to some structs. Add missing derive to public
structs. Makes all tests pass.

Signed-off-by: Rafael Caricio <rafael@caricio.com>
This commit is contained in:
Rafael Caricio 2022-05-06 23:56:17 +02:00
parent 5d72027943
commit 501e060979
Signed by: rafaelcaricio
GPG key ID: 3C86DBCE8E93C947
7 changed files with 808 additions and 741 deletions

View file

@ -1,24 +1,29 @@
use crate::time::SpliceTime;
use crate::{ClockTimeExt, CueError, TransportPacketWrite};
use bitstream_io::{BigEndian, BitWrite, BitWriter};
use crate::ClockTimeExt;
use std::io;
use std::io::Write;
use std::ops::{Deref, DerefMut};
use std::time::Duration;
#[cfg(feature = "serde")]
use serde::Serialize;
pub trait SpliceCommand: TransportPacketWrite {
pub trait SpliceCommand {
fn splice_command_type(&self) -> SpliceCommandType;
fn write_to<W>(&mut self, buffer: &mut W) -> anyhow::Result<u32>
where
W: io::Write;
}
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct SpliceNull {}
impl TransportPacketWrite for SpliceNull {
fn write_to<W>(&self, _: &mut W) -> anyhow::Result<u32>
impl SpliceCommand for SpliceNull {
fn splice_command_type(&self) -> SpliceCommandType {
SpliceCommandType::SpliceNull
}
fn write_to<W>(&mut self, _: &mut W) -> anyhow::Result<u32>
where
W: io::Write,
{
@ -26,21 +31,12 @@ impl TransportPacketWrite for SpliceNull {
}
}
impl SpliceCommand for SpliceNull {
fn splice_command_type(&self) -> SpliceCommandType {
SpliceCommandType::SpliceNull
}
}
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(transparent))]
#[repr(transparent)]
pub struct TimeSignal(SpliceTime);
impl TimeSignal {
pub fn new() -> Self {
TimeSignal(SpliceTime::new())
}
pub fn set_pts<T>(&mut self, pts: Option<T>)
where
T: ClockTimeExt,
@ -49,9 +45,12 @@ impl TimeSignal {
}
}
impl TransportPacketWrite for TimeSignal {
#[inline]
fn write_to<W>(&self, buffer: &mut W) -> anyhow::Result<u32>
impl SpliceCommand for TimeSignal {
fn splice_command_type(&self) -> SpliceCommandType {
SpliceCommandType::TimeSignal
}
fn write_to<W>(&mut self, buffer: &mut W) -> anyhow::Result<u32>
where
W: Write,
{
@ -59,18 +58,12 @@ impl TransportPacketWrite for TimeSignal {
}
}
impl SpliceCommand for TimeSignal {
fn splice_command_type(&self) -> SpliceCommandType {
SpliceCommandType::TimeSignal
}
}
impl<T> From<T> for TimeSignal
where
T: ClockTimeExt,
{
fn from(pts: T) -> Self {
let mut t = Self::new();
let mut t = Self::default();
t.set_pts(Some(pts));
t
}
@ -115,32 +108,3 @@ impl From<SpliceCommandType> for u8 {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use assert_json_diff::assert_json_eq;
#[cfg(feature = "serde")]
#[test]
fn serialize_splice_null() -> Result<()> {
let splice_null = SpliceNull::default();
assert_json_eq!(serde_json::to_value(&splice_null)?, serde_json::json!({}));
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn serialize_time_signal() -> Result<()> {
let time_signal = TimeSignal::new();
assert_json_eq!(
serde_json::to_value(&time_signal)?,
serde_json::json!({
"time_specified_flag": false,
"pts_time": 0.0
})
);
Ok(())
}
}

View file

@ -1,12 +1,8 @@
mod segmentation;
use crate::{CueError, TransportPacketWrite};
pub use segmentation::*;
use std::io;
#[cfg(feature = "serde")]
use serde::Serialize;
#[derive(Debug, Clone, PartialEq)]
pub enum SpliceDescriptor {
Avail,
@ -16,32 +12,6 @@ pub enum SpliceDescriptor {
Audio,
Unknown(u8, u32, Vec<u8>),
}
#[cfg(feature = "serde")]
mod serde_serialization {
use super::*;
use serde::ser::{Error, Serialize, SerializeStruct, Serializer};
impl Serialize for SpliceDescriptor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use SpliceDescriptor::*;
match self {
Segmentation(seg) => seg.serialize(serializer),
Unknown(tag, len, data) => {
let mut struc = serializer.serialize_struct("SpliceDescriptor", 3)?;
struc.serialize_field("tag", &format!("0x{:x}", tag))?;
struc.serialize_field("length", &len)?;
struc.serialize_field("data", &format!("0x{}", hex::encode(data).as_str()))?;
struc.end()
}
// TODO: add other descriptors
_ => serializer.serialize_str(&format!("{:?}", self)),
}
}
}
}
pub(crate) trait SpliceDescriptorExt {
fn splice_descriptor_tag(&self) -> u8;

View file

@ -1,9 +1,6 @@
use crate::{BytesWritten, ClockTimeExt, CueError, TransportPacketWrite};
use anyhow::Context;
use crate::{BytesWritten, ClockTimeExt};
use ascii::AsciiString;
use bitstream_io::{BigEndian, BitRecorder, BitWrite, BitWriter};
use std::ffi::CStr;
use std::io::Write;
use std::{fmt, io};
use crate::descriptors::{SpliceDescriptorExt, SpliceDescriptorTag};
@ -33,191 +30,6 @@ pub struct SegmentationDescriptor {
descriptor_length: Option<u8>,
}
#[cfg(feature = "serde")]
mod serde_serialization {
use super::*;
use crate::ticks_to_secs;
use crate::time::format_duration;
use ascii::AsciiStr;
use serde::ser::{Error, Serialize, SerializeStruct, Serializer};
use std::fmt::{format, LowerHex};
use std::time::Duration;
impl Serialize for SegmentationDescriptor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use SegmentationFieldSyntax::*;
#[inline]
fn as_hex<T>(value: T) -> String
where
T: LowerHex,
{
format!("0x{:02x}", value)
}
let segmentation_syntax = self.segmentation_type.syntax();
// predict number of fields in the struct
let mut num_fields = 6;
if !self.segmentation_event_cancel_indicator {
num_fields += 12;
if !self.delivery_not_restricted_flag {
num_fields += 4;
}
if self.segmentation_duration_flag {
num_fields += 2;
}
match segmentation_syntax.sub_segment_num {
Fixed(_) | NonZero | ZeroOrNonZero => {
num_fields += 1;
}
NotUsed => {}
}
match segmentation_syntax.sub_segments_expected {
Fixed(_) | NonZero | ZeroOrNonZero => {
num_fields += 1;
}
NotUsed => {}
}
}
let mut state = serializer.serialize_struct("SegmentationDescriptor", num_fields)?;
state.serialize_field("name", "Segmentation Descriptor")?;
state.serialize_field(
"splice_descriptor_tag",
&as_hex(self.splice_descriptor_tag()),
)?;
state.serialize_field("descriptor_length", &self.descriptor_length)?;
let id = self.identifier().to_be_bytes();
state.serialize_field(
"identifier",
AsciiStr::from_ascii(id.as_slice())
.expect("ascii characters")
.as_str(),
)?;
state.serialize_field("segmentation_event_id", &as_hex(self.segmentation_event_id))?;
state.serialize_field(
"segmentation_event_cancel_indicator",
&self.segmentation_event_cancel_indicator,
)?;
if !self.segmentation_event_cancel_indicator {
state.serialize_field(
"program_segmentation_flag",
&self.program_segmentation_flag,
)?;
state.serialize_field(
"segmentation_duration_flag",
&self.segmentation_duration_flag,
)?;
state.serialize_field(
"delivery_not_restricted_flag",
&self.delivery_not_restricted_flag,
)?;
if !self.delivery_not_restricted_flag {
state.serialize_field(
"web_delivery_allowed_flag",
&self.web_delivery_allowed_flag,
)?;
state.serialize_field(
"no_regional_blackout_flag",
&self.no_regional_blackout_flag,
)?;
state.serialize_field("archive_allowed_flag", &self.archive_allowed_flag)?;
state.serialize_field("device_restrictions", &self.device_restrictions)?;
}
state.serialize_field("components", &self.components)?;
if self.segmentation_duration_flag {
let duration_secs = ticks_to_secs(self.segmentation_duration);
state.serialize_field("segmentation_duration", &self.segmentation_duration)?;
state.serialize_field("segmentation_duration_secs", &duration_secs)?;
state.serialize_field(
"segmentation_duration_human",
&format_duration(Duration::from_secs_f64(duration_secs)).to_string(),
)?;
}
state.serialize_field(
"segmentation_upid_type",
&as_hex(u8::from(self.segmentation_upid.segmentation_upid_type())),
)?;
state.serialize_field(
"segmentation_upid_type_name",
&format!("{}", self.segmentation_upid.segmentation_upid_type()),
)?;
state.serialize_field(
"segmentation_upid_length",
&self.segmentation_upid.segmentation_upid_length(),
)?;
state.serialize_field("segmentation_upid", &self.segmentation_upid)?;
state.serialize_field(
"segmentation_message",
&format!("{}", self.segmentation_type),
)?;
state.serialize_field("segmentation_type_id", &self.segmentation_type.id())?;
state.serialize_field("segment_num", &self.segment_num)?;
state.serialize_field("segments_expected", &self.segments_expected)?;
match segmentation_syntax.sub_segment_num {
Fixed(v) => {
state.serialize_field("sub_segment_num", &v)?;
}
NonZero | ZeroOrNonZero => {
state.serialize_field("sub_segment_num", &self.sub_segment_num)?;
}
NotUsed => {}
}
match segmentation_syntax.sub_segments_expected {
Fixed(v) => {
state.serialize_field("sub_segments_expected", &v)?;
}
NonZero | ZeroOrNonZero => {
state.serialize_field(
"sub_segments_expected",
&self.sub_segments_expected,
)?;
}
NotUsed => {}
}
}
state.end()
}
}
impl Serialize for SegmentationUpid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use SegmentationUpid::*;
let mut recorder = BitRecorder::<u32, BigEndian>::new();
self.write_to(&mut recorder)
.map_err(|err| S::Error::custom(format!("{}", err)))?;
let mut data = Vec::new();
let mut buffer = BitWriter::endian(&mut data, BigEndian);
recorder
.playback(&mut buffer)
.map_err(|err| S::Error::custom(format!("{}", err)))?;
// TODO: serialize as struct when variant is MPU and MID
match self {
// if field is represented as a character, then show with textual representation
ISCI(v) | AdID(v) | TID(v) | ADSInformation(v) | URI(v) | SCR(v) => {
serializer.serialize_str(v.as_str())
}
// if field is represented as a number, then show as hex
ISAN(v) | EIDR(v) | UUID(v) => serializer.serialize_str(&format!("0x{:x}", v)),
ISANDeprecated(v) | AiringID(v) => serializer.serialize_str(&format!("0x{:x}", v)),
// everything else show as hex, we skip the first byte (which is the length)
_ => serializer.serialize_str(&format!("0x{}", hex::encode(&data[1..]))),
}
}
}
}
impl SegmentationDescriptor {
pub fn set_segmentation_event_id(&mut self, segmentation_event_id: u32) {
self.segmentation_event_id = segmentation_event_id;
@ -287,6 +99,78 @@ impl SegmentationDescriptor {
self.sub_segments_expected = sub_segments_expected;
}
pub fn segmentation_event_id(&self) -> u32 {
self.segmentation_event_id
}
pub fn segmentation_event_cancel_indicator(&self) -> bool {
self.segmentation_event_cancel_indicator
}
pub fn program_segmentation_flag(&self) -> bool {
self.program_segmentation_flag
}
pub fn segmentation_duration_flag(&self) -> bool {
self.segmentation_duration_flag
}
pub fn delivery_not_restricted_flag(&self) -> bool {
self.delivery_not_restricted_flag
}
pub fn web_delivery_allowed_flag(&self) -> bool {
self.web_delivery_allowed_flag
}
pub fn no_regional_blackout_flag(&self) -> bool {
self.no_regional_blackout_flag
}
pub fn archive_allowed_flag(&self) -> bool {
self.archive_allowed_flag
}
pub fn device_restrictions(&self) -> DeviceRestrictions {
self.device_restrictions
}
pub fn components(&self) -> &Vec<Component> {
&self.components
}
pub fn segmentation_duration(&self) -> u64 {
self.segmentation_duration
}
pub fn segmentation_upid(&self) -> &SegmentationUpid {
&self.segmentation_upid
}
pub fn segmentation_type(&self) -> SegmentationType {
self.segmentation_type
}
pub fn segment_num(&self) -> u8 {
self.segment_num
}
pub fn segments_expected(&self) -> u8 {
self.segments_expected
}
pub fn sub_segment_num(&self) -> u8 {
self.sub_segment_num
}
pub fn sub_segments_expected(&self) -> u8 {
self.sub_segments_expected
}
pub fn descriptor_length(&self) -> Option<u8> {
self.descriptor_length
}
pub(crate) fn write_to<W>(&mut self, buffer: &mut W) -> anyhow::Result<u32>
where
W: io::Write,
@ -535,17 +419,17 @@ impl SegmentationUpid {
}
}
fn segmentation_upid_length(&self) -> u8 {
pub fn segmentation_upid_length(&self) -> u8 {
use SegmentationUpid::*;
match self {
NotUsed => 0,
UserDefinedDeprecated(s) => s.len() as u8,
ISCI(s) => 8,
AdID(s) => 12,
UMID(s) => 32,
ISCI(_) => 8,
AdID(_) => 12,
UMID(_) => 32,
ISANDeprecated(_) => 8,
ISAN(_) => 12,
TID(s) => 8,
TID(_) => 8,
AiringID(_) => 8,
ADI(s) => s.len() as u8,
EIDR(_) => 12,
@ -560,7 +444,7 @@ impl SegmentationUpid {
}
}
fn write_to(&self, out: &mut BitRecorder<u32, BigEndian>) -> anyhow::Result<()> {
pub(crate) fn write_to(&self, out: &mut BitRecorder<u32, BigEndian>) -> anyhow::Result<()> {
use SegmentationUpid::*;
let mut recorder = BitRecorder::<u32, BigEndian>::new();
@ -614,13 +498,13 @@ impl SegmentationUpid {
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
struct Component {
pub struct Component {
component_tag: u8,
pts_offset: u64,
}
impl Component {
fn write_to(&self, recorder: &mut BitRecorder<u32, BigEndian>) -> io::Result<()> {
pub(crate) fn write_to(&self, recorder: &mut BitRecorder<u32, BigEndian>) -> io::Result<()> {
recorder.write(8, self.component_tag)?;
recorder.write(7, 0x7f)?;
recorder.write(33, self.pts_offset)
@ -686,7 +570,7 @@ impl Default for SegmentationType {
}
impl SegmentationType {
fn id(&self) -> u8 {
pub fn id(&self) -> u8 {
use SegmentationType::*;
match self {
NotIndicated => 0x00,
@ -739,7 +623,7 @@ impl SegmentationType {
}
/// Reflects definitions on the Table 23 of the spec.
fn syntax(&self) -> SegmentationTypeSyntax {
pub fn syntax(&self) -> SegmentationTypeSyntax {
use SegmentationFieldSyntax::*;
use SegmentationType::*;
@ -1147,26 +1031,46 @@ impl TryFrom<u8> for SegmentationType {
}
}
enum SegmentationFieldSyntax {
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum SegmentationFieldSyntax {
Fixed(u8),
ZeroOrNonZero,
NonZero,
NotUsed,
}
struct SegmentationTypeSyntax {
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct SegmentationTypeSyntax {
segment_num: SegmentationFieldSyntax,
segments_expected: SegmentationFieldSyntax,
sub_segment_num: SegmentationFieldSyntax,
sub_segments_expected: SegmentationFieldSyntax,
}
impl SegmentationTypeSyntax {
pub fn segment_num(&self) -> SegmentationFieldSyntax {
self.segment_num
}
pub fn segments_expected(&self) -> SegmentationFieldSyntax {
self.segments_expected
}
pub fn sub_segment_num(&self) -> SegmentationFieldSyntax {
self.sub_segment_num
}
pub fn sub_segments_expected(&self) -> SegmentationFieldSyntax {
self.sub_segments_expected
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use assert_json_diff::assert_json_eq;
use std::time::Duration;
#[test]
fn write_segmentation_upid_airing_id() -> Result<()> {
@ -1214,87 +1118,4 @@ mod tests {
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn serialize_segmentation_to_json() -> Result<()> {
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(Duration::from_secs_f32(307.0));
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);
// We need to write to calculate the length
let mut data = Vec::new();
descriptor.write_to(&mut data)?;
let expected_json: serde_json::Value = serde_json::from_str(
r#"{
"name": "Segmentation Descriptor",
"splice_descriptor_tag": "0x02",
"descriptor_length": 30,
"identifier": "CUEI",
"segmentation_event_id": "0x4800008e",
"segmentation_event_cancel_indicator": false,
"program_segmentation_flag": true,
"segmentation_duration_flag": true,
"delivery_not_restricted_flag": false,
"web_delivery_allowed_flag": false,
"no_regional_blackout_flag": true,
"archive_allowed_flag": true,
"device_restrictions": "None",
"components": [],
"segmentation_duration": 27630000,
"segmentation_duration_secs": 307.0,
"segmentation_duration_human": "5m 7s",
"segmentation_upid_type": "0x08",
"segmentation_upid_type_name": "AiringID",
"segmentation_upid_length": 8,
"segmentation_upid": "0x2ca0a18a",
"segmentation_message": "Provider Placement Opportunity Start",
"segmentation_type_id": 52,
"segment_num": 2,
"segments_expected": 0,
"sub_segment_num": 154,
"sub_segments_expected": 201
}"#,
)?;
assert_json_eq!(serde_json::to_value(&descriptor)?, expected_json);
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn serialize_segmentation_with_cancel_indicator_to_json() -> Result<()> {
let mut descriptor = SegmentationDescriptor::default();
descriptor.set_segmentation_event_id(0x4800008e);
descriptor.set_segmentation_event_cancel_indicator(true);
// We need to write to calculate the length
let mut data = Vec::new();
descriptor.write_to(&mut data)?;
let expected_json: serde_json::Value = serde_json::from_str(
r#"{
"name": "Segmentation Descriptor",
"splice_descriptor_tag": "0x02",
"descriptor_length": 9,
"identifier": "CUEI",
"segmentation_event_id": "0x4800008e",
"segmentation_event_cancel_indicator": true
}"#,
)?;
assert_json_eq!(serde_json::to_value(&descriptor)?, expected_json);
Ok(())
}
}

View file

@ -1,9 +1,7 @@
use crate::commands::{SpliceCommand, SpliceCommandType};
use crate::descriptors::SpliceDescriptor;
use crate::{CueError, TransportPacketWrite};
use bitstream_io::{BigEndian, BitWrite, BitWriter};
use crc::{Crc, CRC_32_MPEG_2};
use std::fmt;
use std::fmt::{Display, Formatter};
pub const MPEG_2: Crc<u32> = Crc::<u32>::new(&CRC_32_MPEG_2);
@ -14,58 +12,58 @@ where
C: SpliceCommand,
S: EncodingState,
{
state: SpliceInfoState<C>,
encoded: S,
pub(crate) state: SpliceInfoState<C>,
pub(crate) encoded: S,
}
#[derive(Debug, Clone, PartialEq)]
struct SpliceInfoState<C>
pub(crate) struct SpliceInfoState<C>
where
C: SpliceCommand,
{
/// This is an 8-bit field. Its value shall be 0xFC.
table_id: u8,
pub(crate) table_id: u8,
/// The section_syntax_indicator is a 1-bit field that should always be set to 0, indicating
/// that MPEG short sections are to be used.
section_syntax_indicator: bool,
pub(crate) section_syntax_indicator: bool,
/// This is a 1-bit flag that shall be set to 0.
private_indicator: bool,
pub(crate) private_indicator: bool,
/// A two-bit field that indicates if the content preparation system has created a Stream
/// Access Point (SAP) at the signaled point in the stream. SAP types are defined in
/// ISO 14496-12, Annex I. The semantics of SAP types are further informatively elaborated
/// in ISO/IEC 23009-1 DASH, Section 4.5.2.
sap_type: SAPType, // 2 bits
pub(crate) sap_type: SAPType, // 2 bits
protocol_version: u8,
encrypted_packet: bool,
encryption_algorithm: EncryptionAlgorithm,
pts_adjustment: u64, // 33 bits
cw_index: u8,
tier: u16, // 12 bits
pub(crate) protocol_version: u8,
pub(crate) encrypted_packet: bool,
pub(crate) encryption_algorithm: EncryptionAlgorithm,
pub(crate) pts_adjustment: u64, // 33 bits
pub(crate) cw_index: u8,
pub(crate) tier: u16, // 12 bits
splice_command: C,
pub(crate) splice_command: C,
descriptors: Vec<SpliceDescriptor>,
pub(crate) descriptors: Vec<SpliceDescriptor>,
}
pub trait EncodingState {}
#[derive(Debug, Clone, Copy, PartialEq)]
struct NotEncoded;
pub(crate) struct NotEncoded;
impl EncodingState for NotEncoded {}
#[derive(Debug, Clone, PartialEq)]
struct EncodedData {
section_length: u16,
splice_command_length: u16,
splice_command_type: SpliceCommandType,
descriptor_loop_length: u16,
crc32: u32,
final_data: Vec<u8>,
pub(crate) struct EncodedData {
pub section_length: u16,
pub splice_command_length: u16,
pub splice_command_type: SpliceCommandType,
pub descriptor_loop_length: u16,
pub crc32: u32,
pub final_data: Vec<u8>,
}
impl EncodingState for EncodedData {}
@ -74,7 +72,7 @@ impl<C> SpliceInfoSection<C, NotEncoded>
where
C: SpliceCommand,
{
fn new(splice_command: C) -> Self {
pub fn new(splice_command: C) -> Self {
Self {
state: SpliceInfoState {
table_id: 0xFC,
@ -277,82 +275,12 @@ impl From<EncryptionAlgorithm> for u8 {
}
}
#[cfg(feature = "serde")]
mod serde_serialization {
use super::*;
use crate::ticks_to_secs;
use crate::time::format_duration;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::fmt::LowerHex;
use std::time::Duration;
impl<C> Serialize for SpliceInfoSection<C, EncodedData>
where
C: SpliceCommand + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[inline]
fn as_hex<T>(value: T) -> String
where
T: LowerHex,
{
format!("0x{:x}", value)
}
let mut state = serializer.serialize_struct("SpliceInfoSection", 19)?;
state.serialize_field("table_id", &as_hex(self.state.table_id))?;
state.serialize_field(
"section_syntax_indicator",
&self.state.section_syntax_indicator,
)?;
state.serialize_field("private_indicator", &self.state.private_indicator)?;
state.serialize_field("sap_type", &as_hex(self.state.sap_type as u8))?;
state.serialize_field("section_length", &self.encoded.section_length)?;
state.serialize_field("protocol_version", &self.state.protocol_version)?;
state.serialize_field("encrypted_packet", &self.state.encrypted_packet)?;
state.serialize_field(
"encryption_algorithm",
&u8::from(self.state.encryption_algorithm),
)?;
state.serialize_field("pts_adjustment", &self.state.pts_adjustment)?;
let pts_adjustment_secs = ticks_to_secs(self.state.pts_adjustment);
state.serialize_field("pts_adjustment_secs", &pts_adjustment_secs)?;
state.serialize_field(
"pts_adjustment_human",
&format_duration(Duration::from_secs_f64(pts_adjustment_secs)).to_string(),
)?;
state.serialize_field("cw_index", &as_hex(self.state.cw_index))?;
state.serialize_field("tier", &as_hex(self.state.tier))?;
state.serialize_field("splice_command_length", &self.encoded.splice_command_length)?;
state.serialize_field(
"splice_command_type",
&u8::from(self.encoded.splice_command_type),
)?;
state.serialize_field("splice_command_name", &self.encoded.splice_command_type)?;
state.serialize_field("splice_command", &self.state.splice_command)?;
state.serialize_field(
"descriptor_loop_length",
&self.encoded.descriptor_loop_length,
)?;
state.serialize_field("descriptors", &self.state.descriptors)?;
state.serialize_field("crc_32", &as_hex(self.encoded.crc32))?;
state.end()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::*;
use crate::descriptors::{SegmentationDescriptor, SegmentationType, SegmentationUpid};
use crate::ClockTimeExt;
use anyhow::Result;
use assert_json_diff::assert_json_eq;
use std::time::Duration;
#[test]
fn write_splice_null_as_base64() -> Result<()> {
@ -420,111 +348,4 @@ mod tests {
);
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn compliance_spec_14_1_example_time_signal_as_json() -> Result<()> {
let expected_json: serde_json::Value = serde_json::from_str(
r#"{
"table_id": "0xfc",
"section_syntax_indicator": false,
"private_indicator": false,
"sap_type": "0x3",
"section_length": 54,
"protocol_version": 0,
"encrypted_packet": false,
"encryption_algorithm": 0,
"pts_adjustment": 0,
"pts_adjustment_secs": 0.0,
"pts_adjustment_human": "0s",
"cw_index": "0xff",
"tier": "0xfff",
"splice_command_length": 5,
"splice_command_type": 6,
"splice_command_name": "TimeSignal",
"splice_command": {
"name": "Time Signal",
"command_type": 6,
"command_length": 5,
"time_specified_flag": true,
"pts_time": 21388.766756,
"pts_time_ticks": 1924989008
},
"descriptor_loop_length": 32,
"descriptors": [
{
"name": "Segmentation Descriptor",
"splice_descriptor_tag": "0x02",
"descriptor_length": 30,
"identifier": "CUEI",
"segmentation_event_id": "0x4800008e",
"segmentation_event_cancel_indicator": false,
"program_segmentation_flag": true,
"segmentation_duration_flag": true,
"delivery_not_restricted_flag": false,
"web_delivery_allowed_flag": false,
"no_regional_blackout_flag": true,
"archive_allowed_flag": true,
"device_restrictions": "None",
"components": [],
"segmentation_duration": 27630000,
"segmentation_duration_secs": 307.0,
"segmentation_duration_human": "5m 7s",
"segmentation_upid_type": "0x08",
"segmentation_upid_type_name": "AiringID",
"segmentation_upid_length": 8,
"segmentation_upid": "0x2ca0a18a",
"segmentation_message": "Provider Placement Opportunity Start",
"segmentation_type_id": 52,
"segment_num": 2,
"segments_expected": 0,
"sub_segment_num": 154,
"sub_segments_expected": 201
}
],
"crc_32": "0xb6c1a0f1"
}"#,
)?;
assert_json_eq!(
serde_json::to_value(&spec_14_1_example_time_signal()?)?,
expected_json
);
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn serialize_as_json() -> Result<()> {
let splice = SpliceInfoSection::new(SpliceNull::default());
assert_json_eq!(
serde_json::to_value(&splice.into_encoded()?)?,
serde_json::json!({
"table_id": "0xfc",
"section_syntax_indicator": false,
"private_indicator": false,
"sap_type": "0x3",
"section_length": 17,
"protocol_version": 0,
"encrypted_packet": false,
"encryption_algorithm": 0,
"pts_adjustment": 0,
"pts_adjustment_secs": 0.0,
"pts_adjustment_human": "0s",
"cw_index": "0x0",
"tier": "0xfff",
"splice_command_length": 0,
"splice_command_type": 0,
"splice_command_name": "SpliceNull",
"splice_command": {},
"descriptor_loop_length": 0,
"descriptors": [],
"crc_32": "0x7a4fbfff"
})
);
Ok(())
}
}

View file

@ -3,22 +3,18 @@ use std::io;
use std::time::Duration;
use thiserror::Error;
#[cfg(feature = "serde")]
use serde::{Serialize, Serializer};
mod commands;
mod descriptors;
mod info;
mod time;
pub use commands::SpliceNull;
pub use info::{EncryptionAlgorithm, SAPType, SpliceInfoSection};
#[cfg(feature = "serde")]
mod serde;
pub trait TransportPacketWrite {
fn write_to<W>(&self, buffer: &mut W) -> anyhow::Result<u32>
where
W: io::Write;
}
pub use commands::SpliceNull;
pub use descriptors::*;
pub use info::{EncryptionAlgorithm, SAPType, SpliceInfoSection};
pub use time::SpliceTime;
#[derive(Error, Debug)]
#[error("Could not execute operation due to {0}")]
@ -44,11 +40,6 @@ impl ClockTimeExt for Duration {
}
}
/// Truncate to 6 decimal positions, as shown in the spec.
fn ticks_to_secs(value: u64) -> f64 {
(value as f64 / 90_000.0 * 1_000_000.0).ceil() as f64 / 1_000_000.0
}
trait BytesWritten {
fn bytes_written(&self) -> u32;
}
@ -60,14 +51,6 @@ impl BytesWritten for BitRecorder<u32, BigEndian> {
}
}
#[cfg(feature = "serde")]
fn serialize_time<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f64(ticks_to_secs(*value))
}
#[cfg(test)]
mod tests {
use super::*;
@ -83,12 +66,4 @@ mod tests {
let time = Duration::from_secs_f64(21388.766756);
assert_eq!(time.to_90k(), 0x072bd0050);
}
#[test]
fn test_ticks_to_secs() {
let time = Duration::from_secs_f64(21388.766756);
assert_eq!(time.to_90k(), 0x072bd0050);
assert_eq!(ticks_to_secs(0x072bd0050), 21388.766756);
assert_eq!(ticks_to_secs(time.to_90k()), 21388.766756);
}
}

637
src/serde.rs Normal file
View file

@ -0,0 +1,637 @@
use crate::commands::SpliceCommand;
use crate::descriptors::{SegmentationDescriptor, SegmentationUpid, SpliceDescriptorExt};
use crate::info::EncodedData;
use crate::{SpliceDescriptor, SpliceInfoSection, SpliceTime};
use ascii::AsciiStr;
use bitstream_io::{BigEndian, BitRecorder, BitWriter};
use serde::ser::{Error, SerializeStruct};
use serde::{Serialize, Serializer};
use std::fmt;
use std::fmt::LowerHex;
use std::time::Duration;
/// Truncate to 6 decimal positions, as shown in the spec.
fn ticks_to_secs(value: u64) -> f64 {
(value as f64 / 90_000.0 * 1_000_000.0).ceil() as f64 / 1_000_000.0
}
impl Serialize for SegmentationDescriptor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use crate::SegmentationFieldSyntax::*;
#[inline]
fn as_hex<T>(value: T) -> String
where
T: LowerHex,
{
format!("0x{:02x}", value)
}
let segmentation_syntax = self.segmentation_type().syntax();
// predict number of fields in the struct
let mut num_fields = 6;
if !self.segmentation_event_cancel_indicator() {
num_fields += 12;
if !self.delivery_not_restricted_flag() {
num_fields += 4;
}
if self.segmentation_duration_flag() {
num_fields += 2;
}
match segmentation_syntax.sub_segment_num() {
Fixed(_) | NonZero | ZeroOrNonZero => {
num_fields += 1;
}
NotUsed => {}
}
match segmentation_syntax.sub_segments_expected() {
Fixed(_) | NonZero | ZeroOrNonZero => {
num_fields += 1;
}
NotUsed => {}
}
}
let mut state = serializer.serialize_struct("SegmentationDescriptor", num_fields)?;
state.serialize_field("name", "Segmentation Descriptor")?;
state.serialize_field(
"splice_descriptor_tag",
&as_hex(self.splice_descriptor_tag()),
)?;
state.serialize_field("descriptor_length", &self.descriptor_length())?;
let id = self.identifier().to_be_bytes();
state.serialize_field(
"identifier",
AsciiStr::from_ascii(id.as_slice())
.expect("ascii characters")
.as_str(),
)?;
state.serialize_field(
"segmentation_event_id",
&as_hex(self.segmentation_event_id()),
)?;
state.serialize_field(
"segmentation_event_cancel_indicator",
&self.segmentation_event_cancel_indicator(),
)?;
if !self.segmentation_event_cancel_indicator() {
state.serialize_field(
"program_segmentation_flag",
&self.program_segmentation_flag(),
)?;
state.serialize_field(
"segmentation_duration_flag",
&self.segmentation_duration_flag(),
)?;
state.serialize_field(
"delivery_not_restricted_flag",
&self.delivery_not_restricted_flag(),
)?;
if !self.delivery_not_restricted_flag() {
state.serialize_field(
"web_delivery_allowed_flag",
&self.web_delivery_allowed_flag(),
)?;
state.serialize_field(
"no_regional_blackout_flag",
&self.no_regional_blackout_flag(),
)?;
state.serialize_field("archive_allowed_flag", &self.archive_allowed_flag())?;
state.serialize_field("device_restrictions", &self.device_restrictions())?;
}
state.serialize_field("components", self.components())?;
if self.segmentation_duration_flag() {
let duration_secs = ticks_to_secs(self.segmentation_duration());
state.serialize_field("segmentation_duration", &self.segmentation_duration())?;
state.serialize_field("segmentation_duration_secs", &duration_secs)?;
state.serialize_field(
"segmentation_duration_human",
&format_duration(Duration::from_secs_f64(duration_secs)).to_string(),
)?;
}
state.serialize_field(
"segmentation_upid_type",
&as_hex(u8::from(self.segmentation_upid().segmentation_upid_type())),
)?;
state.serialize_field(
"segmentation_upid_type_name",
&format!("{}", self.segmentation_upid().segmentation_upid_type()),
)?;
state.serialize_field(
"segmentation_upid_length",
&self.segmentation_upid().segmentation_upid_length(),
)?;
state.serialize_field("segmentation_upid", &self.segmentation_upid())?;
state.serialize_field(
"segmentation_message",
&format!("{}", self.segmentation_type()),
)?;
state.serialize_field("segmentation_type_id", &self.segmentation_type().id())?;
state.serialize_field("segment_num", &self.segment_num())?;
state.serialize_field("segments_expected", &self.segments_expected())?;
match segmentation_syntax.sub_segment_num() {
Fixed(v) => {
state.serialize_field("sub_segment_num", &v)?;
}
NonZero | ZeroOrNonZero => {
state.serialize_field("sub_segment_num", &self.sub_segment_num())?;
}
NotUsed => {}
}
match segmentation_syntax.sub_segments_expected() {
Fixed(v) => {
state.serialize_field("sub_segments_expected", &v)?;
}
NonZero | ZeroOrNonZero => {
state
.serialize_field("sub_segments_expected", &self.sub_segments_expected())?;
}
NotUsed => {}
}
}
state.end()
}
}
impl Serialize for SegmentationUpid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use SegmentationUpid::*;
let mut recorder = BitRecorder::<u32, BigEndian>::new();
self.write_to(&mut recorder)
.map_err(|err| S::Error::custom(format!("{}", err)))?;
let mut data = Vec::new();
let mut buffer = BitWriter::endian(&mut data, BigEndian);
recorder
.playback(&mut buffer)
.map_err(|err| S::Error::custom(format!("{}", err)))?;
// TODO: serialize as struct when variant is MPU and MID
match self {
// if field is represented as a character, then show with textual representation
ISCI(v) | AdID(v) | TID(v) | ADSInformation(v) | URI(v) | SCR(v) => {
serializer.serialize_str(v.as_str())
}
// if field is represented as a number, then show as hex
ISAN(v) | EIDR(v) | UUID(v) => serializer.serialize_str(&format!("0x{:x}", v)),
ISANDeprecated(v) | AiringID(v) => serializer.serialize_str(&format!("0x{:x}", v)),
// everything else show as hex, we skip the first byte (which is the length)
_ => serializer.serialize_str(&format!("0x{}", hex::encode(&data[1..]))),
}
}
}
impl Serialize for SpliceTime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let num_fields = if self.time_specified_flag() { 3 } else { 1 };
let mut state = serializer.serialize_struct("SpliceTime", num_fields)?;
state.serialize_field("time_specified_flag", &self.time_specified_flag())?;
if self.time_specified_flag() {
state.serialize_field("pts_time", &self.pts_time().unwrap_or(0))?;
state.serialize_field(
"pts_time_secs",
&ticks_to_secs(self.pts_time().unwrap_or(0)),
)?;
}
state.end()
}
}
impl Serialize for SpliceDescriptor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use SpliceDescriptor::*;
match self {
Segmentation(seg) => seg.serialize(serializer),
Unknown(tag, len, data) => {
let mut struc = serializer.serialize_struct("SpliceDescriptor", 3)?;
struc.serialize_field("tag", &format!("0x{:x}", tag))?;
struc.serialize_field("length", &len)?;
struc.serialize_field("data", &format!("0x{}", hex::encode(data).as_str()))?;
struc.end()
}
// TODO: add other descriptors
_ => serializer.serialize_str(&format!("{:?}", self)),
}
}
}
impl<C> Serialize for SpliceInfoSection<C, EncodedData>
where
C: SpliceCommand + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[inline]
fn as_hex<T>(value: T) -> String
where
T: LowerHex,
{
format!("0x{:x}", value)
}
let mut state = serializer.serialize_struct("SpliceInfoSection", 19)?;
state.serialize_field("table_id", &as_hex(self.state.table_id))?;
state.serialize_field(
"section_syntax_indicator",
&self.state.section_syntax_indicator,
)?;
state.serialize_field("private_indicator", &self.state.private_indicator)?;
state.serialize_field("sap_type", &as_hex(self.state.sap_type as u8))?;
state.serialize_field("section_length", &self.encoded.section_length)?;
state.serialize_field("protocol_version", &self.state.protocol_version)?;
state.serialize_field("encrypted_packet", &self.state.encrypted_packet)?;
state.serialize_field(
"encryption_algorithm",
&u8::from(self.state.encryption_algorithm),
)?;
state.serialize_field("pts_adjustment", &self.state.pts_adjustment)?;
let pts_adjustment_secs = ticks_to_secs(self.state.pts_adjustment);
state.serialize_field("pts_adjustment_secs", &pts_adjustment_secs)?;
state.serialize_field(
"pts_adjustment_human",
&format_duration(Duration::from_secs_f64(pts_adjustment_secs)).to_string(),
)?;
state.serialize_field("cw_index", &as_hex(self.state.cw_index))?;
state.serialize_field("tier", &as_hex(self.state.tier))?;
state.serialize_field("splice_command_length", &self.encoded.splice_command_length)?;
state.serialize_field(
"splice_command_type",
&u8::from(self.encoded.splice_command_type),
)?;
state.serialize_field("splice_command_name", &self.encoded.splice_command_type)?;
state.serialize_field("splice_command", &self.state.splice_command)?;
state.serialize_field(
"descriptor_loop_length",
&self.encoded.descriptor_loop_length,
)?;
state.serialize_field("descriptors", &self.state.descriptors)?;
state.serialize_field("crc_32", &as_hex(self.encoded.crc32))?;
state.end()
}
}
// Copyright (c) 2016 The humantime Developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/// Formats duration into a human-readable string
///
/// Note: this format is guaranteed to have same value when using
/// parse_duration, but we can change some details of the exact composition
/// of the value.
pub(crate) fn format_duration(val: Duration) -> FormattedDuration {
FormattedDuration(val)
}
fn item_plural(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u64) -> fmt::Result {
if value > 0 {
if *started {
f.write_str(" ")?;
}
write!(f, "{}{}", value, name)?;
if value > 1 {
f.write_str("s")?;
}
*started = true;
}
Ok(())
}
fn item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32) -> fmt::Result {
if value > 0 {
if *started {
f.write_str(" ")?;
}
write!(f, "{}{}", value, name)?;
*started = true;
}
Ok(())
}
/// A wrapper type that allows you to Display a Duration
#[derive(Debug, Clone)]
pub(crate) struct FormattedDuration(Duration);
impl fmt::Display for FormattedDuration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let secs = self.0.as_secs();
let nanos = self.0.subsec_nanos();
if secs == 0 && nanos == 0 {
f.write_str("0s")?;
return Ok(());
}
let years = secs / 31_557_600; // 365.25d
let ydays = secs % 31_557_600;
let months = ydays / 2_630_016; // 30.44d
let mdays = ydays % 2_630_016;
let days = mdays / 86400;
let day_secs = mdays % 86400;
let hours = day_secs / 3600;
let minutes = day_secs % 3600 / 60;
let seconds = day_secs % 60;
let millis = nanos / 1_000_000;
let micros = nanos / 1000 % 1000;
let nanosec = nanos % 1000;
let started = &mut false;
item_plural(f, started, "year", years)?;
item_plural(f, started, "month", months)?;
item_plural(f, started, "day", days)?;
item(f, started, "h", hours as u32)?;
item(f, started, "m", minutes as u32)?;
item(f, started, "s", seconds as u32)?;
item(f, started, "milli", millis)?;
item(f, started, "us", micros)?;
item(f, started, "ns", nanosec)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::TimeSignal;
use crate::descriptors::SegmentationType;
use crate::{ClockTimeExt, SpliceNull};
use anyhow::Result;
use assert_json_diff::assert_json_eq;
use std::time::Duration;
#[test]
fn test_ticks_to_secs() {
let time = Duration::from_secs_f64(21388.766756);
assert_eq!(time.to_90k(), 0x072bd0050);
assert_eq!(ticks_to_secs(0x072bd0050), 21388.766756);
assert_eq!(ticks_to_secs(time.to_90k()), 21388.766756);
}
#[test]
fn serialize_splice_null() -> Result<()> {
let splice_null = SpliceNull::default();
assert_json_eq!(serde_json::to_value(&splice_null)?, serde_json::json!({}));
Ok(())
}
#[test]
fn serialize_time_signal_without_time() -> Result<()> {
let time_signal = TimeSignal::default();
assert_json_eq!(
serde_json::to_value(&time_signal)?,
serde_json::json!({
"time_specified_flag": false
})
);
Ok(())
}
#[test]
fn serialize_time_signal_with_time() -> Result<()> {
let time_signal = TimeSignal::from(Duration::from_secs(10));
assert_json_eq!(
serde_json::to_value(&time_signal)?,
serde_json::json!({
"time_specified_flag": true,
"pts_time": 900000,
"pts_time_secs": 10.0
})
);
Ok(())
}
#[test]
fn serialize_segmentation_to_json() -> Result<()> {
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(Duration::from_secs_f32(307.0));
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);
// We need to write to calculate the length
let mut data = Vec::new();
descriptor.write_to(&mut data)?;
let expected_json: serde_json::Value = serde_json::from_str(
r#"{
"name": "Segmentation Descriptor",
"splice_descriptor_tag": "0x02",
"descriptor_length": 30,
"identifier": "CUEI",
"segmentation_event_id": "0x4800008e",
"segmentation_event_cancel_indicator": false,
"program_segmentation_flag": true,
"segmentation_duration_flag": true,
"delivery_not_restricted_flag": false,
"web_delivery_allowed_flag": false,
"no_regional_blackout_flag": true,
"archive_allowed_flag": true,
"device_restrictions": "None",
"components": [],
"segmentation_duration": 27630000,
"segmentation_duration_secs": 307.0,
"segmentation_duration_human": "5m 7s",
"segmentation_upid_type": "0x08",
"segmentation_upid_type_name": "AiringID",
"segmentation_upid_length": 8,
"segmentation_upid": "0x2ca0a18a",
"segmentation_message": "Provider Placement Opportunity Start",
"segmentation_type_id": 52,
"segment_num": 2,
"segments_expected": 0,
"sub_segment_num": 154,
"sub_segments_expected": 201
}"#,
)?;
assert_json_eq!(serde_json::to_value(&descriptor)?, expected_json);
Ok(())
}
#[test]
fn serialize_segmentation_with_cancel_indicator_to_json() -> Result<()> {
let mut descriptor = SegmentationDescriptor::default();
descriptor.set_segmentation_event_id(0x4800008e);
descriptor.set_segmentation_event_cancel_indicator(true);
// We need to write to calculate the length
let mut data = Vec::new();
descriptor.write_to(&mut data)?;
let expected_json: serde_json::Value = serde_json::from_str(
r#"{
"name": "Segmentation Descriptor",
"splice_descriptor_tag": "0x02",
"descriptor_length": 9,
"identifier": "CUEI",
"segmentation_event_id": "0x4800008e",
"segmentation_event_cancel_indicator": true
}"#,
)?;
assert_json_eq!(serde_json::to_value(&descriptor)?, expected_json);
Ok(())
}
#[test]
fn compliance_spec_14_1_example_time_signal_as_json() -> Result<()> {
let mut splice = SpliceInfoSection::new(TimeSignal::from(0x072bd0050u64));
splice.set_cw_index(0xff);
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());
let expected_json: serde_json::Value = serde_json::from_str(
r#"{
"table_id": "0xfc",
"section_syntax_indicator": false,
"private_indicator": false,
"sap_type": "0x3",
"section_length": 54,
"protocol_version": 0,
"encrypted_packet": false,
"encryption_algorithm": 0,
"pts_adjustment": 0,
"pts_adjustment_secs": 0.0,
"pts_adjustment_human": "0s",
"cw_index": "0xff",
"tier": "0xfff",
"splice_command_length": 5,
"splice_command_type": 6,
"splice_command_name": "TimeSignal",
"splice_command": {
"time_specified_flag": true,
"pts_time": 1924989008,
"pts_time_secs": 21388.766756
},
"descriptor_loop_length": 32,
"descriptors": [
{
"name": "Segmentation Descriptor",
"splice_descriptor_tag": "0x02",
"descriptor_length": 30,
"identifier": "CUEI",
"segmentation_event_id": "0x4800008e",
"segmentation_event_cancel_indicator": false,
"program_segmentation_flag": true,
"segmentation_duration_flag": true,
"delivery_not_restricted_flag": false,
"web_delivery_allowed_flag": false,
"no_regional_blackout_flag": true,
"archive_allowed_flag": true,
"device_restrictions": "None",
"components": [],
"segmentation_duration": 27630000,
"segmentation_duration_secs": 307.0,
"segmentation_duration_human": "5m 7s",
"segmentation_upid_type": "0x08",
"segmentation_upid_type_name": "AiringID",
"segmentation_upid_length": 8,
"segmentation_upid": "0x2ca0a18a",
"segmentation_message": "Provider Placement Opportunity Start",
"segmentation_type_id": 52,
"segment_num": 2,
"segments_expected": 0,
"sub_segment_num": 154,
"sub_segments_expected": 201
}
],
"crc_32": "0xb6c1a0f1"
}"#,
)?;
assert_json_eq!(
serde_json::to_value(&splice.into_encoded()?)?,
expected_json
);
Ok(())
}
#[test]
fn serialize_info_section_as_json() -> Result<()> {
let splice = SpliceInfoSection::new(SpliceNull::default());
assert_json_eq!(
serde_json::to_value(&splice.into_encoded()?)?,
serde_json::json!({
"table_id": "0xfc",
"section_syntax_indicator": false,
"private_indicator": false,
"sap_type": "0x3",
"section_length": 17,
"protocol_version": 0,
"encrypted_packet": false,
"encryption_algorithm": 0,
"pts_adjustment": 0,
"pts_adjustment_secs": 0.0,
"pts_adjustment_human": "0s",
"cw_index": "0x0",
"tier": "0xfff",
"splice_command_length": 0,
"splice_command_type": 0,
"splice_command_name": "SpliceNull",
"splice_command": {},
"descriptor_loop_length": 0,
"descriptors": [],
"crc_32": "0x7a4fbfff"
})
);
Ok(())
}
}

View file

@ -1,27 +1,19 @@
use crate::{BytesWritten, ClockTimeExt, CueError, TransportPacketWrite};
use crate::{BytesWritten, ClockTimeExt};
use bitstream_io::{BigEndian, BitRecorder, BitWrite, BitWriter};
#[cfg(feature = "serde")]
use serde::{Serialize, Serializer};
use std::time::Duration;
use std::{fmt, io};
use std::io;
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SpliceTime {
time_specified_flag: bool,
#[cfg_attr(feature = "serde", serde(serialize_with = "crate::serialize_time"))]
pts_time: u64,
// Size of the SpliceTime structure after encoding
pub(crate) bytes_length: Option<u32>,
}
impl SpliceTime {
pub fn new() -> Self {
Self {
time_specified_flag: false,
pts_time: 0,
}
}
pub fn from_ticks(ticks: u64) -> Self {
let mut splice_time = Self::new();
let mut splice_time = Self::default();
splice_time.set_pts_time(Some(ticks));
splice_time
}
@ -54,10 +46,8 @@ impl SpliceTime {
None
}
}
}
impl TransportPacketWrite for SpliceTime {
fn write_to<W>(&self, buffer: &mut W) -> anyhow::Result<u32>
pub(crate) fn write_to<W>(&mut self, buffer: &mut W) -> anyhow::Result<u32>
where
W: io::Write,
{
@ -74,130 +64,19 @@ impl TransportPacketWrite for SpliceTime {
let mut buffer = BitWriter::endian(buffer, BigEndian);
recorder.playback(&mut buffer)?;
self.bytes_length = Some(recorder.bytes_written());
Ok(recorder.bytes_written())
}
}
impl<T> From<T> for SpliceTime
where
T: ClockTimeExt,
where
T: ClockTimeExt,
{
fn from(pts: T) -> Self {
let mut t = Self::new();
t.set_pts(Some(pts));
let mut t = Self::default();
t.set_pts_time(Some(pts));
t
}
}
// Copyright (c) 2016 The humantime Developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/// Formats duration into a human-readable string
///
/// Note: this format is guaranteed to have same value when using
/// parse_duration, but we can change some details of the exact composition
/// of the value.
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use humantime::format_duration;
///
/// let val1 = Duration::new(9420, 0);
/// assert_eq!(format_duration(val1).to_string(), "2h 37m");
/// let val2 = Duration::new(0, 32_000_000);
/// assert_eq!(format_duration(val2).to_string(), "32ms");
/// ```
pub(crate) fn format_duration(val: Duration) -> FormattedDuration {
FormattedDuration(val)
}
fn item_plural(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u64) -> fmt::Result {
if value > 0 {
if *started {
f.write_str(" ")?;
}
write!(f, "{}{}", value, name)?;
if value > 1 {
f.write_str("s")?;
}
*started = true;
}
Ok(())
}
fn item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32) -> fmt::Result {
if value > 0 {
if *started {
f.write_str(" ")?;
}
write!(f, "{}{}", value, name)?;
*started = true;
}
Ok(())
}
/// A wrapper type that allows you to Display a Duration
#[derive(Debug, Clone)]
pub(crate) struct FormattedDuration(Duration);
impl FormattedDuration {
/// Returns a reference to the [`Duration`][] that is being formatted.
pub fn get_ref(&self) -> &Duration {
&self.0
}
}
impl fmt::Display for FormattedDuration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let secs = self.0.as_secs();
let nanos = self.0.subsec_nanos();
if secs == 0 && nanos == 0 {
f.write_str("0s")?;
return Ok(());
}
let years = secs / 31_557_600; // 365.25d
let ydays = secs % 31_557_600;
let months = ydays / 2_630_016; // 30.44d
let mdays = ydays % 2_630_016;
let days = mdays / 86400;
let day_secs = mdays % 86400;
let hours = day_secs / 3600;
let minutes = day_secs % 3600 / 60;
let seconds = day_secs % 60;
let millis = nanos / 1_000_000;
let micros = nanos / 1000 % 1000;
let nanosec = nanos % 1000;
let started = &mut false;
item_plural(f, started, "year", years)?;
item_plural(f, started, "month", months)?;
item_plural(f, started, "day", days)?;
item(f, started, "h", hours as u32)?;
item(f, started, "m", minutes as u32)?;
item(f, started, "s", seconds as u32)?;
item(f, started, "milli", millis)?;
item(f, started, "us", micros)?;
item(f, started, "ns", nanosec)?;
Ok(())
}
}