Home Reference Source

js/plugins/ffmpeg-wasm-plugin.js

/* eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
/**
 * @file ffmpeg-wasm-plugin.js
 * @since 4.2.0
 */

import videojs from 'video.js';

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

/**
 * Converter engine using the ffmpeg.wasm library.
 *
 * @class
 * @augments videojs.ConvertEngine
 */
class FFmpegWasmEngine 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 script `ffmpeg-core.js`.
         *
         * @type {string}
         */
        this.coreURL = '/node_modules/@ffmpeg/core-mt/dist/umd/ffmpeg-core.js';
        /**
         * Path to script `ffmpeg-core.worker.js`.
         *
         * @type {string}
         */
        this.convertWorkerURL = '/node_modules/@ffmpeg/core-mt/dist/umd/ffmpeg-core.worker.js';
        /**
         * Path to script `ffmpeg-core.wasm`.
         *
         * @type {string}
         */
        this.audioWebAssemblyURL = '/node_modules/@ffmpeg/core-mt/dist/umd/ffmpeg-core.wasm';
        /**
         * Mime-type for output.
         *
         * @type {string}
         */
        this.outputType = null;
        /**
         * Additional configuration options for the ffmpeg.wasm library.
         *
         * @type {object}
         */
        this.pluginLibraryOptions = {};

        this.ffmpeg = null;
    }

    /**
     * Inject metadata.
     *
     * @param {Blob} data - Recorded data that needs to be converted.
     */
    async convert(data) {
        // set output mime type
        if (this.pluginLibraryOptions.outputType === undefined) {
            throw new Error('no outputType specified!');
        }
        this.outputType = this.pluginLibraryOptions.outputType;

        // setup ffmpeg.wasm
        const {fetchFile} = FFmpegUtil;
        const {FFmpeg} = FFmpegWASM;
        if (this.ffmpeg === null) {
            this.ffmpeg = new FFmpeg();

            if (this.debug) {
                this.ffmpeg.on('log', ({message}) => {
                    console.log(message);
                });
            }

            await this.ffmpeg.load({
                coreURL: this.coreURL,
                wasmURL: this.audioWebAssemblyURL,
                workerURL: this.convertWorkerURL,
            });
        }

        // save timestamp
        const timestamp = new Date();
        timestamp.setTime(data.lastModified);

        // use temporary filenames
        const tempInputName = 'input_' + timestamp.getTime();
        const tempOutputName = 'output_' + timestamp.getTime();

        // add ffmpeg options
        let opts = ['-i', tempInputName];
        opts = opts.concat(this.convertOptions);
        opts.push(tempOutputName);

        // notify listeners
        this.player().trigger('startConvert');

        // load and convert blob
        await this.ffmpeg.writeFile(tempInputName, await fetchFile(data));
        console.log(opts);
        await this.ffmpeg.exec(opts);
        const output = await this.ffmpeg.readFile(tempOutputName);

        // create new blob
        let result = new Blob([output.buffer], {type: this.outputType});

        // add existing file info
        this.addFileInfo(result, timestamp);

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

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

// expose plugin
videojs.FFmpegWasmEngine = FFmpegWasmEngine;

export default FFmpegWasmEngine;