-
Notifications
You must be signed in to change notification settings - Fork 11
/
helpers.go
119 lines (101 loc) · 3.38 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mediatransportutil
import (
"errors"
"fmt"
"math"
"time"
"github.com/pion/rtcp"
)
var (
ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
)
type NtpTime uint64
func (t NtpTime) Duration() time.Duration {
sec := (t >> 32) * 1e9
frac := (t & 0xffffffff) * 1e9
nsec := frac >> 32
if uint32(frac) >= 0x80000000 {
nsec++
}
return time.Duration(sec + nsec)
}
func (t NtpTime) Time() time.Time {
return ntpEpoch.Add(t.Duration())
}
func ToNtpTime(t time.Time) NtpTime {
nsec := uint64(t.Sub(ntpEpoch))
sec := nsec / 1e9
nsec = (nsec - sec*1e9) << 32
frac := nsec / 1e9
if nsec%1e9 >= 1e9/2 {
frac++
}
return NtpTime(sec<<32 | frac)
}
// ------------------------------------------
var (
ErrRttNoLastSenderReport = errors.New("no last sender report")
ErrRttNotLastSenderReport = errors.New("not last sender report")
ErrRttNoLastSendTime = errors.New("no last sent time")
ErrRttAnachronousSenderReport = errors.New("anachronous sender report")
)
func getRttMs(report *rtcp.ReceptionReport, lastSRNTP NtpTime, lastSentAt time.Time, ignoreLast bool) (uint32, error) {
if report.LastSenderReport == 0 {
return 0, ErrRttNoLastSenderReport
}
if !ignoreLast && report.LastSenderReport != uint32((lastSRNTP>>16)&0xFFFFFFFF) {
return 0, fmt.Errorf("%w, lastSR: 0x%x, received: 0x%x", ErrRttNotLastSenderReport, uint32((lastSRNTP>>16)&0xFFFFFFFF), report.LastSenderReport)
}
if !ignoreLast && lastSentAt.IsZero() {
return 0, ErrRttNoLastSendTime
}
// RTT calculation reference: https://datatracker.ietf.org/doc/html/rfc3550#section-6.4.1
// middle 32-bits of current NTP time
wall := time.Now()
now := wall
timeSinceLastSR := time.Duration(0)
if !ignoreLast {
timeSinceLastSR = time.Since(lastSentAt)
now = lastSRNTP.Time().Add(timeSinceLastSR)
}
nowNTP := ToNtpTime(now)
nowNTP32 := uint32(nowNTP >> 16)
if (nowNTP32 - report.LastSenderReport - report.Delay) > (1 << 31) {
return 0, fmt.Errorf(
"%w, lastSRNTP: %d / %+v, lastSentAt: %+v, since: %+v, wall: %+v, now: %+v, nowNTP: %d, lsr: %d, dlsr: %d / %+v, diff: %d",
ErrRttAnachronousSenderReport,
lastSRNTP,
lastSRNTP.Time().String(),
lastSentAt,
timeSinceLastSR,
wall.String(),
now.String(),
nowNTP,
report.LastSenderReport,
report.Delay,
time.Duration(float64(report.Delay)/65536.*float64(time.Second)),
int32(nowNTP32-report.LastSenderReport-report.Delay),
)
}
ntpDiff := nowNTP32 - report.LastSenderReport - report.Delay
return uint32(math.Ceil(float64(ntpDiff) * 1000.0 / 65536.0)), nil
}
func GetRttMs(report *rtcp.ReceptionReport, lastSRNTP NtpTime, lastSentAt time.Time) (uint32, error) {
return getRttMs(report, lastSRNTP, lastSentAt, false)
}
func GetRttMsFromReceiverReportOnly(report *rtcp.ReceptionReport) (uint32, error) {
return getRttMs(report, 0, time.Time{}, true)
}