graphics/webvideo.js

'use strict';

var Thing = require('./thing.js');

var DEFAULT_WIDTH = 150;
var DEFAULT_HEIGHT = (DEFAULT_WIDTH * 3) / 4;

const WEBCAM_INDICATOR = 'WEBCAM';

/**
 * @constructor
 * @augments Thing
 * @param {string} filename - Filepath to the video
 */
function WebVideo(filename) {
    if (typeof filename !== 'string') {
        throw new TypeError(
            'You must pass a string to <span class="code">' +
                "new WebVideo(filename)</span> that has the video's location."
        );
    }
    Thing.call(this);
    var self = this;

    var vid = document.createElement('video');
    this.width = DEFAULT_WIDTH;
    this.height = DEFAULT_HEIGHT;
    this.type = 'WebVideo';

    this.isWebCam = filename === WEBCAM_INDICATOR;

    this.browserSupportsVideo = !!vid.canPlayType;
    if (this.browserSupportsVideo) {
        this.video = vid;
        if (!this.isWebCam) {
            this.video.src = filename;
        } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
            navigator.mediaDevices
                .getUserMedia({video: true})
                .then(function(stream) {
                    self.video.srcObject = stream;
                    self.video.play();
                })
                .catch(function(error) {
                    throw new Error('Web camera access was denied: ' + error);
                });
        } else {
            throw new TypeError('Your browser does not support web camera access');
        }
        this.filename = filename;
        this.video.autoplay = true;
        this.video.loop = false;

        // Treat cross origin URLs as same origin. Allows for videos from different
        // origins to be loaded and played, as long as that origin allows us to load
        // the given video resource.
        this.video.crossOrigin = 'anonymous';
    }
}

WebVideo.WEBCAM = WEBCAM_INDICATOR;

WebVideo.prototype = new Thing();
WebVideo.prototype.constructor = WebVideo;

/**
 * Draws the WebVideo in the canvas.
 *
 * @param {CodeHSGraphics} __graphics__ - Instance of the __graphics__ module.
 */
WebVideo.prototype.draw = function(__graphics__) {
    if (this.browserSupportsVideo) {
        var context = __graphics__.getContext('2d');

        // Scale and translate
        // X scale, X scew, Y scew, Y scale, X position, Y position
        context.setTransform(1, 0, 0, 1, this.x + this.width / 2, this.y + this.height / 2);
        context.rotate(this.rotation);

        context.drawImage(this.video, -this.width / 2, -this.height / 2, this.width, this.height);

        // Reset transformation matrix
        // X scale, X scew, Y scew, Y scale, X position, Y position
        context.setTransform(1, 0, 0, 1, 0, 0);
    }
};

/**
 * Checks if the passed point is contained in the WebVideo.
 *
 * @param {number} x - The x coordinate of the point being tested.
 * @param {number} y - The y coordinate of the point being tested.
 * @returns {boolean} Whether the passed point is contained in the WebVideo.
 */
WebVideo.prototype.containsPoint = function(x, y) {
    if (this.browserSupportsVideo) {
        return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height;
    }
    return false;
};

/**
 * Gets the width of the WebVideo.
 *
 * @returns {number} Width of the WebVideo.
 */
WebVideo.prototype.getWidth = function() {
    return this.width;
};

/**
 * Gets the height of the WebVideo.
 *
 * @returns {number} Height of the WebVideo.
 */
WebVideo.prototype.getHeight = function() {
    return this.height;
};

/**
 * Sets the size of the WebVideo.
 *
 * @param {number} width - The desired width of the resulting WebVideo.
 * @param {number} height - The desired height of the resulting WebVideo.
 */
WebVideo.prototype.setSize = function(width, height) {
    this.width = width;
    this.height = height;
};

/**
 * Sets whether the WebVideo should start playing automatically once loaded.
 *
 * @param {boolean} autoplay - True/false whether the video should start playing automatically.
 */
WebVideo.prototype.setAutoplay = function(autoplay) {
    if (this.browserSupportsVideo) {
        this.video.autoplay = autoplay;
    }
};

/**
 * Sets whether the WebVideo should loop and play again once finished.
 *
 * @param {boolean} loop - True/false whether the video should loop.
 */
WebVideo.prototype.setLoop = function(loop) {
    if (this.browserSupportsVideo) {
        this.video.loop = loop;
    }
};

/**
 * Sets whether the WebVideo is muted or not.
 *
 * @param {boolean} muted - True/false whether the video should be muted.
 */
WebVideo.prototype.setMuted = function(muted) {
    if (this.browserSupportsVideo) {
        this.video.muted = muted;
    }
};

/**
 * Starts playing the WebVideo.
 */
WebVideo.prototype.play = function() {
    if (this.browserSupportsVideo) {
        this.video.play();
    }
};

/**
 * Pauses the WebVideo.
 */
WebVideo.prototype.pause = function() {
    if (this.browserSupportsVideo) {
        this.video.pause();
    }
};

/**
 * Stops the WebVideo.
 */
WebVideo.prototype.stop = function() {
    if (this.browserSupportsVideo) {
        this.video.pause();
        this.video.currentTime = 0;

        if (this.isWebCam && this.video.srcObject) {
            this.video.srcObject.getTracks().forEach(function(track) {
                track.stop();
            });
        }
    }
};

/**
 * Returns whether the WebVideo is currently playing.
 *
 * @returns {boolean} True if the video is playing, false if it is not.
 */
WebVideo.prototype.isPlaying = function() {
    if (this.browserSupportsVideo) {
        return !(this.video.paused || this.video.ended);
    }
    return false;
};

/**
 * Returns whether the WebVideo is currently muted.
 *
 * @returns {boolean} True if the video is muted, false if it is not.
 */
WebVideo.prototype.isMuted = function() {
    if (this.browserSupportsVideo) {
        return this.video.muted;
    }
    return false;
};

/**
 * Defines a function to call once the video has loaded enough and is ready to play.
 * @param  {Function} fn A function to call when the video is ready to play.
 */
WebVideo.prototype.onReadyToPlay = function(fn) {
    if (this.browserSupportsVideo) {
        this.video.oncanplay = fn;
    }
};

module.exports = WebVideo;