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:
parent
5d72027943
commit
501e060979
7 changed files with 808 additions and 741 deletions
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
227
src/info.rs
227
src/info.rs
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
37
src/lib.rs
37
src/lib.rs
|
@ -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
637
src/serde.rs
Normal 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(())
|
||||
}
|
||||
}
|
147
src/time.rs
147
src/time.rs
|
@ -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
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue