From 67d4106adcc31c58d64cee098ef5293d21e0504d Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Mon, 8 May 2023 13:54:51 +0200 Subject: [PATCH] rtptime: fix time encoding on arm32 (#274) (#277) --- client.go | 2 +- pkg/rtptime/decoder.go | 14 +++++++++----- pkg/rtptime/encoder.go | 19 ++++++++++++++----- pkg/rtptime/encoder_test.go | 3 +++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/client.go b/client.go index 2fafb4c0..682fcb77 100644 --- a/client.go +++ b/client.go @@ -943,7 +943,7 @@ func (c *Client) do(req *base.Request, skipResponse bool, allowFrames bool) (*ba c.session = sx.Session if sx.Timeout != nil && *sx.Timeout > 0 { - c.keepalivePeriod = time.Duration(float64(*sx.Timeout)*0.8) * time.Second + c.keepalivePeriod = time.Duration(((*sx.Timeout)*10)/8) * time.Second } } diff --git a/pkg/rtptime/decoder.go b/pkg/rtptime/decoder.go index ede7159f..016c4ed2 100644 --- a/pkg/rtptime/decoder.go +++ b/pkg/rtptime/decoder.go @@ -7,6 +7,14 @@ import ( const negativeThreshold = 0xFFFFFFFF / 2 +// avoid an int64 overflow and preserve resolution by splitting division into two parts: +// first add the integer part, then the decimal part. +func multiplyAndDivide(v, m, d time.Duration) time.Duration { + secs := v / d + dec := v % d + return (secs*m + dec*m/d) +} + // Decoder is a RTP timestamp decoder. type Decoder struct { clockRate time.Duration @@ -42,9 +50,5 @@ func (d *Decoder) Decode(ts uint32) time.Duration { d.overall += 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.overall / d.clockRate - dec := d.overall % d.clockRate - return secs*time.Second + dec*time.Second/d.clockRate + return multiplyAndDivide(d.overall, time.Second, d.clockRate) } diff --git a/pkg/rtptime/encoder.go b/pkg/rtptime/encoder.go index 259a7b89..cb00750f 100644 --- a/pkg/rtptime/encoder.go +++ b/pkg/rtptime/encoder.go @@ -1,25 +1,34 @@ package rtptime import ( - "math" "time" ) +func divCeil(n, d uint64) uint64 { + v := n / d + if (n % d) != 0 { + v++ + } + return v +} + // Encoder is a RTP timestamp encoder. type Encoder struct { - clockRate float64 + clockRate time.Duration 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))), + clockRate: time.Duration(clockRate), + // ((2^32) * 1000000000) is less than 2^63 + initialTimestamp: time.Duration(divCeil(uint64(initialTimestamp)*uint64(time.Second), uint64(clockRate))), } } // Encode encodes a timestamp. func (e *Encoder) Encode(ts time.Duration) uint32 { - return uint32((e.initialTimestamp + ts).Seconds() * e.clockRate) + ts = e.initialTimestamp + ts + return uint32(multiplyAndDivide(ts, e.clockRate, time.Second)) } diff --git a/pkg/rtptime/encoder_test.go b/pkg/rtptime/encoder_test.go index a4e05116..d5313f87 100644 --- a/pkg/rtptime/encoder_test.go +++ b/pkg/rtptime/encoder_test.go @@ -12,6 +12,9 @@ func TestEncoder(t *testing.T) { ts := e.Encode(0) require.Equal(t, uint32(12345), ts) + + ts = e.Encode(3 * time.Second / 2) + require.Equal(t, uint32(12345+135000), ts) } func BenchmarkEncoder(b *testing.B) {