This commit is contained in:
Nirvana
2026-01-16 15:19:40 +01:00
parent 3e8cd19b31
commit 98aa3ef38b
@@ -105,14 +105,15 @@ class MP4PSSHExtractor:
if box_type == b'tenc':
# Parse tenc box
logger.debug(f"Found tenc box at offset {offset}, size {box_size}")
tenc_kid = MP4PSSHExtractor._parse_tenc_box(data[offset:offset + box_size])
if tenc_kid and tenc_kid not in kids:
kids.append(tenc_kid)
logger.debug(f"Found KID from tenc box: {tenc_kid}")
elif box_size > 8:
# Recursively search inside container boxes
# Check if this is a container box (moov, trak, mdia, minf, stbl, sinf, schi)
container_boxes = {b'moov', b'trak', b'mdia', b'minf', b'stbl', b'sinf', b'schi'}
# Check if this is a container box (moov, trak, mdia, minf, stbl, sinf, schi, encv)
container_boxes = {b'moov', b'trak', b'mdia', b'minf', b'stbl', b'stsd', b'encv', b'sinf', b'schi'}
if box_type in container_boxes:
# Recursively search inside the container
inner_data = data[offset + 8:offset + box_size]
@@ -132,8 +133,9 @@ class MP4PSSHExtractor:
def _parse_tenc_box(tenc_bytes: bytes) -> Optional[str]:
"""Parse a tenc box and extract the default key ID"""
try:
# Minimum tenc box size is 28 bytes (short form) or 32 bytes (full)
if len(tenc_bytes) < 28:
# Minimum tenc box size is 20 bytes for version 0
if len(tenc_bytes) < 20:
logger.debug(f"tenc box too small: {len(tenc_bytes)} bytes")
return None
# Parse box header
@@ -141,34 +143,62 @@ class MP4PSSHExtractor:
box_type = tenc_bytes[4:8]
if box_type != b'tenc':
logger.debug(f"Not a tenc box, type: {box_type}")
return None
version = tenc_bytes[8]
logger.debug(f"tenc box version: {version}")
# According to ISO/IEC 23001-7, tenc box structure:
# Bytes 0-3: size
# Bytes 4-7: 'tenc'
# Byte 8: version
# Bytes 9-11: flags
# Byte 12: is_encrypted (bit 0) + reserved (bits 1-7)
# Byte 13: default_iv_size
# Bytes 14-29: default_KID (16 bytes)
# Determine KID offset based on version
if version == 0:
# Version 0: flags (3 bytes) + reserved/is_encrypted (1 byte) + default_iv_size (1 byte)
# KID starts at byte 14
# For version 0, KID is at offset 14
kid_offset = 14
else:
# Version 1: more complex structure
# For now, assume similar offset
# Version 1 has additional fields
kid_offset = 14
# Skip optional fields if version == 1
# Byte 14: pattern
# Byte 15: crypt_byte_block
# Byte 16: skip_byte_block
if version == 1 and len(tenc_bytes) >= 32:
kid_offset = 17
if kid_offset + 16 > len(tenc_bytes):
logger.debug(f"tenc box too small for KID: {len(tenc_bytes)} bytes, need {kid_offset + 16}")
return None
kid_bytes = tenc_bytes[kid_offset:kid_offset + 16]
kid_uuid = str(uuid.UUID(bytes=kid_bytes))
# Convert to lowercase without dashes (same format as PSSH KIDs)
clean_kid = kid_uuid.replace("-", "").lower()
return clean_kid
# Debug: print hex of KID bytes
hex_kid = kid_bytes.hex()
logger.debug(f"Raw KID bytes: {hex_kid}")
# Format as UUID
try:
kid_uuid = str(uuid.UUID(bytes=kid_bytes))
# Convert to lowercase without dashes (same format as PSSH KIDs)
clean_kid = kid_uuid.replace("-", "").lower()
logger.debug(f"Parsed KID: {clean_kid} (UUID: {kid_uuid})")
return clean_kid
except Exception as e:
logger.debug(f"Failed to parse KID bytes as UUID: {e}")
# Fallback: just return hex
return hex_kid
except Exception as e:
logger.debug(f"Failed to parse tenc box: {e}")
return None
# [Keep the rest of the methods the same as before...]
@staticmethod
def _extract_from_moov(moov_data: bytes) -> List[PSSHData]:
"""Extract PSSH boxes from moov container"""