Skip to content

Commit

Permalink
Merge pull request #260 from Eyevinn/stts-final-zero-duration
Browse files Browse the repository at this point in the history
fix: handle stts case with single trailing zero duration
  • Loading branch information
tobbee authored Jul 28, 2023
2 parents 7924ad3 + fe49aa6 commit ca651ef
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- discard of parsing HEVC SPS data
- `SttsBox.GetSampleNrAtTime` now supports a final zero sample duration

## [0.36.0] - 2023-06-07

Expand Down
12 changes: 10 additions & 2 deletions mp4/stts.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,15 @@ func (b *SttsBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string
return bd.err
}

// GetSampleNrAtTime - get sample number at or as soon as possible after time
// GetSampleNrAtTime returns the 1-based sample number at or as soon as possible after time.
// Match a final single zero duration if present.
// If time is too big to reach, an error is returned.
// Time is calculated by summing up durations of previous samples
func (b *SttsBox) GetSampleNrAtTime(sampleStartTime uint64) (sampleNr uint32, err error) {
accTime := uint64(0)
accNr := uint32(0)
for i := 0; i < len(b.SampleCount); i++ {
nrEntries := len(b.SampleCount)
for i := 0; i < nrEntries; i++ {
timeDelta := uint64(b.SampleTimeDelta[i])
if sampleStartTime < accTime+uint64(b.SampleCount[i])*timeDelta {
relTime := (sampleStartTime - accTime)
Expand All @@ -184,5 +187,10 @@ func (b *SttsBox) GetSampleNrAtTime(sampleStartTime uint64) (sampleNr uint32, er
accNr += b.SampleCount[i]
accTime += timeDelta * uint64(b.SampleCount[i])
}
// Check if there is a final single zero duration and time matches.
if b.SampleTimeDelta[nrEntries-1] == 0 && b.SampleCount[nrEntries-1] == 1 &&
sampleStartTime == accTime {
return accNr, nil
}
return 0, fmt.Errorf("no matching sample found for time=%d", sampleStartTime)
}
35 changes: 23 additions & 12 deletions mp4/stts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,37 @@ func TestGetSampleNrAtTime(t *testing.T) {
SampleTimeDelta: []uint32{10, 14},
}

sttsZero := SttsBox{
SampleCount: []uint32{2, 1},
SampleTimeDelta: []uint32{10, 0}, // Single zero duration at end
}

testCases := []struct {
stts SttsBox
startTime uint64
sampleNr uint32
expectError bool
}{
{0, 1, false},
{1, 2, false},
{10, 2, false},
{20, 3, false},
{30, 4, false},
{31, 5, false},
{43, 5, false},
{44, 5, false},
{45, 6, false},
{57, 6, false},
{58, 0, true},
{stts, 0, 1, false},
{stts, 1, 2, false},
{stts, 10, 2, false},
{stts, 20, 3, false},
{stts, 30, 4, false},
{stts, 31, 5, false},
{stts, 43, 5, false},
{stts, 44, 5, false},
{stts, 45, 6, false},
{stts, 57, 6, false},
{stts, 58, 0, true},
{sttsZero, 0, 1, false},
{sttsZero, 10, 2, false},
{sttsZero, 19, 3, false},
{sttsZero, 20, 3, false},
{sttsZero, 21, 0, true},
}

for _, tc := range testCases {
gotNr, err := stts.GetSampleNrAtTime(tc.startTime)
gotNr, err := tc.stts.GetSampleNrAtTime(tc.startTime)
if tc.expectError {
if err == nil {
t.Errorf("Did not get error for startTime %d", tc.startTime)
Expand Down

0 comments on commit ca651ef

Please sign in to comment.