diff --git a/pkg/rtpaac/decoder.go b/pkg/rtpaac/decoder.go index ccdfb067..6001dd66 100644 --- a/pkg/rtpaac/decoder.go +++ b/pkg/rtpaac/decoder.go @@ -7,6 +7,8 @@ import ( "time" "github.com/pion/rtp" + + "github.com/aler9/gortsplib/pkg/rtptimedec" ) // ErrMorePacketsNeeded is returned when more packets are needed. @@ -14,13 +16,7 @@ var ErrMorePacketsNeeded = errors.New("need more packets") // Decoder is a RTP/AAC decoder. type Decoder struct { - clockRate time.Duration - - tsAdd int64 - tsInitial *int64 - tsPrev *int64 - - // for Decode() + timeDecoder *rtptimedec.Decoder isDecodingFragmented bool fragmentedBuf []byte } @@ -28,26 +24,10 @@ type Decoder struct { // NewDecoder allocates a Decoder. func NewDecoder(clockRate int) *Decoder { return &Decoder{ - clockRate: time.Duration(clockRate), + timeDecoder: rtptimedec.New(clockRate), } } -func (d *Decoder) decodeTimestamp(ts uint32) time.Duration { - ts64 := int64(ts) + d.tsAdd - - if d.tsPrev != nil && (ts64-*d.tsPrev) < -0xFFFF { - ts64 += 0xFFFFFFFF - d.tsAdd += 0xFFFFFFFF - } - d.tsPrev = &ts64 - - if d.tsInitial == nil { - d.tsInitial = &ts64 - } - - return time.Duration(ts64-*d.tsInitial) * time.Second / d.clockRate -} - // Decode decodes AUs from a RTP/AAC packet. // It returns the AUs and the PTS of the first AU. // The PTS of subsequent AUs can be calculated by adding time.Second*1000/clockRate. @@ -100,7 +80,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { pkt.Payload = pkt.Payload[dataLen:] } - return aus, d.decodeTimestamp(pkt.Timestamp), nil + return aus, d.timeDecoder.Decode(pkt.Timestamp), nil } if headersLen != 16 { @@ -152,5 +132,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { } d.isDecodingFragmented = false - return [][]byte{d.fragmentedBuf}, d.decodeTimestamp(pkt.Timestamp), nil + return [][]byte{d.fragmentedBuf}, d.timeDecoder.Decode(pkt.Timestamp), nil } diff --git a/pkg/rtpaac/rtpaac_test.go b/pkg/rtpaac/rtpaac_test.go index 2f18530e..c71a2bcb 100644 --- a/pkg/rtpaac/rtpaac_test.go +++ b/pkg/rtpaac/rtpaac_test.go @@ -255,35 +255,6 @@ func TestDecode(t *testing.T) { } } -func TestDecodeTimestampOverflow(t *testing.T) { - d := NewDecoder(90000) - var pts time.Duration - - for _, ts := range []uint32{ - 4294877296, - 90001, - 3240090001, - 565122706, - } { - pkt := rtp.Packet{ - Header: rtp.Header{ - Version: 2, - Marker: true, - PayloadType: 96, - SequenceNumber: 0x01, - Timestamp: ts, - SSRC: 0xba9da416, - }, - Payload: []byte("\x00\x00"), - } - var err error - _, pts, err = d.Decode(&pkt) - require.NoError(t, err) - } - - require.Equal(t, 15*60*60*time.Second+2*time.Second, pts) -} - func TestDecodeErrors(t *testing.T) { for _, ca := range []struct { name string diff --git a/pkg/rtph264/decoder.go b/pkg/rtph264/decoder.go index cadf6ed5..10a0c078 100644 --- a/pkg/rtph264/decoder.go +++ b/pkg/rtph264/decoder.go @@ -11,6 +11,7 @@ import ( "github.com/pion/rtp" "github.com/aler9/gortsplib/pkg/h264" + "github.com/aler9/gortsplib/pkg/rtptimedec" ) // ErrMorePacketsNeeded is returned when more packets are needed. @@ -36,11 +37,7 @@ func (r PacketConnReader) Read(p []byte) (int, error) { // Decoder is a RTP/H264 decoder. type Decoder struct { - tsAdd int64 - tsInitial *int64 - tsPrev *int64 - - // for Decode() + timeDecoder *rtptimedec.Decoder startingPacketReceived bool isDecodingFragmented bool fragmentedBuffer []byte @@ -51,23 +48,9 @@ type Decoder struct { // NewDecoder allocates a Decoder. func NewDecoder() *Decoder { - return &Decoder{} -} - -func (d *Decoder) decodeTimestamp(ts uint32) time.Duration { - ts64 := int64(ts) + d.tsAdd - - if d.tsPrev != nil && (ts64-*d.tsPrev) < -0xFFFF { - ts64 += 0xFFFFFFFF - d.tsAdd += 0xFFFFFFFF + return &Decoder{ + timeDecoder: rtptimedec.New(90000), } - d.tsPrev = &ts64 - - if d.tsInitial == nil { - d.tsInitial = &ts64 - } - - return time.Duration(ts64-*d.tsInitial) * time.Second / rtpClockRate } // Decode decodes NALUs from a RTP/H264 packet. @@ -110,7 +93,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { } d.startingPacketReceived = true - return nalus, d.decodeTimestamp(pkt.Timestamp), nil + return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil case naluTypeFUA: // first packet of a fragmented NALU if len(pkt.Payload) < 2 { @@ -139,7 +122,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { } d.startingPacketReceived = true - return [][]byte{pkt.Payload}, d.decodeTimestamp(pkt.Timestamp), nil + return [][]byte{pkt.Payload}, d.timeDecoder.Decode(pkt.Timestamp), nil } // we are decoding a fragmented NALU @@ -171,7 +154,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { d.isDecodingFragmented = false d.startingPacketReceived = true - return [][]byte{d.fragmentedBuffer}, d.decodeTimestamp(pkt.Timestamp), nil + return [][]byte{d.fragmentedBuffer}, d.timeDecoder.Decode(pkt.Timestamp), nil } // DecodeUntilMarker decodes NALUs from a RTP/H264 packet and puts them in a buffer. diff --git a/pkg/rtph264/rtph264_test.go b/pkg/rtph264/rtph264_test.go index 0f4356a8..8fd47618 100644 --- a/pkg/rtph264/rtph264_test.go +++ b/pkg/rtph264/rtph264_test.go @@ -259,35 +259,6 @@ func TestDecode(t *testing.T) { } } -func TestDecodeTimestampOverflow(t *testing.T) { - d := NewDecoder() - var pts time.Duration - - for _, ts := range []uint32{ - 4294877296, - 90001, - 3240090001, - 565122706, - } { - pkt := rtp.Packet{ - Header: rtp.Header{ - Version: 2, - Marker: true, - PayloadType: 96, - SequenceNumber: 0x01, - Timestamp: ts, - SSRC: 0xba9da416, - }, - Payload: []byte("\x00\x00"), - } - var err error - _, pts, err = d.Decode(&pkt) - require.NoError(t, err) - } - - require.Equal(t, 15*60*60*time.Second+2*time.Second, pts) -} - func TestDecodePartOfFragmentedBeforeSingle(t *testing.T) { d := NewDecoder() diff --git a/pkg/rtptimedec/decoder.go b/pkg/rtptimedec/decoder.go new file mode 100644 index 00000000..2949e6ac --- /dev/null +++ b/pkg/rtptimedec/decoder.go @@ -0,0 +1,38 @@ +// Package rtptimedec contains a RTP timestamp decoder. +package rtptimedec + +import ( + "time" +) + +// Decoder is a RTP timestamp decoder. +type Decoder struct { + clockRate time.Duration + tsAdd int64 + tsInitial *int64 + tsPrev *int64 +} + +// New allocates a Decoder. +func New(clockRate int) *Decoder { + return &Decoder{ + clockRate: time.Duration(clockRate), + } +} + +// Decode decodes a RTP timestamp. +func (d *Decoder) Decode(ts uint32) time.Duration { + ts64 := int64(ts) + d.tsAdd + + if d.tsPrev != nil && (ts64-*d.tsPrev) < -0xFFFF { + ts64 += 0xFFFFFFFF + d.tsAdd += 0xFFFFFFFF + } + d.tsPrev = &ts64 + + if d.tsInitial == nil { + d.tsInitial = &ts64 + } + + return time.Duration(ts64-*d.tsInitial) * time.Second / d.clockRate +} diff --git a/pkg/rtptimedec/decoder_test.go b/pkg/rtptimedec/decoder_test.go new file mode 100644 index 00000000..395f9f23 --- /dev/null +++ b/pkg/rtptimedec/decoder_test.go @@ -0,0 +1,24 @@ +package rtptimedec + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestOverflow(t *testing.T) { + d := New(90000) + var pts time.Duration + + for _, ts := range []uint32{ + 4294877296, + 90001, + 3240090001, + 565122706, + } { + pts = d.Decode(ts) + } + + require.Equal(t, 15*60*60*time.Second+2*time.Second, pts) +}