音频可以在Chrome中播放,但不能在Safari中播放 [英] Audio plays in Chrome but not Safari

查看:515
本文介绍了音频可以在Chrome中播放,但不能在Safari中播放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个angular 5应用程序,在其中设置了按钮的click处理程序以下载音频文件并播放它.我正在使用此代码执行此操作:

I've got an angular 5 application where I've set the click handler of a button to download an audio file and play it. I'm using this code to do so:

onPreviewPressed(media: Media): void {
    const url = ".....";

    this.httpClient.get(url, {responseType: 'blob'}).subscribe(x => {
        const fileReader = new FileReader();

        fileReader.onloadend = () => {
            const context = new ((<any>window).AudioContext || (<any>window).webkitAudioContext)();
            const source = context.createBufferSource();

            context.decodeAudioData(fileReader.result, buffer => {
                source.buffer = buffer;
                source.connect(context.destination);
                source.start(0);
            }, y => {
                console.info("Error: " + y);
            });
        };

        fileReader.readAsArrayBuffer(x);
    });
}

如果我进入Chrome页面并按按钮,音频将立即开始播放.如果我在Safari中执行此操作,则不会发生任何事情.我知道Safari可以锁定所有内容,但这是对按钮单击的响应,而不是自动播放.

If I go to the page in Chrome and press the button the audio starts right up. If I do it in Safari nothing happens. I know Safari locked things down but this is in response to a button click, it's not an auto-play.

音频通过PHP脚本从服务器发送回,并在需要时发送这样的标头:

The audio is sent back from the server via a PHP script, and it's sending headers like this, in case it matters:

header("Content-Type: audio/mpeg");
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($_GET['file']));
header('Cache-Control: no-cache');

推荐答案

不,它不是"响应按钮单击".
为响应此click事件,您正在启动一个异步任务.到您呼叫source.start(0)时,您的事件已经很久很久了(或者至少不再是受信任的用户手势".因此,他们确实会阻止此呼叫.

No, it is not "in response to a button click".
In response to this click event, you are starting an asynchronous task. By the time you call source.start(0), your event is long dead (or at least not anymore an "trusted user gesture". So they will indeed block this call.

要避免这种情况,您可以简单地将上下文标记为允许且保持沉默.然后,当数据可用时,您将可以不受限制地启动它:

To circumvent this, you could simply mark your context as allowed with silence. Then, when the data will be available, you'll be able to start it with no restriction:

function markContextAsAllowed(context) {
  const gain = context.createGain();
  gain.gain.value = 0; // silence
  const osc = context.createOscillator();
  osc.connect(gain);
  gain.connect(context.destination);
  osc.onended = e => gain.disconnect();
  osc.start(0);
  osc.stop(0.01);
}


onPreviewPressed(media: Media): void {
  const url = ".....";
  // declare in the event handler
  const context = new(window.AudioContext || window.webkitAudioContext)();
  const source = context.createBufferSource();
  // allow context synchronously
  markContextAsAllowed(context);


  this.httpClient.get(url, {
    responseType: 'blob'
  }).subscribe(x => {
    const fileReader = new FileReader();

    fileReader.onloadend = () => {
      context.decodeAudioData(fileReader.result, buffer => {
        source.buffer = buffer;
        source.connect(context.destination);
        source.start(0);
      }, y => {
        console.info("Error: " + y);
      });
    };

    fileReader.readAsArrayBuffer(x);
  });
}

因为Safari不喜欢过度保护的StackSnippets®

此外,我的角度知识非常有限,但是如果httpClient.get支持{responseType: 'arraybuffer'}选项,则可以摆脱此FileReader的使用,避免用相同的数据填充两次内存.

Also, my angular knowledge is very limited, but if httpClient.get does support {responseType: 'arraybuffer'} option, you could get rid of this FileReader and avoid populating twice the memory with the same data.

最后,如果要多次播放此音频,请考虑对其进行预取和预解码,这样就可以避免整个异步混乱.

Finally, if you are going to play this audio more than once, consider prefetching and pre-decoding it, you'll then be able to avoid the whole asynchronous mess.

这篇关于音频可以在Chrome中播放,但不能在Safari中播放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆