HTML5 video preload solution - javascript

I'm creating a website with some animations. One of them is the logo animation. It's called 'lbv.mp4'. As it has some weight, there's a lag with autoplay, so I decided to show content when it is fully loaded.
The plan is:
the video is checked for loading
start the video
add a class to the page element to trigger the animation
setTimeout for the length of the video which will make visibility: hidden for the clip to open a static image underneath.
This is better described in the following code:
video.addEventListener('loadeddata', function () {
if (video.readyState === 4) {
video.play();
$('#page').addClass('transition');
document.setTimeout(function(){
video.attr('style', 'visibility: hidden');
}, 750);
}
The only problem is that I can't get it working neither with pure JS, neither with JQuery. The video isn't loading, the classes aren't given. Tested in Safari, Chrome and Firefox.
So the final question is: 'Is there another to make it easier, or how to fix my solution?'

Event loadeddata means :
The first frame of the media has finished loading.
readyState === 4 means :
Enough data is available—and the download rate is high enough—that the media can be played through to the end without interruption.
You have chances that loadeddata is triggered but readyState is not 4.
Since loadeddata is triggered only once, the video won't ever play.
You should try to add logs to verify this assumption.
What I would try is the following :
Use <video> with autoplay
Listen for the playing event on the video to start transition
Listen for the ended event on the video to hide it
Reference for attributes, events and readyState:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Video
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState

Related

Why does a <video> stop playing when removed from the DOM, while it can still play detached?

I was working on a custom component and stumbled upon this strange behavior. Basically, it is possible to play a video file without adding a <video> element to the DOM at all.
const video = document.createElement('video');
video.src = "https://upload.wikimedia.org/wikipedia/commons/transcoded/c/c6/Video_2017-03-19_23-01-33.webm/Video_2017-03-19_23-01-33.webm.480p.webm";
function start()
{
if (video.paused)
{
video.play();
console.log('paused', video.paused);
}
}
<div><button onclick='start()'>Start</button></div>
Anyway, if at some point the element is added to the DOM tree and subsequently removed, then the video pauses automatically (!)
const video = document.createElement('video');
video.src = "https://upload.wikimedia.org/wikipedia/commons/transcoded/c/c6/Video_2017-03-19_23-01-33.webm/Video_2017-03-19_23-01-33.webm.480p.webm";
function start()
{
if (video.paused)
{
video.play().then(
() =>
setTimeout(
() =>
{
document.body.removeChild(video);
setTimeout(() => console.log('paused after', video.paused), 0);
},
3000
)
);
document.body.appendChild(video);
console.log('paused before', video.paused);
}
}
<div><button onclick='start()'>Start</button></div>
The same considerations apply to <audio> elements, too.
I have two questions here:
What part of the specification indicates that when a video element is removed from the DOM then it should stop playing?
What is the rationale for allowing detached videos to play but stopping videos when they are removed from the DOM tree? This is the part that surprises me most. If there is a use case for playing detached videos on a page, then why would subsequently detaching a video stop playback? On the other hand, if a detached video is stopped because there is no reason to keep playing it, then why allow it to start detached in first place?
The specification is confusing. First it says:
Media elements that are potentially playing while not in a document must not play any video, but should play any audio component. Media elements must not stop playing just because all references to them have been removed; only once a media element is in a state where no further audio could ever be played by that element may the element be garbage collected.
If I understand this correctly, removing the element from the DOM should stop the video, but the audio should continue.
But later it says:
When a media element is removed from a Document, the user agent must run the following steps:
Await a stable state, allowing the task that removed the media element from the Document to continue. The synchronous section consists of all the remaining steps of this algorithm. (Steps in the synchronous section are marked with ⌛.)
⌛ If the media element is in a document, return.
⌛ Run the internal pause steps for the media element.
Step 3 says to pause the media.
Step 2 seems redundant -- how can the element be in a document if it's being removed from the document? But step 1 could add it back to the document (or a different document). That's why it requires waiting for a stable state (which is why you needed to use setTimeout() in your example).
I think the second quote takes precedence, because when the pause steps are run, the element is no longer "potentially playing".

Detecting video resolution changes

With some codecs and containers, it's possible for a video to change resolution mid-stream. This is particularly common with RTC-style video streams where resolution can scale up/down based on available bandwidth. In other cases, the recording device might be rotated and the video may flip from portrait to landscape or vice versa.
When playing these videos on a web page (simple <video> tag), how can I detect when this change in size occurs with JavaScript?
The best I can think of is verifying the size of the video every frame, but there is quite a bit of overhead to this method. If there were a way to have a callback fired when the video changed sizes, or an event triggered, that'd be best.
Example video that resizes, severely: https://bugzilla.mozilla.org/attachment.cgi?id=8722238
There is now a resize event which fires when the video resolution changes.
HTML
<p data-content="resolution"></p>
<video src="https://bug1250345.bmoattachments.org/attachment.cgi?id=8722238"></video>
JavaScript
document.querySelector('video').addEventListener('resize', (e) => {
document.querySelector('[data-content="resolution"]').textContent = [
e.target.videoWidth,
e.target.videoHeight
].join('x');
});
(JSFiddle: http://jsfiddle.net/qz61o2xt/)
References:
HTML Living Standard Media Events - resize
Chromium Bug: Add a 'resize' event to elements for when the video data changes dimensions
Chromium Bug: Fire 'resize' event on initial metadata load, too.
look if this helps I'm not sure.
Link - https://www.w3.org/2010/05/video/mediaevents.html
You can use loadedmetadata event to define global variables or utilize Element.dataset to reflect initial .videoWidth, .videoHeight properties of <video> element; at timeupdate event of <video> initially stored and current event .videoWidth, .videoHeight values, if one of the properties changed call function
window.onload = function() {
function handleResolutionChange(event) {
// do stuff if `.videoWidth` or `.videoHeight` changed from initial value
}
var video = document.querySelector("video");
video.addEventListener("loadedmetadata", function(event) {
event.target.dataset.width = event.target.videoWidth;
event.target.dataset.height = event.target.videoHeight;
})
video.addEventListener("timeupdate", function(event) {
if (+event.target.dataset.width !== event.target.videoWidth
&& +event.target.dataset.height !== event.target.videoHeight) {
// call `handleResolutionChange` one or more times
// if `event.target` `.videoWidth` or `.videoHeight` changed
handleResolutionChange.call(event.target, event)
}
})
}

Mediaelement.js wont play with Flash-fallback

I'm using Mediaelement.js to play some video and using javascript to get autoplay working. It works perfectly in Chrome, and IE10, but when it comes to Firefox and IE8 I have a problem with the flash fallback. The following works in Chrome:
jQuery('video,audio').mediaelementplayer();
if(autoPlay == "true") {
player = new MediaElementPlayer("#"+currentPage+" video,audio");
player.play();
}
IE8 returns the following:
And firefox returns no errors, but if I add an alert(alert("hallo");) in front of player.play(), it plays when I dismiss the alert-box.
I can't add fiddle, because of heavy use of XML.
The player isn't loaded up and ready to play when the script presses the play button.
The script needs to press the play button inside the success function in the mediaelement instance creation.
See here: How do I get mediaelement.js player state (paused, volume, etc.)?
Some browsers (webkit specifically) may trigger the play() method before the video is completely ready and the video may just hang while loading.
I would advice to add an event listener to detect when the video can actually play before triggering the play() method like :
success : function (media, domObject) {
media.addEventListener('canplay', function () {
media.play();
}, false);
} // success
Yeah sorry, solved it a half year later:
As mentioned, the play event must be invoked in the success function
jQuery("video,audio").mediaelementplayer();
if(autoPlay == "1") {
media = jQuery("#"+currentPage+" video,audio")[0];
new MediaElement(media, {success: function(media) {
media.play();
}});
}

HTML5 video - Play event not firing

I currently have an HTML5 video event issue in Safari. I am playing a single video on my page. The video loads and plays correctly. However, the play event does not always fire. If the user:
Clicks play
Watches the video to the end (ended event fires)
Clicks play again
The play event does not fire on the second click. If I pause/play the movie at that time, the correct events fire.
How can I make the video tag's play event fire if the video has completed and the user presses play again?
drawVidPlayer is called with the videos index as part of the page render
function drawVidPlayer(vindex){
var turl=vidList[vindex]['thumbUrl'];
var vurl=vidList[vindex]['url'];
var valias=vidList[vindex]['type'];
destroyVidPlayer();
$('#mediaspot').css('backgroundColor', '#000000');
$('#mediaspot').show();
$('#mediaspot').html('<video controls="controls" id="twnvideo" poster="'+turl+'" style="height:225px; width:460px;"><source src="'+vurl+'" type="video/ogg" /><source src="'+vurl+'" type="video/mp4" /><source src="'+vurl+'" type="video/webm" />Your browser does not support the video tag.</video>').appendTo('#wrap_media_vod');
var velem=document.getElementsByTagName('video')[0];
velem.addEventListener('play', initVidTimer, false);
velem.addEventListener('pause', killVidTimer, false);
velem.addEventListener('ended', killVidTimer, false);
}
function destroyVidPlayer(){
var velem=document.getElementsByTagName('video')[0];
if(velem!=undefined){
velem.removeEventListener('play', initVidTimer);
velem.removeEventListener('pause', killVidTimer);
velem.removeEventListener('ended', killVidTimer);
}
$('#mediaspot').empty();
$('#mediaspot').html('');
}
function initVidTimer(){
if(activityTimer==null){
external.OnUserActivity(19);
activityTimer=setInterval(function(){
external.WriteLog('activity timer running');
external.OnUserActivity(19);
}, 5000);
}
}
function killVidTimer(){
clearInterval(activityTimer);
activityTimer=null; // Kill keepAlive timer
var velem=document.getElementsByTagName('video')[0];
external.WriteLog(velem.ended);
}
HTML5 now specifies that the browser must throw the timeupdate, paused, and ended events when the playback position reaches the end of a media file, but the spec wasn't always that clear. As a result, this behavior is inconsistent between browsers. Some don't set paused=true or fire the paused event when the file ends.
In your Safari issue, paused is still equal to false when the video starts to play for the second time - so there is no reason for the browser to fire the play event again.
This may no longer be an issue in Safari 6, but it still breaks in IE 9. Take a look at the End Events column in this chart from longtailvideo.com - they outline the inconsistencies well.
It would easy to normalize this issue with a couple lines of code - like this:
$("video").on("ended", function () {
if (!this.paused) this.pause();
});
This puts the video in the paused state on ended, so it will throw the play event correctly on replay.
You can try this working sample in IE 9 or to see what I mean on this jsfiddle: http://jsfiddle.net/PWnUb/
I had the same issue, I solved with a bit of jquery:
function videoend(){
var duration = $("video").get(0).duration;
var current = $("video").get(0).currentTime;
if(current==duration){
//Whatever you wanna do when video ends
};
}
$(document).ready(function(){
setInterval("videoend()", 200); //or any other time you wanna use
});
Hope this helps.

html5 video "ended" event not working in chrome and IE

I have two buttons on a page that trigger two functions that make two html5 video play, hide and show some elements (including themselves), and call another simple function on ended (that causes the video to go to the first frame and pause, for the effect to work properly).
$('#rotate').click(function rotate(){
$('#rotate').hide();
$('#front_view').css('z-index','2');
$('#back_view').css('z-index','3');
//this is the video:
$('#body_animation').trigger("play").show().bind('ended', function () {
$('#back_view').show();
$('#front_view').fadeOut(500);
$(this).hide();
this.currentTime = 0;
this.pause();
});
$('#rotate_reverse').delay(2000).fadeIn(0);
});
This works fine in firefox and safari, but in chrome and IE something strange happens. The first time the page loads, the "ended" event doesn't seem work. It works fine if you refresh the site (or if you run it offline), though.
You can check the code in here, I narrowed all the site to this problem, so you can see it better:
http://www.hidden-workshop.com/test/
The actual videos and images are different, but the problem is the same. I'm busting my head trying to solve this thing, but I can't find the answer anywhere.
Thanks in advance!!
Your test page isn't live anymore, so I can't check this, but I found that if looping is enabled for the tag (e.g., <video loop="loop">), the "ended" event wasn't firing in Chrome or IE (I didn't test in Firefox). Once I removed the loop attribute, the "ended" event fired in both browsers.
HTML (with loop attribute, which will prevent the 'ended' event form firing):
Remove the loop attribute if you want the 'ended' event to fire...
<video id="video" loop="loop">
<source src="whatever.mp4" width="320" height="240" type="video/mp4" />
Your browser does not support this HTML5 video tag.
</video>
Javascript:
//NOTE: This only fires if looping is disabled for the video!
$("#video").bind("ended", function() {
alert('Video ended!');
});
If you are dynamically adding a <video> to your page that wasn't present in the HTML served from the server you may encounter a race condition that can result in the ended event not being recognized - even if it was added correctly.
I am using knockoutjs to add a different template for phone vs desktop.
<div data-bind="template: { name: videoTemplate, afterRender: initializeVideo }">
This dynamically creates me a <video> element and then calls initializeVideo() after the template is rendered (added to the DOM) to bind the events :
$('video#flexVideo').off('ended').on('ended', (evt) =>
{
alert('ended fired');
}).css('border', '2px solid yellow'); // add yellow border so we know element existed
On my screen the video gets a yellow border (proving the video was present in the DOM and there were no typos). However for some reason the browser isn't yet able to attach the ended event to it - I assume perhaps it was not initialized yet.
Chrome debugging tools shows that it is added, but it doesn't actually work!
It does this in Firefox, Chrome + IE10 which was slightly surprising.
One solution is this:
$('video#flexVideo').off('loadstart').on('loadstart', (evt) =>
{
$('video#flexVideo').off('ended').on('ended', (evt) =>
{
alert('ended fired');
}).css('border', '2px solid yellow');
}).css('border', '2px solid orange');
The loadstart event seems to be bindable right away.
Another is perhaps just a setTimeout - or just bind the event on play.

Categories