4 changed files with 171 additions and 23 deletions
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
// Package mpegtstimedec contains a MPEG-TS timestamp decoder.
|
||||
package mpegtstimedec |
||||
|
||||
import ( |
||||
"time" |
||||
) |
||||
|
||||
const ( |
||||
maximum = 0x1FFFFFFFF // 33 bits
|
||||
negativeThreshold = 0xFFFFFFF |
||||
clockRate = 90000 |
||||
) |
||||
|
||||
// Decoder is a MPEG-TS timestamp decoder.
|
||||
type Decoder struct { |
||||
initialized bool |
||||
tsOverall time.Duration |
||||
tsPrev int64 |
||||
} |
||||
|
||||
// New allocates a Decoder.
|
||||
func New() *Decoder { |
||||
return &Decoder{} |
||||
} |
||||
|
||||
// Decode decodes a MPEG-TS timestamp.
|
||||
func (d *Decoder) Decode(ts int64) time.Duration { |
||||
if !d.initialized { |
||||
d.initialized = true |
||||
d.tsPrev = ts |
||||
return 0 |
||||
} |
||||
|
||||
diff := (ts - d.tsPrev) & maximum |
||||
|
||||
// negative difference
|
||||
if diff > negativeThreshold { |
||||
diff = (d.tsPrev - ts) & maximum |
||||
d.tsPrev = ts |
||||
d.tsOverall -= time.Duration(diff) |
||||
} else { |
||||
d.tsPrev = ts |
||||
d.tsOverall += time.Duration(diff) |
||||
} |
||||
|
||||
// avoid an int64 overflow and preserve resolution by splitting division into two parts:
|
||||
// first add the integer part, then the decimal part.
|
||||
secs := d.tsOverall / clockRate |
||||
dec := d.tsOverall % clockRate |
||||
return secs*time.Second + dec*time.Second/clockRate |
||||
} |
||||
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
package mpegtstimedec |
||||
|
||||
import ( |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestNegativeDiff(t *testing.T) { |
||||
d := New() |
||||
|
||||
i := int64(0) |
||||
pts := d.Decode(i) |
||||
require.Equal(t, time.Duration(0), pts) |
||||
|
||||
i += 90000 * 2 |
||||
pts = d.Decode(i) |
||||
require.Equal(t, 2*time.Second, pts) |
||||
|
||||
i -= 90000 * 1 |
||||
pts = d.Decode(i) |
||||
require.Equal(t, 1*time.Second, pts) |
||||
|
||||
i += 90000 * 2 |
||||
pts = d.Decode(i) |
||||
require.Equal(t, 3*time.Second, pts) |
||||
} |
||||
|
||||
func TestOverflow(t *testing.T) { |
||||
d := New() |
||||
|
||||
i := int64(0x1FFFFFFFF - 20) |
||||
secs := time.Duration(0) |
||||
pts := d.Decode(i) |
||||
require.Equal(t, time.Duration(0), pts) |
||||
|
||||
const stride = 150 |
||||
lim := int64(uint64(0x1FFFFFFFF - (stride * 90000))) |
||||
|
||||
for n := 0; n < 100; n++ { |
||||
// overflow
|
||||
i += 90000 * stride |
||||
secs += stride |
||||
pts = d.Decode(i) |
||||
require.Equal(t, secs*time.Second, pts) |
||||
|
||||
// reach 2^32 slowly
|
||||
secs += stride |
||||
i += 90000 * stride |
||||
for ; i < lim; i += 90000 * stride { |
||||
pts = d.Decode(i) |
||||
require.Equal(t, secs*time.Second, pts) |
||||
secs += stride |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TestOverflowAndBack(t *testing.T) { |
||||
d := New() |
||||
|
||||
pts := d.Decode(0x1FFFFFFFF - 90000 + 1) |
||||
require.Equal(t, time.Duration(0), pts) |
||||
|
||||
pts = d.Decode(90000) |
||||
require.Equal(t, 2*time.Second, pts) |
||||
|
||||
pts = d.Decode(0x1FFFFFFFF - 90000 + 1) |
||||
require.Equal(t, time.Duration(0), pts) |
||||
|
||||
pts = d.Decode(0x1FFFFFFFF - 90000*2 + 1) |
||||
require.Equal(t, -1*time.Second, pts) |
||||
|
||||
pts = d.Decode(0x1FFFFFFFF - 90000 + 1) |
||||
require.Equal(t, time.Duration(0), pts) |
||||
|
||||
pts = d.Decode(90000) |
||||
require.Equal(t, 2*time.Second, pts) |
||||
} |
||||
Loading…
Reference in new issue