node-datachannel 0.1.13 → 0.2.1
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 +16 -4
- package/README.md +214 -64
- package/examples/README.md +2 -2
- package/examples/media/README.md +22 -0
- package/examples/media/main.html +45 -0
- package/examples/media/media.js +55 -0
- package/lib/index.d.ts +96 -1
- package/package.json +21 -7
- package/src/data-channel-wrapper.cpp +85 -101
- package/src/data-channel-wrapper.h +18 -11
- package/src/main.cpp +8 -0
- package/src/media-audio-wrapper.cpp +457 -0
- package/src/media-audio-wrapper.h +54 -0
- package/src/media-direction.cpp +43 -0
- package/src/media-direction.h +10 -0
- package/src/media-rtcpreceivingsession-wrapper.cpp +63 -0
- package/src/media-rtcpreceivingsession-wrapper.h +36 -0
- package/src/media-track-wrapper.cpp +333 -0
- package/src/media-track-wrapper.h +58 -0
- package/src/media-video-wrapper.cpp +490 -0
- package/src/media-video-wrapper.h +56 -0
- package/src/peer-connection-wrapper.cpp +218 -50
- package/src/peer-connection-wrapper.h +25 -9
- package/src/rtc-wrapper.cpp +13 -1
- package/src/rtc-wrapper.h +3 -1
- package/src/thread-safe-callback.cpp +69 -0
- package/src/thread-safe-callback.h +57 -0
- package/test/connectivity.js +2 -1
- package/test/test.js +111 -112
package/CMakeLists.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
cmake_minimum_required(VERSION 3.15)
|
|
2
2
|
cmake_policy(SET CMP0091 NEW)
|
|
3
|
-
project(node_datachannel VERSION 0.1
|
|
3
|
+
project(node_datachannel VERSION 0.2.1)
|
|
4
4
|
|
|
5
5
|
include_directories(${CMAKE_JS_INC})
|
|
6
6
|
|
|
@@ -27,10 +27,10 @@ include(FetchContent)
|
|
|
27
27
|
FetchContent_Declare(
|
|
28
28
|
libdatachannel
|
|
29
29
|
GIT_REPOSITORY https://github.com/paullouisageneau/libdatachannel.git
|
|
30
|
-
GIT_TAG "v0.
|
|
30
|
+
GIT_TAG "v0.16.4"
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
-
option(NO_MEDIA "Disable media transport support in libdatachannel"
|
|
33
|
+
option(NO_MEDIA "Disable media transport support in libdatachannel" OFF)
|
|
34
34
|
option(NO_WEBSOCKET "Disable WebSocket support in libdatachannel" ON)
|
|
35
35
|
|
|
36
36
|
FetchContent_GetProperties(libdatachannel)
|
|
@@ -42,8 +42,14 @@ endif()
|
|
|
42
42
|
|
|
43
43
|
add_library(${PROJECT_NAME} SHARED
|
|
44
44
|
src/rtc-wrapper.cpp
|
|
45
|
+
src/media-direction.cpp
|
|
46
|
+
src/media-rtcpreceivingsession-wrapper.cpp
|
|
47
|
+
src/media-track-wrapper.cpp
|
|
48
|
+
src/media-audio-wrapper.cpp
|
|
49
|
+
src/media-video-wrapper.cpp
|
|
45
50
|
src/data-channel-wrapper.cpp
|
|
46
51
|
src/peer-connection-wrapper.cpp
|
|
52
|
+
src/thread-safe-callback.cpp
|
|
47
53
|
src/main.cpp
|
|
48
54
|
${CMAKE_JS_SRC}
|
|
49
55
|
)
|
|
@@ -53,10 +59,16 @@ set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
|
|
|
53
59
|
|
|
54
60
|
target_include_directories(${PROJECT_NAME} PRIVATE
|
|
55
61
|
${CMAKE_SOURCE_DIR}/node_modules/node-addon-api
|
|
56
|
-
${CMAKE_SOURCE_DIR}/node_modules/napi-thread-safe-callback
|
|
57
62
|
${CMAKE_BINARY_DIR}/_deps/libdatachannel-src/include
|
|
58
63
|
)
|
|
59
64
|
|
|
65
|
+
# For compatibility with Node.js 12
|
|
66
|
+
# This should be removed along with the napi-thread-safe-callback-cancellable dependency when dropping support at end-of-life
|
|
67
|
+
target_compile_definitions(${PROJECT_NAME} PRIVATE -DLEGACY_NAPI_THREAD_SAFE_CALLBACK)
|
|
68
|
+
target_include_directories(${PROJECT_NAME} PRIVATE
|
|
69
|
+
${CMAKE_SOURCE_DIR}/node_modules/napi-thread-safe-callback-cancellable
|
|
70
|
+
)
|
|
71
|
+
|
|
60
72
|
set(LINK_LIBRARIES
|
|
61
73
|
${CMAKE_JS_LIB}
|
|
62
74
|
datachannel-static
|
package/README.md
CHANGED
|
@@ -1,60 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Easy to use WebRTC data channels and media transport
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
+
- Easy to use
|
|
6
|
+
- Lightweight
|
|
7
|
+
- No need to deal with WebRTC stack!
|
|
8
|
+
- Small binary sizes
|
|
9
|
+
- Has Prebuilt binaries (Linux,Windows,ARM)
|
|
10
|
+
- Type infos for Typescript
|
|
11
|
+
|
|
5
12
|
> "libdatachannel is a standalone implementation of WebRTC Data Channels, WebRTC Media Transport, and WebSockets in C++17 with C bindings for POSIX platforms (including GNU/Linux, Android, and Apple macOS) and Microsoft Windows. It enables direct connectivity between native applications and web browsers without the pain of importing the entire WebRTC stack. "
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
|
|
15
|
+
This project is NodeJS bindings for [libdatachannel](https://github.com/paullouisageneau/libdatachannel) library.
|
|
8
16
|
|
|
9
17
|
Please check [libdatachannel](https://github.com/paullouisageneau/libdatachannel) for Compatibility & WebRTC details.
|
|
10
18
|
|
|
11
|
-
##
|
|
19
|
+
## Example Usage
|
|
12
20
|
```js
|
|
13
21
|
const nodeDataChannel = require('node-datachannel');
|
|
14
22
|
|
|
15
23
|
// Log Level
|
|
16
24
|
nodeDataChannel.initLogger("Debug");
|
|
17
25
|
|
|
18
|
-
// SCTP Settings ( use of nodeDataChannel.setSctpSettings() )
|
|
19
|
-
// export interface SctpSettings {
|
|
20
|
-
// recvBufferSize?: number;
|
|
21
|
-
// sendBufferSize?: number;
|
|
22
|
-
// maxChunksOnQueue?: number;
|
|
23
|
-
// initialCongestionWindow?: number;
|
|
24
|
-
// congestionControlModule?: number;
|
|
25
|
-
// delayedSackTime?: number;
|
|
26
|
-
// }
|
|
27
|
-
|
|
28
26
|
let dc1 = null;
|
|
29
27
|
let dc2 = null;
|
|
30
28
|
|
|
31
|
-
// Config options
|
|
32
|
-
// export interface RtcConfig {
|
|
33
|
-
// iceServers: string[];
|
|
34
|
-
// proxyServer?: ProxyServer;
|
|
35
|
-
// enableIceTcp?: boolean;
|
|
36
|
-
// portRangeBegin?: number;
|
|
37
|
-
// portRangeEnd?: number;
|
|
38
|
-
// maxMessageSize?: number;
|
|
39
|
-
// iceTransportPolicy?: TransportPolicy;
|
|
40
|
-
// }
|
|
41
|
-
|
|
42
|
-
// "iceServers" option is an array of stun/turn server urls
|
|
43
|
-
// Examples;
|
|
44
|
-
// STUN Server Example : stun:stun.l.google.com:19302
|
|
45
|
-
// TURN Server Example : turn:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT
|
|
46
|
-
// TURN Server Example (TCP) : turn:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT?transport=tcp
|
|
47
|
-
// TURN Server Example (TLS) : turns:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT
|
|
48
|
-
|
|
49
29
|
let peer1 = new nodeDataChannel.PeerConnection("Peer1", { iceServers: ["stun:stun.l.google.com:19302"] });
|
|
50
30
|
|
|
51
31
|
// Set Callbacks
|
|
52
|
-
peer1.onStateChange((state) => {
|
|
53
|
-
console.log("Peer1 State:", state);
|
|
54
|
-
});
|
|
55
|
-
peer1.onGatheringStateChange((state) => {
|
|
56
|
-
console.log("Peer1 GatheringState:", state);
|
|
57
|
-
});
|
|
58
32
|
peer1.onLocalDescription((sdp, type) => {
|
|
59
33
|
console.log("Peer1 SDP:", sdp, " Type:", type);
|
|
60
34
|
peer2.setRemoteDescription(sdp, type);
|
|
@@ -67,12 +41,6 @@ peer1.onLocalCandidate((candidate, mid) => {
|
|
|
67
41
|
let peer2 = new nodeDataChannel.PeerConnection("Peer2", { iceServers: ["stun:stun.l.google.com:19302"] });
|
|
68
42
|
|
|
69
43
|
// Set Callbacks
|
|
70
|
-
peer2.onStateChange((state) => {
|
|
71
|
-
console.log("Peer2 State:", state);
|
|
72
|
-
});
|
|
73
|
-
peer2.onGatheringStateChange((state) => {
|
|
74
|
-
console.log("Peer2 GatheringState:", state);
|
|
75
|
-
});
|
|
76
44
|
peer2.onLocalDescription((sdp, type) => {
|
|
77
45
|
console.log("Peer2 SDP:", sdp, " Type:", type);
|
|
78
46
|
peer1.setRemoteDescription(sdp, type);
|
|
@@ -90,27 +58,12 @@ peer2.onDataChannel((dc) => {
|
|
|
90
58
|
dc2.sendMessage("Hello From Peer2");
|
|
91
59
|
});
|
|
92
60
|
|
|
93
|
-
// DataChannel Options
|
|
94
|
-
// export interface DataChannelInitConfig {
|
|
95
|
-
// protocol?: string;
|
|
96
|
-
// negotiated?: boolean;
|
|
97
|
-
// id?: number;
|
|
98
|
-
// ordered?: boolean;
|
|
99
|
-
// maxPacketLifeTime?: number;
|
|
100
|
-
// maxRetransmits?: number;
|
|
101
|
-
//
|
|
102
|
-
// // Deprecated, use ordered, maxPacketLifeTime, and maxRetransmits
|
|
103
|
-
// reliability?: {
|
|
104
|
-
// type?: ReliabilityType;
|
|
105
|
-
// unordered?: boolean;
|
|
106
|
-
// rexmit?: number;
|
|
107
|
-
// }
|
|
108
|
-
// }
|
|
109
61
|
dc1 = peer1.createDataChannel("test");
|
|
62
|
+
|
|
110
63
|
dc1.onOpen(() => {
|
|
111
64
|
dc1.sendMessage("Hello from Peer1");
|
|
112
|
-
// Binary message: Use sendMessageBinary(Buffer)
|
|
113
65
|
});
|
|
66
|
+
|
|
114
67
|
dc1.onMessage((msg) => {
|
|
115
68
|
console.log('Peer1 Received Msg:', msg);
|
|
116
69
|
});
|
|
@@ -120,13 +73,12 @@ setTimeout(() => {
|
|
|
120
73
|
dc2.close();
|
|
121
74
|
peer1.close();
|
|
122
75
|
peer2.close();
|
|
123
|
-
dc1 = null;
|
|
124
|
-
dc2 = null;
|
|
125
|
-
peer1 = null;
|
|
126
|
-
peer2 = null;
|
|
127
76
|
nodeDataChannel.cleanup();
|
|
128
77
|
}, 10 * 1000);
|
|
129
78
|
```
|
|
79
|
+
|
|
80
|
+
> Please check examples/media folder for media usage example
|
|
81
|
+
|
|
130
82
|
## Install
|
|
131
83
|
|
|
132
84
|
Prebuilt binaries are available (Node Version >= 10);
|
|
@@ -138,6 +90,204 @@ Prebuilt binaries are available (Node Version >= 10);
|
|
|
138
90
|
> npm install node-datachannel --save
|
|
139
91
|
```
|
|
140
92
|
|
|
93
|
+
## API
|
|
94
|
+
|
|
95
|
+
### PeerConnection Class
|
|
96
|
+
|
|
97
|
+
**Constructor**
|
|
98
|
+
|
|
99
|
+
let pc = new PeerConnection(peerName[,options])
|
|
100
|
+
- peerName `<string>` Peer name to use for logs etc..
|
|
101
|
+
- options `<Object>` WebRTC Config Options
|
|
102
|
+
```
|
|
103
|
+
export interface RtcConfig {
|
|
104
|
+
iceServers: (string | IceServer)[];
|
|
105
|
+
proxyServer?: ProxyServer;
|
|
106
|
+
enableIceTcp?: boolean;
|
|
107
|
+
portRangeBegin?: number;
|
|
108
|
+
portRangeEnd?: number;
|
|
109
|
+
maxMessageSize?: number;
|
|
110
|
+
iceTransportPolicy?: TransportPolicy;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const enum RelayType {
|
|
114
|
+
TurnUdp = 'TurnUdp',
|
|
115
|
+
TurnTcp = 'TurnTcp',
|
|
116
|
+
TurnTls = 'TurnTls'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface IceServer {
|
|
120
|
+
hostname: string;
|
|
121
|
+
port: Number;
|
|
122
|
+
username?: string;
|
|
123
|
+
password?: string;
|
|
124
|
+
relayType?: RelayType;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type TransportPolicy = 'all' | 'relay';
|
|
128
|
+
|
|
129
|
+
"iceServers" option is an array of stun/turn server urls
|
|
130
|
+
Examples;
|
|
131
|
+
STUN Server Example : stun:stun.l.google.com:19302
|
|
132
|
+
TURN Server Example : turn:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT
|
|
133
|
+
TURN Server Example (TCP) : turn:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT?transport=tcp
|
|
134
|
+
TURN Server Example (TLS) : turns:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**close: () => void**
|
|
139
|
+
|
|
140
|
+
Close Peer Connection
|
|
141
|
+
|
|
142
|
+
**setRemoteDescription: (sdp: string, type: DescriptionType) => void**
|
|
143
|
+
|
|
144
|
+
Set Remote Description
|
|
145
|
+
```
|
|
146
|
+
export const enum DescriptionType {
|
|
147
|
+
Unspec = 'Unspec',
|
|
148
|
+
Offer = 'Offer',
|
|
149
|
+
Answer = 'Answer'
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**addRemoteCandidate: (candidate: string, mid: string) => void**
|
|
154
|
+
|
|
155
|
+
Add remote candidate info
|
|
156
|
+
|
|
157
|
+
**createDataChannel: (label: string, config?: DataChannelInitConfig) => DataChannel**
|
|
158
|
+
|
|
159
|
+
Create new data-channel
|
|
160
|
+
* label `<string>` Data channel name
|
|
161
|
+
* config `<Object>` Data channel options
|
|
162
|
+
```
|
|
163
|
+
export interface DataChannelInitConfig {
|
|
164
|
+
protocol?: string;
|
|
165
|
+
negotiated?: boolean;
|
|
166
|
+
id?: number;
|
|
167
|
+
ordered?: boolean;
|
|
168
|
+
maxPacketLifeTime?: number;
|
|
169
|
+
maxRetransmits?: number;
|
|
170
|
+
|
|
171
|
+
// Deprecated, use ordered, maxPacketLifeTime, and maxRetransmits
|
|
172
|
+
reliability?: {
|
|
173
|
+
type?: ReliabilityType;
|
|
174
|
+
unordered?: boolean;
|
|
175
|
+
rexmit?: number;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const enum ReliabilityType {
|
|
180
|
+
Reliable = 0, Rexmit = 1, Timed = 2
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**onLocalDescription: (cb: (sdp: string, type: DescriptionType) => void) => void**
|
|
185
|
+
|
|
186
|
+
Local Description Callback
|
|
187
|
+
```
|
|
188
|
+
export const enum DescriptionType {
|
|
189
|
+
Unspec = 'Unspec',
|
|
190
|
+
Offer = 'Offer',
|
|
191
|
+
Answer = 'Answer'
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**onLocalCandidate: (cb: (candidate: string, mid: string) => void) => void**
|
|
196
|
+
|
|
197
|
+
Local Candidate Callback
|
|
198
|
+
|
|
199
|
+
**onStateChange: (cb: (state: string) => void) => void**
|
|
200
|
+
|
|
201
|
+
State Change Callback
|
|
202
|
+
|
|
203
|
+
**onGatheringStateChange: (state: (sdp: string) => void) => void**
|
|
204
|
+
|
|
205
|
+
Gathering State Change Callback
|
|
206
|
+
|
|
207
|
+
**onDataChannel: (cb: (dc: DataChannel) => void) => void**
|
|
208
|
+
|
|
209
|
+
New Data Channel Callback
|
|
210
|
+
|
|
211
|
+
**bytesSent: () => number**
|
|
212
|
+
|
|
213
|
+
Get bytes sent stat
|
|
214
|
+
|
|
215
|
+
**bytesReceived: () => number**
|
|
216
|
+
|
|
217
|
+
Get bytes received stat
|
|
218
|
+
|
|
219
|
+
**rtt: () => number**
|
|
220
|
+
|
|
221
|
+
Get rtt stat
|
|
222
|
+
|
|
223
|
+
**getSelectedCandidatePair: () => { local: SelectedCandidateInfo, remote: SelectedCandidateInfo }**
|
|
224
|
+
|
|
225
|
+
Get info about selected candidate pair
|
|
226
|
+
```
|
|
227
|
+
export interface SelectedCandidateInfo {
|
|
228
|
+
address: string;
|
|
229
|
+
port: number;
|
|
230
|
+
type: string;
|
|
231
|
+
transportType: string;
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### DataChannel Class
|
|
236
|
+
|
|
237
|
+
> You can create a new Datachannel instance by calling `PeerConnection.createDataChannel` function.
|
|
238
|
+
|
|
239
|
+
**close: () => void**
|
|
240
|
+
|
|
241
|
+
Close data channel
|
|
242
|
+
|
|
243
|
+
**getLabel: () => string**
|
|
244
|
+
|
|
245
|
+
Get label of data-channel
|
|
246
|
+
|
|
247
|
+
**sendMessage: (msg: string) => boolean**
|
|
248
|
+
|
|
249
|
+
Send Message as string
|
|
250
|
+
|
|
251
|
+
**sendMessageBinary: (buffer: Buffer) => boolean**
|
|
252
|
+
|
|
253
|
+
Send Message as binary
|
|
254
|
+
|
|
255
|
+
**isOpen: () => boolean**
|
|
256
|
+
|
|
257
|
+
Query data-channel
|
|
258
|
+
|
|
259
|
+
**bufferedAmount: () => Number**
|
|
260
|
+
|
|
261
|
+
Get current buffered amount level
|
|
262
|
+
|
|
263
|
+
**maxMessageSize: () => Number**
|
|
264
|
+
|
|
265
|
+
Get max message size of the data-channel, that could be sent
|
|
266
|
+
|
|
267
|
+
**setBufferedAmountLowThreshold: (newSize: Number) => void**
|
|
268
|
+
|
|
269
|
+
Set buffer level of the `onBufferedAmountLow` callback
|
|
270
|
+
|
|
271
|
+
**onOpen: (cb: () => void) => void**
|
|
272
|
+
|
|
273
|
+
Open callback
|
|
274
|
+
|
|
275
|
+
**onClosed: (cb: () => void) => void**
|
|
276
|
+
|
|
277
|
+
Closed callback
|
|
278
|
+
|
|
279
|
+
**onError: (cb: (err: string) => void) => void**
|
|
280
|
+
|
|
281
|
+
Error callback
|
|
282
|
+
|
|
283
|
+
**onBufferedAmountLow: (cb: () => void) => void**
|
|
284
|
+
|
|
285
|
+
Buffer level low callback
|
|
286
|
+
|
|
287
|
+
**onMessage: (cb: (msg: string | Buffer) => void) => void**
|
|
288
|
+
|
|
289
|
+
New Message callback
|
|
290
|
+
|
|
141
291
|
## Build
|
|
142
292
|
|
|
143
293
|
### Requirements
|
package/examples/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Examples
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# client-server
|
|
4
4
|
* You can use client-server example project to test WebRTC Data Channels with WebSocket signaling.
|
|
5
5
|
* It uses same logic of [libdatachannel/examples/client](https://github.com/paullouisageneau/libdatachannel/tree/master/examples) project.
|
|
6
6
|
* Contains an equivalent implementation for a node.js signaling server
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
* node client.js
|
|
19
19
|
* Enter answerer ID
|
|
20
20
|
|
|
21
|
-
> You can also use [libdatachannel/examples/client](https://github.com/paullouisageneau/libdatachannel/tree/master/examples) project's client & signaling server
|
|
21
|
+
> You can also use [libdatachannel/examples/client](https://github.com/paullouisageneau/libdatachannel/tree/master/examples) project's client & signaling server
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
# media
|
|
4
|
+
|
|
5
|
+
## Example Webcam from Browser to Port 5000
|
|
6
|
+
This is an example copy/paste demo to send your webcam from your browser and out port 5000 through the demo application.
|
|
7
|
+
|
|
8
|
+
## How to use
|
|
9
|
+
Open main.html in your browser (you must open it either as HTTPS or as a domain of http://localhost).
|
|
10
|
+
|
|
11
|
+
Start the application and copy it's offer into the text box of the web page.
|
|
12
|
+
|
|
13
|
+
Copy the answer of the webpage back into the application.
|
|
14
|
+
|
|
15
|
+
You will now see RTP traffic on `localhost:5000` of the computer that the application is running on.
|
|
16
|
+
|
|
17
|
+
Use the following gstreamer demo pipeline to display the traffic
|
|
18
|
+
(you might need to wave your hand in front of your camera to force an I-frame).
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
$ gst-launch-1.0 udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! video/x-h264,stream-format=byte-stream ! queue ! avdec_h264 ! queue ! autovideosink
|
|
22
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>libdatachannel media example</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
|
|
9
|
+
<p>Please enter the offer provided to you by the application: </p>
|
|
10
|
+
<textarea cols="50" rows="50"></textarea>
|
|
11
|
+
<button>Submit</button>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
document.querySelector('button').addEventListener('click', async () => {
|
|
15
|
+
let offer = JSON.parse(document.querySelector('textarea').value);
|
|
16
|
+
rtc = new RTCPeerConnection({
|
|
17
|
+
// Recommended for libdatachannel
|
|
18
|
+
bundlePolicy: "max-bundle",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
rtc.onicegatheringstatechange = (state) => {
|
|
22
|
+
if (rtc.iceGatheringState === 'complete') {
|
|
23
|
+
// We only want to provide an answer once all of our candidates have been added to the SDP.
|
|
24
|
+
let answer = rtc.localDescription;
|
|
25
|
+
document.querySelector('textarea').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
|
|
26
|
+
document.querySelector('p').value = 'Please paste the answer in the application.';
|
|
27
|
+
alert('Please paste the answer in the application.');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
await rtc.setRemoteDescription(offer);
|
|
31
|
+
|
|
32
|
+
let media = await navigator.mediaDevices.getUserMedia({
|
|
33
|
+
video: {
|
|
34
|
+
width: 1280,
|
|
35
|
+
height: 720
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
media.getTracks().forEach(track => rtc.addTrack(track, media));
|
|
39
|
+
let answer = await rtc.createAnswer();
|
|
40
|
+
await rtc.setLocalDescription(answer);
|
|
41
|
+
})
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const nodeDataChannel = require('../../lib/index');
|
|
2
|
+
const readline = require("readline");
|
|
3
|
+
var dgram = require('dgram');
|
|
4
|
+
|
|
5
|
+
var client = dgram.createSocket('udp4');
|
|
6
|
+
|
|
7
|
+
// Read Line Interface
|
|
8
|
+
const rl = readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// Init Logger
|
|
14
|
+
nodeDataChannel.initLogger('Debug');
|
|
15
|
+
|
|
16
|
+
let peerConnection = new nodeDataChannel.PeerConnection('pc', { iceServers: [] });
|
|
17
|
+
|
|
18
|
+
peerConnection.onStateChange((state) => {
|
|
19
|
+
console.log('State: ', state);
|
|
20
|
+
});
|
|
21
|
+
peerConnection.onGatheringStateChange((state) => {
|
|
22
|
+
// console.log('GatheringState: ', state);
|
|
23
|
+
|
|
24
|
+
if(state == 'complete'){
|
|
25
|
+
let desc = peerConnection.localDescription();
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log('## Please copy the offer below to the web page:');
|
|
28
|
+
console.log(JSON.stringify(desc));
|
|
29
|
+
console.log('\n\n');
|
|
30
|
+
console.log('## Expect RTP video traffic on localhost:5000');
|
|
31
|
+
rl.question('## Please copy/paste the answer provided by the browser: \n', (sdp) => {
|
|
32
|
+
let sdpObj = JSON.parse(sdp);
|
|
33
|
+
peerConnection.setRemoteDescription(sdpObj.sdp, sdpObj.type);
|
|
34
|
+
console.log(track.isOpen())
|
|
35
|
+
rl.close();
|
|
36
|
+
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
let video = new nodeDataChannel.Video('video','RecvOnly');
|
|
42
|
+
video.addH264Codec(96);
|
|
43
|
+
video.setBitrate(3000);
|
|
44
|
+
|
|
45
|
+
let track = peerConnection.addTrack(video);
|
|
46
|
+
let session = new nodeDataChannel.RtcpReceivingSession();
|
|
47
|
+
|
|
48
|
+
track.setMediaHandler(session);
|
|
49
|
+
track.onMessage((msg)=>{
|
|
50
|
+
client.send(msg,5000,'127.0.0.1',(err,n)=>{
|
|
51
|
+
if(err) console.log(err,n);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
peerConnection.setLocalDescription();
|
package/lib/index.d.ts
CHANGED
|
@@ -57,7 +57,9 @@ export interface RtcConfig {
|
|
|
57
57
|
export const enum DescriptionType {
|
|
58
58
|
Unspec = 'Unspec',
|
|
59
59
|
Offer = 'Offer',
|
|
60
|
-
Answer = 'Answer'
|
|
60
|
+
Answer = 'Answer',
|
|
61
|
+
Pranswer = 'Pranswer',
|
|
62
|
+
Rollback = 'Rollback'
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
export const enum ReliabilityType {
|
|
@@ -87,6 +89,94 @@ export interface SelectedCandidateInfo {
|
|
|
87
89
|
transportType: string;
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
// Must be same as rtc enum class Direction
|
|
93
|
+
export enum Direction {
|
|
94
|
+
SendOnly = 'SendOnly',
|
|
95
|
+
RecvOnly = 'RecvOnly',
|
|
96
|
+
SendRecv = 'SendRecv',
|
|
97
|
+
Inactive = 'Inactive',
|
|
98
|
+
Unknown = 'Unknown'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class RtcpReceivingSession {
|
|
102
|
+
requestBitrate: (bitRate: Number) => void;
|
|
103
|
+
requestKeyframe: () => boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export class Audio {
|
|
107
|
+
constructor(mid: string, dir: Direction);
|
|
108
|
+
addAudioCodec: (payloadType: Number, codec: string, profile?: string) => void;
|
|
109
|
+
addOpusCodec: (payloadType: Number, profile?: string) => string;
|
|
110
|
+
|
|
111
|
+
direction: () => Direction;
|
|
112
|
+
generateSdp: (eol: string, addr: string, port: string) => string;
|
|
113
|
+
mid: () => string;
|
|
114
|
+
setDirection: (dir: Direction) => void;
|
|
115
|
+
description: () => string;
|
|
116
|
+
removeFormat: (fmt: string) => void;
|
|
117
|
+
addSSRC: (ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
|
|
118
|
+
removeSSRC: (ssrc: Number) => void;
|
|
119
|
+
replaceSSRC: (oldSsrc: Number, ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
|
|
120
|
+
hasSSRC: (ssrc: Number) => boolean;
|
|
121
|
+
getSSRCs: () => Number[];
|
|
122
|
+
getCNameForSsrc: (ssrc: Number) => string;
|
|
123
|
+
setBitrate: (bitRate: Number) => void;
|
|
124
|
+
getBitrate: () => Number;
|
|
125
|
+
hasPayloadType: (payloadType: Number) => boolean;
|
|
126
|
+
addRTXCodec: (payloadType: Number, originalPayloadType: Number, clockRate: Number) => void;
|
|
127
|
+
addRTPMap: () => void;
|
|
128
|
+
parseSdpLine: (line: string) => void;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export class Video {
|
|
132
|
+
constructor(mid: string, dir: Direction);
|
|
133
|
+
addVideoCodec: (payloadType: Number, codec: string, profile?: string) => void;
|
|
134
|
+
addH264Codec: (payloadType: Number, profile?: string) => void;
|
|
135
|
+
addVP8Codec: (payloadType: Number) => void;
|
|
136
|
+
addVP9Codec: (payloadType: Number) => void;
|
|
137
|
+
|
|
138
|
+
direction: () => Direction;
|
|
139
|
+
generateSdp: (eol: string, addr: string, port: string) => string;
|
|
140
|
+
mid: () => string;
|
|
141
|
+
setDirection: (dir: Direction) => void;
|
|
142
|
+
description: () => string;
|
|
143
|
+
removeFormat: (fmt: string) => void;
|
|
144
|
+
addSSRC: (ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
|
|
145
|
+
removeSSRC: (ssrc: Number) => void;
|
|
146
|
+
replaceSSRC: (oldSsrc: Number, ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
|
|
147
|
+
hasSSRC: (ssrc: Number) => boolean;
|
|
148
|
+
getSSRCs: () => Number[];
|
|
149
|
+
getCNameForSsrc: (ssrc: Number) => string;
|
|
150
|
+
setBitrate: (bitRate: Number) => void;
|
|
151
|
+
getBitrate: () => Number;
|
|
152
|
+
hasPayloadType: (payloadType: Number) => boolean;
|
|
153
|
+
addRTXCodec: (payloadType: Number, originalPayloadType: Number, clockRate: Number) => void;
|
|
154
|
+
addRTPMap: () => void;
|
|
155
|
+
parseSdpLine: (line: string) => void;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export class Track {
|
|
159
|
+
direction: () => Direction;
|
|
160
|
+
mid: () => string;
|
|
161
|
+
close: () => void;
|
|
162
|
+
sendMessage: (msg: string) => boolean;
|
|
163
|
+
sendMessageBinary: (buffer: Buffer) => boolean;
|
|
164
|
+
isOpen: () => boolean;
|
|
165
|
+
isClosed: () => boolean;
|
|
166
|
+
availableAmount: () => Number;
|
|
167
|
+
bufferedAmount: () => Number;
|
|
168
|
+
maxMessageSize: () => Number;
|
|
169
|
+
setBufferedAmountLowThreshold: (newSize: Number) => void;
|
|
170
|
+
requestKeyframe: () => boolean;
|
|
171
|
+
setMediaHandler: (handler: RtcpReceivingSession) => void
|
|
172
|
+
onOpen: (cb: () => void) => void;
|
|
173
|
+
onClosed: (cb: () => void) => void;
|
|
174
|
+
onError: (cb: (err: string) => void) => void;
|
|
175
|
+
onAvailable: (cb: () => void) => void;
|
|
176
|
+
onBufferedAmountLow: (cb: () => void) => void;
|
|
177
|
+
onMessage: (cb: (msg: string | Buffer) => void) => void;
|
|
178
|
+
}
|
|
179
|
+
|
|
90
180
|
export class DataChannel {
|
|
91
181
|
close: () => void;
|
|
92
182
|
getLabel: () => string;
|
|
@@ -108,14 +198,19 @@ export class DataChannel {
|
|
|
108
198
|
export class PeerConnection {
|
|
109
199
|
constructor(peerName: string, config: RtcConfig);
|
|
110
200
|
close: () => void;
|
|
201
|
+
setLocalDescription: (type: DescriptionType) => void;
|
|
111
202
|
setRemoteDescription: (sdp: string, type: DescriptionType) => void;
|
|
203
|
+
localDescription: () => { type: string, sdp: string };
|
|
112
204
|
addRemoteCandidate: (candidate: string, mid: string) => void;
|
|
113
205
|
createDataChannel: (label: string, config?: DataChannelInitConfig) => DataChannel;
|
|
206
|
+
addTrack: (media: Video | Audio) => Track;
|
|
207
|
+
hasMedia: () => boolean;
|
|
114
208
|
onLocalDescription: (cb: (sdp: string, type: DescriptionType) => void) => void;
|
|
115
209
|
onLocalCandidate: (cb: (candidate: string, mid: string) => void) => void;
|
|
116
210
|
onStateChange: (cb: (state: string) => void) => void;
|
|
117
211
|
onGatheringStateChange: (state: (sdp: string) => void) => void;
|
|
118
212
|
onDataChannel: (cb: (dc: DataChannel) => void) => void;
|
|
213
|
+
onTrack: () => Track;
|
|
119
214
|
bytesSent: () => number;
|
|
120
215
|
bytesReceived: () => number;
|
|
121
216
|
rtt: () => number;
|