node-datachannel 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +12 -2
- package/dist/cjs/lib/index.cjs +21 -0
- package/dist/cjs/lib/index.cjs.map +1 -1
- package/dist/cjs/polyfill/RTCPeerConnection.cjs.map +1 -1
- package/dist/esm/lib/index.mjs +15 -1
- package/dist/esm/lib/index.mjs.map +1 -1
- package/dist/esm/polyfill/RTCPeerConnection.mjs.map +1 -1
- package/dist/types/lib/index.d.ts +58 -4
- package/dist/types/lib/types.d.ts +3 -1
- package/dist/types/polyfill/RTCPeerConnection.d.ts +2 -1
- package/package.json +1 -1
- package/rollup.config.mjs +1 -1
- package/src/cpp/main.cpp +14 -0
- package/src/cpp/media-av1packetization.cpp +24 -0
- package/src/cpp/media-av1packetization.h +11 -0
- package/src/cpp/media-av1rtppacketizer-wrapper.cpp +126 -0
- package/src/cpp/media-av1rtppacketizer-wrapper.h +29 -0
- package/src/cpp/media-h264rtppacketizer-wrapper.cpp +126 -0
- package/src/cpp/media-h264rtppacketizer-wrapper.h +30 -0
- package/src/cpp/media-h265rtppacketizer-wrapper.cpp +126 -0
- package/src/cpp/media-h265rtppacketizer-wrapper.h +30 -0
- package/src/cpp/media-h26xseparator.cpp +32 -0
- package/src/cpp/media-h26xseparator.h +11 -0
- package/src/cpp/media-mediahandler-helper.cpp +28 -0
- package/src/cpp/media-mediahandler-helper.h +11 -0
- package/src/cpp/media-rtcpnackresponder-wrapper.cpp +68 -0
- package/src/cpp/media-rtcpnackresponder-wrapper.h +28 -0
- package/src/cpp/media-rtcpreceivingsession-wrapper.cpp +23 -6
- package/src/cpp/media-rtcpreceivingsession-wrapper.h +1 -1
- package/src/cpp/media-rtcpsrreporter-wrapper.cpp +93 -0
- package/src/cpp/media-rtcpsrreporter-wrapper.h +30 -0
- package/src/cpp/media-rtppacketizationconfig-wrapper.cpp +167 -0
- package/src/cpp/media-rtppacketizationconfig-wrapper.h +35 -0
- package/src/cpp/media-rtppacketizer-wrapper.cpp +95 -0
- package/src/cpp/media-rtppacketizer-wrapper.h +30 -0
- package/src/cpp/media-track-wrapper.cpp +9 -4
- package/src/cpp/media-video-wrapper.cpp +36 -1
- package/src/cpp/media-video-wrapper.h +2 -0
- package/src/lib/index.ts +69 -2
- package/src/lib/types.ts +6 -0
- package/src/polyfill/RTCPeerConnection.ts +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#include "media-rtppacketizer-wrapper.h"
|
|
2
|
+
#include "media-rtppacketizationconfig-wrapper.h"
|
|
3
|
+
#include "media-mediahandler-helper.h"
|
|
4
|
+
|
|
5
|
+
Napi::FunctionReference RtpPacketizerWrapper::constructor = Napi::FunctionReference();
|
|
6
|
+
|
|
7
|
+
Napi::Object RtpPacketizerWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
8
|
+
{
|
|
9
|
+
Napi::HandleScope scope(env);
|
|
10
|
+
|
|
11
|
+
Napi::Function func = Napi::ObjectWrap<RtpPacketizerWrapper>::DefineClass(env, "RtpPacketizer",
|
|
12
|
+
{
|
|
13
|
+
// Instance Methods
|
|
14
|
+
InstanceMethod("addToChain", &RtpPacketizerWrapper::addToChain),
|
|
15
|
+
// Accessors
|
|
16
|
+
InstanceAccessor("rtpConfig", &RtpPacketizerWrapper::getRtpPacketizationConfig, nullptr),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// If this is not the first call, we don't want to reassign the constructor (hot-reload problem)
|
|
20
|
+
if (constructor.IsEmpty())
|
|
21
|
+
{
|
|
22
|
+
constructor = Napi::Persistent(func);
|
|
23
|
+
constructor.SuppressDestruct();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.Set("RtpPacketizer", func);
|
|
27
|
+
return exports;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
RtpPacketizerWrapper::RtpPacketizerWrapper(const Napi::CallbackInfo &info)
|
|
31
|
+
: Napi::ObjectWrap<RtpPacketizerWrapper>(info)
|
|
32
|
+
{
|
|
33
|
+
Napi::Env env = info.Env();
|
|
34
|
+
|
|
35
|
+
if (!info[0].IsObject())
|
|
36
|
+
{
|
|
37
|
+
Napi::TypeError::New(env, "rtpConfig must be a RtpPacketizationConfig instance").ThrowAsJavaScriptException();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
auto obj = info[0].As<Napi::Object>();
|
|
41
|
+
if (!obj.InstanceOf(RtpPacketizationConfigWrapper::constructor.Value()))
|
|
42
|
+
{
|
|
43
|
+
Napi::TypeError::New(env, "rtpConfig must be a RtpPacketizationConfig instance").ThrowAsJavaScriptException();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// store original JS object so we can return it later
|
|
47
|
+
mRtpConfigObject = Napi::Persistent(obj);
|
|
48
|
+
mRtpConfigObject.SuppressDestruct();
|
|
49
|
+
auto rtpConfig = RtpPacketizationConfigWrapper::Unwrap(obj)->getConfigInstance();
|
|
50
|
+
|
|
51
|
+
mPacketizerPtr = std::make_unique<rtc::RtpPacketizer>(rtpConfig);
|
|
52
|
+
|
|
53
|
+
auto asMediaHandler = new std::shared_ptr<rtc::MediaHandler>(mPacketizerPtr);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
RtpPacketizerWrapper::~RtpPacketizerWrapper()
|
|
57
|
+
{
|
|
58
|
+
mPacketizerPtr.reset();
|
|
59
|
+
mRtpConfigObject.Reset();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
std::shared_ptr<rtc::RtpPacketizer> RtpPacketizerWrapper::getPacketizerInstance() { return mPacketizerPtr; }
|
|
63
|
+
|
|
64
|
+
void RtpPacketizerWrapper::addToChain(const Napi::CallbackInfo &info)
|
|
65
|
+
{
|
|
66
|
+
auto env = info.Env();
|
|
67
|
+
if (info.Length() < 1 || !info[0].IsObject())
|
|
68
|
+
{
|
|
69
|
+
Napi::TypeError::New(env, "Expected a MediaHandler instance").ThrowAsJavaScriptException();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
auto mediaHandler = asMediaHandler(info[0].As<Napi::Object>());
|
|
73
|
+
if (!mediaHandler)
|
|
74
|
+
{
|
|
75
|
+
Napi::TypeError::New(env, "Expected a MediaHandler instance. If this error is unexpected, please report a bug!")
|
|
76
|
+
.ThrowAsJavaScriptException();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
mPacketizerPtr->addToChain(mediaHandler);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Napi::Value RtpPacketizerWrapper::getRtpPacketizationConfig(const Napi::CallbackInfo &info)
|
|
83
|
+
{
|
|
84
|
+
Napi::Env env = info.Env();
|
|
85
|
+
if (!mPacketizerPtr)
|
|
86
|
+
{
|
|
87
|
+
Napi::Error::New(env, "getRtpPacketizationConfig() called on destroyed packetizer").ThrowAsJavaScriptException();
|
|
88
|
+
return env.Null();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (mRtpConfigObject.IsEmpty())
|
|
92
|
+
return env.Null();
|
|
93
|
+
|
|
94
|
+
return mRtpConfigObject.Value();
|
|
95
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#ifndef MEDIA_RTPPACKETIZER_WRAPPER_H
|
|
2
|
+
#define MEDIA_RTPPACKETIZER_WRAPPER_H
|
|
3
|
+
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <unordered_set>
|
|
6
|
+
|
|
7
|
+
#include <napi.h>
|
|
8
|
+
#include <rtc/rtc.hpp>
|
|
9
|
+
|
|
10
|
+
class RtpPacketizerWrapper : public Napi::ObjectWrap<RtpPacketizerWrapper>
|
|
11
|
+
{
|
|
12
|
+
public:
|
|
13
|
+
static Napi::FunctionReference constructor;
|
|
14
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
15
|
+
RtpPacketizerWrapper(const Napi::CallbackInfo &info);
|
|
16
|
+
~RtpPacketizerWrapper();
|
|
17
|
+
std::shared_ptr<rtc::RtpPacketizer> getPacketizerInstance();
|
|
18
|
+
|
|
19
|
+
// Functions
|
|
20
|
+
void addToChain(const Napi::CallbackInfo &info);
|
|
21
|
+
Napi::Value getRtpPacketizationConfig(const Napi::CallbackInfo &info);
|
|
22
|
+
|
|
23
|
+
// Callbacks
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
std::shared_ptr<rtc::RtpPacketizer> mPacketizerPtr = nullptr;
|
|
27
|
+
Napi::ObjectReference mRtpConfigObject;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
#endif // MEDIA_RTPPACKETIZER_WRAPPER_H
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#include "media-track-wrapper.h"
|
|
2
2
|
#include "media-direction.h"
|
|
3
|
-
#include "media-
|
|
3
|
+
#include "media-mediahandler-helper.h"
|
|
4
4
|
|
|
5
5
|
#include "plog/Log.h"
|
|
6
6
|
|
|
@@ -292,9 +292,14 @@ void TrackWrapper::setMediaHandler(const Napi::CallbackInfo &info)
|
|
|
292
292
|
return;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
std::shared_ptr<rtc::MediaHandler> handler = asMediaHandler(info[0].As<Napi::Object>());
|
|
296
|
+
if (!handler)
|
|
297
|
+
{
|
|
298
|
+
Napi::TypeError::New(env, "MediaHandler class instance expected. If this error is unexpected, please report a bug!")
|
|
299
|
+
.ThrowAsJavaScriptException();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
mTrackPtr->setMediaHandler(handler);
|
|
298
303
|
}
|
|
299
304
|
|
|
300
305
|
void TrackWrapper::onOpen(const Napi::CallbackInfo &info)
|
|
@@ -12,10 +12,12 @@ Napi::Object VideoWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
|
12
12
|
Napi::ObjectWrap<VideoWrapper>::DefineClass(env, "Video",
|
|
13
13
|
{
|
|
14
14
|
InstanceValue("media-type-video", Napi::Boolean::New(env, true)),
|
|
15
|
-
InstanceMethod("addH264Codec", &VideoWrapper::addH264Codec),
|
|
16
15
|
InstanceMethod("addVideoCodec", &VideoWrapper::addVideoCodec),
|
|
16
|
+
InstanceMethod("addH264Codec", &VideoWrapper::addH264Codec),
|
|
17
|
+
InstanceMethod("addH265Codec", &VideoWrapper::addH265Codec),
|
|
17
18
|
InstanceMethod("addVP8Codec", &VideoWrapper::addVP8Codec),
|
|
18
19
|
InstanceMethod("addVP9Codec", &VideoWrapper::addVP9Codec),
|
|
20
|
+
InstanceMethod("addAV1Codec", &VideoWrapper::addAV1Codec),
|
|
19
21
|
InstanceMethod("direction", &VideoWrapper::direction),
|
|
20
22
|
InstanceMethod("generateSdp", &VideoWrapper::generateSdp),
|
|
21
23
|
InstanceMethod("mid", &VideoWrapper::mid),
|
|
@@ -147,6 +149,22 @@ void VideoWrapper::addH264Codec(const Napi::CallbackInfo &info)
|
|
|
147
149
|
mVideoPtr->addH264Codec(payloadType, profile);
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
void VideoWrapper::addH265Codec(const Napi::CallbackInfo &info)
|
|
153
|
+
{
|
|
154
|
+
Napi::Env env = info.Env();
|
|
155
|
+
int length = info.Length();
|
|
156
|
+
|
|
157
|
+
if (length < 1 || !info[0].IsNumber())
|
|
158
|
+
{
|
|
159
|
+
Napi::TypeError::New(env, "We expect (Number) as param").ThrowAsJavaScriptException();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
int payloadType = info[0].As<Napi::Number>().ToNumber();
|
|
164
|
+
|
|
165
|
+
mVideoPtr->addH265Codec(payloadType);
|
|
166
|
+
}
|
|
167
|
+
|
|
150
168
|
void VideoWrapper::addVP8Codec(const Napi::CallbackInfo &info)
|
|
151
169
|
{
|
|
152
170
|
Napi::Env env = info.Env();
|
|
@@ -179,6 +197,23 @@ void VideoWrapper::addVP9Codec(const Napi::CallbackInfo &info)
|
|
|
179
197
|
mVideoPtr->addVP9Codec(payloadType);
|
|
180
198
|
}
|
|
181
199
|
|
|
200
|
+
void VideoWrapper::addAV1Codec(const Napi::CallbackInfo &info)
|
|
201
|
+
{
|
|
202
|
+
Napi::Env env = info.Env();
|
|
203
|
+
int length = info.Length();
|
|
204
|
+
|
|
205
|
+
if (length < 1 || !info[0].IsNumber())
|
|
206
|
+
{
|
|
207
|
+
Napi::TypeError::New(env, "We expect (Number) as param").ThrowAsJavaScriptException();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
int payloadType = info[0].As<Napi::Number>().ToNumber();
|
|
212
|
+
|
|
213
|
+
mVideoPtr->addAV1Codec(payloadType);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
182
217
|
Napi::Value VideoWrapper::direction(const Napi::CallbackInfo &info)
|
|
183
218
|
{
|
|
184
219
|
Napi::Env env = info.Env();
|
|
@@ -19,8 +19,10 @@ public:
|
|
|
19
19
|
// Functions
|
|
20
20
|
void addVideoCodec(const Napi::CallbackInfo &info);
|
|
21
21
|
void addH264Codec(const Napi::CallbackInfo &info);
|
|
22
|
+
void addH265Codec(const Napi::CallbackInfo &info);
|
|
22
23
|
void addVP8Codec(const Napi::CallbackInfo &info);
|
|
23
24
|
void addVP9Codec(const Napi::CallbackInfo &info);
|
|
25
|
+
void addAV1Codec(const Napi::CallbackInfo &info);
|
|
24
26
|
|
|
25
27
|
// class Entry
|
|
26
28
|
Napi::Value direction(const Napi::CallbackInfo &info);
|
package/src/lib/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
DescriptionType,
|
|
8
8
|
Direction,
|
|
9
9
|
LogLevel,
|
|
10
|
+
NalUnitSeparator,
|
|
11
|
+
ObuPacketization,
|
|
10
12
|
RtcConfig,
|
|
11
13
|
RTCIceConnectionState,
|
|
12
14
|
RTCIceGatheringState,
|
|
@@ -63,8 +65,10 @@ export const Audio: {
|
|
|
63
65
|
export interface Video {
|
|
64
66
|
addVideoCodec(payloadType: number, codec: string, profile?: string): void;
|
|
65
67
|
addH264Codec(payloadType: number, profile?: string): void;
|
|
68
|
+
addH265Codec(payloadType: number): void;
|
|
66
69
|
addVP8Codec(payloadType: number): void;
|
|
67
70
|
addVP9Codec(payloadType: number): void;
|
|
71
|
+
addAV1Codec(payloadType: number): void;
|
|
68
72
|
direction(): Direction;
|
|
69
73
|
generateSdp(eol: string, addr: string, port: number): string;
|
|
70
74
|
mid(): string;
|
|
@@ -102,7 +106,7 @@ export interface Track {
|
|
|
102
106
|
requestBitrate(bitRate: number): boolean;
|
|
103
107
|
setBufferedAmountLowThreshold(newSize: number): void;
|
|
104
108
|
requestKeyframe(): boolean;
|
|
105
|
-
setMediaHandler(handler:
|
|
109
|
+
setMediaHandler(handler: MediaHandler): void;
|
|
106
110
|
onOpen(cb: () => void): void;
|
|
107
111
|
onClosed(cb: () => void): void;
|
|
108
112
|
onError(cb: (err: string) => void): void;
|
|
@@ -182,12 +186,68 @@ export const IceUdpMuxListener: {
|
|
|
182
186
|
new (port: number, address?: string): IceUdpMuxListener;
|
|
183
187
|
} = nodeDataChannel.IceUdpMuxListener;
|
|
184
188
|
|
|
185
|
-
export interface
|
|
189
|
+
export interface RtpPacketizationConfig {
|
|
190
|
+
playoutDelayId: number,
|
|
191
|
+
playoutDelayMin: number,
|
|
192
|
+
playoutDelayMax: number,
|
|
193
|
+
timestamp: number,
|
|
194
|
+
get clockRate(): number
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const RtpPacketizationConfig: {
|
|
198
|
+
new(ssrc: number, cname: string, payloadType: number, clockRate: number, videoOrientationId?: number): RtpPacketizationConfig
|
|
199
|
+
} = nodeDataChannel.RtpPacketizationConfig
|
|
200
|
+
|
|
201
|
+
export interface MediaHandler {
|
|
202
|
+
addToChain(handler: MediaHandler): void
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface RtcpReceivingSession extends MediaHandler {}
|
|
186
206
|
|
|
187
207
|
export const RtcpReceivingSession: {
|
|
188
208
|
new (): RtcpReceivingSession;
|
|
189
209
|
} = nodeDataChannel.RtcpReceivingSession;
|
|
190
210
|
|
|
211
|
+
export interface RtcpNackResponder extends MediaHandler {}
|
|
212
|
+
|
|
213
|
+
export const RtcpNackResponder: {
|
|
214
|
+
new (maxSize?: number): RtcpNackResponder
|
|
215
|
+
} = nodeDataChannel.RtcpNackResponder;
|
|
216
|
+
|
|
217
|
+
export interface RtcpSrReporter extends MediaHandler {
|
|
218
|
+
get rtpConfig(): RtpPacketizationConfig
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const RtcpSrReporter: {
|
|
222
|
+
new (rtpConfig: RtpPacketizationConfig): RtcpSrReporter
|
|
223
|
+
} = nodeDataChannel.RtcpSrReporter;
|
|
224
|
+
|
|
225
|
+
export interface RtpPacketizer extends MediaHandler {
|
|
226
|
+
get rtpConfig(): RtpPacketizationConfig
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export const RtpPacketizer: {
|
|
230
|
+
new (rtpConfig: RtpPacketizationConfig): RtpPacketizer
|
|
231
|
+
} = nodeDataChannel.RtpPacketizer;
|
|
232
|
+
|
|
233
|
+
export interface H264RtpPacketizer extends RtpPacketizer {}
|
|
234
|
+
|
|
235
|
+
export const H264RtpPacketizer: {
|
|
236
|
+
new (separator: NalUnitSeparator, rtpConfig: RtpPacketizationConfig, maxFragmentSize?: number): H264RtpPacketizer
|
|
237
|
+
} = nodeDataChannel.H264RtpPacketizer
|
|
238
|
+
|
|
239
|
+
export interface H265RtpPacketizer extends RtpPacketizer {}
|
|
240
|
+
|
|
241
|
+
export const H265RtpPacketizer: {
|
|
242
|
+
new (separator: NalUnitSeparator, rtpConfig: RtpPacketizationConfig, maxFragmentSize?: number): H265RtpPacketizer
|
|
243
|
+
} = nodeDataChannel.H265RtpPacketizer
|
|
244
|
+
|
|
245
|
+
export interface AV1RtpPacketizer extends RtpPacketizer {}
|
|
246
|
+
|
|
247
|
+
export const AV1RtpPacketizer: {
|
|
248
|
+
new (packetization: ObuPacketization, rtpConfig: RtpPacketizationConfig, maxFragmentSize?: number): AV1RtpPacketizer
|
|
249
|
+
} = nodeDataChannel.AV1RtpPacketizer
|
|
250
|
+
|
|
191
251
|
export { WebSocketServer } from './websocket-server';
|
|
192
252
|
export { WebSocket } from './websocket';
|
|
193
253
|
|
|
@@ -200,6 +260,13 @@ export default {
|
|
|
200
260
|
setSctpSettings,
|
|
201
261
|
getLibraryVersion,
|
|
202
262
|
RtcpReceivingSession,
|
|
263
|
+
RtcpNackResponder,
|
|
264
|
+
RtcpSrReporter,
|
|
265
|
+
RtpPacketizationConfig,
|
|
266
|
+
RtpPacketizer,
|
|
267
|
+
H264RtpPacketizer,
|
|
268
|
+
H265RtpPacketizer,
|
|
269
|
+
AV1RtpPacketizer,
|
|
203
270
|
Track,
|
|
204
271
|
Video,
|
|
205
272
|
Audio,
|
package/src/lib/types.ts
CHANGED
|
@@ -160,3 +160,9 @@ export interface IceUdpMuxRequest {
|
|
|
160
160
|
host: string;
|
|
161
161
|
port: number;
|
|
162
162
|
}
|
|
163
|
+
|
|
164
|
+
// Same as rtc::NalUnit::Separator
|
|
165
|
+
export type NalUnitSeparator = 'Length' | 'ShortStartSequence' | 'LongStartSequence' | 'StartSequence';
|
|
166
|
+
|
|
167
|
+
// Same as rtc::AV1RtpPacketizer::Packetization
|
|
168
|
+
export type ObuPacketization = 'Obu' | 'TemporalUnit';
|
|
@@ -36,7 +36,7 @@ export default class RTCPeerConnection extends EventTarget implements globalThis
|
|
|
36
36
|
// events
|
|
37
37
|
onconnectionstatechange: globalThis.RTCPeerConnection['onconnectionstatechange'] = null;
|
|
38
38
|
// For ondatachannel we need to define type manually
|
|
39
|
-
ondatachannel: ((this: globalThis.RTCPeerConnection, ev:
|
|
39
|
+
ondatachannel: ((this: globalThis.RTCPeerConnection, ev: RTCDataChannelEvent) => any) | null;
|
|
40
40
|
onicecandidate: globalThis.RTCPeerConnection['onicecandidate'] = null;
|
|
41
41
|
onicecandidateerror: globalThis.RTCPeerConnection['onicecandidateerror'] = null;
|
|
42
42
|
oniceconnectionstatechange: globalThis.RTCPeerConnection['oniceconnectionstatechange'] = null;
|