mirror of
https://github.com/aler9/gortsplib
synced 2025-10-18 05:00:47 +08:00
Fractional part now is in 1/(2^32) units, while it was in 1/(1^9) units.
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2570,7 +2571,7 @@ func TestClientPlayRTCPReport(t *testing.T) {
|
|||||||
|
|
||||||
_, err2 = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
_, err2 = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
||||||
SSRC: 753621,
|
SSRC: 753621,
|
||||||
NTPTime: ntpTimeGoToRTCP(time.Date(2017, 8, 12, 15, 30, 0, 0, time.UTC)),
|
NTPTime: ntp.Encode(time.Date(2017, 8, 12, 15, 30, 0, 0, time.UTC)),
|
||||||
RTPTime: 54352,
|
RTPTime: 54352,
|
||||||
PacketCount: 1,
|
PacketCount: 1,
|
||||||
OctetCount: 4,
|
OctetCount: 4,
|
||||||
@@ -3581,7 +3582,7 @@ func TestClientPlayPacketNTP(t *testing.T) {
|
|||||||
|
|
||||||
_, err2 = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
_, err2 = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
||||||
SSRC: 753621,
|
SSRC: 753621,
|
||||||
NTPTime: ntpTimeGoToRTCP(time.Date(2017, 8, 12, 15, 30, 0, 0, time.UTC)),
|
NTPTime: ntp.Encode(time.Date(2017, 8, 12, 15, 30, 0, 0, time.UTC)),
|
||||||
RTPTime: 54352,
|
RTPTime: 54352,
|
||||||
PacketCount: 1,
|
PacketCount: 1,
|
||||||
OctetCount: 4,
|
OctetCount: 4,
|
||||||
|
@@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,11 +70,6 @@ var testRTCPPacket = rtcp.SourceDescription{
|
|||||||
|
|
||||||
var testRTCPPacketMarshaled = mustMarshalPacketRTCP(&testRTCPPacket)
|
var testRTCPPacketMarshaled = mustMarshalPacketRTCP(&testRTCPPacket)
|
||||||
|
|
||||||
func ntpTimeGoToRTCP(v time.Time) uint64 {
|
|
||||||
s := uint64(v.UnixNano()) + 2208988800*1000000000
|
|
||||||
return (s/1000000000)<<32 | (s % 1000000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
func record(c *Client, ur string, medias []*description.Media, cb func(*description.Media, rtcp.Packet)) error {
|
func record(c *Client, ur string, medias []*description.Media, cb func(*description.Media, rtcp.Packet)) error {
|
||||||
u, err := base.ParseURL(ur)
|
u, err := base.ParseURL(ur)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1371,7 +1367,7 @@ func TestClientRecordRTCPReport(t *testing.T) {
|
|||||||
require.Equal(t, []rtcp.Packet{
|
require.Equal(t, []rtcp.Packet{
|
||||||
&rtcp.SenderReport{
|
&rtcp.SenderReport{
|
||||||
SSRC: packets[0].(*rtcp.SenderReport).SSRC,
|
SSRC: packets[0].(*rtcp.SenderReport).SSRC,
|
||||||
NTPTime: ntpTimeGoToRTCP(time.Date(1996, 2, 13, 14, 33, 5, 0, time.UTC)),
|
NTPTime: ntp.Encode(time.Date(1996, 2, 13, 14, 33, 5, 0, time.UTC)),
|
||||||
RTPTime: 1300000 + 60*90000,
|
RTPTime: 1300000 + 60*90000,
|
||||||
PacketCount: 1,
|
PacketCount: 1,
|
||||||
OctetCount: 1,
|
OctetCount: 1,
|
||||||
|
24
pkg/ntp/ntp.go
Normal file
24
pkg/ntp/ntp.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Package ntp contains functions to encode and decode timestamps to/from NTP format.
|
||||||
|
package ntp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encode encodes a timestamp in NTP format.
|
||||||
|
// Specification: RFC3550, section 4
|
||||||
|
func Encode(t time.Time) uint64 {
|
||||||
|
ntp := uint64(t.UnixNano()) + 2208988800*1000000000
|
||||||
|
secs := ntp / 1000000000
|
||||||
|
fractional := uint64(math.Round(float64((ntp%1000000000)*(1<<32)) / 1000000000))
|
||||||
|
return secs<<32 | fractional
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes a timestamp from NTP format.
|
||||||
|
// Specification: RFC3550, section 4
|
||||||
|
func Decode(v uint64) time.Time {
|
||||||
|
secs := int64((v >> 32) - 2208988800)
|
||||||
|
nanos := int64(math.Round(float64(((v & 0xFFFFFFFF) * 1000000000) / (1 << 32))))
|
||||||
|
return time.Unix(secs, nanos)
|
||||||
|
}
|
43
pkg/ntp/ntp_test.go
Normal file
43
pkg/ntp/ntp_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package ntp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
dec time.Time
|
||||||
|
enc uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
time.Date(2013, 4, 15, 11, 15, 17, 958404853, time.UTC).Local(),
|
||||||
|
15354565283395798332,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
time.Date(2013, 4, 15, 11, 15, 18, 0, time.UTC).Local(),
|
||||||
|
15354565283574448128,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncode(t *testing.T) {
|
||||||
|
for _, ca := range cases {
|
||||||
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
|
v := Encode(ca.dec)
|
||||||
|
require.Equal(t, ca.enc, v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
for _, ca := range cases {
|
||||||
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
|
v := Decode(ca.enc)
|
||||||
|
require.Equal(t, ca.dec, v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -7,17 +7,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// seconds since 1st January 1900
|
|
||||||
// higher 32 bits are the integer part, lower 32 bits are the fractional part
|
|
||||||
func ntpTimeRTCPToGo(v uint64) time.Time {
|
|
||||||
nano := int64((v>>32)*1000000000+(v&0xFFFFFFFF)) - 2208988800*1000000000
|
|
||||||
return time.Unix(0, nano)
|
|
||||||
}
|
|
||||||
|
|
||||||
func randUint32() (uint32, error) {
|
func randUint32() (uint32, error) {
|
||||||
var b [4]byte
|
var b [4]byte
|
||||||
_, err := rand.Read(b[:])
|
_, err := rand.Read(b[:])
|
||||||
@@ -418,7 +412,7 @@ func (rr *RTCPReceiver) packetNTPUnsafe(ts uint32) (time.Time, bool) {
|
|||||||
timeDiff := int32(ts - rr.lastSenderReportTimeRTP)
|
timeDiff := int32(ts - rr.lastSenderReportTimeRTP)
|
||||||
timeDiffGo := (time.Duration(timeDiff) * time.Second) / time.Duration(rr.ClockRate)
|
timeDiffGo := (time.Duration(timeDiff) * time.Second) / time.Duration(rr.ClockRate)
|
||||||
|
|
||||||
return ntpTimeRTCPToGo(rr.lastSenderReportTimeNTP).Add(timeDiffGo), true
|
return ntp.Decode(rr.lastSenderReportTimeNTP).Add(timeDiffGo), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// PacketNTP returns the NTP (absolute timestamp) of the packet.
|
// PacketNTP returns the NTP (absolute timestamp) of the packet.
|
||||||
|
@@ -5,17 +5,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// seconds since 1st January 1900
|
|
||||||
// higher 32 bits are the integer part, lower 32 bits are the fractional part
|
|
||||||
func ntpTimeGoToRTCP(v time.Time) uint64 {
|
|
||||||
s := uint64(v.UnixNano()) + 2208988800*1000000000
|
|
||||||
return (s/1000000000)<<32 | (s % 1000000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RTCPSender is a utility to send RTP packets.
|
// RTCPSender is a utility to send RTP packets.
|
||||||
// It is in charge of generating RTCP sender reports.
|
// It is in charge of generating RTCP sender reports.
|
||||||
type RTCPSender struct {
|
type RTCPSender struct {
|
||||||
@@ -112,7 +106,7 @@ func (rs *RTCPSender) report() rtcp.Packet {
|
|||||||
|
|
||||||
return &rtcp.SenderReport{
|
return &rtcp.SenderReport{
|
||||||
SSRC: rs.localSSRC,
|
SSRC: rs.localSSRC,
|
||||||
NTPTime: ntpTimeGoToRTCP(ntpTime),
|
NTPTime: ntp.Encode(ntpTime),
|
||||||
RTPTime: rtpTime,
|
RTPTime: rtpTime,
|
||||||
PacketCount: rs.packetCount,
|
PacketCount: rs.packetCount,
|
||||||
OctetCount: rs.octetCount,
|
OctetCount: rs.octetCount,
|
||||||
|
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSessionID(header base.Header) string {
|
func getSessionID(header base.Header) string {
|
||||||
@@ -87,7 +88,7 @@ func mikeyGenerate(ctx *wrappedSRTPContext) (*mikey.Message, error) {
|
|||||||
msg.Payloads = []mikey.Payload{
|
msg.Payloads = []mikey.Payload{
|
||||||
&mikey.PayloadT{
|
&mikey.PayloadT{
|
||||||
TSType: 0,
|
TSType: 0,
|
||||||
TSValue: mikeyEncodeTime(time.Now()),
|
TSValue: ntp.Encode(time.Now()),
|
||||||
},
|
},
|
||||||
&mikey.PayloadRAND{
|
&mikey.PayloadRAND{
|
||||||
Data: randData,
|
Data: randData,
|
||||||
|
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1526,7 +1527,7 @@ func TestServerPlayRTCPReport(t *testing.T) {
|
|||||||
require.Equal(t, []rtcp.Packet{
|
require.Equal(t, []rtcp.Packet{
|
||||||
&rtcp.SenderReport{
|
&rtcp.SenderReport{
|
||||||
SSRC: packets[0].(*rtcp.SenderReport).SSRC,
|
SSRC: packets[0].(*rtcp.SenderReport).SSRC,
|
||||||
NTPTime: ntpTimeGoToRTCP(time.Date(2017, 8, 10, 12, 22, 30, 0, time.UTC)),
|
NTPTime: ntp.Encode(time.Date(2017, 8, 10, 12, 22, 30, 0, time.UTC)),
|
||||||
RTPTime: 240000 + 90000*30,
|
RTPTime: 240000 + 90000*30,
|
||||||
PacketCount: 1,
|
PacketCount: 1,
|
||||||
OctetCount: 1,
|
OctetCount: 1,
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1079,7 +1080,7 @@ func TestServerRecordRTCPReport(t *testing.T) {
|
|||||||
|
|
||||||
_, err = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
_, err = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
||||||
SSRC: 753621,
|
SSRC: 753621,
|
||||||
NTPTime: ntpTimeGoToRTCP(time.Date(2018, 2, 20, 19, 0, 0, 0, time.UTC)),
|
NTPTime: ntp.Encode(time.Date(2018, 2, 20, 19, 0, 0, 0, time.UTC)),
|
||||||
RTPTime: 54352,
|
RTPTime: 54352,
|
||||||
PacketCount: 1,
|
PacketCount: 1,
|
||||||
OctetCount: 4,
|
OctetCount: 4,
|
||||||
@@ -1682,7 +1683,7 @@ func TestServerRecordPacketNTP(t *testing.T) {
|
|||||||
|
|
||||||
_, err = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
_, err = l2.WriteTo(mustMarshalPacketRTCP(&rtcp.SenderReport{
|
||||||
SSRC: 753621,
|
SSRC: 753621,
|
||||||
NTPTime: ntpTimeGoToRTCP(time.Date(2018, 2, 20, 19, 0, 0, 0, time.UTC)),
|
NTPTime: ntp.Encode(time.Date(2018, 2, 20, 19, 0, 0, 0, time.UTC)),
|
||||||
RTPTime: 54352,
|
RTPTime: 54352,
|
||||||
PacketCount: 1,
|
PacketCount: 1,
|
||||||
OctetCount: 4,
|
OctetCount: 4,
|
||||||
|
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
"github.com/bluenviron/gortsplib/v4/pkg/mikey"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/ntp"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtcpreceiver"
|
"github.com/bluenviron/gortsplib/v4/pkg/rtcpreceiver"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtcpsender"
|
"github.com/bluenviron/gortsplib/v4/pkg/rtcpsender"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
||||||
@@ -201,21 +202,6 @@ func pickFirstSupportedTransport(s *Server, tsh headers.Transports) *headers.Tra
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mikeyDecodeTime(t uint64) time.Time {
|
|
||||||
sec := t >> 32
|
|
||||||
dec := t & 0xFFFFFFFF
|
|
||||||
sec -= 2208988800
|
|
||||||
return time.Unix(int64(sec), int64(dec))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mikeyEncodeTime(n time.Time) uint64 {
|
|
||||||
nano := uint64(n.UnixNano())
|
|
||||||
sec := nano / 1000000000
|
|
||||||
dec := nano % 1000000000
|
|
||||||
sec += 2208988800
|
|
||||||
return sec<<32 | dec
|
|
||||||
}
|
|
||||||
|
|
||||||
func mikeyGetPayload[T mikey.Payload](mikeyMsg *mikey.Message) (T, bool) {
|
func mikeyGetPayload[T mikey.Payload](mikeyMsg *mikey.Message) (T, bool) {
|
||||||
var zero T
|
var zero T
|
||||||
for _, wrapped := range mikeyMsg.Payloads {
|
for _, wrapped := range mikeyMsg.Payloads {
|
||||||
@@ -241,7 +227,7 @@ func mikeyToContext(mikeyMsg *mikey.Message) (*wrappedSRTPContext, error) {
|
|||||||
return nil, fmt.Errorf("time payload not present")
|
return nil, fmt.Errorf("time payload not present")
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := mikeyDecodeTime(timePayload.TSValue)
|
ts := ntp.Decode(timePayload.TSValue)
|
||||||
diff := time.Since(ts)
|
diff := time.Since(ts)
|
||||||
if diff < -time.Hour || diff > time.Hour {
|
if diff < -time.Hour || diff > time.Hour {
|
||||||
return nil, fmt.Errorf("NTP difference is too high")
|
return nil, fmt.Errorf("NTP difference is too high")
|
||||||
|
Reference in New Issue
Block a user