记 iOS 申请设备权限与扬声器设备的坑

2023-02-23

今天测试又发现一个非常奇怪的问题,在 iOS 16 的设备上,如果 getUserMedia 申请设备权限后执行了清理设备的操作,会导致其他人说话的声音也没有了,除非手动开启一次麦克风才会“解除”这种状态。

function useFixSafariNoSound({ isPCMode }: FixSafariNoSoundProps) {
  useEffect(() => {
    let stream: MediaStream | undefined;

    if ((isPCMode && isIpad) || isSafari) {
      navigator.mediaDevices.getUserMedia({ audio: true }).then((res) => {
        stream = res;
      });
    }

    return () => {
      if (stream) {
        stream.getTracks().forEach((track) => {
          track.stop();
        });
      }
    };
  }, []);
}

目前写了这样一个 Hooks 处理,只要是 iPhone 和 iPad 环境上的 Safari 才会执行这个操作,缺点就是在没有打开麦克风的状态下系统也会提示正在使用麦克风。这段代码只能算是填补了设备授权后清除设备导致没有声音的问题,因为正常情况下没调用申请设备权限应该是不会没声音的。

还有另外一个问题,就是任意音频有概率会使用「听筒播放」而不是「扬声器外放」。测试反馈我前面这个代码应用之后在 iOS 16 上会使用扬声器,但 iOS 15 依旧会默认使用听筒,非常小声,就比较离谱。我测试后发现线上环境也是这样的,估计已经有一段时间了。

和其他项目组的同事确认了下,他们那的设计是“被动型”申请权限,而我这边是“主动型”。简单描述就是他那边需要点击按钮调用,而我们这边是组件挂载就开始轮询调用。

我把这个挂载后的调用注释了,再使用 iOS 15 的设备测试后,发现任意音频的确是默认外放的。也就是说系统选择使用听筒还是外放,是取决于调用 getUserMedia 之前是否已经有音频在外放。

没调用 getUserMedia 的时候有 Audio 对象播放 就是正常外放
调用过 getUserMedia 之后有 Audio 对象播放 就会是听筒

题外话,前阵子买的那几件 T 恤都退货了,花了 8 块邮费,花钱买教训。但糟心感也少了,还是考虑下那些稍贵些的正版吧。

东京不太热 一般
概览页 时间轴
奇趣音乐盒 技术源于 Kico Player
Emmm,这里是歌词君