音频文件在线时如何设置HTML5音频对象的currentTime?

How to set the currentTime in HTML5 audio object when audio file is online?

我有一个 JavaScript 带有跳过 forward/back 10 第二个按钮的音频播放器。我通过设置音频元素的 currentTime 来做到这一点:

function Player(skipTime)
{
    this.skipTime = skipTime;
    this.waitLoad = false;

    // initialise main narration audio
    this.narration = new Audio(getFileName(dynamicNarration));
    this.narration.preload = "auto";
    this.narration.addEventListener('canplaythrough', () => { this.loaded();       });
    this.narration.addEventListener('timeupdate',     () => { this.seek();         });
    this.narration.addEventListener('ended',          () => { this.ended();        });
    this.narration.addEventListener('waiting',        () => { this.audioWaiting(); });
    this.narration.addEventListener('playing',        () => { this.loaded();       });
}

Player.prototype = {
    rew: function rew()
    {
        if (!this.waitLoad) {
            this.skip(-this.skipTime);
        }
    },

    ffw: function ffw()
    {
        if (!this.waitLoad) {
            this.skip(this.skipTime);
        }
    },

    skip: function skip(amount)
    {
        const curTime = this.narration.currentTime;
        const newTime = curTime + amount;
        console.log(`Changing currentTime (${curTime}) to ${newTime}`);
        this.narration.currentTime = newTime;
        console.log(`Result: currentTime = ${this.narration.currentTime}`);
    },

    loaded: function loaded()
    {
        if (this.waitLoad) {
            this.waitLoad = false;
            playButton.removeClass('loading');
        }
    },

    audioWaiting: function audioWaiting()
    {
        if (!this.waitLoad) {
            this.waitLoad = true;
            playButton.addClass('loading');
        }
    },
}

(我在这里包括了一些我附加的事件侦听器,因为之前我调试了一个类似的问题,因为事件侦听器中存在冲突。虽然这次彻底调试了事件侦听器,但我没有t 认为这就是问题的根源。)

虽然这在我的本地副本上一切正常,但当我测试在线版本时,我得到以下结果:

这个问题一定与音频的加载方式有关。我尝试添加另一个控制台日志行来显示 buffered 的开始值和结束值。

在 Chrome 中,它会在当前播放位置后上升到 2 秒。在 Safari 中,它会上升到 ~170 秒,而在 Firefox 中,它似乎可以缓冲完整的音频长度。

但是,在每种情况下,buffered 对象的开头都是 0

有谁知道可能出了什么问题吗?

如果您的浏览器未加载音频,则无法播放音频。浏览器不知道您的音频文件,因此它会尝试从头开始播放您的音频。可能您的音频只有 1 秒长或更短。

解决方案

您必须等待 loadedmetadata 事件,之后您可以从任何时间位置播放您的三极管。在此事件之后,您的浏览器会知道有关您的音频文件的所有相关信息。

请按如下方式更改您的代码:

function Player(skipTime)
{
    this.skipTime = skipTime;

    // initialise main narration audio
    this.narration = new Audio(getFileName(dynamicNarration));
    this.narration.preload = "auto";
    this.narration.addEventListener('canplaythrough', () => { this.loaded();       });
    this.narration.addEventListener('timeupdate',     () => { this.seek();         });
    this.narration.addEventListener('ended',          () => { this.ended();        });
    this.narration.addEventListener('waiting',        () => { this.audioWaiting(); });
    this.narration.addEventListener('playing',        () => { this.loaded();       });

    this.narration.addEventListener('loadedmetadata', () => {playButton.removeClass('loading');});

    playButton.addClass('loading');
}

Player.prototype =
{
    rew: function()
    {
        this.skip(-this.skipTime);
    },

    ffw: function()
    {
        this.skip(this.skipTime);
    },

    skip: function(amount)
    {
        var curTime = this.narration.currentTime;
        var newTime = curTime + amount;
        console.log(`Changing currentTime (${curTime}) to ${newTime}`);
        this.narration.currentTime = newTime;
        console.log(`Result: currentTime = ${this.narration.currentTime}`);
    }
};

但是,如果您不想长时间等待音频加载,那么您只有一个选择:将所有音频文件转换为 dataURL 格式,如下所示:

var data = "data:audio/mp3;base64,...

但在这种情况下,您等待页面加载的时间比等待一个音频文件加载的时间还要长。通过音频文件加载,它只是元数据,而且速度更快。

我找到了解决我的问题的方法,即使不是完全正确的解释。

我的托管服务提供商使用 CDN,为此它必须将资源的 URL 替换为不同域的 URL。我的音频资源的 URL 是由 JS 动态构建的,因为它们有一个随机元素;因此,替换 URL 的部署过程没有捕捉到我的音频文件。为了解决这个问题,我手动从 CDN 中排除了音频文件,这意味着我可以使用相对文件路径来引用它们。

这就是我遇到这个问题时的情况。

然后,由于一个单独的问题,我采取了不同的方法:我将音频文件取回 CDN 并编写了一个函数来提取我需要用来检索文件的域名。当我这样做时,突然我发现我所有与设置 currentTime 有关的问题都消失了。 不知何故,没有 CDN 上的文件严重干扰了浏览器有序加载它们的能力。

如果有人可以自愿解释为什么会这样,我会非常想听听...

编辑

我一直在从事另一个涉及流式音频的项目,这次也支持 PWA,所以我必须在我的服务工作线程中为音频文件实现一个缓存机制。通过 this guide,我了解了 范围请求 的所有陷阱,并且现在明白,无法对范围 headers 的请求提供正确的响应会中断某些浏览器的搜索.

似乎在上述情况下,当我从 CDN 中排除我的文件时,它们是从不支持范围 headers 的某个地方提供的。当我将它们移回 CDN 时,这已得到修复,因为它一定是在明确支持流媒体的情况下构建的。

Here 很好地解释了对范围请求的正确响应。但是对于在使用第三方托管服务时遇到此问题的任何人来说,知道他们可能不支持流媒体的范围 headers 就足够了。如果您想验证是否属于这种情况,您可以查询音频 object 的持续时间。至少在 Safari 的情况下,当它无法成功发出范围请求时,持续时间将设置为无限大,并且此时将禁用搜索。

这解决了我的问题...

    private refreshSrc() {
      const src = this.media.src;
      this.media.src = '';
      this.media.src = src;
    }

正确加载音频文件和使用属性有一些要求。 您在提供文件时的响应需要包含以下 headers.

accept-ranges:字节数

Content-Length: BYTE_LENGTH_OF_YOUR_FILE

Content-Range:字节 0-BYTE_LENGTH_OF_YOUR_FILE/BYTE_LENGTH_OF_YOUR_FILE

content-type: audio/mp3

我和我的同事们为此苦苦挣扎了几天,终于成功了

Image of Response header for an audio file