WebRTC 远端流调节音量问题记录

3,519 阅读1分钟

场景

将 PeerConnection 的 track 事件监听到的音轨进行本地播放,并调节其音量。

原思路

使用 AudioContext 处理音频源。

GainNode

以下是部分实现代码

class AudioHelper {
  ...
    
  get audioTrack(): MediaStreamTrack | undefined {
    this.destAudioNode.stream.getAudioTracks()[0];
  }

  constructor(track: MediaStreamTrack) {
    this.mediaStream = new MediaStream();
    this.mediaStream.addTrack(track);

    this.audioctx = new AudioContext();
    this.srcAudioNode = this.audioctx.createMediaStreamSource(this.mediaStream);
    this.gainNode = this.audioctx.createGain(); // 用来控制音量
    this.destAudioNode = this.audioctx.createMediaStreamDestination();
    
    this.srcAudioNode.connect(this.gainNode);
    this.gainNode.connect(this.destAudioNode);
  }
    
  setVolume(volume: number) {
    this.gainNode.gain.value = volume;
  }
}

需要调节音量时,直接调用 AudioHelper 中的 setVolume 即可。

存在问题

利用 AudioHelper 操作本地流,这种方法没有一点问题,示例代码:

const mediaStream: MediaStream = await navigator.mediaDevices.getUserMedia(constraints);
mediaStream.getTracks().forEach((track: MediaStreamTrack) => {
  if (track.kind === 'audio') {
    this.audioHelper = new AudioHelper(track);
    if (this.audioHelper.audioTrack) {
      this.peerConnection.addTrack(this.audioHelper.audioTrack);
    }
    ...
  }
});

问题出在操作远端流上,示例代码:

this.peerConnection.on('track', (ev: RTCTrackEvent) => {
  if (ev.track && 'audio' === ev.track.kind) {
    this.audioHelper = new AudioHelper(ev.track);
    if (this.audioHelper.audioTrack) {
      this.mediaStream.addTrack(this.audioHelper.audioTrack);
    }
  }
  ...
});

上面代码看上去没毛病,但实际测试会发现播放时,听不到远端流的声音。于是产生了如下代码:

this.peerConnection.addEventListener('track', function (ev: RTCTrackEvent) {
  if (ev.track && 'audio' === ev.track.kind) {
    new Audio().srcObject = ev.streams[0]; // hack - 为远端流指定一个输出源,不然会无声音
    self.audioHelper = new AudioHelper(ev.track);
    if (self.audioHelper.audioTrack) {
      self.mediaStream.addTrack(self.audioHelper.audioTrack);
    }
  }
  ...
});

嗯,这样处理后既能听到远端流的声音,也能调节远端流的音量,看上去很完美。but ... 经反复测试发现另外的一个大问题 -- 回音问题,且没有找到解决方案,后来不得不使用感觉有点 low 但更简单直接的操作 Audio/Video 元素的方式来调节音量。

element.volume = volume;

找解决方案时,找到一篇类似问题的文章 - Changing Volume on Remote WebRTC Streams in Chrome,也是一种思路。