Home Reference Source

js/plugins/ffmpegjs-plugin.js

/**
 * @file ffmpegjs-plugin.js
 * @since 3.8.0
 */

import videojs from 'video.js';

const ConvertEngine = videojs.getComponent('ConvertEngine');

/**
 * Converter engine using the ffmpeg.js library.
 *
 * Deprecated. Use the ffmpeg.wasm plugin instead.
 *
 * @class
 * @augments videojs.ConvertEngine
 */
class FFmpegjsEngine extends ConvertEngine {
    /**
     * Creates an instance of this class.
     *
     * @param  {Player} player
     *         The `Player` that this class should be attached to.
     *
     * @param  {Object} [options]
     *         The key/value store of player options.
     */
    constructor(player, options) {
        super(player, options);

        /**
         * Enables console logging for debugging purposes.
         *
         * @type {boolean}
         */
        this.debug = false;
        /**
         * Path to worker script `ffmpeg-worker-mp4.js` (H.264 & AAC & MP3 encoders)
         * or `ffmpeg-worker-webm.js` (VP8 & Opus encoders).
         *
         * @type {string}
         */
        this.convertWorkerURL = 'ffmpeg-worker-mp4.js';
        /**
         * Mime-type for output.
         *
         * @type {string}
         */
        this.outputType = null;
        /**
         * Additional configuration options for the ffmpeg.js library.
         *
         * @type {object}
         */
        this.pluginLibraryOptions = {};
    }

    /**
     * Setup recording engine.
     *
     * @param {Object} mediaType - Object describing the media type of this
     *     engine.
     * @param {Boolean} debug - Indicating whether or not debug messages should
     *     be printed in the console.
     */
    setup(mediaType, debug) {
        this.mediaType = mediaType;
        this.debug = debug;
        this.stdout = this.stderr = '';

        // set output mime type
        if (this.pluginLibraryOptions.outputType === undefined) {
            throw new Error('no outputType specified!');
        }
        this.outputType = this.pluginLibraryOptions.outputType;

        // setup worker
        this.engine = new Worker(this.convertWorkerURL);
        this.engine.onmessage = this.onWorkerMessage.bind(this);
    }

    /**
     * Invoked when recording is stopped and resulting stream is available.
     *
     * @param {blob} data - Reference to the recorded `Blob` that needs to be
     *     converted.
     */
    convert(data) {
        // save timestamp
        this.timestamp = new Date();
        this.timestamp.setTime(data.lastModified);

        // load and convert blob
        this.loadBlob(data).then((buffer) => {
            // specify input
            let opts = ['-i', data.name];

            // add ffmpeg options
            opts = opts.concat(this.convertOptions);

            // use a temporary name
            opts.push('output_' + this.timestamp.getTime());

            // start conversion
            this.engine.postMessage({
                type: 'run',
                MEMFS: [{name: data.name, data: buffer}],
                arguments: opts
            });
        });
    }

    /**
     * Received a message from the worker.
     *
     * @param {Object} event - Event containing converted data.
     * @private
     */
    onWorkerMessage(event) {
        let msg = event.data;
        switch (msg.type) {
            // worker loaded and ready to accept commands
            case 'ready':
                break;

            // worker started job
            case 'run':
                // notify listeners
                this.player().trigger('startConvert');
                break;

            // job finished with some result
            case 'done':
                // converted data
                let buf;
                try {
                    buf = msg.data.MEMFS[0].data;
                } catch (e) {
                    this.player().trigger('error', this.stderr);
                }

                // store in blob
                let result = new Blob(buf, {type: this.outputType});

                // inject date and name into blob
                this.addFileInfo(result, this.timestamp);

                // store result
                this.player().convertedData = result;

                // notify listeners
                this.player().trigger('finishConvert');
                break;

            // FFmpeg printed to stdout
            case 'stdout':
                this.stdout += msg.data + '\n';
                break;

            // FFmpeg printed to stderr
            case 'stderr':
                this.stderr += msg.data + "\n";
                break;

            // FFmpeg exited
            case 'exit':
                break;

            // FFmpeg terminated abnormally (e.g. out of memory, wasm error)
            case 'abort':
            case 'error':
                this.player().trigger('error', msg.data);
                break;
        }
    }
}

// expose plugin
videojs.FFmpegjsEngine = FFmpegjsEngine;

export default FFmpegjsEngine;