HTML5 video error handling - html

I need to tell, whether video cannot be played ("x" sign is shown in browser).
This code does't works. "onerror" event will never be fired under Firefox
var v = document.getElementsByTagName("video")[0];
if ( v != undefined )
v.onerror = function(e) {
if ( v.networkState == v.NETWORK_NO_SOURCE )
{
// handle error
}
}
What's wrong here ?

"onerror" is not a valid event type for <video>
Use "error" instead.
document.getElementsByTagName('video')[0].addEventListener('error', function(event) { ... }, true);
For a complete list of events for <video> go here: https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox

From Firefox 4 onwards, the 'error' event is dispatched on the <source> element.
And you should add an error handler on the only/last source:
HTML
<video id="vid" controls>
<source src="dynamicsearch.mp4" type="video/mp4"></source>
<source src="otherdynamicsearch.avi" type="video/avi"></source>
</video>
JS
var v = document.querySelector('video#vid');
var sources = v.querySelectorAll('source');
if (sources.length !== 0) {
var lastSource = sources[sources.length-1];
lastSource.addEventListener('error', function() {
alert('uh oh');
});
}
JQuery
$('video source').last().on('error', function() {
alert('uh oh');
});
AngularJS
You can create an error handling directive (or just use ng-error):
<video id="vid" controls>
<source src="dynamicsearch.mp4" type="video/mp4"></source>
<source src="otherdynamicsearch.avi" type="video/avi" ng-error="handleError()"></source>
</video>
Where the error handling directive's link function should do (copied from ng-error):
element.on('error', function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});

It's good to know that Chrome and Firefox have different onerror callbacks. The error must therefore be mapped. Mozilla uses error.originalTarget.
Here is a sample on how to do it with pure JavaScript:
const file = 'https://samples.ffmpeg.org/MPEG-4/MPEGSolution_jurassic.mp4';
window.fetch(file, {mode: 'no-cors'})
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob);
const video = document.createElement('video');
video.addEventListener('error', (event) => {
let error = event;
// Chrome v60
if (event.path && event.path[0]) {
error = event.path[0].error;
}
// Firefox v55
if (event.originalTarget) {
error = error.originalTarget.error;
}
// Here comes the error message
alert(`Video error: ${error.message}`);
window.URL.revokeObjectURL(url);
}, true);
video.src = url;
document.body.appendChild(video);
});
The above example maps an incoming error event into a MediaError which can be used to display an error playback message.

To catch error event, you should use video.addEventListener():
var video = document.createElement('video');
var onError = function() { // your handler};
video.addEventListener('error', onError, true);
...
// remove listener eventually
video.removeEventListener('error', onError, true);
Note that the 3rd parameter of addEventListener (on capture) should be set to true. Error event is typically fired from descendants of video element ( tags).
Anyway, relying on video tag to fire an error event is not the best strategy to detect if video has played. This event is not fired on some android and iOS devices.
The most reliable method, I can think of, is to listen to timeupdate and ended events. If video was playing, you'll get at least 3 timeupdate events. In the case of error, ended will be triggered more reliably than error.

Try adding the event listener to the tag instead - I think the onerror attribute ("error" event) works on the source tag now, not the video tag.

Pug example
video(src= encodeURI(item.urlVideo), type='video/mp4' onerror="myFunction('param',this)")
script(src='/javascripts/onerror.js')
function myFunction(param, me) {
console.log(me);
me.poster = './images/placeholder.jpg'; }

Related

MediaStream events active and inactive are not triggered in a remote WebRTC Peer Connection

I have a remote MediaStream object obtained by a remote WebRTC Peer Connection.
I want to check when the remote MediaStream becomes inactive (indipendently by the reason).
I have read that for this purpose I should use the events active and inactive of the MediaStream object.
But these two events are never triggered: even if I set a specific handler for these two events, the handlers are never executed.
Here my implementation:
function onRemoteStream(event) {
event.stream.addEventListener("active", function(){
console.log('The video is active');
}, false);
event.stream.addEventListener("inactive", function(){
console.log('The video is not active');
}, false);
remoteVideo.src = window.URL.createObjectURL(event.stream);
}
The two messages are never showed.
I also tried:
function onRemoteStream(event) {
event.stream.onactive = function(){
console.log('The video is active');
};
event.stream.oninactive = function(){
console.log('The video is not active');
}
remoteVideo.src = window.URL.createObjectURL(event.stream);
}
But the behaviour is the same.
I don't understand why the two events are not triggered.
I'm using Google Chrome 52.0.2743.116 m
No it is working properly, it is just that you cannot directly write a function for that event (As per the documentation which is now removed). You can also log your event.stream object in console and see the details. You have to use predefined properties onactive and oninactive
I have made a quick code to test how to trigger these events.
HTML
<video src="" autoplay id="video"></video>
JavaScript
var video = document.getElementById('video')
function onactive() {
console.log("on active event");
}
function oninactive() {
console.log("on inactive event");
}
navigator.getUserMedia({video:true, audio:false}, function (stream) {
stream.onactive = onactive;
stream.oninactive = oninactive;
video.src = window.URL.createObjectURL(stream);
}, function (error) {
console.log(error);
})

Angularjs HTML5 Video onended

I am loading html5 mp4 video and I want to trigger function from angular scope when video end. I tried below simple code but onended event cannot find the function in angular scope.
HTML
<video controls="controls" autoplay="true" ng-show="showVideo" ng-src="{{vidSrc}}" vid-dir onended="vidEnded()">
Angularjs function added in main controller. onended event triggered but function is undefined
$scope.vidEnded = function(){
console.log('vid ended')
}
Also tried adding function in directory like this but the function is not triggered.
.directive('vidDir', [function () {
return {
restrict: 'A',
link: function (scope, elem, attr) {
console.log(elem)
elem.onended = function(){
console.log('vid ended')
}
}
};
}]);
I believe the following code would achieve what you desire.
<video controls="controls" autoplay="true" ng-show="showVideo" ng-src="{{vidSrc}}" vid-dir (ended)="vidEnded()">
Add the following in front of your angular function:
angular.element(this).scope().vidEnded()
The result:
<video controls="controls" autoplay="true" ng-show="showVideo" ng-src="{{vidSrc}}" vid-dir onended="angular.element(this).scope().vidEnded()">
Another option would be to add a separate directive on-ended instead of the actual onended, like this:
/**
* Handles onended event for video tags.
* #example <video on-ended="awesomeFn()">
*/
.directive('onEnded', function () {
return {
scope: {
onEnded: '&'
},
link: function (scope, element) {
element.on('ended', scope.onEnded);
}
};
});
use this.vidEnded();
.directive('vidDir', [function () {
return {
restrict: 'A',
link: function (scope, elem, attr) {
console.log(elem)
this.vidEnded= function(){
console.log('vid ended')
}
}
};
}]);
Worked at this issue for a while and found a hack to the same problem that worked for me. Uses jQuery $timeOut function (you could also use the vanilla JS timeout, both are a typical hack with angular I find), and the html5 video events: "duration", "currentTime", and "timeupdate", which you can learn more about here. Using those event values you can calculate the end of the video and simulate the html5 video "ended" event. Here's the code
myCtrl.myVideo = document.getElementbyId("my-video-id"); //you need to have an id for your html5 video
//function to define in the controller
myCtrl.videoListener = function()
{
//javascript event listener function
myCtrl.myVideo.addEventListener('timeupdate',
function()
{
myCtrl.myVideo = this; //define "this"
var videoDuration = this.duration;
var videoCurrentTime = this.currentTime;
if (videoCurrentTime >= videoDuration)
{
//wrapping in a $timeOut function is the only way this works!
$timeout(function(){
//do something when the video ends, set variables, etc.
}, 0);
}
else if (videoCurrentTime < videoDuration)
{
//wrapping in a $timeOut function is the only way this works.
$timeout(function(){
//do something while the video is playing, set variables, etc.
}, 0);
}
});
}
You actually do not need a directive or plugin to run the onended event.(if jquery is integrated) Here is some logic to add in your controller. Make sure the video tag has an Id.remove the on ended attribute if you already have one in there. Then within your controller add this
var jq = $;
jq('#IdOfVideo').on('ended', function(){
//Whatever you want to happen after it has ended
});

Capturing video from web browser in HTML5

I'm trying to follow this guide on capturing video from webcam in HTML5
http://www.html5rocks.com/en/tutorials/getusermedia/intro/
I have copied and pasted the following code but Chrome does not ask for permission to use my camera
<video autoplay></video>
<script>
var onFailSoHard = function(e) {
console.log('Reeeejected!', e);
};
// Not showing vendor prefixes.
navigator.getUserMedia({video: true, audio: true}, function(localMediaStream) {
var video = document.querySelector('video');
video.src = window.URL.createObjectURL(localMediaStream);
// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
// See crbug.com/110938.
video.onloadedmetadata = function(e) {
// Ready to go. Do some stuff.
};
}, onFailSoHard);
</script>
Whereas when I click "capture video" in the guide it works, my webcam shows...
Another website has similar code but yet again it's not working for me
http://dev.opera.com/articles/view/playing-with-html5-video-and-getusermedia-support/
<!-- HTML code -->
<video id="sourcevid" autoplay>Put your fallback message here.</video>
/* JavaScript code */
window.addEventListener('DOMContentLoaded', function() {
// Assign the <video> element to a variable
var video = document.getElementById('sourcevid');
// Replace the source of the video element with the stream from the camera
if (navigator.getUserMedia) {
navigator.getUserMedia('video', successCallback, errorCallback);
// Below is the latest syntax. Using the old syntax for the time being for backwards compatibility.
// navigator.getUserMedia({video: true}, successCallback, errorCallback);
function successCallback(stream) {
video.src = stream;
}
function errorCallback(error) {
console.error('An error occurred: [CODE ' + error.code + ']');
return;
}
} else {
console.log('Native web camera streaming (getUserMedia) is not supported in this browser.');
return;
}
}, false);
I was wondering if I'm missing something or has something changed, because none of the sample code has worked for me so far.
Found out what was happening. For anyone wondering, on Chrome you only have access to the webcam if running from a server. It won't work on just a file.

How to disable seeking with HTML5 video tag ?

I know this is not advisable. But still need this feature to be implemented. Tried everything from onseeking,onseeked to media controller. Nothing worked. Are there any external libraries to disable seeking. would be helpful if some pointers on how to go about using custom controls.
The question is quite old but still relevant so here is my solution:
var video = document.getElementById('video');
var supposedCurrentTime = 0;
video.addEventListener('timeupdate', function() {
if (!video.seeking) {
supposedCurrentTime = video.currentTime;
}
});
// prevent user from seeking
video.addEventListener('seeking', function() {
// guard agains infinite recursion:
// user seeks, seeking is fired, currentTime is modified, seeking is fired, current time is modified, ....
var delta = video.currentTime - supposedCurrentTime;
if (Math.abs(delta) > 0.01) {
console.log("Seeking is disabled");
video.currentTime = supposedCurrentTime;
}
});
// delete the following event handler if rewind is not required
video.addEventListener('ended', function() {
// reset state in order to allow for rewind
supposedCurrentTime = 0;
});
JsFiddle
It is player agnostic, works even when the default controls are shown and cannot be circumvented even by typing code in the console.
Extending the answer from #svetlin-mladenov, you can do the following to prevent the user from seeking any part of the video which has not been watched yet. This will also allow the user to rewind and the seek out any part of the video which had already watched previously.
var timeTracking = {
watchedTime: 0,
currentTime: 0
};
var lastUpdated = 'currentTime';
video.addEventListener('timeupdate', function () {
if (!video.seeking) {
if (video.currentTime > timeTracking.watchedTime) {
timeTracking.watchedTime = video.currentTime;
lastUpdated = 'watchedTime';
}
//tracking time updated after user rewinds
else {
timeTracking.currentTime = video.currentTime;
lastUpdated = 'currentTime';
}
}
});
// prevent user from seeking
video.addEventListener('seeking', function () {
// guard against infinite recursion:
// user seeks, seeking is fired, currentTime is modified, seeking is fired, current time is modified, ....
var delta = video.currentTime - timeTracking.watchedTime;
if (delta > 0) {
video.pause();
//play back from where the user started seeking after rewind or without rewind
video.currentTime = timeTracking[lastUpdated];
video.play();
}
});
You could use an HTML5 video player like video.js and use CSS to hide the seek bar.
Or you could build your own controls for HTML5 video.
Also, the event you're looking for is 'seeking'. As in (with new jquery event binding):
$(myVideoElement).on('seeking', function(){
// do something to stop seeking
})
<!DOCTYPE html>
<html>
<body>
<video controls onseeking="myFunction(this.currentTime)">
<source src="mov_bbb.mp4" type="video/mp4">
<source src="mov_bbb.ogg" type="video/ogg">
Your browser does not support HTML5 video.
</video>
<p>Video courtesy of Big Buck Bunny.</p>
<script>
var currentpos = 0;
function myFunction(time) {
if(time > currentpos) {
video.currentTime = currentpos;
}
}
var video = document.getElementsByTagName('video')[0];
function getpos(){
currentpos = video.currentTime;
}
onesecond = setInterval('getpos()', 1000);
</script>
</body>
</html>
Did the trick for me :)
var supposedCurrentTime = 0;
$("video").on("timeupdate", function() {
if (!this.seeking) {
supposedCurrentTime = this.currentTime;
}
});
// prevent user from seeking
$("video").on('seeking', function() {
// guard agains infinite recursion:
// user seeks, seeking is fired, currentTime is modified, seeking is fired, current time is modified, ....
var delta = this.currentTime - supposedCurrentTime;
if (Math.abs(delta) > 0.01) {
//console.log("Seeking is disabled");
this.currentTime = supposedCurrentTime;
}
});
$("video").on("ended", function() {
// reset state in order to allow for rewind
supposedCurrentTime = 0;
});
To hide all the video controls use this -
document.getElementById("myVideo").controls = false;
Maybe it can help someone, I use this way to remove all the progress control callbacks.
player.controlBar.progressControl.off();
player.controlBar.progressControl.seekBar.off();
Assuming this is for a standard HTML5 video player and with no 3rd party JS library interfering:
This should stop them dead in their tracks, albeit over a decade late for your needs but still useful to others...
V.onseeking=function(){event.preventDefault(); alert('Please don\'t skip the tute!');};
NB: "V" being the ID of your player.
With video js as my video player instead of the vanilla HTML5 version:
.vjs-default-skin .vjs-progress-holder {
height: 100%;
pointer-events: none;
}

Html5 audio can't play again after it stopped

I declare a audio file in my page:
var audio = new Audio('my.mp3');
then I use click event to play it:
$(document).click(function () {
audio.currentTime = 0; // to make sure it play from the begain
audio.play();
})
It can play only at the first click time, after it ended, I click the document, it can not play again.How can I let it play again?
I tested this in Chrome and an exception occurs due to the audio.currentTime statement.
This worked (also in IE9):
$(document).click(function () {
audio.src = "my.mp3";
audio.play();
})