【问题标题】:How to properly unload/destroy a VIDEO element如何正确卸载/销毁 VIDEO 元素
【发布时间】:2010-07-15 18:17:59
【问题描述】:

我正在开发一个实时媒体浏览/播放应用程序,该应用程序使用浏览器中的<video> 对象进行播放(如果可用)。

我混合使用了直接的 javascript 和 jQuery,

我特别关心的是记忆。应用程序永远不会在窗口中重新加载,并且用户可以观看许多视频,因此随着时间的推移内存管理成为一个大问题。在今天的测试中,我看到内存配置文件随着每次后续加载而随着要流式传输的视频大小而跳跃,并且永远不会回落到基线。

我尝试了以下方法,结果相同:

1 - 清空包含创建元素的父容器,例如:

$(container_selector).empty();

2 - 暂停并删除匹配“视频”的子项,然后清空父容器:

$(container_selector).children().filter("video").each(function(){
    this.pause();
    $(this).remove();
});
$(container_selector).empty();

有没有其他人遇到过这个问题,有没有更好的方法来解决这个问题?

【问题讨论】:

  • 自从提出这个问题以来,随着事情的发展,我正在更新答案!我相信“删除”修复在当时几乎可以肯定是一个浏览器错误,而这些天,我建议暂停视频元素,删除 src 属性并触发重新加载。之后,它可以安全地从 DOM 中移除。

标签: javascript jquery dom memory-management html


【解决方案1】:

从 DOM 结构中处理视频非常棘手。可能会导致浏览器崩溃。这是在我的项目中帮助我的解决方案。

var videoElement = document.getElementById('id_of_the_video_element_here');
videoElement.pause();
videoElement.removeAttribute('src'); // empty source
videoElement.load();

这将重置一切,无声无息!

编辑:以下是标准中推荐的完整详细信息:https://html.spec.whatwg.org/multipage/media.html#best-practices-for-authors-using-media-elements

希望它能解决您的问题。

【讨论】:

  • videoElement.load() 似乎是它在 Firefox 中工作的关键。
  • 这个解决方案解决了在chrome v.47中移除并重新添加到DOM后视频无法播放的问题。
  • 这项工作是因为它会停止播放视频,但会引发 MEDIA_ERR_SRC_NOT_SUPPORTED 错误,因此请注意您是否有事件侦听器以查找错误
  • 你救了我的命。如果我遇见你,我会给你5杯啤酒
  • 如果 src 设置为 "",IE 和 Edge 将向服务器发送请求,类似于 img 元素。应该删除该属性,as recommended by the standard
【解决方案2】:

据报道此“解决方案”有效,大概是因为它会使这些视频容器对象可用于垃圾回收(请参阅下面的注释,了解为什么 delete 不应该有所作为的讨论)。无论如何,您的结果可能会因浏览器而异:

$(container_selector).children().filter("video").each(function(){
    this.pause(); // can't hurt
    delete this; // @sparkey reports that this did the trick (even though it makes no sense!)
    $(this).remove(); // this is probably what actually does the trick
});
$(container_selector).empty();

注意: 毫无疑问,delete 关键字仅用于从对象中删除属性(正如其他人在 cmets 中指出的那样)。在上面的delete this 行之前和之后将this 记录到控制台,每次都显示相同的结果。 delete this 应该什么都不做,也没什么区别。然而,这个答案继续获得少量选票,人们报告说省略delete this 会使其停止工作。也许某些浏览器 JS 引擎如何实现 delete 的方式有些奇怪,或者浏览器的 delete 与 jQuery 对 this 的操作之间存在不寻常的交互。

所以,请注意,如果这个答案解决了您的问题,如果它确实有效,那么目前尚不清楚为什么会这样,而且它很可能由于多种原因而停止工作。

【讨论】:

  • 好的,所以你让我走上了正轨——this = null;没用,但删除(这个);做了!
  • 很高兴听到这个消息。我会更新答案以包含您的发现。
  • 再次感谢!顺便说一句,this=null 引发了错误——应该避免。
  • delete 语句根本不会做任何事情,因为它只会删除属性。我相信,如果它被删除,它会完全一样。
  • 奇怪的是它起作用了 - 可能只是 Safari 或 WebKit 中的怪癖的组合。删除该行会导致内存不断上升,随着它的加入,它会增加,然后突然下降,我假设 gc 定期运行时
【解决方案3】:

将视频重置为空白而不删除它

$("#video-intro").first().attr('src','')

它会停止视频

【讨论】:

  • 谢谢。上述解决方案对我不起作用。但是,你的做到了。
【解决方案4】:
delete(this); 

不是解决方案。如果它适用于 x 或 y,则它是浏览器的不当行为。阅读here

删除操作符从对象中删除一个属性。

事实上,当自动播放属性打开时,某些浏览器(例如 Firefox)会将视频缓冲区缓存在内存中。处理起来很痛苦。

从 DOM 中移除 video 标签或暂停它只会产生不稳定的结果。您必须卸载缓冲区。

var video = document.getElementById('video-id');
video.src = "";

我的实验表明它是这样做的,但不幸的是,这是spec 未完全指定的浏览器实现。您不需要在 src 更改后调用 load() 。当更改视频标签的 src 时,您会隐式调用 load(),这在 W3C 规范中有所说明。

【讨论】:

  • 最新的 Chrome (48) 在这里工作。我使用了这种技术,它清理了我的内存泄漏。我认为如果使用 innerHTML 替换它所在的任何视频元素,浏览器应该自动清除 .src 属性。但是,这是另一个主题,谢谢/
【解决方案5】:

this answer 不同,此 sn-p 不执行任何有效的 DOM 操作(不删除标签)并且不会为 <video> 触发 error 事件:

var video = document.getElementById('video');
video.removeAttribute('src');
video.load();

此外,它不会触发 loadstart 事件。而且它应该可以工作 - 没有视频,没有加载开始。

在 Chrome 54 / FF 49 中检查。

【讨论】:

    【解决方案6】:

    只是为了澄清以后尝试这个的人,解决方案是这样的:(在 Safari 5.0 中通过 h264 视频确认,在 FF/opera 中尚未测试)

    $(container_selector).children().filter("video").each(function(){
        this.pause();
        delete(this);
        $(this).remove();
    });
    $(container_selector).empty();
    

    【讨论】:

      【解决方案7】:

      好的,这是一个确实有效的简单解决方案:

      var bodypage = document.getElementsByTagName('body')[0];
      var control_to_remove = document.getElementById('id_of_the_element_here');
      bodypage.removeChild(control_to_remove);
      

      【讨论】:

        【解决方案8】:
        【解决方案9】:
        var video = document.getElementById('video');
                if (video.firstChild) {
                    video.removeChild(video.firstChild);
                    video.load();
                }
        

        【讨论】:

        • 如果您单独使用视频标签和源标签,这就是这样做的方法。
        【解决方案10】:

        我在更复杂的层面上遇到过这个问题,我们在一个页面上加载了大约 80 个视频,并且在 IE 和 Edge 中存在内存管理问题。我在一个类似的问题上发布了我们的解决方案,我专门询问了我们的问题:https://stackoverflow.com/a/52119742/1253298

        【讨论】:

          【解决方案11】:

          我的代码没有使用带有src 标签的<video> 元素,而是使用多个<source> 子元素来设置多种格式的视频。

          为了正确销毁和卸载此视频,我不得不在此页面上使用多个答案的组合,结果是:

          var videoElement = $('#my-video')
          videoElement[0].pause()  // Pause video
          videoElement.empty()     // Remove all <source> children
          videoElement.load()      // Load the now sourceless video
          delete videoElement      // The call mentioned in other answers
          videoElement.remove()    // Removing the video element altogether
          

          希望这对某人有所帮助。

          【讨论】:

            【解决方案12】:

            我在动态加载一些视频时遇到了问题。我的&lt;video&gt; 元素中有两个来源。一个 mp4 和另一个 webm 作为后备。所以我不得不像这样遍历&lt;source&gt;

            function removeMedia(){
                let videos = document.getElementsByTagName('video');
                for(let vid in videos){
                    if(typeof videos[vid] == 'object'){
                        let srcs = videos[vid].getElementsByTagName('source');
                        videos[vid].pause();
                        for(let xsrc in srcs){
                            if(srcs[xsrc].src !== undefined){
                                srcs[xsrc].src = '';
                            }
                        }
                        videos[vid].load();
                        videos[vid].parentNode.removeChild(videos[vid]);
                    }
                }
            }
            

            【讨论】:

              【解决方案13】:

              这是关于如何关闭相机的答案 - 不仅仅是暂停。应该停止的是流 - 而不是视频元素引用: stream.stop()

              【讨论】:

                【解决方案14】:

                没那么复杂。只需将您的 src 设置为 null。

                Eg: document.querySelector('#yourVideo').src = null;
                

                它将删除您的视频src 属性。完成。

                【讨论】:

                • 在 FF/Chrome(可能还有其他浏览器)中,这会产生一个额外的网络请求,对相对于当前文档 URL 的 URI“null”,因为属性值被转换为字符串。
                【解决方案15】:

                这就是我为解决这个问题所做的。 我创建了 2 个视频元素(视频 1 和视频 2)。 使用完 video1 后,获取 source(src) 属性值,然后将 video1 从 DOM 中移除。

                然后将 video2 源 (src) 设置为您从 video1 获得的任何值。

                不要使用来自 video1 的流,因为它缓存在内存中。

                希望这会有所帮助。

                【讨论】:

                  【解决方案16】:

                  在 AngularJS 中对我有用的一个解决方案是使用以下代码: 如果您不想删除源网址,并重置为视频的开头

                  let videoElement = $document[0].getElementById('video-id');
                  videoElement.pause();
                  videoElement.seekable.start(0);
                  videoElement.load();
                  

                  如果你想从视频标签中删除源:

                  let videoElement = $document[0].getElementById('video-id');
                  videoElement.pause();
                  videoElement.src="";
                  videoElement.load();
                  

                  希望有人觉得它有用。

                  【讨论】:

                    【解决方案17】:

                    我知道这是一个老问题,但我遇到了同样的问题,并且尝试了几乎所有提到 &lt;video&gt;src 属性的解决方案,所有解决方案似乎都有其缺点。

                    在我指定的情况下,除了&lt;video&gt; 元素,我还同时使用&lt;audio&gt; 元素。

                    当我意识到处理src 属性可能是错误的事情时,我正在阅读MDN 的一篇文章。相反,我重写了所有代码,将&lt;source&gt; 元素附加和删除到&lt;video&gt;&lt;audio&gt; 元素。

                    这是我发现的唯一不会触发新加载或生成错误或其他不良通知的方法。

                    这是我正在使用的代码的最小/简化版本(在 Firefox 86 和 Chrome 88 上测试)。

                    <!DOCTYPE html>
                    <html xmlns="http://www.w3.org/1999/xhtml" lang="en-us">
                    <head>
                        <meta charset="utf-8" />
                        <meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui, shrink-to-fit=no" />
                    </head>
                    <body>
                        <button type="button" onclick="play()">Play</button>
                    
                        <button type="button" onclick="stop()">Stop</button>
                    
                        <video id="myVideo"></video>
                    
                        <script type="text/javascript">
                            "use strict";
                    
                            var myVideo = document.getElementById("myVideo");
                    
                            myVideo.onloadstart = () => {
                                console.log("onloadstart");
                            };
                    
                            myVideo.onloadeddata = () => {
                                console.log("onloadeddata");
                            };
                    
                            myVideo.onload = () => {
                                console.log("onload");
                            };
                    
                            myVideo.onerror = () => {
                                console.log("onerror");
                            };
                    
                            function play() {
                                while (myVideo.firstChild)
                                    myVideo.removeChild(myVideo.firstChild);
                    
                                var source = document.createElement("source");
                                source.src = "example.mp4";
                                myVideo.appendChild(source);
                                myVideo.load();
                                myVideo.play();
                            }
                    
                            function stop() {
                                while (myVideo.firstChild)
                                    myVideo.removeChild(myVideo.firstChild);
                    
                                myVideo.load();
                            }
                        </script>
                    </body>
                    </html>
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2021-03-17
                      • 1970-01-01
                      • 1970-01-01
                      • 2023-03-30
                      • 1970-01-01
                      • 2011-04-06
                      • 2017-01-20
                      • 1970-01-01
                      相关资源
                      最近更新 更多