在Safari中无法使用Chrome录制的音频Blob文件

4
我正在尝试在Safari中播放音频Blob,但它只播放了一小部分时间,我听不到任何声音。媒体元素会在播放的极短时间内触发“暂停”事件(例如:0.038秒)。
该Blob是在Chrome中录制的,在Chrome和Firefox中可以正常播放。
此外,Safari报告的媒体持续时间要比实际时长短得多。例如,某个录音的实际长度为7.739秒,Chrome能够正确识别持续时间,但Safari显示持续时间为1.584。另一个录音的实际长度为9.96,但Safari报告持续时间为6.552。
我尝试确保这不是Safari防止非用户启动的播放的问题。因此,播放是通过点击开始的。我还尝试了不同的mime类型。mpeg。带有h264和vp8编解码器的webm。
我确保下载的Blob在Safari中的大小与Chrome中的大小相同。
我查看了许多类似的帖子,包括由@lastmjs提供答案的帖子(在Safari中通过Blob URL加载音频失败),其中提供了演示文稿。演示文稿有效,并且我正在做更多或更少与演示文稿中所示的相同的事情。我怀疑问题在录音端。
录音器:
self.mediaRecorder = new MediaRecorder(stream,{'audio' : {'sampleRate' : 22000}});

  ...assemble the chunks...

self.audioBlob = new Blob(self.audioChunks, {type: 'audio/webm; codecs=vp8'});

  ...upload the blob to cloud (S3)...

玩家:

  ...in the success handler that downloads blob...

self.audioBlob =  new Blob([data],{type: 'audio/webm'});

  ...I later prepare the element for playback...

let audioUrl = window.URL.createObjectURL(self.audioBlob);
let audioElement = document.createElement('audio');
let sourceElement = document.createElement('source');

audioElement.muted = true;

audioElement.appendChild(sourceElement);

sourceElement.src = audioUrl;
sourceElement.type = 'audio/webm';

document.body.appendChild(audioElement);
audioElement.load()

   ... when the user taps on a button...

self.audioElement.muted = false;
let playPromise = self.audioElement.play();

playPromise.then(()=>{
    console.log("playing should have started: " + self.audioElement.muted + " - " + self.audioElement.paused);
  });

  ...shortly after this - the paused event handler gets fired. 

没有错误消息。我在Mac和iOS上的Safari上尝试了这个。没有错误。我还监听媒体元素上的错误事件,但什么也没发生。它只是不能播放很长时间。我显然缺少了一些东西。再次在Chrome中捕获和回放效果非常好。并且Firefox中的回放效果也很好。但是Safari中的回放就无法工作。我应该尝试什么?


webm在Safari中不受支持。您可以尝试在浏览器控制台中运行以下命令来测试浏览器支持的MimeType。MediaRecorder.isTypeSupported('audio/webm;codecs=opus')我也遇到了类似的问题,如果您找到任何解决方案,请告诉我。如果在此之前我能够解决它,我会更新这个线程。 - Shubham Gautam
5个回答

1
将blob更改为new Blob([data], {type: 'audio/mpeg'})对我有效。
'audio/wav'和'audio/webm'不起作用。我不知道为什么,但对于Safari 15.4只有'audio/mpeg'起作用。如果没有这个设置,您可以播放/暂停,但没有声音。

1

你可以尝试将Blob格式转换为WAV格式,以便与Safari兼容。可以使用库或audioContext API来实现。以下是我在Angular应用程序中将Blob转换为WAV的方法:

  1. 添加一个名为convertBlobToWav的函数,该函数接受来自mediarecorder api的录制blob并将其转换为WAV格式。

          async convertBlobToWav(blob: Blob): Promise<Blob> {
          const arrayBuffer = await new Promise<ArrayBuffer>((resolve, reject) => {
          const fileReader = new FileReader();
          fileReader.onload = () => resolve(fileReader.result as ArrayBuffer);
          fileReader.onerror = reject;
          fileReader.readAsArrayBuffer(blob);
          });
    
          const audioContext = new AudioContext();
          const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
          const wavBuffer = this.audioBufferToWav(audioBuffer);
    
          return new Blob([wavBuffer], { type: 'audio/wav' });
          }
    
  2. 为解码的blob音频数据中的audioBuffer添加audioBufferToWav函数:

       audioBufferToWav(buffer: AudioBuffer): ArrayBuffer {
       const numChannels = buffer.numberOfChannels;
       const sampleRate = buffer.sampleRate;
       const numFrames = buffer.length;
    
       const bytesPerSample = 2;
       const blockAlign = numChannels * bytesPerSample;
       const byteRate = sampleRate * blockAlign;
    
       const wavBuffer = new ArrayBuffer(44 + numFrames * blockAlign);
       const dataView = new DataView(wavBuffer);
    
    
       dataView.setUint32(0, 0x52494646);
       dataView.setUint32(4, 36 + numFrames * blockAlign, true);
       dataView.setUint32(8, 0x57415645);
       dataView.setUint32(12, 0x666d7420);
       dataView.setUint32(16, 16, true);
       dataView.setUint16(20, 1, true);
       dataView.setUint16(22, numChannels, true);
       dataView.setUint32(24, sampleRate, true);
       dataView.setUint32(28, byteRate, true);
       dataView.setUint16(32, blockAlign, true);
       dataView.setUint16(34, 8 * bytesPerSample, true);
       dataView.setUint32(36, 0x64617461);
       dataView.setUint32(40, numFrames * blockAlign, true);
    
    
       const channelData = new Array(numChannels);
       for (let i = 0; i < numChannels; i++) {
       channelData[i] = buffer.getChannelData(i);
       }
    
       let offset = 44;
       for (let i = 0; i < numFrames; i++) {
       for (let j = 0; j < numChannels; j++) {
       const sample = Math.max(-1, Math.min(1, channelData[j][i]));
       const int16Value = sample < 0 ? sample * 0x8000 : sample * 0x7fff;
       dataView.setInt16(offset, int16Value, true);
       offset += bytesPerSample;
       }
       }
    
       return wavBuffer;
       }
    
  3. 最后将异步函数调用到一个新变量中:

       this.newBlob= await convertBlobToWav(this.recordedBlob)
    

0

对于所有遇到相同问题的人,请尝试将“audio/webm”更改为“audio/wav”


1
我已经有一段时间没有在这上面工作了,但我很确定wav不起作用。wav通常是未压缩的,对于这种情况来说是行不通的。虽然理论上可以使用wav来存储压缩音频...但我不知道任何浏览器真正支持这种方式。 - Pineapple Joe

0

0
截至2023年6月17日,此功能在Safari/iOS(iPadOS 16.3.1)上可用。
关键部分是
    let audio = document.createElement("audio");
    let blob = new Blob(data, { type: "audio/mpeg" });
    audio.src = window.URL.createObjectURL(blob);
    res.appendChild(audio);

一个完整的例子在这里。

https://dk-minimal-mediarecorder.glitch.me/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接