Repository: Eyevinn/mp4ff
Pull request #226 (2023-03-09 11:24:32)

fix(mp4): only start new segment at start or styp box


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4efa26f..86a9c5b 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
+
+- Only start new segment at start or styp box
+
 ## [0.34.0] - 2023-02-28
 
 ### Added
diff --git a/mp4/file.go b/mp4/file.go
index 0b35163..def96fd 100644
--- a/mp4/file.go
+++ b/mp4/file.go
@@ -255,7 +255,8 @@ func (f *File) AddChild(child Box, boxStartPos uint64) {
 		moof.StartPos = boxStartPos
 		f.startSegmentIfNeeded(moof)
 		currSeg := f.LastSegment()
-		if currSeg.LastFragment() == nil {
+		lastFrag := currSeg.LastFragment()
+		if lastFrag == nil || lastFrag.Moof != nil {
 			currSeg.AddFragment(NewFragment())
 		}
 		frag := currSeg.LastFragment()
@@ -271,20 +272,13 @@ func (f *File) AddChild(child Box, boxStartPos uint64) {
 	f.Children = append(f.Children, child)
 }
 
-// startSegmentIfNeeded starts a new segment if there is none, or the latest has a moof box.
+// startSegmentIfNeeded starts a new segment if there is none.
 func (f *File) startSegmentIfNeeded(b Box) {
 	if len(f.Segments) == 0 {
 		f.isFragmented = true
 		f.AddMediaSegment(NewMediaSegmentWithoutStyp())
 		return
 	}
-	lastSeg := f.LastSegment()
-	lastFrag := lastSeg.LastFragment()
-	if lastFrag != nil {
-		if lastFrag.Moof != nil {
-			f.AddMediaSegment(NewMediaSegmentWithoutStyp())
-		}
-	}
 }
 
 // AddSidx adds a sidx box to the File and not a MediaSegment.


Test patch:
diff --git a/mp4/file_test.go b/mp4/file_test.go
index b3e168e..8cd42f5 100644
--- a/mp4/file_test.go
+++ b/mp4/file_test.go
@@ -174,23 +174,12 @@ func TestFilesWithEmsg(t *testing.T) {
 		t.Error(err)
 	}
 	seg := NewMediaSegment()
-	frag, err := CreateFragment(1, 1)
-	if err != nil {
-		t.Error(err)
-	}
-	frag.AddFullSample(FullSample{
-		Sample: Sample{
-			Flags:                 0x0,
-			Dur:                   1024,
-			Size:                  6,
-			CompositionTimeOffset: 0,
-		},
-		DecodeTime: 0,
-		Data:       []byte{0, 1, 2, 3, 4, 5},
-	})
+	frag := createFragment(t, 1, 1024, 0)
 	frag.AddEmsg(&EmsgBox{ID: 1})
 	frag.AddEmsg(&EmsgBox{ID: 2})
 	seg.AddFragment(frag)
+	frag = createFragment(t, 2, 1024, 1024)
+	seg.AddFragment(frag)
 	err = seg.Encode(buf)
 	if err != nil {
 		t.Error(err)
@@ -204,12 +193,12 @@ func TestFilesWithEmsg(t *testing.T) {
 	if len(decFile.Segments) != 1 {
 		t.Error("not 1 segment in file")
 	}
-	if len(decFile.Segments[0].Fragments) != 1 {
-		t.Error("not 1 fragment in segment")
+	if len(decFile.Segments[0].Fragments) != 2 {
+		t.Error("not 2 fragments in segment")
 	}
 	dFrag := decFile.Segments[0].Fragments[0]
 	if len(dFrag.Emsgs) != 2 {
-		t.Error("not 2 emsg boxes in fragment")
+		t.Error("not 2 emsg boxes in fragment 0")
 	}
 	if dFrag.Emsgs[0].ID != 1 {
 		t.Error("first emsg box does not have index 1")
@@ -227,3 +216,56 @@ func TestFilesWithEmsg(t *testing.T) {
 		t.Errorf("re-encoded bytes differ from encoded bytes")
 	}
 }
+
+func TestSegmentWith2Fragments(t *testing.T) {
+	// File styp, moof, mdat, moof, mdat
+	seg := NewMediaSegment()
+	frag := createFragment(t, 1, 1024, 0)
+	seg.AddFragment(frag)
+	frag = createFragment(t, 2, 1024, 1024)
+	seg.AddFragment(frag)
+	buf := bytes.Buffer{}
+	err := seg.Encode(&buf)
+	if err != nil {
+		t.Error(err)
+	}
+	encData := buf.Bytes()
+	sr := bits.NewFixedSliceReader(encData)
+	decFile, err := DecodeFileSR(sr)
+	if err != nil {
+		t.Error(err)
+	}
+	if len(decFile.Segments) != 1 {
+		t.Error("not 1 segment in file")
+	}
+	if len(decFile.Segments[0].Fragments) != 2 {
+		t.Error("not 2 fragments in segment")
+	}
+	sw := bits.NewFixedSliceWriter(int(decFile.Size()))
+	err = decFile.EncodeSW(sw)
+	if err != nil {
+		t.Error(err)
+	}
+	reEncData := sw.Bytes()
+	if !bytes.Equal(reEncData, encData) {
+		t.Errorf("re-encoded bytes differ from encoded bytes")
+	}
+}
+
+func createFragment(t *testing.T, seqNr, dur uint32, decTime uint64) *Fragment {
+	frag, err := CreateFragment(seqNr, 1)
+	if err != nil {
+		t.Fail()
+	}
+	frag.AddFullSample(FullSample{
+		Sample: Sample{
+			Flags:                 0x0,
+			Dur:                   dur,
+			Size:                  6,
+			CompositionTimeOffset: 0,
+		},
+		DecodeTime: decTime,
+		Data:       []byte{0, 1, 2, 3, 4, 5},
+	})
+	return frag
+}