0
# Media Streams
1
2
Add, remove, and manage audio/video streams with support for track-level operations, dynamic stream modification, and advanced WebRTC transceiver management.
3
4
## Capabilities
5
6
### Stream Management
7
8
Add and remove complete MediaStreams from the peer connection.
9
10
```javascript { .api }
11
/**
12
* Add a MediaStream to the connection
13
* @param stream - MediaStream to add (from getUserMedia, etc.)
14
*/
15
peer.addStream(stream: MediaStream): void;
16
17
/**
18
* Remove a MediaStream from the connection
19
* @param stream - MediaStream to remove
20
*/
21
peer.removeStream(stream: MediaStream): void;
22
```
23
24
**Usage Examples:**
25
26
```javascript
27
const Peer = require('simple-peer');
28
29
// Get user media
30
navigator.mediaDevices.getUserMedia({
31
video: true,
32
audio: true
33
}).then(stream => {
34
// Create peer with initial stream
35
const peer = new Peer({
36
initiator: true,
37
stream: stream // Constructor option
38
});
39
40
// Or add stream after creation
41
const peer2 = new Peer();
42
peer2.addStream(stream);
43
44
// Remove stream later
45
setTimeout(() => {
46
peer.removeStream(stream);
47
}, 10000);
48
});
49
50
// Multiple streams
51
Promise.all([
52
navigator.mediaDevices.getUserMedia({ video: true }),
53
navigator.mediaDevices.getDisplayMedia({ video: true })
54
]).then(([cameraStream, screenStream]) => {
55
const peer = new Peer({
56
initiator: true,
57
streams: [cameraStream, screenStream] // Constructor option
58
});
59
60
// Or add individually
61
// peer.addStream(cameraStream);
62
// peer.addStream(screenStream);
63
});
64
```
65
66
### Track Management
67
68
Manage individual MediaStreamTracks for fine-grained control over media.
69
70
```javascript { .api }
71
/**
72
* Add a MediaStreamTrack to the connection
73
* @param track - MediaStreamTrack to add
74
* @param stream - MediaStream to attach the track to
75
*/
76
peer.addTrack(track: MediaStreamTrack, stream: MediaStream): void;
77
78
/**
79
* Remove a MediaStreamTrack from the connection
80
* @param track - MediaStreamTrack to remove
81
* @param stream - MediaStream the track was attached to
82
*/
83
peer.removeTrack(track: MediaStreamTrack, stream: MediaStream): void;
84
85
/**
86
* Replace a MediaStreamTrack with another track
87
* @param oldTrack - Track to replace
88
* @param newTrack - New track to use
89
* @param stream - MediaStream the old track was attached to
90
*/
91
peer.replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack, stream: MediaStream): void;
92
```
93
94
**Usage Examples:**
95
96
```javascript
97
navigator.mediaDevices.getUserMedia({
98
video: true,
99
audio: true
100
}).then(stream => {
101
const peer = new Peer({ initiator: true });
102
const videoTrack = stream.getVideoTracks()[0];
103
const audioTrack = stream.getAudioTracks()[0];
104
105
// Add individual tracks
106
peer.addTrack(videoTrack, stream);
107
peer.addTrack(audioTrack, stream);
108
109
// Replace video track (switch camera)
110
navigator.mediaDevices.getUserMedia({
111
video: { facingMode: 'environment' }
112
}).then(newStream => {
113
const newVideoTrack = newStream.getVideoTracks()[0];
114
peer.replaceTrack(videoTrack, newVideoTrack, stream);
115
});
116
117
// Remove audio track (mute)
118
peer.removeTrack(audioTrack, stream);
119
});
120
```
121
122
### Transceiver Management
123
124
Advanced WebRTC transceiver management for complex media scenarios.
125
126
```javascript { .api }
127
/**
128
* Add an RTCRtpTransceiver to the connection
129
* @param kind - Media kind (commonly 'audio' or 'video', but accepts any string)
130
* @param init - RTCRtpTransceiverInit options
131
*/
132
peer.addTransceiver(kind: string, init?: RTCRtpTransceiverInit): void;
133
134
interface RTCRtpTransceiverInit {
135
direction?: 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive';
136
streams?: MediaStream[];
137
sendEncodings?: RTCRtpEncodingParameters[];
138
}
139
```
140
141
**Usage Examples:**
142
143
```javascript
144
const peer = new Peer({ initiator: true });
145
146
// Add receive-only transceiver
147
peer.addTransceiver('video', {
148
direction: 'recvonly'
149
});
150
151
// Add transceiver with specific streams
152
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
153
peer.addTransceiver('audio', {
154
direction: 'sendrecv',
155
streams: [stream]
156
});
157
});
158
159
// Add transceiver with encoding parameters
160
peer.addTransceiver('video', {
161
direction: 'sendonly',
162
sendEncodings: [
163
{ rid: 'high', maxBitrate: 1000000 },
164
{ rid: 'low', maxBitrate: 200000, scaleResolutionDownBy: 2 }
165
]
166
});
167
```
168
169
### Media Events
170
171
Handle incoming media streams and tracks from the remote peer.
172
173
```javascript { .api }
174
// Receive complete MediaStream
175
peer.on('stream', (stream: MediaStream) => void);
176
177
// Receive individual MediaStreamTrack
178
peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => void);
179
```
180
181
**Usage Examples:**
182
183
```javascript
184
const peer = new Peer();
185
186
// Handle incoming streams
187
peer.on('stream', stream => {
188
console.log('Received stream with', stream.getTracks().length, 'tracks');
189
190
// Display video stream
191
const video = document.querySelector('video');
192
if ('srcObject' in video) {
193
video.srcObject = stream;
194
} else {
195
video.src = URL.createObjectURL(stream); // Older browsers
196
}
197
video.play();
198
});
199
200
// Handle individual tracks
201
peer.on('track', (track, stream) => {
202
console.log('Received track:', track.kind, track.id);
203
204
if (track.kind === 'video') {
205
const video = document.createElement('video');
206
video.srcObject = new MediaStream([track]);
207
video.play();
208
document.body.appendChild(video);
209
} else if (track.kind === 'audio') {
210
const audio = document.createElement('audio');
211
audio.srcObject = new MediaStream([track]);
212
audio.play();
213
}
214
});
215
```
216
217
### Dynamic Media Control
218
219
Dynamically add, remove, and modify media streams during an active connection.
220
221
**Usage Examples:**
222
223
```javascript
224
const peer1 = new Peer({ initiator: true });
225
const peer2 = new Peer();
226
227
// Setup signaling
228
peer1.on('signal', data => peer2.signal(data));
229
peer2.on('signal', data => peer1.signal(data));
230
231
// Start without media
232
peer1.on('connect', () => {
233
console.log('Connected - adding media later');
234
});
235
236
// Add media after connection
237
function addCamera() {
238
navigator.mediaDevices.getUserMedia({
239
video: true,
240
audio: true
241
}).then(stream => {
242
peer1.addStream(stream);
243
return stream;
244
}).then(stream => {
245
// Store for later removal
246
window.currentStream = stream;
247
});
248
}
249
250
// Remove media
251
function removeCamera() {
252
if (window.currentStream) {
253
peer1.removeStream(window.currentStream);
254
window.currentStream.getTracks().forEach(track => track.stop());
255
window.currentStream = null;
256
}
257
}
258
259
// Switch camera
260
function switchCamera() {
261
if (window.currentStream) {
262
const videoTrack = window.currentStream.getVideoTracks()[0];
263
264
navigator.mediaDevices.getUserMedia({
265
video: { facingMode: 'environment' }
266
}).then(newStream => {
267
const newVideoTrack = newStream.getVideoTracks()[0];
268
peer1.replaceTrack(videoTrack, newVideoTrack, window.currentStream);
269
270
// Stop old track
271
videoTrack.stop();
272
273
// Update stream reference
274
window.currentStream.removeTrack(videoTrack);
275
window.currentStream.addTrack(newVideoTrack);
276
});
277
}
278
}
279
280
// Toggle audio
281
function toggleAudio(enabled) {
282
if (window.currentStream) {
283
const audioTrack = window.currentStream.getAudioTracks()[0];
284
if (audioTrack) {
285
audioTrack.enabled = enabled;
286
}
287
}
288
}
289
```
290
291
### Screen Sharing
292
293
Handle screen sharing streams with proper setup and cleanup.
294
295
**Usage Examples:**
296
297
```javascript
298
const peer = new Peer({ initiator: true });
299
300
async function startScreenShare() {
301
try {
302
// Get screen share stream
303
const screenStream = await navigator.mediaDevices.getDisplayMedia({
304
video: true,
305
audio: true
306
});
307
308
// Add to peer connection
309
peer.addStream(screenStream);
310
311
// Handle screen share ending
312
screenStream.getVideoTracks()[0].addEventListener('ended', () => {
313
console.log('Screen sharing ended');
314
peer.removeStream(screenStream);
315
316
// Optionally switch back to camera
317
return navigator.mediaDevices.getUserMedia({ video: true });
318
});
319
320
return screenStream;
321
} catch (err) {
322
console.error('Screen sharing failed:', err);
323
}
324
}
325
326
// Replace camera with screen share
327
async function switchToScreenShare() {
328
const cameraStream = window.currentCameraStream;
329
const screenStream = await startScreenShare();
330
331
if (cameraStream && screenStream) {
332
const cameraVideoTrack = cameraStream.getVideoTracks()[0];
333
const screenVideoTrack = screenStream.getVideoTracks()[0];
334
335
// Replace camera with screen
336
peer.replaceTrack(cameraVideoTrack, screenVideoTrack, cameraStream);
337
338
// Stop camera
339
cameraVideoTrack.stop();
340
}
341
}
342
```
343
344
### Error Handling
345
346
Handle media-related errors and track failures.
347
348
```javascript { .api }
349
// Media-specific error codes
350
interface MediaError extends Error {
351
code: 'ERR_ADD_TRANSCEIVER' | 'ERR_SENDER_REMOVED' | 'ERR_SENDER_ALREADY_ADDED' |
352
'ERR_TRACK_NOT_ADDED' | 'ERR_REMOVE_TRACK' | 'ERR_UNSUPPORTED_REPLACETRACK';
353
}
354
```
355
356
**Usage Examples:**
357
358
```javascript
359
const peer = new Peer({ initiator: true });
360
361
peer.on('error', err => {
362
switch (err.code) {
363
case 'ERR_ADD_TRANSCEIVER':
364
console.error('Failed to add transceiver:', err.message);
365
break;
366
case 'ERR_SENDER_REMOVED':
367
console.error('Track sender was removed:', err.message);
368
break;
369
case 'ERR_SENDER_ALREADY_ADDED':
370
console.error('Track already added to stream:', err.message);
371
break;
372
case 'ERR_TRACK_NOT_ADDED':
373
console.error('Cannot remove track - was never added:', err.message);
374
break;
375
case 'ERR_UNSUPPORTED_REPLACETRACK':
376
console.error('replaceTrack not supported in this browser');
377
break;
378
}
379
});
380
381
// Safe track operations
382
function safeAddTrack(peer, track, stream) {
383
try {
384
peer.addTrack(track, stream);
385
return true;
386
} catch (err) {
387
console.error('Failed to add track:', err.message);
388
return false;
389
}
390
}
391
392
function safeReplaceTrack(peer, oldTrack, newTrack, stream) {
393
try {
394
peer.replaceTrack(oldTrack, newTrack, stream);
395
return true;
396
} catch (err) {
397
console.error('Failed to replace track:', err.message);
398
// Fallback: remove old and add new
399
try {
400
peer.removeTrack(oldTrack, stream);
401
peer.addTrack(newTrack, stream);
402
return true;
403
} catch (fallbackErr) {
404
console.error('Fallback also failed:', fallbackErr.message);
405
return false;
406
}
407
}
408
}
409
```