【问题标题】:How can I stop and resume a live audio stream in HTML5 instead of just pausing it?如何停止和恢复 HTML5 中的实时音频流,而不仅仅是暂停它?
【发布时间】:2015-01-31 05:11:10
【问题描述】:

当我在 HTML5 中构建一个简单的音频流元素时出现的一个值得注意的问题是,<audio> 标记在播放和暂停实时音频流方面的行为与预期不同。

我正在使用最基本的 HTML5 代码来流式传输音频,带有控件的 <audio> 标记,其来源是实时流。

当前结果:第一次播放流时,它会按预期播放任何正在播放的内容。但是,当它暂停并再次播放时,音频会在流之前暂停时完全恢复到它停止的位置。用户现在正在收听流的延迟版本。这种情况并非特定于浏览器。

期望的结果:当直播暂停时,我希望直播停止。再次播放时,我希望它恢复流当前所在的位置,而不是用户暂停流时的位置。

有没有人知道如何让这个音频流在暂停后正确恢复?

我为解决此问题所做的一些失败尝试:

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

【问题讨论】:

    标签: html audio html5-audio audio-streaming


    【解决方案1】:

    注意:为了后代,我留下这个答案,因为这是我或任何人当时可以为我的问题提出的最佳解决方案。但我后来将 Ciantic 的后来想法标记为最佳解决方案,因为它实际上像我最初想要的那样停止了流下载和播放。考虑那个解决方案而不是这个解决方案。


    我在解决此问题时提出的一个解决方案是完全忽略音频元素上的播放和暂停功能,当用户希望停止播放时将音频元素的音量属性设置为 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 设备创建一个备用设备,否则您的界面将无法暂停流。

    【讨论】:

    • 查看另一个答案 - 这是一个非常有效的担忧,即在用户不想收听的情况下消费一个提要是一种资源浪费 - 无论是从服务器的角度来看,还是从用户的角度来看,如果他们'重新移动或连接速度有限。
    【解决方案2】:

    停止流然后重新启动它的最佳方法似乎是删除源然后调用加载:

    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();
    }
    

    【讨论】:

    • 我已将此添加到我的代码中,我可以播放和暂停一次流。但是当我尝试再次开始播放第二次流时,它不会开始。当我点击播放时没有任何反应。
    【解决方案3】:

    当您想停止从流中下载时,重置音频源并调用load() 方法似乎是最简单的解决方案。

    由于是流式传输,因此只有在用户离线时浏览器才会停止下载。重置是必要的,以防止您的用户烧毁他们的蜂窝数据或避免提供浏览器在暂停音频时下载的过时内容。

    请记住,当源属性设置为空字符串时,例如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:" 引起的控制台警告。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-09
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2012-11-28
      • 1970-01-01
      • 2012-08-04
      • 1970-01-01
      相关资源
      最近更新 更多