From f0540b4eee760583b2d94f022674b0f3b0f8c8b0 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Tue, 14 Mar 2023 17:55:06 +0100 Subject: [PATCH] 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)) --- pkg/formatdecenc/rtph264/decoder.go | 6 ++--- pkg/formatdecenc/rtph264/encoder.go | 13 +++++------ pkg/formatdecenc/rtph265/decoder.go | 6 ++--- pkg/formatdecenc/rtph265/encoder.go | 14 ++++++------ pkg/formatdecenc/rtplpcm/decoder.go | 6 ++--- pkg/formatdecenc/rtplpcm/encoder.go | 10 ++++----- pkg/formatdecenc/rtpmjpeg/decoder.go | 6 ++--- pkg/formatdecenc/rtpmjpeg/encoder.go | 9 ++++---- pkg/formatdecenc/rtpmpeg4audio/decoder.go | 6 ++--- pkg/formatdecenc/rtpmpeg4audio/encoder.go | 11 +++++---- pkg/formatdecenc/rtpsimpleaudio/decoder.go | 6 ++--- pkg/formatdecenc/rtpsimpleaudio/encoder.go | 10 ++++----- pkg/formatdecenc/rtpvp8/decoder.go | 6 ++--- pkg/formatdecenc/rtpvp8/encoder.go | 10 ++++----- pkg/formatdecenc/rtpvp9/decoder.go | 6 ++--- pkg/formatdecenc/rtpvp9/encoder.go | 10 ++++----- pkg/{rtptimedec => rtptime}/decoder.go | 12 +++++----- pkg/{rtptimedec => rtptime}/decoder_test.go | 16 ++++++------- pkg/rtptime/encoder.go | 25 +++++++++++++++++++++ pkg/rtptime/encoder_test.go | 14 ++++++++++++ 20 files changed, 119 insertions(+), 83 deletions(-) rename pkg/{rtptimedec => rtptime}/decoder.go (73%) rename pkg/{rtptimedec => rtptime}/decoder_test.go (86%) create mode 100644 pkg/rtptime/encoder.go create mode 100644 pkg/rtptime/encoder_test.go diff --git a/pkg/formatdecenc/rtph264/decoder.go b/pkg/formatdecenc/rtph264/decoder.go index b51b6a18..e19d7b9c 100644 --- a/pkg/formatdecenc/rtph264/decoder.go +++ b/pkg/formatdecenc/rtph264/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtph264/encoder.go b/pkg/formatdecenc/rtph264/encoder.go index dc86f6e5..564bbb11 100644 --- a/pkg/formatdecenc/rtph264/encoder.go +++ b/pkg/formatdecenc/rtph264/encoder.go @@ -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, }, diff --git a/pkg/formatdecenc/rtph265/decoder.go b/pkg/formatdecenc/rtph265/decoder.go index 49ee48a7..1993c5f5 100644 --- a/pkg/formatdecenc/rtph265/decoder.go +++ b/pkg/formatdecenc/rtph265/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtph265/encoder.go b/pkg/formatdecenc/rtph265/encoder.go index e6f4ccf8..3f3871f4 100644 --- a/pkg/formatdecenc/rtph265/encoder.go +++ b/pkg/formatdecenc/rtph265/encoder.go @@ -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, }, diff --git a/pkg/formatdecenc/rtplpcm/decoder.go b/pkg/formatdecenc/rtplpcm/decoder.go index 4e48ea01..0b94ab02 100644 --- a/pkg/formatdecenc/rtplpcm/decoder.go +++ b/pkg/formatdecenc/rtplpcm/decoder.go @@ -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 } diff --git a/pkg/formatdecenc/rtplpcm/encoder.go b/pkg/formatdecenc/rtplpcm/encoder.go index f065e230..c81744f8 100644 --- a/pkg/formatdecenc/rtplpcm/encoder.go +++ b/pkg/formatdecenc/rtplpcm/encoder.go @@ -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, }, diff --git a/pkg/formatdecenc/rtpmjpeg/decoder.go b/pkg/formatdecenc/rtpmjpeg/decoder.go index 4d9a0acd..44ee7fdf 100644 --- a/pkg/formatdecenc/rtpmjpeg/decoder.go +++ b/pkg/formatdecenc/rtpmjpeg/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtpmjpeg/encoder.go b/pkg/formatdecenc/rtpmjpeg/encoder.go index a4e72eec..cbc93fd3 100644 --- a/pkg/formatdecenc/rtpmjpeg/encoder.go +++ b/pkg/formatdecenc/rtpmjpeg/encoder.go @@ -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, }, diff --git a/pkg/formatdecenc/rtpmpeg4audio/decoder.go b/pkg/formatdecenc/rtpmpeg4audio/decoder.go index 825b4437..d366c4e8 100644 --- a/pkg/formatdecenc/rtpmpeg4audio/decoder.go +++ b/pkg/formatdecenc/rtpmpeg4audio/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtpmpeg4audio/encoder.go b/pkg/formatdecenc/rtpmpeg4audio/encoder.go index d85fa5a9..a67aa339 100644 --- a/pkg/formatdecenc/rtpmpeg4audio/encoder.go +++ b/pkg/formatdecenc/rtpmpeg4audio/encoder.go @@ -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, }, diff --git a/pkg/formatdecenc/rtpsimpleaudio/decoder.go b/pkg/formatdecenc/rtpsimpleaudio/decoder.go index 67379183..116f6175 100644 --- a/pkg/formatdecenc/rtpsimpleaudio/decoder.go +++ b/pkg/formatdecenc/rtpsimpleaudio/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtpsimpleaudio/encoder.go b/pkg/formatdecenc/rtpsimpleaudio/encoder.go index bf6e544c..dacd35b1 100644 --- a/pkg/formatdecenc/rtpsimpleaudio/encoder.go +++ b/pkg/formatdecenc/rtpsimpleaudio/encoder.go @@ -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, }, diff --git a/pkg/formatdecenc/rtpvp8/decoder.go b/pkg/formatdecenc/rtpvp8/decoder.go index b82f16ab..25f645c4 100644 --- a/pkg/formatdecenc/rtpvp8/decoder.go +++ b/pkg/formatdecenc/rtpvp8/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtpvp8/encoder.go b/pkg/formatdecenc/rtpvp8/encoder.go index 58f6bac8..09bb6bdc 100644 --- a/pkg/formatdecenc/rtpvp8/encoder.go +++ b/pkg/formatdecenc/rtpvp8/encoder.go @@ -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), }, diff --git a/pkg/formatdecenc/rtpvp9/decoder.go b/pkg/formatdecenc/rtpvp9/decoder.go index 76c11250..2f2b2ab2 100644 --- a/pkg/formatdecenc/rtpvp9/decoder.go +++ b/pkg/formatdecenc/rtpvp9/decoder.go @@ -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. diff --git a/pkg/formatdecenc/rtpvp9/encoder.go b/pkg/formatdecenc/rtpvp9/encoder.go index ca830bac..03040e5e 100644 --- a/pkg/formatdecenc/rtpvp9/encoder.go +++ b/pkg/formatdecenc/rtpvp9/encoder.go @@ -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), }, diff --git a/pkg/rtptimedec/decoder.go b/pkg/rtptime/decoder.go similarity index 73% rename from pkg/rtptimedec/decoder.go rename to pkg/rtptime/decoder.go index ed3ceb4b..ede7159f 100644 --- a/pkg/rtptimedec/decoder.go +++ b/pkg/rtptime/decoder.go @@ -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 diff --git a/pkg/rtptimedec/decoder_test.go b/pkg/rtptime/decoder_test.go similarity index 86% rename from pkg/rtptimedec/decoder_test.go rename to pkg/rtptime/decoder_test.go index cb33ab56..051a60bb 100644 --- a/pkg/rtptimedec/decoder_test.go +++ b/pkg/rtptime/decoder_test.go @@ -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 { diff --git a/pkg/rtptime/encoder.go b/pkg/rtptime/encoder.go new file mode 100644 index 00000000..259a7b89 --- /dev/null +++ b/pkg/rtptime/encoder.go @@ -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) +} diff --git a/pkg/rtptime/encoder_test.go b/pkg/rtptime/encoder_test.go new file mode 100644 index 00000000..823be83b --- /dev/null +++ b/pkg/rtptime/encoder_test.go @@ -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) +}