peerpigeon
Version:
WebRTC-based peer-to-peer mesh networking library with intelligent routing and signaling server
428 lines (327 loc) • 12.3 kB
Markdown
# PeerPigeon Selective Streaming Guide
## Overview
PeerPigeon now supports **selective streaming**, allowing you to control exactly which peers receive your media streams. This enables sophisticated streaming patterns including 1:1, 1:many, and many:many configurations with individual peer control.
## Key Features
- **🎯 Selective Streaming**: Stream to specific peers only
- **📡 Broadcast Mode**: Traditional stream-to-all behavior
- **🔧 Dynamic Control**: Block/allow peers during active streaming
- **👥 Multiple Patterns**: Support for 1:1, 1:many, many:many topologies
- **📊 Status Monitoring**: Real-time streaming status for all peers
## API Reference
### Core Methods
#### `startSelectiveStream(peerIds, options)`
Start streaming to specific peers only.
```javascript
// 1:1 streaming (single peer)
await mesh.startSelectiveStream('peer-id-123', {
video: true,
audio: true
});
// 1:many streaming (multiple peers)
await mesh.startSelectiveStream([
'peer-id-123',
'peer-id-456',
'peer-id-789'
], {
video: true,
audio: true
});
```
**Parameters:**
- `peerIds` (string | string[]): Target peer ID(s) to stream to
- `options` (Object): Media stream options
- `video` (boolean): Enable video streaming
- `audio` (boolean): Enable audio streaming
- `deviceIds` (Object): Specific device IDs for camera/microphone
**Returns:** `Promise<MediaStream>` - The local media stream
#### `stopSelectiveStream(returnToBroadcast)`
Stop selective streaming with option to switch to broadcast mode.
```javascript
// Stop streaming entirely
await mesh.stopSelectiveStream(false);
// Stop selective and switch to broadcast mode
await mesh.stopSelectiveStream(true);
```
**Parameters:**
- `returnToBroadcast` (boolean): If true, switch to broadcast mode; if false, stop entirely
#### `enableStreamingForAllPeers()`
Switch to broadcast mode - stream to all connected peers.
```javascript
await mesh.enableStreamingForAllPeers();
```
#### `blockStreamingToPeers(peerIds)`
Block streaming to specific peers while maintaining streams to others.
```javascript
// Block streaming to specific peers
await mesh.blockStreamingToPeers(['peer-id-456', 'peer-id-789']);
// Block single peer
await mesh.blockStreamingToPeers('peer-id-123');
```
#### `allowStreamingToPeers(peerIds)`
Re-enable streaming to previously blocked peers.
```javascript
// Allow streaming to specific peers
await mesh.allowStreamingToPeers(['peer-id-456', 'peer-id-789']);
// Allow single peer
await mesh.allowStreamingToPeers('peer-id-123');
```
### Status Methods
#### `getStreamingStatus()`
Get detailed streaming status for all connected peers.
```javascript
const status = mesh.getStreamingStatus();
// Returns Map<string, StreamingStatus>
// StreamingStatus: {
// sendingStream: boolean,
// receivingStreams: boolean,
// streamType: 'broadcast' | 'selective' | 'none'
// }
```
#### `isStreamingToAll()`
Check if currently streaming to all connected peers.
```javascript
const isBroadcast = mesh.isStreamingToAll();
// Returns boolean
```
#### `getStreamingPeers()`
Get list of peers currently receiving streams.
```javascript
const streamingPeers = mesh.getStreamingPeers();
// Returns string[] - Array of peer IDs
```
#### `getBlockedStreamingPeers()`
Get list of peers blocked from receiving streams.
```javascript
const blockedPeers = mesh.getBlockedStreamingPeers();
// Returns string[] - Array of peer IDs
```
## Events
The selective streaming system emits several new events:
### `selectiveStreamStarted`
Fired when selective streaming begins.
```javascript
mesh.addEventListener('selectiveStreamStarted', (data) => {
console.log(`Selective ${data.streamType} streaming started`);
console.log(`Target peers: ${data.targetPeerIds.join(', ')}`);
console.log(`Stream:`, data.stream);
});
```
### `selectiveStreamStopped`
Fired when selective streaming stops.
```javascript
mesh.addEventListener('selectiveStreamStopped', (data) => {
console.log(`Selective streaming stopped`);
console.log(`Switched to broadcast: ${data.returnToBroadcast}`);
});
```
### `broadcastStreamEnabled`
Fired when broadcast mode is enabled.
```javascript
mesh.addEventListener('broadcastStreamEnabled', () => {
console.log('Broadcasting to all peers');
});
```
### `streamingBlockedToPeers`
Fired when peers are blocked from streaming.
```javascript
mesh.addEventListener('streamingBlockedToPeers', (data) => {
console.log(`Blocked peers: ${data.blockedPeerIds.join(', ')}`);
});
```
### `streamingAllowedToPeers`
Fired when peers are allowed streaming access.
```javascript
mesh.addEventListener('streamingAllowedToPeers', (data) => {
console.log(`Allowed peers: ${data.allowedPeerIds.join(', ')}`);
});
```
## Streaming Patterns
### 1:1 Streaming (Private Call)
Stream to exactly one peer for private communication.
```javascript
// Start 1:1 streaming
await mesh.startSelectiveStream('target-peer-id', {
video: true,
audio: true
});
console.log('Private call started');
```
### 1:Many Streaming (Broadcast to Group)
Stream to multiple selected peers (like a webinar or presentation).
```javascript
// Select target audience
const audience = ['peer-1', 'peer-2', 'peer-3'];
// Start group streaming
await mesh.startSelectiveStream(audience, {
video: true,
audio: true
});
console.log(`Presenting to ${audience.length} participants`);
```
### Many:Many Streaming (Conference)
Multiple peers streaming to each other in various patterns.
```javascript
// Each peer can define their own streaming targets
// Peer A streams to B and C
await meshA.startSelectiveStream(['peer-b', 'peer-c'], options);
// Peer B streams to A and C
await meshB.startSelectiveStream(['peer-a', 'peer-c'], options);
// Peer C streams only to A (selective participation)
await meshC.startSelectiveStream(['peer-a'], options);
```
### Dynamic Control During Streaming
Modify streaming targets while active.
```javascript
// Start streaming to group
await mesh.startSelectiveStream(['peer-1', 'peer-2', 'peer-3'], options);
// Later: Block disruptive participant
await mesh.blockStreamingToPeers(['peer-2']);
// Later: Allow them back
await mesh.allowStreamingToPeers(['peer-2']);
// Or switch to broadcast mode
await mesh.stopSelectiveStream(true);
```
## Migration from Traditional Broadcasting
### Before (Traditional Broadcasting)
```javascript
// Old way - always streams to ALL connected peers
await mesh.startMedia({
video: true,
audio: true
});
// No control over who receives the stream
```
### After (Selective Streaming)
```javascript
// New way - full control over streaming targets
// Option 1: Selective streaming
await mesh.startSelectiveStream(['peer-1', 'peer-2'], {
video: true,
audio: true
});
// Option 2: Traditional broadcast (still supported)
await mesh.enableStreamingForAllPeers();
await mesh.startMedia({
video: true,
audio: true
});
// Option 3: Use new selective method for broadcast
await mesh.startSelectiveStream(mesh.getConnectedPeerIds(), options);
```
## Best Practices
### 1. Check Connected Peers
Always verify peers are connected before starting selective streaming.
```javascript
const connectedPeers = mesh.getConnectedPeerIds();
const targetPeers = ['peer-1', 'peer-2'];
// Filter to only connected targets
const validTargets = targetPeers.filter(id => connectedPeers.includes(id));
if (validTargets.length > 0) {
await mesh.startSelectiveStream(validTargets, options);
} else {
console.log('No valid targets for streaming');
}
```
### 2. Monitor Streaming Status
Use status methods to keep UI updated.
```javascript
function updateStreamingUI() {
const isStreaming = mesh.isStreamingToAll();
const streamingPeers = mesh.getStreamingPeers();
const blockedPeers = mesh.getBlockedStreamingPeers();
document.getElementById('streaming-mode').textContent =
isStreaming ? 'Broadcast' : 'Selective';
document.getElementById('active-peers').textContent =
streamingPeers.length;
document.getElementById('blocked-peers').textContent =
blockedPeers.length;
}
// Update on any streaming event
mesh.addEventListener('selectiveStreamStarted', updateStreamingUI);
mesh.addEventListener('streamingBlockedToPeers', updateStreamingUI);
mesh.addEventListener('streamingAllowedToPeers', updateStreamingUI);
```
### 3. Handle Errors Gracefully
Always wrap streaming operations in try-catch blocks.
```javascript
async function startConferenceCall(participants) {
try {
await mesh.startSelectiveStream(participants, {
video: true,
audio: true
});
console.log('Conference started successfully');
} catch (error) {
console.error('Failed to start conference:', error);
// Fallback to broadcast mode
try {
await mesh.enableStreamingForAllPeers();
await mesh.startMedia({ video: true, audio: true });
console.log('Fallback to broadcast mode');
} catch (fallbackError) {
console.error('All streaming methods failed:', fallbackError);
}
}
}
```
### 4. Cleanup on Disconnect
Stop streaming when peers disconnect.
```javascript
mesh.addEventListener('peerDisconnected', async (data) => {
const disconnectedPeer = data.peerId;
const streamingPeers = mesh.getStreamingPeers();
// If we were streaming only to the disconnected peer
if (streamingPeers.length === 1 && streamingPeers[0] === disconnectedPeer) {
await mesh.stopSelectiveStream(false);
console.log('Stopped streaming - target peer disconnected');
}
});
```
## Demo and Examples
Check out the complete demo at `examples/selective-streaming-demo.html` for a working implementation with UI controls.
### Running the Demo
1. Start a signaling server:
```bash
npm run server
# or directly:
# node server/start.js
```
2. Open the demo in multiple browser tabs:
```bash
# Open examples/selective-streaming-demo.html in 2+ browser tabs
```
3. Connect all tabs to the signaling server
4. Experiment with different streaming patterns using the UI controls
## Backward Compatibility
All existing PeerPigeon applications continue to work unchanged. The traditional `startMedia()` method still broadcasts to all connected peers by default. Selective streaming is purely additive functionality.
## Use Cases
- **Video Conferences**: Each participant can choose who they stream to
- **Webinars**: Host streams to all, participants can selectively unmute to specific people
- **Private Calls**: 1:1 streaming for confidential conversations
- **Breakout Rooms**: Dynamic group formation with selective streaming
- **Gaming**: Players can stream to teammates but not opponents
- **Education**: Teachers can create focused groups for different lesson components
- **Moderation**: Ability to block disruptive participants while maintaining streams to others
## Performance Considerations
- Selective streaming can reduce bandwidth usage by limiting stream targets
- Each peer connection requires separate encoding, so many targets still use significant resources
- The mesh forwarding system automatically handles indirect peer connections
- Monitor connection quality and adjust streaming targets accordingly
## Troubleshooting
### Common Issues
1. **"No peers selected" error**: Ensure you've selected peers in the UI or passed valid peer IDs
2. **Streaming not working**: Check that crypto keys are exchanged and data channels are ready
3. **Peers not receiving streams**: Verify the target peers have called `allowRemoteStreamEmission()`
4. **Performance issues**: Reduce number of streaming targets or lower video quality
### Debug Information
```javascript
// Check streaming status
console.log('Streaming Status:', mesh.getStreamingStatus());
console.log('Connected Peers:', mesh.getConnectedPeerIds());
console.log('Streaming To:', mesh.getStreamingPeers());
console.log('Blocked Peers:', mesh.getBlockedStreamingPeers());
// Monitor events
mesh.addEventListener('selectiveStreamStarted', console.log);
mesh.addEventListener('streamingBlockedToPeers', console.log);
mesh.addEventListener('streamingAllowedToPeers', console.log);
```