Repository: Eyevinn/mp4ff
Pull request #373 (2024-08-30 13:23:58)

fix: allow missing optional DecoderSpecificInfo
The standard allows DecoderSpecificInfo to be absent as pointed out in #372.
This PR fixes this.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f78ea9..2728659 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Fixed
+
+- Allow missing optional DecoderSpecificInfo
+
 - Nothing yet
 
 ## [0.46.0] - 2024-08-08
diff --git a/mp4/descriptors.go b/mp4/descriptors.go
index 01205b9..bf42bd3 100644
--- a/mp4/descriptors.go
+++ b/mp4/descriptors.go
@@ -228,7 +228,9 @@ func (e *ESDescriptor) Size() uint64 {
 	if ocrStreamFlag == 1 {
 		size += 2
 	}
-	size += e.DecConfigDescriptor.SizeSize()
+	if e.DecConfigDescriptor != nil {
+		size += e.DecConfigDescriptor.SizeSize()
+	}
 	if e.SLConfigDescriptor != nil {
 		size += e.SLConfigDescriptor.SizeSize()
 	}
@@ -379,14 +381,17 @@ func DecodeDecoderConfigDescriptor(tag byte, sr bits.SliceReader, maxNrBytes int
 
 	currPos := sr.GetPos()
 	nrBytesLeft := int(size) - (currPos - dataStart)
+	if nrBytesLeft == 0 {
+		return &dd, nil
+	}
 	desc, err := DecodeDescriptor(sr, nrBytesLeft)
 	if err != nil {
-		return nil, fmt.Errorf("failed to decode DecSpecificInfoDescriptor: %w", err)
+		return nil, fmt.Errorf("failed to decode descriptor: %w", err)
 	}
 	var ok bool
 	dd.DecSpecificInfo, ok = desc.(*DecSpecificInfoDescriptor)
-	if !ok {
-		return nil, fmt.Errorf("expected DecSpecificInfoDescriptor")
+	if !ok { // The optional decoderSpeicificInfo is not present
+		dd.OtherDescriptors = append(dd.OtherDescriptors, desc)
 	}
 	for {
 		currPos := sr.GetPos()
@@ -417,7 +422,10 @@ func (d *DecoderConfigDescriptor) Type() string {
 }
 
 func (d *DecoderConfigDescriptor) Size() uint64 {
-	size := 13 + d.DecSpecificInfo.SizeSize()
+	size := uint64(13)
+	if d.DecSpecificInfo != nil {
+		size += d.DecSpecificInfo.SizeSize()
+	}
 	for _, od := range d.OtherDescriptors {
 		size += od.SizeSize()
 	}
@@ -437,12 +445,14 @@ func (d *DecoderConfigDescriptor) EncodeSW(sw bits.SliceWriter) error {
 	sw.WriteUint32(streamTypeAndBufferSizeDB)
 	sw.WriteUint32(d.MaxBitrate)
 	sw.WriteUint32(d.AvgBitrate)
-	err := d.DecSpecificInfo.EncodeSW(sw)
-	if err != nil {
-		return err
+	if d.DecSpecificInfo != nil {
+		err := d.DecSpecificInfo.EncodeSW(sw)
+		if err != nil {
+			return err
+		}
 	}
 	for _, desc := range d.OtherDescriptors {
-		err = desc.EncodeSW(sw)
+		err := desc.EncodeSW(sw)
 		if err != nil {
 			return err
 		}
@@ -463,12 +473,14 @@ func (d *DecoderConfigDescriptor) Info(w io.Writer, specificLevels, indent, inde
 	bd.write(" - BufferSizeDB: %d", d.BufferSizeDB)
 	bd.write(" - MaxBitrate: %d", d.MaxBitrate)
 	bd.write(" - AvgBitrate: %d", d.AvgBitrate)
-	err := d.DecSpecificInfo.Info(w, specificLevels, indent+indentStep, indentStep)
-	if err != nil {
-		return err
+	if d.DecSpecificInfo != nil {
+		err := d.DecSpecificInfo.Info(w, specificLevels, indent+indentStep, indentStep)
+		if err != nil {
+			return err
+		}
 	}
 	for _, od := range d.OtherDescriptors {
-		err = od.Info(w, specificLevels, indent+indentStep, indentStep)
+		err := od.Info(w, specificLevels, indent+indentStep, indentStep)
 		if err != nil {
 			return err
 		}


Test patch:
diff --git a/mp4/descriptors_test.go b/mp4/descriptors_test.go
index 8fb3f90..a6ec46f 100644
--- a/mp4/descriptors_test.go
+++ b/mp4/descriptors_test.go
@@ -12,6 +12,7 @@ import (
 const badSizeDescriptor = `031900010004134015000000000000000001f40005021190060102`
 const missingSLConfig = `031600010004114015000000000000000001d40005021190`
 const partOfEsdsProgIn = `03808080250002000480808017401500000000010d88000003f80580808005128856e500068080800102`
+const missingDecSpecificInfo = `0315000100040d4015000000000000000001d400060102`
 
 func TestDecodeDescriptor(t *testing.T) {
 	cases := []struct {
@@ -21,6 +22,7 @@ func TestDecodeDescriptor(t *testing.T) {
 		{"badSizeDescriptor", badSizeDescriptor},
 		{"missingSLConfig", missingSLConfig},
 		{"partOfEsdsProgIn", partOfEsdsProgIn},
+		{"missingDecSpecificInfo", missingDecSpecificInfo},
 	}
 	for _, c := range cases {
 		t.Run(c.desc, func(t *testing.T) {
@@ -92,6 +94,22 @@ func TestDescriptorInfo(t *testing.T) {
 			- DecConfig (2B): 1190
 		- Missing SLConfigDescriptor
 		`},
+		{"missingDecSpecificInfo", missingDecSpecificInfo,
+			`Descriptor "tag=3 ES" size=2+21
+		- EsID: 1
+		- DependsOnEsID: 0
+		- OCResID: 0
+		- FlagsAndPriority: 0
+		- URLString:
+		 Descriptor "tag=4 DecoderConfig" size=2+13
+		  - ObjectType: 64
+		  - StreamType: 21
+		  - BufferSizeDB: 0
+		  - MaxBitrate: 0
+		  - AvgBitrate: 119808
+		 Descriptor "tag=6 SLConfig" size=2+1
+		  - ConfigValue: 2
+		`},
 	}
 	for _, c := range cases {
 		t.Run(c.desc, func(t *testing.T) {