Repository: Eyevinn/mp4ff
Pull request #438 (2025-07-06 20:11:19)

Minimal support for AC-4
* interpret ac-4 as AudioVisualSampleEntry box
* New pointer AC4 in StsdBox


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86678e9..c452a84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - Basic VVC support with vvcC box and VvcDecoderConfigurationRecord
+- Minimal AC-4 support by recognizing ac-4 as AudioSampleDescrition box
 
 ## [0.49.0] - 2025-06-26
 
diff --git a/mp4/box.go b/mp4/box.go
index 80ec1d7..7b1a387 100644
--- a/mp4/box.go
+++ b/mp4/box.go
@@ -24,6 +24,7 @@ func init() {
 		"\xa9too": DecodeGenericContainerBox,
 		"\xa9cpy": DecodeGenericContainerBox,
 		"ac-3":    DecodeAudioSampleEntry,
+		"ac-4":    DecodeAudioSampleEntry,
 		"alou":    DecodeLoudnessBaseBox,
 		"av01":    DecodeVisualSampleEntry,
 		"av1C":    DecodeAv1C,
diff --git a/mp4/boxsr.go b/mp4/boxsr.go
index f9968e0..aab3fca 100644
--- a/mp4/boxsr.go
+++ b/mp4/boxsr.go
@@ -15,6 +15,7 @@ func init() {
 		"\xa9nam": DecodeGenericContainerBoxSR,
 		"\xa9too": DecodeGenericContainerBoxSR,
 		"ac-3":    DecodeAudioSampleEntrySR,
+		"ac-4":    DecodeAudioSampleEntrySR,
 		"alou":    DecodeLoudnessBaseBoxSR,
 		"av01":    DecodeVisualSampleEntrySR,
 		"av1C":    DecodeAv1CSR,
diff --git a/mp4/stsd.go b/mp4/stsd.go
index 7324a8b..1d0e916 100644
--- a/mp4/stsd.go
+++ b/mp4/stsd.go
@@ -32,6 +32,8 @@ type StsdBox struct {
 	AC3 *AudioSampleEntryBox
 	// EC3 is a pointer to a box with name ec-3
 	EC3 *AudioSampleEntryBox
+	// AC4 is a pointer to a box with name ac-4
+	AC4 *AudioSampleEntryBox
 	// Enca is a pointer to a box with name enca
 	Enca *AudioSampleEntryBox
 	// Wvtt is a pointer to a WvttBox
@@ -67,6 +69,8 @@ func (s *StsdBox) AddChild(box Box) {
 		s.AC3 = box.(*AudioSampleEntryBox)
 	case "ec-3":
 		s.EC3 = box.(*AudioSampleEntryBox)
+	case "ac-4":
+		s.AC4 = box.(*AudioSampleEntryBox)
 	case "enca":
 		s.Enca = box.(*AudioSampleEntryBox)
 	case "wvtt":


Test patch:
diff --git a/mp4/stsd_test.go b/mp4/stsd_test.go
index e8675e2..7703894 100644
--- a/mp4/stsd_test.go
+++ b/mp4/stsd_test.go
@@ -3,9 +3,11 @@ package mp4_test
 import (
 	"bytes"
 	"encoding/hex"
+	"os"
 	"testing"
 
 	"github.com/Eyevinn/mp4ff/aac"
+	"github.com/Eyevinn/mp4ff/bits"
 	"github.com/Eyevinn/mp4ff/mp4"
 )
 
@@ -90,4 +92,42 @@ func TestStsdVP9(t *testing.T) {
 	}
 
 	cmpAfterDecodeEncodeBox(t, binData)
+	// Check that VP9 pointer is set
+	stsd := decodeStsdBox(t, binData)
+	if stsd.VpXX == nil {
+		t.Errorf("Expected VP9 box pointer, got nil")
+	}
+	if stsd.VpXX.Type() != "vp09" {
+		t.Errorf("VpXX type is %s, expected vp09", stsd.VpXX.Type())
+	}
+}
+
+func TestStsdAC4(t *testing.T) {
+	data, err := os.ReadFile("testdata/stsd_ac4.bin")
+	if err != nil {
+		t.Fatal(err)
+	}
+	cmpAfterDecodeEncodeBox(t, data)
+	// Check that AC4 pointer is set
+	stsd := decodeStsdBox(t, data)
+	if stsd.AC4 == nil {
+		t.Errorf("Expected AC4 box pointer, got nil")
+	}
+	if stsd.AC4.Type() != "ac-4" {
+		t.Errorf("AC4 type is %s, expected ac-4", stsd.AC4.Type())
+	}
+}
+
+func decodeStsdBox(t *testing.T, data []byte) *mp4.StsdBox {
+	t.Helper()
+	sr := bits.NewFixedSliceReader(data)
+	box, err := mp4.DecodeBoxSR(0, sr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	stsd, ok := box.(*mp4.StsdBox)
+	if !ok {
+		t.Fatalf("Expected StsdBox, got %T", box)
+	}
+	return stsd
 }