HTML5 video error handling

2020-02-03 10:59发布

问题:

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 ?

回答1:

"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



回答2:

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


回答3:

To catch error event, you should use video.addEventListner():

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 descendatns 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.



回答4:

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.



回答5:

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.



回答6:

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'; }