CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-simple-peer

Simple one-to-one WebRTC video/voice and data channels

Pending
Overview
Eval results
Files

media.mddocs/

Media Streams

Add, remove, and manage audio/video streams with support for track-level operations, dynamic stream modification, and advanced WebRTC transceiver management.

Capabilities

Stream Management

Add and remove complete MediaStreams from the peer connection.

/**
 * Add a MediaStream to the connection
 * @param stream - MediaStream to add (from getUserMedia, etc.)
 */
peer.addStream(stream: MediaStream): void;

/**
 * Remove a MediaStream from the connection
 * @param stream - MediaStream to remove
 */
peer.removeStream(stream: MediaStream): void;

Usage Examples:

const Peer = require('simple-peer');

// Get user media
navigator.mediaDevices.getUserMedia({ 
  video: true, 
  audio: true 
}).then(stream => {
  // Create peer with initial stream
  const peer = new Peer({ 
    initiator: true,
    stream: stream  // Constructor option
  });
  
  // Or add stream after creation
  const peer2 = new Peer();
  peer2.addStream(stream);
  
  // Remove stream later
  setTimeout(() => {
    peer.removeStream(stream);
  }, 10000);
});

// Multiple streams
Promise.all([
  navigator.mediaDevices.getUserMedia({ video: true }),
  navigator.mediaDevices.getDisplayMedia({ video: true })
]).then(([cameraStream, screenStream]) => {
  const peer = new Peer({ 
    initiator: true,
    streams: [cameraStream, screenStream]  // Constructor option
  });
  
  // Or add individually
  // peer.addStream(cameraStream);
  // peer.addStream(screenStream);
});

Track Management

Manage individual MediaStreamTracks for fine-grained control over media.

/**
 * Add a MediaStreamTrack to the connection
 * @param track - MediaStreamTrack to add
 * @param stream - MediaStream to attach the track to
 */
peer.addTrack(track: MediaStreamTrack, stream: MediaStream): void;

/**
 * Remove a MediaStreamTrack from the connection
 * @param track - MediaStreamTrack to remove
 * @param stream - MediaStream the track was attached to
 */
peer.removeTrack(track: MediaStreamTrack, stream: MediaStream): void;

/**
 * Replace a MediaStreamTrack with another track
 * @param oldTrack - Track to replace
 * @param newTrack - New track to use
 * @param stream - MediaStream the old track was attached to
 */
peer.replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack, stream: MediaStream): void;

Usage Examples:

navigator.mediaDevices.getUserMedia({ 
  video: true, 
  audio: true 
}).then(stream => {
  const peer = new Peer({ initiator: true });
  const videoTrack = stream.getVideoTracks()[0];
  const audioTrack = stream.getAudioTracks()[0];
  
  // Add individual tracks
  peer.addTrack(videoTrack, stream);
  peer.addTrack(audioTrack, stream);
  
  // Replace video track (switch camera)
  navigator.mediaDevices.getUserMedia({ 
    video: { facingMode: 'environment' } 
  }).then(newStream => {
    const newVideoTrack = newStream.getVideoTracks()[0];
    peer.replaceTrack(videoTrack, newVideoTrack, stream);
  });
  
  // Remove audio track (mute)
  peer.removeTrack(audioTrack, stream);
});

Transceiver Management

Advanced WebRTC transceiver management for complex media scenarios.

/**
 * Add an RTCRtpTransceiver to the connection
 * @param kind - Media kind (commonly 'audio' or 'video', but accepts any string)
 * @param init - RTCRtpTransceiverInit options
 */
peer.addTransceiver(kind: string, init?: RTCRtpTransceiverInit): void;

interface RTCRtpTransceiverInit {
  direction?: 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive';
  streams?: MediaStream[];
  sendEncodings?: RTCRtpEncodingParameters[];
}

Usage Examples:

const peer = new Peer({ initiator: true });

// Add receive-only transceiver
peer.addTransceiver('video', { 
  direction: 'recvonly' 
});

// Add transceiver with specific streams
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
  peer.addTransceiver('audio', {
    direction: 'sendrecv',
    streams: [stream]
  });
});

// Add transceiver with encoding parameters
peer.addTransceiver('video', {
  direction: 'sendonly',
  sendEncodings: [
    { rid: 'high', maxBitrate: 1000000 },
    { rid: 'low', maxBitrate: 200000, scaleResolutionDownBy: 2 }
  ]
});

Media Events

Handle incoming media streams and tracks from the remote peer.

// Receive complete MediaStream
peer.on('stream', (stream: MediaStream) => void);

// Receive individual MediaStreamTrack
peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => void);

Usage Examples:

const peer = new Peer();

// Handle incoming streams
peer.on('stream', stream => {
  console.log('Received stream with', stream.getTracks().length, 'tracks');
  
  // Display video stream
  const video = document.querySelector('video');
  if ('srcObject' in video) {
    video.srcObject = stream;
  } else {
    video.src = URL.createObjectURL(stream); // Older browsers
  }
  video.play();
});

// Handle individual tracks
peer.on('track', (track, stream) => {
  console.log('Received track:', track.kind, track.id);
  
  if (track.kind === 'video') {
    const video = document.createElement('video');
    video.srcObject = new MediaStream([track]);
    video.play();
    document.body.appendChild(video);
  } else if (track.kind === 'audio') {
    const audio = document.createElement('audio');
    audio.srcObject = new MediaStream([track]);
    audio.play();
  }
});

Dynamic Media Control

Dynamically add, remove, and modify media streams during an active connection.

Usage Examples:

const peer1 = new Peer({ initiator: true });
const peer2 = new Peer();

// Setup signaling
peer1.on('signal', data => peer2.signal(data));
peer2.on('signal', data => peer1.signal(data));

// Start without media
peer1.on('connect', () => {
  console.log('Connected - adding media later');
});

// Add media after connection
function addCamera() {
  navigator.mediaDevices.getUserMedia({ 
    video: true, 
    audio: true 
  }).then(stream => {
    peer1.addStream(stream);
    return stream;
  }).then(stream => {
    // Store for later removal
    window.currentStream = stream;
  });
}

// Remove media
function removeCamera() {
  if (window.currentStream) {
    peer1.removeStream(window.currentStream);
    window.currentStream.getTracks().forEach(track => track.stop());
    window.currentStream = null;
  }
}

// Switch camera
function switchCamera() {
  if (window.currentStream) {
    const videoTrack = window.currentStream.getVideoTracks()[0];
    
    navigator.mediaDevices.getUserMedia({ 
      video: { facingMode: 'environment' } 
    }).then(newStream => {
      const newVideoTrack = newStream.getVideoTracks()[0];
      peer1.replaceTrack(videoTrack, newVideoTrack, window.currentStream);
      
      // Stop old track
      videoTrack.stop();
      
      // Update stream reference
      window.currentStream.removeTrack(videoTrack);
      window.currentStream.addTrack(newVideoTrack);
    });
  }
}

// Toggle audio
function toggleAudio(enabled) {
  if (window.currentStream) {
    const audioTrack = window.currentStream.getAudioTracks()[0];
    if (audioTrack) {
      audioTrack.enabled = enabled;
    }
  }
}

Screen Sharing

Handle screen sharing streams with proper setup and cleanup.

Usage Examples:

const peer = new Peer({ initiator: true });

async function startScreenShare() {
  try {
    // Get screen share stream
    const screenStream = await navigator.mediaDevices.getDisplayMedia({
      video: true,
      audio: true
    });
    
    // Add to peer connection
    peer.addStream(screenStream);
    
    // Handle screen share ending
    screenStream.getVideoTracks()[0].addEventListener('ended', () => {
      console.log('Screen sharing ended');
      peer.removeStream(screenStream);
      
      // Optionally switch back to camera
      return navigator.mediaDevices.getUserMedia({ video: true });
    });
    
    return screenStream;
  } catch (err) {
    console.error('Screen sharing failed:', err);
  }
}

// Replace camera with screen share
async function switchToScreenShare() {
  const cameraStream = window.currentCameraStream;
  const screenStream = await startScreenShare();
  
  if (cameraStream && screenStream) {
    const cameraVideoTrack = cameraStream.getVideoTracks()[0];
    const screenVideoTrack = screenStream.getVideoTracks()[0];
    
    // Replace camera with screen
    peer.replaceTrack(cameraVideoTrack, screenVideoTrack, cameraStream);
    
    // Stop camera
    cameraVideoTrack.stop();
  }
}

Error Handling

Handle media-related errors and track failures.

// Media-specific error codes
interface MediaError extends Error {
  code: 'ERR_ADD_TRANSCEIVER' | 'ERR_SENDER_REMOVED' | 'ERR_SENDER_ALREADY_ADDED' | 
        'ERR_TRACK_NOT_ADDED' | 'ERR_REMOVE_TRACK' | 'ERR_UNSUPPORTED_REPLACETRACK';
}

Usage Examples:

const peer = new Peer({ initiator: true });

peer.on('error', err => {
  switch (err.code) {
    case 'ERR_ADD_TRANSCEIVER':
      console.error('Failed to add transceiver:', err.message);
      break;
    case 'ERR_SENDER_REMOVED':
      console.error('Track sender was removed:', err.message);
      break;  
    case 'ERR_SENDER_ALREADY_ADDED':
      console.error('Track already added to stream:', err.message);
      break;
    case 'ERR_TRACK_NOT_ADDED':
      console.error('Cannot remove track - was never added:', err.message);
      break;
    case 'ERR_UNSUPPORTED_REPLACETRACK':
      console.error('replaceTrack not supported in this browser');
      break;
  }
});

// Safe track operations
function safeAddTrack(peer, track, stream) {
  try {
    peer.addTrack(track, stream);
    return true;
  } catch (err) {
    console.error('Failed to add track:', err.message);
    return false;
  }
}

function safeReplaceTrack(peer, oldTrack, newTrack, stream) {
  try {
    peer.replaceTrack(oldTrack, newTrack, stream);
    return true;
  } catch (err) {
    console.error('Failed to replace track:', err.message);
    // Fallback: remove old and add new
    try {
      peer.removeTrack(oldTrack, stream);
      peer.addTrack(newTrack, stream);
      return true;
    } catch (fallbackErr) {
      console.error('Fallback also failed:', fallbackErr.message);
      return false;
    }
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-simple-peer

docs

connection.md

data.md

index.md

info.md

media.md

tile.json