use super::error::{BuilderError, BuilderResult, DurationExt};
use crate::descriptors::SegmentationDescriptor;
use crate::types::SegmentationType;
use crate::upid::SegmentationUpidType;
use std::time::Duration;
#[derive(Debug)]
pub struct SegmentationDescriptorBuilder {
segmentation_event_id: Option<u32>,
program_segmentation: bool,
duration: Option<Duration>,
delivery_restrictions: Option<DeliveryRestrictions>,
upid: Option<Upid>,
segmentation_type: SegmentationType,
segment_num: u8,
segments_expected: u8,
sub_segmentation: Option<SubSegmentation>,
}
#[derive(Debug, Clone)]
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)]
pub enum DeviceRestrictions {
None,
RestrictGroup1,
RestrictGroup2,
RestrictBoth,
}
#[derive(Debug, Clone)]
pub struct SubSegmentation {
pub sub_segment_num: u8,
pub sub_segments_expected: u8,
}
#[derive(Debug, Clone)]
pub enum Upid {
None,
UserDefinedDeprecated(Vec<u8>),
Isci(String),
AdId(String),
Umid([u8; 32]),
IsanDeprecated([u8; 12]),
Isan([u8; 12]),
Tid(String),
AiringId(u64),
Adi(Vec<u8>),
Eidr([u8; 12]),
AtscContentIdentifier(Vec<u8>),
Mpu(Vec<u8>),
Mid(Vec<u8>),
AdsInformation(Vec<u8>),
Uri(String),
Uuid([u8; 16]),
Scr(Vec<u8>),
Reserved(u8, Vec<u8>),
}
impl SegmentationDescriptorBuilder {
pub fn new(event_id: u32, segmentation_type: SegmentationType) -> Self {
Self {
segmentation_event_id: Some(event_id),
program_segmentation: true,
duration: None,
delivery_restrictions: None,
upid: None,
segmentation_type,
segment_num: 1,
segments_expected: 1,
sub_segmentation: None,
}
}
pub fn cancel_event(mut self) -> Self {
self.segmentation_event_id = None;
self
}
pub fn duration(mut self, duration: Duration) -> BuilderResult<Self> {
let ticks = duration.to_pts_ticks();
if ticks > 0x1_FFFF_FFFF {
return Err(BuilderError::DurationTooLarge {
field: "segmentation_duration",
duration,
});
}
self.duration = Some(duration);
Ok(self)
}
pub fn no_restrictions(mut self) -> Self {
self.delivery_restrictions = None;
self
}
pub fn delivery_restrictions(mut self, restrictions: DeliveryRestrictions) -> Self {
self.delivery_restrictions = Some(restrictions);
self
}
pub fn upid(mut self, upid: Upid) -> BuilderResult<Self> {
match &upid {
Upid::Isci(s) | Upid::AdId(s) | Upid::Tid(s) => {
if s.len() != 12 {
return Err(BuilderError::InvalidUpidLength {
expected: 12,
actual: s.len(),
});
}
}
Upid::Uri(s) => {
if s.is_empty() || s.len() > 255 {
return Err(BuilderError::InvalidValue {
field: "uri",
reason: "URI must be 1-255 bytes".to_string(),
});
}
}
Upid::UserDefinedDeprecated(data)
| Upid::Adi(data)
| Upid::AtscContentIdentifier(data)
| Upid::Mpu(data)
| Upid::Mid(data)
| Upid::AdsInformation(data)
| Upid::Scr(data) => {
if data.len() > 255 {
return Err(BuilderError::InvalidValue {
field: "upid_data",
reason: "UPID data must be <= 255 bytes".to_string(),
});
}
}
Upid::Reserved(_, data) => {
if data.len() > 255 {
return Err(BuilderError::InvalidValue {
field: "reserved_upid_data",
reason: "Reserved UPID data must be <= 255 bytes".to_string(),
});
}
}
_ => {} }
self.upid = Some(upid);
Ok(self)
}
pub fn segment(mut self, num: u8, expected: u8) -> Self {
self.segment_num = num;
self.segments_expected = expected;
self
}
pub fn sub_segment(mut self, num: u8, expected: u8) -> Self {
self.sub_segmentation = Some(SubSegmentation {
sub_segment_num: num,
sub_segments_expected: expected,
});
self
}
pub fn build(self) -> BuilderResult<SegmentationDescriptor> {
let (event_id, cancel) = match self.segmentation_event_id {
Some(id) => (id, false),
None => (0, true),
};
let (delivery_not_restricted, web, blackout, archive, device) =
match self.delivery_restrictions {
None => (true, None, None, None, None),
Some(r) => (
false,
Some(r.web_delivery_allowed),
Some(r.no_regional_blackout),
Some(r.archive_allowed),
Some(r.device_restrictions.into()),
),
};
let (upid_type, upid_bytes) = self.upid.unwrap_or(Upid::None).into();
let duration_ticks = match self.duration {
Some(duration) => {
let ticks = duration.to_pts_ticks();
if ticks > 0x1_FFFF_FFFF {
return Err(BuilderError::DurationTooLarge {
field: "segmentation_duration",
duration,
});
}
Some(ticks)
}
None => None,
};
Ok(SegmentationDescriptor {
segmentation_event_id: event_id,
segmentation_event_cancel_indicator: cancel,
program_segmentation_flag: self.program_segmentation,
segmentation_duration_flag: self.duration.is_some(),
delivery_not_restricted_flag: delivery_not_restricted,
web_delivery_allowed_flag: web,
no_regional_blackout_flag: blackout,
archive_allowed_flag: archive,
device_restrictions: device,
segmentation_duration: duration_ticks,
segmentation_upid_type: upid_type,
segmentation_upid_length: upid_bytes.len() as u8,
segmentation_upid: upid_bytes,
segmentation_type_id: self.segmentation_type.id(),
segmentation_type: self.segmentation_type,
segment_num: self.segment_num,
segments_expected: self.segments_expected,
sub_segment_num: self.sub_segmentation.as_ref().map(|s| s.sub_segment_num),
sub_segments_expected: self
.sub_segmentation
.as_ref()
.map(|s| s.sub_segments_expected),
})
}
}
impl From<DeviceRestrictions> for u8 {
fn from(restrictions: DeviceRestrictions) -> Self {
match restrictions {
DeviceRestrictions::None => 0x00,
DeviceRestrictions::RestrictGroup1 => 0x01,
DeviceRestrictions::RestrictGroup2 => 0x02,
DeviceRestrictions::RestrictBoth => 0x03,
}
}
}
impl From<Upid> for (SegmentationUpidType, Vec<u8>) {
fn from(upid: Upid) -> Self {
match upid {
Upid::None => (SegmentationUpidType::NotUsed, vec![]),
Upid::UserDefinedDeprecated(data) => {
(SegmentationUpidType::UserDefinedDeprecated, data)
}
Upid::Isci(s) => (SegmentationUpidType::ISCI, s.into_bytes()),
Upid::AdId(s) => (SegmentationUpidType::AdID, s.into_bytes()),
Upid::Umid(bytes) => (SegmentationUpidType::UMID, bytes.to_vec()),
Upid::IsanDeprecated(bytes) => (SegmentationUpidType::ISANDeprecated, bytes.to_vec()),
Upid::Isan(bytes) => (SegmentationUpidType::ISAN, bytes.to_vec()),
Upid::Tid(s) => (SegmentationUpidType::TID, s.into_bytes()),
Upid::AiringId(id) => (SegmentationUpidType::AiringID, id.to_be_bytes().to_vec()),
Upid::Adi(data) => (SegmentationUpidType::ADI, data),
Upid::Eidr(bytes) => (SegmentationUpidType::EIDR, bytes.to_vec()),
Upid::AtscContentIdentifier(data) => {
(SegmentationUpidType::ATSCContentIdentifier, data)
}
Upid::Mpu(data) => (SegmentationUpidType::MPU, data),
Upid::Mid(data) => (SegmentationUpidType::MID, data),
Upid::AdsInformation(data) => (SegmentationUpidType::ADSInformation, data),
Upid::Uri(s) => (SegmentationUpidType::URI, s.into_bytes()),
Upid::Uuid(bytes) => (SegmentationUpidType::UUID, bytes.to_vec()),
Upid::Scr(data) => (SegmentationUpidType::SCR, data),
Upid::Reserved(type_id, data) => (SegmentationUpidType::Reserved(type_id), data),
}
}
}