Serialization support using serde
This commit is contained in:
parent
e4ccc470a6
commit
02843e70f1
6 changed files with 272 additions and 96 deletions
13
Cargo.toml
13
Cargo.toml
|
@ -7,6 +7,13 @@ edition = "2021"
|
|||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
bitstream-io = "1.3.0"
|
||||
crc = "3.0.0"
|
||||
base64 = "0.13"
|
||||
bitstream-io = "1.3"
|
||||
crc = "3.0"
|
||||
thiserror = "1"
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
anyhow = "1"
|
||||
assert-json-diff = "2"
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use crate::{CueError, TransportPacketWrite};
|
||||
use std::io;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::Serialize;
|
||||
|
||||
pub trait SpliceCommand: TransportPacketWrite {
|
||||
fn splice_command_type(&self) -> u8;
|
||||
fn splice_command_type(&self) -> SpliceCommandType;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
pub struct SpliceNull {}
|
||||
|
||||
impl SpliceNull {
|
||||
|
@ -23,8 +27,8 @@ impl TransportPacketWrite for SpliceNull {
|
|||
}
|
||||
|
||||
impl SpliceCommand for SpliceNull {
|
||||
fn splice_command_type(&self) -> u8 {
|
||||
SpliceCommandType::SpliceNull.into()
|
||||
fn splice_command_type(&self) -> SpliceCommandType {
|
||||
SpliceCommandType::SpliceNull
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
mod segmentation;
|
||||
|
||||
use crate::{CueError, TransportPacketWrite};
|
||||
pub use segmentation::*;
|
||||
use std::io;
|
||||
|
||||
pub use segmentation::*;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
pub enum SpliceDescriptor {
|
||||
Avail,
|
||||
DTMF,
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use crate::{CueError, TransportPacketWrite};
|
||||
use std::io;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
pub struct SegmentationDescriptor {
|
||||
identifier: u32,
|
||||
segmentation_event_id: u32,
|
||||
|
@ -20,6 +25,8 @@ impl TransportPacketWrite for SegmentationDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
struct DeliveryRestriction {
|
||||
web_delivery_allowed_flag: bool,
|
||||
no_regional_blackout_flag: bool,
|
||||
|
@ -27,6 +34,8 @@ struct DeliveryRestriction {
|
|||
device_restrictions: DeviceRestrictions,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
enum DeviceRestrictions {
|
||||
RestrictGroup0 = 0x00,
|
||||
RestrictGroup1 = 0x01,
|
||||
|
@ -56,6 +65,8 @@ enum SegmentationUpidType {
|
|||
Reserved,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
enum SegmentationUpid {
|
||||
NotUsed,
|
||||
UserDefinedDeprecated,
|
||||
|
@ -78,6 +89,8 @@ enum SegmentationUpid {
|
|||
Reserved,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize))]
|
||||
struct Component {
|
||||
component_tag: u8,
|
||||
pts_offset: u64,
|
||||
|
|
284
src/info.rs
284
src/info.rs
|
@ -1,13 +1,25 @@
|
|||
use crate::commands::SpliceCommand;
|
||||
use crate::commands::{SpliceCommand, SpliceCommandType};
|
||||
use crate::descriptors::SpliceDescriptor;
|
||||
use crate::{CueError, TransportPacketWrite};
|
||||
use bitstream_io::{BigEndian, BitWrite, BitWriter};
|
||||
use std::io;
|
||||
use crc::{Crc, Algorithm, CRC_32_MPEG_2};
|
||||
use crc::{Crc, CRC_32_MPEG_2};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::Serialize;
|
||||
|
||||
pub const MPEG_2: Crc<u32> = Crc::<u32>::new(&CRC_32_MPEG_2);
|
||||
|
||||
pub struct SpliceInfoSection<C>
|
||||
pub struct SpliceInfoSection<C, S>
|
||||
where
|
||||
C: SpliceCommand,
|
||||
S: EncodingState,
|
||||
{
|
||||
state: SpliceInfoState<C>,
|
||||
encoded: S,
|
||||
}
|
||||
|
||||
struct SpliceInfoState<C>
|
||||
where
|
||||
C: SpliceCommand,
|
||||
{
|
||||
|
@ -39,12 +51,30 @@ where
|
|||
descriptors: Vec<SpliceDescriptor>,
|
||||
}
|
||||
|
||||
impl<C> SpliceInfoSection<C>
|
||||
pub trait EncodingState {}
|
||||
|
||||
struct NotEncoded;
|
||||
|
||||
impl EncodingState for NotEncoded {}
|
||||
|
||||
struct EncodedData {
|
||||
section_length: u16,
|
||||
splice_command_length: u16,
|
||||
splice_command_type: SpliceCommandType,
|
||||
descriptor_loop_length: u16,
|
||||
crc32: u32,
|
||||
final_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl EncodingState for EncodedData {}
|
||||
|
||||
impl<C> SpliceInfoSection<C, NotEncoded>
|
||||
where
|
||||
C: SpliceCommand,
|
||||
{
|
||||
fn new(splice_command: C) -> Self {
|
||||
Self {
|
||||
state: SpliceInfoState {
|
||||
table_id: 0xFC,
|
||||
section_syntax_indicator: false,
|
||||
private_indicator: false,
|
||||
|
@ -57,13 +87,95 @@ where
|
|||
tier: 0xFFF,
|
||||
splice_command,
|
||||
descriptors: Vec::new(),
|
||||
},
|
||||
encoded: NotEncoded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> SpliceInfoSection<C, NotEncoded>
|
||||
where
|
||||
C: SpliceCommand,
|
||||
{
|
||||
fn into_encoded(self) -> Result<SpliceInfoSection<C, EncodedData>, CueError> {
|
||||
// Write splice command to a temporary buffer
|
||||
let mut splice_data = Vec::new();
|
||||
self.state.splice_command.write_to(&mut splice_data)?;
|
||||
|
||||
// Write the descriptors to a temporary buffer
|
||||
let mut descriptor_data = Vec::new();
|
||||
for descriptor in &self.state.descriptors {
|
||||
descriptor.write_to(&mut descriptor_data)?;
|
||||
}
|
||||
|
||||
// Start writing the final output to a temporary buffer
|
||||
let mut data = Vec::new();
|
||||
let mut buffer = BitWriter::endian(&mut data, BigEndian);
|
||||
buffer.write(8, self.state.table_id)?;
|
||||
buffer.write_bit(self.state.section_syntax_indicator)?;
|
||||
buffer.write_bit(self.state.private_indicator)?;
|
||||
buffer.write(2, self.state.sap_type as u8)?;
|
||||
|
||||
// We know the section length by computing all known fixed size elements from now plus the
|
||||
// splice command length and descriptors which are also known by now
|
||||
const FIXED_INFO_SIZE_BYTES: usize = (8 + 1 + 6 + 33 + 8 + 12 + 12 + 8 + 16 + 32) / 8;
|
||||
let mut section_length =
|
||||
(FIXED_INFO_SIZE_BYTES + splice_data.len() + descriptor_data.len()) as u16;
|
||||
if self.state.encrypted_packet {
|
||||
section_length += 4;
|
||||
}
|
||||
buffer.write(12, section_length)?;
|
||||
buffer.write(8, self.state.protocol_version)?;
|
||||
buffer.write_bit(self.state.encrypted_packet)?;
|
||||
let encryption_algorithm: u8 = self.state.encryption_algorithm.into();
|
||||
buffer.write(6, encryption_algorithm)?;
|
||||
buffer.write(33, self.state.pts_adjustment)?;
|
||||
buffer.write(8, self.state.cw_index)?;
|
||||
buffer.write(12, self.state.tier)?;
|
||||
let splice_command_length = splice_data.len() as u16;
|
||||
buffer.write(12, splice_command_length)?;
|
||||
let splice_command_type = self.state.splice_command.splice_command_type();
|
||||
buffer.write(8, u8::from(splice_command_type))?;
|
||||
buffer.write_bytes(splice_data.as_slice())?;
|
||||
let descriptor_loop_length = descriptor_data.len() as u16;
|
||||
buffer.write(16, descriptor_loop_length)?;
|
||||
buffer.write_bytes(descriptor_data.as_slice())?;
|
||||
buffer.flush()?;
|
||||
|
||||
// Finally, write to out
|
||||
let mut final_data = Vec::new();
|
||||
let mut buffer = BitWriter::endian(&mut final_data, BigEndian);
|
||||
buffer.write_bytes(data.as_slice())?;
|
||||
// CRC 32
|
||||
if self.state.encrypted_packet {
|
||||
// TODO: alignment stuffing here, in case of DES encryption this needs to be 8 bytes aligned
|
||||
// encrypted_packet_crc32:
|
||||
buffer.write(32, 0)?;
|
||||
}
|
||||
let crc32 = MPEG_2.checksum(data.as_slice());
|
||||
buffer.write(32, crc32)?;
|
||||
buffer.flush()?;
|
||||
|
||||
Ok(SpliceInfoSection {
|
||||
state: self.state,
|
||||
encoded: EncodedData {
|
||||
section_length,
|
||||
splice_command_length,
|
||||
splice_command_type,
|
||||
descriptor_loop_length,
|
||||
crc32,
|
||||
final_data,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> SpliceInfoSection<C, EncodedData>
|
||||
where
|
||||
C: SpliceCommand,
|
||||
{
|
||||
pub fn as_base64(&self) -> Result<String, CueError> {
|
||||
let mut out = Vec::new();
|
||||
self.write_to(&mut out)?;
|
||||
Ok(base64::encode(out.as_slice()))
|
||||
Ok(base64::encode(self.encoded.final_data.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +188,17 @@ pub enum SAPType {
|
|||
NotSpecified = 0x03,
|
||||
}
|
||||
|
||||
impl Display for SAPType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SAPType::Type1 => write!(f, "Type 1"),
|
||||
SAPType::Type2 => write!(f, "Type 2"),
|
||||
SAPType::Type3 => write!(f, "Type 3"),
|
||||
SAPType::NotSpecified => write!(f, "Not Specified"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum EncryptionAlgorithm {
|
||||
NotEncrypted,
|
||||
|
@ -112,69 +235,60 @@ impl From<EncryptionAlgorithm> for u8 {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C> TransportPacketWrite for SpliceInfoSection<C>
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde_serialization {
|
||||
use super::*;
|
||||
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||
use std::fmt::LowerHex;
|
||||
|
||||
impl<C> Serialize for SpliceInfoSection<C, EncodedData>
|
||||
where
|
||||
C: SpliceCommand,
|
||||
C: SpliceCommand + Serialize,
|
||||
{
|
||||
fn write_to<W>(&self, out: &mut W) -> Result<(), CueError>
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
W: io::Write,
|
||||
S: Serializer,
|
||||
{
|
||||
// Write splice command to a temporary buffer
|
||||
let mut splice_data = Vec::new();
|
||||
self.splice_command.write_to(&mut splice_data)?;
|
||||
|
||||
// Write the descriptors to a temporary buffer
|
||||
let mut descriptor_data = Vec::new();
|
||||
for descriptor in &self.descriptors {
|
||||
descriptor.write_to(&mut descriptor_data)?;
|
||||
#[inline]
|
||||
fn as_hex<T>(value: T) -> String
|
||||
where
|
||||
T: LowerHex,
|
||||
{
|
||||
format!("0x{:x}", value)
|
||||
}
|
||||
|
||||
// Start writing the final output to a temporary buffer
|
||||
let mut data = Vec::new();
|
||||
let mut buffer = BitWriter::endian(&mut data, BigEndian);
|
||||
buffer.write(8, self.table_id)?;
|
||||
buffer.write_bit(self.section_syntax_indicator)?;
|
||||
buffer.write_bit(self.private_indicator)?;
|
||||
buffer.write(2, self.sap_type as u8)?;
|
||||
|
||||
// We know the section length by computing all known fixed size elements from now plus the
|
||||
// splice command length and descriptors which are also known by now
|
||||
const FIXED_INFO_SIZE_BYTES: usize = (8 + 1 + 6 + 33 + 8 + 12 + 12 + 8 + 16 + 32) / 8;
|
||||
let mut section_length = FIXED_INFO_SIZE_BYTES + splice_data.len() + descriptor_data.len();
|
||||
if self.encrypted_packet {
|
||||
section_length += 4;
|
||||
let mut state = serializer.serialize_struct("SpliceInfoSection", 17)?;
|
||||
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)?;
|
||||
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", &self.state.splice_command)?;
|
||||
state.serialize_field(
|
||||
"descriptor_loop_length",
|
||||
&self.encoded.descriptor_loop_length,
|
||||
)?;
|
||||
state.serialize_field("descriptor_loop", &self.state.descriptors)?;
|
||||
state.serialize_field("crc_32", &as_hex(self.encoded.crc32))?;
|
||||
state.end()
|
||||
}
|
||||
buffer.write(12, section_length as u16)?;
|
||||
buffer.write(8, self.protocol_version)?;
|
||||
buffer.write_bit(self.encrypted_packet)?;
|
||||
let encryption_algorithm: u8 = self.encryption_algorithm.into();
|
||||
buffer.write(6, encryption_algorithm)?;
|
||||
buffer.write(33, self.pts_adjustment)?;
|
||||
buffer.write(8, self.cw_index)?;
|
||||
buffer.write(12, self.tier)?;
|
||||
buffer.write(12, splice_data.len() as u16)?;
|
||||
buffer.write(8, self.splice_command.splice_command_type())?;
|
||||
buffer.write_bytes(splice_data.as_slice())?;
|
||||
buffer.write(16, descriptor_data.len() as u16)?;
|
||||
buffer.write_bytes(descriptor_data.as_slice())?;
|
||||
buffer.flush()?;
|
||||
|
||||
// Finally, write to out
|
||||
let mut buffer = BitWriter::endian(out, BigEndian);
|
||||
buffer.write_bytes(data.as_slice())?;
|
||||
// CRC 32
|
||||
if self.encrypted_packet {
|
||||
// TODO: alignment stuffing here, in case of DES encryption this needs to be 8 bytes aligned
|
||||
// encrypted_packet_crc32:
|
||||
buffer.write(32, 0)?;
|
||||
}
|
||||
// TODO: Calculate CRC32. Use the data information
|
||||
// crc32:
|
||||
buffer.write(32, MPEG_2.checksum(data.as_slice()))?;
|
||||
buffer.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,11 +296,49 @@ where
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::commands::SpliceNull;
|
||||
use anyhow::Result;
|
||||
use assert_json_diff::assert_json_eq;
|
||||
|
||||
#[test]
|
||||
fn write_null_splice() {
|
||||
fn write_null_splice() -> Result<()> {
|
||||
let splice = SpliceInfoSection::new(SpliceNull::new());
|
||||
|
||||
assert_eq!(splice.as_base64().unwrap(), "/DARAAAAAAAAAP/wAAAAAHpPv/8=".to_string());
|
||||
assert_eq!(
|
||||
splice.into_encoded()?.as_base64()?,
|
||||
"/DARAAAAAAAAAP/wAAAAAHpPv/8=".to_string()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn serialize_as_json() -> Result<()> {
|
||||
let splice = SpliceInfoSection::new(SpliceNull::new());
|
||||
|
||||
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,
|
||||
"cw_index": "0x0",
|
||||
"tier": "0xfff",
|
||||
"splice_command_length": 0,
|
||||
"splice_command_type": 0,
|
||||
"splice_command": {},
|
||||
"descriptor_loop_length": 0,
|
||||
"descriptor_loop": [],
|
||||
"crc_32": "0x7a4fbfff"
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,4 +1,5 @@
|
|||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
mod commands;
|
||||
mod descriptors;
|
||||
|
@ -10,13 +11,8 @@ pub trait TransportPacketWrite {
|
|||
W: io::Write;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Could not execute operation due to {0}")]
|
||||
pub enum CueError {
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for CueError {
|
||||
fn from(err: io::Error) -> CueError {
|
||||
CueError::Io(err)
|
||||
}
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue