1pub mod playready;
33pub mod widevine;
34pub mod irdeto;
35pub mod nagra;
36pub mod wiseplay;
37
38use std::fmt;
39use std::io::{Cursor, Read, Write};
40use hex_literal::hex;
41use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
42use zerocopy::FromBytes;
43use serde::{Serialize, Deserialize};
44use prost::Message;
45use base64::prelude::{Engine as _, BASE64_STANDARD};
46use base64::engine;
47use anyhow::{Result, Context, anyhow};
48use tracing::trace;
49use crate::widevine::WidevinePsshData;
50use crate::playready::PlayReadyPsshData;
51use crate::irdeto::IrdetoPsshData;
52use crate::nagra::NagraPsshData;
53use crate::wiseplay::WisePlayPsshData;
54
55
56pub fn version() -> &'static str {
58 env!("CARGO_PKG_VERSION")
59}
60
61pub trait ToBytes {
62 fn to_bytes(&self) -> Vec<u8>;
63}
64
65#[non_exhaustive]
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub enum PsshData {
69 Widevine(WidevinePsshData),
70 PlayReady(PlayReadyPsshData),
71 Irdeto(IrdetoPsshData),
72 WisePlay(WisePlayPsshData),
73 Nagra(NagraPsshData),
74 Marlin(Vec<u8>),
75 CommonEnc(Vec<u8>),
76 FairPlay(Vec<u8>),
77}
78
79impl ToBytes for PsshData {
80 fn to_bytes(&self) -> Vec<u8> {
81 match self {
82 PsshData::Widevine(wv) => wv.to_bytes(),
83 PsshData::PlayReady(pr) => pr.to_bytes(),
84 PsshData::Irdeto(ir) => ir.to_bytes(),
85 PsshData::WisePlay(c) => c.to_bytes(),
86 PsshData::Nagra(n) => n.to_bytes(),
87 PsshData::Marlin(m) => m.to_vec(),
88 PsshData::CommonEnc(c) => c.to_vec(),
89 PsshData::FairPlay(c) => c.to_vec(),
90 }
91 }
92}
93
94#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
96pub struct DRMSystemId {
97 id: [u8; 16],
98}
99
100impl TryFrom<&[u8]> for DRMSystemId {
101 type Error = ();
102
103 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
104 if let Ok(id) = value.try_into() {
105 Ok(DRMSystemId { id })
106 } else {
107 Err(())
108 }
109 }
110}
111
112impl TryFrom<Vec<u8>> for DRMSystemId {
113 type Error = ();
114
115 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
116 if value.len() == 16 {
117 DRMSystemId::try_from(&value[0..16])
118 } else {
119 Err(())
120 }
121 }
122}
123
124impl TryFrom<&str> for DRMSystemId {
125 type Error = ();
126
127 fn try_from(value: &str) -> Result<Self, Self::Error> {
128 if value.len() == 32 {
129 if let Ok(id) = hex::decode(value) {
130 return DRMSystemId::try_from(id);
131 }
132 }
133 Err(())
134 }
135}
136
137impl ToBytes for DRMSystemId {
138 fn to_bytes(&self) -> Vec<u8> {
139 self.id.into()
140 }
141}
142
143impl fmt::Display for DRMSystemId {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 let family = if self.id == hex!("1077efecc0b24d02ace33c1e52e2fb4b") {
147 "Common"
148 } else if self.id == hex!("69f908af481646ea910ccd5dcccb0a3a") {
149 "CENC"
150 } else if self.id == hex!("edef8ba979d64acea3c827dcd51d21ed") {
151 "Widevine"
152 } else if self.id == hex!("9a04f07998404286ab92e65be0885f95") {
153 "PlayReady"
154 } else if self.id == hex!("6dd8b3c345f44a68bf3a64168d01a4a6") {
155 "ABV"
156 } else if self.id == hex!("f239e769efa348509c16a903c6932efb") {
157 "Adobe Primetime"
158 } else if self.id == hex!("616c7469636173742d50726f74656374") {
159 "Alticast"
160 } else if self.id == hex!("94ce86fb07ff4f43adb893d2fa968ca2") {
161 "Apple FairPlay"
162 } else if self.id == hex!("29701fe43cc74a348c5bae90c7439a47") {
163 "Apple FairPlay-Netflix variant"
166 } else if self.id == hex!("3ea8778f77424bf9b18be834b2acbd47") {
167 "ClearKey AES-128"
168 } else if self.id == hex!("be58615b19c4468488b3c8c57e99e957") {
169 "ClearKey SAMPLE-AES"
170 } else if self.id == hex!("e2719d58a985b3c9781ab030af78d30e") {
171 "ClearKey DASH-IF"
172 } else if self.id == hex!("45d481cb8fe049c0ada9ab2d2455b2f2") {
173 "CoreTrust"
174 } else if self.id == hex!("80a6be7e14484c379e70d5aebe04c8d2") {
175 "Irdeto"
176 } else if self.id == hex!("5e629af538da4063897797ffbd9902d4") {
177 "Marlin"
178 } else if self.id == hex!("adb41c242dbf4a6d958b4457c0d27b95") {
179 "Nagra"
180 } else if self.id == hex!("1f83e1e86ee94f0dba2f5ec4e3ed1a66") {
181 "SecureMedia"
182 } else if self.id == hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") {
183 "WisePlay-ChinaDRM"
186 } else if self.id == hex!("793b79569f944946a94223e7ef7e44b4") {
187 "VisionCrypt"
188 } else {
189 "Unknown"
190 };
191 let hex = hex::encode(self.id);
192 write!(f, "{}/DRMSystemId<{}-{}-{}-{}-{}>",
193 family,
194 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
195 }
196}
197
198impl fmt::Debug for DRMSystemId {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(f, "DRMSystemId<{}>", hex::encode(self.id))
201 }
202}
203
204pub const COMMON_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("1077efecc0b24d02ace33c1e52e2fb4b") };
205pub const CENC_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("69f908af481646ea910ccd5dcccb0a3a") };
206pub const WIDEVINE_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("edef8ba979d64acea3c827dcd51d21ed") };
207pub const PLAYREADY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("9a04f07998404286ab92e65be0885f95") };
208pub const FAIRPLAYNFLX_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("29701fe43cc74a348c5bae90c7439a47") };
209pub const IRDETO_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("80a6be7e14484c379e70d5aebe04c8d2") };
210pub const MARLIN_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("5e629af538da4063897797ffbd9902d4") };
211pub const NAGRA_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("adb41c242dbf4a6d958b4457c0d27b95") };
212pub const WISEPLAY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") };
213
214#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
216pub struct DRMKeyId {
217 id: [u8; 16],
218}
219
220impl TryFrom<&[u8]> for DRMKeyId {
221 type Error = ();
222
223 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
224 if let Ok(id) = value.try_into() {
225 Ok(DRMKeyId { id })
226 } else {
227 Err(())
228 }
229 }
230}
231
232impl TryFrom<Vec<u8>> for DRMKeyId {
233 type Error = ();
234
235 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
236 if value.len() == 16 {
237 DRMKeyId::try_from(&value[0..16])
238 } else {
239 Err(())
240 }
241 }
242}
243
244impl TryFrom<&str> for DRMKeyId {
245 type Error = ();
246
247 fn try_from(value: &str) -> Result<Self, Self::Error> {
248 if value.len() == 32 {
249 if let Ok(id) = hex::decode(value) {
250 return DRMKeyId::try_from(id);
251 }
252 }
253 if value.len() == 36 {
255 let v36 = value.as_bytes();
256 if v36[8] == b'-' &&
257 v36[13] == b'-' &&
258 v36[18] == b'-' &&
259 v36[23] == b'-'
260 {
261 let maybe_hex = value.replace('-', "");
262 if let Ok(id) = hex::decode(maybe_hex) {
263 return DRMKeyId::try_from(id);
264 }
265 }
266 }
267 Err(())
268 }
269}
270
271impl ToBytes for DRMKeyId {
272 fn to_bytes(&self) -> Vec<u8> {
273 self.id.into()
274 }
275}
276
277impl fmt::Display for DRMKeyId {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 let hex = hex::encode(self.id);
281 write!(f, "DRMKeyId<{}-{}-{}-{}-{}>",
282 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
283 }
284}
285
286impl fmt::Debug for DRMKeyId {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "DRMKeyId<{}>", hex::encode(self.id))
289 }
290}
291
292
293#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
295pub struct PsshBox {
296 pub version: u8,
297 pub flags: u32,
298 pub system_id: DRMSystemId,
299 pub key_ids: Vec<DRMKeyId>,
300 pub pssh_data: PsshData,
301}
302
303impl PsshBox {
304 pub fn new_widevine() -> PsshBox {
306 let empty = WidevinePsshData {
307 provider: None,
308 ..Default::default()
309 };
310 PsshBox {
311 version: 1,
312 flags: 0,
313 system_id: WIDEVINE_SYSTEM_ID,
314 key_ids: vec![],
315 pssh_data: PsshData::Widevine(empty),
316 }
317 }
318
319 pub fn new_playready() -> PsshBox {
321 let empty = PlayReadyPsshData::new();
322 PsshBox {
323 version: 1,
324 flags: 0,
325 system_id: PLAYREADY_SYSTEM_ID,
326 key_ids: vec![],
327 pssh_data: PsshData::PlayReady(empty),
328 }
329 }
330
331 pub fn add_key_id(&mut self, kid: DRMKeyId) {
332 self.key_ids.push(kid);
333 }
334
335 pub fn to_base64(self) -> String {
336 BASE64_STANDARD.encode(self.to_bytes())
337 }
338
339 pub fn to_hex(self) -> String {
340 hex::encode(self.to_bytes())
341 }
342}
343
344impl fmt::Display for PsshBox {
347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 let mut keys = Vec::new();
349 if self.version == 1 {
350 for key in &self.key_ids {
351 keys.push(hex::encode(key.id));
352 }
353 }
354 let key_str = match keys.len() {
355 0 => String::from(""),
356 1 => format!("key_id: {}, ", keys.first().unwrap()),
357 _ => format!("key_ids: {}, ", keys.join(", ")),
358 };
359 match &self.pssh_data {
360 PsshData::Widevine(wv) => {
361 let mut items = Vec::new();
362 let json = wv.to_json();
363 if let Some(alg) = json.get("algorithm") {
364 if let Some(a) = alg.as_str() {
365 items.push(String::from(a));
366 }
367 }
368 if let Some(kav) = json.get("key_id") {
371 if let Some(ka) = kav.as_array() {
372 for kv in ka {
373 if let Some(k) = kv.as_str() {
374 keys.push(String::from(k));
375 }
376 }
377 }
378 }
379 if keys.len() == 1 {
380 items.push(format!("key_id: {}", keys.first().unwrap()));
381 }
382 if keys.len() > 1 {
383 items.push(format!("key_ids: {}", keys.join(", ")));
384 }
385 if let Some(jo) = json.as_object() {
386 for (k, v) in jo.iter() {
387 if k.ne("algorithm") && k.ne("key_id") {
388 items.push(format!("{k}: {v}"));
389 }
390 }
391 }
392 write!(f, "WidevinePSSH<{}>", items.join(", "))
393 },
394 PsshData::PlayReady(pr) => write!(f, "PlayReadyPSSH<{key_str}{pr:?}>"),
395 PsshData::Irdeto(pd) => write!(f, "IrdetoPSSH<{key_str}{}>", pd.xml),
396 PsshData::Marlin(pd) => write!(f, " MarlinPSSH<{key_str}pssh data len {} octets>", pd.len()),
397 PsshData::Nagra(pd) => write!(f, "NagraPSSH<{key_str}{pd:?}>"),
398 PsshData::WisePlay(pd) => write!(f, "WisePlayPSSH<{key_str}{}>", pd.json),
399 PsshData::CommonEnc(pd) => write!(f, "CommonPSSH<{key_str}pssh data len {} octets>", pd.len()),
400 PsshData::FairPlay(pd) => write!(f, "FairPlayPSSH<{key_str}pssh data len {} octets>", pd.len()),
401 }
402 }
403}
404
405
406impl ToBytes for PsshBox {
407 #[allow(unused_must_use)]
408 fn to_bytes(self: &PsshBox) -> Vec<u8> {
409 let mut out = Vec::new();
410 let pssh_data_bytes = self.pssh_data.to_bytes();
411 let mut total_length: u32 = 4 + 4 + 4 + 16 + 4 + pssh_data_bytes.len() as u32;
417 if self.version == 1 {
418 total_length += 4 + self.key_ids.len() as u32 * 16;
420 }
421 out.write_u32::<BigEndian>(total_length);
422 out.write_all(b"pssh");
423 let version_and_flags: u32 = self.flags ^ ((self.version as u32) << 24);
424 out.write_u32::<BigEndian>(version_and_flags);
425 out.write_all(&self.system_id.id);
426 if self.version == 1 {
427 out.write_u32::<BigEndian>(self.key_ids.len() as u32);
428 for k in &self.key_ids {
429 out.write_all(&k.id);
430 }
431 }
432 out.write_u32::<BigEndian>(pssh_data_bytes.len() as u32);
433 out.write_all(&pssh_data_bytes);
434 out
435 }
436}
437
438
439#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
440pub struct PsshBoxVec(Vec<PsshBox>);
441
442impl PsshBoxVec {
443 pub fn new() -> PsshBoxVec {
444 PsshBoxVec(Vec::new())
445 }
446
447 pub fn contains(&self, bx: &PsshBox) -> bool {
448 self.0.contains(bx)
449 }
450
451 pub fn add(&mut self, bx: PsshBox) {
452 self.0.push(bx);
453 }
454
455 pub fn len(&self) -> usize {
456 self.0.len()
457 }
458
459 pub fn is_empty(&self) -> bool {
460 self.0.is_empty()
461 }
462
463 pub fn iter(&self) -> impl Iterator<Item=&PsshBox>{
464 self.0.iter()
465 }
466
467 pub fn to_base64(self) -> String {
468 let mut buf = Vec::new();
469 for bx in self.0 {
470 buf.append(&mut bx.to_bytes());
471 }
472 BASE64_STANDARD.encode(buf)
473 }
474
475 pub fn to_hex(self) -> String {
476 let mut buf = Vec::new();
477 for bx in self.0 {
478 buf.append(&mut bx.to_bytes());
479 }
480 hex::encode(buf)
481 }
482}
483
484impl Default for PsshBoxVec {
485 fn default() -> Self {
486 Self::new()
487 }
488}
489
490impl IntoIterator for PsshBoxVec {
491 type Item = PsshBox;
492 type IntoIter = std::vec::IntoIter<Self::Item>;
493
494 fn into_iter(self) -> Self::IntoIter {
495 self.0.into_iter()
496 }
497}
498
499impl std::ops::Index<usize> for PsshBoxVec {
500 type Output = PsshBox;
501
502 fn index(&self, index: usize) -> &PsshBox {
503 &self.0[index]
504 }
505}
506
507impl fmt::Display for PsshBoxVec {
508 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
509 let mut items = Vec::new();
510 for pssh in self.iter() {
511 items.push(pssh.to_string());
512 }
513 write!(f, "{}", items.join("\n"))
515 }
516}
517
518pub fn from_base64(init_data: &str) -> Result<PsshBoxVec> {
524 let b64_tolerant_config = engine::GeneralPurposeConfig::new()
525 .with_decode_allow_trailing_bits(true)
526 .with_decode_padding_mode(engine::DecodePaddingMode::Indifferent);
527 let b64_tolerant_engine = engine::GeneralPurpose::new(&base64::alphabet::STANDARD, b64_tolerant_config);
528 if init_data.len() < 8 {
529 return Err(anyhow!("insufficient length for init data"));
530 }
531 if let Ok(buf) = b64_tolerant_engine.decode(init_data) {
533 return from_bytes(&buf);
534 }
535 let total_len = init_data.len();
539 let mut start = 0;
540 let mut boxes = Vec::new();
541 while start < total_len - 1 {
542 let buf = b64_tolerant_engine.decode(&init_data[start..start+7])
543 .context("base64 decoding first 32-bit length word")?;
544 let mut rdr = Cursor::new(buf);
545 let box_size: u32 = rdr.read_u32::<BigEndian>()
546 .context("reading PSSH box size")?;
547 trace!("box size from header = {box_size}");
548 let wanted_octets = (box_size.div_ceil(3) * 4) as usize;
550 let end = start + wanted_octets;
551 trace!("attempting to decode {wanted_octets} octets out of {}", init_data.len());
552 if end > init_data.len() {
553 return Err(anyhow!("insufficient length for init data (wanted {end}, have {})", init_data.len()));
556 }
557 let buf = b64_tolerant_engine.decode(&init_data[start..end])
558 .context("decoding base64")?;
559 let bx = from_bytes(&buf)
560 .context("parsing the PSSH initialization data")?;
561 assert!(bx.len() == 1);
562 trace!("Got one box {}", bx[0].clone());
563 boxes.push(bx[0].clone());
564 start = end;
565 }
566 Ok(PsshBoxVec(boxes))
567}
568
569pub fn from_hex(init_data: &str) -> Result<PsshBoxVec> {
571 let buf = hex::decode(init_data)
572 .context("decoding hex")?;
573 from_bytes(&buf)
574 .context("parsing the PSSH initialization_data")
575}
576
577fn read_pssh_box(rdr: &mut Cursor<&[u8]>) -> Result<PsshBox> {
579 let size: u32 = rdr.read_u32::<BigEndian>()
580 .context("reading PSSH box size")?;
581 trace!("PSSH box of size {size} octets");
582 let mut box_header = [0u8; 4];
583 rdr.read_exact(&mut box_header)
584 .context("reading box header")?;
585 if !box_header.eq(b"pssh") {
587 return Err(anyhow!("expecting BMFF header"));
588 }
589 let version_and_flags: u32 = rdr.read_u32::<BigEndian>()
590 .context("reading PSSH version/flags")?;
591 let version: u8 = (version_and_flags >> 24).try_into().unwrap();
592 trace!("PSSH box version {version}");
593 if version > 1 {
594 return Err(anyhow!("unknown PSSH version {version}"));
595 }
596 let mut system_id_buf = [0u8; 16];
597 rdr.read_exact(&mut system_id_buf)
598 .context("reading system_id")?;
599 let system_id = DRMSystemId { id: system_id_buf };
600 let mut key_ids = Vec::new();
601 if version == 1 {
602 let mut kid_count = rdr.read_u32::<BigEndian>()
603 .context("reading KID count")?;
604 trace!("PSSH box has {kid_count} KIDs in box header");
605 while kid_count > 0 {
606 let mut key = [0u8; 16];
607 rdr.read_exact(&mut key)
608 .context("reading key_id")?;
609 key_ids.push(DRMKeyId { id: key });
610 kid_count -= 1;
611 }
612 }
613 let pssh_data_len = rdr.read_u32::<BigEndian>()
614 .context("reading PSSH data length")?;
615 trace!("PSSH box data length {pssh_data_len} octets");
616 let mut pssh_data = Vec::new();
617 rdr.take(pssh_data_len.into()).read_to_end(&mut pssh_data)
618 .context("extracting PSSH data")?;
619 match system_id {
620 WIDEVINE_SYSTEM_ID => {
621 let wv_pssh_data = WidevinePsshData::decode(Cursor::new(pssh_data))
622 .context("parsing Widevine PSSH data")?;
623 Ok(PsshBox {
624 version,
625 flags: version_and_flags & 0xF,
626 system_id,
627 key_ids,
628 pssh_data: PsshData::Widevine(wv_pssh_data),
629 })
630 },
631 PLAYREADY_SYSTEM_ID => {
632 let pr_pssh_data = playready::parse_pssh_data(&pssh_data)
633 .context("parsing PlayReady PSSH data")?;
634 Ok(PsshBox {
635 version,
636 flags: version_and_flags & 0xF,
637 system_id,
638 key_ids,
639 pssh_data: PsshData::PlayReady(pr_pssh_data),
640 })
641 },
642 IRDETO_SYSTEM_ID => {
643 let ir_pssh_data = irdeto::parse_pssh_data(&pssh_data)
644 .context("parsing Irdeto PSSH data")?;
645 Ok(PsshBox {
646 version,
647 flags: version_and_flags & 0xF,
648 system_id,
649 key_ids,
650 pssh_data: PsshData::Irdeto(ir_pssh_data),
651 })
652 },
653 MARLIN_SYSTEM_ID => {
654 Ok(PsshBox {
655 version,
656 flags: version_and_flags & 0xF,
657 system_id,
658 key_ids,
659 pssh_data: PsshData::Marlin(pssh_data),
660 })
661 },
662 NAGRA_SYSTEM_ID => {
663 let pd = nagra::parse_pssh_data(&pssh_data)
664 .context("parsing Nagra PSSH data")?;
665 Ok(PsshBox {
666 version,
667 flags: version_and_flags & 0xF,
668 system_id,
669 key_ids,
670 pssh_data: PsshData::Nagra(pd),
671 })
672 },
673 WISEPLAY_SYSTEM_ID => {
674 let cdrm_pssh_data = wiseplay::parse_pssh_data(&pssh_data)
675 .context("parsing WisePlay PSSH data")?;
676 Ok(PsshBox {
677 version,
678 flags: version_and_flags & 0xF,
679 system_id,
680 key_ids,
681 pssh_data: PsshData::WisePlay(cdrm_pssh_data),
682 })
683 },
684 COMMON_SYSTEM_ID => {
685 Ok(PsshBox {
686 version,
687 flags: version_and_flags & 0xF,
688 system_id,
689 key_ids,
690 pssh_data: PsshData::CommonEnc(pssh_data),
691 })
692 },
693 FAIRPLAYNFLX_SYSTEM_ID => {
694 Ok(PsshBox {
695 version,
696 flags: version_and_flags & 0xF,
697 system_id,
698 key_ids,
699 pssh_data: PsshData::FairPlay(pssh_data),
700 })
701 },
702 _ => Err(anyhow!("can't parse this system_id type: {:?}", system_id)),
703 }
704}
705
706pub fn from_bytes(init_data: &[u8]) -> Result<PsshBoxVec> {
709 let total_len = init_data.len();
710 let mut rdr = Cursor::new(init_data);
711 let mut boxes = PsshBoxVec::new();
712 while (rdr.position() as usize) < total_len - 1 {
713 let bx = read_pssh_box(&mut rdr)?;
714 boxes.add(bx.clone());
715 trace!("Read one box {bx} from bytes, remaining {} octets", total_len as u64 - rdr.position());
716 let pos = rdr.position() as usize;
717 if let Some(remaining) = &rdr.get_ref().get(pos..total_len) {
718 if remaining.iter().all(|b| *b == 0) {
720 break;
721 }
722 }
723 }
724 Ok(boxes)
725}
726
727pub fn from_buffer(init_data: &[u8]) -> Result<PsshBoxVec> {
730 let total_len = init_data.len();
731 let mut rdr = Cursor::new(init_data);
732 let mut boxes = PsshBoxVec::new();
733 while (rdr.position() as usize) < total_len - 1 {
734 if let Ok(bx) = read_pssh_box(&mut rdr) {
735 boxes.add(bx);
736 } else {
737 break;
738 }
739 }
740 Ok(boxes)
741}
742
743pub fn find_iter(buffer: &[u8]) -> impl Iterator<Item = usize> + '_ {
746 use bstr::ByteSlice;
747
748 buffer.find_iter(b"pssh")
749 .filter(|offset| {
750 if offset+24 > buffer.len() {
751 return false;
752 }
753 let start = offset - 4;
754 let mut rdr = Cursor::new(&buffer[start..]);
755 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
756 let end = start + size as usize;
757 from_bytes(&buffer[start..end]).is_ok()
758 })
759 .map(|offset| offset - 4)
760}
761
762pub fn pprint(pssh: &PsshBox) {
764 println!("PSSH Box v{}", pssh.version);
765 println!(" SystemID: {}", pssh.system_id);
766 if pssh.version == 1 {
767 for key in &pssh.key_ids {
768 println!(" Key ID: {}", key);
769 }
770 }
771 match &pssh.pssh_data {
772 PsshData::Widevine(wv) => println!(" {wv:?}"),
773 PsshData::PlayReady(pr) => println!(" {pr:?}"),
774 PsshData::Irdeto(pd) => {
775 println!("Irdeto XML: {}", pd.xml);
776 },
777 PsshData::Marlin(pd) => {
778 println!(" Marlin PSSH data ({} octets)", pd.len());
779 if !pd.is_empty() {
780 println!("== Hexdump of pssh data ==");
781 let mut hxbuf = Vec::new();
782 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
783 println!("{}", String::from_utf8_lossy(&hxbuf));
784 }
785 },
786 PsshData::Nagra(pd) => println!(" {pd:?}"),
787 PsshData::WisePlay(pd) => {
788 println!(" WisePlay JSON: {}", pd.json);
789 },
790 PsshData::CommonEnc(pd) => {
791 println!(" Common PSSH data ({} octets)", pd.len());
792 if !pd.is_empty() {
793 println!("== Hexdump of pssh data ==");
794 let mut hxbuf = Vec::new();
795 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
796 println!("{}", String::from_utf8_lossy(&hxbuf));
797 }
798 },
799 PsshData::FairPlay(pd) => {
800 println!(" FairPlay PSSH data ({} octets)", pd.len());
801 if !pd.is_empty() {
802 println!("== Hexdump of pssh data ==");
803 let mut hxbuf = Vec::new();
804 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
805 println!("{}", String::from_utf8_lossy(&hxbuf));
806 }
807 },
808 }
809}