UNPKG

trtc-electron-sdk

Version:

trtc electron sdk

1,216 lines 271 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const Renderer_1 = require("./Renderer"); const util_1 = require("./Renderer/util"); const yuv_buffer_1 = __importDefault(require("./Renderer/yuv-buffer")); // @ts-ignore const YCbCr_1 = __importDefault(require("yuv-canvas/src/YCbCr")); // import TRTCLocalMediaTranscoder from './LocalMediaTranscoder'; const trtc_define_1 = require("./trtc_define"); const validate_util_1 = require("./validate-util"); const logger_1 = __importStar(require("./logger")); const LocalVideoRenderController_1 = __importDefault(require("./VideoRenderControl/LocalVideoRenderController")); const VideoRenderFPSMonitor_1 = require("./VideoRenderControl/VideoRenderFPSMonitor"); const DeviceManager_1 = __importDefault(require("./extensions/DeviceManager")); const AudioEffectManager_1 = __importDefault(require("./extensions/AudioEffectManager")); const MediaMixingManager_1 = __importDefault(require("./extensions/MediaMixingManager")); const PluginManager_1 = __importDefault(require("./extensions/PluginManager")); const NodeTRTCEngine = require('../build/Release/trtc_electron_sdk.node'); const pkg = require('../package.json'); // 监听该事件,返回自定义视频渲染数据 ( bgra 格式) const CUSTOM_VIDEO_DATA = 'onVideoData'; const LOCAL_VIDEO_DATA = 'onLocalVideoData'; const CAMERA_DEVICE_TEST_VIEW = 'camera_device_test_view'; const CAMERA_DEVICE_TEST_ID = 'camera_device_test'; const LOCAL_USER_VIDEO_ID = 'local_video'; const DEFAULT_VIDEO_WIDTH = 640; const DEFAULT_VIDEO_HEIGHT = 360; const SDK_LOG_LEVEL = 2; const genSecFunc = function (instance, key) { return function () { let param = Array.from(arguments); let result = undefined; try { result = instance[key].apply(instance, param); if (result) return result; } catch (err) { logger_1.default.warn(err); } }; }; const copyMethods = function (instance) { for (let key in instance) { if (typeof instance[key] != 'function' || key === 'constructor' || key.slice(0, 1) === '_' || typeof instance[key] != 'undefined') { continue; } instance[key] = genSecFunc(key, instance); } }; let copyPropMethods = function (instance) { const disList = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(instance)); for (let key in disList) { if (typeof instance[key] != 'function' || key === 'constructor' || key.slice(0, 1) === '_' || typeof instance[key] != 'undefined') { continue; } instance[key] = genSecFunc(key, instance); } }; const secMethods = function (instance) { copyMethods(instance); copyPropMethods(instance); }; const formatLogParams = function (params) { let logParamsStr = ''; const paramsKeyList = Object.keys(params); for (let i = 0; i < paramsKeyList.length; i++) { const paramKey = paramsKeyList[i]; logParamsStr += `${paramKey}:${params[paramKey]}`; if (i !== paramsKeyList.length - 1) { logParamsStr += ','; } } return logParamsStr; }; const getComponentNO = function () { var _a, _b; let componentName = ''; // @ts-ignore if ((_a = window === null || window === void 0 ? void 0 : window.__TRTCElectron) === null || _a === void 0 ? void 0 : _a.componentName) { // @ts-ignore componentName = window.__TRTCElectron.componentName; } else { if ((_b = window === null || window === void 0 ? void 0 : window.localStorage) === null || _b === void 0 ? void 0 : _b.getItem) { const jsonStr = window.localStorage.getItem('__TRTCElectron'); if (jsonStr) { try { const jsonObj = JSON.parse(jsonStr); if (jsonObj.componentName && typeof jsonObj.componentName === 'string') { componentName = jsonObj.componentName; } } catch (error) { logger_1.default.warn('localStorage TRTCElectron parse error.', error); } } } else { /** * localStorage not defined or accessing is denied. * * Will occur when third-party scripts cookies are disallowed and access to localStorage may result in thrown SecurityError exceptions. * ref 1: https://www.chromium.org/for-testers/bug-reporting-guidelines/uncaught-securityerror-failed-to-read-the-localstorage-property-from-window-access-is-denied-for-this-document/ * ref 2: https://cloud.tencent.com/developer/article/1803097 */ } } let componentNO = ''; switch (componentName) { case 'TUIRoom': componentNO = '5'; break; default: break; } return componentNO; }; /** * 腾讯云视频通话功能的主要接口类 * * * @example * // 创建/使用/销毁 TRTCCloud 对象的示例代码: * import TRTCCloud from 'trtc-electron-sdk'; * const rtcCloud = TRTCCloud.getTRTCShareInstance(); * // 获取 SDK 版本号 * const version = rtcCloud.getSDKVersion(); * */ class TRTCCloud extends events_1.EventEmitter { /** * @param {TRTCInitConfig} [config] - 初始化参数,可选 * @param {Record<string, any>} [config.networkProxy] - 网络代理参数,可选。为空时,默认不开启网络代理。 * @param {Boolean} [config.isIPCMode] - 是否跨进程模式,跨进程模式支持本地混流且混流视频采用原生窗口渲染。默认:false,不开启。 */ constructor(config) { var _a; super(); this.id = !TRTCCloud.sharedTRTCCloudInstance ? 1 : (2 + (((_a = TRTCCloud.subInstances) === null || _a === void 0 ? void 0 : _a.length) || 0)); this.logger = new logger_1.Logger(`TRTC_${this.id}`); const componentNO = getComponentNO(); if (TRTCCloud.isIPCMode === null || TRTCCloud.isIPCMode === undefined) { TRTCCloud.isIPCMode = (config === null || config === void 0 ? void 0 : config.isIPCMode) || false; if (TRTCCloud.isIPCMode) { NodeTRTCEngine.setRemoteTrtcWithAcrossProcessMode(TRTCCloud.isIPCMode); } logger_1.default.log(`TRTC use mode:${TRTCCloud.isIPCMode}`); } const newConfig = Object.assign({ jsVersion: pkg.version, componentNO }, config); if (!TRTCCloud.isIPCMode) { this.rtcCloud = new NodeTRTCEngine.NodeTRTCCloud(newConfig); } else { this.rtcCloud = new NodeTRTCEngine.NodeRemoteTRTCCloud(newConfig); } this.stateStore = new Map(); this.videoRendererMap = new Map(); this.pixelFormat = trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420; this.pixelLength = 1.5; this.isExternalRenderEnabled = false; this.localFillMode = trtc_define_1.TRTCVideoFillMode.TRTCVideoFillMode_Fit; this.remoteFillModeMap = new Map(); this.localRotation = trtc_define_1.TRTCVideoRotation.TRTCVideoRotation0; this.localMirrorType = trtc_define_1.TRTCVideoMirrorType.TRTCVideoMirrorType_Disable; this.remoteMainStreamFillMode = trtc_define_1.TRTCVideoFillMode.TRTCVideoFillMode_Fit; this.remoteMainStreamRotation = trtc_define_1.TRTCVideoRotation.TRTCVideoRotation0; this.remoteMainStreamMirrorType = trtc_define_1.TRTCVideoMirrorType.TRTCVideoMirrorType_Disable; this.remoteSubStreamFillMode = trtc_define_1.TRTCVideoFillMode.TRTCVideoFillMode_Fit; this.remoteSubStreamRotation = trtc_define_1.TRTCVideoRotation.TRTCVideoRotation0; this.remoteSubStreamMirrorType = trtc_define_1.TRTCVideoMirrorType.TRTCVideoMirrorType_Disable; this.priorRemoteVideoStreamType = trtc_define_1.TRTCVideoStreamType.TRTCVideoStreamTypeBig; this.audioQuality = trtc_define_1.TRTCAudioQuality.TRTCAudioQualityDefault; // 生成一个足够大, 不会与用户输入重复的 id this.bgmId = 1 << 29; this.playAudioEffectIdList = []; this.enableRenderFrame = true; this.screenCaptureStreamType = trtc_define_1.TRTCVideoStreamType.TRTCVideoStreamTypeSub; this.isScreenCapturing = false; this.isCaptureLocalVideoEnabled = false; this.videoProcessBufferType = trtc_define_1.TRTCVideoBufferType.TRTCVideoBufferType_Buffer; this.videoProcessPixelFormat = trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420; this.initEventHandler(); this.logger.log('constructor pixelFormat:' + this.pixelFormat + '|sdk version:' + pkg.version + '|native sdk version:' + this.rtcCloud.getSDKVersion()); //视频数据回调 this.imageData = null; //VideoCallback 本地和远程 this.localVideoCallback = null; this.remoteVideoCallback = new Map(); this.vodPlayers = {}; secMethods(this); // 视频渲染共享buffer this.remoteVideoBufferMap = new Map(); this.localVideoBufferMap = { big: new trtc_define_1.VideoBufferInfo(), sub: new trtc_define_1.VideoBufferInfo(), cameraTest: new trtc_define_1.VideoBufferInfo(), }; // 本地合图插件 // this.localMediaTranscoder = null; this.videoRenderFPS = 60; this.onFPSChanged = this.onFPSChanged.bind(this); VideoRenderFPSMonitor_1.videoRenderFPSMonitor.on('videoRenderFPS', this.onFPSChanged); this.callExperimentalAPI("{\"api\":\"setCurrentEnvironment\",\"params\" :{\"electron\":true}}"); this._localVideoRenderCallback = this._localVideoRenderCallback.bind(this); this._remoteVideoRenderCallback = this._remoteVideoRenderCallback.bind(this); this.localVideoRenderController = new LocalVideoRenderController_1.default(this); this.deviceManager = new DeviceManager_1.default({ isIPCMode: TRTCCloud.isIPCMode, nodeTRTCCloud: this.rtcCloud, }); this.audioEffectManager = new AudioEffectManager_1.default({ isIPCMode: TRTCCloud.isIPCMode, nodeTRTCCloud: this.rtcCloud, }); this.pluginManager = new PluginManager_1.default({ isIPCMode: TRTCCloud.isIPCMode, nodeTRTCCloud: this.rtcCloud, }); if (TRTCCloud.isIPCMode) { this.mediaMixingManager = new MediaMixingManager_1.default({ deviceManager: this.deviceManager, nodeTRTCCloud: this.rtcCloud, }); } else { this.mediaMixingManager = null; } if (!TRTCCloud.sharedTRTCCloudInstance) { TRTCCloud.sharedTRTCCloudInstance = this; } else { if (!TRTCCloud.subInstances) { TRTCCloud.subInstances = []; } TRTCCloud.subInstances.push(this); } } /** * 创建 TRTCCloud 主实例对象(单例模式) * * @param {TRTCInitConfig} [config] - 初始化参数,可选 * @param {Record<string, any>} [config.networkProxy] - 网络代理参数,可选。为空时,默认不开启网络代理。 * @param {Boolean} [config.isIPCMode] - 是否跨进程模式,跨进程模式支持本地混流且混流视频采用原生窗口渲染。默认:false,不开启。 * * @returns {TRTCCloud} */ static getTRTCShareInstance(config) { if (!TRTCCloud.sharedTRTCCloudInstance) { TRTCCloud.sharedTRTCCloudInstance = new TRTCCloud(config); } return TRTCCloud.sharedTRTCCloudInstance; } /** * 销毁 TRTCCloud 主实例对象(单例模式) * * 注释:会同时销毁所有子实例 */ static destroyTRTCShareInstance() { if (TRTCCloud.sharedTRTCCloudInstance) { TRTCCloud.sharedTRTCCloudInstance.destroy(); TRTCCloud.sharedTRTCCloudInstance = null; } } onFPSChanged(options) { this.videoRenderFPS = options.videoFPS !== -1 ? options.videoFPS : 60; if (!TRTCCloud.isIPCMode) { this.rtcCloud.setVideoRenderFPS(this.videoRenderFPS); } } /** * 创建子实例 * * 注意:只有主实例才能创建子实例,子实例不能创建子实例 * * @example * import TRTCCloud from 'trtc-electron-sdk'; * * const rtcCloud = TRTCCloud.getTRTCShareInstance(); * rtcCloud.startLocalAudio(); // 主实例开启麦克风采集 * * const childRtcCloud = rtcCloud.createSubCloud(); * childRtcCloud.startSystemAudioLoopback(); // 子实例开启系统音采集 * * @returns {TRTCCloud} */ createSubCloud(config) { if (this !== TRTCCloud.sharedTRTCCloudInstance) { throw new Error("createSubCloud() can only be called on the main instance created by getTRTCShareInstance()."); } if (TRTCCloud.isIPCMode) { throw new Error("createSubCloud() is not supported when `isIPCMode` is true."); } return new TRTCCloud(config); } /** * 获取 TRTC 配置对象 * * 可以通过 TRTC 配置对象 {@link TRTCConfig} 打开 debug 模式 * * @example * // Enable 'debug' mode * import TRTCCloud from 'trtc-electron-sdk'; * const rtcCloud = TRTCCloud.getTRTCShareInstance(); * rtcCloud.getConfigObject().setDebugMode(true); * * @returns {TRTCConfig} */ getConfigObject() { return util_1.config; } setExternalRenderEnabled(enabled) { this.isExternalRenderEnabled = enabled; this.localVideoRenderController.setExternalRenderEnabled(enabled); if (this.isExternalRenderEnabled) { this.pixelFormat = trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_RGBA32; this.pixelLength = 4; } else { this.pixelFormat = trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420; this.pixelLength = 1.5; } } setCaptureLocalVideoEnabled(enabled) { this.isCaptureLocalVideoEnabled = enabled; } /** * 销毁当前实例,释放资源 */ destroy() { var _a; if (this === TRTCCloud.sharedTRTCCloudInstance) { if (TRTCCloud.subInstances) { TRTCCloud.subInstances.forEach(sub => { this.logger.log('destroy sub cloud'); sub.destroy(); }); TRTCCloud.subInstances = []; } } else { TRTCCloud.subInstances = TRTCCloud.subInstances.filter((item) => { return item != this; }); } VideoRenderFPSMonitor_1.videoRenderFPSMonitor.off('videoRenderFPS', this.onFPSChanged); this.playAudioEffectIdList = []; this.remoteFillModeMap.clear(); // this.destroyLocalMediaTranscoder(); this.rtcCloud.destroy(); this.rtcCloud = null; this.deviceManager.destroy(); this.audioEffectManager.destroy(); this.pluginManager.destroy(); (_a = this.mediaMixingManager) === null || _a === void 0 ? void 0 : _a.destroy(); } ///////////////////////////////////////////////////////////////////////////////// // // 设置 TRTCCallback 回调 // ///////////////////////////////////////////////////////////////////////////////// /** * 设置 TRTCCloud 回调 * * @example * // 创建/使用/销毁 TRTCCloud 对象的示例代码: * import TRTCCloud from 'trtc-electron-sdk'; * this.rtcCloud = TRTCCloud.getTRTCShareInstance(); * * // 注册回调的方法,事件关键字详见下方"通用事件回调" * subscribeEvents = (rtcCloud) => { * rtcCloud.on('onError', (errcode, errmsg) => { * console.info('trtc_demo: onError :' + errcode + " msg" + errmsg); * }); * }; * * @namespace TRTCCallback */ initEventHandler() { this.rtcCloud.setTRTCCloudCallback((args) => { const key = args[0]; const data = args[1]; switch (key) { case 'onError': this.handleOnError(data.errCode, data.errMsg); break; case 'onWarning': this.handleOnWarning(data.warningCode, data.warningMsg, data.extraInfo); break; case 'onEnterRoom': this.handleOnEnterRoom(data.result); break; case 'onExitRoom': this.handleOnExitRoom(data.reason); break; case 'onSwitchRoom': this.handleOnSwitchRoom(data.errCode, data.errMsg); break; case 'onSwitchRole': this.handleOnSwitchRole(data.errCode, data.errMsg); break; case 'onConnectOtherRoom': this.handleOnConnectOtherRoom(data.userId, data.errCode, data.errMsg); break; case 'onDisconnectOtherRoom': this.handleOnDisconnectOtherRoom(data.errCode, data.errMsg); break; case 'onRemoteUserEnterRoom': this.handleOnRemoteUserEnterRoom(data.userId); break; case 'onRemoteUserLeaveRoom': this.handleOnRemoteUserLeaveRoom(data.userId, data.reason); break; case 'onUserVideoAvailable': this.handleOnUserVideoAvailable(data.userId, data.available === true ? 1 : 0); break; case 'onUserSubStreamAvailable': this.handleOnUserSubStreamAvailable(data.userId, data.available === true ? 1 : 0); break; case 'onUserAudioAvailable': this.handleOnUserAudioAvailable(data.userId, data.available === true ? 1 : 0); break; case 'onFirstVideoFrame': this.handleOnFirstVideoFrame(data.userId, data.streamType, data.width, data.height); break; case 'onFirstAudioFrame': this.handleOnFirstAudioFrame(data.userId); break; case 'onSendFirstLocalVideoFrame': this.handleOnSendFirstLocalVideoFrame(data.streamType); break; case 'onSendFirstLocalAudioFrame': this.handleOnSendFirstLocalAudioFrame(); break; case 'onUserEnter': this.handleOnUserEnter(data.userId); break; case 'onUserExit': this.handleOnUserExit(data.userId, data.reason); break; case 'onConnectionLost': this.handleOnConnectionLost(); break; case 'onTryToReconnect': this.handleOnTryToReconnect(); break; case 'onConnectionRecovery': this.handleOnConnectionRecovery(); break; case 'onCameraDidReady': this.handleOnCameraDidReady(); break; case 'onMicDidReady': this.handleOnMicDidReady(); break; case 'onDeviceChange': this.handleOnDeviceChange(data.deviceId, data.type, data.state); break; case 'onTestMicVolume': this.handleOnTestMicVolume(data.volume); break; case 'onTestSpeakerVolume': this.handleOnTestSpeakerVolume(data.volume); break; case 'onStartPublishing': this.handleOnStartPublishing(data.err, data.errMsg); break; case 'onStopPublishing': this.handleOnStopPublishing(data.err, data.errMsg); break; case 'onStartPublishCDNStream': this.handleOnStartPublishCDNStream(data.errCode, data.errMsg); break; case 'onStopPublishCDNStream': this.handleOnStopPublishCDNStream(data.errCode, data.errMsg); break; case 'onSetMixTranscodingConfig': this.handleOnSetMixTranscodingConfig(data.err, data.errMsg); break; case 'onAudioEffectFinished': this.handleOnAudioEffectFinished(data.effectId, data.code); break; case 'onStartPublishMediaStream': this.handleOnStartPublishMediaStream(data.taskId, data.code, data.message); break; case 'onUpdatePublishMediaStream': this.handleOnUpdatePublishMediaStream(data.taskId, data.code, data.message); break; case 'onStopPublishMediaStream': this.handleOnStopPublishMediaStream(data.taskId, data.code, data.message); break; case 'onCdnStreamStateChanged': this.handleOnCdnStreamStateChanged(data.cdnUrl, data.status, data.code, data.msg); break; case 'onScreenCaptureCovered': this.handleOnScreenCaptureCovered(); break; case 'onScreenCaptureStarted': this.handleOnScreenCaptureStarted(); break; case 'onScreenCapturePaused': this.handleOnScreenCapturePaused(data.reason); break; case 'onScreenCaptureResumed': this.handleOnScreenCaptureResumed(data.reason); break; case 'onScreenCaptureStoped': this.handleOnScreenCaptureStopped(data.reason); break; case 'onSnapshotComplete': this.handleOnSnapshotComplete(data.userId, data.type, data.data, data.width, data.height); break; case 'onAudioDeviceCaptureVolumeChanged': this.handleOnAudioDeviceCaptureVolumeChanged(data.volume, data.muted); break; case 'onAudioDevicePlayoutVolumeChanged': this.handleOnAudioDevicePlayoutVolumeChanged(data.volume, data.muted); break; case 'onSystemAudioLoopbackError': this.handleOnSystemAudioLoopbackError(data.errCode); break; case 'onLocalRecordBegin': this.handleOnLocalRecordBegin(data.errCode, data.storagePath); break; case 'onLocalRecording': this.handleOnLocalRecording(data.duration, data.storagePath); break; case 'onLocalRecordComplete': this.handleOnLocalRecordComplete(data.errCode, data.storagePath); break; case 'onNetworkQuality': this.handleOnNetworkQuality(data.localQuality, data.remoteQuality); break; case 'onStatistics': this.handleOnStatistics(data.statistics); break; case 'onUserVoiceVolume': this.handleOnUserVoiceVolume(data.userVolumes, data.userVolumesCount, data.totalVolume); break; case 'onRecvCustomCmdMsg': this.handleOnRecvCustomCmdMsg(data.userId, data.cmdID, data.seq, data.message); break; case 'onMissCustomCmdMsg': this.handleOnMissCustomCmdMsg(data.userId, data.cmdID, data.errCode, data.missed); break; case 'onRecvSEIMsg': this.handleOnRecvSEIMsg(data.userId, data.message); break; case 'onSpeedTestResult': this.handleOnSpeedTestResult(data.result); break; default: break; } }); this.bindOnSpeedTest(); } fire(event, ...args) { setImmediate(() => { this.emit(event, ...args); }); } ///////////////////////////////////////////////////////////////////////////////// // // (一)错误事件和警告事件 // ///////////////////////////////////////////////////////////////////////////////// /** * 错误回调:SDK 不可恢复的错误,一定要监听,并分情况给用户适当的界面提示。 * * @event TRTCCallback#onError * @param {Number} errCode - 错误码 * @param {String} errMsg - 错误信息 */ handleOnError(errCode, errMsg) { this.fire('onError', errCode, errMsg); } /** * 警告回调:用于告知您一些非严重性问题,例如出现了卡顿或者可恢复的解码失败。 * * @event TRTCCallback#onWarning * @param {Number} warningCode - 警告码 * @param {String} warningMsg - 警告信息 * @param {Any} extra - 补充信息 */ handleOnWarning(warningCode, warningMsg, extra) { this.fire('onWarning', warningCode, warningMsg, extra); } ///////////////////////////////////////////////////////////////////////////////// // // (二)房间事件回调 // ///////////////////////////////////////////////////////////////////////////////// /** * 已加入房间的回调 * * 调用 TRTCCloud 中的 [enterRoom()]{@link TRTCCloud#enterRoom} 接口执行进房操作后,会收到来自 SDK 的 onEnterRoom(result) 回调: * 如果加入成功,result 会是一个正数(result > 0),代表加入房间的时间消耗,单位是毫秒(ms)。 * 如果加入失败,result 会是一个负数(result < 0),代表进房失败的错误码。 * 进房失败的错误码含义请参见 [错误码](https://cloud.tencent.com/document/product/647/32257)。 * * 注意: * - 在 Ver6.6 之前的版本,只有进房成功会抛出 onEnterRoom(result) 回调,进房失败由 onError() 回调抛出。 * - 在 Ver6.6 及之后改为:进房成功返回正的 result,进房失败返回负的 result,同时进房失败也会有 onError() 回调抛出。 * * @event TRTCCallback#onEnterRoom * @param {Number} result - result > 0 时为进房耗时(ms),result < 0 时为进房错误码。 */ handleOnEnterRoom(result) { this.fire('onEnterRoom', result); const params = { js_version: pkg.version, electron_version: process && process.versions && process.versions.electron || '', arch: process && process.arch || '', ua: window.navigator.userAgent }; this.rtcCloud.callExperimentalAPI(JSON.stringify({ api: 'reportOnlineLog', params: { level: SDK_LOG_LEVEL, msg: '[trtc-electron-sdk] trtcstats', more_msg: formatLogParams(params) } })); } /** * 退出房间的事件回调 * * 调用 TRTCCloud 中的 [exitRoom()]{@link TRTCCloud#exitRoom} 接口会执行退出房间的相关逻辑,例如释放音视频设备资源和编解码器资源等。 * 待资源释放完毕,SDK 会通过 onExitRoom() 回调通知到您。 * * 如果您要再次调用 enterRoom() 或者切换到其他的音视频 SDK,请等待 onExitRoom() 回调到来后再执行相关操作。 * 否则可能会遇到例如摄像头、麦克风设备被强占等各种异常问题。 * * @event TRTCCallback#onExitRoom * @param {Number} reason - 离开房间原因,0:主动调用 exitRoom 退房;1:被服务器踢出当前房间;2:当前房间整个被解散。 */ handleOnExitRoom(reason) { this.fire('onExitRoom', reason); } /** * 切换房间的事件回调 * * @event TRTCCallback#onSwitchRoom * @param {Number} errCode - 错误码,ERR_NULL 代表切换成功,其他请参见[错误码](https://cloud.tencent.com/document/product/647/32257)。 * @param {String} errMsg - 错误信息。 */ handleOnSwitchRoom(errCode, errMsg) { this.fire('onSwitchRoom', errCode, errMsg); } /** * 切换角色的事件回调 * * 调用 TRTCCloud 中的 [switchRole()]{@link TRTCCloud#switchRole} 接口会切换主播和观众的角色,该操作会伴随一个线路切换的过程, * 待 SDK 切换完成后,会抛出 onSwitchRole() 事件回调。 * * @event TRTCCallback#onSwitchRole * @param {Number} errCode - 错误码,ERR_NULL 代表切换成功,其他请参见[错误码](https://cloud.tencent.com/document/product/647/32257)。 * @param {String} errMsg - 错误信息。 */ handleOnSwitchRole(errCode, errMsg) { this.logger.warn(`onSwitchRole errCode:${errCode} errMsg:${errMsg}`); this.fire('onSwitchRole', errCode, errMsg); } /** * 请求跨房连麦(主播跨房 PK)的结果回调 * * 调用 TRTCCloud 中的 [connectOtherRoom()]{@link TRTCCloud#connectOtherRoom} 接口会将两个不同房间中的主播拉通视频通话,也就是所谓的“主播PK”功能。 * 调用者会收到 onConnectOtherRoom() 回调来获知跨房通话是否成功, * 如果成功,两个房间中的所有用户都会收到 PK 主播的 onUserVideoAvailable() 回调。 * * @event TRTCCallback#onConnectOtherRoom * @param {String} userId - 要 PK 的目标主播 userId。 * @param {Number} errCode - 错误码,ERR_NULL 代表切换成功,其他请参见[错误码](https://cloud.tencent.com/document/product/647/32257)。 * @param {String} errMsg - 错误信息。 */ handleOnConnectOtherRoom(userId, errCode, errMsg) { this.fire('onConnectOtherRoom', userId, errCode, errMsg); } /** * 关闭跨房连麦(主播跨房 PK)的结果回调 * * @event TRTCCallback#onDisconnectOtherRoom * @param {Number} errCode - 错误码,ERR_NULL 代表切换成功,其他请参见 [错误码](https://cloud.tencent.com/document/product/647/32257)。 * @param {String} errMsg - 错误信息。 */ handleOnDisconnectOtherRoom(errCode, errMsg) { this.fire('onDisconnectOtherRoom', errCode, errMsg); } ///////////////////////////////////////////////////////////////////////////////// // // (三)成员事件回调 // ///////////////////////////////////////////////////////////////////////////////// /** * 有用户加入当前房间 * * 出于性能方面的考虑,在两种不同的应用场景下,该通知的行为会有差别: * - 通话场景(TRTCAppSceneVideoCall 和 TRTCAppSceneAudioCall):该场景下用户没有角色的区别,任何用户进入房间都会触发该通知。 * - 直播场景(TRTCAppSceneLIVE 和 TRTCAppSceneVoiceChatRoom):该场景不限制观众的数量,如果任何用户进出都抛出回调会引起很大的性能损耗,所以该场景下只有主播进入房间时才会触发该通知,观众进入房间不会触发该通知。 * * 注意:onRemoteUserEnterRoom 和 onRemoteUserLeaveRoom 只适用于维护当前房间里的“成员列表”,如果需要显示远程画面,建议使用监听 onUserVideoAvailable() 事件回调。 * * @event TRTCCallback#onRemoteUserEnterRoom * @param {String} userId - 用户标识 */ handleOnRemoteUserEnterRoom(userId) { this.fire('onRemoteUserEnterRoom', userId); } /** * 有用户离开当前房间 * * 与 onRemoteUserEnterRoom 相对应,在两种不同的应用场景下,该通知的行为会有差别: * - 通话场景(TRTCAppSceneVideoCall 和 TRTCAppSceneAudioCall):该场景下用户没有角色的区别,任何用户的离开都会触发该通知。 * - 直播场景(TRTCAppSceneLIVE 和 TRTCAppSceneVoiceChatRoom):只有主播离开房间时才会触发该通知,观众离开房间不会触发该通知。 * * @event TRTCCallback#onRemoteUserLeaveRoom * @param {String} userId - 用户标识 * @param {Number} reason - 离开原因,0表示用户主动退出房间,1表示用户超时退出,2表示被踢出房间。 */ handleOnRemoteUserLeaveRoom(userId, reason) { const streamTypeList = [ trtc_define_1.TRTCVideoStreamType.TRTCVideoStreamTypeBig, trtc_define_1.TRTCVideoStreamType.TRTCVideoStreamTypeSub, trtc_define_1.TRTCVideoStreamType.TRTCVideoStreamTypeSmall ]; streamTypeList.forEach(streamType => { const key = this._getKey(userId, streamType); this._destroyRenderer(key, null); }); this.fire('onRemoteUserLeaveRoom', userId, reason); } /** * 用户是否开启摄像头视频 * * 当您收到 onUserVideoAvailable(userId, 1) 通知时,代表该路画面已经有可用的视频数据帧到达。 * 之后,您需要调用 [startRemoteView()]{@link TRTCCloud#startRemoteView} 接口加载该用户的远程画面。 * 再之后,您还会收到名为 onFirstVideoFrame(userId) 的首帧画面渲染回调。 * * 当您收到了 onUserVideoAvailable(userId, 0) 通知时,代表该路远程画面已经被关闭,这可能是 * 由于该用户调用了 [muteLocalVideo()]{@link TRTCCloud#muteLocalVideo} 或 [stopLocalPreview()]{@link TRTCCloud#stopLocalPreview} 所致。 * * @event TRTCCallback#onUserVideoAvailable * @param {String} userId - 用户标识 * @param {Number} available - 画面是否开启 */ handleOnUserVideoAvailable(userId, available) { this.logger.log(`onUserVideoAvailable userId:${userId} available:${available}`); if (available === 0) { const remoteUserVideoBuffers = this.remoteVideoBufferMap.get(userId); if (remoteUserVideoBuffers) { if (remoteUserVideoBuffers.big) { this.rtcCloud.setRemoteVideoBuffer(userId, remoteUserVideoBuffers.big.buffer, remoteUserVideoBuffers.big.streamType, 0, 0, remoteUserVideoBuffers.big.pixelFormat, remoteUserVideoBuffers.big.id); remoteUserVideoBuffers.big.buffer = null; remoteUserVideoBuffers.big = undefined; } if (remoteUserVideoBuffers.small) { this.rtcCloud.setRemoteVideoBuffer(userId, remoteUserVideoBuffers.small.buffer, remoteUserVideoBuffers.small.streamType, 0, 0, remoteUserVideoBuffers.small.pixelFormat, remoteUserVideoBuffers.small.id); remoteUserVideoBuffers.small.buffer = null; remoteUserVideoBuffers.small = undefined; } if (!remoteUserVideoBuffers.sub) { this.remoteVideoBufferMap.delete(userId); } } } this.fire('onUserVideoAvailable', userId, available); } /** * 用户是否开启了辅路画面(TRTCVideoStreamTypeSub,一般用于屏幕分享) * * 注意:显示辅路画面使用的函数是[startRemoteView()]{@link TRTCCloud#startRemoteView}, 从 8.0 版本开始已不再推荐使用 [startRemoteSubStreamView()]{@link TRTCCloud#startRemoteSubStreamView}。 * * @event TRTCCallback#onUserSubStreamAvailable * @param {String} userId - 用户标识 * @param {Number} available - 辅路画面是否开启 */ handleOnUserSubStreamAvailable(userId, available) { this.logger.log(`onUserSubStreamAvailable userId:${userId} available:${available}`); if (available === 0) { const remoteVideoBuffers = this.remoteVideoBufferMap.get(userId); if (remoteVideoBuffers && remoteVideoBuffers.sub) { const targetVideoBuffer = remoteVideoBuffers.sub; if (targetVideoBuffer && targetVideoBuffer.buffer) { this.rtcCloud.setRemoteVideoBuffer(userId, targetVideoBuffer.buffer, targetVideoBuffer.streamType, 0, 0, targetVideoBuffer.pixelFormat, targetVideoBuffer.id); targetVideoBuffer.buffer = null; } remoteVideoBuffers.sub = undefined; if (!remoteVideoBuffers.big && !remoteVideoBuffers.small) { this.remoteVideoBufferMap.delete(userId); } } } this.fire('onUserSubStreamAvailable', userId, available); } /** * 用户是否开启音频上行 * * @event TRTCCallback#onUserAudioAvailable * @param {String} userId - 用户标识 * @param {Number} available - 声音是否开启 */ handleOnUserAudioAvailable(userId, available) { this.logger.log(`onUserAudioAvailable userId:${userId} available:${available}`); this.fire('onUserAudioAvailable', userId, available); } /** * 开始渲染本地或远程用户的首帧画面 * * 如果 userId 为 null,表示开始渲染本地采集的摄像头画面,需要您先调用 [startLocalPreview()]{@link TRTCCloud#startLocalPreview} 触发。 * 如果 userId 不为 null,表示开始渲染远程用户的首帧画面,需要您先调用 [startRemoteView()]{@link TRTCCloud#startRemoteView} 触发。 * * 注意:只有当您调用 startLocalPreview() 或 startRemoteView() 之后,才会触发该回调。 * * @event TRTCCallback#onFirstVideoFrame * @param {String} userId - 本地或远程用户 ID,如果 userId == '' 代表本地,userId != '' 代表远程。 * @param {Number} streamType - 视频流类型,TRTCVideoStreamTypeBig:主路画面,一般用于摄像头;TRTCVideoStreamTypeSub:辅路画面,一般用于屏幕分享。 * @param {Number} width - 画面宽度 * @param {Number} height - 画面高度 */ handleOnFirstVideoFrame(userId, streamType, width, height) { this.logger.log(`onFirstVideoFrame userId:${userId} streamType:${streamType} width:${width} height:${height}`); if (userId) { this._setRemoteVideoBuffer(userId, streamType); } this.fire('onFirstVideoFrame', userId, streamType, width, height); } /** * 开始播放远程用户的首帧音频(本地声音暂不通知) * * @event TRTCCallback#onFirstAudioFrame * @param {String} userId - 远程用户 ID。 */ handleOnFirstAudioFrame(userId) { this.logger.log(`onFirstAudioFrame userId:${userId}`); this.fire('onFirstAudioFrame', userId); } /** * 首帧本地视频数据已经被送出 * * SDK 会在 enterRoom() 并 startLocalPreview() 成功后开始摄像头采集,并将采集到的画面进行编码。 * 当 SDK 成功向云端送出第一帧视频数据后,会抛出这个回调事件。 * * @event TRTCCallback#onSendFirstLocalVideoFrame * @param {Number} streamType - - 视频流类型,TRTCVideoStreamTypeBig:主路画面,一般用于摄像头;TRTCVideoStreamTypeSub:辅路画面,一般用于屏幕分享。 */ handleOnSendFirstLocalVideoFrame(streamType) { this.logger.log(`onSendFirstLocalVideoFrame streamType:${streamType}`); this.fire('onSendFirstLocalVideoFrame', streamType); } /** * 首帧本地音频数据已经被送出 * * SDK 会在 enterRoom() 并 startLocalAudio() 成功后开始麦克风采集,并将采集到的声音进行编码。 * 当 SDK 成功向云端送出第一帧音频数据后,会抛出这个回调事件。 * * @event TRTCCallback#onSendFirstLocalAudioFrame */ handleOnSendFirstLocalAudioFrame() { this.logger.log(`onSendFirstLocalAudioFrame`); this.fire('onSendFirstLocalAudioFrame'); } /** * 废弃事件:有主播加入当前房间 * * 该回调接口可以被看作是 onRemoteUserEnterRoom 的废弃版本,不推荐使用。请使用 onUserVideoAvailable 或 onRemoteUserEnterRoom 进行替代。 * * @deprecated 从 TRTCSDK6.8 后该接口已被废弃,不推荐使用 * @event TRTCCallback#onUserEnter * @param {String} userId - 用户标识 */ handleOnUserEnter(userId) { this.fire('onUserEnter', userId); } /** * 废弃事件:有主播离开当前房间 * * 该回调接口可以被看作是 onRemoteUserLeaveRoom 的废弃版本,不推荐使用。请使用 onUserVideoAvailable 或 onRemoteUserEnterRoom 进行替代。 * * @deprecated 从 TRTCSDK6.8 后该接口已被废弃,不推荐使用 * @event TRTCCallback#onUserExit * @param {String} userId - 用户标识 * @param {Number} reason - 离开原因代码,区分用户是正常离开,还是由于网络断线等原因离开。 */ handleOnUserExit(userId, reason) { this.fire('onUserExit', userId, reason); } ///////////////////////////////////////////////////////////////////////////////// // // (四)统计和质量回调 // ///////////////////////////////////////////////////////////////////////////////// /** * 网络质量:该回调每2秒触发一次,统计当前网络的上行和下行质量 * * 注意:userId == '' 代表自己当前的视频质量 * * @event TRTCCallback#onNetworkQuality * @param {TRTCQualityInfo} localQuality - 上行网络质量 * @param {TRTCQualityInfo[]} remoteQuality - 下行网络质量 */ handleOnNetworkQuality(localQuality, remoteQuality) { this.fire('onNetworkQuality', localQuality, remoteQuality); } /** * 技术指标统计回调 * * 如果您是熟悉音视频领域相关术语,可以通过这个回调获取 SDK 的所有技术指标。 * 如果您是首次开发音视频相关项目,可以只关注 onNetworkQuality 回调。 * * 注意:每2秒回调一次 * * @event TRTCCallback#onStatistics * @param {TRTCStatistics} statis - 统计数据,包括本地和远程的 */ handleOnStatistics(statis) { this.fire('onStatistics', statis); } ///////////////////////////////////////////////////////////////////////////////// // // (五)服务器事件回调 // ///////////////////////////////////////////////////////////////////////////////// /** * SDK 跟服务器的连接断开 * * @event TRTCCallback#onConnectionLost */ handleOnConnectionLost() { this.fire('onConnectionLost'); } /** * SDK 尝试重新连接到服务器 * * @event TRTCCallback#onTryToReconnect */ handleOnTryToReconnect() { this.fire('onTryToReconnect'); } /** * SDK 跟服务器的连接恢复 * * @event TRTCCallback#onConnectionRecovery */ handleOnConnectionRecovery() { this.fire('onConnectionRecovery'); } /** * 废弃事件:服务器测速的回调,SDK 对多个服务器 IP 做测速,每个 IP 的测速结果通过这个回调通知 * * @deprecated 从 TRTCSDK 9.3 后该接口已被废弃,不会抛出该事件, 请采用 onSpeedTestResult 事件代替 * * @event TRTCCallback#onSpeedTest * @param {TRTCSpeedTestResult} currentResult - 当前完成的测速结果 * @param {Number} finishedCount - 已完成测速的服务器数量 * @param {Number} totalCount - 需要测速的服务器总数量 */ bindOnSpeedTest() { // this.rtcCloud.onEvent('onSpeedTest', ( // currentResult: TRTCSpeedTestResult, // finishedCount: number, totalCount: number // ) => { // this.fire('onSpeedTest', currentResult, finishedCount, totalCount); // }); } /** * 网速测试的结果回调 * * 该统计回调由 startSpeedTest 触发。 * * @event TRTCCallback#onSpeedTestResult * @param {TRTCSpeedTestResult} result 网速测试数据数据,包括丢包、往返延迟、上下行的带宽速率。 */ handleOnSpeedTestResult(result) { this.fire('onSpeedTestResult', result); } ///////////////////////////////////////////////////////////////////////////////// // // (六)硬件设备事件回调 // ///////////////////////////////////////////////////////////////////////////////// /** * 摄像头准备就绪 * * @event TRTCCallback#onCameraDidReady */ handleOnCameraDidReady() { this.fire('onCameraDidReady'); } /** * 麦克风准备就绪 * * @event TRTCCallback#onMicDidReady */ handleOnMicDidReady() { this.fire('onMicDidReady'); } /** * userId 对应的成员语音音量 * * 您可以通过调用 TRTCCloud 中的 [enableAudioVolumeEvaluation()]{@link TRTCCloud#enableAudioVolumeEvaluation} 接口来开关这个回调或者设置它的触发间隔。 * 需要注意的是,调用 enableAudioVolumeEvaluation 开启音量回调后,无论频道内是否有人说话,都会按设置的时间间隔调用这个回调, * 如果没有人说话,则 totalVolume 为0。 * * @event TRTCCallback#onUserVoiceVolume * @param {TRTCVolumeInfo} userVolumes - 每位发言者的语音音量,取值范围0 - 100 * @param {Number} userVolumesCount - 发言者的人数,即 userVolumes 数组的大小 * @param {Number} totalVolume - 总的语音音量, 取值范围0 - 100 */ handleOnUserVoiceVolume(userVolumes, userVolumesCount, totalVolume) { this.fire('onUserVoiceVolume', userVolumes, userVolumesCount, totalVolume); } /** * 本地设备通断回调 * * @event TRTCCallback#onDeviceChange * @param {String} deviceId - Windows 端返回设备名,Mac 端返回设备 ID * @param {TRTCDeviceType} type - 设备类型 * @param {TRTCDeviceState} state - 事件类型 */ handleOnDeviceChange(deviceId, type, state) { this.fire('onDeviceChange', deviceId, type, state); } /** * 麦克风测试音量回调 * * 麦克风测试接口 [startMicDeviceTest()]{@link TRTCCloud#startMicDeviceTest} 会触发这个回调 * * @event TRTCCallback#onTestMicVolume * @param {Number} volume - 音量值,取值范围0 - 100 */ handleOnTestMicVolume(volume) { this.fire('onTestMicVolume', volume); } /** * 扬声器测试音量回调 * * 扬声器测试接口 [startSpeakerDeviceTest()]{@link TRTCCloud#startSpeakerDeviceTest} 会触发这个回调 * * @event TRTCCallback#onTestSpeakerVolume * @param {Number} volume - 音量值,取值范围0 - 100 */ handleOnTestSpeakerVolume(volume) { this.fire('onTestSpeakerVolume', volume); } /** * 当前音频采集设备音量变化回调 * * 使用 [enableAudioVolumeEvaluation]{@link TRTCCloud#enableAudioVolumeEvaluation}(interval>0)开启,(interval==0)关闭 * * @event TRTCCallback#onAudioDeviceCaptureVolumeChanged * @param {Number} volume - 音量值,取值范围0 - 100 * @param {Boolean} muted - 当前采集音频设备是否被静音 */ handleOnAudioDeviceCaptureVolumeChanged(volume, muted) { this.fire('onAudioDeviceCaptureVolumeChanged', volume, muted); } /** * 当前音频播放设备音量变化回调 * * 使用 [enableAudioVolumeEvaluation]{@link TRTCCloud#enableAudioVolumeEvaluation}(interval>0)开启,(interval==0)关闭 * * @event TRTCCallback#onAudioDevicePlayoutVolumeChanged * @param {Number} volume - 音量值,取值范围0 - 100 * @param {Boolean} muted - 当前音频播放设备是否被静音 */ handleOnAudioDevicePlayoutVolumeChanged(volume, muted) { this.fire('onAudioDevicePlayoutVolumeChanged', volume, muted); } ///////////////////////////////////////////////////////////////////////////////// // // (七)自定义消息的接收回调 // // ///////////////////////////////////////////////////////////////////////////////// /** * 收到自定义消息回调 * * 当房间中的某个用户使用 [sendCustomCmdMsg()]{@link TRTCCloud#sendCustomCmdMsg} 发送自定义消息时,房间中的其它用户可以通过 onRecvCustomCmdMsg 接口接收消息 * * @event TRTCCallback#onRecvCustomCmdMsg * @param {String} userId - 用户标识 * @param {Number} cmdId - 命令 ID * @param {Number} seq - 消息序号 * @param {String} msg - 消息数据 */ handleOnRecvCustomCmdMsg(userId, cmdId, seq, msg) { this.fire('onRecvCustomCmdMsg', userId, cmdId, seq, msg); } /** * 自定义消息丢失回调 * * 实时音视频使用 UDP 通道,即使设置了可靠传输(reliable)也无法确保100%不丢失,只是丢消息概率极低,能满足常规可靠性要求。 * 在发送端设置了可靠传输(reliable)后,SDK 都会通过此回调通知过去时间段内(通常为5s)传输途中丢失的自定义消息数量统计信息。 * * 注意:只有在发送端设置了可靠传输(reliable),接收方才能收到消息的丢失回调 * * @event TRTCCallback#onMissCustomCmdMsg * @param {String} userId - 用户标识 * @param {Number} cmdId - 命令 ID * @param {Number} errCode - 错误码 * @param {Number} missed - 丢失的消息数量 */ handleOnMissCustomCmdMsg(userId, cmdId, errCode, missed) { this.fire('onMissCustomCmdMsg', userId, cmdId, errCode, missed); } /** * 收到 SEI 消息的回调 * * 当房间中的某个用户使用 [sendSEIMsg()]{@link TRTCCloud#sendSEIMsg} 发送数据时,房间中的其它用户可以通过 onRecvSEIMsg 接口接收数据。 * * @event TRTCCallback#onRecvSEIMsg * @param {String} userId - 用户标识 * @param {String} message - 数据 */ handleOnRecvSEIMsg(userId, message) { this.fire('onRecvSEIMsg', userId, message); } ///////////////////////////////////////////////////////////////////////////////// // // (八)CDN 旁路转推回调 // ///////////////////////////////////////////////////////////////////////////////// /** * 开始向腾讯云的直播 CDN 推流的回调,对应于 TRTCCloud 中的 [startPublishing()]{@link TRTCCloud#startPublishing} 接口 * * @event TRTCCallback#onStartPublishing * @param {Number} errCode - 0 表示成功,其余值表示失败 * @param {String} errMsg - 具体错误原因 * @deprecated */ handleOnStartPublishing(errCode, errMsg) { this.fire('onStartPublishing', errCode, errMsg); } /** * 停止向腾讯云的直播 CDN 推流的回调,对应于 TRTCCloud 中的 [stopPublishing()]{@link TRTCCloud#stopPublishing} 接口 * * @event TRTCCallback#onStopPublishing * @param {Number} errCode - 0 表示成功,其余值表示失败 * @param {String} errMsg - 具体错误原因 * @deprecated */ handleOnStopPublishing(errCode, errMsg) { this.fire('onStopPublishing', errCode, errMsg); } /** * 旁路推流到 CDN 的回调 * * 对应于 TRTCCloud 的 [startPublishCDNStream()]{@link TRTCCloud#startPublishCDNStream} 接口 * * 注意:Start 回调如果成功,只能说明转推请求已经成功告知给腾讯云,如果目标 CDN 有异常,还是有可能会转推失败。 * * @event TRTCCallback#onStartPublishCDNStream * @param {Number} errCode - 错误码 * @param {String} errMsg - 错误详细信息 * @deprecated */ handleOnStartPublishCDNStream(errCode, errMsg) { this.fire('onStartPublishCDNStream', errCode, errMsg); } /** * 停止旁路推流到 CDN 的回调 * * 对应于 TRTCCloud 中的 [stopPublishCDNStream()]{@link TRTCCloud#stopPublishCDNStream} 接口 * * @event TRTCCallback#onStopPublishCDNStream * @param {Number} errCode - 错误码 * @param {String} errMsg - 错误详细信息 * @deprecated */ handleOnStopPublishCDNStream(errCode, errMsg) { this.fire('onStopPublishCDNStream', errCode, errMsg); } /** * 设置云端的混流转码参数的回调 * * 对应于 TRTCCloud 中的 [setMixTranscodingConfig()]{@link TRTCCloud#setMixTranscodingConfig} 接口 * * @event TRTCCallback#onSetMixTranscodingConfig * @param {Number} errCode - 错误码 * @param {String} errMsg - 错误详细信息 * @deprecated */ handleOnSetMixTranscodingConfig(errCode, errMsg) { this.fire('onSetMixTranscodingConfig', errCode, errMsg); } /** * 开始发布媒体流的事件回调 * * 当您调用 [startPublishMediaStream]{@link TRTCCloud#startPublishMediaStream} 开始向 TRTC 后台服务发布媒体流时,SDK 会立刻将这一指令同步给云端服务器,随后 SDK 会收到来自云端服务器的处理结果,并将指令的执行结果通过本事件回调通知给您。 * * @event TRTCCallback#onStartPublishMediaStream * @param taskId {String} - 当请求成功时,TRTC 后台会在回调中提供给您这项任务的 taskId,后续您可以通过该 taskId 结合 [updatePublishMediaStream]{@link TRTCCloud#updatePublishMediaStream} 和 [stopPublishMediaStream]{@link TRTCCloud#stopPublishMediaStream} 进行更新和停止。 * @pa