H5 worker 系列三 webworkify处理音视频解码

在H5 worker 系列一 基础知识提到过browserify/webworkify,原理可以参考WebWorker实战使用中,作者表示:

实际开发中我们不会把所有的代码都放在一个文件中让子线程加载,肯定会选择模块化开发。官方提供的方式是使用importScripts,但是这个在实际开发中很不实用,importScripts的加载方式是阻塞式的,所以我们最好用打包工具将所有worker中需要的文件打包成一个文件。这里我推荐browserify/webworkify,这是webpack的一个插件。对于webworkify-webpack的原理其实并没有使用importScripts而是使用另一种方式来创建worker,将js代码stringify后创建Blob对象,然后又createObjectURL创建对象url来实例化worker。类似如下过程:

image.png

我们在github上下载browserify/webworkify源码后,先运行一下npm i -D安装一下环境,然后browserify example/main.js -o bundle.js,就可以在index.html中使用bundle.js了,关于browserify,可以参考Browserify + watchify




    
    browserify
    




一、官方的使用Example
//main.js
var work = require('../');

var w = work(require('./worker.js'));

var first = true;
w.addEventListener('message', function (ev) {
    if (first) {
        // revoke the Object URL that was used to 
        // create this worker, so as not to leak it
        URL.revokeObjectURL(w.objectURL);
        first = false;
    }
    console.log(ev.data);
});

w.postMessage(4); // send the worker a message

//worker.js
var gamma = require('gamma');

module.exports = function (self) {
    self.addEventListener('message',function (ev){
         // ev.data=4 from main.js
        var startNum = parseInt(ev.data);
        
        setInterval(function () {
            var r = startNum / Math.random() - 1;
            self.postMessage([ startNum, r, gamma(r) ]);
        }, 500);
    });
};

二、hls.js demuxer.js的使用
在hls.js 源码解读【3】中提到

import work from 'webworkify-webpack';
if (config.enableWorker && (typeof (Worker) !== 'undefined')) {
    logger.log('demuxing in webworker');
    let w;
    try {
        w = this.w = work(require.resolve('../demux/demuxer-worker.js'));
        this.onwmsg = this.onWorkerMessage.bind(this);
        w.addEventListener('message', this.onwmsg);
        w.onerror = function (event) {
            hls.trigger(Event.ERROR, {
                type: ErrorTypes.OTHER_ERROR,
                details: ErrorDetails.INTERNAL_EXCEPTION,
                fatal: true,
                event: 'demuxerWorker',
                err: {
                    message: event.message +
                    ' (' + event.filename + ':' + event.lineno + ')'
                }
            });
        };
        w.postMessage({
            cmd: 'init',
            typeSupported: typeSupported,
            vendor: vendor,
            id: id,
            config: JSON.stringify(config)
        });
    } catch (err) {
        logger.error('error while initializing DemuxerWorker' +
            ', fallback on DemuxerInline');
        if (w) {
            // revoke the Object URL that was used to create 
            //demuxer worker, so as not to leak it
            URL.revokeObjectURL(w.objectURL);
        }
        this.demuxer = new DemuxerInline(observer, typeSupported, config, vendor);
        this.w = undefined;
    }
} else {
    this.demuxer = new DemuxerInline(observer, typeSupported, config, vendor);
}

onWorkerMessage(ev) {
    let data = ev.data,
    hls = this.hls;
    //console.log('onWorkerMessage:' + data.event);
    ...
}

关于require.resolve:

// 用法
require.resolve('a.js')
// 返回 /home/ruanyf/tmp/a.js

简单的说,在 Node.js 中使用 fs 读取文件的时候,经常碰到要拼一个文件的绝对路径的问题 (fs 处理相对路径均以进程执行目录为准)。之前一直的方法都是,使用 path 模块以及 __dirname 变量 。代码如下所示:fs.readFileSync(path.join(__dirname, './assets/some-file.txt'));使用 require.resolve 可以简化这一过程fs.readFileSync(require.resolve('./assets/some-file.txt'));此外, require.resolve 还会在拼接好路径之后检查该路径是否存在, 如果 resolve 的目标路径不存在, 就会抛出 Cannot find module './some-file.txt'的异常. 省略了一道检查文件是否存在的工序 (fs.exists).这个报错并不会加重你的检查负担, 毕竟使用 fs 去操作文件时, 如果发现文件不存在也会抛出异常. 反之, 通过 require.resovle 可以在提前在文件中作为常量定义, 那么在应用启动时就可以抛异常, 而不是等到具体操作文件的时候才抛异常.

三、flv.js

在transmuxer.js中能看到类似的写法

import TransmuxingWorker from './transmuxing-worker.js';
...
if (config.enableWorker && typeof (Worker) !== 'undefined') {
    try {
        let work = require('webworkify');
        this._worker = work(TransmuxingWorker);
        this._workerDestroying = false;
        this._worker.addEventListener('message',
            this._onWorkerMessage.bind(this));
        this._worker.postMessage({
            cmd: 'init',
            param: [mediaDataSource, config]
        });
        this.e = {
            onLoggingConfigChanged:
            this._onLoggingConfigChanged.bind(this)
        };
        LoggingControl.registerListener(this.e.onLoggingConfigChanged);
        this._worker.postMessage({
            cmd: 'logging_config',
            param: LoggingControl.getConfig()
        });
    } catch (error) {
        Log.e(this.TAG, 'Error while initialize transmuxing worker' +
            ', fallback to inline transmuxing');
        this._worker = null;
        this._controller = new TransmuxingController(mediaDataSource, config);
    }
} else {
    this._controller = new TransmuxingController(mediaDataSource, config);
}

destroy() {
    if (this._worker) {
        if (!this._workerDestroying) {
            this._workerDestroying = true;
            this._worker.postMessage({ cmd: 'destroy' });
            LoggingControl.removeListener(
                this.e.onLoggingConfigChanged);
            this.e = null;
        }
    } else {
        this._controller.destroy();
        this._controller = null;
    }
    this._emitter.removeAllListeners();
    this._emitter = null;
}

然后就是transmuxing-worker.js里面各种通讯

/* post message to worker:
   data: {
       cmd: string
       param: any
   }

   receive message from worker:
   data: {
       msg: string,
       data: any
   }
 */

你可能感兴趣的:(H5 worker 系列三 webworkify处理音视频解码)