Skip to content

Commit

Permalink
mpegts: add time decoder that works with native timestamp (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 authored Oct 6, 2024
1 parent 62c5cf1 commit 393a408
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 19 deletions.
27 changes: 8 additions & 19 deletions pkg/formats/mpegts/time_decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import (
)

const (
maximum = 0x1FFFFFFFF // 33 bits
negativeThreshold = 0x1FFFFFFFF / 2
clockRate = 90000
clockRate = 90000
)

// avoid an int64 overflow and preserve resolution by splitting division into two parts:
Expand All @@ -19,31 +17,22 @@ func multiplyAndDivide(v, m, d time.Duration) time.Duration {
}

// TimeDecoder is a MPEG-TS timestamp decoder.
//
// Deprecated: replaced by TimeDecoder2
type TimeDecoder struct {
overall time.Duration
prev int64
wrapped *TimeDecoder2
}

// NewTimeDecoder allocates a TimeDecoder.
//
// Deprecated: replaced by NewTimeDecoder2
func NewTimeDecoder(start int64) *TimeDecoder {
return &TimeDecoder{
prev: start,
wrapped: NewTimeDecoder2(start),
}
}

// Decode decodes a MPEG-TS timestamp.
func (d *TimeDecoder) Decode(ts int64) time.Duration {
diff := (ts - d.prev) & maximum

// negative difference
if diff > negativeThreshold {
diff = (d.prev - ts) & maximum
d.prev = ts
d.overall -= time.Duration(diff)
} else {
d.prev = ts
d.overall += time.Duration(diff)
}

return multiplyAndDivide(d.overall, time.Second, clockRate)
return multiplyAndDivide(time.Duration(d.wrapped.Decode(ts)), time.Second, clockRate)
}
36 changes: 36 additions & 0 deletions pkg/formats/mpegts/time_decoder2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package mpegts

const (
maximum = 0x1FFFFFFFF // 33 bits
negativeThreshold = 0x1FFFFFFFF / 2
)

// TimeDecoder2 is a MPEG-TS timestamp decoder.
type TimeDecoder2 struct {
overall int64
prev int64
}

// NewTimeDecoder2 allocates a TimeDecoder.
func NewTimeDecoder2(start int64) *TimeDecoder2 {
return &TimeDecoder2{
prev: start,
}
}

// Decode decodes a MPEG-TS timestamp.
func (d *TimeDecoder2) Decode(ts int64) int64 {
diff := (ts - d.prev) & maximum

// negative difference
if diff > negativeThreshold {
diff = (d.prev - ts) & maximum
d.prev = ts
d.overall -= diff
} else {
d.prev = ts
d.overall += diff
}

return d.overall
}
71 changes: 71 additions & 0 deletions pkg/formats/mpegts/time_decoder2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package mpegts

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestTimeDecoder2NegativeDiff(t *testing.T) {
d := NewTimeDecoder2(64523434)

ts := d.Decode(64523434 - 90000)
require.Equal(t, int64(-90000), ts)

ts = d.Decode(64523434)
require.Equal(t, int64(0), ts)

ts = d.Decode(64523434 + 90000*2)
require.Equal(t, int64(2*90000), ts)

ts = d.Decode(64523434 + 90000)
require.Equal(t, int64(1*90000), ts)
}

func TestTimeDecoder2Overflow(t *testing.T) {
d := NewTimeDecoder2(0x1FFFFFFFF - 20)

i := int64(0x1FFFFFFFF - 20)
secs := int64(0)
const stride = 150
lim := int64(uint64(0x1FFFFFFFF - (stride * 90000)))

for n := 0; n < 100; n++ {
// overflow
i += 90000 * stride
secs += stride
ts := d.Decode(i)
require.Equal(t, secs*90000, ts)

// reach 2^32 slowly
secs += stride
i += 90000 * stride
for ; i < lim; i += 90000 * stride {
ts = d.Decode(i)
require.Equal(t, secs*90000, ts)
secs += stride
}
}
}

func TestTimeDecoder2OverflowAndBack(t *testing.T) {
d := NewTimeDecoder2(0x1FFFFFFFF - 90000 + 1)

ts := d.Decode(0x1FFFFFFFF - 90000 + 1)
require.Equal(t, int64(0), ts)

ts = d.Decode(90000)
require.Equal(t, int64(2*90000), ts)

ts = d.Decode(0x1FFFFFFFF - 90000 + 1)
require.Equal(t, int64(0), ts)

ts = d.Decode(0x1FFFFFFFF - 90000*2 + 1)
require.Equal(t, int64(-1*90000), ts)

ts = d.Decode(0x1FFFFFFFF - 90000 + 1)
require.Equal(t, int64(0), ts)

ts = d.Decode(90000)
require.Equal(t, int64(2*90000), ts)
}

0 comments on commit 393a408

Please sign in to comment.