Related
I am creating a chrome extension that adds a button to the youtube player HUD (to the left of the watch later button). This works fine, but the only problem is that when the HUD autofades after a few seconds of inactivity, this button element doesn't.
I've added the button to the following div using javascript:
<div class="ytp-chrome-top-buttons"> </div>
and my button looks like this:
<button class="customClass" id="customID" title="custom title"><svg class="customClass"></svg></button>
I've looked through the source of the page to try and see how exactly youtube makes the other icons disappear, and whether I can copy a class to my svg to make it disappear, but I haven't been able to find it.
I've looked at using a timer and javascript to make the element disappear, but I figured if the functionality is already there, why complicate it.
As a last resort I will be resorting to using the timer method, but I'd much prefer using CSS classes.
I have had a cursory glance into the Youtube Player API, but to be frank, I'm having a hard time understanding how it works (While I have a few years of programming experience, I only started learning javascript, HTML, and CSS yesterday). I also understand that this API is only for embedded youtube videos?
To clarify, I DO know how to use API's in javascript, just not this particular one. I have used the youtube data API to implement the button's functionality successfully.
Any help is appreciated.
Thanks.
If you examine the element changes, you can see that the class list of the video has changed with the change of the bottom bar. When the bottom bar is hidden, the ytp-autohide class is added. Keeping this in mind, after scanning the class changes with the mutation observer, you will reach the desired result when the ytp-autohide class's existence status changes.
var video = document.querySelector("#movie_player");
var lastState = video.classList.contains('ytp-autohide');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.attributeName == "class"){
var currentState = mutation.target.classList.contains('ytp-autohide');
if(lastState !== currentState) {
lastState = currentState;
if(currentState)
console.log("Hided");
else
console.log("Shown");
}
}
});
});
observer.observe(video, {attributes: true});
I am trying to view a video stream from an IP camera in a web page, when the stream can be played I want it to start automatically. Trying to do that with a timer, try to play and if that fails, try again.
The timer (timeout) doesn't seem to do that, however if I execute the script using a button, it does. What am I missing?
see the code below.
thanks,
Ron
PS: I commented out the setTimeout functions, to make the button work.
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
function playVid() {
var videoElem = document.getElementById("IPcamerastream");
var playPromise = videoElem.play();
// In browsers that don’t yet support this functionality playPromise won’t be defined.
if (playPromise !== undefined) {
playPromise.then(function() {
// Automatic playback started!
videoElem.controls = true;
}).catch(function(error) {
// Automatic playback failed.
// setTimeout(playVid, 1000);
});
}
}
//setTimeout(playVid, 1000);
</script>
<button onclick="playVid()" type="button">Play Video</button><BR>
<video id="IPcamerastream" src="http://192.168.2.8:8080" width="960" height="540"></video>
</body>
</html>
Look into the features of the video html5 tag:(https://www.w3schools.com/tags/tag_video.asp)
one of the optional attributes is autoplay (Specifies that the video will start playing as soon as it is ready) so there is no need to set timeout.
<video id="IPcamerastream" src="http://192.168.2.8:8080" width="960" height="540" autoplay></video>
Move your script below the video element, and you should not need the timeout at all, because the element will already be initialized when the script is executed. So document.getElementById should resolve the element right away.
Using timers will introduce race conditions. If anything, you should add a listener to the DOMContentLoaded event.
Welcome Ron,
This is a well formatted question, but target browser info could also assist in helping you resolve your issue. Please consider adding it in future!
As for your problem, you tell us that you wish the video to autoplay, I'm assuming on page load?
I've also removed the duplicate source paste.
In this case, you only call playVid() from within the promise erroring out. The initial call is bound to the button click event.
In short, only clicking the button will initiate the playVid() function.
You need to add an event listener for DOM readiness and call playVid() within that. Otherwise, it's only being called when you click your button!
document.addEventListener('DOMContentLoaded', (event) => {
//the event occurred
playVid();
});
You can also use the autoplay HTML option in the video tag.
lang-html
<video {...} autoplay />
I had an almost similar problem ، when I received the video stream in a webrtc project, the video would not be displayed without clicking a button.
if you want to play automatically received stream video, you should add "muted" in the video tag.
I have an ember application with nested resources in which I'm showing videos. The outer resource (videos) simply displays all of the videos. When you click a video the nested video resource is activated and a title/player is shown.
This works great the first time you click something. The video shows up and it plays. HOWEVER, when clicking another video, the nested resource video is updated and the DOMs <source> gets updated but the OLD video continues to play! Why is this happening and how do I fix it?
Working Example on JSFiddle
I would use a component to create the video player, and wrap up the rerender logic in the component.
The component would look something like
App.VideoPlayerComponent = Ember.Component.extend({
src: null,
rerenderPlayer: function(){
if(this.$()) {
Ember.run.debounce(this, this.rerender, 200);
}
},
srcObserver: function(){
this.rerenderPlayer();
}.observes('src')
});
and the component template would look like
<script type="text/x-handlebars" data-template-name="components/video-player">
<video controls>
<source {{bind-attr src=src}} type = "video/mp4"></source>
</video>
</script>
You would then be able to just reference the component in your template like this
<script type="text/x-handlebars" data-template-name="video">
<h2>{{title}}</h2>
{{video-player src=src}}
</script>
You can see a working bin here: http://emberjs.jsbin.com/fehipa/2/edit
As a bonus.. If you created a video player component you would also be able to wrap up the ability to pause and play the video in the component. So that you can have a totally reusable video player to use anywhere in your app.
If you move the {{bind-attr src="src"}} from the <source> element up to the <video> element, it just works without any hacks.
your code would change from:
<video controls>
<source {{bind-attr src="src"}} type = "video/mp4"></source>
</video>
to
<video controls {{bind-attr src="src"}}></video>
Working example: http://jsfiddle.net/jfmzwhxx/
It's a bit ugly, but this can be achieved by rerendering the view. Add the following code:
App.VideoRoute = Ember.Route.extend({
oldVideoId: null,
afterModel: function (resolvedModel) {
if (this.get('oldVideoId') !== resolvedModel.id) {
this.set('oldVideoId', resolvedModel.id);
$.each(Ember.View.views, function( i, view ) {
if (view.renderedName === "videos"){
view.rerender();
}
});
}
}
});
Working Fiddle
I would take a similar approach to #AlliterativeAlice, but I would definitely not recommend doing this operation at the route level. This problem is a consequence of DOM security and thus is a job for the view. You're best bet is to setup an observer in your view class and call rerender from there. E.g.:
App.VideoView = Ember.View.extend({
_didChangeVideoSrc: function() {
// Make the view is still in the DOM
if(this.$()) {
this.rerender();
}
}.observes('controller.src')
});
I also updated your fiddle to make this work: http://jsfiddle.net/rverobx9/2/
While answers suggesting to re-render in any way might solve your problem, if you have access to the API of the player it'd be nice to see if stopping the video in the willDestroyElement of your view won't fix that.
Also you could have your video player being an Ember component, ensuring good setup, teardown and update:
App.VideoPlayerComponent = Ember.Component.extend({
source: null, // used in the template using that component
isPlayerReady: false, // used to know internally if the player is ready
setupPlayer: (function(){
// somehow setup the video player if needed
// (if Flash player for example, make the flash detection and whatever)
// once ready, trigger our `videoPlayerReady` event
this.set('isPlayerReady', true);
this.trigger('videoPlayerReady');
}).on('didInsertElement'),
updatePlayerSource: (function(){
// start playing if we have a source and our player is ready
if ( this.get('isPlayerReady') && this.get('source') )
{
// replace with the correct js to start the video
this.$('.class-of-player').videoPlay(this.get('source'));
}
}).observes('source').on('videoPlayerReady'),
teardownSource: (function(){
// stop playing if our player is ready since our source is going to change
if ( this.get('source') && this.get('isPlayerReady') )
{
// replace with the correct js to stop the player
this.$('.class-of-player').videoStop();
}
}).observesBefore('source'),
teardownPlayer: (function(){
// teardown the player somehow (do the opposite of what is setup in `setupPlayer`)
// then register that our player isn't ready
this.set('isPlayerReady', false);
}).on('willDestroyElement')
});
This will allow you to be sure everything is setup and teardown correctly, and since it's a component you can re-use, it'll be very easy to have a fallback to a flash player for example. Then whatever you have to handle and fix stuff related to the player, it will be in that component, and you'll just have to replace the part of your template with the player with:
{{video-player source=ctrl.videoUrl}}
I've seen a few discussions about this, but no real answers. I've had a lot of success getting mediaelement.js working for me except that it simply will not let me setSrc() on flash fallbacks. This is a huge bummer after so much work.
For a little background I'm using mediaelement-and-player.js v2.1.9 and using their player's API to change the media src via player.setSrc. I'm playing audio MP3s.
I'm getting this error in FF Mac:
this.media.setSrc is not a function
And this error in IE8 Win:
SCRIPT445: Object doesn't support this action
I find it hard to believe that this wasn't fully tested given that it seems a base part of their API. I've seen some other issues about similar problems but again, no real answers.
You would need to add "flashmediaelement.swf" to your code.
Had the same problem. Solved it by adding non-empty src and type="audio/mp3" attributes:
<audio id="player" controls src="#" type="audio/mp3" preload="none"></audio>
Presence of preload="none" is recommended here, because without it the element will send an additional request to a current page's URL in an attempt to download the audio.
Update: found an alternative way, zero-length WAV file can be embedded in src, thus you may use preload attribute normally and stop worrying about that an unneeded request will be sent if a user will click the play button before you set normal src.
<audio id="player" controls type="audio/mp3" src="data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=">
Don't worry about type and src incompatibility, because, according to audio element specification, type isn't legal attribute of audio tag at all (type is only a source tag's attribute), here it's placed only to fix MediaElement.js behavior.
I answered a similar question on github. Here's my solution:
This occurs when the setSrc method is called too soon after initializing the mediaElement player. Due to the flash fallback the swf (and therefore its api methods) will not be available until the success event is fired. After that setSrc works fine in IE8..
I didn't want to set the initial source from within the success handler. Therefore I used a boolean var to check whether the success event had occurred. In my source setting method I check for its value and use recursiveness (with a setTimeout to prevent overkill) whenever the boolean var equals false.. Did the trick for me.
//create the tag
var video = $("<video>",{id:"videoElement",width:640,height:360}).appendTo('body');//jquery
var mediaElementInitialized = true
//create the mediaelement
var mediaElement = new MediaElementPlayer("#videoElement",{
/**
* YOU MUST SET THE TYPE WHEN NO SRC IS PROVIDED AT INITIALISATION
* (This one is not very well documented.. If one leaves the type out, the success event will never fire!!)
**/
type: ["video/mp4"],
features: ['playpause','progress','current','duration','tracks','volume'],
//more options here..
success: function(mediaElement, domObject){
mediaElementInitialized = true;
},
error: function(e){alert(e);}
}
);
var setSource = function(src){
if(mediaElementInitialized == true){
if(mediaElement){
mediaElement.setSrc(src);
mediaElement.play();
}
} else {
//recursive.. ie8/flashplayer fallback fix..
var self = this;
setTimeout(function(){
self.setSource(src);
},100);
}
}
var plugin = new MediaElementPlayer(#mplay_audio_p',
{
//...params...
});
var url="http://www.somesite.com/audiofile.mp3";
plugin.setSrc(url);
plugin.load();
plugin.play();
I'm working on a realtime media browsing/playback application that uses <video> objects in the browser for playback, when available.
I'm using a mix of straight javascript, and jQuery,
My concern is specifically with memory. The application never reloads in the window, and the user can watch many videos, so memory management becomes a large concern over time. In testing today, I see the memory profile jumping by the size of the video to be streamed with each subsequent load, and never dropping back down to the baseline.
I've tried the following things with the same result:
1 - Empty the parent container containing the created element, eg:
$(container_selector).empty();
2 - Pause and remove children matching 'video', and then empty the parent container:
$(container_selector).children().filter("video").each(function(){
this.pause();
$(this).remove();
});
$(container_selector).empty();
Has anyone else run into this issue, and is there a better way to do this?
It is very tricky to dispose video from the DOM structure. It may lead to browser crashing. Here is the solution that helped me in my project.
var videoElement = document.getElementById('id_of_the_video_element_here');
videoElement.pause();
videoElement.removeAttribute('src'); // empty source
videoElement.load();
this will reset everything, silent without errors !
Edit: Here are the full details as recommended in the Standard: https://html.spec.whatwg.org/multipage/media.html#best-practices-for-authors-using-media-elements
Hope it resolve your query.
This "solution" is reported to work, presumably because it would make those video container objects available for garbage collection (see the note below for a discussion of why delete shouldn't be making a difference). In any case, your results are likely to vary by browser:
$(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();
Note: There's no doubt that the delete keyword is specified only to remove properties from objects (as others have pointed out in the comments). Logging this to the console both before and after the delete this line, above, shows the same result each time. delete this should do nothing and make no difference. Yet this answer continues to receive a trickle of votes, and people have reported that omitting delete this makes it stop working. Perhaps there's strangeness in how some browser JS engines implement delete, or an unusual interaction between a browser's delete and what jQuery is doing with this.
So, just be aware, if this answer solves your problem, that if it does work, it's not clear why that's the case, and it's just as likely to stop working for any number of reasons.
To reset the video to Blank without removing it
$("#video-intro").first().attr('src','')
It stops the video
delete(this);
is not a solution. If it worked for x or y it is a browser misbehaviour. Read here:
The delete operator removes a property from an object.
The truth is that some browsers (Firefox for example) will cache in memory the video buffer when autoplay property is on. It is a pain to deal with.
Removing the video tag from the DOM or pausing it can only produce unstable results. You have to unload the buffer.
var video = document.getElementById('video-id');
video.src = "";
My experiment shows that it is done as so but unfortunately this is browser implementation not completely specified by the spec. You do not need to call load() after src change. When changing the src of a video tag you implicitly call a load() on it, this is stated in the W3C spec.
This snippet doesn't do any effecient DOM manipulations (no tag removal) and doesn't fire error event for <video> unlike this answer:
var video = document.getElementById('video');
video.removeAttribute('src');
video.load();
Furthermore, it doesn't fire loadstart event. And it's like it should work - no video, no load start.
Checked in Chrome 54 / FF 49.
Just to clarify for anyone trying this later, the solution was this: (confirmed with h264 videos in Safari 5.0, untested in FF/opera yet)
$(container_selector).children().filter("video").each(function(){
this.pause();
delete(this);
$(this).remove();
});
$(container_selector).empty();
I was having an issue while dynamically loading some videos. I had two sources in my <video> element. One mp4 and the other webm as fallback. So I had to iterate through the <source>'s like so.
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]);
}
}
}
ok, here's a simple solution which certainly works:
var bodypage = document.getElementsByTagName('body')[0];
var control_to_remove = document.getElementById('id_of_the_element_here');
bodypage.removeChild(control_to_remove);
According to this bug:
https://bugs.chromium.org/p/chromium/issues/detail?id=255456&can=2&q=255456&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified
this seems to be a memory leak issue in Chrome!
var video = document.getElementById('video');
if (video.firstChild) {
video.removeChild(video.firstChild);
video.load();
}
I've encountered this problem on a more complicated level where we are loading ~80 videos on a page, and having problems with memory management in IE and Edge. I posted our solution on a similar question I asked specifically about our issue: https://stackoverflow.com/a/52119742/1253298
My code did not use a <video> element with a src tag, but instead used multiple <source> children to set a video in multiple formats.
To properly destroy and unload this video, I had to use a combination of multiple answers on this page, which resulted in:
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
Hope this helps someone.
Here is an answer on how to close the camera - not only pausing. It is the stream that should be stopped - not the video elements reference:
stream.stop()
Not much complicated. Just put your src to null.
Eg: document.querySelector('#yourVideo').src = null;
It will remove your video src attribute. Done.
This is what I did to solve this problem.
I created 2 video elements (video1 & video2).
After finished using video1, get the source(src) attribute value and then remove video1 from DOM.
Then set video2 source (src) to whatever value you got from video1.
Do not use stream from video1 as it is cached in memory.
Hope this will help.
One solution that worked for me in AngularJS is using below code:
In case you don't want to remove your source url, and reset to start of the video
let videoElement = $document[0].getElementById('video-id');
videoElement.pause();
videoElement.seekable.start(0);
videoElement.load();
And in case you want to remove the source from video tag:
let videoElement = $document[0].getElementById('video-id');
videoElement.pause();
videoElement.src="";
videoElement.load();
Hope someone finds it useful.
I know this is an old question, but I came across the same issue, and tried almost every solution mentioning <video>'s src attribute, and all solutions seemed to have their drawbacks.
In my specify case, besides <video> elements, I am also using <audio> elements at the same time.
I was reading an article at MDN when I realized that dealing with the src attribute could be the wrong thing to do. Instead, I rewrote all my code to append and remove <source> elements to both <video> and <audio> elements.
That was the only way I found that does not trigger a new load or generates error or other undesirable notifications.
This is a minimal/simplified version of the code I am using (tested on Firefox 86 and 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>
In my case, i used the solution mentioned above by #toon lite:
Array.from(document.getElementsByTagName('video')).forEach(video => {
video.pause();
video.removeAttribute('src');
video.load();
})
But it occurs the another problem in Chrome browser (version 93):
[Intervention] Blocked attempt to create a WebMediaPlayer as there are too many WebMediaPlayers already in existence. See crbug.com/1144736#c27
I guess it is all about the browser version's limit (mine is too old), anyway i fixed this bug by adding some extra operations:
video.src = '';
video.srcObject = null;
video.remove()
Finally the code looks like:
Array.from(document.getElementsByTagName('video')).forEach(video => {
video.pause();
video.removeAttribute('src'); // video.src = '' works so this line can be deleted
video.load();
video.src = '';
video.srcObject = null;
video.remove()
})