ios iPhone 14无法使用MediaRecorder进行录制

dsekswqp  于 2022-12-15  发布在  iOS
关注(0)|答案(1)|浏览(845)

我们的网站录制音频并为用户播放。它已经在许多不同的设备上工作了多年,但在iPhone14上开始出现故障。我在https://nmp-recording-test.netlify.app/上创建了一个测试应用,这样我就可以看到发生了什么。它在所有设备上都能完美运行,但它只在iPhone14上第一次运行。它可以在其他iPhone上工作,也可以在使用Safari或任何其他浏览器的iPad和MacBook上工作。
如果这是你做的第一个音频,它看起来会记录下来。如果我在其他地方得到一个音频上下文,音频播放会工作,但然后记录不会。
我能看到的唯一症状是,当它不工作时,它不调用MediaRecorder.ondataavailable,但我认为这是因为它没有记录。
下面是我在测试站点上看到的模式:
1.点击“新记录”。(电平指示器移动,触发可用数据回调)
1.单击“听”我听到我刚才所做的
1.点击“新记录”。(无水平移动,无数据报告)
1.单击“听”,不播放任何内容。
但是如果我做了什么,比如打开和关闭节拍器,那么它也不会记录第一次。
“Oidogg.录音”是我最初做录音的方式,使用了过时的方法createMediaStreamSource()createScriptProcessor()/createJavaScriptNode()。我想也许iPhone终于摆脱了这一点,所以我创建了MediaRecorder版本。
我所做的,基本上,是(截断显示的重要部分):

const chunks = []
function onSuccess(stream: MediaStream) {
  mediaRecorder = new MediaRecorder(stream);
  mediaRecorder.ondataavailable = function (e) {
    chunks.push(e.data);
  }
  mediaRecorder.start(1000);
}
navigator.mediaDevices.getUserMedia({ audio: true }).then(onSuccess, onError);

有没有其他人看到iPhone 14处理录音的方式有什么不同?
有人对如何调试这个有什么建议吗?
如果你有一部iPhone 14,你会尝试我上面的测试程序,如果你得到同样的结果,让我知道吗?我们只有一部iPhone 14可以测试,也许那台设备有什么奇怪的地方。
如果它工作,你应该看到一些行,如data {"len":6784}出现每秒当你录制。
---编辑---
我按照Frank zeng的建议重新编写了代码,现在正在录制,但仍然不对,音量很低,看起来像是有一些漏音,恢复AudioContext时有一个很长的停顿。
新代码似乎在我可以访问的其他设备和浏览器中运行得很完美。

pexxcrt2

pexxcrt21#

我有和你一样的问题,我认为AudioContent.createScriptProcessor的API在Iphone14中是无效的,我用新的API About AudioWorkletNode来替换它.并且不要关闭流,因为iPhone 14的第二个录制会话太滞后,录制后记得销毁数据.经过测试,我已经解决了这个问题,这是我的代码,

// get stream
window.navigator.mediaDevices.getUserMedia(options).then(async (stream) => {
          // that.stream = stream
          that.context = new AudioContext()
          await that.context.resume()
          const rate = that.context.sampleRate || 44100
          that.mp3Encoder = new lamejs.Mp3Encoder(1, rate, 128)
          that.mediaSource = that.context.createMediaStreamSource(stream)
          // API开始逐步淘汰了,如果可用则继续用,如果不可用则采用worklet方案写入音频数据
          if (that.context.createScriptProcessor && typeof that.context.createScriptProcessor === 'function') {
            that.mediaProcessor = that.context.createScriptProcessor(0, 1, 1)
            that.mediaProcessor.onaudioprocess = event => {
              window.postMessage({ cmd: 'encode', buf: event.inputBuffer.getChannelData(0) }, '*')
              that._decode(event.inputBuffer.getChannelData(0))
            }
          } else { // 采用新方案
            that.mediaProcessor = await that.initWorklet()
          }
          resolve()
        })
// content of audioworklet function
async initWorklet() {
    try {
      /*音频流数据分析节点*/
      let audioWorkletNode;
       /*---------------加载AudioWorkletProcessor模块并将其添加到当前的Worklet----------------------------*/
       await this.context.audioWorklet.addModule('/get-voice-node.js');
       /*---------------AudioWorkletNode绑定加载后的AudioWorkletProcessor---------------------------------*/
       audioWorkletNode = new AudioWorkletNode(this.context, "get-voice-node");
      /*-------------AudioWorkletNode和AudioWorkletProcessor通信使用MessagePort--------------------------*/
      console.log('audioWorkletNode', audioWorkletNode)
      const messagePort = audioWorkletNode.port;
      messagePort.onmessage = (e) => {
        let channelData = e.data[0];
        window.postMessage({ cmd: 'encode', buf: channelData }, '*')
        this._decode(channelData)
      }
      return audioWorkletNode;
    } catch (e) {
      console.log(e)
    }
  }
// content of get-voice-node.js, Remember to put it in the static resource directory
class GetVoiceNode extends AudioWorkletProcessor {
  /*
  * options由new AudioWorkletNode()时传递
  * */
  constructor() {
      super()
  }

  /*
  * `inputList`和outputList`都是输入或输出的数组
  * 比较坑的是只有128个样本???如何设置
  * */
  process (inputList, outputList, parameters) {
    // console.log(inputList)
    if(inputList.length>0&&inputList[0].length>0){
      this.port.postMessage(inputList[0]);
    }
    return true //回来让系统知道我们仍处于活动状态并准备处理音频。
  }
}
registerProcessor('get-voice-node', GetVoiceNode)

销毁记录示例并释放内存,如果下次要使用它,最好创建新示例

this.recorder.stop()
   this.audioDurationTimer && window.clearInterval(this.audioDurationTimer)
   const audioBlob = this.recorder.getMp3Blob()
   // Destroy the recording instance and free the memory
   this.recorder = null

相关问题