From 57a5713390001f0b3b7c8074bbee2b9b5ab6e2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbjo=CC=88rn=20Einarsson?= Date: Tue, 5 Jan 2021 18:25:11 +0100 Subject: [PATCH 1/3] fix: clarification and fix of sample numbers to be one-based --- README.md | 10 ++++++++++ examples/segmenter/segment.go | 4 ++-- mp4/ctts.go | 10 ++++++++-- mp4/stsc.go | 2 +- mp4/stss.go | 2 +- mp4/stts.go | 12 +++++++++--- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6079c1fd..8ef68a22 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,11 @@ This value is the address of the first media sample relative to the start of the It therefore depends on the size of `MoofBox` and is unknown until all values are in place so that it can be calculated. It is set to `MoofBox.Size()+8`. +## Sample Number Offset +Following the ISOBMFF standard, sample numbers and other numbers start at 1 (one-based). +This applies to arguments of functions. The actual storage in slices are zero-based, so +sample nr 1 has index 0 in the corresponding slice. + ## Command Line Tools Some simple command line tools are available in `cmd`. @@ -136,6 +141,11 @@ These are ## Stability The APIs should be fairly stable, but minor non-backwards-compatible tweaks may happen until version 1. +## Specifications +The main specification for the MP4 file format is the ISO Base Media File Format (ISOBMFF) standard +ISO/IEC 14496-12 6'th ed 2020. Some boxes are specified in other standards, as should be commented +in the code. + ## LICENSE MIT, see [LICENSE.md](LICENSE.md). diff --git a/examples/segmenter/segment.go b/examples/segmenter/segment.go index ce4ad0c1..9155c8ee 100644 --- a/examples/segmenter/segment.go +++ b/examples/segmenter/segment.go @@ -96,14 +96,14 @@ func (s *Segmenter) GetSamplesUntilTime(mp4f *mp4.File, tr *Track, startSampleNr } var sampleFlags mp4.SampleFlags if stbl.Stss != nil { - isSync := stbl.Stss.IsSyncSample(uint32(sampleNr - 1)) + isSync := stbl.Stss.IsSyncSample(uint32(sampleNr)) sampleFlags.SampleIsNonSync = !isSync if isSync { sampleFlags.SampleDependsOn = 2 //2 = does not depend on others (I-picture). May be overridden by sdtp entry } } if stbl.Sdtp != nil { - entry := stbl.Sdtp.Entries[uint32(sampleNr)-1] + entry := stbl.Sdtp.Entries[uint32(sampleNr)-1] // table starts at 0, but sampleNr is one-based sampleFlags.IsLeading = entry.IsLeading() sampleFlags.SampleDependsOn = entry.SampleDependsOn() sampleFlags.SampleHasRedundancy = entry.SampleHasRedundancy() diff --git a/mp4/ctts.go b/mp4/ctts.go index c90243fa..81dbdfa6 100644 --- a/mp4/ctts.go +++ b/mp4/ctts.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "io" "io/ioutil" + "log" ) // CttsBox - Composition Time to Sample Box (ctts - optional) @@ -69,9 +70,14 @@ func (b *CttsBox) Encode(w io.Writer) error { return err } -// GetCompositionTimeOffset - composition time offset for sampleNr in track timescale +// GetCompositionTimeOffset - composition time offset for (one-based) sampleNr in track timescale func (b *CttsBox) GetCompositionTimeOffset(sampleNr uint32) int32 { - sampleNr-- // 1-based + if sampleNr == 0 { + // This is bad index input. Should not happen + log.Print("ERROR: CttsBox.GetCompositionTimeOffset called with sampleNr == 0, although one-based") + return 0 + } + sampleNr-- // one-based for i := range b.SampleCount { if sampleNr >= b.SampleCount[i] { sampleNr -= b.SampleCount[i] diff --git a/mp4/stsc.go b/mp4/stsc.go index 9fdf5827..b15b0023 100644 --- a/mp4/stsc.go +++ b/mp4/stsc.go @@ -92,7 +92,7 @@ func (b *StscBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string return bd.err } -// ChunkNrFromSampleNr - get chunk number from sampleNr (1-based) +// ChunkNrFromSampleNr - get chunk number from sampleNr (one-based) func (b *StscBox) ChunkNrFromSampleNr(sampleNr int) (chunkNr, firstSampleInChunk int, err error) { nrEntries := len(b.FirstChunk) // Nr entries in stsc box firstSampleInChunk = 1 diff --git a/mp4/stss.go b/mp4/stss.go index 2ae485a3..14858f10 100644 --- a/mp4/stss.go +++ b/mp4/stss.go @@ -48,7 +48,7 @@ func (b *StssBox) Size() uint64 { return uint64(boxHeaderSize + 8 + len(b.SampleNumber)*4) } -// IsSyncSample - check if a sample is a sync sample +// IsSyncSample - check if sample (one-based) sampleNr is a sync sample func (b *StssBox) IsSyncSample(sampleNr uint32) (isSync bool) { if b.lookUp == nil { b.lookUp = make(map[uint32]bool) diff --git a/mp4/stts.go b/mp4/stts.go index 4315cc05..726724ac 100644 --- a/mp4/stts.go +++ b/mp4/stts.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "io" "io/ioutil" + "log" "time" ) @@ -72,9 +73,14 @@ func (b *SttsBox) GetTimeCode(sample, timescale uint32) time.Duration { return time.Second * time.Duration(units) / time.Duration(timescale) } -// GetDecodeTime - decode time and duration for sampleNr in track timescale +// GetDecodeTime - decode time and duration for (one-based) sampleNr in track timescale func (b *SttsBox) GetDecodeTime(sampleNr uint32) (decTime uint64, dur uint32) { - sampleNr-- // 1-based + if sampleNr == 0 { + // This is bad index input. Should not happen + log.Print("ERROR: SttsBox.GetDecodeTime called with sampleNr == 0, although one-based") + return 0, 1 + } + sampleNr-- // one-based decTime = 0 i := 0 for sampleNr > 0 && i < len(b.SampleCount) { @@ -89,7 +95,7 @@ func (b *SttsBox) GetDecodeTime(sampleNr uint32) (decTime uint64, dur uint32) { } i++ } - return + return decTime, dur } // Encode - write box to w From 800acc88d88c5a7ac75b61eadc4780f95ad7ae26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbjo=CC=88rn=20Einarsson?= Date: Tue, 5 Jan 2021 18:27:23 +0100 Subject: [PATCH 2/3] fix: detailed defaultSampleFlags Info for tfhd box --- mp4/testdata/golden_1_frag_m4s_dump.txt | 8 ++++---- mp4/tfhd.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mp4/testdata/golden_1_frag_m4s_dump.txt b/mp4/testdata/golden_1_frag_m4s_dump.txt index d2fbf4c0..7fcf4e4a 100644 --- a/mp4/testdata/golden_1_frag_m4s_dump.txt +++ b/mp4/testdata/golden_1_frag_m4s_dump.txt @@ -12,7 +12,7 @@ - trackID: 2 - defaultBaseIsMoof: true - defaultSampleDuration: 3000 - - defaultSampleFlags: 01010000 + - defaultSampleFlags: 01010000 (isLeading=0 dependsOn=1 isDependedOn=0 hasRedundancy=0 padding=0 isNonSync=true degradationPriority=0) [tfdt] size=16 version=0 - baseMediaDecodeTime: 0 [trun] size=144 version=1 @@ -45,7 +45,7 @@ - trackID: 2 - defaultBaseIsMoof: true - defaultSampleDuration: 3000 - - defaultSampleFlags: 01010000 + - defaultSampleFlags: 01010000 (isLeading=0 dependsOn=1 isDependedOn=0 hasRedundancy=0 padding=0 isNonSync=true degradationPriority=0) [tfdt] size=16 version=0 - baseMediaDecodeTime: 45000 [trun] size=140 version=1 @@ -77,7 +77,7 @@ - trackID: 2 - defaultBaseIsMoof: true - defaultSampleDuration: 3000 - - defaultSampleFlags: 01010000 + - defaultSampleFlags: 01010000 (isLeading=0 dependsOn=1 isDependedOn=0 hasRedundancy=0 padding=0 isNonSync=true degradationPriority=0) [tfdt] size=16 version=0 - baseMediaDecodeTime: 90000 [trun] size=144 version=1 @@ -110,7 +110,7 @@ - trackID: 2 - defaultBaseIsMoof: true - defaultSampleDuration: 3000 - - defaultSampleFlags: 01010000 + - defaultSampleFlags: 01010000 (isLeading=0 dependsOn=1 isDependedOn=0 hasRedundancy=0 padding=0 isNonSync=true degradationPriority=0) [tfdt] size=16 version=0 - baseMediaDecodeTime: 135000 [trun] size=140 version=1 diff --git a/mp4/tfhd.go b/mp4/tfhd.go index d743a7d2..b15cb9e8 100644 --- a/mp4/tfhd.go +++ b/mp4/tfhd.go @@ -194,7 +194,8 @@ func (t *TfhdBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string bd.write(" - defaultSampleSize: %d", t.DefaultSampleSize) } if t.HasDefaultSampleFlags() { - bd.write(" - defaultSampleFlags: %08x", t.DefaultSampleFlags) + bd.write(" - defaultSampleFlags: %08x (%s)", t.DefaultSampleFlags, DecodeSampleFlags(t.DefaultSampleFlags)) + } return bd.err } From 1f2f6f6d98c6488213592bbeedebf0c943c57ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbjo=CC=88rn=20Einarsson?= Date: Tue, 5 Jan 2021 18:29:37 +0100 Subject: [PATCH 3/3] doc: Version 0.16.2 --- Versions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Versions.md b/Versions.md index 9b6e52e1..48a80a54 100644 --- a/Versions.md +++ b/Versions.md @@ -2,6 +2,7 @@ | Version | Highlight | | ------ | --------- | +| 0.16.2 | fix: Minor fixes to sampleNumber and tfhd Info | | 0.16.1 | fix: isNonSync flag declaration and use sdtp values in segmenter example | | 0.16.0 | New mp4ff-info tool. Many new boxes incl. encryption boxes and sdtp. ADTS support. Test improvements with golden files | | 0.15.0 | Support for multiple SPS and more explicit NALU types for AVC |