Javascript中预加载音频遇到的问题

7
我正在为一个 GameAPI 设计跨设备/浏览器的图像和音频预加载方案。音频文件将被预加载,并在完成后发出回调。
问题是,在页面加载缓慢时,音频不会开始加载,但通常在第二次尝试时就能正常工作,可能是因为它已经缓存并知道其存在。
我把问题缩小到了 audio.load() 函数。去掉它可以解决问题,但有趣的是,我的 Motorola Droid 需要该函数。
你在 HTML5 音频预加载方面有哪些经验?
这是我的代码。是的,我知道在单独的函数中加载图片可能会导致竞争条件 :)
var resourcesLoading = 0;

function loadImage(imgSrc) {
    //alert("Starting to load an image");
    resourcesLoading++;

    var image = new Image();
    image.src = imgSrc;

    image.onload = function() {
        //CODE GOES HERE
        //alert("A image has been loaded");
        resourcesLoading--;
        onResourceLoad();
    }
}

function loadSound(soundSrc) {
    //alert("Starting to load a sound");
    resourcesLoading++;

    var loaded = false;

    //var soundFile = document.createElement("audio");
    var soundFile = document.createElement("audio");
    console.log(soundFile);
    soundFile.autoplay = false;
    soundFile.preload = false;

    var src = document.createElement("source");
    src.src = soundSrc + ".mp3";
    soundFile.appendChild(src);

    function onLoad() {
        loaded = true;

        soundFile.removeEventListener("canplaythrough", onLoad, true);
        soundFile.removeEventListener("error", onError, true);

        //CODE GOES HERE
        //alert("A sound has been loaded");
        resourcesLoading--;
        onResourceLoad();
    }

    //Attempt to reload the resource 5 times
    var retrys = 4;

    function onError(e) {
        retrys--;

        if(retrys > 0) {
            soundFile.load();
        } else {
            loaded = true;

            soundFile.removeEventListener("canplaythrough", onLoad, true);
            soundFile.removeEventListener("error", onError, true);

            alert("A sound has failed to loaded");
            resourcesLoading--;
            onResourceLoad();
        }
    }

    soundFile.addEventListener("canplaythrough", onLoad, true);
    soundFile.addEventListener("error", onError, true);
}

function onResourceLoad() {
    if(resourcesLoading == 0)
        onLoaded();
}

由于没有错误提示,只是偶尔失败,因此很难诊断该问题。


1
这有些烦人,但我通过将音频作为标准的 AJAX 文本请求加载来实现了伪装。我知道这听起来很愚蠢,但一旦数据被缓存,它就被缓存了,嘭:预加载音频。存在一些问题,例如浏览器不会加载超过一个媒体流,我在处理视频时也遇到同样的问题,因此我只加载了缩略图并动态创建了 HTML5 播放器。我发现将其伪装成 AJAX 可以欺骗浏览器认为它不是媒体。 - Ahmed Nuaman
有趣,我得试试看。 - Jeffrey Sweeney
4个回答

6

我已经把它搞定了。实际上,解决方案相当简单:

基本上,它的工作原理是这样的:

channel.load();
channel.volume = 0.00000001;
channel.play();

如果不明显,load函数告诉支持它的浏览器和设备开始加载,然后声音立即尝试以几乎为零的volume播放。因此,如果load函数不足够,声音“需要”播放的事实足以在我测试过的所有设备上触发加载。 load函数现在可能是多余的,但基于音频实现的不一致性,这可能没有什么坏处。
编辑:在Opera、Safari、Firefox和Chrome上测试后,设置volume为0仍会预加载资源。

至少对于视频元素,您可以使用 videoElem.mute = true 来将其静音... 音频元素可能也存在相同的属性。 - superjos
我发现这个方法可行,在我的情况下,我在两个播放器之间淡入/淡出,使其无缝切换。我必须让第二个音轨保持静音,然后当我切换到第二个音轨时,它会立即加载并播放。 - Elim Garak

2
canplaythrough事件会在足够的数据被缓冲后触发,如果在此事件上开始播放,则可能无间断地播放到结束。HTML音频元素是为流式传输而设计的,因此文件在此事件触发时可能尚未完全下载完成。
与之相比,图像只有在完全下载后才会触发其事件。
如果您离开页面并且音频尚未完全下载完成,则浏览器可能不会缓存它。但是,如果它已经完全下载完成,则可能会被缓存,这就解释了您所看到的行为。
我建议使用HTML5 AppCache来确保图像和音频得到缓存。

感谢AshleysBrian。经过一些研究,建议我使用canplaythrough而不是loadeddata,但我会考虑你的第二个意见。缓存并不是主要问题;偶尔loaded回调没有被调用。 - Jeffrey Sweeney

1

如上所述,AppCache 可能是您唯一的解决方案,以便将音频从一个浏览器会话缓存到另一个浏览器会话(这不是您要求的,对吧?)。但请记住,某些浏览器提供的空间有限。例如,Safari 允许用户在设置中更改此值,但默认值为 5MB,几乎无法保存一堆歌曲,特别是如果其他经常被用户访问的网站也使用 AppCache。此外,IE <10 不支持 AppCache。


好知道,谢谢。当然IE不支持它 :) - Jeffrey Sweeney

0

好的,最近我也遇到了同样的问题,我的解决方法是使用简单的ajax请求一次性加载整个文件(以便缓存),然后直接从缓存中再次加载声音并使用事件绑定canplaythrough

我使用Buzz.js作为我的HTML5音频库,我的代码基本上是这样的:

var self = this;
$.get(this.file_name+".mp3", function(data) {
    self.sound = new buzz.sound(self.file_name, {formats: [ "mp3" ], preload: true});
    self.sound.bind("error", function(e) {
        console.log("Music Error: " + this.getErrorMessage());
    });
    self.sound.decreaseVolume(20);
    self.sound.bind("canplaythrough",function(){ self.onSoundLoaded(self); }); 
});

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