Check to see if a video loads in iPad with JavaScript - javascript

I am having a bit of an issue with checking to see if a video is being loaded in iPad. I need to check and see if loads because I am looping through to load all videos with an increment like video_1.mp4, video_2.mp4, video_3.mp4. However, it seems like it ignores the "readyState" and goes straight to the else statement.
Here is the code:
function loadMedia() {
var media = document.getElementsByTagName("video")[0];
if (media.readyState === 4) {
alert("Video has been loaded!");
} else {
alert("Video hasn't been loaded!");
}
}
Is readyState supported by iPad?
Edit: Added more code.
The loadMedia function is binded to window.onload via an anonymous function.
window.onload = (function () {
loadMedia();
});
Here is the HTML:
<video class="video" controls="controls" poster="images/posters/tb_1.jpg" preload="metadata">
<source src="media/tb_1.mp4" type="video/mp4; codecs='avc1.42E01E, mp4a.40.2'" />
We apologize, but your browser does not support this video. Please consider an upgrade.
</video>

Apple documentation seems to at least suggest that readyState for media elements has been around since iOS 3.0:
http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLMediaElementClassReference/HTMLMediaElement/HTMLMediaElement.html
That's sort of indirect documentation, perhaps, but it's something.
Since firing up a JS debugger on devices can be annoying, you might want to change this:
alert("Video hasn't been loaded!")
To this:
alert("Video hasn't been loaded! " + media.readyState);
If you turn on the console in the browser (and, if you like, switch to console.log() rather than alert()), this should tell you if your problem is that readyState is undefined (and therefore likely not supported) or if the problem is that maybe media is undefined (seems unlikely).
Of course, you may have also found a bug. You might want to search Apple's bug tracker, if they allow such things.

Related

Receive events from multiple HTML5 videos

I have multiple videos and want to get an event from all of them. But I get only the event from one of them (tested in Chrome). Why?
HTML:
<video id="video1" preload="auto" src="video1.ogg"></video>
<video id="video2" preload="auto" src="video2.ogg"></video>
JS:
$('video').on('canplay', function(){
console.log($(this).attr('id'));
});
jsFiddle
EDIT:
Ok.. it is really strange. On the very first load I get sometimes both events. But after that on every reload I get just an event from one of them!? Here a screenshot of my console:
The problem is that the 'canplay' event has already fired by the time you register it. I believe this is because jsFiddle wraps your code inside of $(window).load(.... If you were to move the script into the html, like on this example, it should work.
Most of the time, if you know for sure that your script is going to run synchronously right after the video element is created with a src, you can assume that none of those events have been fired. But, just to be safe, and especially when you don't know how your code will be used, I recommend something like this:
function handleCanplay(video) {
console.log($(video).attr('id'));
};
$('video').each(function () {
//first, check if we missed the 'canplay' event
if (this.readyState >= this.HAVE_FUTURE_DATA) {
//yup, we missed it, so run this immediately.
handleCanplay(this);
} else {
//no, we didn't miss it, so listen for it
$(this).on('canplay', function () {
handleCanplay(this);
});
}
});
Working example here.

seek to a point in html5 video

Is it possible to seek to a particular point in html5 video displayed in a web page? I mean ,can I input a particular time value (say 01:20:30:045 ) and have the player control (slider) move to that point and play from that point onwards?
In older version of mozilla vlcplugin I think this is possible by seek(seconds,is_relative) method..but I would like to know if this is possible in html video.
Edit:
I created the page with video and added javascript as below.When I click on the link ,it displays the time of click..but it doesn't increment the play location..but continues to play normally.
Shouldn't the video play location get changed?
html
<video id="vid" width="640" height="360" controls>
<source src="/myvid/test.mp4" type="video/mp4" />
</video>
<a id="gettime" href="#">time</a>
<p>
you clicked at:<span id="showtime"> </span>
</p>
javascript
$(document).ready(function(){
var player = $('#vid').get(0);
$('#gettime').click(function(){
if(player){
current_time=player.currentTime;
$('#showtime').html(current_time+" seconds");
player.currentTime=current_time+10;
}
});
}
);
You can use v.currentTime = seconds; to seek to a given position.
Reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/currentTime
Unfortunately it seems with some movie elements it behaves differently than others. For instance with an amazon video_element, it seems you must call pause before you can seek anywhere, then call play. However, if you call play "too quickly" after setting the currentTime then it won't stick. Odd.
Here is my current work around:
function seekToTime(ts) {
// try and avoid pauses after seeking
video_element.pause();
video_element.currentTime = ts; // if this is far enough away from current, it implies a "play" call as well...oddly. I mean seriously that is junk.
// however if it close enough, then we need to call play manually
// some shenanigans to try and work around this:
var timer = setInterval(function() {
if (video_element.paused && video_element.readyState ==4 || !video_element.paused) {
video_element.play();
clearInterval(timer);
}
}, 50);
}
Top answer is outdated.
You can still use:
this.video.currentTime = 10 // seconds
But now you also have:
this.video.faskSeek(10) // seconds
The docs provide the following warnings regarding the fastSeek method:
Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.
The HTMLMediaElement.fastSeek() method quickly seeks the media to the new time with precision tradeoff.
If you need to seek with precision, you should set HTMLMediaElement.currentTime instead.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/fastSeek
Based on the above I guess the following is best if cross browser compatibility and performance are your top priority:
const seek = secs => {
if (this.video.fastSeek) {
this.video.fastSeek(secs)
} else {
this.video.currentTime = secs
}
}
seek(10)
If you prefer accuracy over performance then stick with:
this.video.currentTime = secs
At the time of writing faskSeek is only rolled out to Safari and Firefox but expect this to change. Check the compatibility table at the above link for the latest info on browser compatibility.

MediaElement.js setSrc not working for flash fallbacks on FF, IE7-8

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

Flash and JavaScript communication within IE

I am having in issue with IE passing a string back into an swf using the EternalInterface class in Flash CS4.
I have an swf with the following code:
var externalString:String = ExternalInterface.call("IncomingJS")
which is inside an event listener attached to an Event.ENTERFRAME and an if statement waiting for ExternalInterface.available.
The IncomingJS function looks like:
function IncomingJS() {
return stringFromHTML;
}
and sits on the HTML page with the swf.
I am able to successfully get the externalString variable and procceed with the rest of the AS3 script in Firefox, Safari and Chrome, but not in IE.
If I add in an alert (stringFromHTML) before the return statement in the Javascript, I get the value of the stringFromHTML spammed, which looks like Flash is firing the function at the right rate.
The embed code in HTML for the swf is a little simple:
<object width="750" height="200" id="controlledScale"><param name="movie" value="http://www.myURL.com/controlledScale.swf"><param name="allowScriptAccess" value="sameDomain" /><embed src="http://www.myURL.com/controlledScale.swf" width="750" height="200" allowScriptAccess="sameDomain"></embed></object>
Any help or advice would be greatly appreciated.
Thanks,
DavidB
Edit
I realise how poor the SWF embed code is.
Unfortunately, the HTML code is actually working within a 3rd party HTML generator, and one of it's limitations is that I can only have a single line (with unlimited length) of html at a time.
Are the other options (swfObject etc) able to run either with no line breaks in the code, or would I be asking for trouble with Javascript and the SWF to, instead of embedding the SWF directly, use something like an iFrame and refer to a 'proper' flash delpoyment html file?
Kind of at a point on this one where I'm not even sure where the problem is actually located. The swf's are find sending out to Javascript across all browsers, just not getting info back in IE only.
You must add an id to the object tag to work in IE.
<object width="750" id="myflash" height="200"><param name="movie" value="http://www.myURL.com/controlledScale.swf"><param name="allowScriptAccess" value="sameDomain" /><embed src="http://www.myURL.com/controlledScale.swf" width="750" height="200" allowScriptAccess="sameDomain"></embed></object>
I pretty sure there is a security "feature" in IE that stops JS being called too many times from Flash, to stop it crashing the browser.
Is there a reason why you have to call it every frame?
I'd suggest against this at all costs as it's putting a LOT of extra stress on the browser.
***** EDIT *****
If you want to call an ExternalInterface method from JS -> Flash in IE you have to reference the object slightly differently, like this:
function thisMovie(movieName) {
if (navigator.appName.indexOf("Microsoft") != -1) {
return window[movieName];
} else {
return document[movieName];
}
}
Then once you're sure the string you want to pass is constructed correctly you can call it like this from the JS:
thisMovie( "theFlashElementID" ).giveMeMyStringAlready();
Then in your Flash you would have something like this:
if( ExternalInterface.available )
{
ExternalInterface.addCallback( "giveMeMyStringAlready", handleTheStringFromJS );
}
else
{
handleTheFactIDontHaveExternalInterfaceAvailable();
// the only reason this would be is if container that
// is embedding the swf isn't fully loaded by the browser
}
The standout line from the AS3 docs regarding ExternalInterface is this:
Note: When using the External API with
HTML, always check that the HTML has
finished loading before you attempt to
call any JavaScript methods.
This:
Unfortunately, the HTML code is
actually working within a 3rd party
HTML generator
is the problem.
The swf is sitting inside a <form> tag.
At the browser stage, there is a huge volume of really verbose code, and I missed the tags at the very beginning and end of the html code.
Thanks for the help. If I have learnt nothing else from the experience, it's to be full with the question and look well beyond the immediate problem, breaking each element down as fully as I can.

How to properly unload/destroy a VIDEO element

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

Categories

Resources