Repository: Eyevinn/mp4ff
Pull request #328 (2024-02-22 13:06:06)

feat: add nclc and unknown as colr box color types


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2728e6a..6da3c37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,12 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - InitSegment.TweakSingleTrakLive changes an init segment to fit live streaming
 - Made bits.Mask() function public
 - New counter methods added to bits.Reader
+- colr box support for nclc and unknown colour_type
 
 ### Changed
 
 - All readers and writers in bits package now stop working at first error and provides the first error as AccError()
 - Renamed bits.AccErrReader, bits.AccErrEBSPReader, bits.AccErrWriter to corresponiding names without AccErr
 - Renamed bits.SliceWriterError to bits.ErrSliceWrite
+- colr box supports unknown colrType
 
 ## [0.42.0] - 2024-01-26
 
diff --git a/mp4/colr.go b/mp4/colr.go
index a37daad..7ecf76c 100644
--- a/mp4/colr.go
+++ b/mp4/colr.go
@@ -2,7 +2,6 @@ package mp4
 
 import (
 	"encoding/hex"
-	"fmt"
 	"io"
 
 	"github.com/Eyevinn/mp4ff/bits"
@@ -13,10 +12,14 @@ const (
 	onScreenColors      = "nclx"
 	restrictedICCType   = "rICC"
 	unrestrictedICCType = "prof"
-	fullRangeBit        = 0x80
+	// quickTimeColorParameters defined in [nclc]
+	//
+	// [nclc]: https://developer.apple.com/library/archive/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG10
+	quickTimeColorParameters = "nclc"
+	fullRangeBit             = 0x80
 )
 
-// ColrBox i colr box defined in ISO/IEC 14496-2 2021 12.1.5
+// ColrBox is colr box defined in ISO/IEC 14496-2 2021 12.1.5.
 type ColrBox struct {
 	ColorType               string
 	ICCProfile              []byte
@@ -24,6 +27,7 @@ type ColrBox struct {
 	TransferCharacteristics uint16
 	MatrixCoefficients      uint16
 	FullRangeFlag           bool
+	UnknownPayload          []byte
 }
 
 // DecodeColr decodes a ColrBox
@@ -50,8 +54,12 @@ func DecodeColrSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, err
 		c.FullRangeFlag = (b & fullRangeBit) == fullRangeBit
 	case restrictedICCType, unrestrictedICCType:
 		c.ICCProfile = sr.RemainingBytes()
+	case quickTimeColorParameters:
+		c.ColorPrimaries = sr.ReadUint16()
+		c.TransferCharacteristics = sr.ReadUint16()
+		c.MatrixCoefficients = sr.ReadUint16()
 	default:
-		return nil, fmt.Errorf("unknown color type %q", c.ColorType)
+		c.UnknownPayload = sr.RemainingBytes()
 	}
 	return &c, sr.AccError()
 }
@@ -69,6 +77,10 @@ func (c *ColrBox) Size() uint64 {
 		size += 7
 	case restrictedICCType, unrestrictedICCType:
 		size += uint64(len(c.ICCProfile))
+	case quickTimeColorParameters:
+		size += 6
+	default:
+		size += uint64(len(c.UnknownPayload))
 	}
 	return size
 }
@@ -101,8 +113,14 @@ func (c *ColrBox) EncodeSW(sw bits.SliceWriter) error {
 			b = fullRangeBit
 		}
 		sw.WriteUint8(b)
-	default:
+	case restrictedICCType, unrestrictedICCType:
 		sw.WriteBytes(c.ICCProfile)
+	case quickTimeColorParameters:
+		sw.WriteUint16(c.ColorPrimaries)
+		sw.WriteUint16(c.TransferCharacteristics)
+		sw.WriteUint16(c.MatrixCoefficients)
+	default:
+		sw.WriteBytes(c.UnknownPayload)
 	}
 	return sw.AccError()
 }
@@ -115,9 +133,13 @@ func (c *ColrBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string
 	case onScreenColors:
 		bd.write(" - ColorPrimaries: %d, TransferCharacteristics: %d, MatrixCoefficients: %d, FullRange: %t",
 			c.ColorPrimaries, c.TransferCharacteristics, c.MatrixCoefficients, c.FullRangeFlag)
-	default:
+	case restrictedICCType, unrestrictedICCType:
 		bd.write(" - ICCProfile: %s", hex.EncodeToString(c.ICCProfile))
-
+	case quickTimeColorParameters:
+		bd.write(" - ColorPrimaries: %d, TransferCharacteristics: %d, MatrixCoefficients: %d",
+			c.ColorPrimaries, c.TransferCharacteristics, c.MatrixCoefficients)
+	default:
+		bd.write(" - Payload: %s", hex.EncodeToString(c.UnknownPayload))
 	}
 	return bd.err
 }


Test patch:
diff --git a/mp4/colr_test.go b/mp4/colr_test.go
index 3ed8c31..91f758d 100644
--- a/mp4/colr_test.go
+++ b/mp4/colr_test.go
@@ -29,6 +29,16 @@ func TestColrEncodeDecode(t *testing.T) {
 			ColorType:  unrestrictedICCType,
 			ICCProfile: []byte{1, 2, 2, 43, 4, 5},
 		},
+		{
+			ColorType:               quickTimeColorParameters,
+			ColorPrimaries:          1,
+			TransferCharacteristics: 1,
+			MatrixCoefficients:      1,
+		},
+		{
+			ColorType:      "xyzd",
+			UnknownPayload: []byte{0x0, 0x1, 0x0, 0x1},
+		},
 	}
 	for _, tc := range cases {
 		boxDiffAfterEncodeAndDecode(t, &tc)