From ce46aee084ccfd082a8fae9f5af60dfdbe15e8e4 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sun, 25 Feb 2024 23:25:44 +0100 Subject: [PATCH] change rtptime.Encoder signature and initialize time to a random value (#525) --- .../main.go | 8 ++- pkg/rtptime/encoder.go | 49 ++++++++++++++----- pkg/rtptime/encoder_test.go | 22 +++++++-- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/examples/client-record-format-mjpeg-from-image/main.go b/examples/client-record-format-mjpeg-from-image/main.go index eb11fa79..c22e5ffe 100644 --- a/examples/client-record-format-mjpeg-from-image/main.go +++ b/examples/client-record-format-mjpeg-from-image/main.go @@ -45,7 +45,11 @@ func main() { } // setup timestamp generator - rtpTime := rtptime.NewEncoder(forma.ClockRate(), 0) + rtpTime := &rtptime.Encoder{ClockRate: forma.ClockRate()} + err = rtpTime.Initialize() + if err != nil { + panic(err) + } start := time.Now() // setup a ticker to sleep between frames @@ -89,7 +93,7 @@ func main() { } // get current timestamp - ts := rtpTime.Encode(time.Now().Sub(start)) + ts := rtpTime.Encode(time.Since(start)) // write packets to the server for _, pkt := range pkts { diff --git a/pkg/rtptime/encoder.go b/pkg/rtptime/encoder.go index cb00750f..6b89231d 100644 --- a/pkg/rtptime/encoder.go +++ b/pkg/rtptime/encoder.go @@ -1,6 +1,7 @@ package rtptime import ( + "crypto/rand" "time" ) @@ -12,23 +13,47 @@ func divCeil(n, d uint64) uint64 { return v } -// Encoder is a RTP timestamp encoder. -type Encoder struct { - clockRate time.Duration - initialTimestamp time.Duration +func randUint32() (uint32, error) { + var b [4]byte + _, err := rand.Read(b[:]) + if err != nil { + return 0, err + } + return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil } -// NewEncoder allocates an Encoder. -func NewEncoder(clockRate int, initialTimestamp uint32) *Encoder { - return &Encoder{ - clockRate: time.Duration(clockRate), - // ((2^32) * 1000000000) is less than 2^63 - initialTimestamp: time.Duration(divCeil(uint64(initialTimestamp)*uint64(time.Second), uint64(clockRate))), +// Encoder is a RTP timestamp encoder. +type Encoder struct { + // Clock rate. + ClockRate int + + // (optional) initial timestamp. + // It defaults to a random value. + InitialTimestamp *uint32 + + clockRateTD time.Duration + initialTimestampTD time.Duration +} + +// Initialize initializes an Encoder. +func (e *Encoder) Initialize() error { + e.clockRateTD = time.Duration(e.ClockRate) + + if e.InitialTimestamp == nil { + v, err := randUint32() + if err != nil { + return err + } + e.InitialTimestamp = &v } + + e.initialTimestampTD = time.Duration(divCeil(uint64(*e.InitialTimestamp)*uint64(time.Second), uint64(e.ClockRate))) + + return nil } // Encode encodes a timestamp. func (e *Encoder) Encode(ts time.Duration) uint32 { - ts = e.initialTimestamp + ts - return uint32(multiplyAndDivide(ts, e.clockRate, time.Second)) + ts += e.initialTimestampTD + return uint32(multiplyAndDivide(ts, e.clockRateTD, time.Second)) } diff --git a/pkg/rtptime/encoder_test.go b/pkg/rtptime/encoder_test.go index d5313f87..0ed3827c 100644 --- a/pkg/rtptime/encoder_test.go +++ b/pkg/rtptime/encoder_test.go @@ -7,8 +7,17 @@ import ( "github.com/stretchr/testify/require" ) +func uint32Ptr(v uint32) *uint32 { + return &v +} + func TestEncoder(t *testing.T) { - e := NewEncoder(90000, 12345) + e := &Encoder{ + ClockRate: 90000, + InitialTimestamp: uint32Ptr(12345), + } + err := e.Initialize() + require.NoError(t, err) ts := e.Encode(0) require.Equal(t, uint32(12345), ts) @@ -18,10 +27,13 @@ func TestEncoder(t *testing.T) { } func BenchmarkEncoder(b *testing.B) { + e := &Encoder{ + ClockRate: 90000, + InitialTimestamp: uint32Ptr(12345), + } + e.Initialize() //nolint:errcheck + for i := 0; i < b.N; i++ { - func() { - d := NewEncoder(90000, 0) - d.Encode(200 * time.Second) - }() + e.Encode(200 * time.Second) } }