forked from keroserene/go-webrtc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
configuration.go
332 lines (292 loc) · 9.56 KB
/
configuration.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
package webrtc
// #include <stdlib.h>
// #include "peerconnection.h"
// #include "ctestenums.h"
import "C"
import (
"errors"
"fmt"
"strings"
"unsafe"
)
// Working draft spec: http://www.w3.org/TR/webrtc/#idl-def-Configuration
// There are quite a few differences in the latest Editor's draft, but
// for now they are omitted from this Go interface, or commented out with
// an [ED] above.)
// See https://w3c.github.io/webrtc-pc/#idl-def-Configuration
type (
BundlePolicy int
IceTransportPolicy int
RtcpMuxPolicy int
IceCredentialType int
SignalingState int
)
type Configuration struct {
IceServers []IceServer
IceTransportPolicy
BundlePolicy
// [ED] RtcpMuxPolicy RtcpMuxPolicy
PeerIdentity string // Target peer identity
// This would allow key continuity.
// [ED] Certificates []string
// [ED] IceCandidatePoolSize int
}
// These "Enum" consts must match order in: peerconnectioninterface.h
// There doesn't seem to be a way to have a named container for enums
// in go, and the idiomatic way seems to be just prefixes.
const (
BundlePolicyBalanced BundlePolicy = iota
BundlePolicyMaxBundle
BundlePolicyMaxCompat
)
var BundlePolicyString = []string{"Balanced", "MaxBundle", "MaxCompat"}
func (p BundlePolicy) String() string {
return EnumToStringSafe(int(p), BundlePolicyString)
}
const (
IceTransportPolicyNone IceTransportPolicy = iota
IceTransportPolicyRelay
// TODO: Look into why nohost is not exposed in w3c spec, but is available
// in native code? If it does need to be exposed, capitalize the i.
// (It still needs to exist, to ensure the enum values match up.
iceTransportPolicyNoHost
IceTransportPolicyAll
)
var IceTransportPolicyString = []string{"None", "Relay", "NoHost", "All"}
func (p IceTransportPolicy) String() string {
return EnumToStringSafe(int(p), IceTransportPolicyString)
}
const (
SignalingStateStable SignalingState = iota
SignalingStateHaveLocalOffer
SignalingStateHaveLocalPrAnswer
SignalingStateHaveRemoteOffer
SignalingStateHaveRemotePrAnswer
SignalingStateClosed
)
var SignalingStateString = []string{"Stable",
"HaveLocalOffer", "HaveLocalPrAnswer",
"HaveRemoteOffer", "HaveRemotePrAnswer",
"Closed"}
func (s SignalingState) String() string {
return EnumToStringSafe(int(s), SignalingStateString)
}
// TODO: [ED]
/* const (
RtcpMuxPolicyNegotiate RtcpMuxPolicy = iota
RtcpMuxPolicyRequire
) */
// TODO: [ED]
/* const (
IceCredentialTypePassword IceCredentialType = iota
IceCredentialTypeToken
) */
type IceServer struct {
Urls []string // The only "required" element.
Username string
Credential string
// [ED] CredentialType IceCredentialType
}
// Create a new IceServer object.
// Expects anywhere from one to three strings, in this order:
// - comma-separated list of urls.
// - username
// - credential
// TODO: For the ED version, may need to support CredentialType.
func NewIceServer(params ...string) (*IceServer, error) {
if len(params) < 1 {
return nil, errors.New("IceServer: missing first comma-separated Urls string.")
}
if len(params) > 3 {
WARN.Printf("IceServer: got %d strings, expect <= 3. Ignoring extras.\n",
len(params))
}
if "" == params[0] {
return nil, errors.New("IceServer: requires at least one Url")
}
urls := strings.Split(params[0], ",")
username := ""
credential := ""
for i, url := range urls {
url = strings.TrimSpace(url)
// TODO: Better url validation.
if !strings.HasPrefix(url, "stun:") &&
!strings.HasPrefix(url, "turn:") {
msg := fmt.Sprintf("IceServer: received malformed url: <%s>", url)
ERROR.Println(msg)
return nil, errors.New(msg)
}
urls[i] = url
}
if len(params) > 1 {
username = params[1]
}
if len(params) > 2 {
credential = params[2]
}
return &IceServer{
Urls: urls,
Username: username,
Credential: credential,
}, nil
}
// Create a new Configuration with default values according to spec.
// Accepts any number of |IceServer|s.
// Returns nil if there's an error.
func NewConfiguration(options ...ConfigurationOption) *Configuration {
c := new(Configuration)
c.IceTransportPolicy = IceTransportPolicyAll
c.BundlePolicy = BundlePolicyBalanced
for _, op := range options {
err := op(c)
if nil != err {
ERROR.Println(err)
}
}
// [ED] c.RtcpMuxPolicy = RtcpMuxPolicyRequire
// [ED] c.Certificates = make([]string, 0)
INFO.Println("Created Configuration at ", c)
// TODO: Determine whether the below is true.
// if 0 == len(c.IceServers) {
// ERROR.Println("Need at least one ICE server.")
// return nil
// }
return c
}
// Used in Configuration's variadic functional constructor
type ConfigurationOption func(c *Configuration) error
func OptionIceServer(params ...string) ConfigurationOption {
return func(config *Configuration) error {
return config.AddIceServer(params...)
}
}
func OptionIceTransportPolicy(policy IceTransportPolicy) ConfigurationOption {
return func(config *Configuration) error {
INFO.Println("OptionIceTransportPolicy: ", policy,
IceTransportPolicyString[policy])
config.IceTransportPolicy = policy
return nil
}
}
func OptionBundlePolicy(policy BundlePolicy) ConfigurationOption {
return func(config *Configuration) error {
config.BundlePolicy = policy
return nil
}
}
func (config *Configuration) AddIceServer(params ...string) error {
server, err := NewIceServer(params...)
if nil != err {
return err
}
config.IceServers = append(config.IceServers, *server)
return nil
}
// Helpers which prepare Go-side of cast to eventual C++ Configuration struct.
func (server *IceServer) _CGO() C.CGO_IceServer {
cServer := new(C.CGO_IceServer)
// TODO: Make this conversion nicer.
total := len(server.Urls)
if total > 0 {
sizeof := unsafe.Sizeof(uintptr(0)) // FIXME(arlolra): sizeof *void
cUrls := unsafe.Pointer(C.malloc(C.size_t(sizeof * uintptr(total))))
ptr := uintptr(cUrls)
for _, url := range server.Urls {
*(**C.char)(unsafe.Pointer(ptr)) = C.CString(url)
ptr += sizeof
}
cServer.urls = (**C.char)(cUrls)
}
cServer.numUrls = C.int(total)
cServer.username = C.CString(server.Username)
cServer.credential = C.CString(server.Credential)
return *cServer
}
const maxUrls = 1 << 24
func freeIceServer(cServer C.CGO_IceServer) {
total := int(cServer.numUrls)
if total > maxUrls {
panic("Too many urls. Something went wrong.")
}
cUrls := (*[maxUrls](*C.char))(unsafe.Pointer(cServer.urls))
for i := 0; i < total; i++ {
C.free(unsafe.Pointer(cUrls[i]))
}
C.free(unsafe.Pointer(cServer.username))
C.free(unsafe.Pointer(cServer.credential))
C.free(unsafe.Pointer(cServer.urls))
}
// The C side of things will still need to allocate memory, due to the slices.
// Assumes Configuration is valid.
func (config *Configuration) _CGO() *C.CGO_Configuration {
INFO.Println("Converting Config: ", config)
size := C.size_t(unsafe.Sizeof(C.CGO_Configuration{}))
c := (*C.CGO_Configuration)(C.malloc(size))
// Need to convert each IceServer struct individually.
total := len(config.IceServers)
if total > 0 {
sizeof := unsafe.Sizeof(C.CGO_IceServer{})
cServers := unsafe.Pointer(C.malloc(C.size_t(sizeof * uintptr(total))))
ptr := uintptr(cServers)
for _, server := range config.IceServers {
*(*C.CGO_IceServer)(unsafe.Pointer(ptr)) = server._CGO()
ptr += sizeof
}
c.iceServers = (*C.CGO_IceServer)(cServers)
}
c.numIceServers = C.int(total)
// c.iceServers = (*C.CGO_IceServer)(unsafe.Pointer(&config.IceServers))
c.iceTransportPolicy = C.int(config.IceTransportPolicy)
c.bundlePolicy = C.int(config.BundlePolicy)
// [ED] c.RtcpMuxPolicy = C.int(config.RtcpMuxPolicy)
c.peerIdentity = C.CString(config.PeerIdentity)
// [ED] c.Certificates = config.Certificates
// [ED] c.IceCandidatePoolSize = C.int(config.IceCandidatePoolSize)
return c
}
const maxIceServers = 1 << 24
func freeConfig(cConfig *C.CGO_Configuration) {
total := int(cConfig.numIceServers)
if total > maxIceServers {
panic("Too many ice servers. Something went wrong.")
} else if total > 0 {
cServers := (*[maxIceServers]C.CGO_IceServer)(unsafe.Pointer(cConfig.iceServers))
for i := 0; i < total; i++ {
freeIceServer(cServers[i])
}
C.free(unsafe.Pointer(cConfig.iceServers))
}
C.free(unsafe.Pointer(cConfig.peerIdentity))
C.free(unsafe.Pointer(cConfig))
}
/*
const {
stable SignallingState = iota
have-local-offer
have-remote-offer
have-local-pranswer
have-remote-pranswer
closed
}
*/
//
// Below are Go wrappers around intermediary C externs that extract the integer value of enums
// declared in native webrtc. This allows testing that the Go enums are correct.
// They unfortunately cannot be directly applied to the consts above.
//
var _cgoIceTransportPolicyNone = int(C.CGO_IceTransportPolicyNone)
var _cgoIceTransportPolicyRelay = int(C.CGO_IceTransportPolicyRelay)
var _cgoIceTransportPolicyNoHost = int(C.CGO_IceTransportPolicyNoHost)
var _cgoIceTransportPolicyAll = int(C.CGO_IceTransportPolicyAll)
var _cgoBundlePolicyBalanced = int(C.CGO_BundlePolicyBalanced)
var _cgoBundlePolicyMaxCompat = int(C.CGO_BundlePolicyMaxCompat)
var _cgoBundlePolicyMaxBundle = int(C.CGO_BundlePolicyMaxBundle)
// [ED]
// var _cgoRtcpMuxPolicyNegotiate = int(C.CGO_RtcpMuxPolicyNegotiate)
// var _cgoRtcpMuxPolicyRequire = int(C.CGO_RtcpMuxPolicyRequire)
var _cgoSignalingStateStable = int(C.CGO_SignalingStateStable)
var _cgoSignalingStateHaveLocalOffer = int(C.CGO_SignalingStateHaveLocalOffer)
var _cgoSignalingStateHaveLocalPrAnswer = int(C.CGO_SignalingStateHaveLocalPrAnswer)
var _cgoSignalingStateHaveRemoteOffer = int(C.CGO_SignalingStateHaveRemoteOffer)
var _cgoSignalingStateHaveRemotePrAnswer = int(C.CGO_SignalingStateHaveRemotePrAnswer)
var _cgoSignalingStateClosed = int(C.CGO_SignalingStateClosed)