H5 worker 系列四 flv.js源碼之log

H5 worker 系列三 webworkify處理音視頻解碼中提到了 flv.js使用webworkify進行音視頻解碼肖爵,那么在解碼的worker線程中,如果想使用公共的Log模塊鳖敷,想把log日志集成到主線程中丹泉,要怎么做呢元潘?

一、worker線程和主線程關于log的數據
//logger.js
Log.GLOBAL_TAG = 'flv.js';
Log.FORCE_GLOBAL_TAG = false;
Log.ENABLE_ERROR = true;
Log.ENABLE_INFO = true;
Log.ENABLE_WARN = true;
Log.ENABLE_DEBUG = true;
Log.ENABLE_VERBOSE = true;

Log.ENABLE_CALLBACK = false;

Log.emitter = new EventEmitter();

log.js里有很多開關贡珊,當改變這些開關值時,需要使用logging_control.js中的一些get/set方法涉馁,比如:

//logging-control.js
static get enableWarn() {
    return Log.ENABLE_WARN;
}

static set enableWarn(enable) {
    Log.ENABLE_WARN = enable;
    LoggingControl._notifyChange();
}

static _notifyChange() {
    let emitter = LoggingControl.emitter;

    if (emitter.listenerCount('change') > 0) {
        let config = LoggingControl.getConfig();
        emitter.emit('change', config);
    }
}

static registerListener(listener) {
    LoggingControl.emitter.addListener('change', listener);
}

static removeListener(listener) {
    LoggingControl.emitter.removeListener('change', listener);
}

這里使用了EventEmitter拋出change事件门岔,來通知一下registerListener中注冊的handler。關于EventEmitter基礎知識谨胞,可以參考Node.js EventEmitter

搜索了下固歪,使用registerListener注冊的,也只有transmuxer.js使用webworker的時候胯努。關于webworker基礎知識牢裳,參考H5 worker 系列三 webworkify處理音視頻解碼

this.e = {
    onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this)
};
LoggingControl.registerListener(this.e.onLoggingConfigChanged);

_onLoggingConfigChanged(config) {
    if (this._worker) {
        this._worker.postMessage({cmd: 'logging_config', param: config});
    }
}

然后看看worker里怎么處理的:

case 'logging_config': {
    let config = e.data.param;
    LoggingControl.applyConfig(config);

    if (config.enableCallback === true) {
        LoggingControl.addLogListener(logcatListener);
    } else {
        LoggingControl.removeLogListener(logcatListener);
    }
    break;

applyConfig是啥

static getConfig() {
    return {
        globalTag: Log.GLOBAL_TAG,
        forceGlobalTag: Log.FORCE_GLOBAL_TAG,
        enableVerbose: Log.ENABLE_VERBOSE,
        enableDebug: Log.ENABLE_DEBUG,
        enableInfo: Log.ENABLE_INFO,
        enableWarn: Log.ENABLE_WARN,
        enableError: Log.ENABLE_ERROR,
        enableCallback: Log.ENABLE_CALLBACK
    };
}

static applyConfig(config) {
    Log.GLOBAL_TAG = config.globalTag;
    Log.FORCE_GLOBAL_TAG = config.forceGlobalTag;
    Log.ENABLE_VERBOSE = config.enableVerbose;
    Log.ENABLE_DEBUG = config.enableDebug;
    Log.ENABLE_INFO = config.enableInfo;
    Log.ENABLE_WARN = config.enableWarn;
    Log.ENABLE_ERROR = config.enableError;
    Log.ENABLE_CALLBACK = config.enableCallback;
}

這里就讓人費解了,在_notifyChange中拋出的getConfig叶沛,繞到worker里蒲讯,又把配置用applyConfig寫回去了,莫非worker這個線程里灰署,和主線程讀取的不是一份數據判帮?必須通過postMessage這種方式同步么局嘁??

二晦墙、測試一下悦昵,確實不是一份數據

1.先改一下transmuxing-worker.js偵聽message方法,無論收到啥消息晌畅,在最后console一下

self.addEventListener('message', function (e) {
    switch (e.data.cmd) {
         ...
        }
console.log('test' + Log.FORCE_GLOBAL_TAG + ',event:' + e.data.cmd);

這樣運行一下但指,forceGlobalTag默認值是false,沒問題
2.在transmuxer.js添加一個test方法

    test() {
        Log.FORCE_GLOBAL_TAG = true;
        // LoggingControl.forceGlobalTag = true;
        console.log('Log.FORCE_GLOBAL_TAG' + Log.FORCE_GLOBAL_TAG);
        this._worker.postMessage({cmd: 'test'});
    }

3.在flv-player.js中添加一個test方法

    test() {
        this._transmuxer.test();
    }

4.在index.html中抗楔,改一下暫停按鈕的操作

        function flv_pause() {
            player.test();
        }

5.現(xiàn)在結論出來了棋凳,直接改Log.FORCE_GLOBAL_TAG之后,在worker中讀取這個值连躏,還是原先的值剩岳。而使用LoggingControl.forceGlobalTag觸發(fā)_notifyChange這種方式就可以。把LoggingControl.applyConfig(config);注釋掉入热,也可以證實這一點拍棕。

三、ENABLE_CALLBACK屬性

1.普通模式
從logger.js中可以看出才顿,ENABLE_CALLBACK默認值為false莫湘,如果為true的時候,在調用logger的幾個輸出方法時郑气,會拋通知出來幅垮,比如

    static v(tag, msg) {
        if (!tag || Log.FORCE_GLOBAL_TAG)
            tag = Log.GLOBAL_TAG;

        let str = `[${tag}] > ${msg}`;

        if (Log.ENABLE_CALLBACK) {
            Log.emitter.emit('log', 'verbose', str);
        }
   ...

這個事件在logging-control.js中有處理

    static addLogListener(listener) {
        Log.emitter.addListener('log', listener);
        if (Log.emitter.listenerCount('log') > 0) {
            Log.ENABLE_CALLBACK = true;
            LoggingControl._notifyChange();
        }
    }

    static removeLogListener(listener) {
        Log.emitter.removeListener('log', listener);
        if (Log.emitter.listenerCount('log') === 0) {
            Log.ENABLE_CALLBACK = false;
            LoggingControl._notifyChange();
        }
    }

使用addLogListener可以添加一個handler,這樣在Log.v等方法調用時尾组,就會執(zhí)行這個handler了忙芒。可以測試一下:

//transmuxer.js
    addOneLogListener() {
        LoggingControl.addLogListener(function (type, str) { 
            console.log('type:' + type + ',str:' + str); 
        });
    }

    mainThreadLogV() {
        Log.v('mainThreadLogV', 'msg:');
    }

先在html頁面上點擊按鈕觸發(fā)addOneLogListener讳侨,然后點擊按鈕觸發(fā)mainThreadLogV呵萨。一切工作正常!

2.worker模式
當我們執(zhí)行addLogListener時跨跨,還會觸發(fā)_notifyChange潮峦,根據上面的分析,會觸發(fā)worker里的邏輯:

case 'logging_config': {
    let config = e.data.param;
    LoggingControl.applyConfig(config);

    if (config.enableCallback === true) {
        LoggingControl.addLogListener(logcatListener);
    } else {
        LoggingControl.removeLogListener(logcatListener);
    }
    break;

這里在worker里勇婴,又來了一遍addLogListener或removeLogListener忱嘹,難道與applyConfig一樣,worker中不但數據不同步耕渴。剛才添加的logHandler(其實就是console.log('type:' + type + ',str:' + str);這個function)也不同步么拘悦?做個試驗,先把LoggingControl.addLogListener(logcatListener);注釋掉橱脸,然后在worker中添加一個message的響應:

//transmuxing-worker.js
            case 'wokerLogV':
                Log.v('wokerLogV', 'msg:');
                break;

然后在主線程中础米,去讓worker執(zhí)行Log.v

//transmuxer.js
    wokerLogV() {
        this._worker.postMessage({cmd: 'wokerLogV'});
    }

再在html頁面上點擊觸發(fā)wokerLogV分苇,果然不出所料,自己添加的logHandler沒有執(zhí)行屁桑。所以logcatListener怎么處理的呢医寿?參考上面的config處理,是用logging_config把數據發(fā)射出去蘑斧,然后在worker線程中用applyConfig這種方式重寫了一遍新數據糟红。但是logHandler就不方便發(fā)射了,只能用worker線程把這兩個參數轉發(fā)回去乌叶。也就是說,不管在main線程上使用addLogListener添加了多少個logHandler柒爸,在worker線程中始終只添加了一個logcatListener准浴,這個線程就是負責轉發(fā)參數的,發(fā)回到main線程中捎稚,才是真實的logHandler去響應乐横。

let logcatListener = onLogcatCallback.bind(this);

function onLogcatCallback(type, str) {
    self.postMessage({
        msg: 'logcat_callback',
        data: {
            type: type,
            logcat: str
        }
    });
}

主線程:

case 'logcat_callback':
    Log.emitter.emit('log', data.type, data.logcat);
    break;
四、總結

可能我一開始就忽略了一些事情今野,在this._worker = work(TransmuxingWorker);時葡公,生成的transmuxing-worker.js就完全import了另外的副本:

import Log from '../utils/logger.js';
import LoggingControl from '../utils/logging-control.js';
import Polyfill from '../utils/polyfill.js';
import TransmuxingController from './transmuxing-controller.js';
import TransmuxingEvents from './transmuxing-events.js';

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

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

let TransmuxingWorker = function (self) {
...

雖然transmuxer.js中import的是同樣的logger.js,并且里面有l(wèi)ogger.js很多static公共常量

import EventEmitter from 'events';
import Log from '../utils/logger.js';
import LoggingControl from '../utils/logging-control.js';
import TransmuxingController from './transmuxing-controller.js';
import TransmuxingEvents from './transmuxing-events.js';
import TransmuxingWorker from './transmuxing-worker.js';
import MediaInfo from './media-info.js';

class Transmuxer {
...

說得有點啰嗦条霜,但也印證了催什,兩個線程無法共享數據,必須通過postMessage來交互宰睡,并且交互傳遞的只是字符串化的副本蒲凶,也就是傳統(tǒng)上講的深復制

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末拆内,一起剝皮案震驚了整個濱河市旋圆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌麸恍,老刑警劉巖灵巧,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抹沪,居然都是意外死亡刻肄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門采够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肄方,“玉大人,你說我怎么就攤上這事蹬癌∪ㄋ” “怎么了咸这?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俏讹。 經常有香客問我研底,道長,這世上最難降的妖魔是什么步清? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任要门,我火速辦了婚禮,結果婚禮上廓啊,老公的妹妹穿的比我還像新娘欢搜。我一直安慰自己,他們只是感情好谴轮,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布炒瘟。 她就那樣靜靜地躺著,像睡著了一般第步。 火紅的嫁衣襯著肌膚如雪疮装。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天粘都,我揣著相機與錄音廓推,去河邊找鬼。 笑死翩隧,一個胖子當著我的面吹牛樊展,可吹牛的內容都是我干的。 我是一名探鬼主播堆生,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼滚局,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了顽频?” 一聲冷哼從身側響起藤肢,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糯景,沒想到半個月后嘁圈,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蟀淮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年最住,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怠惶。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡涨缚,死狀恐怖,靈堂內的尸體忽然破棺而出策治,到底是詐尸還是另有隱情脓魏,我是刑警寧澤兰吟,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站茂翔,受9級特大地震影響混蔼,放射性物質發(fā)生泄漏。R本人自食惡果不足惜珊燎,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一惭嚣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悔政,春花似錦晚吞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烹卒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弯洗,已是汗流浹背旅急。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牡整,地道東北人藐吮。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像逃贝,于是被迫代替她去往敵國和親谣辞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內容