javascript/html: Replacing embedded videos memory leak?
I was wondering how to best replace embedded videos on a webpage. I have a bunch of video files (avi) that a visitor will be able to select and watch.
I am pretty new to javascript and html, but what I am currently doing is embedding the videos on the webpage as objects, and when a new video开发者_如何转开发 is selected, I change the url param for the object. This changes the video correctly, however after a few videos the browser becomes unresponsive. Looking at the task manager, the memory usage goes up with each video opened.
I originally thought that since the object has the same id, it would delete the first video and load the next. However, it seems like the first video is still in memory. Is there a better way to do this?
I am using using Windows 7, Windows Media Player 12, IE8. I am also wondering if this could be related to these technologies, because it doesn't seem to leak memory when I run this on my older PC (Windows XP, WMP 9, IE8).
Here is basically what I am doing: Video tags in html:
<object width="100%" height="100%" id="video"
classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">
<param name="url" value="video1.avi">
<param name="autostart" value="1">
<param name="uiMode" value="full" />
</object>
Then when a user selects a new video (javascript - assume newVideoPath contains the path to the next video to play):
$("video").url = newVideoPath;
Is there any memory cleanup I should be taking care of?
First of all I want to mention that I don't have much experience with cross-browser video embedding. Perhaps the explanations help nevertheless.
There are basically 2 ways to play video: Using the browser to play video and using a plugin.
Using a browser means that you need to serve different video files for different browsers. There are WebM or OGV for Firefox, Chrome, Opera and H264 for IE9 and Safari. IE8 and below do not support video at all.
The plugin approach has the advantage that you can serve what the plugin needs. The problem is that you never know if a plugin such as Windows Media Player, Flash or another is present at all.
This leads to the conclusion that you need to serve different media files (different file formats) to get a reasonably high probability of working videos. John Dyer's tutorial list the important steps to take – however you should additionally serve WebM before trying to use a plugin. Serving AVI files does not make much sense since this format is only a container that could contain any video and audio codecs.
<video>
<source src="myfile.mp4">
<source src="myfile.ogg">
<object src="flashplayer.swf?file=myfile.mp4">
<embed src="flashplayer.swf?file=myfile.mp4">
</object>
</video>
This code tries to serve myfile.mp4
and if that fails myfile.ogg
and if that fails, too, to serve myfile.mp4
using Adobe's Flash. You can add your files with as many source
tags as you have file formats available. The browser will then use the first file it understands.
The tutorial has an extended version that uses JavaScript to detect video support in the first place and adds fallbacks to Flash when necessary. You would replace the Flash part with Windows Media Player.
A word on the classid
attribute: This attribute refers to a specify ActiveX control. There are 2 problems with this. ActiveX is only available on Windows and will fail on all other platforms. Since the classid
specifies a specific control it will also fail for Windows systems without the very plugin implementation – so avoid such things. Therefore I would recommend to use the HTML5 approach with the video tag first and just give some files. Doing so allows browsers to choose a suitable player which increases the change of your video playing. Then a bit of JavaScript needs to be added to provide fallbacks.
Finally we end up with something similar to:
<video id="video">
<source src="myfile.webm">
<source src="myfile.mp4">
<source src="myfile.ogg">
</video>
<script type="text/javascript">
(function(){
var dummy = document.createElement("video");
// See if native video support is not available
if(typeof(video.canPlayType)===undefined || video.canPlayType('video/webm') == '' || add other video types here corresponding to source tags){
var videoElement = document.getElementById('video');
var fallbackContainer = document.createElement('div');
// Insert your HTML string for Windows Media, Flash, MPlayer or whatever else here
var fallback = '…';
fallbackContainer.innerHTML = fallback;
// This replaces the native (not available) video player with the plugin
videoElement.parentNode.replaceChild(fallbackContainer, videoElement);
}
})();
</script>
You could also use something like MediaElement.js and save yourself from handling all the different environments yourself.
Now, let's come back to your questions regarding memory leaks and replacing videos. If there are memory leaks caused by your script then the implementation that you are using has some serious problems. But before blaming the implementation you should make sure that a leak is really present.
Garbage collection (the process of freeing memory) takes time and is thus done at “suitable” times and not as soon as possible. So it could well be that your browser (or plugin) cleans up but only after some time or when memory is low. You would then see increasing memory whenever you change the video but this would not be a leak because it would be cleaned up eventually. Try to trigger the garbage collection by changing the video a lot of times until you run out of memory – does a cleanup happen? Try changing tabs in your browser or closing a tab – does a cleanup happen? If you've seen a cleanup now, there is probably no leak to worry about. The implementation just thinks your test case did not need freeing of memory.
In case you found it is really a leak you could try to detach to video an use a new video instead of replacing the old video. To do so, use the same approach as before:
var oldVideoElement = document.getElementById('video');
// Add some video element creation here for the new video
var newVideoElement = …;
// Replace video but make the browser aware of the replacement by using DOM methods
oldVideoElement.parentNode.replaceChild(newVideoElement, oldVideoElement);
// Get rid of the reference to the old video (just in case IE8 has still problems with discarding references)
var oldVideoElement = null;
Using the DOM methods allow the browser to remove the plugins entirely from memory. With a bit of luck your leak is now gone. Use the same technique to switch between your files by creating a new video and use it to replace the old video (the whole element, not just the file name).
If this did not help you need to provide more information: Which operating systems and browsers are you targeting? Do you know that a specific plugin is present? What video and audio codecs are within your AVI files?
精彩评论