如何在HTML5中停止和恢复实时音频流,而不是仅仅暂停它?

yftpprvb  于 2022-12-25  发布在  HTML5
关注(0)|答案(4)|浏览(246)

当我在HTML5中构建一个简单的音频流元素时,出现了一个值得注意的问题,即<audio>标记在播放和暂停实时音频流方面的行为与预期的不同。
我使用最基本的HTML5代码来流式传输音频,这是一个带有控件的<audio>标记,其源代码是一个实时流。

目前成果:首次播放流时,它会按预期播放正在流式传输的内容。但是,暂停并再次播放流时,音频会从流上次暂停时停止的位置准确恢复。用户现在收听的是流的延迟版本。这种情况不特定于浏览器。
预期成果:暂停流时,希望流停止。再次播放时,希望流从当前所在位置继续播放,而不是从用户暂停流时所在位置继续播放。

有没有人知道一种方法,使这个音频流恢复正常后,它被暂停?
我为解决此问题所做的一些失败尝试:

  • 更改音频元素的currentTime对音频流没有任何影响。
  • 我已经在用户停止流播放时从DOM中删除了音频元素,并在用户恢复播放时将其添加回来。流仍然在用户停止的地方继续,更糟糕的是,在幕后下载了流的另一个副本。
  • 我在每次播放流时都在流URL的末尾添加了一个随机GET变量,试图欺骗浏览器,使其相信正在播放一个新的流。
h4cxqtbf

h4cxqtbf1#

停止流,然后再次启动它的最佳方法似乎是删除源,然后调用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();
}
gudnpqoy

gudnpqoy2#

当您希望停止从流中下载时,重置音频源并调用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"等可以很好地工作。
一个二个一个一个
重置audio.src并调用.load()方法将使音频尝试加载新的源。如果您希望在加载音频时显示微调组件,但不希望在重置音频源时也显示该组件,则上述方法非常有用。
一个工作示例可以在这里找到:https://jsfiddle.net/v2xuczrq/
如果使用随机字重置源,则可能会在暂停音频时显示加载程序,或者直到onError事件处理程序捕获它为止。https://jsfiddle.net/jcwvue0s/
更新:字符串"javascript:; "和" javascript:void(0)"可以用来代替" about:"URI,这似乎工作得更好,因为它还将停止由" about:"引起的控制台警告。

xkrw2x1b

xkrw2x1b3#

:我把这个答案留给后人,因为这是当时我或任何人能想到的最好的解决方案。但我后来把Ciantic后来的想法标记为最好的解决方案,因为它实际上停止了我最初想要的流下载和播放。考虑那个解决方案,而不是这个。

在解决这个问题时,我想到的一个解决方案是完全忽略音频元素上的播放和暂停功能,当用户希望停止播放时,只将音频元素的volume属性设置为0,然后当用户希望继续播放时,将volume属性设置回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设备创建一个回退,否则您的界面将无法暂停流。

3hvapo4f

3hvapo4f4#

测试了@ Citics代码,如果你想使用多个源代码,它可以做一些修改。
当源被删除时,HTML音频播放器变为非活动状态,因此需要直接添加源(URL)才能再次变为活动状态。
还在末尾添加了一个事件侦听器,以便在暂停时连接函数:

var audioElement = document.querySelector("audio");
var sources = document.querySelector("audio").children;
var sourceList = [];
    
for(i=0;i<sources.length;i++){
    sourceList[i] = sources[i].getAttribute("src");
}
    
function pause() {
    for(i=0;i<sources.length;i++){
        sources[i].setAttribute("src", "");
    }
    audioElement.pause();
    // settimeout, otherwise pause event is not raised normally
    setTimeout(function () { 
        audioElement.load(); // This stops the stream from downloading
    });
    for(i=0;i<sources.length;i++){
        if (!sources[i].getAttribute("src")) {
            sources[i].setAttribute("src", sourceList[i]);
            audioElement.load(); // This restarts the stream download
        }
    }
}
    
audioElement.addEventListener("pause", pause);

相关问题