1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | 'use strict';
|
9 |
|
10 |
|
11 |
|
12 | var Ssim = require('./ssim');
|
13 |
|
14 | function VideoFrameChecker(videoElement) {
|
15 | this.frameStats = {
|
16 | numFrozenFrames: 0,
|
17 | numBlackFrames: 0,
|
18 | numFrames: 0
|
19 | };
|
20 |
|
21 | this.running_ = true;
|
22 |
|
23 | this.nonBlackPixelLumaThreshold = 20;
|
24 | this.previousFrame_ = [];
|
25 | this.identicalFrameSsimThreshold = 0.985;
|
26 | this.frameComparator = new Ssim();
|
27 |
|
28 | this.canvas_ = document.createElement('canvas');
|
29 | this.videoElement_ = videoElement;
|
30 | this.listener_ = this.checkVideoFrame_.bind(this);
|
31 | this.videoElement_.addEventListener('play', this.listener_, false);
|
32 | }
|
33 |
|
34 | VideoFrameChecker.prototype = {
|
35 | stop: function() {
|
36 | this.videoElement_.removeEventListener('play' , this.listener_);
|
37 | this.running_ = false;
|
38 | },
|
39 |
|
40 | getCurrentImageData_: function() {
|
41 | this.canvas_.width = this.videoElement_.width;
|
42 | this.canvas_.height = this.videoElement_.height;
|
43 |
|
44 | var context = this.canvas_.getContext('2d');
|
45 | context.drawImage(this.videoElement_, 0, 0, this.canvas_.width,
|
46 | this.canvas_.height);
|
47 | return context.getImageData(0, 0, this.canvas_.width, this.canvas_.height);
|
48 | },
|
49 |
|
50 | checkVideoFrame_: function() {
|
51 | if (!this.running_) {
|
52 | return;
|
53 | }
|
54 | if (this.videoElement_.ended) {
|
55 | return;
|
56 | }
|
57 |
|
58 | var imageData = this.getCurrentImageData_();
|
59 |
|
60 | if (this.isBlackFrame_(imageData.data, imageData.data.length)) {
|
61 | this.frameStats.numBlackFrames++;
|
62 | }
|
63 |
|
64 | if (this.frameComparator.calculate(this.previousFrame_, imageData.data) >
|
65 | this.identicalFrameSsimThreshold) {
|
66 | this.frameStats.numFrozenFrames++;
|
67 | }
|
68 | this.previousFrame_ = imageData.data;
|
69 |
|
70 | this.frameStats.numFrames++;
|
71 | setTimeout(this.checkVideoFrame_.bind(this), 20);
|
72 | },
|
73 |
|
74 | isBlackFrame_: function(data, length) {
|
75 |
|
76 | var thresh = this.nonBlackPixelLumaThreshold;
|
77 | var accuLuma = 0;
|
78 | for (var i = 4; i < length; i += 4) {
|
79 |
|
80 | accuLuma += 0.21 * data[i] + 0.72 * data[i + 1] + 0.07 * data[i + 2];
|
81 |
|
82 | if (accuLuma > (thresh * i / 4)) {
|
83 | return false;
|
84 | }
|
85 | }
|
86 | return true;
|
87 | }
|
88 | };
|
89 |
|
90 | module.exports = VideoFrameChecker;
|