PSSH: Fix loading of PlayReadyHeaders

Previously it would load PlayReadyHeader data under Widevine's SystemId breaking all PlayReady checks.

The actual PlayReadyHeader init_data still needs code to parse it into an object.
This commit is contained in:
rlaphoenix 2022-12-26 23:27:51 +00:00
parent e31ba61302
commit 3a910bd03a
1 changed files with 35 additions and 14 deletions

View File

@ -24,13 +24,19 @@ class PSSH:
def __init__(self, data: Union[Container, str, bytes], strict: bool = False): def __init__(self, data: Union[Container, str, bytes], strict: bool = False):
""" """
Load a PSSH box or Widevine Cenc Header data as a new v0 PSSH box. Load a PSSH box, WidevineCencHeader, or PlayReadyHeader.
When loading a WidevineCencHeader or PlayReadyHeader, a new v0 PSSH box will be
created and the header will be parsed and stored in the init_data field. However,
PlayReadyHeaders (and PlayReadyObjects) are not yet currently parsed and are
stored as bytes.
[Strict mode (strict=True)] [Strict mode (strict=True)]
Supports the following forms of input data in either Base64 or Bytes form: Supports the following forms of input data in either Base64 or Bytes form:
- Full PSSH mp4 boxes (as defined by pymp4 Box). - Full PSSH mp4 boxes (as defined by pymp4 Box).
- Full Widevine Cenc Headers (as defined by WidevinePsshData proto). - Full Widevine Cenc Headers (as defined by WidevinePsshData proto).
- Full PlayReady Objects and Headers (as defined by Microsoft Docs).
[Lenient mode (strict=False, default)] [Lenient mode (strict=False, default)]
@ -77,14 +83,6 @@ class PSSH:
cenc_header = cenc_header.SerializeToString() cenc_header = cenc_header.SerializeToString()
if cenc_header != data: # not actually a WidevinePsshData if cenc_header != data: # not actually a WidevinePsshData
raise DecodeError() raise DecodeError()
except DecodeError: # not a widevine cenc header
if strict:
raise DecodeError(f"Could not parse data as a {Container} nor a {WidevinePsshData}.")
# Data is not a Widevine Cenc Header, it's something custom.
# The license server likely has something custom to parse it.
# See doc-string about Lenient mode for more information.
cenc_header = data
box = Box.parse(Box.build(dict( box = Box.parse(Box.build(dict(
type=b"pssh", type=b"pssh",
version=0, version=0,
@ -92,6 +90,29 @@ class PSSH:
system_ID=PSSH.SystemId.Widevine, system_ID=PSSH.SystemId.Widevine,
init_data=cenc_header init_data=cenc_header
))) )))
except DecodeError: # not a widevine cenc header
if "</WRMHEADER>".encode("utf-16-le") in data:
# TODO: Actually parse `data` as a PlayReadyHeader object and store that instead
box = Box.parse(Box.build(dict(
type=b"pssh",
version=0,
flags=0,
system_ID=PSSH.SystemId.PlayReady,
init_data=data
)))
elif strict:
raise DecodeError(f"Could not parse data as a {Container} nor a {WidevinePsshData}.")
else:
# Data is not a WidevineCencHeader nor a PlayReadyHeader.
# The license server likely has something custom to parse it.
# See doc-string about Lenient mode for more information.
box = Box.parse(Box.build(dict(
type=b"pssh",
version=0,
flags=0,
system_ID=PSSH.SystemId.Widevine,
init_data=data
)))
self.version = box.version self.version = box.version
self.flags = box.flags self.flags = box.flags