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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ import (
"github.com/aler9/gortsplib/v2/pkg/codecs/jpeg" "github.com/aler9/gortsplib/v2/pkg/codecs/jpeg"
"github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpmjpeg/headers" "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. // ErrMorePacketsNeeded is returned when more packets are needed.
@@ -96,7 +96,7 @@ var chmAcSymbols = []byte{ //nolint:dupl
// Decoder is a RTP/M-JPEG decoder. // Decoder is a RTP/M-JPEG decoder.
type Decoder struct { type Decoder struct {
timeDecoder *rtptimedec.Decoder timeDecoder *rtptime.Decoder
firstPacketReceived bool firstPacketReceived bool
fragmentedSize int fragmentedSize int
fragments [][]byte fragments [][]byte
@@ -106,7 +106,7 @@ type Decoder struct {
// Init initializes the decoder. // Init initializes the decoder.
func (d *Decoder) Init() { func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes an image from a RTP/M-JPEG packet. // 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/codecs/jpeg"
"github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpmjpeg/headers" "github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpmjpeg/headers"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
) )
const ( const (
@@ -41,6 +42,7 @@ type Encoder struct {
PayloadMaxSize int PayloadMaxSize int
sequenceNumber uint16 sequenceNumber uint16
timeEncoder *rtptime.Encoder
} }
// Init initializes the encoder. // Init initializes the encoder.
@@ -62,10 +64,7 @@ func (e *Encoder) Init() {
} }
e.sequenceNumber = *e.InitialSequenceNumber e.sequenceNumber = *e.InitialSequenceNumber
} e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
} }
// Encode encodes an image into RTP/M-JPEG packets. // Encode encodes an image into RTP/M-JPEG packets.
@@ -260,7 +259,7 @@ outer:
Version: rtpVersion, Version: rtpVersion,
PayloadType: 26, PayloadType: 26,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts), Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: len(data) == 0, Marker: len(data) == 0,
}, },

View File

@@ -9,7 +9,7 @@ import (
"github.com/aler9/gortsplib/v2/pkg/bits" "github.com/aler9/gortsplib/v2/pkg/bits"
"github.com/aler9/gortsplib/v2/pkg/codecs/mpeg4audio" "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. // 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. // The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
IndexDeltaLength int IndexDeltaLength int
timeDecoder *rtptimedec.Decoder timeDecoder *rtptime.Decoder
firstAUParsed bool firstAUParsed bool
adtsMode bool adtsMode bool
fragments [][]byte fragments [][]byte
@@ -38,7 +38,7 @@ type Decoder struct {
// Init initializes the decoder. // Init initializes the decoder.
func (d *Decoder) Init() { 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. // 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/bits"
"github.com/aler9/gortsplib/v2/pkg/codecs/mpeg4audio" "github.com/aler9/gortsplib/v2/pkg/codecs/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
) )
const ( const (
@@ -54,6 +55,7 @@ type Encoder struct {
IndexDeltaLength int IndexDeltaLength int
sequenceNumber uint16 sequenceNumber uint16
timeEncoder *rtptime.Encoder
} }
// Init initializes the encoder. // Init initializes the encoder.
@@ -75,10 +77,7 @@ func (e *Encoder) Init() {
} }
e.sequenceNumber = *e.InitialSequenceNumber e.sequenceNumber = *e.InitialSequenceNumber
} e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*float64(e.SampleRate))
} }
// Encode encodes AUs into RTP/MPEG4-audio packets. // 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) ret := make([]*rtp.Packet, packetCount)
encPTS := e.encodeTimestamp(pts) encPTS := e.timeEncoder.Encode(pts)
for i := range ret { for i := range ret {
var le int var le int
@@ -260,7 +259,7 @@ func (e *Encoder) writeAggregated(aus [][]byte, pts time.Duration) ([]*rtp.Packe
Version: rtpVersion, Version: rtpVersion,
PayloadType: e.PayloadType, PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts), Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: true, Marker: true,
}, },

View File

@@ -5,19 +5,19 @@ import (
"github.com/pion/rtp" "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. // Decoder is a RTP/simple audio decoder.
type Decoder struct { type Decoder struct {
SampleRate int SampleRate int
timeDecoder *rtptimedec.Decoder timeDecoder *rtptime.Decoder
} }
// Init initializes the decoder. // Init initializes the decoder.
func (d *Decoder) Init() { 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. // Decode decodes an audio frame from a RTP packet.

View File

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

View File

@@ -8,7 +8,7 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/pion/rtp/codecs" "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. // ErrMorePacketsNeeded is returned when more packets are needed.
@@ -23,14 +23,14 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
// Decoder is a RTP/VP8 decoder. // Decoder is a RTP/VP8 decoder.
type Decoder struct { type Decoder struct {
timeDecoder *rtptimedec.Decoder timeDecoder *rtptime.Decoder
firstPacketReceived bool firstPacketReceived bool
fragments [][]byte fragments [][]byte
} }
// Init initializes the decoder. // Init initializes the decoder.
func (d *Decoder) Init() { func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes a VP8 frame from a RTP/VP8 packet. // 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"
"github.com/pion/rtp/codecs" "github.com/pion/rtp/codecs"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
) )
const ( const (
@@ -41,6 +43,7 @@ type Encoder struct {
PayloadMaxSize int PayloadMaxSize int
sequenceNumber uint16 sequenceNumber uint16
timeEncoder *rtptime.Encoder
vp codecs.VP8Payloader vp codecs.VP8Payloader
} }
@@ -63,10 +66,7 @@ func (e *Encoder) Init() {
} }
e.sequenceNumber = *e.InitialSequenceNumber e.sequenceNumber = *e.InitialSequenceNumber
} e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
return *e.InitialTimestamp + uint32(ts.Seconds()*rtpClockRate)
} }
// Encode encodes a VP8 frame into RTP/VP8 packets. // 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, Version: rtpVersion,
PayloadType: e.PayloadType, PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts), Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: i == (plen - 1), Marker: i == (plen - 1),
}, },

View File

@@ -8,7 +8,7 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/pion/rtp/codecs" "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. // ErrMorePacketsNeeded is returned when more packets are needed.
@@ -23,14 +23,14 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
// Decoder is a RTP/VP9 decoder. // Decoder is a RTP/VP9 decoder.
type Decoder struct { type Decoder struct {
timeDecoder *rtptimedec.Decoder timeDecoder *rtptime.Decoder
firstPacketReceived bool firstPacketReceived bool
fragments [][]byte fragments [][]byte
} }
// Init initializes the decoder. // Init initializes the decoder.
func (d *Decoder) Init() { func (d *Decoder) Init() {
d.timeDecoder = rtptimedec.New(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes a VP9 frame from a RTP/VP9 packet. // 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"
"github.com/pion/rtp/codecs" "github.com/pion/rtp/codecs"
"github.com/aler9/gortsplib/v2/pkg/rtptime"
) )
const ( const (
@@ -45,6 +47,7 @@ type Encoder struct {
InitialPictureID *uint16 InitialPictureID *uint16
sequenceNumber uint16 sequenceNumber uint16
timeEncoder *rtptime.Encoder
vp codecs.VP9Payloader vp codecs.VP9Payloader
} }
@@ -71,16 +74,13 @@ func (e *Encoder) Init() {
} }
e.sequenceNumber = *e.InitialSequenceNumber e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
e.vp.InitialPictureIDFn = func() uint16 { e.vp.InitialPictureIDFn = func() uint16 {
return *e.InitialPictureID 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. // Encode encodes a VP9 frame into RTP/VP9 packets.
func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) { func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame) 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, Version: rtpVersion,
PayloadType: e.PayloadType, PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
Timestamp: e.encodeTimestamp(pts), Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: i == (plen - 1), Marker: i == (plen - 1),
}, },

View File

@@ -1,5 +1,5 @@
// Package rtptimedec contains a RTP timestamp decoder. // Package rtptime contains a RTP timestamp decoder and encoder.
package rtptimedec package rtptime
import ( import (
"time" "time"
@@ -15,14 +15,14 @@ type Decoder struct {
prev uint32 prev uint32
} }
// New allocates a Decoder. // NewDecoder allocates a Decoder.
func New(clockRate int) *Decoder { func NewDecoder(clockRate int) *Decoder {
return &Decoder{ return &Decoder{
clockRate: time.Duration(clockRate), clockRate: time.Duration(clockRate),
} }
} }
// Decode decodes a RTP timestamp. // Decode decodes a timestamp.
func (d *Decoder) Decode(ts uint32) time.Duration { func (d *Decoder) Decode(ts uint32) time.Duration {
if !d.initialized { if !d.initialized {
d.initialized = true d.initialized = true
@@ -42,7 +42,7 @@ func (d *Decoder) Decode(ts uint32) time.Duration {
d.overall += time.Duration(diff) 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. // first add the integer part, then the decimal part.
secs := d.overall / d.clockRate secs := d.overall / d.clockRate
dec := d.overall % d.clockRate dec := d.overall % d.clockRate

View File

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