为什么JavaScript在iPhone Safari上无法.play()音频文件?

73

我已经成功地制作了一个JavaScript网络应用程序,它可以像这样定期播放一些音频:

var SOUND_SUCCESS = new Audio('success.mp3');
SOUND_SUCCESS.play();

这在桌面浏览器上效果很好(在Edge和Chrome中测试过),但在iPhone的Safari上无法播放。

我在Stack Overflow上找到了一些几年前的答案,说除非你在全屏播放器中,否则无法从Safari播放音频。 这种情况还是吗?


我遇到了类似的问题,但声音的重复在我的代码中无法正常工作。 我在这里发布了一个问题,如果有人能帮助我:https://dev59.com/U3kPtIcB2Jgan1znsPW0 - Luca
11个回答

62

补充xingliang cai的回答,以下是一个我使用过并且可以工作的代码示例(经过iOS14的编辑,感谢@AndrewL!):

const soundEffect = new Audio();
soundEffect.autoplay = true;

// onClick of first interaction on page before I need the sounds
// (This is a tiny MP3 file that is silent and extremely short - retrieved from https://bigsoundbank.com and then modified)
soundEffect.src = "data:audio/mpeg;base64,SUQzBAAAAAABEVRYWFgAAAAtAAADY29tbWVudABCaWdTb3VuZEJhbmsuY29tIC8gTGFTb25vdGhlcXVlLm9yZwBURU5DAAAAHQAAA1N3aXRjaCBQbHVzIMKpIE5DSCBTb2Z0d2FyZQBUSVQyAAAABgAAAzIyMzUAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAAAAD/80DEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/zQsRbAAADSAAAAABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/zQMSkAAADSAAAAABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV";

// later on when you actually want to play a sound at any point without user interaction
soundEffect.src = 'path/to/file.mp3';

4
这在我使用的Safari版本14.0.3上已经无效了 :/ 返回经典的 Unhandled Promise Rejection: AbortError: The operation was aborted. - capitalg
1
我的问题是我想通过浏览器播放我录制的内容,但苹果公司尚未实现浏览器录制材料的播放 - 但显示的错误是相同的 - 请参见 https://bugs.webkit.org/show_bug.cgi?id=85851 和 https://github.com/muaz-khan/RecordRTC/issues/655。 - capitalg
2
很遗憾,这个答案在iOS 14上已经不再适用。 - Thomas Bui
1
谢谢,我已经更新了我的答案以符合你的解决方案,@AndrewL! - user2415116
1
一个更短的版本是 soundEffect.src = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA";。来自这里 - https://gist.github.com/novwhisky/8a1a0168b94f3b6abfaa - Angstrom
显示剩余6条评论

53

iOS禁用自动播放,并要求播放作为用户操作的一部分启动(例如,您可以在touchstart监听器中开始播放)。关于此问题有一些文档在苹果开发者文档中。IBM的开发者网站上也有这篇文章《克服iOS HTML5音频限制》,其中包含示例和更多细节。


1
我想补充一点,使用iframe也无法避免这个问题。我已经尝试过了。 - gesell
2
链接失效了。唉... - Kyle Baker
请参见:https://web.archive.org/web/20170215083556/http://www.ibm.com/developerworks/library/wa-ioshtml5/ - Rounin
我遇到了类似的问题,但声音的重复在我的代码中无法正常工作。 我在这里发布了一个问题,如果有人能帮助我:https://dev59.com/U3kPtIcB2Jgan1znsPW0 - Luca

23

默认情况下,移动设备上的IOS禁用自动播放声音。因此,要解决这个问题,可以在页面上某处放置启用/禁用开关按钮,并在用户单击按钮开关时使用音频元素("audioElement")播放一些声音。

之后,通过更改"src"属性并调用其"play()"方法,就可以使用同一"audioElement"播放未来的声音,而无需进一步的用户交互。


13
我发现这个答案的第二部分很有用——你可以改变 Audio(x).src 并调用 play(),但是你不能创建新的音频对象并调用 play() 而不需要用户交互。 - Alexander
6
太棒了!非常感谢您的调查和解决!在用户交互时创建一个 new Audio() 对象并存储它,然后稍后更改该对象的 .src 并调用 .play() 方法即可解决问题(在我的情况下使用 React 和 useRef)。干杯! - Greg Sadetsky

13
为了让@user2415116的解决方案在iOS 14上起作用,我做了以下操作:
const soundEffect = new Audio();
soundEffect.autoplay = true;

// onClick of first interaction on page before I need the sounds
// (This is a tiny MP3 file that is silent and extremely short - retrieved from https://bigsoundbank.com and then modified)
soundEffect.src = "data:audio/mpeg;base64,SUQzBAAAAAABEVRYWFgAAAAtAAADY29tbWVudABCaWdTb3VuZEJhbmsuY29tIC8gTGFTb25vdGhlcXVlLm9yZwBURU5DAAAAHQAAA1N3aXRjaCBQbHVzIMKpIE5DSCBTb2Z0d2FyZQBUSVQyAAAABgAAAzIyMzUAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAAAAD/80DEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/zQsRbAAADSAAAAABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/zQMSkAAADSAAAAABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV";

// later on when you actually want to play a sound at any point without user interaction
soundEffect.src = 'path/to/file.mp3';

3

我知道这个问题已经有了答案,但对于一些人可能会有所帮助。如果您在setTimeout函数中使用了.play触发器,则必须将时间保持在951以下。

setTimeout(function(){$('audio').play}, 999) 防止自动播放

setTimeout(function(){$('audio').play}, 950) 自动播放正常工作


为什么少于951毫秒? - Dermo909
这只是一个更安全的值。975 可能会起作用,但我没有测试过。我知道 999 可以防止视频/音频自动播放。我也不想测试 950-999 之间所有数字来找到边界。哈哈。 - Ron Ross
这看起来很傻,但我有过同样的经历。当我设置一个事件监听器(例如按钮点击/轻触)并在 +/- 950 毫秒内触发音频时,它运行得很好。每当延迟超过 996 毫秒时,音频就会被阻止,并显示错误消息:
NotAllowedError:请求在当前上下文中不被用户代理或平台允许,可能是因为用户拒绝了权限。
- romek

2

Safari在音频文件和视频声音之间会优先播放视频的声音!即使视频被设置为静音,但只要它有声音轨道,它仍然会阻塞音频。解决方法是从视频中去除声音轨道,而不是覆盖它。

希望这能帮助到大家!我因为这个问题浪费了好几天的工作时间。


2
我使用了这个:
useEffect(() => {
  window.addEventListener('touchstart', () => {
    document.getElementById('audio').muted = false
    document.getElementById('audio').play()
  })
})

当用户滚动时,声音播放。

最好也在 useEffect 的返回回调中移除事件监听器。 - Ali Mert Çakar

1

这是一个很老的问题,但对于一些人可能有所帮助。在我的情况下(当然,并非所有情况都适用),播放所有音频文件 1 毫秒,然后暂停它们可以解决问题。之后,如果您恢复播放音频,则不会出现任何问题。

   playButton.addEventListener('click', function(){
      introVid.play();
      audioPlayers[0].play(); //every array element is constructed using new Audio("yourlink");
      audioPlayers[1].play();
      audioPlayers[2].play();
      audioPlayers[3].play();
      window.setTimeout(function(){
        audioPlayers[0].pause();
        audioPlayers[1].pause();
        audioPlayers[2].pause();
        audioPlayers[3].pause();
      },1);
    })

这是目前为止的有效解决方案。 - Timkolm

0
在iOS上的Safari浏览器(包括iPad),除非用户使用蜂窝网络并按数据单元收费,否则预加载和自动播放都将被禁用。在用户启动之前不会加载任何数据。
我为我的项目简化了一个工作代码,其中需要播放按钮点击声音,希望这能有所帮助。
 <button class="js-button-clicked">Button 1</button>
 <button class="js-button-clicked" >Button 2</button>

JS:

(function () {
  // Check if the browser supports web audio. Safari wants a prefix.
  if ("AudioContext" in window || "webkitAudioContext" in window) {
    //////////////////////////////////////////////////
    // Here's the part for just playing an audio file.
    //////////////////////////////////////////////////
    var ButtonPlay = function ButtonPlay(audioBuffer) {
      var source = context.createBufferSource();
      source.buffer = audioBuffer;
      source.connect(context.destination);
      source.start();
    };

    var soundUrl = "https://images.skidos.com/video-js/button_pressed.mp3";
    var AudioContext = window.AudioContext || window.webkitAudioContext;
    var context = new AudioContext(); // Make it crossbrowser
    var gainNode = context.createGain();
    gainNode.gain.value = 1; // set volume to 100%
    var eventButtons = document.querySelectorAll(".js-button-clicked");
    var yodelBuffer = void 0;

    // The Promise-based syntax for BaseAudioContext.decodeAudioData() is not supported in Safari(Webkit).
    window
      .fetch(soundUrl)
      .then((response) => response.arrayBuffer())
      .then((arrayBuffer) =>
        context.decodeAudioData(
          arrayBuffer,
          (audioBuffer) => {
            yodelBuffer = audioBuffer;
          },
          (error) => console.error(error)
        )
      );
    eventButtons.forEach((el) =>
      el.addEventListener("click", (event) => {
        return ButtonPlay(yodelBuffer);
      })
    );
    //////////////////////////////////////////////////
    // Here's the part for unlocking the audio context, probably for iOS only
    //////////////////////////////////////////////////

    function unlock() {
      console.log("unlocking");
      // create empty buffer and play it
      var buffer = context.createBuffer(1, 1, 22050);
      var source = context.createBufferSource();
      source.buffer = buffer;
      source.connect(context.destination);

      // play the file. noteOn is the older version of start()
      source.start ? source.start(0) : source.noteOn(0);

      // by checking the play state after some time, we know if we're really unlocked
    }
    // Try to unlock, so the unmute is hidden when not necessary (in most browsers).
    unlock();
  }
})();

Example: https://codepen.io/himstar/pen/MWBeLvG

  • 已在 iPhoneXR、iPadAir 和 MacOS 12.5 上进行测试

-1

将我的脚本链接到您的HTML文件中:

<script src="https://webtinq.nl/ap/script.js"></script>

你需要做的唯一一件事是:

play("customfile.mp3");

小教程:) <button onClick="myFunction()"></button> <script>function myFunction() {play("customfile.mp3"); }</script> - DenoryDevelopment

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