重启后第一次在 iOS 6 (iPhone 5) 上 WebKit 音频失真

Posted

技术标签:

【中文标题】重启后第一次在 iOS 6 (iPhone 5) 上 WebKit 音频失真【英文标题】:WebKit Audio distorts on iOS 6 (iPhone 5) first time after power cycling 【发布时间】:2013-07-26 23:00:58 【问题描述】:

我一直在努力解决在 ios 6 下使用 html5 中的 webkitAudioContext 难以捉摸的音频失真错误。它可能发生在其他情况下,但我可以获得 100% 重现的唯一方法是在电源循环后第一次访问我的页面装置。如果您在访问此页面之前访问任何支持音频的页面,似乎不会出现此问题。

失真只发生在由 webkitAudioContext.decodeAudioData() 生成然后通过 webkitAudioContext.createBufferSource() 播放的音频。 webkitAudioContext.createMediaElementSource() 的音频播放将不会失真。

我错过了一些初始化步骤吗?以下是我作为错误报告提交给 Apple 的完整代码和 HTML(但未收到回复):

<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript">
        var buffer = null;
        var context = null;
        var voice = null;

        function load_music(file) 
            context = new webkitAudioContext();
            voice = context.createBufferSource();
            var request = new XMLHttpRequest();
            request.onload = function() 
                context.decodeAudioData(request.response, function(result) 
                    buffer = result;
                    document.getElementById("start").value = "Start";
                );
            ;
            var base = window.location.pathname;
            base = base.substring(0, base.lastIndexOf("/") + 1);
            request.open("GET", base + file, true);
            request.responseType = "arraybuffer";
            request.send(null);
        

        function start_music() 
            if (!buffer) 
                alert("Not ready yet");
                return;
            
            voice.buffer = buffer;
            voice.connect(context.destination);
            voice.noteOn(0);

            document.getElementById("compare").style.display = "block";
        
        </script>       
    </head>

    <body onload="load_music('music.mp3')">
        <p>This is a simple demo page to reproduce a <strong>webkitAudio</strong>
        problem occurring in Safari on iOS 6.1.4. This is a stripped down demo
        of a phenomenon discovered in our HTML5 game under development,
        using different assets.</p>

        <p><u>Steps to reproduce:</u></p>

        <ol>
            <li>Power cycle <strong>iPhone 5 with iOS 6.1.4</strong>.</li>
            <li>Launch Safari immediately, and visit this page.</li>
            <li>Wait for &quot;Loading...&quot; below to change to
                &quot;Start&quot;.</li>
            <li>Tap &quot;Start&quot;.</li>
        </ol>

        <p><u>Issue:</u></p>

        <p>Audio will be excessively distorted and play at wrong pitch. If 
        another audio-enabled web site is visited before this one, or this 
        site is reloaded, the audio will fix. The distortion only happens on 
        the first visit after cold boot.  <strong>To reproduce the bug, it is 
        critical to power cycle before testing.</strong></p>

        <p>This bug has not been observed on any other iOS version (e.g. does
        not occur on iPad Mini or iPod 5 using iOS 6.1.3).</p>

        <input id="start" type="button" value="Loading..." onmousedown="start_music()" />

        <span id="compare" style="display:none;"><p><a href="music.mp3">Direct link</a> to audio file, for
        comparison.</p></span>
    </body>
</html>

注意:正文表明这仅在 iOS 6.1.4 上发生,但我的意思是在这种情况下,仅在电源循环时才会出现问题。我在 6.1.3 以下的 iPad Mini 上也遇到过这个问题,但在重启电源时没有。

编辑:我尝试过的一些事情...推迟创建缓冲区源没有任何区别。使用不同的转码器生成它播放的 .mp3 文件没有区别。播放一次性静音作为第一个声音没有任何区别,因为每个 decodeAudioData 声音都会继续失真,直到页面重新加载。如果 createMediaElementSource 和 createBufferSource 源混合在同一页面中,则只有 createBufferSource 音频(使用 decodeAudioData)会失真。当我在失败情况和非失败情况下检查 request.response.byteLength 时,它们是相同的,表明 XMLHttpRequest 没有返回不正确的数据,尽管我认为数据损坏会损坏 MP3 标头并呈现文件无论如何都无法播放。

故障条件和非故障条件之间存在一个可观察到的差异。只读值 context.sampleRate 在故障状态下为 48000,在非故障状态下为 44100。 (但故障状态听起来比非故障状态低。)我唯一发生的事情是一个黑客,如果在应该报告 44100 的浏览器上检测到 48000,我会通过 JavaScript 刷新页面,但这是严重的 userAgent筛选而不是未来的证明,这让我很紧张。

【问题讨论】:

【参考方案1】:

即使在 iOS 9.2 上,我也遇到过类似的问题。

即使没有&lt;video&gt; 标签,冷启动后第一次在页面上播放音频时播放也会失真。重新加载后,它工作正常。

最初的AudioContext 似乎默认为 48 kHz,这是发生失真的地方(即使我们的音频采样率为 48 kHz)。当播放正常时,AudioContext 的采样率为 44.1 kHz。

我确实找到了解决方法:可以在播放初始声音后重新创建 AudioContext。新创建的AudioContext 似乎具有正确的采样率。为此:

// inside the click/touch handler
var playInitSound = function playInitSound() 
    var source = context.createBufferSource();
    source.buffer = context.createBuffer(1, 1, 48000);
    source.connect(context.destination);
    if (source.start) 
        source.start(0);
     else 
        source.noteOn(0);
    
;

playInit();
if (context.sampleRate === 48000) 
    context = new AudioContext();
    playInit();

【讨论】:

为我工作,谢谢!把它放在一个初始的 touchEnd 处理程序中,也可以解除对 iOS 网络音频的阻塞。【参考方案2】:

我发现了一个与 HTML5 视频相关的错误,并认为我发现了问题的根源。 我注意到,如果您使用&lt;video&gt; 标签播放视频,它会将 context.sampleRate 值设置为视频音频编码的任何值。似乎 iOS Safari 有一个用于所有内容的全局 sampleRate。要查看此内容,请尝试以下操作:

// Play a video with audio encoded at 44100 Hz
video.play();

// This will console log 44100
var ctx = new webkitAudioContext();
console.log(ctx.sampleRate);

// Play a video with audio encoded at 48000 Hz
video2.play();

// This will console log 48000
var ctx = new webkitAudioContext();
console.log(ctx.sampleRate);

此全局采样率似乎在页面加载时保持不变,并在选项卡和浏览器实例之间共享。因此,在另一个标签中播放 youtube 视频可能会破坏所有解码的音频。

当以一种采样率解码并以另一种采样率播放时,音频会失真。

    解码音频并存储缓冲区 做一些改变采样率的事情,例如播放视频或音频文件 播放缓冲区(失真)

我不知道为什么在冷启动后会发生这种情况。如果我不得不猜测,是 Safari 在您尝试使用它之前不会初始化这个全局采样率。

这个问题在 iOS 7 上仍然存在,所以我认为不会很快修复。与此同时,我们还遇到了一些黑客行为,比如检查采样率的变化。

【讨论】:

在 IOS 8 上仍然是一个问题。 以及 9.2.1(撰写本文时的最新版本)【参考方案3】:

一个 npm 包在线解决这个问题:

https://github.com/Jam3/ios-safe-audio-context

npm install ios-safe-audio-context

【讨论】:

以上是关于重启后第一次在 iOS 6 (iPhone 5) 上 WebKit 音频失真的主要内容,如果未能解决你的问题,请参考以下文章

iphone重启后iOS应用需要登录

如何关闭 iPhone 5 iOS 6 中的弹出框?

iOS 8 代码适用于 iPhone 5s 但不适用于 iPhone 5

关于升级 Dubbo 版本到 2.6.5 后启动失败的“坑”

现在越狱后怎么下载烧饼修改器啊?官网的源都没有了

iPhone6升级到ios9之后通知栏清除不掉