Repository: Eyevinn/mp4ff
Pull request #318 (2024-01-21 16:20:10)

feat: add w3c common-pssh-box uuid + make uuid check case-insensitive
Better handling of UUID in mp4 library.
Also detect W3C Common PSSH box UUID.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 820498e..ab202a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Support avc3 sample description when encrypting
 - Full ProfileLevelTier parsing for HEVC
+- Make pssh UUID comparison case-insensitive
+
+### Added
+
+- W3C Common PSSH Box UUID
 
 ## [0.41.0] - 2024-01-12
 
diff --git a/mp4/pssh.go b/mp4/pssh.go
index cc20723..04dd46a 100644
--- a/mp4/pssh.go
+++ b/mp4/pssh.go
@@ -5,16 +5,18 @@ import (
 	"encoding/hex"
 	"fmt"
 	"io"
+	"strings"
 
 	"github.com/Eyevinn/mp4ff/bits"
 )
 
 // UUIDs for different DRM systems
 const (
-	UUIDPlayReady = "9a04f079-9840-4286-ab92-e65be0885f95"
-	UUIDWidevine  = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
-	UUIDFairPlay  = "94CE86FB-07FF-4F43-ADB8-93D2FA968CA2"
-	UUID_VCAS     = "9a27dd82-fde2-4725-8cbc-4234aa06ec09"
+	UUIDPlayReady   = "9a04f079-9840-4286-ab92-e65be0885f95"
+	UUIDWidevine    = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
+	UUIDFairPlay    = "94ce86fb-07ff-4f43-adb8-93d2fa968ca2"
+	UUID_VCAS       = "9a27dd82-fde2-4725-8cbc-4234aa06ec09"
+	UUID_W3C_COMMON = "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"
 )
 
 // UUID - 16-byte KeyID or SystemID
@@ -28,8 +30,11 @@ func (u UUID) String() string {
 	return fmt.Sprintf("%s-%s-%s-%s-%s", h[0:8], h[8:12], h[12:16], h[16:20], h[20:32])
 }
 
-// NewUUIDFromHex creates a UUID from a hexadecimal string with 32 chars
+// NewUUIDFromHex creates a UUID from a hexadecimal string with 32 chars or 36 chars (with dashes)
 func NewUUIDFromHex(h string) (UUID, error) {
+	if len(h) == 36 {
+		h = strings.ReplaceAll(h, "-", "")
+	}
 	if len(h) != 32 {
 		return nil, fmt.Errorf("hex has %d chars, not 32", len(h))
 	}
@@ -40,8 +45,10 @@ func NewUUIDFromHex(h string) (UUID, error) {
 	return UUID(s), nil
 }
 
-func systemName(systemID UUID) string {
+// ProtectionSystemName returns name of protection system if known.
+func ProtectionSystemName(systemID UUID) string {
 	uStr := systemID.String()
+	uStr = strings.ToLower(uStr)
 	switch uStr {
 	case UUIDPlayReady:
 		return "PlayReady"
@@ -51,6 +58,8 @@ func systemName(systemID UUID) string {
 		return "FairPlay"
 	case UUID_VCAS:
 		return "Verimatrix VCAS"
+	case UUID_W3C_COMMON:
+		return "W3C Common PSSH box"
 	default:
 		return "Unknown"
 	}
@@ -146,7 +155,7 @@ func (b *PsshBox) EncodeSW(sw bits.SliceWriter) error {
 // Info - write box info to w
 func (b *PsshBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string) (err error) {
 	bd := newInfoDumper(w, indent, b, int(b.Version), b.Flags)
-	bd.write(" - systemID: %s (%s)", b.SystemID, systemName(b.SystemID))
+	bd.write(" - systemID: %s (%s)", b.SystemID, ProtectionSystemName(b.SystemID))
 	if b.Version > 0 {
 		for i, kid := range b.KIDs {
 			bd.write(" - KID[%d]=%s", i+1, kid)


Test patch:
diff --git a/mp4/pssh_test.go b/mp4/pssh_test.go
index 2ecc994..34a58cd 100644
--- a/mp4/pssh_test.go
+++ b/mp4/pssh_test.go
@@ -55,5 +55,28 @@ func TestEncodeDecodePSSH(t *testing.T) {
 		Data:     []byte("some data"),
 	}
 	boxDiffAfterEncodeAndDecode(t, pssh)
+}
+
+func TestPsshUUIDs(t *testing.T) {
+	cases := []struct {
+		hexUUIDs     string
+		expectedName string
+	}{
+		{"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", "Widevine"},
+		{"9a04f079-9840-4286-ab92-e65be0885f95", "PlayReady"},
+		{"94CE86FB-07FF-4F43-ADB8-93D2FA968CA2", "FairPlay"},
+		{"9a27dd82-fde2-4725-8cbc-4234aa06ec09", "Verimatrix VCAS"},
+		{"1077efec-c0b2-4d02-ace3-3c1e52e2fb4b", "W3C Common PSSH box"},
+		{"00000000-0000-0000-0000-000000000000", "Unknown"},
+	}
 
+	for _, c := range cases {
+		u, err := NewUUIDFromHex(c.hexUUIDs)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if ProtectionSystemName(u) != c.expectedName {
+			t.Errorf("Expected %s, got %s", c.expectedName, ProtectionSystemName(u))
+		}
+	}
 }