更改WebRTC流中的播放延迟

End*_*ess 8 javascript google-chrome webrtc web-mediarecorder

我正在尝试将实时MediaStream(最终从摄像机)从peerA投射到peerB,并且我希望peerB实时接收实时流,然后以增加的延迟重播它。不幸的是,由于无法跳入直播,因此无法简单地暂停播放并继续播放。

因此,我发现可以使用MediaRecorder + SourceBuffer重新观看直播。记录流并将缓冲区附加到MSE(SourceBuffer),然后在5秒钟后播放。这在本地设备(流)上起作用。但是,当我尝试在接收方上使用Media Recorder时,MediaStream(from pc.onaddstream)看起来会获取一些数据,并且能够将缓冲区附加到sourceBuffer上。但是它不能重播。有时我只得到一帧。

const [pc1, pc2] = localPeerConnectionLoop()
const canvasStream = canvas.captureStream(200)

videoA.srcObject = canvasStream
videoA.play()

// Note: using two MediaRecorder at the same time seem problematic
// But this one works
// stream2mediaSorce(canvasStream, videoB)
// setTimeout(videoB.play.bind(videoB), 5000)

pc1.addTransceiver(canvasStream.getTracks()[0], {
  streams: [ canvasStream ]
})

pc2.onaddstream = (evt) => {
  videoC.srcObject = evt.stream
  videoC.play()

  // Note: using two MediaRecorder at the same time seem problematic
  // THIS DOSE NOT WORK
  stream2mediaSorce(evt.stream, videoD)
  setTimeout(() => videoD.play(), 2000)
}

/**
 * Turn a MediaStream into a SourceBuffer
 * 
 * @param  {MediaStream}      stream   Live Stream to record
 * @param  {HTMLVideoElement} videoElm Video element to play the recorded video in
 * @return {undefined}
 */
function stream2mediaSorce (stream, videoElm) {
  const RECORDER_MIME_TYPE = 'video/webm;codecs=vp9'
  const recorder = new MediaRecorder(stream, { mimeType : RECORDER_MIME_TYPE })

  const mediaSource = new MediaSource()
  videoElm.src = URL.createObjectURL(mediaSource)
  mediaSource.onsourceopen = (e) => {
    sourceBuffer = mediaSource.addSourceBuffer(RECORDER_MIME_TYPE);

    const fr = new FileReader()
    fr.onerror = console.log
    fr.onload = ({ target }) => {
      console.log(target.result)
      sourceBuffer.appendBuffer(target.result)
    }
    recorder.ondataavailable = ({ data }) => {
      console.log(data)
      fr.readAsArrayBuffer(data)
    }
    setInterval(recorder.requestData.bind(recorder), 1000)
  }

  console.log('Recorder created')
  recorder.start() 
}
Run Code Online (Sandbox Code Playgroud)

您知道为什么它不能播放视频吗?

我用所有必要的代码创建了一个小提琴,以进行尝试,javascript选项卡与上面的代码相同,(html基本上无关紧要,不需要更改)

有些人试图减少延迟,但我实际上想将其增加到〜10秒,以重新观看您在高尔夫挥杆中做错的事情或其他事情,如果可能的话,请完全避免使用MediaRecorder

编辑: 我在某些RTC扩展名中找到了称为“播放延迟”的内容

允许发件人控制从捕获到渲染时间的最小和最大延迟

我该如何使用?对我有帮助吗?

End*_*ess 6

更新,有一个新功能可以启用此功能,称为playoutDelayHint.

我们希望为 JavaScript 应用程序提供一种方法来设置他们想要渲染音频或视频数据的速度的首选项。对于专注于实时体验的应用程序来说,尽可能快可能是有益的。对于其他人来说,额外的数据缓冲可能会在出现网络问题时提供更顺畅的体验。

参考文献:
https://discourse.wicg.io/t/hint-attribute-in-webrtc-to-influence-underlying-audio-video-buffering/4038

https://bugs.chromium.org/p/webrtc/issues /详细信息?id=10287

演示:https: //jsfiddle.net/rvekxns5/ doe 我只能在浏览器中设置最长 10 秒,但更多的是由 UA 供应商利用可用资源尽最大努力

import('https://jimmy.warting.se/packages/dummycontent/canvas-clock.js')
.then(({AnalogClock}) => {
  const {canvas} = new AnalogClock(100)
  document.querySelector('canvas').replaceWith(canvas)
  
  const [pc1, pc2] = localPeerConnectionLoop()
  const canvasStream = canvas.captureStream(200)

  videoA.srcObject = canvasStream
  videoA.play()

  pc1.addTransceiver(canvasStream.getTracks()[0], {
    streams: [ canvasStream ]
  })

  pc2.onaddstream = (evt) => {
    videoC.srcObject = evt.stream
    videoC.play()
  }

  $dur.onchange = () => {
    pc2.getReceivers()[0].playoutDelayHint = $dur.valueAsNumber
  }
})
Run Code Online (Sandbox Code Playgroud)
<!-- all the irrelevant part, that you don't need to know anything about -->
<h3 style="border-bottom: 1px solid">Original canvas</h3>
<canvas id="canvas" width="100" height="100"></canvas>
<script>
function localPeerConnectionLoop(cfg = {sdpSemantics: 'unified-plan'}) {
  const setD = (d, a, b) => Promise.all([a.setLocalDescription(d), b.setRemoteDescription(d)]);
  return [0, 1].map(() => new RTCPeerConnection(cfg)).map((pc, i, pcs) => Object.assign(pc, {
    onicecandidate: e => e.candidate && pcs[i ^ 1].addIceCandidate(e.candidate),
    onnegotiationneeded: async e => {
      try {
        await setD(await pc.createOffer(), pc, pcs[i ^ 1]);
        await setD(await pcs[i ^ 1].createAnswer(), pcs[i ^ 1], pc);
      } catch (e) {
        console.log(e);
      }
    }
  }));
}
</script>
<h3 style="border-bottom: 1px solid">Local peer (PC1)</h3>
<video id="videoA" muted width="100" height="100"></video>

<h3 style="border-bottom: 1px solid">Remote peer (PC2)</h3>
<video id="videoC" muted width="100" height="100"></video>
<label> Change playoutDelayHint
<input type="number" value="1" id="$dur">
</label>
Run Code Online (Sandbox Code Playgroud)