/
helpers.go
142 lines (120 loc) · 3.84 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package sfu
import (
"encoding/binary"
"strings"
"sync/atomic"
"time"
"github.com/pion/ion-sfu/pkg/buffer"
"github.com/pion/webrtc/v3"
)
var (
ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
)
type atomicBool int32
type ntpTime uint64
func (a *atomicBool) set(value bool) (swapped bool) {
if value {
return atomic.SwapInt32((*int32)(a), 1) == 0
}
return atomic.SwapInt32((*int32)(a), 0) == 1
}
func (a *atomicBool) get() bool {
return atomic.LoadInt32((*int32)(a)) != 0
}
// setVp8TemporalLayer is a helper to detect and modify accordingly the vp8 payload to reflect
// temporal changes in the SFU.
// VP8 temporal layers implemented according https://tools.ietf.org/html/rfc7741
func setVP8TemporalLayer(p *buffer.ExtPacket, d *DownTrack) (buf []byte, picID uint16, tlz0Idx uint8, drop bool) {
pkt, ok := p.Payload.(buffer.VP8)
if !ok {
return
}
layer := atomic.LoadInt32(&d.temporalLayer)
currentLayer := uint16(layer)
currentTargetLayer := uint16(layer >> 16)
// Check if temporal getLayer is requested
if currentTargetLayer != currentLayer {
if pkt.TID <= uint8(currentTargetLayer) {
atomic.StoreInt32(&d.temporalLayer, int32(currentTargetLayer)<<16|int32(currentTargetLayer))
}
} else if pkt.TID > uint8(currentLayer) {
drop = true
return
}
buf = *d.payload
buf = buf[:len(p.Packet.Payload)]
copy(buf, p.Packet.Payload)
picID = pkt.PictureID - d.simulcast.refPicID + d.simulcast.pRefPicID + 1
tlz0Idx = pkt.TL0PICIDX - d.simulcast.refTlZIdx + d.simulcast.pRefTlZIdx + 1
if p.Head {
d.simulcast.lPicID = picID
d.simulcast.lTlZIdx = tlz0Idx
}
modifyVP8TemporalPayload(buf, pkt.PicIDIdx, pkt.TlzIdx, picID, tlz0Idx, pkt.MBit)
return
}
func modifyVP8TemporalPayload(payload []byte, picIDIdx, tlz0Idx int, picID uint16, tlz0ID uint8, mBit bool) {
pid := make([]byte, 2)
binary.BigEndian.PutUint16(pid, picID)
payload[picIDIdx] = pid[0]
if mBit {
payload[picIDIdx] |= 0x80
payload[picIDIdx+1] = pid[1]
}
payload[tlz0Idx] = tlz0ID
}
// Do a fuzzy find for a codec in the list of codecs
// Used for lookup up a codec in an existing list to find a match
func codecParametersFuzzySearch(needle webrtc.RTPCodecParameters, haystack []webrtc.RTPCodecParameters) (webrtc.RTPCodecParameters, error) {
// First attempt to match on MimeType + SDPFmtpLine
for _, c := range haystack {
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) &&
c.RTPCodecCapability.SDPFmtpLine == needle.RTPCodecCapability.SDPFmtpLine {
return c, nil
}
}
// Fallback to just MimeType
for _, c := range haystack {
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) {
return c, nil
}
}
return webrtc.RTPCodecParameters{}, webrtc.ErrCodecNotFound
}
func ntpToMillisSinceEpoch(ntp uint64) uint64 {
// ntp time since epoch calculate fractional ntp as milliseconds
// (lower 32 bits stored as 1/2^32 seconds) and add
// ntp seconds (stored in higher 32 bits) as milliseconds
return (((ntp & 0xFFFFFFFF) * 1000) >> 32) + ((ntp >> 32) * 1000)
}
func fastForwardTimestampAmount(newestTimestamp uint32, referenceTimestamp uint32) uint32 {
if buffer.IsTimestampWrapAround(newestTimestamp, referenceTimestamp) {
return uint32(uint64(newestTimestamp) + 0x100000000 - uint64(referenceTimestamp))
}
if newestTimestamp < referenceTimestamp {
return 0
}
return newestTimestamp - referenceTimestamp
}
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)
}