前端使用WebRTC———开启OPUS的DTX功能

35 阅读2分钟

什么是DTX功能

Opus编解码器中的DTX(Discontinuous Transmission,不连续传输)功能是一种优化技术,旨在通过动态调整数据传输以节省带宽和功耗。

工作原理

  1. 语音活动检测(VAD)

DTX通过VAD实时检测输入音频中是否有语音活动。当检测到静音或背景噪声时,停止发送有效音频数据包。

  1. 舒适噪声生成(CNG)

在静音期间,发送端会生成极低比特率的“舒适噪声”参数(而非传输完整音频包),接收端据此合成与环境匹配的背景噪声,避免用户因完全静音误以为通话中断。

核心优势

  1. 带宽节省

静音时段仅发送少量CNG数据(如每400毫秒一个包),显著降低平均带宽占用(尤其在语音通话中,静音约占50%时间)。

  1. 降低功耗

减少数据包传输和处理频率,延长移动设备电池续航。

  1. 无缝体验

CNG维持自然通话氛围,避免静音突兀感。

实现方式

在Answer中找到描述opus的行

a=fmtp:111 minptime=10;useinbandfec=1

在后面追加内容: usedtx=1

a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1

然后将修改后的answer送到浏览器中

效果

代码

可以直接从gittee获取源代码,也可以直接使用下面的代码,代码的测试环境要在设备连接wifi下才能正常建立连接。

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>webrtc连接</title>
</head>

<body>
  <video id="localStream" style="width: 320px; height: 240px;" autoplay muted></video>
  <video id="remoteStream" style="width: 320px; height: 240px;" autoplay controls > </video>
  <button id="deviceBtn">打开设备</button>
  <button id="startBtn">建立连接</button>
  
  <script>
    let localStream = null;
    let localVideo = document.getElementById("localStream");
    let remoteVideo = document.getElementById("remoteStream")
    let deviceBtn = document.getElementById("deviceBtn");
    let startBtn = document.getElementById("startBtn");

    let pc_pub = new RTCPeerConnection();
    let pc_sub = new RTCPeerConnection();

    deviceBtn.addEventListener("click", () => {
      navigator.mediaDevices.getUserMedia({audio: true }).then((mediastream) => {
        localStream = mediastream;
        localVideo.srcObject = mediastream;
      })
    })

    function stopPlay () {
      if (remoteVideo.srcObject) {
        remoteVideo.srcObject = null;
      }
    }

    startBtn.addEventListener("click", () => {
      pc_pub.addTrack(localStream.getAudioTracks()[0], localStream);
      pc_pub.createOffer().then((offer) => {
        pc_pub.setLocalDescription(offer).then(() => {
          pc_sub.setRemoteDescription(offer).then(() => {
            pc_sub.createAnswer().then((answer) => {
              pc_sub.setLocalDescription(answer).then(() => {
                answer.sdp = answer.sdp.replace(/(a=fmtp:\d+.*?\buseinbandfec=1\b)(?![^;\n]*\busedtx=1\b)/gi,'$1;usedtx=1');
                pc_pub.setRemoteDescription(answer).then(() => {
                })
              })
            })
          })
        })
      })
    })

    pc_pub.addEventListener('icecandidate', (event) => {
      if (event.candidate) {
        pc_sub.addIceCandidate(event.candidate);
      }
    })

    pc_sub.addEventListener('icecandidate', (event) => {
      if (event.candidate) {
        pc_pub.addIceCandidate(event.candidate);
      }
    })

    pc_sub.addEventListener('track', (event) => {
      remoteVideo.srcObject = event.streams[0];
    })
  </script>
</body>

其他

如果你也是专注前端多媒体或者对前端多媒体感兴趣,可以关注