swap-tracks

Example of how to have Pion swap incoming tracks on a single outgoing track

HTML

Browser base64 Session Description<br />
<textarea id="localSessionDescription" readonly="true"></textarea> <br />

Golang base64 Session Description<br />
<textarea id="remoteSessionDescription"></textarea> <br/>
<button onclick="window.startSession()"> Start Session </button><br />

<br />

<div style="display: flex">
  <div>
    Browser stream 1<br />
    <canvas id="canvasOne" height="200" width="200"></canvas>
  </div>
  <div>
    Browser stream 2<br />
    <canvas id="canvasTwo" height="200" width="200"></canvas>
  </div>
  <div>
    Browser stream 3<br />
    <canvas id="canvasThree" height="200" width="200"></canvas>
  </div>
</div>

Video from server<br />
<video id="serverVideo" width="200" height="200" autoplay muted></video> <br />

CSS

textarea {
    width: 500px;
    min-height: 75px;
}

JavaScript

// Create peer conn
const pc = new RTCPeerConnection({
  iceServers: [
    {
      urls: 'stun:stun.l.google.com:19302'
    }
  ]
})

pc.oniceconnectionstatechange = e => {
  console.debug('connection state change', pc.iceConnectionState)
}
pc.onicecandidate = event => {
  if (event.candidate === null) {
    document.getElementById('localSessionDescription').value = btoa(JSON.stringify(pc.localDescription))
  }
}

pc.onnegotiationneeded = e =>
  pc.createOffer().then(d => pc.setLocalDescription(d)).catch(console.error)

pc.ontrack = event => {
  console.log('Got track event', event)
  document.getElementById('serverVideo').srcObject = new MediaStream([event.track])
}

// Capture canvas streams and add to peer conn
const streams = [
  document.getElementById('canvasOne').captureStream(),
  document.getElementById('canvasTwo').captureStream(),
  document.getElementById('canvasThree').captureStream()
]
streams.forEach(stream => stream.getVideoTracks().forEach(track => pc.addTrack(track, stream)))

// Start circles
requestAnimationFrame(() => drawCircle(document.getElementById('canvasOne').getContext('2d'), '#006699', 0))
requestAnimationFrame(() => drawCircle(document.getElementById('canvasTwo').getContext('2d'), '#cf635f', 0))
requestAnimationFrame(() => drawCircle(document.getElementById('canvasThree').getContext('2d'), '#46c240', 0))

function drawCircle(ctx, color, angle) {
  // Background
  ctx.clearRect(0, 0, 200, 200)
  ctx.fillStyle = '#eeeeee'
  ctx.fillRect(0, 0, 200, 200)
  // Draw and fill in circle
  ctx.beginPath()
  const radius = 25 + 50 * Math.abs(Math.cos(angle))
  ctx.arc(100, 100, radius, 0, Math.PI * 2, false)
  ctx.closePath()
  ctx.fillStyle = color
  ctx.fill()
  // Call again
  requestAnimationFrame(() => drawCircle(ctx, color, angle + (Math.PI / 64)))
}

window.startSession = () => {
  const sd = document.getElementById('remoteSessionDescription').value
  if (sd === '') {
    return alert('Session Description must not be empty')
  }

  try...