use alloc::vec::Vec;
use super::header::{self, CUEI, HEADER_LEN};
use super::segmentation_enums::{DeviceRestrictions, SegmentationTypeId, SegmentationUpidType};
use crate::error::{Error, Result};
use crate::traits::SpliceDescriptorDef;
use dvb_common::{Parse, Serialize};
const MPU_FORMAT_IDENTIFIER_LEN: usize = 4;
const MID_ENTRY_HEADER_LEN: usize = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Mpu<'a> {
pub format_identifier: u32,
pub private_data: &'a [u8],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct MidUpid<'a> {
pub upid_type: SegmentationUpidType,
pub upid: &'a [u8],
}
pub const TAG: u8 = 0x02;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DeliveryRestrictions {
pub web_delivery_allowed: bool,
pub no_regional_blackout: bool,
pub archive_allowed: bool,
pub device_restrictions: DeviceRestrictions,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SegmentationComponent {
pub component_tag: u8,
pub pts_offset: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SegmentationDescriptor<'a> {
pub identifier: u32,
pub segmentation_event_id: u32,
pub segmentation_event_cancel_indicator: bool,
pub segmentation_event_id_compliance_indicator: bool,
pub program_segmentation_flag: bool,
pub delivery_restrictions: Option<DeliveryRestrictions>,
pub components: Vec<SegmentationComponent>,
pub segmentation_duration: Option<u64>,
pub segmentation_upid_type: SegmentationUpidType,
pub segmentation_upid: &'a [u8],
pub segmentation_type_id: SegmentationTypeId,
pub segment_num: u8,
pub segments_expected: u8,
pub sub_segments: Option<(u8, u8)>,
}
impl<'a> Default for SegmentationDescriptor<'a> {
fn default() -> Self {
Self {
identifier: CUEI,
segmentation_event_id: 0,
segmentation_event_cancel_indicator: false,
segmentation_event_id_compliance_indicator: true,
program_segmentation_flag: true,
delivery_restrictions: None,
components: Vec::new(),
segmentation_duration: None,
segmentation_upid_type: SegmentationUpidType::NotUsed,
segmentation_upid: &[],
segmentation_type_id: SegmentationTypeId::NotIndicated,
segment_num: 0,
segments_expected: 0,
sub_segments: None,
}
}
}
impl<'a> Parse<'a> for SegmentationDescriptor<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (identifier, body) = header::descriptor_body(bytes, TAG, "segmentation_descriptor")?;
if body.len() < 5 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + 5,
have: bytes.len(),
what: "segmentation_descriptor header",
});
}
let (eid_bytes, rest) = body.split_first_chunk::<4>().ok_or(Error::BufferTooShort {
need: HEADER_LEN + 5,
have: bytes.len(),
what: "segmentation_descriptor header",
})?;
let segmentation_event_id = u32::from_be_bytes(*eid_bytes);
let b = rest[0];
let cancel = b & 0x80 != 0;
let compliance = b & 0x40 != 0;
let mut out = Self {
identifier,
segmentation_event_id,
segmentation_event_cancel_indicator: cancel,
segmentation_event_id_compliance_indicator: compliance,
..Self::default()
};
if cancel {
return Ok(out);
}
if body.len() < 6 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + 6,
have: bytes.len(),
what: "segmentation_descriptor flags",
});
}
let flags = body[5];
out.program_segmentation_flag = flags & 0x80 != 0;
let duration_flag = flags & 0x40 != 0;
let delivery_not_restricted = flags & 0x20 != 0;
if !delivery_not_restricted {
out.delivery_restrictions = Some(DeliveryRestrictions {
web_delivery_allowed: flags & 0x10 != 0,
no_regional_blackout: flags & 0x08 != 0,
archive_allowed: flags & 0x04 != 0,
device_restrictions: DeviceRestrictions::from_bits(flags & 0x03),
});
}
let mut pos = 6;
if !out.program_segmentation_flag {
if body.len() < pos + 1 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + pos + 1,
have: bytes.len(),
what: "segmentation_descriptor component_count",
});
}
let count = body[pos] as usize;
pos += 1;
for _ in 0..count {
if body.len() < pos + 6 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + pos + 6,
have: bytes.len(),
what: "segmentation_descriptor component",
});
}
let component_tag = body[pos];
let pts_offset = ((u64::from(body[pos + 1] & 0x01)) << 32)
| (u64::from(body[pos + 2]) << 24)
| (u64::from(body[pos + 3]) << 16)
| (u64::from(body[pos + 4]) << 8)
| u64::from(body[pos + 5]);
out.components.push(SegmentationComponent {
component_tag,
pts_offset,
});
pos += 6;
}
}
if duration_flag {
if body.len() < pos + 5 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + pos + 5,
have: bytes.len(),
what: "segmentation_descriptor segmentation_duration",
});
}
let d = (u64::from(body[pos]) << 32)
| (u64::from(body[pos + 1]) << 24)
| (u64::from(body[pos + 2]) << 16)
| (u64::from(body[pos + 3]) << 8)
| u64::from(body[pos + 4]);
out.segmentation_duration = Some(d);
pos += 5;
}
if body.len() < pos + 2 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + pos + 2,
have: bytes.len(),
what: "segmentation_descriptor upid header",
});
}
out.segmentation_upid_type = SegmentationUpidType::from_u8(body[pos]);
let upid_len = body[pos + 1] as usize;
pos += 2;
if body.len() < pos + upid_len {
return Err(Error::LengthOverflow {
declared: upid_len,
available: body.len().saturating_sub(pos),
what: "segmentation_descriptor segmentation_upid",
});
}
out.segmentation_upid = &body[pos..pos + upid_len];
pos += upid_len;
if body.len() < pos + 3 {
return Err(Error::BufferTooShort {
need: HEADER_LEN + pos + 3,
have: bytes.len(),
what: "segmentation_descriptor type/segment",
});
}
out.segmentation_type_id = SegmentationTypeId::from_u8(body[pos]);
out.segment_num = body[pos + 1];
out.segments_expected = body[pos + 2];
pos += 3;
if body.len() >= pos + 2 {
out.sub_segments = Some((body[pos], body[pos + 1]));
pos += 2;
}
let _ = pos;
Ok(out)
}
}
impl<'a> SegmentationDescriptor<'a> {
fn body_len(&self) -> usize {
if self.segmentation_event_cancel_indicator {
return 5; }
let mut len = 6; if !self.program_segmentation_flag {
len += 1; len += self.components.len() * 6;
}
if self.segmentation_duration.is_some() {
len += 5;
}
len += 2 + self.segmentation_upid.len(); len += 3; if self.sub_segments.is_some() {
len += 2;
}
len
}
#[must_use]
pub fn mpu(&self) -> Option<Result<Mpu<'a>>> {
if self.segmentation_upid_type != SegmentationUpidType::Mpu {
return None;
}
let bytes = self.segmentation_upid;
Some(
bytes
.split_first_chunk::<MPU_FORMAT_IDENTIFIER_LEN>()
.ok_or(Error::BufferTooShort {
need: MPU_FORMAT_IDENTIFIER_LEN,
have: bytes.len(),
what: "MPU() format_identifier",
})
.map(|(fi_bytes, private_data)| Mpu {
format_identifier: u32::from_be_bytes(*fi_bytes),
private_data,
}),
)
}
#[must_use]
pub fn mid(&self) -> Option<Result<Vec<MidUpid<'a>>>> {
if self.segmentation_upid_type != SegmentationUpidType::Mid {
return None;
}
let mut entries = Vec::new();
let mut pos = 0;
let bytes = self.segmentation_upid;
while pos < bytes.len() {
if bytes.len() - pos < MID_ENTRY_HEADER_LEN {
return Some(Err(Error::BufferTooShort {
need: pos + MID_ENTRY_HEADER_LEN,
have: bytes.len(),
what: "MID() entry header",
}));
}
let upid_type = SegmentationUpidType::from_u8(bytes[pos]);
let entry_len = bytes[pos + 1] as usize;
pos += MID_ENTRY_HEADER_LEN;
if bytes.len() - pos < entry_len {
return Some(Err(Error::LengthOverflow {
declared: entry_len,
available: bytes.len() - pos,
what: "MID() entry segmentation_upid",
}));
}
let upid = &bytes[pos..pos + entry_len];
pos += entry_len;
entries.push(MidUpid { upid_type, upid });
}
Some(Ok(entries))
}
}
impl Serialize for SegmentationDescriptor<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
HEADER_LEN + self.body_len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let body_len = self.body_len();
if body_len + 4 > u8::MAX as usize {
return Err(Error::InvalidValue {
field: "segmentation_descriptor.descriptor_length",
reason: "descriptor body exceeds 8-bit descriptor_length",
});
}
header::write_header(buf, TAG, self.identifier, body_len);
let mut pos = HEADER_LEN;
buf[pos..pos + 4].copy_from_slice(&self.segmentation_event_id.to_be_bytes());
buf[pos + 4] = (u8::from(self.segmentation_event_cancel_indicator) << 7)
| (u8::from(self.segmentation_event_id_compliance_indicator) << 6)
| 0x3F;
pos += 5;
if self.segmentation_event_cancel_indicator {
return Ok(need);
}
let duration_flag = self.segmentation_duration.is_some();
let mut flags =
(u8::from(self.program_segmentation_flag) << 7) | (u8::from(duration_flag) << 6);
match &self.delivery_restrictions {
Some(dr) => {
flags |= u8::from(dr.web_delivery_allowed) << 4;
flags |= u8::from(dr.no_regional_blackout) << 3;
flags |= u8::from(dr.archive_allowed) << 2;
flags |= dr.device_restrictions.bits() & 0x03;
}
None => {
flags |= 0x20 | 0x1F;
}
}
buf[pos] = flags;
pos += 1;
if !self.program_segmentation_flag {
buf[pos] = self.components.len() as u8;
pos += 1;
for c in &self.components {
buf[pos] = c.component_tag;
let o = c.pts_offset & ((1u64 << 33) - 1);
buf[pos + 1] = 0xFE | ((o >> 32) as u8 & 0x01);
buf[pos + 2] = (o >> 24) as u8;
buf[pos + 3] = (o >> 16) as u8;
buf[pos + 4] = (o >> 8) as u8;
buf[pos + 5] = o as u8;
pos += 6;
}
}
if let Some(d) = self.segmentation_duration {
let d = d & ((1u64 << 40) - 1);
buf[pos] = (d >> 32) as u8;
buf[pos + 1] = (d >> 24) as u8;
buf[pos + 2] = (d >> 16) as u8;
buf[pos + 3] = (d >> 8) as u8;
buf[pos + 4] = d as u8;
pos += 5;
}
buf[pos] = self.segmentation_upid_type.to_u8();
buf[pos + 1] = self.segmentation_upid.len() as u8;
pos += 2;
buf[pos..pos + self.segmentation_upid.len()].copy_from_slice(self.segmentation_upid);
pos += self.segmentation_upid.len();
buf[pos] = self.segmentation_type_id.to_u8();
buf[pos + 1] = self.segment_num;
buf[pos + 2] = self.segments_expected;
pos += 3;
if let Some((sn, se)) = self.sub_segments {
buf[pos] = sn;
buf[pos + 1] = se;
pos += 2;
}
debug_assert_eq!(pos, need);
Ok(need)
}
}
impl<'a> SpliceDescriptorDef<'a> for SegmentationDescriptor<'a> {
const TAG: u8 = TAG;
const NAME: &'static str = "SEGMENTATION";
}
#[cfg(test)]
mod tests {
use super::*;
fn rt(d: &SegmentationDescriptor) {
let bytes = d.to_bytes();
assert_eq!(bytes.len(), d.serialized_len());
assert_eq!(bytes[0], TAG);
assert_eq!(bytes[1] as usize, 4 + d.body_len());
let back = SegmentationDescriptor::parse(&bytes).unwrap();
assert_eq!(*d, back);
assert_eq!(back.to_bytes(), bytes);
}
#[test]
fn round_trip_cancel() {
rt(&SegmentationDescriptor {
segmentation_event_id: 0x1234,
segmentation_event_cancel_indicator: true,
..Default::default()
});
}
#[test]
fn round_trip_program_with_duration_and_upid() {
rt(&SegmentationDescriptor {
segmentation_event_id: 0x4800_000A,
segmentation_event_id_compliance_indicator: false,
program_segmentation_flag: true,
delivery_restrictions: Some(DeliveryRestrictions {
web_delivery_allowed: false,
no_regional_blackout: true,
archive_allowed: true,
device_restrictions: DeviceRestrictions::RestrictGroup1,
}),
segmentation_duration: Some(90_000 * 30),
segmentation_upid_type: SegmentationUpidType::AdId,
segmentation_upid: b"ABCD12345678",
segmentation_type_id: SegmentationTypeId::ProviderPlacementOpportunityStart,
segment_num: 1,
segments_expected: 1,
sub_segments: Some((1, 2)),
..Default::default()
});
}
#[test]
fn round_trip_no_restrictions_no_subsegments() {
rt(&SegmentationDescriptor {
segmentation_event_id: 7,
delivery_restrictions: None,
segmentation_type_id: SegmentationTypeId::ProgramStart,
segment_num: 1,
segments_expected: 1,
..Default::default()
});
}
#[test]
fn round_trip_component_mode() {
rt(&SegmentationDescriptor {
segmentation_event_id: 9,
program_segmentation_flag: false,
components: vec![
SegmentationComponent {
component_tag: 1,
pts_offset: 0x1_0000,
},
SegmentationComponent {
component_tag: 2,
pts_offset: 0,
},
],
segmentation_upid_type: SegmentationUpidType::NotUsed,
segmentation_type_id: SegmentationTypeId::BreakStart,
..Default::default()
});
}
#[test]
fn mpu_accessor_decodes_correctly() {
const FORMAT_ID: u32 = 0x4142_4344;
let upid_bytes: &[u8] = &[0x41, 0x42, 0x43, 0x44, 0x01, 0x02, 0x03];
let d = SegmentationDescriptor {
segmentation_event_id: 0x1,
segmentation_upid_type: SegmentationUpidType::Mpu,
segmentation_upid: upid_bytes,
segmentation_type_id: SegmentationTypeId::ContentIdentification,
segment_num: 1,
segments_expected: 1,
..Default::default()
};
let mpu = d.mpu().expect("Some").expect("Ok");
assert_eq!(mpu.format_identifier, FORMAT_ID);
assert_eq!(mpu.private_data, &[0x01u8, 0x02, 0x03]);
let d_ti = SegmentationDescriptor {
segmentation_upid_type: SegmentationUpidType::Ti,
segmentation_upid: &[0u8; 8],
..d.clone()
};
assert!(d_ti.mpu().is_none());
rt(&d);
}
#[test]
fn mpu_accessor_truncated_returns_err() {
let d = SegmentationDescriptor {
segmentation_event_id: 0x2,
segmentation_upid_type: SegmentationUpidType::Mpu,
segmentation_upid: &[0xAA, 0xBB], segmentation_type_id: SegmentationTypeId::NotIndicated,
..Default::default()
};
assert!(matches!(
d.mpu(),
Some(Err(Error::BufferTooShort {
what: "MPU() format_identifier",
..
}))
));
}
#[test]
fn mid_accessor_decodes_two_entries() {
let mut upid_bytes = Vec::new();
upid_bytes.push(0x03u8); upid_bytes.push(12u8); upid_bytes.extend_from_slice(b"ABCD12345678");
upid_bytes.push(0x08u8); upid_bytes.push(8u8); upid_bytes.extend_from_slice(&[0u8; 8]);
let d = SegmentationDescriptor {
segmentation_event_id: 0x3,
segmentation_upid_type: SegmentationUpidType::Mid,
segmentation_upid: &upid_bytes,
segmentation_type_id: SegmentationTypeId::ProgramStart,
segment_num: 1,
segments_expected: 1,
..Default::default()
};
let entries = d.mid().expect("Some").expect("Ok");
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].upid_type, SegmentationUpidType::AdId);
assert_eq!(entries[0].upid, b"ABCD12345678");
assert_eq!(entries[1].upid_type, SegmentationUpidType::Ti);
assert_eq!(entries[1].upid, &[0u8; 8]);
let d_adid = SegmentationDescriptor {
segmentation_upid_type: SegmentationUpidType::AdId,
..d.clone()
};
assert!(d_adid.mid().is_none());
rt(&d);
}
#[test]
fn mid_accessor_truncated_entry_returns_err() {
let upid_bytes: &[u8] = &[
0x03u8, 5u8, 0xAA, 0xBB, ];
let d = SegmentationDescriptor {
segmentation_event_id: 0x4,
segmentation_upid_type: SegmentationUpidType::Mid,
segmentation_upid: upid_bytes,
segmentation_type_id: SegmentationTypeId::NotIndicated,
..Default::default()
};
assert!(matches!(
d.mid(),
Some(Err(Error::LengthOverflow {
what: "MID() entry segmentation_upid",
..
}))
));
}
}