音频文件在线时如何设置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 认为这就是问题的根源。)
虽然这在我的本地副本上一切正常,但当我测试在线版本时,我得到以下结果:
- Chrome: 将播放位置重置为
0
。最终控制台行显示结果:currentTime = 0
.
- Safari: 根本不改变播放位置。最后的
console.log
行给出了 currentTime
等于 newTime
的值(即使播放位置实际上没有改变)。
- Firefox: 向前跳过有效;向后跳会中断音频几秒钟,然后它会从播放头之前的几秒钟再次开始播放。在这两种情况下,最后的 console.log 行给出了
currentTime
等于 newTime
的值
这个问题一定与音频的加载方式有关。我尝试添加另一个控制台日志行来显示 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
我和我的同事们为此苦苦挣扎了几天,终于成功了
我有一个 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 认为这就是问题的根源。)
虽然这在我的本地副本上一切正常,但当我测试在线版本时,我得到以下结果:
- Chrome: 将播放位置重置为
0
。最终控制台行显示结果:currentTime = 0
. - Safari: 根本不改变播放位置。最后的
console.log
行给出了currentTime
等于newTime
的值(即使播放位置实际上没有改变)。 - Firefox: 向前跳过有效;向后跳会中断音频几秒钟,然后它会从播放头之前的几秒钟再次开始播放。在这两种情况下,最后的 console.log 行给出了
currentTime
等于newTime
的值
这个问题一定与音频的加载方式有关。我尝试添加另一个控制台日志行来显示 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
我和我的同事们为此苦苦挣扎了几天,终于成功了