@marianmeres/webrtc 0.0.2 → 1.0.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/AGENTS.md +357 -0
- package/API.md +916 -0
- package/README.md +6 -1
- package/dist/webrtc-manager.d.ts +45 -0
- package/dist/webrtc-manager.js +45 -1
- package/llm.txt +364 -0
- package/package.json +6 -6
package/API.md
ADDED
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Complete API documentation for `@marianmeres/webrtc`.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [WebRtcManager](#webrtcmanager)
|
|
8
|
+
- [Constructor](#constructor)
|
|
9
|
+
- [Properties](#properties)
|
|
10
|
+
- [Lifecycle Methods](#lifecycle-methods)
|
|
11
|
+
- [Audio Methods](#audio-methods)
|
|
12
|
+
- [Data Channel Methods](#data-channel-methods)
|
|
13
|
+
- [Signaling Methods](#signaling-methods)
|
|
14
|
+
- [Event Methods](#event-methods)
|
|
15
|
+
- [Utility Methods](#utility-methods)
|
|
16
|
+
- [Types](#types)
|
|
17
|
+
- [WebRtcFactory](#webrtcfactory)
|
|
18
|
+
- [WebRtcManagerConfig](#webrtcmanagerconfig)
|
|
19
|
+
- [WebRtcState](#webrtcstate)
|
|
20
|
+
- [WebRtcFsmEvent](#webrtcfsmevent)
|
|
21
|
+
- [WebRtcEvents](#webrtcevents)
|
|
22
|
+
- [Event Constants](#event-constants)
|
|
23
|
+
- [State Machine](#state-machine)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## WebRtcManager
|
|
28
|
+
|
|
29
|
+
The main class for managing WebRTC connections.
|
|
30
|
+
|
|
31
|
+
### Constructor
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
new WebRtcManager(factory: WebRtcFactory, config?: WebRtcManagerConfig)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Parameters:**
|
|
38
|
+
|
|
39
|
+
| Name | Type | Required | Description |
|
|
40
|
+
|------|------|----------|-------------|
|
|
41
|
+
| factory | `WebRtcFactory` | Yes | Factory for creating WebRTC primitives |
|
|
42
|
+
| config | `WebRtcManagerConfig` | No | Configuration options |
|
|
43
|
+
|
|
44
|
+
**Example:**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { WebRtcManager } from '@marianmeres/webrtc';
|
|
48
|
+
|
|
49
|
+
const factory = {
|
|
50
|
+
createPeerConnection: (config) => new RTCPeerConnection(config),
|
|
51
|
+
getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
|
|
52
|
+
enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const manager = new WebRtcManager(factory, {
|
|
56
|
+
peerConfig: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
|
|
57
|
+
enableMicrophone: true,
|
|
58
|
+
autoReconnect: true,
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Properties
|
|
65
|
+
|
|
66
|
+
#### state
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
get state(): WebRtcState
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Returns the current state of the WebRTC connection.
|
|
73
|
+
|
|
74
|
+
**Returns:** `WebRtcState` - One of: `IDLE`, `INITIALIZING`, `CONNECTING`, `CONNECTED`, `RECONNECTING`, `DISCONNECTED`, `ERROR`
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
#### localStream
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
get localStream(): MediaStream | null
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Returns the local media stream (microphone audio), or `null` if not initialized.
|
|
85
|
+
|
|
86
|
+
**Returns:** `MediaStream | null`
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
#### remoteStream
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
get remoteStream(): MediaStream | null
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Returns the remote media stream received from peer, or `null` if not connected.
|
|
97
|
+
|
|
98
|
+
**Returns:** `MediaStream | null`
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
#### dataChannels
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
get dataChannels(): ReadonlyMap<string, RTCDataChannel>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Returns a readonly map of all active data channels, indexed by label.
|
|
109
|
+
|
|
110
|
+
**Returns:** `ReadonlyMap<string, RTCDataChannel>`
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
#### peerConnection
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
get peerConnection(): RTCPeerConnection | null
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Returns the underlying RTCPeerConnection, or `null` if not initialized.
|
|
121
|
+
|
|
122
|
+
**Returns:** `RTCPeerConnection | null`
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Lifecycle Methods
|
|
127
|
+
|
|
128
|
+
#### initialize()
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
async initialize(): Promise<void>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Initializes the WebRTC peer connection and sets up media tracks.
|
|
135
|
+
|
|
136
|
+
- Creates RTCPeerConnection using the factory
|
|
137
|
+
- Sets up connection state listeners
|
|
138
|
+
- Enables microphone if configured
|
|
139
|
+
- Creates default data channel if configured
|
|
140
|
+
- Sets up device change detection
|
|
141
|
+
|
|
142
|
+
**State Requirement:** Must be in `IDLE` state.
|
|
143
|
+
|
|
144
|
+
**Transitions:** `IDLE` → `INITIALIZING`
|
|
145
|
+
|
|
146
|
+
**Example:**
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
await manager.initialize();
|
|
150
|
+
console.log(manager.state); // "INITIALIZING"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
#### connect()
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
async connect(): Promise<void>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Transitions to the `CONNECTING` state. Automatically calls `initialize()` if in `IDLE` state. If `DISCONNECTED`, reinitializes the peer connection.
|
|
162
|
+
|
|
163
|
+
**State Transitions:**
|
|
164
|
+
- From `IDLE`: Initializes first, then transitions to `CONNECTING`
|
|
165
|
+
- From `DISCONNECTED`: Cleans up, resets, and reinitializes
|
|
166
|
+
- From `INITIALIZING`: Transitions to `CONNECTING`
|
|
167
|
+
- From `CONNECTED` or `CONNECTING`: No-op
|
|
168
|
+
|
|
169
|
+
**Example:**
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
await manager.connect();
|
|
173
|
+
// Now ready to create offer/answer
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
#### disconnect()
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
disconnect(): void
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Disconnects the peer connection and cleans up all resources:
|
|
185
|
+
- Closes all data channels
|
|
186
|
+
- Stops local media tracks
|
|
187
|
+
- Closes peer connection
|
|
188
|
+
- Clears reconnection timers
|
|
189
|
+
|
|
190
|
+
**Transitions:** Any state → `DISCONNECTED`
|
|
191
|
+
|
|
192
|
+
**Example:**
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
manager.disconnect();
|
|
196
|
+
console.log(manager.state); // "DISCONNECTED"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
#### reset()
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
reset(): void
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Resets the manager to `IDLE` state from any state. Performs full cleanup and allows reinitialization.
|
|
208
|
+
|
|
209
|
+
**Transitions:** Any state → `IDLE`
|
|
210
|
+
|
|
211
|
+
**Example:**
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
manager.reset();
|
|
215
|
+
console.log(manager.state); // "IDLE"
|
|
216
|
+
// Can now call initialize() again
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### Audio Methods
|
|
222
|
+
|
|
223
|
+
#### enableMicrophone()
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
async enableMicrophone(enable: boolean): Promise<boolean>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Enables or disables the microphone.
|
|
230
|
+
|
|
231
|
+
**Parameters:**
|
|
232
|
+
|
|
233
|
+
| Name | Type | Description |
|
|
234
|
+
|------|------|-------------|
|
|
235
|
+
| enable | `boolean` | `true` to enable, `false` to disable |
|
|
236
|
+
|
|
237
|
+
**Returns:** `boolean` - `true` if successful, `false` if failed
|
|
238
|
+
|
|
239
|
+
**Events Emitted:**
|
|
240
|
+
- `local_stream` - When stream changes
|
|
241
|
+
- `microphone_failed` - On failure
|
|
242
|
+
|
|
243
|
+
**Example:**
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const success = await manager.enableMicrophone(true);
|
|
247
|
+
if (success) {
|
|
248
|
+
console.log('Microphone enabled');
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
#### switchMicrophone()
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
async switchMicrophone(deviceId: string): Promise<boolean>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Switches the active microphone to a different audio input device.
|
|
261
|
+
|
|
262
|
+
**Parameters:**
|
|
263
|
+
|
|
264
|
+
| Name | Type | Description |
|
|
265
|
+
|------|------|-------------|
|
|
266
|
+
| deviceId | `string` | Device ID from `getAudioInputDevices()` |
|
|
267
|
+
|
|
268
|
+
**Returns:** `boolean` - `true` if successful, `false` otherwise
|
|
269
|
+
|
|
270
|
+
**Requirements:** Peer connection must be initialized and microphone must be enabled.
|
|
271
|
+
|
|
272
|
+
**Example:**
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
const devices = await manager.getAudioInputDevices();
|
|
276
|
+
const success = await manager.switchMicrophone(devices[1].deviceId);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
#### getAudioInputDevices()
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
async getAudioInputDevices(): Promise<MediaDeviceInfo[]>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Retrieves all available audio input devices.
|
|
288
|
+
|
|
289
|
+
**Returns:** `MediaDeviceInfo[]` - Array of audio input devices, empty array on error
|
|
290
|
+
|
|
291
|
+
**Example:**
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
const devices = await manager.getAudioInputDevices();
|
|
295
|
+
devices.forEach(d => console.log(d.label, d.deviceId));
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### Data Channel Methods
|
|
301
|
+
|
|
302
|
+
#### createDataChannel()
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
createDataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel | null
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Creates a new data channel with the specified label. Returns existing channel if one with the same label already exists.
|
|
309
|
+
|
|
310
|
+
**Parameters:**
|
|
311
|
+
|
|
312
|
+
| Name | Type | Required | Description |
|
|
313
|
+
|------|------|----------|-------------|
|
|
314
|
+
| label | `string` | Yes | Unique identifier for the channel |
|
|
315
|
+
| options | `RTCDataChannelInit` | No | Channel configuration |
|
|
316
|
+
|
|
317
|
+
**Returns:** `RTCDataChannel | null` - The channel, or `null` if peer connection not initialized
|
|
318
|
+
|
|
319
|
+
**Example:**
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
const chatChannel = manager.createDataChannel('chat');
|
|
323
|
+
const fileChannel = manager.createDataChannel('file-transfer', { ordered: true });
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
#### getDataChannel()
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
getDataChannel(label: string): RTCDataChannel | undefined
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Retrieves an existing data channel by label.
|
|
335
|
+
|
|
336
|
+
**Parameters:**
|
|
337
|
+
|
|
338
|
+
| Name | Type | Description |
|
|
339
|
+
|------|------|-------------|
|
|
340
|
+
| label | `string` | The channel label |
|
|
341
|
+
|
|
342
|
+
**Returns:** `RTCDataChannel | undefined`
|
|
343
|
+
|
|
344
|
+
**Example:**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
const channel = manager.getDataChannel('chat');
|
|
348
|
+
if (channel?.readyState === 'open') {
|
|
349
|
+
// Channel is ready
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
#### sendData()
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
sendData(label: string, data: string | Blob | ArrayBuffer | ArrayBufferView): boolean
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Sends data through a data channel. Verifies channel exists and is open before sending.
|
|
362
|
+
|
|
363
|
+
**Parameters:**
|
|
364
|
+
|
|
365
|
+
| Name | Type | Description |
|
|
366
|
+
|------|------|-------------|
|
|
367
|
+
| label | `string` | The channel label |
|
|
368
|
+
| data | `string \| Blob \| ArrayBuffer \| ArrayBufferView` | Data to send |
|
|
369
|
+
|
|
370
|
+
**Returns:** `boolean` - `true` if sent, `false` otherwise
|
|
371
|
+
|
|
372
|
+
**Example:**
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// Send string
|
|
376
|
+
manager.sendData('chat', 'Hello!');
|
|
377
|
+
|
|
378
|
+
// Send binary
|
|
379
|
+
const buffer = new ArrayBuffer(8);
|
|
380
|
+
manager.sendData('binary', buffer);
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
### Signaling Methods
|
|
386
|
+
|
|
387
|
+
#### createOffer()
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
async createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit | null>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Creates an SDP offer for initiating a WebRTC connection.
|
|
394
|
+
|
|
395
|
+
**Parameters:**
|
|
396
|
+
|
|
397
|
+
| Name | Type | Description |
|
|
398
|
+
|------|------|-------------|
|
|
399
|
+
| options | `RTCOfferOptions` | Optional offer configuration |
|
|
400
|
+
|
|
401
|
+
**Returns:** `RTCSessionDescriptionInit | null` - The offer, or `null` on error
|
|
402
|
+
|
|
403
|
+
**Example:**
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const offer = await manager.createOffer();
|
|
407
|
+
await manager.setLocalDescription(offer);
|
|
408
|
+
// Send offer to remote peer via signaling channel
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
#### createAnswer()
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
async createAnswer(options?: RTCAnswerOptions): Promise<RTCSessionDescriptionInit | null>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Creates an SDP answer in response to a received offer.
|
|
420
|
+
|
|
421
|
+
**Parameters:**
|
|
422
|
+
|
|
423
|
+
| Name | Type | Description |
|
|
424
|
+
|------|------|-------------|
|
|
425
|
+
| options | `RTCAnswerOptions` | Optional answer configuration |
|
|
426
|
+
|
|
427
|
+
**Returns:** `RTCSessionDescriptionInit | null` - The answer, or `null` on error
|
|
428
|
+
|
|
429
|
+
**Example:**
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// After receiving and setting remote offer
|
|
433
|
+
const answer = await manager.createAnswer();
|
|
434
|
+
await manager.setLocalDescription(answer);
|
|
435
|
+
// Send answer to remote peer
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
#### setLocalDescription()
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
async setLocalDescription(description: RTCSessionDescriptionInit): Promise<boolean>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Sets the local SDP description (offer or answer).
|
|
447
|
+
|
|
448
|
+
**Parameters:**
|
|
449
|
+
|
|
450
|
+
| Name | Type | Description |
|
|
451
|
+
|------|------|-------------|
|
|
452
|
+
| description | `RTCSessionDescriptionInit` | The SDP description |
|
|
453
|
+
|
|
454
|
+
**Returns:** `boolean` - `true` if successful
|
|
455
|
+
|
|
456
|
+
**Example:**
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
const offer = await manager.createOffer();
|
|
460
|
+
const success = await manager.setLocalDescription(offer);
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
#### setRemoteDescription()
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
async setRemoteDescription(description: RTCSessionDescriptionInit): Promise<boolean>
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Sets the remote SDP description received from the peer.
|
|
472
|
+
|
|
473
|
+
**Parameters:**
|
|
474
|
+
|
|
475
|
+
| Name | Type | Description |
|
|
476
|
+
|------|------|-------------|
|
|
477
|
+
| description | `RTCSessionDescriptionInit` | The remote SDP |
|
|
478
|
+
|
|
479
|
+
**Returns:** `boolean` - `true` if successful
|
|
480
|
+
|
|
481
|
+
**Example:**
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// Received from signaling channel
|
|
485
|
+
await manager.setRemoteDescription(remoteOffer);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
#### addIceCandidate()
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
async addIceCandidate(candidate: RTCIceCandidateInit | null): Promise<boolean>
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Adds an ICE candidate received from the remote peer.
|
|
497
|
+
|
|
498
|
+
**Parameters:**
|
|
499
|
+
|
|
500
|
+
| Name | Type | Description |
|
|
501
|
+
|------|------|-------------|
|
|
502
|
+
| candidate | `RTCIceCandidateInit \| null` | The ICE candidate, or `null` for end-of-candidates |
|
|
503
|
+
|
|
504
|
+
**Returns:** `boolean` - `true` if successful
|
|
505
|
+
|
|
506
|
+
**Example:**
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// From signaling channel
|
|
510
|
+
await manager.addIceCandidate(remoteCandidate);
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
#### iceRestart()
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
async iceRestart(): Promise<boolean>
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Performs an ICE restart to recover from connection issues. Creates a new offer with the `iceRestart` flag.
|
|
522
|
+
|
|
523
|
+
**Returns:** `boolean` - `true` if successful
|
|
524
|
+
|
|
525
|
+
**Example:**
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
const success = await manager.iceRestart();
|
|
529
|
+
// New ICE candidates will be generated
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
#### getLocalDescription()
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
getLocalDescription(): RTCSessionDescription | null
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
Returns the current local session description.
|
|
541
|
+
|
|
542
|
+
**Returns:** `RTCSessionDescription | null`
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
#### getRemoteDescription()
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
getRemoteDescription(): RTCSessionDescription | null
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Returns the current remote session description.
|
|
553
|
+
|
|
554
|
+
**Returns:** `RTCSessionDescription | null`
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
#### getStats()
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
async getStats(): Promise<RTCStatsReport | null>
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Retrieves WebRTC statistics for the peer connection.
|
|
565
|
+
|
|
566
|
+
**Returns:** `RTCStatsReport | null` - Statistics report, or `null` if not initialized
|
|
567
|
+
|
|
568
|
+
**Example:**
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
const stats = await manager.getStats();
|
|
572
|
+
stats?.forEach(report => {
|
|
573
|
+
console.log(report.type, report);
|
|
574
|
+
});
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
### Event Methods
|
|
580
|
+
|
|
581
|
+
#### on()
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
on<K extends keyof WebRtcEvents>(
|
|
585
|
+
event: K,
|
|
586
|
+
handler: (data: WebRtcEvents[K]) => void
|
|
587
|
+
): () => void
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Subscribe to a specific WebRTC event.
|
|
591
|
+
|
|
592
|
+
**Parameters:**
|
|
593
|
+
|
|
594
|
+
| Name | Type | Description |
|
|
595
|
+
|------|------|-------------|
|
|
596
|
+
| event | `keyof WebRtcEvents` | Event name |
|
|
597
|
+
| handler | `function` | Callback receiving event data |
|
|
598
|
+
|
|
599
|
+
**Returns:** `() => void` - Unsubscribe function
|
|
600
|
+
|
|
601
|
+
**Example:**
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
const unsub = manager.on('state_change', (state) => {
|
|
605
|
+
console.log('New state:', state);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// Later: unsub();
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
#### subscribe()
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
subscribe(handler: (state: OverallState) => void): () => void
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
Subscribe to the overall state of the manager. Svelte store compatible - immediately calls handler with current state, then on changes.
|
|
620
|
+
|
|
621
|
+
**Parameters:**
|
|
622
|
+
|
|
623
|
+
| Name | Type | Description |
|
|
624
|
+
|------|------|-------------|
|
|
625
|
+
| handler | `function` | Callback receiving overall state |
|
|
626
|
+
|
|
627
|
+
**Overall State Object:**
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
{
|
|
631
|
+
state: WebRtcState;
|
|
632
|
+
localStream: MediaStream | null;
|
|
633
|
+
remoteStream: MediaStream | null;
|
|
634
|
+
dataChannels: ReadonlyMap<string, RTCDataChannel>;
|
|
635
|
+
peerConnection: RTCPeerConnection | null;
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Returns:** `() => void` - Unsubscribe function
|
|
640
|
+
|
|
641
|
+
**Example (Svelte):**
|
|
642
|
+
|
|
643
|
+
```svelte
|
|
644
|
+
<script>
|
|
645
|
+
const manager = new WebRtcManager(factory, config);
|
|
646
|
+
</script>
|
|
647
|
+
|
|
648
|
+
<p>State: {$manager.state}</p>
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Example (Vanilla):**
|
|
652
|
+
|
|
653
|
+
```typescript
|
|
654
|
+
const unsub = manager.subscribe((state) => {
|
|
655
|
+
console.log('Overall state:', state);
|
|
656
|
+
});
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
### Utility Methods
|
|
662
|
+
|
|
663
|
+
#### toMermaid()
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
toMermaid(): string
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Returns a Mermaid diagram representation of the FSM state machine.
|
|
670
|
+
|
|
671
|
+
**Returns:** `string` - Mermaid diagram source
|
|
672
|
+
|
|
673
|
+
**Example:**
|
|
674
|
+
|
|
675
|
+
```typescript
|
|
676
|
+
console.log(manager.toMermaid());
|
|
677
|
+
// Outputs Mermaid state diagram
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## Types
|
|
683
|
+
|
|
684
|
+
### WebRtcFactory
|
|
685
|
+
|
|
686
|
+
Interface for dependency injection of WebRTC primitives.
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
interface WebRtcFactory {
|
|
690
|
+
createPeerConnection(config?: RTCConfiguration): RTCPeerConnection;
|
|
691
|
+
getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
|
|
692
|
+
enumerateDevices(): Promise<MediaDeviceInfo[]>;
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**Browser Implementation:**
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
const factory: WebRtcFactory = {
|
|
700
|
+
createPeerConnection: (config) => new RTCPeerConnection(config),
|
|
701
|
+
getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
|
|
702
|
+
enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
|
|
703
|
+
};
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
### WebRtcManagerConfig
|
|
709
|
+
|
|
710
|
+
Configuration options for WebRtcManager.
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
interface WebRtcManagerConfig {
|
|
714
|
+
/** RTCConfiguration for ICE servers, certificates, etc. */
|
|
715
|
+
peerConfig?: RTCConfiguration;
|
|
716
|
+
|
|
717
|
+
/** Enable microphone on initialization. Default: false */
|
|
718
|
+
enableMicrophone?: boolean;
|
|
719
|
+
|
|
720
|
+
/** Create a data channel with this label on connect */
|
|
721
|
+
dataChannelLabel?: string;
|
|
722
|
+
|
|
723
|
+
/** Enable automatic reconnection on failure. Default: false */
|
|
724
|
+
autoReconnect?: boolean;
|
|
725
|
+
|
|
726
|
+
/** Maximum reconnection attempts. Default: 5 */
|
|
727
|
+
maxReconnectAttempts?: number;
|
|
728
|
+
|
|
729
|
+
/** Initial reconnection delay in ms (doubles each attempt). Default: 1000 */
|
|
730
|
+
reconnectDelay?: number;
|
|
731
|
+
|
|
732
|
+
/** Enable debug logging. Default: false */
|
|
733
|
+
debug?: boolean;
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
### WebRtcState
|
|
740
|
+
|
|
741
|
+
Enum of possible connection states.
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
enum WebRtcState {
|
|
745
|
+
IDLE = "IDLE",
|
|
746
|
+
INITIALIZING = "INITIALIZING",
|
|
747
|
+
CONNECTING = "CONNECTING",
|
|
748
|
+
CONNECTED = "CONNECTED",
|
|
749
|
+
RECONNECTING = "RECONNECTING",
|
|
750
|
+
DISCONNECTED = "DISCONNECTED",
|
|
751
|
+
ERROR = "ERROR"
|
|
752
|
+
}
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
| State | Description |
|
|
756
|
+
|-------|-------------|
|
|
757
|
+
| IDLE | Initial state, no resources allocated |
|
|
758
|
+
| INITIALIZING | Creating peer connection and setting up |
|
|
759
|
+
| CONNECTING | Performing offer/answer exchange |
|
|
760
|
+
| CONNECTED | Connection established, ready for communication |
|
|
761
|
+
| RECONNECTING | Attempting to restore failed connection |
|
|
762
|
+
| DISCONNECTED | Connection closed, can reconnect |
|
|
763
|
+
| ERROR | Error occurred, must call `reset()` |
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
### WebRtcFsmEvent
|
|
768
|
+
|
|
769
|
+
Internal FSM events (for reference).
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
enum WebRtcFsmEvent {
|
|
773
|
+
INIT = "initialize",
|
|
774
|
+
CONNECT = "connect",
|
|
775
|
+
CONNECTED = "connected",
|
|
776
|
+
RECONNECTING = "reconnecting",
|
|
777
|
+
DISCONNECT = "disconnect",
|
|
778
|
+
ERROR = "error",
|
|
779
|
+
RESET = "reset"
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
### WebRtcEvents
|
|
786
|
+
|
|
787
|
+
Type definition for all events and their payloads.
|
|
788
|
+
|
|
789
|
+
```typescript
|
|
790
|
+
interface WebRtcEvents {
|
|
791
|
+
state_change: WebRtcState;
|
|
792
|
+
local_stream: MediaStream | null;
|
|
793
|
+
remote_stream: MediaStream | null;
|
|
794
|
+
data_channel_open: RTCDataChannel;
|
|
795
|
+
data_channel_message: { channel: RTCDataChannel; data: any };
|
|
796
|
+
data_channel_close: RTCDataChannel;
|
|
797
|
+
ice_candidate: RTCIceCandidate | null;
|
|
798
|
+
reconnecting: { attempt: number; strategy: "ice-restart" | "full" };
|
|
799
|
+
reconnect_failed: { attempts: number };
|
|
800
|
+
device_changed: MediaDeviceInfo[];
|
|
801
|
+
microphone_failed: { error?: any; reason?: string };
|
|
802
|
+
error: Error;
|
|
803
|
+
}
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## Event Constants
|
|
809
|
+
|
|
810
|
+
Static event name constants on `WebRtcManager`.
|
|
811
|
+
|
|
812
|
+
| Constant | Value | Payload |
|
|
813
|
+
|----------|-------|---------|
|
|
814
|
+
| `EVENT_STATE_CHANGE` | `"state_change"` | `WebRtcState` |
|
|
815
|
+
| `EVENT_LOCAL_STREAM` | `"local_stream"` | `MediaStream \| null` |
|
|
816
|
+
| `EVENT_REMOTE_STREAM` | `"remote_stream"` | `MediaStream \| null` |
|
|
817
|
+
| `EVENT_DATA_CHANNEL_OPEN` | `"data_channel_open"` | `RTCDataChannel` |
|
|
818
|
+
| `EVENT_DATA_CHANNEL_MESSAGE` | `"data_channel_message"` | `{ channel, data }` |
|
|
819
|
+
| `EVENT_DATA_CHANNEL_CLOSE` | `"data_channel_close"` | `RTCDataChannel` |
|
|
820
|
+
| `EVENT_ICE_CANDIDATE` | `"ice_candidate"` | `RTCIceCandidate \| null` |
|
|
821
|
+
| `EVENT_RECONNECTING` | `"reconnecting"` | `{ attempt, strategy }` |
|
|
822
|
+
| `EVENT_RECONNECT_FAILED` | `"reconnect_failed"` | `{ attempts }` |
|
|
823
|
+
| `EVENT_DEVICE_CHANGED` | `"device_changed"` | `MediaDeviceInfo[]` |
|
|
824
|
+
| `EVENT_MICROPHONE_FAILED` | `"microphone_failed"` | `{ error?, reason? }` |
|
|
825
|
+
| `EVENT_ERROR` | `"error"` | `Error` |
|
|
826
|
+
|
|
827
|
+
**Usage:**
|
|
828
|
+
|
|
829
|
+
```typescript
|
|
830
|
+
manager.on(WebRtcManager.EVENT_ICE_CANDIDATE, (candidate) => {
|
|
831
|
+
// Send to remote peer
|
|
832
|
+
});
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## State Machine
|
|
838
|
+
|
|
839
|
+
### State Transition Diagram
|
|
840
|
+
|
|
841
|
+
```
|
|
842
|
+
┌─────────────────────────────────────────────────────────┐
|
|
843
|
+
│ │
|
|
844
|
+
▼ │
|
|
845
|
+
┌──────┐ INIT ┌──────────────┐ CONNECT ┌────────────┐ CONNECTED │
|
|
846
|
+
│ IDLE │────────▶│ INITIALIZING │──────────▶│ CONNECTING │─────────────┤
|
|
847
|
+
└──────┘ └──────────────┘ └────────────┘ │
|
|
848
|
+
▲ │ │ │
|
|
849
|
+
│ │ ERROR │ ERROR │
|
|
850
|
+
│ ▼ ▼ ▼
|
|
851
|
+
│ ┌─────────┐◀──────────────────────────────────┌───────────┐
|
|
852
|
+
│ │ ERROR │ │ CONNECTED │
|
|
853
|
+
│ └─────────┘ └───────────┘
|
|
854
|
+
│ │ │
|
|
855
|
+
│ RESET DISCONNECT
|
|
856
|
+
│ │ │
|
|
857
|
+
│ ▼ ▼
|
|
858
|
+
│ ┌──────┐◀─────────────────────────────────┌──────────────┐
|
|
859
|
+
└──────────────│ IDLE │ RESET │ DISCONNECTED │
|
|
860
|
+
└──────┘◀────────────────────┬────────────└──────────────┘
|
|
861
|
+
│ │
|
|
862
|
+
│ RECONNECTING
|
|
863
|
+
│ │
|
|
864
|
+
RESET/ ▼
|
|
865
|
+
DISCONNECT ┌──────────────┐
|
|
866
|
+
│ │ RECONNECTING │
|
|
867
|
+
└─────────────└──────────────┘
|
|
868
|
+
│
|
|
869
|
+
CONNECT
|
|
870
|
+
│
|
|
871
|
+
▼
|
|
872
|
+
┌────────────┐
|
|
873
|
+
│ CONNECTING │
|
|
874
|
+
└────────────┘
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
### Valid Transitions
|
|
878
|
+
|
|
879
|
+
| From State | Event | To State |
|
|
880
|
+
|------------|-------|----------|
|
|
881
|
+
| IDLE | INIT | INITIALIZING |
|
|
882
|
+
| INITIALIZING | CONNECT | CONNECTING |
|
|
883
|
+
| INITIALIZING | ERROR | ERROR |
|
|
884
|
+
| CONNECTING | CONNECTED | CONNECTED |
|
|
885
|
+
| CONNECTING | DISCONNECT | DISCONNECTED |
|
|
886
|
+
| CONNECTING | ERROR | ERROR |
|
|
887
|
+
| CONNECTED | DISCONNECT | DISCONNECTED |
|
|
888
|
+
| CONNECTED | ERROR | ERROR |
|
|
889
|
+
| RECONNECTING | CONNECT | CONNECTING |
|
|
890
|
+
| RECONNECTING | DISCONNECT | DISCONNECTED |
|
|
891
|
+
| RECONNECTING | RESET | IDLE |
|
|
892
|
+
| DISCONNECTED | CONNECT | CONNECTING |
|
|
893
|
+
| DISCONNECTED | RECONNECTING | RECONNECTING |
|
|
894
|
+
| DISCONNECTED | RESET | IDLE |
|
|
895
|
+
| ERROR | RESET | IDLE |
|
|
896
|
+
|
|
897
|
+
### Reconnection Strategy
|
|
898
|
+
|
|
899
|
+
When `autoReconnect: true`:
|
|
900
|
+
|
|
901
|
+
1. **Attempts 1-2:** ICE restart (quick, preserves connection)
|
|
902
|
+
2. **Attempts 3+:** Full reconnection (new peer connection)
|
|
903
|
+
3. **Backoff:** `reconnectDelay * 2^(attempt-1)` milliseconds
|
|
904
|
+
|
|
905
|
+
For "full" strategy reconnections, listen for the `reconnecting` event and re-perform signaling:
|
|
906
|
+
|
|
907
|
+
```typescript
|
|
908
|
+
manager.on('reconnecting', ({ attempt, strategy }) => {
|
|
909
|
+
if (strategy === 'full') {
|
|
910
|
+
// Re-do offer/answer exchange
|
|
911
|
+
const offer = await manager.createOffer();
|
|
912
|
+
await manager.setLocalDescription(offer);
|
|
913
|
+
sendToRemote(offer);
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
```
|