当我在HTML5中构建一个简单的音频流元素时出现的一个值得注意的问题是,<audio>
标记在播放和暂停实时音频流方面的表现并不像人们期望的那样。
我正在使用最基本的HTML5代码来传输音频,带有控件的<audio>
标签,其来源是直播,可在此处演示:http://jsfiddle.net/uzzz03ha/
当前结果:当首次播放流时,它会播放按预期方式播放的内容。但是,当它暂停并再次播放时,音频会在流暂停时从其停止的位置恢复。用户现在正在侦听流的延迟版本。此事件不是特定于浏览器的。
期望的结果:当流暂停时,我希望流停止。当它再次播放时,我希望它恢复当前流所在的位置,而不是用户暂停流时的位置。
有没有人知道如何让这个音频流在暂停后正常恢复?
我为解决这个问题做了一些失败的尝试:
currentTime
对流音频没有任何作用。停止流,然后再次启动它的最佳方法似乎是删除源,然后调用load:
var sourceElement = document.querySelector("source");
var originalSourceUrl = sourceElement.getAttribute("src");
var audioElement = document.querySelector("audio");
function pause() {
sourceElement.setAttribute("src", "");
audioElement.pause();
// settimeout, otherwise pause event is not raised normally
setTimeout(function () {
audioElement.load(); // This stops the stream from downloading
});
}
function play() {
if (!sourceElement.getAttribute("src")) {
sourceElement.setAttribute("src", originalSourceUrl);
audioElement.load(); // This restarts the stream download
}
audioElement.play();
}
我在解决此问题时提出的一个解决方案是完全忽略音频元素上的播放和暂停功能,并在用户希望停止播放时将音频元素的音量属性设置为0,然后将音量属性设置为1当用户希望恢复播放时。
如果你使用jQuery(also demonstrated in this fiddle),这样一个函数的JavaScript代码看起来很像:
/*
* Play/Stop Live Audio Streams
* "audioElement" should be a jQuery object
*/
function streamPlayStop(audioElement) {
if (audioElement[0].paused) {
audioElement[0].play();
} else if (!audioElement[0].volume) {
audioElement[0].volume = 1;
} else {
audioElement[0].volume = 0;
}
}
我应该提醒一下,即使这实现了停止和恢复实时音频流所需的功能,但它并不理想,因为当停止时,流实际上仍在播放并在后台下载,在此过程中使用了带宽。
但是,此解决方案不一定占用比在流式音频元素上使用.play()
和.pause()
更多的带宽。简单地使用带流式音频的audio
标签无论如何都会占用大量带宽,因为一旦播放流式音频,它会在暂停时继续在后台下载流的内容。
应该注意的是,由于purposefully built-in limitations for iPhones and iPads,这种方法在iOS中不起作用:
在iOS设备上,音频级别始终在用户的物理控制之下。
volume
属性不能在JavaScript中设置。阅读volume
属性总是返回1
。
如果您选择使用此答案中的变通方法,则需要为正常使用play()
和pause()
函数的iOS设备创建回退,否则您的界面将无法暂停流。
当您想要从流中停止下载时,重置音频源并调用load()
方法似乎是最简单的解决方案。
由于它是一个流,浏览器将仅在用户下线时停止下载。重置是必要的,以保护您的用户免受其蜂窝数据的烧录,或避免提供浏览器在暂停音频时下载的过时内容。
请记住,当source属性设置为空字符串时,如audio.src = ""
,音频源将设置为页面的主机名。如果您使用随机单词,该单词将作为路径附加。
如下所示,设置audio.src =""
,意味着audio.src === "https://stacksnippets.net/js"
。设置audio.src="meow"
将使源成为audio.src === "https://stacksnippets.net/js/meow"
。因此3d段落不可见。
const audio1 = document.getElementById('audio1');
const audio2 = document.getElementById('audio2');
document.getElementById('p1').innerHTML = `First audio source: ${audio1.src}`;
document.getElementById('p2').innerHTML = `Second audio source: ${audio2.src}`;
if (audio1.src === "") {
document.getElementById('p3').innerHTML = "You can see me because the audio source is set to an empty string";
}
<audio id="audio1" src=""></audio>
<audio id="audio2" src="meow"></audio>
<p id="p1"></p>
<p id="p2"></p>
<p id="p3"></p>
如果您在给定时刻依赖音频源,请注意该行为。使用about URI scheme似乎欺骗它以更可靠的方式行事。所以使用“about:”或“about:about”,“about:blank”等将会正常工作。
const resetAudioSource = "about:"
const audio = document.getElementById('audio');
audio.src = resetAudioSource;
document.getElementById('p1').innerHTML = `Audio source: -- "${audio.src}"`;
// Somewhere else in your code...
if (audio.src === resetAudioSource){
document.getElementById('p2').innerHTML = "You can see me because you reset the audio source."
}
<audio id="audio"></audio>
<p id="p1"></p>
<p id="p2"></p>
重置audio.src
并调用.load()
方法将使音频尝试加载新源。如果您想在加载音频时显示微调器组件,但是在重置音频源时也不想显示该组件,则上述操作非常方便。
一个工作的例子可以在这里找到:https://jsfiddle.net/v2xuczrq/
如果使用随机单词重置源,那么当您也暂停音频时,或者直到onError
事件处理程序捕获它时,您最终可能会显示加载程序。 https://jsfiddle.net/jcwvue0s/
更新:字符串“javascript:;”并且可以使用“javascript:void(0)”代替“about:”URI,这看起来效果更好,因为它还会停止由“about:”引起的控制台警告。
不幸的是,HTML5中的音频流有点用词不当,因为它实际上并不是直播。 HTML5需要一个音频文件(.wav,.mp3等)来尝试下载。它不接受实时流式传输格式(在线广播,流式PCM数据等)。
您的jsfiddle.net/uzzz03ha/示例未使用实时音频流,它使用预先录制的文件。 HTML5稍微缓冲这个文件,然后播放它。暂停和恢复音频会暂停时间,因为它“知道”它是预先录制的文件。 Cole的回答可以帮助你解决暂停/播放问题,但你仍然会遇到html5不接受直播流的问题。你也必须破解你的方式。
所以,遗憾的是我没有回答你的问题,因为我在尝试解决同样的问题时发现了你的问题。这篇文章可能会帮助您指出正确的方向:Play live audio stream - html5