use crate::{
bail, data,
error::Result,
parser,
parser::{Mp4Parser, ParsedBox},
pssh::{playready, widevine},
};
use base64::Engine;
const COMMON_SYSTEM_ID: &str = "1077efecc0b24d02ace33c1e52e2fb4b";
const PLAYREADY_SYSTEM_ID: &str = "9a04f07998404286ab92e65be0885f95";
const WIDEVINE_SYSTEM_ID: &str = "edef8ba979d64acea3c827dcd51d21ed";
#[derive(Debug)]
pub struct PsshBox {
pub boxes: Vec<PsshData>,
}
impl PsshBox {
pub fn from_init(data: &[u8]) -> Result<Self> {
let boxes = data!(Vec::new());
Mp4Parser::new()
.base_box("moov", parser::children)
.base_box("moof", parser::children)
.full_box("pssh", {
let boxes = boxes.clone();
move |mut box_| {
Self::parse(&mut box_, &mut boxes.borrow_mut())?;
Ok(())
}
})
.parse(data, false, false)?;
Ok(Self {
boxes: boxes.take(),
})
}
fn parse(box_: &mut ParsedBox, boxes: &mut Vec<PsshData>) -> Result<()> {
let Some(box_version) = box_.version else {
bail!("PSSH boxes are full boxes and must have a valid version.");
};
if box_.flags.is_none() {
bail!("PSSH boxes are full boxes and must have a valid flag.");
}
if box_version > 1 {
bail!("Unrecognized PSSH version found!");
}
let system_id = hex::encode(box_.reader.read_bytes_u8(16)?);
if box_version > 0 {
let mut data = PsshData {
data: box_.full_data(),
key_ids: Vec::new(),
system_id: if system_id == COMMON_SYSTEM_ID {
SystemId::Common
} else {
SystemId::Other(system_id.to_owned())
},
};
let num_key_ids = box_.reader.read_u32()?;
for _ in 0..num_key_ids {
let key_id = hex::encode(box_.reader.read_bytes_u8(16)?);
data.key_ids.push(key_id);
}
boxes.push(data);
}
let pssh_data_size = box_.reader.read_u32()?;
let pssh_data = box_.reader.read_bytes_u8(pssh_data_size as usize)?;
let mut key_ids = Vec::new();
match system_id.as_str() {
PLAYREADY_SYSTEM_ID => key_ids = playready::parse_key_ids(&pssh_data)?,
WIDEVINE_SYSTEM_ID => key_ids = widevine::parse_key_ids(&pssh_data)?,
_ => (),
}
boxes.push(PsshData {
data: box_.full_data(),
key_ids,
system_id: match system_id.as_str() {
PLAYREADY_SYSTEM_ID => SystemId::PlayReady,
WIDEVINE_SYSTEM_ID => SystemId::WideVine,
_ => SystemId::Other(system_id.to_owned()),
},
});
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SystemId {
Common,
Other(String),
PlayReady,
WideVine,
}
impl std::fmt::Display for SystemId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
SystemId::Common => "cen",
SystemId::Other(x) => x,
SystemId::PlayReady => "prd",
SystemId::WideVine => "wvd",
}
)
}
}
#[derive(Debug, Clone)]
pub struct PsshData {
pub data: Vec<u8>,
pub key_ids: Vec<String>,
pub system_id: SystemId,
}
impl PartialEq for PsshData {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl PsshData {
pub fn as_base64(&self) -> String {
base64::engine::general_purpose::STANDARD.encode(&self.data)
}
}