fix timestamp encode error after some hours (#206)

previous formula was: uint32(a + uint32(b))
this formula overflows two times, while it should overflow once.
new formula is: uint32(uint64(a) + uint64(b))
This commit is contained in:
Alessandro Ros
2023-03-14 17:55:06 +01:00
committed by GitHub
parent bc248c8e1d
commit f0540b4eee
20 changed files with 119 additions and 83 deletions

View File

@@ -9,7 +9,7 @@ import (
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/codecs/h264"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -27,7 +27,7 @@ type Decoder struct {
// indicates the packetization mode.
PacketizationMode int
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentedSize int
fragments [][]byte
@@ -39,7 +39,7 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate)
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
}
// Decode decodes NALUs from a RTP/H264 packet.

View File

@@ -8,6 +8,7 @@ import (
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/codecs/h264"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -44,6 +45,7 @@ type Encoder struct {
PacketizationMode int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -65,10 +67,7 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
}
// Encode encodes NALUs into RTP/H264 packets.
@@ -131,7 +130,7 @@ func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*r
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},
@@ -153,7 +152,7 @@ func (e *Encoder) writeFragmented(nalu []byte, pts time.Duration, marker bool) (
}
ret := make([]*rtp.Packet, packetCount)
encPTS := e.encodeTimestamp(pts)
encPTS := e.timeEncoder.Encode(pts)
nri := (nalu[0] >> 5) & 0x03
typ := nalu[0] & 0x1F
@@ -238,7 +237,7 @@ func (e *Encoder) writeAggregated(nalus [][]byte, pts time.Duration, marker bool
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},

View File

@@ -8,7 +8,7 @@ import (
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/codecs/h265"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -26,7 +26,7 @@ type Decoder struct {
// indicates that NALUs have an additional field that specifies the decoding order.
MaxDONDiff int
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentedSize int
fragments [][]byte
@@ -37,7 +37,7 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate)
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
}
// Decode decodes NALUs from a RTP/H265 packet.

View File

@@ -6,6 +6,8 @@ import (
"time"
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -43,6 +45,7 @@ type Encoder struct {
MaxDONDiff int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -64,10 +67,7 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
}
// Encode encodes NALUs into RTP/H265 packets.
@@ -130,7 +130,7 @@ func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*r
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},
@@ -150,7 +150,7 @@ func (e *Encoder) writeFragmentationUnits(nalu []byte, pts time.Duration, marker
}
ret := make([]*rtp.Packet, n)
encPTS := e.encodeTimestamp(pts)
encPTS := e.timeEncoder.Encode(pts)
head := nalu[:2]
nalu = nalu[2:]
@@ -234,7 +234,7 @@ func (e *Encoder) writeAggregationUnit(nalus [][]byte, pts time.Duration, marker
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},

View File

@@ -6,7 +6,7 @@ import (
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// Decoder is a RTP/LPCM decoder.
@@ -15,13 +15,13 @@ type Decoder struct {
SampleRate int
ChannelCount int
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
sampleSize int
}
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(d.SampleRate)
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
d.sampleSize = d.BitDepth * d.ChannelCount / 8
}

View File

@@ -6,6 +6,8 @@ import (
"time"
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -44,6 +46,7 @@ type Encoder struct {
ChannelCount int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
sampleSize int
maxPayloadSize int
}
@@ -67,14 +70,11 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
e.sampleSize = e.BitDepth * e.ChannelCount / 8
e.maxPayloadSize = (e.PayloadMaxSize / e.sampleSize) * e.sampleSize
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*float64(e.SampleRate))
}
// Encode encodes audio samples into RTP packets.
func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, error) {
slen := len(samples)
@@ -102,7 +102,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: false,
},

View File

@@ -9,7 +9,7 @@ import (
"github.com/aler9/gortsplib/v2/pkg/codecs/jpeg"
"github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpmjpeg/headers"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -96,7 +96,7 @@ var chmAcSymbols = []byte{ //nolint:dupl
// Decoder is a RTP/M-JPEG decoder.
type Decoder struct {
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentedSize int
fragments [][]byte
@@ -106,7 +106,7 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate)
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
}
// Decode decodes an image from a RTP/M-JPEG packet.

View File

@@ -10,6 +10,7 @@ import (
"github.com/aler9/gortsplib/v2/pkg/codecs/jpeg"
"github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpmjpeg/headers"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -41,6 +42,7 @@ type Encoder struct {
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -62,10 +64,7 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
}
// Encode encodes an image into RTP/M-JPEG packets.
@@ -260,7 +259,7 @@ outer:
Version: rtpVersion,
PayloadType: 26,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: len(data) == 0,
},

View File

@@ -9,7 +9,7 @@ import (
"github.com/aler9/gortsplib/v2/pkg/bits"
"github.com/aler9/gortsplib/v2/pkg/codecs/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -29,7 +29,7 @@ type Decoder struct {
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
IndexDeltaLength int
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
firstAUParsed bool
adtsMode bool
fragments [][]byte
@@ -38,7 +38,7 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(d.SampleRate)
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
}
// Decode decodes AUs from a RTP/MPEG4-audio packet.

View File

@@ -8,6 +8,7 @@ import (
"github.com/aler9/gortsplib/v2/pkg/bits"
"github.com/aler9/gortsplib/v2/pkg/codecs/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -54,6 +55,7 @@ type Encoder struct {
IndexDeltaLength int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -75,10 +77,7 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*float64(e.SampleRate))
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
}
// Encode encodes AUs into RTP/MPEG4-audio packets.
@@ -145,7 +144,7 @@ func (e *Encoder) writeFragmented(au []byte, pts time.Duration) ([]*rtp.Packet,
}
ret := make([]*rtp.Packet, packetCount)
encPTS := e.encodeTimestamp(pts)
encPTS := e.timeEncoder.Encode(pts)
for i := range ret {
var le int
@@ -260,7 +259,7 @@ func (e *Encoder) writeAggregated(aus [][]byte, pts time.Duration) ([]*rtp.Packe
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: true,
},

View File

@@ -5,19 +5,19 @@ import (
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// Decoder is a RTP/simple audio decoder.
type Decoder struct {
SampleRate int
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
}
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(d.SampleRate)
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
}
// Decode decodes an audio frame from a RTP packet.

View File

@@ -6,6 +6,8 @@ import (
"time"
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -42,6 +44,7 @@ type Encoder struct {
SampleRate int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -63,10 +66,7 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*float64(e.SampleRate))
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
}
// Encode encodes an audio frame into a RTP packet.
@@ -80,7 +80,7 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) (*rtp.Packet, error) {
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: false,
},

View File

@@ -8,7 +8,7 @@ import (
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -23,14 +23,14 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
// Decoder is a RTP/VP8 decoder.
type Decoder struct {
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragments [][]byte
}
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate)
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
}
// Decode decodes a VP8 frame from a RTP/VP8 packet.

View File

@@ -7,6 +7,8 @@ import (
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -41,6 +43,7 @@ type Encoder struct {
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
vp codecs.VP8Payloader
}
@@ -63,10 +66,7 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
}
// Encode encodes a VP8 frame into RTP/VP8 packets.
@@ -85,7 +85,7 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: i == (plen - 1),
},

View File

@@ -8,7 +8,7 @@ import (
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/aler9/gortsplib/v2/pkg/rtptimedec"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -23,14 +23,14 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
// Decoder is a RTP/VP9 decoder.
type Decoder struct {
timeDecoder *rtptimedec.Decoder
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragments [][]byte
}
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate)
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
}
// Decode decodes a VP9 frame from a RTP/VP9 packet.

View File

@@ -7,6 +7,8 @@ import (
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
)
const (
@@ -45,6 +47,7 @@ type Encoder struct {
InitialPictureID *uint16
sequenceNumber uint16
timeEncoder *rtptime.Encoder
vp codecs.VP9Payloader
}
@@ -71,16 +74,13 @@ func (e *Encoder) Init() {
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
e.vp.InitialPictureIDFn = func() uint16 {
return *e.InitialPictureID
}
}
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
}
// Encode encodes a VP9 frame into RTP/VP9 packets.
func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame)
@@ -97,7 +97,7 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts),
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: i == (plen - 1),
},

View File

@@ -1,5 +1,5 @@
// Package rtptimedec contains a RTP timestamp decoder.
package rtptimedec
// Package rtptime contains a RTP timestamp decoder and encoder.
package rtptime
import (
"time"
@@ -15,14 +15,14 @@ type Decoder struct {
prev uint32
}
// New allocates a Decoder.
func New(clockRate int) *Decoder {
// NewDecoder allocates a Decoder.
func NewDecoder(clockRate int) *Decoder {
return &Decoder{
clockRate: time.Duration(clockRate),
}
}
// Decode decodes a RTP timestamp.
// Decode decodes a timestamp.
func (d *Decoder) Decode(ts uint32) time.Duration {
if !d.initialized {
d.initialized = true
@@ -42,7 +42,7 @@ func (d *Decoder) Decode(ts uint32) time.Duration {
d.overall += time.Duration(diff)
}
// avoid an int64 overflow and keep resolution by splitting division into two parts:
// avoid an int64 overflow and preserve resolution by splitting division into two parts:
// first add the integer part, then the decimal part.
secs := d.overall / d.clockRate
dec := d.overall % d.clockRate

View File

@@ -1,4 +1,4 @@
package rtptimedec
package rtptime
import (
"testing"
@@ -7,8 +7,8 @@ import (
"github.com/stretchr/testify/require"
)
func TestNegativeDiff(t *testing.T) {
d := New(90000)
func TestDecoderNegativeDiff(t *testing.T) {
d := NewDecoder(90000)
i := uint32(0)
pts := d.Decode(i)
@@ -27,8 +27,8 @@ func TestNegativeDiff(t *testing.T) {
require.Equal(t, 3*time.Second, pts)
}
func TestOverflow(t *testing.T) {
d := New(90000)
func TestDecoderOverflow(t *testing.T) {
d := NewDecoder(90000)
i := uint32(0xFFFFFFFF - 90000 + 1)
secs := time.Duration(0)
@@ -56,8 +56,8 @@ func TestOverflow(t *testing.T) {
}
}
func TestOverflowAndBack(t *testing.T) {
d := New(90000)
func TestDecoderOverflowAndBack(t *testing.T) {
d := NewDecoder(90000)
pts := d.Decode(0xFFFFFFFF - 90000 + 1)
require.Equal(t, time.Duration(0), pts)
@@ -81,7 +81,7 @@ func TestOverflowAndBack(t *testing.T) {
func BenchmarkDecoder(b *testing.B) {
for i := 0; i < b.N; i++ {
func() {
d := New(90000)
d := NewDecoder(90000)
n := uint32(0)
for j := 0; j < 200; j++ {
if (j % 2) == 0 {

25
pkg/rtptime/encoder.go Normal file
View File

@@ -0,0 +1,25 @@
package rtptime
import (
"math"
"time"
)
// Encoder is a RTP timestamp encoder.
type Encoder struct {
clockRate float64
initialTimestamp time.Duration
}
// NewEncoder allocates an Encoder.
func NewEncoder(clockRate int, initialTimestamp uint32) *Encoder {
return &Encoder{
clockRate: float64(clockRate),
initialTimestamp: time.Duration(math.Ceil(float64(initialTimestamp) * float64(time.Second) / float64(clockRate))),
}
}
// Encode encodes a timestamp.
func (e *Encoder) Encode(ts time.Duration) uint32 {
return uint32((e.initialTimestamp + ts).Seconds() * e.clockRate)
}

View File

@@ -0,0 +1,14 @@
package rtptime
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEncoder(t *testing.T) {
e := NewEncoder(90000, 12345)
ts := e.Encode(0)
require.Equal(t, uint32(12345), ts)
}