You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter与Angular通过WebRTC传输音频流无声音问题

解决方案:Flutter与Angular WebRTC音频无声音问题

一、Flutter端排查与修复

1. 验证本地媒体流采集状态

先确认Flutter端是否正常获取麦克风权限并采集到音频,在_getUserMedia中添加状态校验:

_getUserMedia() async {
  final Map<String, dynamic> mediaConstraints = {
    'audio': true,
    'video': false
  };
  try {
    MediaStream stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
    // 打印本地轨道状态,确认是否正常采集
    stream.getAudioTracks().forEach((track) {
      print('本地音频轨道: enabled=${track.enabled}, muted=${track.muted}');
    });
    return stream;
  } catch (e) {
    print('获取媒体流失败: $e');
    rethrow;
  }
}

如果本地轨道muted为true,说明麦克风权限未授权或设备故障,需先解决本地采集问题。

2. 更新SDP约束格式

旧的mandatory约束已被废弃,改用标准的RTCOfferOptions:

// 替换原有的offerSdpConstraints
final offerOptions = RTCOfferOptions(
  offerToReceiveAudio: true,
  offerToReceiveVideo: false,
);
// 创建PeerConnection时使用新约束
_peerConnection = await createPeerConnection(configuration, offerOptions);

旧格式可能导致SDP协商异常,影响音频轨道传输。

3. 确保ICE候选完整传输

需将所有ICE候选(包括最后一个标识收集完成的null候选)发送给Angular端:

_peerConnection?.onIceCandidate = (e) {
  // 即使candidate为null也要发送,通知对方候选收集完成
  print(json.encode({
    'candidate': e.candidate?.toString(),
    'sdpMid': e.sdpMid.toString(),
    'sdpMlineIndex': e.sdpMLineIndex,
  }));
};

Angular端收到candidate为null时,无需调用addIceCandidate即可。

二、Angular端排查与修复

1. 处理浏览器自动播放限制

现代浏览器要求音频播放必须触发于用户交互(如点击按钮),修改音频元素与播放逻辑:

<!-- 移除autoplay,改为手动触发 -->
<div *ngIf="inCall">
  <audio id="remote_audio" #remote_audio controls></audio>
</div>

在用户操作(如开始通话按钮点击)后触发播放:

startCall() {
  this.inCall = true;
  setTimeout(() => {
    if (this.remoteAudio.srcObject) {
      this.remoteAudio.play().catch(err => {
        console.log('自动播放失败,请点击播放按钮:', err);
      });
    }
  }, 100);
}

2. 修正远程流绑定逻辑

优先使用轨道自身关联的流,避免event.streams[0]的潜在关联错误:

private handleTrackEvent = (event: RTCTrackEvent) => {
  console.log('收到远程音频轨道:', event.track);
  const remoteStream = event.track.stream;
  // 检查远程轨道状态
  console.log('远程轨道状态: enabled=' + event.track.enabled + ', muted=' + event.track.muted);
  
  this.remoteAudio.srcObject = remoteStream;
  // 需在用户交互后调用play
  this.remoteAudio.play().catch(err => console.log('播放失败:', err));
}

3. 检查媒体权限状态

初始化时验证浏览器麦克风权限:

checkMediaPermission() {
  navigator.permissions.query({ name: 'microphone' as PermissionName })
    .then(status => {
      console.log('麦克风权限状态:', status.state);
      if (status.state === 'denied') {
        alert('请授予浏览器麦克风访问权限');
      }
    });
}

三、通用排查步骤

  • 打开Chrome开发者工具Media面板(更多工具 > Media),查看是否有远程音频轨道数据传输,无轨道则说明协商或连接异常。
  • 检查SDP内容,确认Offer和Answer中存在m=audio的媒体描述行。
  • 在Flutter端调用addTrack后,打印_peerConnection?.getSenders(),确认音频发送者已存在。

内容的提问来源于stack exchange,提问作者ROB99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:15:35