@rongcloud/plugin-call 5.0.4 → 5.0.5-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,15 +1,1717 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@rongcloud/plugin-call-engine"),o=require("@rongcloud/engine"),t=require("@rongcloud/plugin-rtc");const i=new o.Logger("RCCall");
2
- /*! *****************************************************************************
3
- Copyright (c) Microsoft Corporation.
4
-
5
- Permission to use, copy, modify, and/or distribute this software for any
6
- purpose with or without fee is hereby granted.
7
-
8
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14
- PERFORMANCE OF THIS SOFTWARE.
15
- ***************************************************************************** */function s(e,o,t,i){return new(t||(t=Promise))((function(s,r){function n(e){try{a(i.next(e))}catch(e){r(e)}}function l(e){try{a(i.throw(e))}catch(e){r(e)}}function a(e){var o;e.done?s(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(n,l)}a((i=i.apply(e,o||[])).next())}))}var r;!function(e){e[e.CALLER=1]="CALLER",e[e.CALLEE=2]="CALLEE"}(r||(r={}));var n=new class{constructor(){this.list={}}on(e,o){return(this.list[e]||(this.list[e]=[])).push(o),this}once(e,o){const t=i=>{this.off(e,t),o.call(this,i)};t.fun=o,this.on(e,t)}off(e,o){const t=this.list[e];if(!t)return!1;if(o){let e;for(let i=0,s=t.length;i<s;i++)if(e=t[i],e===o||e.fun===o){t.splice(i,1);break}}else t&&(t.length=0)}emit(e,o){const t=[...this.list[e]];if(!t||0===t.length)return!1;t.forEach((e=>{e.call(this,o)}))}};const l=t=>{if(!t)return{result:!1,msg:"Initialization missing parameter -> options"};if("object"!=typeof t)return{result:!1,msg:"Initialization options must be an object"};const i=Object.keys(t),s=[];return["rtcClient","onSession","onSessionClose"].forEach((e=>{i.includes(e)||s.push(e)})),s.length?{result:!1,msg:`Initialization missing parameter -> "${s.join(",")}"`}:"object"!=typeof t.rtcClient?{result:!1,msg:"Initialization 'rtcClient' parameter must be of type 'object'"}:"function"!=typeof t.onSession?{result:!1,msg:"Initialization 'onSession' parameter must be of type 'function'"}:"function"!=typeof t.onSessionClose?{result:!1,msg:"Initialization 'onSessionClose' parameter must be of type 'function'"}:void 0!==t.isAllowSubscribeRetry&&"boolean"!=typeof t.isAllowSubscribeRetry?{result:!1,msg:"Initialization 'isAllowSubscribeRetry' parameter must be of type 'boolean'"}:void 0!==t.isAllowPublishRetry&&"boolean"!=typeof t.isAllowPublishRetry?{result:!1,msg:"Initialization 'isAllowPublishRetry' parameter must be of type 'boolean'"}:void 0!==t.isOffCameraWhenVideoDisable&&"boolean"!=typeof t.isOffCameraWhenVideoDisable?{result:!1,msg:"Initialization 'isOffCameraWhenVideoDisable' parameter must be of type 'boolean'"}:void 0===t.joinType||(r=t.joinType,Object.values(o.RTCJoinType).includes(r))?void 0!==t.isAllowDemotionGetStream&&"boolean"!=typeof t.isAllowDemotionGetStream?{result:!1,msg:"Initialization 'isAllowDemotionGetStream' parameter must be of type 'boolean'"}:void 0===t.lang||function(o){return Object.values(e.RCCallLanguage).includes(o)}(t.lang)?void 0===t.logLevel||function(e){return Object.values(o.LogLevel).includes(e)}(t.logLevel)?void 0!==t.logStdout&&"function"!=typeof t.logStdout?{result:!1,msg:"Initialization 'logStdout' parameter must be of type 'function'"}:{result:!0}:{result:!1,msg:"Initialization 'logLevel' parameter must be of type correct type"}:{result:!1,msg:"Initialization 'lang' parameter must be of type correct type"}:{result:!1,msg:"Initialization 'joinType' parameter must be of type correct type"};var r},a=e=>{if(!e)return{result:!1,msg:"missing parameter -> listener"};if("object"!=typeof e)return{result:!1,msg:"listener must be an object"};const o=Object.keys(e),t=[];return["onRinging","onAccept","onHungup","onTrackReady"].forEach((e=>{o.includes(e)||t.push(e)})),t.length?{result:!1,msg:`missing parameter -> "${t.join(",")}"`}:"function"!=typeof e.onRinging?{result:!1,msg:"'onRinging' parameter must be of type 'function'"}:"function"!=typeof e.onAccept?{result:!1,msg:"'onAccept' parameter must be of type 'function'"}:"function"!=typeof e.onHungup?{result:!1,msg:"'onHungup' parameter must be of type 'function'"}:"function"!=typeof e.onTrackReady?{result:!1,msg:"'onTrackReady' parameter must be of type 'function'"}:{result:!0}},c=e=>e&&"string"==typeof e?{result:!0}:{result:!1,msg:"'targetId' parameter is required, must be of type 'string'"},C=o=>o===e.RCCallMediaType.AUDIO||o===e.RCCallMediaType.AUDIO_VIDEO?{result:!0}:{result:!1,msg:"'mediaType' parameter is required, must be of type 'RCCallMediaType'"},d=e=>Array.isArray(e)&&e.length?e.every((e=>"string"==typeof e&&e.length>0))?{result:!0}:{result:!1,msg:"'userIds' parameter is required"}:{result:!1,msg:"'userIds' parameter is required, must be of type 'string[]'"};class u{constructor(o,t,r={}){this._stateMachine=o,this._rtcClient=t,this._options=r,this._listener=null,this._RETRYCOUNT=2,this._stateMachine.registerEventListener({onUserStateChange:({user:e,reason:o})=>{i.info(`[RCCallSession onUserStateChange] userId->${null==e?void 0:e.userId} state->${null==e?void 0:e.state} reason->${o}`)},onStateChange:o=>s(this,void 0,void 0,(function*(){const{state:t,reason:s}=o;if(i.info(`[RCCallSession onStateChange] : state->${t} reason->${s}`),t===e.RCCallSessionState.KEEPING){const o=this._stateMachine.getCallId();i.info(`[RCCallSession onStateChange] roomId: ${o}`);try{yield this._joinRoom(o)}catch(t){this._exceptionClose(e.RCCallEndReason.NETWORK_ERROR),i.error(`[RCCallSession onStateChange] joinRoom throw exception roomId -> ${o}`),console.error(t)}}else if(t===e.RCCallSessionState.END){if(!this._room){this._options.localTracks&&this._destroyTracks(this._options.localTracks);const e=this._stateMachine.getSummary();return void n.emit("sessionClose",{session:this,summaryInfo:e})}this._options.localTracks&&this._destroyTracks(this._options.localTracks),i.info("[RCCallSession onStateChange] localTracks destroyed"),this._leaveRoom(),this._room=null}})),onRinging:e=>{i.info(`[RCCallSession onRinging]sender: sender.userId -> ${e.userId}`);try{this._listener.onRinging(e,this)}catch(e){i.error("[RCCallSession onRinging] method exception -> onRinging"),console.error(e)}},onAccept:e=>{i.info(`[RCCallSession onAccept]sender: sender.userId -> ${e.userId}`);try{this._listener.onAccept(e,this)}catch(e){i.error("[RCCallSession onAccept] method exception -> onAccept"),console.error(e)}},onHungup:(e,o)=>{i.info(`[RCCallSession onHungup]sender: sender.userId -> ${e.userId} reason->${o}`);try{this._listener.onHungup(e,o,this)}catch(e){i.error("[RCCallSession onHungup] method exception -> onHungup"),console.error(e)}},onMemberModify:({sender:e,invitedUsers:o})=>{i.info(`[RCCallSession onMemberModify] sender.userId -> ${e.userId}`);try{this._listener.onMemberModify(e,o,this)}catch(e){i.error("[RCCallSession onMemberModify] method exception -> onMemberModify"),console.error(e)}},onMediaModify:({sender:o,mediaType:t})=>{i.info(`[RCCallSession onMediaModify]sender: sender.userId -> ${o.userId} mediaType: ${t}`),t===e.RCCallMediaType.AUDIO&&this._setMediaTypeToAudio();try{this._listener.onMediaModify(o,t,this)}catch(e){i.error("[RCCallSession onMediaModify] method exception -> onMediaModify"),console.error(e)}}})}_joinRoom(o){return s(this,void 0,void 0,(function*(){try{const{code:s,room:r}=yield this._rtcClient.joinRTCRoom(o,this._options.joinType);if(s!==t.RCRTCCode.SUCCESS)return s===t.RCRTCCode.NOT_OPEN_VIDEO_AUDIO_SERVER&&this._exceptionClose(e.RCCallEndReason.SERVICE_NOT_OPENED),s===t.RCRTCCode.SIGNAL_JOIN_RTC_ROOM_REFUSED?this._exceptionClose(e.RCCallEndReason.OTHER_CLIENT_IN_CALL):this._exceptionClose(e.RCCallEndReason.NETWORK_ERROR),i.info(`[RCCallClient _joinRoom] join room failed: roomId -> ${o} RCRTCCode -> ${s}`),{code:e.RCCallErrorCode.JOIN_ROOM_ERROR};this._room=r}catch(t){return this._exceptionClose(e.RCCallEndReason.NETWORK_ERROR),i.error(`[RCCallSession _joinRoom] _rtcClient.joinRTCRoom throw exception roomId -> ${o}`),console.error(t),{code:e.RCCallErrorCode.JOIN_ROOM_ERROR}}this._registerRoomEventListener(),this._registerReportListener();try{yield this._subscribeInRoomRemoteTrack()}catch(t){return this._exceptionClose(e.RCCallEndReason.SUBSCRIBE_ERROR),i.error(`[RCCallSession _joinRoom] _subscribeInRoomRemoteTrack Exception roomId -> ${o}`),console.error(t),{code:e.RCCallErrorCode.JOIN_ROOM_ERROR}}try{yield this._publish()}catch(t){return this._exceptionClose(e.RCCallEndReason.PUBLISH_ERROR),i.error(`[RCCallSession _joinRoom] _publish Exception roomId -> ${o}`),console.error(t),{code:e.RCCallErrorCode.JOIN_ROOM_ERROR}}return{code:e.RCCallErrorCode.SUCCESS}}))}_subscribeInRoomRemoteTrack(){return s(this,void 0,void 0,(function*(){const o=this._room.getRemoteTracks();if(o.length){const{code:s}=yield this._subscribeRetry(o,this._options.isAllowSubscribeRetry,this._RETRYCOUNT);s!==t.RCRTCCode.SUCCESS&&(this._exceptionClose(e.RCCallEndReason.SUBSCRIBE_ERROR),i.error(`[RCCallSession _subscribeInRoomRemoteTrack] Resource subscription failed roomId -> ${this._stateMachine.getCallId()} RTC code -> ${s}`))}}))}_subscribeRetry(e,o=!1,r=0){return s(this,void 0,void 0,(function*(){const{code:s}=yield this._room.subscribe(e);if(s!==t.RCRTCCode.SUCCESS){try{this._listener.onTrackSubscribeFail&&this._listener.onTrackSubscribeFail(s,this)}catch(e){i.error("[RCCallSession] _listener.onTrackSubscribeFail exception"),console.error(e)}if(!o)return{code:s};if(r>0)return r--,this._subscribeRetry(e,o,r)}return{code:s}}))}_publish(){return s(this,void 0,void 0,(function*(){const o=this._options.localTracks,{code:s}=yield this._publishRetry(o,this._options.isAllowPublishRetry,this._RETRYCOUNT);if(s!==t.RCRTCCode.SUCCESS)return this._exceptionClose(e.RCCallEndReason.PUBLISH_ERROR),void i.info(`[RCCallSession _publist] Resource publishing failed: roomId -> ${this._stateMachine.getCallId()} RCRTCCode -> ${s}`);this._options.produceType===r.CALLEE&&this._notifyTrackReady(o)}))}_publishRetry(e,o=!1,r=0){return s(this,void 0,void 0,(function*(){const{code:s}=yield this._room.publish(e);if(s!==t.RCRTCCode.SUCCESS){try{this._listener.onTrackPublishFail&&this._listener.onTrackPublishFail(s,this)}catch(e){i.error("[RCCallSession] _listener.onTrackPublishFail exception"),console.error(e)}if(!o)return{code:s};if(r>0)return r--,this._publishRetry(e,o,r)}return{code:s}}))}_leaveRoom(){return s(this,void 0,void 0,(function*(){try{const{code:e}=yield this._rtcClient.leaveRoom(this._room);i.info("[RCCallSession _leaveRoom] Successfully exited the room")}catch(e){i.error("[RCCallSession _leaveRoom] leaveRoom throw exception"),console.error(e)}finally{const e=this._stateMachine.getSummary();n.emit("sessionClose",{session:this,summaryInfo:e})}}))}_exceptionClose(e){this._options.localTracks&&this._destroyTracks(this._options.localTracks),this._stateMachine.close(e)}registerSessionListener(e){const o=a(e);if(!o.result)throw new Error(`[RCCallSession registerSessionListener] ${o.msg}`);this._listener=Object.assign({},e)}_getLocalTrackCore(o,r){return s(this,void 0,void 0,(function*(){if(o===e.RCCallMediaType.AUDIO){const{code:o,track:s}=yield this._rtcClient.createMicrophoneAudioTrack("RongCloudRTC",r&&r.audio&&Object.assign({},r.audio));return o!==t.RCRTCCode.SUCCESS?(i.error(`[RCCallSession _getLocalTrackCore] get Audio local tracks failed RCT code -> ${o}`),{code:e.RCCallErrorCode.GET_LOCAL_AUDIO_TRACK_ERROR}):(i.info("[RCCallSession _getLocalTrackCore] successfully get Audio local tracks"),{code:e.RCCallErrorCode.SUCCESS,tracks:[s]})}{const{code:o,tracks:s}=yield this._rtcClient.createMicrophoneAndCameraTracks("RongCloudRTC",r&&Object.assign({},r));return o!==t.RCRTCCode.SUCCESS?(i.error(`[RCCallSession _getLocalTrackCore] get Audio and Video local tracks failed RCT code -> ${o}`),{code:e.RCCallErrorCode.GET_LOCAL_AUDIO_AND_VIDEO_TRACK_ERROR}):(i.info("[RCCallSession _getLocalTrackCore] successfully get audio and video local tracks"),{code:e.RCCallErrorCode.SUCCESS,tracks:s})}}))}_getLocalTrack(o,t){return s(this,void 0,void 0,(function*(){if(this._options.isAllowDemotionGetStream&&o===e.RCCallMediaType.AUDIO_VIDEO){const{code:o,tracks:i}=yield this._getLocalTrackCore(e.RCCallMediaType.AUDIO_VIDEO,t);if(o!==e.RCCallErrorCode.SUCCESS){const{code:o,tracks:i}=yield this._getLocalTrackCore(e.RCCallMediaType.AUDIO,t);return o!==e.RCCallErrorCode.SUCCESS?(this._exceptionClose(e.RCCallEndReason.GET_MEDIA_RESOURCES_ERROR),{code:o}):{code:o,tracks:i}}return{code:o,tracks:i}}{const{code:i,tracks:s}=yield this._getLocalTrackCore(o,t);return i!==e.RCCallErrorCode.SUCCESS?(this._exceptionClose(e.RCCallEndReason.GET_MEDIA_RESOURCES_ERROR),{code:i}):{code:i,tracks:s}}}))}changeAudioDevice(o){return s(this,void 0,void 0,(function*(){const s=[],r=[],{code:n,track:l}=yield this._rtcClient.createMicrophoneAudioTrack("RongCloudRTC",o);if(n!==t.RCRTCCode.SUCCESS)return i.error(`[RCCallSession changeDevice] get local Audio tracks failed RCTLib code -> ${n}`),{code:e.RCCallErrorCode.GET_LOCAL_AUDIO_TRACK_ERROR};if(this._options.localTracks&&this._options.localTracks.forEach((e=>{e.isAudioTrack()||r.push(e)})),s.push(l),r.push(l),this._options.localTracks=r,this._notifyTrackReady(s),this._room){const{code:o}=yield this._room.publish(s);if(o!==t.RCRTCCode.SUCCESS)return{code:e.RCCallErrorCode.AUDIO_PUBLISH_ERROR}}return{code:e.RCCallErrorCode.SUCCESS}}))}invite(t){return s(this,void 0,void 0,(function*(){if(this._stateMachine.getConversationType()!==o.ConversationType.GROUP)return{code:e.RCCallErrorCode.CONVERSATION_NOT_GROUP_ERROR};const i=d(t);if(!i.result)throw new Error(`[RCCallSession invite] ${i.msg}`);const{code:s}=yield this._stateMachine.invite(t);return{code:s}}))}accept(o){return s(this,void 0,void 0,(function*(){const t=(e=>{return e&&e.audio&&void 0!==e.audio.micphoneId&&"string"!=typeof e.audio.micphoneId?{result:!1,msg:"'constraints.audio.micphoneId' must be of type 'string'"}:e&&e.audio&&void 0!==e.audio.sampleRate&&"number"!=typeof e.audio.sampleRate?{result:!1,msg:"'constraints.audio.sampleRate' must be of type 'number'"}:e&&e.video&&void 0!==e.video.cameraId&&"string"!=typeof e.video.cameraId?{result:!1,msg:"'constraints.video.cameraId' must be of type 'string'"}:e&&e.video&&void 0!==e.video.frameRate&&"string"!=typeof e.video.frameRate?{result:!1,msg:"'constraints.video.frameRate' must be of type 'string'"}:e&&e.video&&void 0!==e.video.frameRate&&(o=e.video.frameRate,!["FPS_10","FPS_15","FPS_24","FPS_30"].includes(o))?{result:!1,msg:"'frameRate' value is out of range"}:e&&e.video&&void 0!==e.video.resolution&&"string"!=typeof e.video.resolution?{result:!1,msg:"'constraints.video.frameRate' must be of type 'string'"}:e&&e.video&&void 0!==e.video.resolution&&!function(e){return["W176_H132","W176_H144","W256_H144","W320_H180","W240_H240","W320_H240","W480_H360","W640_H360","W480_H480","W640_H480","W720_H480","W1280_H720","W1920_H1080"].includes(e)}(e.video.resolution)?{result:!1,msg:"'resolution' value is out of range"}:!e||!e.video||e.video.frameRate&&e.video.resolution?{result:!0}:{result:!1,msg:"'resolution' and 'resolution' is required"};var o})(o);if(!t.result)throw new Error(`[RCCallSession accept] ${t.msg}`);n.emit("hungupOtherSession",{session:this});const s=this._stateMachine.getMediaType(),{code:r,tracks:l}=yield this._getLocalTrack(s,o);if(r!==e.RCCallErrorCode.SUCCESS)return{code:r};this._options.localTracks=l;const{code:a}=yield this._stateMachine.accept();return a!==e.RCCallErrorCode.SUCCESS?(i.error(`[RCCallSession accept]Send accept message failed -> code: ${a}`),{code:a}):{code:a}}))}hungup(){return s(this,void 0,void 0,(function*(){return this._stateMachine.hungup()}))}_changeMediaType(o){return s(this,void 0,void 0,(function*(){const{code:t}=yield this._stateMachine.changeMediaType(o);return t!==e.RCCallErrorCode.SUCCESS&&i.error(`[RCCallSession _changeMediaType] change media type fail code-> ${t}`),{code:t}}))}_getLocalVideoTracks(){let e=[];return this._room?(this._options.localTracks&&(e=this._options.localTracks.filter((e=>e.isVideoTrack()))),e):e}_getLocalAudioTracks(){let e=[];return this._room?(this._options.localTracks&&(e=this._options.localTracks.filter((e=>e.isAudioTrack()))),e):e}_setMediaTypeToAudioAndVideo(){return s(this,void 0,void 0,(function*(){const{code:o,track:s}=yield this._rtcClient.createCameraVideoTrack();if(o!==t.RCRTCCode.SUCCESS)return{code:e.RCCallErrorCode.GET_LOCAL_AUDIO_AND_VIDEO_TRACK_ERROR};const{code:r}=yield this._room.publish([s]);r===t.RCRTCCode.SUCCESS?(this._notifyTrackReady([s]),this._changeMediaType(e.RCCallMediaType.AUDIO_VIDEO)):i.error(`[RCCallSession _enableVideo] Resource publishing failed: RCRTCCode -> ${o}`)}))}_setMediaTypeToAudio(){return s(this,void 0,void 0,(function*(){const e=this._getLocalVideoTracks();if(e.length){e.forEach((e=>{e.mute()}));const{code:o}=yield this._room.unpublish(e);o!==t.RCRTCCode.SUCCESS&&i.error(`[RCCallSession disableVideo] unpublish failed -> ${o}`),this._destroyTracks(e)}}))}descendAbility(){return s(this,void 0,void 0,(function*(){const{code:o}=yield this._changeMediaType(e.RCCallMediaType.AUDIO);return o===e.RCCallErrorCode.SUCCESS&&this._setMediaTypeToAudio(),{code:o}}))}disableVideoTrack(){return s(this,void 0,void 0,(function*(){const o=this._getLocalVideoTracks();if(!o.length)return i.error(`[RCCallSession disableVideoTrack] Room missing video track -> ${e.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR}`),{code:e.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR};if(o.forEach((e=>{e.mute()})),!this._options.isOffCameraWhenVideoDisable)return{code:e.RCCallErrorCode.SUCCESS};const{code:s}=yield this._room.unpublish(o);return s!==t.RCRTCCode.SUCCESS?(i.error(`[RCCallSession disableVideo] unpublish failed -> ${s}`),{code:e.RCCallErrorCode.UNPUBLISH_VIDEO_ERROR}):(o.forEach((e=>{e.destroy()})),{code:e.RCCallErrorCode.SUCCESS})}))}enableVideoTrack(){return s(this,void 0,void 0,(function*(){if(!this._options.isOffCameraWhenVideoDisable){const o=this._getLocalVideoTracks();return o.length?(o.forEach((e=>{e.unmute()})),{code:e.RCCallErrorCode.SUCCESS}):(i.error(`[RCCallSession EnableVideoTrack] Room missing video track -> ${e.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR}`),{code:e.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR})}const{code:o,track:s}=yield this._rtcClient.createCameraVideoTrack();if(o!==t.RCRTCCode.SUCCESS)return i.error(`[RCCallSession EnableVideoTrack] Get Resource failed: RCRTCCode -> ${o}`),{code:e.RCCallErrorCode.GET_LOCAL_VIDEO_TRACK_ERROR};const r=[];this._options.localTracks&&this._options.localTracks.forEach((e=>{e.isVideoTrack()?e.destroy():r.push(e)})),r.push(s),this._options.localTracks=r,s.mute();const{code:n}=yield this._room.publish([s]);return n!==t.RCRTCCode.SUCCESS?(i.error(`[RCCallSession EnableVideoTrack] Resource publishing failed: RCRTCCode -> ${o}`),{code:e.RCCallErrorCode.VIDEO_PUBLISH_ERROR}):(s.unmute(),this._notifyTrackReady([s]),{code:e.RCCallErrorCode.SUCCESS})}))}disableAudioTrack(){return s(this,void 0,void 0,(function*(){this._getLocalAudioTracks().forEach((e=>{e.mute()}))}))}enableAudioTrack(){return s(this,void 0,void 0,(function*(){this._getLocalAudioTracks().forEach((e=>{e.unmute()}))}))}_destroyTracks(e){e.forEach((e=>{e.destroy()}))}_notifyTrackReady(e){e.forEach((e=>{try{this._listener.onTrackReady(e,this)}catch(e){i.error("[RCCallSession _notifyTrackReady] _listener onTrackReady exception"),console.error(e)}}))}_registerRoomEventListener(){this._room.registerRoomEventListener({onKickOff:(o,i)=>{const s=this._rtcClient.getCurrentId();this._stateMachine.userLeave([s]),o?(i===t.RCKickReason.SERVER_KICK&&this._exceptionClose(e.RCCallEndReason.KICKED_BY_SERVER),i===t.RCKickReason.OTHER_KICK&&this._exceptionClose(e.RCCallEndReason.OTHER_CLIENT_JOINED_CALL)):this._exceptionClose(e.RCCallEndReason.NETWORK_ERROR)},onMessageReceive(e,o,t,i){},onRoomAttributeChange(e,o){},onAudioMuteChange:e=>{i.info(`[RCCallSession onAudioMuteChange] userId->${e.getUserId()} muted -> ${e.isOwnerMuted()}`);const o={userId:e.getUserId(),muted:e.isOwnerMuted(),kind:"audio",trackId:e.getTrackId()};try{this._listener.onAudioMuteChange(o,this)}catch(e){i.error("[RCCallSession onAudioMuteChange] Missing listening method -> onTrackMuteChange"),console.error(e)}},onVideoMuteChange:e=>{i.info(`[RCCallSession onVideoMuteChange]userId->${e.getUserId()} muted -> ${e.isOwnerMuted()}`);const o={userId:e.getUserId(),muted:e.isOwnerMuted(),kind:"video",trackId:e.getTrackId()};try{this._listener.onVideoMuteChange(o,this)}catch(e){i.error("[RCCallSession onVideoMuteChange] Missing listening method -> onVideoMuteChange"),console.error(e)}},onTrackPublish:e=>s(this,void 0,void 0,(function*(){if(this._room){const{code:o}=yield this._room.subscribe(e);o!==t.RCRTCCode.SUCCESS&&i.error(`[RCCallSession onTrackPublish] subscribe failed RTCCode ->${o}`)}})),onTrackUnpublish:e=>{},onTrackReady:o=>{this._stateMachine.getMediaType()===e.RCCallMediaType.AUDIO&&o.isVideoTrack()||this._notifyTrackReady([o])},onUserJoin:e=>{this._stateMachine.userJoin(e)},onUserLeave:e=>{i.info(`[RCCallSession onUserLeave] listening onUserLeave userIds -> ${null==e?void 0:e.join(",")}`),this._stateMachine.userLeave(e)},onPing:e=>{i.info(`[RCCallSession onPing]${e}`);try{this._listener.onPing&&this._listener.onPing(e,this)}catch(e){i.error("[RCCallSession onPing] listening onPing exception"),console.error(e)}}})}_registerReportListener(){this._room.registerReportListener({onStateReport:e=>{try{this._listener.onRTCStateReport&&this._listener.onRTCStateReport(e,this)}catch(e){i.error("[RCCallSession onStateReport] listener onStateReport exception"),console.error(e)}},onICEConnectionStateChange:e=>{try{this._listener.onICEConnectionStateChange&&this._listener.onICEConnectionStateChange(e,this)}catch(e){i.error("[RCCallSession onICEConnectionStateChange] onICEConnectionStateChange exception"),console.error(e)}}})}getSessionId(){return this._stateMachine.getCallId()}getRTCSessionId(){return this._room.getSessionId()}getTargetId(){return this._stateMachine.getTargetId()}getConversationType(){return this._stateMachine.getConversationType()}getChannelId(){return this._stateMachine.getChannelId()}getRemoteUsers(){return this._stateMachine.getRemoteUsers()}getUserState(e){if(!e||"string"!=typeof e)throw new Error("userId is required, must be of type 'string'");return this._stateMachine.getUserState(e)}getState(){return this._stateMachine.getState()}getCallerId(){return this._stateMachine.getCallerId()}getMediaType(){return this._stateMachine.getMediaType()}}o.VersionManage.add("plugin-call","5.0.4"),o.VersionManage.validEngine(">=4.5.2")||i.error(`The current engine version '${o.VersionManage.getInfo().engine}' error,plugin-call required engine version at least '>=4.5.2'.`);class R{constructor(t,s,r){this._context=t,this._runtime=s,this._sessionList=[],this._rtcClient=r.rtcClient,this._options=Object.assign({isAllowPublishRetry:!1,isAllowSubscribeRetry:!1,isOffCameraWhenVideoDisable:!0,joinType:o.RTCJoinType.COEXIST,isAllowDemotionGetStream:!1,lang:e.RCCallLanguage.ZH},r),this._callEngine=new e.RCCallEngine(this._context,s,i,{onInvite:this._onInvite.bind(this),onOfflineRecord:this._onOfflineRecord.bind(this)},{lang:this._options.lang||e.RCCallLanguage.ZH}),n.on("sessionClose",(({session:e,summaryInfo:o})=>{this._removeSession(e);try{this._options.onSessionClose(e,o)}catch(e){i.error("[RCCCallClient] options.onSessionClose exception"),console.log(e)}})),n.on("hungupOtherSession",(({session:e})=>{const o=e.getSessionId();i.info(`[RCCallClient hungupOtherSession] sessionId ready to accept -> ${o}`),i.info(`[RCCallClient hungupOtherSession] sessionList ->${this._sessionList.map((e=>e.getSessionId())).join(",")}`);let t=0;for(;this._sessionList.length>1;)this._sessionList[t].getSessionId()!==o?(this._sessionList[t].hungup(),this._sessionList.splice(t,1)):t++;i.info(`[RCCallClient hungupOtherSession] current sessionList length ->${this._sessionList.length}`)}))}_onInvite(e){i.info("[RCCallClient _onInvite] Received invite message");const o=new u(e,this._rtcClient,{isAllowSubscribeRetry:this._options.isAllowSubscribeRetry,isAllowPublishRetry:this._options.isAllowPublishRetry,isOffCameraWhenVideoDisable:this._options.isOffCameraWhenVideoDisable,joinType:this._options.joinType,isAllowDemotionGetStream:this._options.isAllowDemotionGetStream,produceType:r.CALLEE});i.info("[RCCallClient _onInvite] Received invite message, successfully created session"),this._sessionList.push(o);try{this._options.onSession(o)}catch(e){i.error("[RCCallClient _options.onSession] onSession exception"),console.log(e)}if(!o._listener)throw i.error("[RCCallClient _options.onSession] session Must Have Listener"),new Error("[RCCallSession _options.onSession] session Must Have Listener");{const e=a(o._listener);if(!e.result)throw new Error(e.msg)}}_onOfflineRecord(e){try{this._options.onOfflineRecord&&this._options.onOfflineRecord(e)}catch(e){i.error("[RCCallClient _options.onOfflineRecord] onOfflineRecord exception"),console.log(e)}}registerUserInfo(e={}){this._callEngine.registerUserInfo(e),i.info("[RCCallClient registerUserInfo] successfully register user info data")}call({targetId:o,mediaType:t=e.RCCallMediaType.AUDIO,listener:n,constraints:l,channelId:d=""}){return s(this,void 0,void 0,(function*(){const s=[c(o),C(t),a(n)],R=[];if(!s.every((e=>(!e.result&&R.push(e.msg),e.result))))throw new Error(`[RCCallClient call] ${R.join("\n")}`);let h=[];const{code:_,tracks:p}=yield this._getLocalTrack(t,l);if(_!==e.RCCallErrorCode.SUCCESS)return{code:_};h=p,h.forEach((e=>{n.onTrackReady(e)}));const{code:g,stateMachine:f}=yield this._callEngine.call(d,o,t);if(g===e.RCCallErrorCode.SUCCESS&&f){i.info("[RCCallClient call] successfully created state machine");const e=new u(f,this._rtcClient,{localTracks:h,isAllowSubscribeRetry:this._options.isAllowSubscribeRetry,isAllowPublishRetry:this._options.isAllowPublishRetry,isOffCameraWhenVideoDisable:this._options.isOffCameraWhenVideoDisable,joinType:this._options.joinType,isAllowDemotionGetStream:this._options.isAllowDemotionGetStream,produceType:r.CALLER});return e.registerSessionListener(n),this._sessionList.push(e),i.info(`[RCCallClient call] successfully created session object, sessionId: ${e.getSessionId()}`),{code:g,session:e}}return i.error(`[RCCallClient call] call failed code ->: ${g}`),h.forEach((e=>{e.mute(),e.destroy()})),{code:g}}))}callInGroup({targetId:o,userIds:t,mediaType:n=e.RCCallMediaType.AUDIO,listener:l,constraints:R,channelId:h=""}){return s(this,void 0,void 0,(function*(){const s=[c(o),d(t),C(n),a(l)],_=[];if(!s.every((e=>(!e.result&&_.push(e.msg),e.result))))throw new Error(`[RCCallClient callInGroup] ${_.join("\n")}`);let p=[];const{code:g,tracks:f}=yield this._getLocalTrack(n,R);if(g!==e.RCCallErrorCode.SUCCESS)return{code:g};p=f,p.forEach((e=>{l.onTrackReady(e)}));const{code:S,stateMachine:m}=yield this._callEngine.callInGroup(h,o,n,t);if(S===e.RCCallErrorCode.SUCCESS&&m){i.info("[RCCallClient callInGroup] successfully created state machine");const e=new u(m,this._rtcClient,{localTracks:p,isAllowSubscribeRetry:this._options.isAllowSubscribeRetry,isAllowPublishRetry:this._options.isAllowPublishRetry,isOffCameraWhenVideoDisable:this._options.isOffCameraWhenVideoDisable,joinType:this._options.joinType,isAllowDemotionGetStream:this._options.isAllowDemotionGetStream,produceType:r.CALLER});return e.registerSessionListener(l),this._sessionList.push(e),i.info(`[RCCallClient callInGroup] successfully created session object, sessionId: ${e.getSessionId()}`),{code:S,session:e}}return i.info(`[RCCallClient callInGroup] callInGroup failed code -> ${S}`),p.forEach((e=>{e.mute(),e.destroy()})),{code:S}}))}_getLocalTrackCore(o,r){return s(this,void 0,void 0,(function*(){if(o===e.RCCallMediaType.AUDIO){const{code:o,track:s}=yield this._rtcClient.createMicrophoneAudioTrack("RongCloudRTC",r&&r.audio&&Object.assign({},r.audio));return o!==t.RCRTCCode.SUCCESS?(i.error(`[RCCallClient _getTrack] get Audio local tracks failed RCT code -> ${o}`),{code:e.RCCallErrorCode.GET_LOCAL_AUDIO_TRACK_ERROR}):(i.info("[RCCallClient _getTrack] successfully get Audio local tracks"),{code:e.RCCallErrorCode.SUCCESS,tracks:[s]})}{const{code:o,tracks:s}=yield this._rtcClient.createMicrophoneAndCameraTracks("RongCloudRTC",r&&Object.assign({},r));return o!==t.RCRTCCode.SUCCESS?(i.error(`[RCCallClient _getTrack] get Audio and Video local tracks failed RCT code -> ${o}`),{code:e.RCCallErrorCode.GET_LOCAL_AUDIO_AND_VIDEO_TRACK_ERROR}):(i.info("[RCCallClient _getTrack] successfully get audio and video local tracks"),{code:e.RCCallErrorCode.SUCCESS,tracks:s})}}))}_getLocalTrack(o,t){return s(this,void 0,void 0,(function*(){if(this._options.isAllowDemotionGetStream&&o===e.RCCallMediaType.AUDIO_VIDEO){const{code:o,tracks:i}=yield this._getLocalTrackCore(e.RCCallMediaType.AUDIO_VIDEO,t);if(o!==e.RCCallErrorCode.SUCCESS){const{code:o,tracks:i}=yield this._getLocalTrackCore(e.RCCallMediaType.AUDIO,t);return o!==e.RCCallErrorCode.SUCCESS?{code:o}:{code:o,tracks:i}}return{code:o,tracks:i}}{const{code:i,tracks:s}=yield this._getLocalTrackCore(o,t);return i!==e.RCCallErrorCode.SUCCESS?{code:i}:{code:i,tracks:s}}}))}_removeSession(e){const o=e.getSessionId();this._sessionList=this._sessionList.filter((e=>e.getSessionId()!==o))}getJoinedRoomInfo(){return s(this,void 0,void 0,(function*(){const{code:t,data:s}=yield this._context.getRTCJoinedUserInfo(this._context.getCurrentId());return t!==o.ErrorCode.SUCCESS?(i.error("getJoinedUserInfo error",t),{code:e.RCCallErrorCode.QUERY_JOINED_USER_INFO_ERROR}):{code:e.RCCallErrorCode.SUCCESS,data:s}}))}}const h={tag:"RCCall",verify:e=>"browser"===e.tag,setup(e,o,t){const s=l(t);if(!s.result)throw new Error(`[RCCallLib installer steup]${s.msg}`);return i.setLogLevel(t.logLevel),i.setLogStdout(t.logStdout),i.warn("RCCall Version: 5.0.4, Commit: 821c103f7abc8a24e22ce0e488dd5228818f9f13"),new R(e,o,t)}};Object.defineProperty(exports,"RCCallEndReason",{enumerable:!0,get:function(){return e.RCCallEndReason}}),Object.defineProperty(exports,"RCCallErrorCode",{enumerable:!0,get:function(){return e.RCCallErrorCode}}),Object.defineProperty(exports,"RCCallLanguage",{enumerable:!0,get:function(){return e.RCCallLanguage}}),Object.defineProperty(exports,"RCCallMediaType",{enumerable:!0,get:function(){return e.RCCallMediaType}}),Object.defineProperty(exports,"RCCallSessionState",{enumerable:!0,get:function(){return e.RCCallSessionState}}),Object.defineProperty(exports,"RCCallUserState",{enumerable:!0,get:function(){return e.RCCallUserState}}),exports.RCCallClient=R,exports.RCCallSession=u,exports.installer=h;
1
+ /*
2
+ * RCCall - v5.0.5-alpha.1
3
+ * CommitId - 92132946bea607c42e2d1fe5fe641bed05997834
4
+ * Mon Dec 06 2021 16:29:43 GMT+0800 (China Standard Time)
5
+ * ©2020 RongCloud, Inc. All rights reserved.
6
+ */
7
+ 'use strict';
8
+
9
+ Object.defineProperty(exports, '__esModule', { value: true });
10
+
11
+ var pluginCallEngine = require('@rongcloud/plugin-call-engine');
12
+ var engine = require('@rongcloud/engine');
13
+ var pluginRtc = require('@rongcloud/plugin-rtc');
14
+
15
+ const logger = new engine.Logger('RCCall');
16
+
17
+ /*! *****************************************************************************
18
+ Copyright (c) Microsoft Corporation.
19
+
20
+ Permission to use, copy, modify, and/or distribute this software for any
21
+ purpose with or without fee is hereby granted.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
24
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
25
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
26
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
27
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
28
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
29
+ PERFORMANCE OF THIS SOFTWARE.
30
+ ***************************************************************************** */
31
+
32
+ function __awaiter(thisArg, _arguments, P, generator) {
33
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
34
+ return new (P || (P = Promise))(function (resolve, reject) {
35
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
36
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
37
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
38
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
39
+ });
40
+ }
41
+
42
+ /**
43
+ * 产生session的场景
44
+ */
45
+ var ProduceTypes;
46
+ (function (ProduceTypes) {
47
+ /**
48
+ * 主叫
49
+ */
50
+ ProduceTypes[ProduceTypes["CALLER"] = 1] = "CALLER";
51
+ /**
52
+ * 被叫
53
+ */
54
+ ProduceTypes[ProduceTypes["CALLEE"] = 2] = "CALLEE";
55
+ })(ProduceTypes || (ProduceTypes = {}));
56
+
57
+ class EventEmitter {
58
+ constructor() {
59
+ this.list = {};
60
+ }
61
+ on(event, fun) {
62
+ (this.list[event] || (this.list[event] = [])).push(fun);
63
+ return this;
64
+ }
65
+ once(event, fun) {
66
+ const on = (data) => {
67
+ this.off(event, on);
68
+ fun.call(this, data);
69
+ };
70
+ on.fun = fun;
71
+ this.on(event, on);
72
+ }
73
+ off(event, fun) {
74
+ const funs = this.list[event];
75
+ if (!funs) {
76
+ return false;
77
+ }
78
+ if (!fun) {
79
+ // 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fun 都清空
80
+ funs && (funs.length = 0);
81
+ }
82
+ else {
83
+ // 若有 fun,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
84
+ let cb;
85
+ for (let i = 0, length = funs.length; i < length; i++) {
86
+ cb = funs[i];
87
+ if (cb === fun || cb.fun === fun) {
88
+ funs.splice(i, 1);
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ emit(event, data) {
95
+ // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
96
+ const funs = [...this.list[event]];
97
+ // 如果缓存列表里没有 fun 就返回 false
98
+ if (!funs || funs.length === 0) {
99
+ return false;
100
+ }
101
+ // 遍历 event 值对应的缓存列表,依次执行 fn
102
+ funs.forEach(fun => {
103
+ fun.call(this, data);
104
+ });
105
+ }
106
+ }
107
+
108
+ var eventEmitter = new EventEmitter();
109
+
110
+ function isLanguage(val) {
111
+ const values = Object.values(pluginCallEngine.RCCallLanguage);
112
+ return values.includes(val);
113
+ }
114
+ function isJoinType(val) {
115
+ const values = Object.values(engine.RTCJoinType);
116
+ return values.includes(val);
117
+ }
118
+ function isLogLevel(val) {
119
+ const values = Object.values(engine.LogLevel);
120
+ return values.includes(val);
121
+ }
122
+ const validateCallInitOptions = (options) => {
123
+ if (!options) {
124
+ return { result: false, msg: 'Initialization missing parameter -> options' };
125
+ }
126
+ if (typeof options !== 'object') {
127
+ return { result: false, msg: 'Initialization options must be an object' };
128
+ }
129
+ const keyNames = ['rtcClient', 'onSession', 'onSessionClose'];
130
+ const keys = Object.keys(options);
131
+ const missingKeys = [];
132
+ // 校验填项是否都包含,如果哪个不包含就收集起来
133
+ keyNames.forEach((key) => {
134
+ if (!keys.includes(key)) {
135
+ missingKeys.push(key);
136
+ }
137
+ });
138
+ // 如果缺少必填的监听函数或对象
139
+ if (missingKeys.length) {
140
+ return { result: false, msg: `Initialization missing parameter -> "${missingKeys.join(',')}"` };
141
+ }
142
+ if (typeof options.rtcClient !== 'object') {
143
+ return { result: false, msg: 'Initialization \'rtcClient\' parameter must be of type \'object\'' };
144
+ }
145
+ if (typeof options.onSession !== 'function') {
146
+ return { result: false, msg: 'Initialization \'onSession\' parameter must be of type \'function\'' };
147
+ }
148
+ if (typeof options.onSessionClose !== 'function') {
149
+ return { result: false, msg: 'Initialization \'onSessionClose\' parameter must be of type \'function\'' };
150
+ }
151
+ // 如果传了isAllowSubscribeRetry 但不是boolean类型
152
+ if (typeof options.isAllowSubscribeRetry !== 'undefined' && typeof options.isAllowSubscribeRetry !== 'boolean') {
153
+ return { result: false, msg: 'Initialization \'isAllowSubscribeRetry\' parameter must be of type \'boolean\'' };
154
+ }
155
+ // 如果传了,但不是boolean类型
156
+ if (typeof options.isAllowPublishRetry !== 'undefined' && typeof options.isAllowPublishRetry !== 'boolean') {
157
+ return { result: false, msg: 'Initialization \'isAllowPublishRetry\' parameter must be of type \'boolean\'' };
158
+ }
159
+ // 如果传了,但不是boolean类型
160
+ if (typeof options.isOffCameraWhenVideoDisable !== 'undefined' && typeof options.isOffCameraWhenVideoDisable !== 'boolean') {
161
+ return { result: false, msg: 'Initialization \'isOffCameraWhenVideoDisable\' parameter must be of type \'boolean\'' };
162
+ }
163
+ // 如果传了,但不是RTCJoinType的枚举
164
+ if (typeof options.joinType !== 'undefined' && !isJoinType(options.joinType)) {
165
+ return { result: false, msg: 'Initialization \'joinType\' parameter must be of type correct type' };
166
+ }
167
+ // 如果传了,但不是boolean类型
168
+ if (typeof options.isAllowDemotionGetStream !== 'undefined' && typeof options.isAllowDemotionGetStream !== 'boolean') {
169
+ return { result: false, msg: 'Initialization \'isAllowDemotionGetStream\' parameter must be of type \'boolean\'' };
170
+ }
171
+ // 如果传了,但不是RCCallLanguage的枚举
172
+ if (typeof options.lang !== 'undefined' && !isLanguage(options.lang)) {
173
+ return { result: false, msg: 'Initialization \'lang\' parameter must be of type correct type' };
174
+ }
175
+ // 如果传了,但不是LogLevel的枚举
176
+ if (typeof options.logLevel !== 'undefined' && !isLogLevel(options.logLevel)) {
177
+ return { result: false, msg: 'Initialization \'logLevel\' parameter must be of type correct type' };
178
+ }
179
+ // 如果传了,但不是function类型
180
+ if (typeof options.logStdout !== 'undefined' && typeof options.logStdout !== 'function') {
181
+ return { result: false, msg: 'Initialization \'logStdout\' parameter must be of type \'function\'' };
182
+ }
183
+ return { result: true };
184
+ };
185
+ /**
186
+ * 校验registerSessionListener参数
187
+ */
188
+ const validateListener = (listener) => {
189
+ if (!listener) {
190
+ return { result: false, msg: 'missing parameter -> listener' };
191
+ }
192
+ if (typeof listener !== 'object') {
193
+ return { result: false, msg: 'listener must be an object' };
194
+ }
195
+ const keyNames = ['onRinging', 'onAccept', 'onHungup', 'onMemberModify', 'onMediaModify', 'onTrackReady'];
196
+ const keys = Object.keys(listener);
197
+ const missingKeys = [];
198
+ keyNames.forEach((key) => {
199
+ if (!keys.includes(key)) {
200
+ missingKeys.push(key);
201
+ }
202
+ });
203
+ if (missingKeys.length) {
204
+ return { result: false, msg: `missing parameter -> "${missingKeys.join(',')}"` };
205
+ }
206
+ if (typeof listener.onRinging !== 'function') {
207
+ return { result: false, msg: '\'onRinging\' parameter must be of type \'function\'' };
208
+ }
209
+ if (typeof listener.onAccept !== 'function') {
210
+ return { result: false, msg: '\'onAccept\' parameter must be of type \'function\'' };
211
+ }
212
+ if (typeof listener.onHungup !== 'function') {
213
+ return { result: false, msg: '\'onHungup\' parameter must be of type \'function\'' };
214
+ }
215
+ if (typeof listener.onMemberModify !== 'function') {
216
+ return { result: false, msg: '\'onMemberModify\' parameter must be of type \'function\'' };
217
+ }
218
+ if (typeof listener.onMediaModify !== 'function') {
219
+ return { result: false, msg: '\'onMediaModify\' parameter must be of type \'function\'' };
220
+ }
221
+ if (typeof listener.onTrackReady !== 'function') {
222
+ return { result: false, msg: '\'onTrackReady\' parameter must be of type \'function\'' };
223
+ }
224
+ return { result: true };
225
+ };
226
+ const validateTargetId = (targetId) => {
227
+ if (targetId && typeof targetId === 'string') {
228
+ return { result: true };
229
+ }
230
+ else {
231
+ return { result: false, msg: '\'targetId\' parameter is required, must be of type \'string\'' };
232
+ }
233
+ };
234
+ const validateMediaType = (mediaType) => {
235
+ if (mediaType === pluginCallEngine.RCCallMediaType.AUDIO || mediaType === pluginCallEngine.RCCallMediaType.AUDIO_VIDEO) {
236
+ return { result: true };
237
+ }
238
+ else {
239
+ return { result: false, msg: '\'mediaType\' parameter is required, must be of type \'RCCallMediaType\'' };
240
+ }
241
+ };
242
+ const validateUserIds = (userIds) => {
243
+ if (!Array.isArray(userIds)) {
244
+ return { result: false, msg: '\'userIds\' parameter is required, must be of type \'string[]\'' };
245
+ }
246
+ if (!userIds.length) {
247
+ return { result: false, msg: '\'userIds\' parameter is required, must be of type \'string[]\'' };
248
+ }
249
+ if (!userIds.every(str => typeof str === 'string' && str.length > 0)) {
250
+ return { result: false, msg: '\'userIds\' parameter is required' };
251
+ }
252
+ return { result: true };
253
+ };
254
+ function isRCFrameRate(val) {
255
+ const arrs = ['FPS_10', 'FPS_15', 'FPS_24', 'FPS_30'];
256
+ return arrs.includes(val);
257
+ }
258
+ function isRCResolution(val) {
259
+ const arrs = ['W176_H132', 'W176_H144', 'W256_H144', 'W320_H180', 'W240_H240', 'W320_H240', 'W480_H360', 'W640_H360', 'W480_H480', 'W640_H480', 'W720_H480', 'W1280_H720', 'W1920_H1080'];
260
+ return arrs.includes(val);
261
+ }
262
+ const validateMediaStreamConstraints = (constraints) => {
263
+ if (constraints && constraints.audio && typeof constraints.audio.micphoneId !== 'undefined' && typeof constraints.audio.micphoneId !== 'string') {
264
+ return { result: false, msg: '\'constraints.audio.micphoneId\' must be of type \'string\'' };
265
+ }
266
+ if (constraints && constraints.audio && typeof constraints.audio.sampleRate !== 'undefined' && typeof constraints.audio.sampleRate !== 'number') {
267
+ return { result: false, msg: '\'constraints.audio.sampleRate\' must be of type \'number\'' };
268
+ }
269
+ if (constraints && constraints.video && typeof constraints.video.cameraId !== 'undefined' && typeof constraints.video.cameraId !== 'string') {
270
+ return { result: false, msg: '\'constraints.video.cameraId\' must be of type \'string\'' };
271
+ }
272
+ // if (constraints && constraints.video && typeof constraints.video.faceMode !== 'undefined' && constraints.video.cameraId !== 'user' && constraints.video.faceMode !== 'environment') {
273
+ // return { result: false, msg: '\'constraints.video.cameraId\' must be \'user\' or \'environment\'' }
274
+ // }
275
+ if (constraints && constraints.video && typeof constraints.video.frameRate !== 'undefined' && typeof constraints.video.frameRate !== 'string') {
276
+ return { result: false, msg: '\'constraints.video.frameRate\' must be of type \'string\'' };
277
+ }
278
+ if (constraints && constraints.video && typeof constraints.video.frameRate !== 'undefined' && !isRCFrameRate(constraints.video.frameRate)) {
279
+ return { result: false, msg: '\'frameRate\' value is out of range' };
280
+ }
281
+ if (constraints && constraints.video && typeof constraints.video.resolution !== 'undefined' && typeof constraints.video.resolution !== 'string') {
282
+ return { result: false, msg: '\'constraints.video.frameRate\' must be of type \'string\'' };
283
+ }
284
+ if (constraints && constraints.video && typeof constraints.video.resolution !== 'undefined' && !isRCResolution(constraints.video.resolution)) {
285
+ return { result: false, msg: '\'resolution\' value is out of range' };
286
+ }
287
+ if (constraints && constraints.video && (!constraints.video.frameRate || !constraints.video.resolution)) {
288
+ return { result: false, msg: '\'resolution\' and \'resolution\' is required' };
289
+ }
290
+ return { result: true };
291
+ };
292
+
293
+ class RCCallSession {
294
+ constructor(
295
+ /**
296
+ * 状态机实例
297
+ */
298
+ _stateMachine,
299
+ /**
300
+ * rtc实例
301
+ */
302
+ _rtcClient,
303
+ /**
304
+ * session的其它选项
305
+ */
306
+ _options = {}) {
307
+ this._stateMachine = _stateMachine;
308
+ this._rtcClient = _rtcClient;
309
+ this._options = _options;
310
+ /**
311
+ * 用户传进来的 对session的监听 (要在RCCallClient的_onInvite里判断,要求执行完onSession必须注册session的监听,所以这里是public)
312
+ */
313
+ this._listener = null;
314
+ /**
315
+ * RTC订阅、发布重试的次数
316
+ */
317
+ this._RETRYCOUNT = 2;
318
+ // 监听状态机
319
+ this._stateMachine.registerEventListener({
320
+ /**
321
+ * 用户状态变更
322
+ * @param info
323
+ */
324
+ onUserStateChange: ({ user, reason }) => {
325
+ logger.info(`[RCCallSession onUserStateChange] userId->${user === null || user === void 0 ? void 0 : user.userId} state->${user === null || user === void 0 ? void 0 : user.state} reason->${reason}`);
326
+ },
327
+ /**
328
+ * 房间状态变更
329
+ * @param
330
+ */
331
+ onStateChange: (info) => __awaiter(this, void 0, void 0, function* () {
332
+ const { state, reason } = info;
333
+ logger.info(`[RCCallSession onStateChange] : state->${state} reason->${reason}`);
334
+ // 如果在通话中,就加房间
335
+ if (state === pluginCallEngine.RCCallSessionState.KEEPING) {
336
+ const roomId = this._stateMachine.getCallId();
337
+ logger.info(`[RCCallSession onStateChange] roomId: ${roomId}`);
338
+ try {
339
+ // 加房间
340
+ yield this._joinRoom(roomId);
341
+ }
342
+ catch (error) {
343
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.NETWORK_ERROR);
344
+ logger.error(`[RCCallSession onStateChange] joinRoom throw exception roomId -> ${roomId}`);
345
+ console.error(error);
346
+ }
347
+ /**
348
+ * 以下三条只要满足一条,状态会变成RCCallSessionState.END
349
+ * 1、本端用户自己主动挂断
350
+ * 2、服务端把本端用户踢出RTC房间
351
+ * 3、房间里小于2个人
352
+ */
353
+ }
354
+ else if (state === pluginCallEngine.RCCallSessionState.END) {
355
+ // 还未加入房间就挂断
356
+ if (!this._room) {
357
+ // 销毁本地流,关闭摄像头
358
+ this._options.localTracks && this._destroyTracks(this._options.localTracks);
359
+ const summaryInfo = this._stateMachine.getSummary();
360
+ eventEmitter.emit('sessionClose', { session: this, summaryInfo });
361
+ return;
362
+ }
363
+ this._options.localTracks && this._destroyTracks(this._options.localTracks);
364
+ logger.info('[RCCallSession onStateChange] localTracks destroyed');
365
+ this._leaveRoom();
366
+ this._room = null;
367
+ }
368
+ }),
369
+ /**
370
+ * 收到响铃
371
+ * @param sender 发起用户信息
372
+ */
373
+ onRinging: (sender) => {
374
+ logger.info(`[RCCallSession onRinging]sender: sender.userId -> ${sender.userId}`);
375
+ try {
376
+ // 通知用户响铃
377
+ this._listener.onRinging(sender, this);
378
+ }
379
+ catch (error) {
380
+ logger.error('[RCCallSession onRinging] method exception -> onRinging');
381
+ console.error(error);
382
+ }
383
+ },
384
+ /**
385
+ * 当远端用户同意接听
386
+ */
387
+ onAccept: (sender) => {
388
+ logger.info(`[RCCallSession onAccept]sender: sender.userId -> ${sender.userId}`);
389
+ try {
390
+ // 通知本端,远端用户已接听
391
+ this._listener.onAccept(sender, this);
392
+ }
393
+ catch (error) {
394
+ logger.error('[RCCallSession onAccept] method exception -> onAccept');
395
+ console.error(error);
396
+ }
397
+ },
398
+ /**
399
+ * 当有远端用户挂断
400
+ */
401
+ onHungup: (sender, reason) => {
402
+ logger.info(`[RCCallSession onHungup]sender: sender.userId -> ${sender.userId} reason->${reason}`);
403
+ try {
404
+ // 通知本端,远端用户已挂断
405
+ this._listener.onHungup(sender, reason, this);
406
+ }
407
+ catch (error) {
408
+ logger.error('[RCCallSession onHungup] method exception -> onHungup');
409
+ console.error(error);
410
+ }
411
+ },
412
+ /**
413
+ * 收到人员变更
414
+ * @param sender 发起用户信息
415
+ */
416
+ onMemberModify: ({ sender, invitedUsers }) => {
417
+ logger.info(`[RCCallSession onMemberModify] sender.userId -> ${sender.userId}`);
418
+ try {
419
+ // 通知用户人员变更
420
+ this._listener.onMemberModify(sender, invitedUsers, this);
421
+ }
422
+ catch (error) {
423
+ logger.error('[RCCallSession onMemberModify] method exception -> onMemberModify');
424
+ console.error(error);
425
+ }
426
+ },
427
+ /**
428
+ * 收到通话类型变更 (通话降级)
429
+ * @param sender 发起用户信息
430
+ */
431
+ onMediaModify: ({ sender, mediaType }) => {
432
+ logger.info(`[RCCallSession onMediaModify]sender: sender.userId -> ${sender.userId} mediaType: ${mediaType}`);
433
+ if (mediaType === pluginCallEngine.RCCallMediaType.AUDIO) {
434
+ // 远端收到通话降级通知后,远端执行降级通话(不发消息)
435
+ this._setMediaTypeToAudio();
436
+ }
437
+ try {
438
+ this._listener.onMediaModify(sender, mediaType, this);
439
+ }
440
+ catch (error) {
441
+ logger.error('[RCCallSession onMediaModify] method exception -> onMediaModify');
442
+ console.error(error);
443
+ }
444
+ }
445
+ });
446
+ }
447
+ /**
448
+ * 加入房间
449
+ */
450
+ _joinRoom(roomId) {
451
+ return __awaiter(this, void 0, void 0, function* () {
452
+ try {
453
+ // 加房间
454
+ const { code, room } = yield this._rtcClient.joinRTCRoom(roomId, this._options.joinType);
455
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
456
+ // 如果音视频服务未开通
457
+ if (code === pluginRtc.RCRTCCode.NOT_OPEN_VIDEO_AUDIO_SERVER) {
458
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.SERVICE_NOT_OPENED);
459
+ // 己方其他端已在通话中
460
+ }
461
+ if (code === pluginRtc.RCRTCCode.SIGNAL_JOIN_RTC_ROOM_REFUSED) {
462
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.OTHER_CLIENT_IN_CALL);
463
+ }
464
+ else {
465
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.NETWORK_ERROR);
466
+ }
467
+ logger.info(`[RCCallClient _joinRoom] join room failed: roomId -> ${roomId} RCRTCCode -> ${code}`);
468
+ return { code: pluginCallEngine.RCCallErrorCode.JOIN_ROOM_ERROR };
469
+ }
470
+ this._room = room;
471
+ }
472
+ catch (error) {
473
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.NETWORK_ERROR);
474
+ logger.error(`[RCCallSession _joinRoom] _rtcClient.joinRTCRoom throw exception roomId -> ${roomId}`);
475
+ console.error(error);
476
+ return { code: pluginCallEngine.RCCallErrorCode.JOIN_ROOM_ERROR };
477
+ }
478
+ // 房间上注册监听事件
479
+ this._registerRoomEventListener();
480
+ // 注册房间质量数据监听器
481
+ this._registerReportListener();
482
+ try {
483
+ // 订阅远程的流,把远程的流抛给用户
484
+ yield this._subscribeInRoomRemoteTrack();
485
+ }
486
+ catch (error) {
487
+ // 结束通话session
488
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.SUBSCRIBE_ERROR);
489
+ logger.error(`[RCCallSession _joinRoom] _subscribeInRoomRemoteTrack Exception roomId -> ${roomId}`);
490
+ console.error(error);
491
+ return { code: pluginCallEngine.RCCallErrorCode.JOIN_ROOM_ERROR };
492
+ }
493
+ try {
494
+ // 往房间里发布本地资源
495
+ yield this._publish();
496
+ }
497
+ catch (error) {
498
+ // 结束通话session
499
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.PUBLISH_ERROR);
500
+ logger.error(`[RCCallSession _joinRoom] _publish Exception roomId -> ${roomId}`);
501
+ console.error(error);
502
+ return { code: pluginCallEngine.RCCallErrorCode.JOIN_ROOM_ERROR };
503
+ }
504
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS };
505
+ });
506
+ }
507
+ /**
508
+ * (初始化房间的时候) 订阅远程的流,把远程的流抛给用户
509
+ */
510
+ _subscribeInRoomRemoteTrack() {
511
+ return __awaiter(this, void 0, void 0, function* () {
512
+ // 获取所有远程已发布的音视频资源列表
513
+ const tracks = this._room.getRemoteTracks();
514
+ if (tracks.length) {
515
+ const { code } = yield this._subscribeRetry(tracks, this._options.isAllowSubscribeRetry, this._RETRYCOUNT);
516
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
517
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.SUBSCRIBE_ERROR);
518
+ logger.error(`[RCCallSession _subscribeInRoomRemoteTrack] Resource subscription failed roomId -> ${this._stateMachine.getCallId()} RTC code -> ${code}`);
519
+ }
520
+ }
521
+ });
522
+ }
523
+ /**
524
+ * 可以重试的订阅
525
+ * @param params.tracks tracks
526
+ * @param params.isAllowSubscribeRetry 是否允许重试
527
+ * @param params.count 允许重试的次数
528
+ */
529
+ _subscribeRetry(tracks, isAllowSubscribeRetry = false, count = 0) {
530
+ return __awaiter(this, void 0, void 0, function* () {
531
+ const { code } = yield this._room.subscribe(tracks);
532
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
533
+ try {
534
+ this._listener.onTrackSubscribeFail && this._listener.onTrackSubscribeFail(code, this);
535
+ }
536
+ catch (error) {
537
+ logger.error('[RCCallSession] _listener.onTrackSubscribeFail exception');
538
+ console.error(error);
539
+ }
540
+ // 如果不允许重试,直接返回
541
+ if (!isAllowSubscribeRetry) {
542
+ return { code };
543
+ }
544
+ if (count > 0) {
545
+ count--;
546
+ return this._subscribeRetry(tracks, isAllowSubscribeRetry, count);
547
+ }
548
+ }
549
+ return { code };
550
+ });
551
+ }
552
+ /**
553
+ * 发布本地资源的逻辑
554
+ *
555
+ */
556
+ _publish() {
557
+ return __awaiter(this, void 0, void 0, function* () {
558
+ const tracks = this._options.localTracks;
559
+ const { code } = yield this._publishRetry(tracks, this._options.isAllowPublishRetry, this._RETRYCOUNT);
560
+ // 若资源发布失败
561
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
562
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.PUBLISH_ERROR);
563
+ logger.info(`[RCCallSession _publist] Resource publishing failed: roomId -> ${this._stateMachine.getCallId()} RCRTCCode -> ${code}`);
564
+ return;
565
+ }
566
+ // 如果是主动发起的呼叫,已提前抛出了资源, 被动呼叫,这里才需要抛出
567
+ if (this._options.produceType === ProduceTypes.CALLEE) {
568
+ // 向外抛出本地流, 通知业务层trackReady
569
+ this._notifyTrackReady(tracks);
570
+ }
571
+ });
572
+ }
573
+ /**
574
+ * 可以重试的发布
575
+ * @param params.tracks tracks
576
+ * @param params.isAllowPublishRetry 是否允许重试
577
+ * @param params.count 允许重试的次数
578
+ */
579
+ _publishRetry(tracks, isAllowPublishRetry = false, count = 0) {
580
+ return __awaiter(this, void 0, void 0, function* () {
581
+ const { code } = yield this._room.publish(tracks);
582
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
583
+ try {
584
+ this._listener.onTrackPublishFail && this._listener.onTrackPublishFail(code, this);
585
+ }
586
+ catch (error) {
587
+ logger.error('[RCCallSession] _listener.onTrackPublishFail exception');
588
+ console.error(error);
589
+ }
590
+ // 如果不允许重试,直接返回
591
+ if (!isAllowPublishRetry) {
592
+ return { code };
593
+ }
594
+ if (count > 0) {
595
+ count--;
596
+ return this._publishRetry(tracks, isAllowPublishRetry, count);
597
+ }
598
+ }
599
+ return { code };
600
+ });
601
+ }
602
+ /**
603
+ * 退出房间
604
+ */
605
+ _leaveRoom() {
606
+ return __awaiter(this, void 0, void 0, function* () {
607
+ try {
608
+ // 退出房间
609
+ const { code } = yield this._rtcClient.leaveRoom(this._room);
610
+ // 成功退出房间,触发RCCallClient实例上的onSessionClose监听,抛给用户信息
611
+ logger.info('[RCCallSession _leaveRoom] Successfully exited the room');
612
+ }
613
+ catch (error) {
614
+ logger.error('[RCCallSession _leaveRoom] leaveRoom throw exception');
615
+ console.error(error);
616
+ }
617
+ finally {
618
+ const summaryInfo = this._stateMachine.getSummary();
619
+ eventEmitter.emit('sessionClose', { session: this, summaryInfo });
620
+ }
621
+ });
622
+ }
623
+ /**
624
+ * 出现异常后要处理的逻辑,
625
+ * @param endReason 原因
626
+ */
627
+ _exceptionClose(endReason) {
628
+ // 销毁本地流
629
+ this._options.localTracks && this._destroyTracks(this._options.localTracks);
630
+ // 结束状态机
631
+ this._stateMachine.close(endReason);
632
+ }
633
+ /**
634
+ * 用户调用的,注册session上的监听
635
+ */
636
+ registerSessionListener(listener) {
637
+ // 先校验listener, 如果不通过,会trow error
638
+ const conclusion = validateListener(listener);
639
+ if (!conclusion.result) {
640
+ throw new Error(`[RCCallSession registerSessionListener] ${conclusion.msg}`);
641
+ }
642
+ this._listener = Object.assign({}, listener);
643
+ }
644
+ /**
645
+ * 调RTC API 获得本地流
646
+ */
647
+ _getLocalTrackCore(mediaType, constraints) {
648
+ return __awaiter(this, void 0, void 0, function* () {
649
+ // 检测是否能够获得本地流
650
+ if (mediaType === pluginCallEngine.RCCallMediaType.AUDIO) {
651
+ const { code, track } = yield this._rtcClient.createMicrophoneAudioTrack('RongCloudRTC', constraints && constraints.audio && Object.assign({}, constraints.audio));
652
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
653
+ logger.error(`[RCCallSession _getLocalTrackCore] get Audio local tracks failed RCT code -> ${code}`);
654
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_AUDIO_TRACK_ERROR };
655
+ }
656
+ logger.info('[RCCallSession _getLocalTrackCore] successfully get Audio local tracks');
657
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS, tracks: [track] };
658
+ }
659
+ else {
660
+ const { code, tracks } = yield this._rtcClient.createMicrophoneAndCameraTracks('RongCloudRTC', constraints && Object.assign({}, constraints));
661
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
662
+ logger.error(`[RCCallSession _getLocalTrackCore] get Audio and Video local tracks failed RCT code -> ${code}`);
663
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_AUDIO_AND_VIDEO_TRACK_ERROR };
664
+ }
665
+ logger.info('[RCCallSession _getLocalTrackCore] successfully get audio and video local tracks');
666
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS, tracks };
667
+ }
668
+ });
669
+ }
670
+ _getLocalTrack(mediaType, constraints) {
671
+ return __awaiter(this, void 0, void 0, function* () {
672
+ // 并且是获得音视频, 并且 (如果获得音视频不成功,允许降级获得音频)
673
+ if (this._options.isAllowDemotionGetStream && mediaType === pluginCallEngine.RCCallMediaType.AUDIO_VIDEO) {
674
+ const { code, tracks } = yield this._getLocalTrackCore(pluginCallEngine.RCCallMediaType.AUDIO_VIDEO, constraints);
675
+ // 如果音视频不能获得,就降级获得音频
676
+ if (code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
677
+ const { code, tracks } = yield this._getLocalTrackCore(pluginCallEngine.RCCallMediaType.AUDIO, constraints);
678
+ if (code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
679
+ // 获取资源失败,需要调状态机state 为 end
680
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.GET_MEDIA_RESOURCES_ERROR);
681
+ return { code };
682
+ }
683
+ return { code, tracks: tracks };
684
+ }
685
+ return { code, tracks: tracks };
686
+ }
687
+ else {
688
+ const { code: _code, tracks } = yield this._getLocalTrackCore(mediaType, constraints);
689
+ if (_code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
690
+ // 获取资源失败,需要调状态机state 为 end
691
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.GET_MEDIA_RESOURCES_ERROR);
692
+ return { code: _code };
693
+ }
694
+ return { code: _code, tracks: tracks };
695
+ }
696
+ });
697
+ }
698
+ /**
699
+ * 通话中更换音频设备
700
+ */
701
+ changeAudioDevice(audioConstraints) {
702
+ return __awaiter(this, void 0, void 0, function* () {
703
+ // 新设备的track
704
+ const recentTracks = [];
705
+ // 整理后的本地track
706
+ const localTracks = [];
707
+ const { code, track } = yield this._rtcClient.createMicrophoneAudioTrack('RongCloudRTC', audioConstraints);
708
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
709
+ logger.error(`[RCCallSession changeDevice] get local Audio tracks failed RCTLib code -> ${code}`);
710
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_AUDIO_TRACK_ERROR };
711
+ }
712
+ this._options.localTracks && this._options.localTracks.forEach((track) => {
713
+ if (track.isAudioTrack()) {
714
+ // 之前的音频都销毁
715
+ track.destroy();
716
+ }
717
+ else {
718
+ // 只把之前的视频留下
719
+ localTracks.push(track);
720
+ }
721
+ });
722
+ recentTracks.push(track);
723
+ // 加上本地新产生的音频
724
+ localTracks.push(track);
725
+ this._options.localTracks = localTracks;
726
+ // 通知业务层trackReady
727
+ this._notifyTrackReady(recentTracks);
728
+ // 如果当前已加入房间,发布新流
729
+ if (this._room) {
730
+ // 发布新流
731
+ const { code } = yield this._room.publish(recentTracks);
732
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
733
+ return { code: pluginCallEngine.RCCallErrorCode.AUDIO_PUBLISH_ERROR };
734
+ }
735
+ }
736
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS };
737
+ });
738
+ }
739
+ /**
740
+ * 群呼叫中继续邀请
741
+ * @param userIds 被邀请用户 ID 列表
742
+ */
743
+ invite(userIds) {
744
+ return __awaiter(this, void 0, void 0, function* () {
745
+ const conversationType = this._stateMachine.getConversationType();
746
+ // 如果当前不是群组通话,直接返回错误码
747
+ if (conversationType !== engine.ConversationType.GROUP) {
748
+ return { code: pluginCallEngine.RCCallErrorCode.CONVERSATION_NOT_GROUP_ERROR };
749
+ }
750
+ const conclusion = validateUserIds(userIds);
751
+ if (!conclusion.result) {
752
+ throw new Error(`[RCCallSession invite] ${conclusion.msg}`);
753
+ }
754
+ const { code } = yield this._stateMachine.invite(userIds);
755
+ return { code };
756
+ });
757
+ }
758
+ /**
759
+ * 同意接听
760
+ */
761
+ accept(constraints) {
762
+ return __awaiter(this, void 0, void 0, function* () {
763
+ const conclusion = validateMediaStreamConstraints(constraints);
764
+ if (!conclusion.result) {
765
+ throw new Error(`[RCCallSession accept] ${conclusion.msg}`);
766
+ }
767
+ // 接听之前,先挂断当前之外的session,现阶段不允许用户先择接听session,事先会在状态机内部挂断,这里抛出去,会清理其它的seesion
768
+ eventEmitter.emit('hungupOtherSession', { session: this });
769
+ const mediaType = this._stateMachine.getMediaType();
770
+ const { code: _code, tracks } = yield this._getLocalTrack(mediaType, constraints);
771
+ if (_code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
772
+ return { code: _code };
773
+ }
774
+ this._options.localTracks = tracks;
775
+ // 发送接听的消息
776
+ const { code } = yield this._stateMachine.accept();
777
+ if (code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
778
+ logger.error(`[RCCallSession accept]Send accept message failed -> code: ${code}`);
779
+ return { code };
780
+ }
781
+ return { code };
782
+ });
783
+ }
784
+ /**
785
+ * 挂断
786
+ */
787
+ hungup() {
788
+ return __awaiter(this, void 0, void 0, function* () {
789
+ return this._stateMachine.hungup();
790
+ });
791
+ }
792
+ /**
793
+ * 通话媒体变更
794
+ * @param mediaType RCCallMediaType.AUDIO 改为音频通话 | RCCallMediaType.AUDIO_VIDEO 改为音视频通话
795
+ */
796
+ _changeMediaType(mediaType) {
797
+ return __awaiter(this, void 0, void 0, function* () {
798
+ const { code } = yield this._stateMachine.changeMediaType(mediaType);
799
+ if (code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
800
+ logger.error(`[RCCallSession _changeMediaType] change media type fail code-> ${code}`);
801
+ }
802
+ return { code };
803
+ });
804
+ }
805
+ /**
806
+ * 获得本地视频
807
+ */
808
+ _getLocalVideoTracks() {
809
+ let localVideoTracks = [];
810
+ if (!this._room) {
811
+ return localVideoTracks;
812
+ }
813
+ if (this._options.localTracks) {
814
+ localVideoTracks = this._options.localTracks.filter((track) => {
815
+ return track.isVideoTrack();
816
+ });
817
+ }
818
+ return localVideoTracks;
819
+ }
820
+ /**
821
+ * 获得本地音频
822
+ */
823
+ _getLocalAudioTracks() {
824
+ let localAudiotracks = [];
825
+ if (!this._room) {
826
+ return localAudiotracks;
827
+ }
828
+ if (this._options.localTracks) {
829
+ localAudiotracks = this._options.localTracks.filter((track) => {
830
+ return track.isAudioTrack();
831
+ });
832
+ }
833
+ return localAudiotracks;
834
+ }
835
+ /**
836
+ * 把通话的MediaType升级到音视频
837
+ */
838
+ _setMediaTypeToAudioAndVideo() {
839
+ return __awaiter(this, void 0, void 0, function* () {
840
+ // 获得本端视频资源
841
+ const { code, track } = yield this._rtcClient.createCameraVideoTrack();
842
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
843
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_AUDIO_AND_VIDEO_TRACK_ERROR };
844
+ }
845
+ // 发布本端视频资源
846
+ const { code: _code } = yield this._room.publish([track]);
847
+ // 若资源发布失败
848
+ if (_code !== pluginRtc.RCRTCCode.SUCCESS) {
849
+ logger.error(`[RCCallSession _enableVideo] Resource publishing failed: RCRTCCode -> ${code}`);
850
+ return;
851
+ }
852
+ // 通知业务层trackReady
853
+ this._notifyTrackReady([track]);
854
+ // 发消息
855
+ this._changeMediaType(pluginCallEngine.RCCallMediaType.AUDIO_VIDEO);
856
+ });
857
+ }
858
+ /**
859
+ * 把通话的MediaType降级到音频
860
+ * @param isSendMesssage 是否需要发消息, 默认发消息
861
+ */
862
+ _setMediaTypeToAudio() {
863
+ return __awaiter(this, void 0, void 0, function* () {
864
+ const tracks = this._getLocalVideoTracks();
865
+ if (tracks.length) {
866
+ // 禁用视频
867
+ tracks.forEach((track) => {
868
+ track.mute();
869
+ });
870
+ // 取消发布视频
871
+ const { code } = yield this._room.unpublish(tracks);
872
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
873
+ logger.error(`[RCCallSession disableVideo] unpublish failed -> ${code}`);
874
+ }
875
+ // 关闭摄像头
876
+ this._destroyTracks(tracks);
877
+ }
878
+ });
879
+ }
880
+ /**
881
+ * 通话降级,目前需求只做通话降级,音视频可以降级为音频,音频不能升到音视频, 发消息成功才算降级成功
882
+ *
883
+ */
884
+ descendAbility() {
885
+ return __awaiter(this, void 0, void 0, function* () {
886
+ const { code } = yield this._changeMediaType(pluginCallEngine.RCCallMediaType.AUDIO);
887
+ if (code === pluginCallEngine.RCCallErrorCode.SUCCESS) {
888
+ this._setMediaTypeToAudio();
889
+ }
890
+ return { code };
891
+ });
892
+ }
893
+ /**
894
+ * 禁用视频track
895
+ */
896
+ disableVideoTrack() {
897
+ return __awaiter(this, void 0, void 0, function* () {
898
+ const tracks = this._getLocalVideoTracks();
899
+ if (!tracks.length) {
900
+ logger.error(`[RCCallSession disableVideoTrack] Room missing video track -> ${pluginCallEngine.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR}`);
901
+ return { code: pluginCallEngine.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR };
902
+ }
903
+ // 禁用视频
904
+ tracks.forEach((track) => {
905
+ track.mute();
906
+ });
907
+ // 如果不需关闭摄像头
908
+ if (!this._options.isOffCameraWhenVideoDisable) {
909
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS };
910
+ }
911
+ // 取消发布视频
912
+ const { code } = yield this._room.unpublish(tracks);
913
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
914
+ logger.error(`[RCCallSession disableVideo] unpublish failed -> ${code}`);
915
+ return { code: pluginCallEngine.RCCallErrorCode.UNPUBLISH_VIDEO_ERROR };
916
+ }
917
+ tracks.forEach((track) => {
918
+ // 关闭摄像头
919
+ track.destroy();
920
+ });
921
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS };
922
+ });
923
+ }
924
+ /**
925
+ * 启用视频track
926
+ */
927
+ enableVideoTrack() {
928
+ return __awaiter(this, void 0, void 0, function* () {
929
+ // 如果不需关闭摄像头
930
+ if (!this._options.isOffCameraWhenVideoDisable) {
931
+ const tracks = this._getLocalVideoTracks();
932
+ if (!tracks.length) {
933
+ logger.error(`[RCCallSession EnableVideoTrack] Room missing video track -> ${pluginCallEngine.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR}`);
934
+ return { code: pluginCallEngine.RCCallErrorCode.MISSING_VIDEO_TRACK_ERROR };
935
+ }
936
+ // 启用视频
937
+ tracks.forEach((track) => {
938
+ track.unmute();
939
+ });
940
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS };
941
+ }
942
+ // 获得本端视频资源
943
+ const { code, track } = yield this._rtcClient.createCameraVideoTrack();
944
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
945
+ logger.error(`[RCCallSession EnableVideoTrack] Get Resource failed: RCRTCCode -> ${code}`);
946
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_VIDEO_TRACK_ERROR };
947
+ }
948
+ const localTracks = [];
949
+ this._options.localTracks && this._options.localTracks.forEach((track) => {
950
+ if (track.isVideoTrack()) {
951
+ // 之前的视频都销毁
952
+ track.destroy();
953
+ }
954
+ else {
955
+ // 只留下之前的音频
956
+ localTracks.push(track);
957
+ }
958
+ });
959
+ // 加上本地新产生的视频
960
+ localTracks.push(track);
961
+ this._options.localTracks = localTracks;
962
+ // 为了触发对方的onVideoMuteChange 先禁用
963
+ track.mute();
964
+ // 发布本端视频资源
965
+ const { code: _code } = yield this._room.publish([track]);
966
+ // 若资源发布失败
967
+ if (_code !== pluginRtc.RCRTCCode.SUCCESS) {
968
+ logger.error(`[RCCallSession EnableVideoTrack] Resource publishing failed: RCRTCCode -> ${code}`);
969
+ return { code: pluginCallEngine.RCCallErrorCode.VIDEO_PUBLISH_ERROR };
970
+ }
971
+ // 启用
972
+ track.unmute();
973
+ // 通知业务层trackReady
974
+ this._notifyTrackReady([track]);
975
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS };
976
+ });
977
+ }
978
+ /**
979
+ * 禁用音频track
980
+ */
981
+ disableAudioTrack() {
982
+ return __awaiter(this, void 0, void 0, function* () {
983
+ const tracks = this._getLocalAudioTracks();
984
+ // 禁用音频
985
+ tracks.forEach((track) => {
986
+ track.mute();
987
+ });
988
+ });
989
+ }
990
+ /**
991
+ * 启用音频track
992
+ */
993
+ enableAudioTrack() {
994
+ return __awaiter(this, void 0, void 0, function* () {
995
+ const tracks = this._getLocalAudioTracks();
996
+ // 启用音频
997
+ tracks.forEach((track) => {
998
+ track.unmute();
999
+ });
1000
+ });
1001
+ }
1002
+ /**
1003
+ * 销毁本地流
1004
+ */
1005
+ _destroyTracks(tracks) {
1006
+ tracks.forEach((track) => {
1007
+ track.destroy();
1008
+ });
1009
+ }
1010
+ /**
1011
+ * 向外抛出本地流
1012
+ */
1013
+ _notifyTrackReady(tracks) {
1014
+ tracks.forEach((track) => {
1015
+ try {
1016
+ this._listener.onTrackReady(track, this);
1017
+ }
1018
+ catch (error) {
1019
+ logger.error('[RCCallSession _notifyTrackReady] _listener onTrackReady exception');
1020
+ console.error(error);
1021
+ }
1022
+ });
1023
+ }
1024
+ /**
1025
+ * 房间上注册事件
1026
+ */
1027
+ _registerRoomEventListener() {
1028
+ this._room.registerRoomEventListener({
1029
+ /**
1030
+ * 本端被踢出房间时触发
1031
+ * @description 被踢出房间可能是由于服务端超出一定时间未能收到 rtcPing 消息,所以认为己方离线。
1032
+ * 另一种可能是己方 rtcPing 失败次数超出上限,故而主动断线
1033
+ * @param byServer
1034
+ * 当值为 false 时,说明本端 rtcPing 超时
1035
+ * 当值为 true 时,说明本端收到被踢出房间通知
1036
+ */
1037
+ onKickOff: (byServer, state) => {
1038
+ const currentUserId = this._rtcClient.getCurrentId();
1039
+ this._stateMachine.userLeave([currentUserId]);
1040
+ if (!byServer) {
1041
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.NETWORK_ERROR);
1042
+ }
1043
+ else {
1044
+ if (state === pluginRtc.RCKickReason.SERVER_KICK) {
1045
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.KICKED_BY_SERVER);
1046
+ }
1047
+ if (state === pluginRtc.RCKickReason.OTHER_KICK) {
1048
+ this._exceptionClose(pluginCallEngine.RCCallEndReason.OTHER_CLIENT_JOINED_CALL);
1049
+ }
1050
+ }
1051
+ },
1052
+ /**
1053
+ * 接收到房间信令时回调,用户可通过房间实例的 `sendMessage(name, content)` 接口发送信令
1054
+ * @param name 信令名
1055
+ * @param content 信令内容
1056
+ * @param senderUserId 发送者 Id
1057
+ * @param messageUId 消息唯一标识
1058
+ */
1059
+ onMessageReceive: (name, content, senderUserId, messageUId) => {
1060
+ try {
1061
+ // 通知给业务
1062
+ this._listener.onMessageReceive && this._listener.onMessageReceive(name, content, senderUserId, messageUId, this);
1063
+ }
1064
+ catch (error) {
1065
+ logger.error('[RCCallSession onMessageReceive] listening onMessageReceive exception');
1066
+ console.error(error);
1067
+ }
1068
+ },
1069
+ /**
1070
+ * 监听房间属性变更通知
1071
+ * @param name
1072
+ * @param content
1073
+ */
1074
+ onRoomAttributeChange: (name, content) => {
1075
+ try {
1076
+ // 通知给业务
1077
+ this._listener.onRoomAttributeChange && this._listener.onRoomAttributeChange(name, content, this);
1078
+ }
1079
+ catch (error) {
1080
+ logger.error('[RCCallSession onRoomAttributeChange] listening onRoomAttributeChange exception');
1081
+ console.error(error);
1082
+ }
1083
+ },
1084
+ /**
1085
+ * 发布者禁用/启用音频
1086
+ * @param audioTrack RCRemoteAudioTrack 类实例
1087
+ */
1088
+ onAudioMuteChange: (audioTrack) => {
1089
+ logger.info(`[RCCallSession onAudioMuteChange] userId->${audioTrack.getUserId()} muted -> ${audioTrack.isOwnerMuted()}`);
1090
+ const muteUser = {
1091
+ userId: audioTrack.getUserId(),
1092
+ muted: audioTrack.isOwnerMuted(),
1093
+ kind: 'audio',
1094
+ trackId: audioTrack.getTrackId()
1095
+ };
1096
+ try {
1097
+ // 通知给业务
1098
+ this._listener.onAudioMuteChange(muteUser, this);
1099
+ }
1100
+ catch (error) {
1101
+ logger.error('[RCCallSession onAudioMuteChange] Missing listening method -> onTrackMuteChange');
1102
+ console.error(error);
1103
+ }
1104
+ },
1105
+ /**
1106
+ * 发布者禁用/启用视频
1107
+ * @param videoTrack RCRemoteVideoTrack 类实例对象
1108
+ */
1109
+ onVideoMuteChange: (videoTrack) => {
1110
+ logger.info(`[RCCallSession onVideoMuteChange]userId->${videoTrack.getUserId()} muted -> ${videoTrack.isOwnerMuted()}`);
1111
+ const muteUser = {
1112
+ userId: videoTrack.getUserId(),
1113
+ muted: videoTrack.isOwnerMuted(),
1114
+ kind: 'video',
1115
+ trackId: videoTrack.getTrackId()
1116
+ };
1117
+ try {
1118
+ // 通知给业务
1119
+ this._listener.onVideoMuteChange(muteUser, this);
1120
+ }
1121
+ catch (error) {
1122
+ logger.error('[RCCallSession onVideoMuteChange] Missing listening method -> onVideoMuteChange');
1123
+ console.error(error);
1124
+ }
1125
+ },
1126
+ /**
1127
+ * 房间内其他用户新发布资源时触发
1128
+ * 如需获取加入房间之前房间内某个用户发布的资源列表,可使用 room.getRemoteTracksByUserId('userId') 获取
1129
+ * @param tracks 新发布的音轨与视轨数据列表,包含新发布的 RCRemoteAudioTrack 与 RCRemoteVideoTrack 实例
1130
+ */
1131
+ onTrackPublish: (tracks) => __awaiter(this, void 0, void 0, function* () {
1132
+ // 退出房间后,还会走到这??,所以判断一下,没有room不执行订阅
1133
+ if (this._room) {
1134
+ // 按业务需求选择需要订阅资源,通过 room.subscribe 接口进行订阅
1135
+ const { code } = yield this._room.subscribe(tracks);
1136
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
1137
+ logger.error(`[RCCallSession onTrackPublish] subscribe failed RTCCode ->${code}`);
1138
+ }
1139
+ }
1140
+ }),
1141
+ /**
1142
+ * 房间用户取消发布资源
1143
+ * @param tracks 被取消发布的音轨与视轨数据列表
1144
+ * @description 当资源被取消发布时,SDK 内部会取消对相关资源的订阅,业务层仅需处理 UI 业务
1145
+ */
1146
+ onTrackUnpublish: (tracks) => {
1147
+ },
1148
+ /**
1149
+ * 订阅的音视频流通道已建立, track 已可以进行播放
1150
+ * @param track RCRemoteTrack 类实例
1151
+ */
1152
+ onTrackReady: (track) => {
1153
+ const mediaType = this._stateMachine.getMediaType();
1154
+ // 有时对方没有降级成功,扔抛过来视频,这时的视频不对外抛出
1155
+ if (mediaType === pluginCallEngine.RCCallMediaType.AUDIO && track.isVideoTrack()) {
1156
+ return;
1157
+ }
1158
+ // 执行用户的onTrackReady监听
1159
+ this._notifyTrackReady([track]);
1160
+ },
1161
+ /**
1162
+ * 人员加入
1163
+ * @param userIds 加入的人员 id 列表
1164
+ */
1165
+ onUserJoin: (userIds) => {
1166
+ this._stateMachine.userJoin(userIds);
1167
+ },
1168
+ /**
1169
+ * 人员退出
1170
+ * @param userIds
1171
+ */
1172
+ onUserLeave: (userIds) => {
1173
+ logger.info(`[RCCallSession onUserLeave] listening onUserLeave userIds -> ${userIds === null || userIds === void 0 ? void 0 : userIds.join(',')}`);
1174
+ this._stateMachine.userLeave(userIds);
1175
+ },
1176
+ /**
1177
+ * RTC 每次 Ping 结果
1178
+ */
1179
+ onPing: (result) => {
1180
+ logger.info(`[RCCallSession onPing]${result}`);
1181
+ try {
1182
+ // 通知给业务
1183
+ this._listener.onPing && this._listener.onPing(result, this);
1184
+ }
1185
+ catch (error) {
1186
+ logger.error('[RCCallSession onPing] listening onPing exception');
1187
+ console.error(error);
1188
+ }
1189
+ }
1190
+ });
1191
+ }
1192
+ /**
1193
+ * 注册房间质量数据监听器
1194
+ */
1195
+ _registerReportListener() {
1196
+ // 注册房间质量数据监听器
1197
+ this._room.registerReportListener({
1198
+ /**
1199
+ * 用于接收状态数据报告
1200
+ * @param report
1201
+ */
1202
+ onStateReport: (report) => {
1203
+ try {
1204
+ this._listener.onRTCStateReport && this._listener.onRTCStateReport(report, this);
1205
+ }
1206
+ catch (error) {
1207
+ logger.error('[RCCallSession onStateReport] listener onStateReport exception');
1208
+ console.error(error);
1209
+ }
1210
+ },
1211
+ /**
1212
+ * ~ICE 连接状态变更通知~
1213
+ * @since version 5.1.5
1214
+ */
1215
+ onICEConnectionStateChange: (state) => {
1216
+ try {
1217
+ this._listener.onICEConnectionStateChange && this._listener.onICEConnectionStateChange(state, this);
1218
+ }
1219
+ catch (error) {
1220
+ logger.error('[RCCallSession onICEConnectionStateChange] onICEConnectionStateChange exception');
1221
+ console.error(error);
1222
+ }
1223
+ }
1224
+ });
1225
+ }
1226
+ /**
1227
+ * 通话唯一标识
1228
+ */
1229
+ getSessionId() {
1230
+ return this._stateMachine.getCallId();
1231
+ }
1232
+ /**
1233
+ * 获取房间当前会话 Id,当房间内已无成员时房间会回收,重新加入时 sessionId 将更新,(用户录制资源用的)
1234
+ */
1235
+ getRTCSessionId() {
1236
+ return this._room.getSessionId();
1237
+ }
1238
+ /**
1239
+ * 目标 ID,单呼对方人员 Id, 群呼群组 Id
1240
+ */
1241
+ getTargetId() {
1242
+ return this._stateMachine.getTargetId();
1243
+ }
1244
+ /**
1245
+ * 获取会话类型
1246
+ */
1247
+ getConversationType() {
1248
+ return this._stateMachine.getConversationType();
1249
+ }
1250
+ /**
1251
+ * 组织 ID
1252
+ */
1253
+ getChannelId() {
1254
+ return this._stateMachine.getChannelId();
1255
+ }
1256
+ /**
1257
+ * 房间人员列表,不包含本端信息
1258
+ */
1259
+ getRemoteUsers() {
1260
+ return this._stateMachine.getRemoteUsers();
1261
+ }
1262
+ /**
1263
+ * 获取人员状态
1264
+ */
1265
+ getUserState(userId) {
1266
+ if (!userId || typeof userId !== 'string') {
1267
+ throw new Error('userId is required, must be of type \'string\'');
1268
+ }
1269
+ return this._stateMachine.getUserState(userId);
1270
+ }
1271
+ /**
1272
+ * 获取session状态
1273
+ */
1274
+ getState() {
1275
+ return this._stateMachine.getState();
1276
+ }
1277
+ /**
1278
+ * 获得会话发起者id
1279
+ */
1280
+ getCallerId() {
1281
+ return this._stateMachine.getCallerId();
1282
+ }
1283
+ /**
1284
+ * 获得mediaType
1285
+ */
1286
+ getMediaType() {
1287
+ return this._stateMachine.getMediaType();
1288
+ }
1289
+ /**
1290
+ * 获取 RTC Room 实例
1291
+ */
1292
+ getRTCRoomInstance() {
1293
+ return this._room;
1294
+ }
1295
+ }
1296
+
1297
+ class RCCallClient {
1298
+ constructor(_context, _runtime, _options) {
1299
+ this._context = _context;
1300
+ this._runtime = _runtime;
1301
+ /**
1302
+ * session列表
1303
+ */
1304
+ this._sessionList = [];
1305
+ this._rtcClient = _options.rtcClient;
1306
+ this._options = Object.assign({
1307
+ /**
1308
+ * 是否允许发布重试, 默认不允许
1309
+ */
1310
+ isAllowPublishRetry: false,
1311
+ /**
1312
+ * 是否允许订阅重试,默认不允许
1313
+ */
1314
+ isAllowSubscribeRetry: false,
1315
+ /**
1316
+ * 禁用视频时关摄像头, 默认关闭
1317
+ */
1318
+ isOffCameraWhenVideoDisable: true,
1319
+ /**
1320
+ * RTC 房间加入类型,默认 RTCJoinType.COEXIST = 2 两个设备共存
1321
+ * RTCJoinType.KICK = 0,踢前一个设备
1322
+ * RTCJoinType.REFUSE = 1,当前加入拒绝
1323
+ * RTCJoinType.COEXIST = 2 两个设备共存
1324
+ */
1325
+ joinType: engine.RTCJoinType.COEXIST,
1326
+ /**
1327
+ * 允许降级获得流,获得音视频不成功 ,降级获得音频, 默认不允许
1328
+ */
1329
+ isAllowDemotionGetStream: false,
1330
+ /**
1331
+ * 语言设置 (推送), 不传默认为中文
1332
+ */
1333
+ lang: pluginCallEngine.RCCallLanguage.ZH
1334
+ }, _options);
1335
+ // 初始化callEngine, 并监听onInvite
1336
+ this._callEngine = new pluginCallEngine.RCCallEngine(this._context, _runtime, logger, {
1337
+ /**
1338
+ * 监听收到invite
1339
+ */
1340
+ onInvite: this._onInvite.bind(this),
1341
+ /**
1342
+ * 监听离线消息报告
1343
+ */
1344
+ onOfflineRecord: this._onOfflineRecord.bind(this)
1345
+ }, {
1346
+ /**
1347
+ * 语言设置 (推送), 不传默认为中文
1348
+ */
1349
+ lang: this._options.lang || pluginCallEngine.RCCallLanguage.ZH
1350
+ });
1351
+ eventEmitter.on('sessionClose', ({ session, summaryInfo }) => {
1352
+ // 从sessionList去掉这个关闭的session
1353
+ this._removeSession(session);
1354
+ try {
1355
+ this._options.onSessionClose(session, summaryInfo);
1356
+ }
1357
+ catch (error) {
1358
+ logger.error('[RCCCallClient] options.onSessionClose exception');
1359
+ console.log(error);
1360
+ }
1361
+ });
1362
+ // 接听之前挂断其它的session
1363
+ eventEmitter.on('hungupOtherSession', ({ session }) => {
1364
+ const id = session.getSessionId();
1365
+ logger.info(`[RCCallClient hungupOtherSession] sessionId ready to accept -> ${id}`);
1366
+ logger.info(`[RCCallClient hungupOtherSession] sessionList ->${this._sessionList.map(ses => ses.getSessionId()).join(',')}`);
1367
+ let i = 0;
1368
+ while (this._sessionList.length > 1) {
1369
+ // 如果与要接听的session不一致
1370
+ if (this._sessionList[i].getSessionId() !== id) {
1371
+ // 挂断
1372
+ this._sessionList[i].hungup();
1373
+ // 挂断后删除
1374
+ this._sessionList.splice(i, 1);
1375
+ }
1376
+ else {
1377
+ // 如果是要接听的session,跳过这个索引,所以加1
1378
+ i++;
1379
+ }
1380
+ }
1381
+ logger.info(`[RCCallClient hungupOtherSession] current sessionList length ->${this._sessionList.length}`);
1382
+ });
1383
+ }
1384
+ /**
1385
+ * 监听onInvite
1386
+ */
1387
+ _onInvite(stateMachine) {
1388
+ logger.info('[RCCallClient _onInvite] Received invite message');
1389
+ const session = new RCCallSession(stateMachine, this._rtcClient, {
1390
+ // 是否允许订阅重试
1391
+ isAllowSubscribeRetry: this._options.isAllowSubscribeRetry,
1392
+ // 是否允许发布重试
1393
+ isAllowPublishRetry: this._options.isAllowPublishRetry,
1394
+ /**
1395
+ * 禁用视频时关摄像头
1396
+ */
1397
+ isOffCameraWhenVideoDisable: this._options.isOffCameraWhenVideoDisable,
1398
+ /**
1399
+ * RTC 房间加入类型
1400
+ */
1401
+ joinType: this._options.joinType,
1402
+ // 允许降级获得流,获得音视频不成功 ,降级获得音频, 默认不允许
1403
+ isAllowDemotionGetStream: this._options.isAllowDemotionGetStream,
1404
+ // 标明是被叫产生的session
1405
+ produceType: ProduceTypes.CALLEE
1406
+ });
1407
+ logger.info('[RCCallClient _onInvite] Received invite message, successfully created session');
1408
+ /**
1409
+ * 如果通话的时候不允许接听新的通话,直接挂断, 这些工作在callEngine里完成
1410
+ */
1411
+ this._sessionList.push(session);
1412
+ try {
1413
+ // 执行用户API的监听
1414
+ this._options.onSession(session);
1415
+ }
1416
+ catch (error) {
1417
+ logger.error('[RCCallClient _options.onSession] onSession exception');
1418
+ console.log(error);
1419
+ }
1420
+ // 必须在onSession里注册session监听事件,这里检测一下有没有注册
1421
+ if (session._listener) {
1422
+ const conclusion = validateListener(session._listener);
1423
+ if (!conclusion.result) {
1424
+ throw new Error(conclusion.msg);
1425
+ }
1426
+ }
1427
+ else {
1428
+ logger.error('[RCCallClient _options.onSession] session Must Have Listener');
1429
+ throw new Error('[RCCallSession _options.onSession] session Must Have Listener');
1430
+ }
1431
+ }
1432
+ /**
1433
+ * 监听离线消息报告
1434
+ * @param record
1435
+ */
1436
+ _onOfflineRecord(record) {
1437
+ try {
1438
+ // 执行用户API的监听
1439
+ this._options.onOfflineRecord && this._options.onOfflineRecord(record);
1440
+ }
1441
+ catch (error) {
1442
+ logger.error('[RCCallClient _options.onOfflineRecord] onOfflineRecord exception');
1443
+ console.log(error);
1444
+ }
1445
+ }
1446
+ /**
1447
+ * 注册用户信息。注册后,在发起邀请或挂断等操作时,会将该信息一并发送给对端
1448
+ * @param info.name 用户名称
1449
+ * @param info.portraitUri 用户头像信息
1450
+ * @param info.extra 预留拓展字段
1451
+ */
1452
+ registerUserInfo(info = {}) {
1453
+ this._callEngine.registerUserInfo(info);
1454
+ logger.info('[RCCallClient registerUserInfo] successfully register user info data');
1455
+ }
1456
+ /**
1457
+ * 单呼,发送invite消息,回调回来接收stateMachine, 建session
1458
+ * @param params.targetId 被呼叫一方的用户 id 必填
1459
+ * @param params.mediaType 音频呼叫 or 音视频呼叫 必填
1460
+ * @param params.listener (session上的监听) 必填
1461
+ * @param params.constraints 获取音频或音视频资源时的参数 可选
1462
+ * @param params.channelId 组织 Id 可选
1463
+ */
1464
+ call({ targetId, mediaType = pluginCallEngine.RCCallMediaType.AUDIO, listener, constraints, channelId = '' }) {
1465
+ return __awaiter(this, void 0, void 0, function* () {
1466
+ const conclusion = [validateTargetId(targetId), validateMediaType(mediaType), validateListener(listener)];
1467
+ const messages = [];
1468
+ const result = conclusion.every((obj) => {
1469
+ !obj.result && messages.push(obj.msg);
1470
+ return obj.result;
1471
+ });
1472
+ if (!result) {
1473
+ throw new Error(`[RCCallClient call] ${messages.join('\n')}`);
1474
+ }
1475
+ let localTracks = [];
1476
+ const { code: _code, tracks } = yield this._getLocalTrack(mediaType, constraints);
1477
+ if (_code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
1478
+ return { code: _code };
1479
+ }
1480
+ localTracks = tracks;
1481
+ localTracks.forEach(track => {
1482
+ // 向外抛出本地流
1483
+ listener.onTrackReady(track);
1484
+ });
1485
+ // 调用callEngine的call返回一个状态机的实例
1486
+ const { code, stateMachine } = yield this._callEngine.call(channelId, targetId, mediaType);
1487
+ if (code === pluginCallEngine.RCCallErrorCode.SUCCESS && stateMachine) {
1488
+ logger.info('[RCCallClient call] successfully created state machine');
1489
+ const session = new RCCallSession(stateMachine, this._rtcClient, {
1490
+ localTracks,
1491
+ // 是否允许订阅重试
1492
+ isAllowSubscribeRetry: this._options.isAllowSubscribeRetry,
1493
+ // 是否允许订阅重试
1494
+ isAllowPublishRetry: this._options.isAllowPublishRetry,
1495
+ /**
1496
+ * 禁用视频时关摄像头
1497
+ */
1498
+ isOffCameraWhenVideoDisable: this._options.isOffCameraWhenVideoDisable,
1499
+ /**
1500
+ * RTC 房间加入类型
1501
+ */
1502
+ joinType: this._options.joinType,
1503
+ // 允许降级获得流,获得音视频不成功 ,降级获得音频, 默认不允许
1504
+ isAllowDemotionGetStream: this._options.isAllowDemotionGetStream,
1505
+ // 标明是主叫产生的session
1506
+ produceType: ProduceTypes.CALLER
1507
+ });
1508
+ // session上注册监听事件
1509
+ session.registerSessionListener(listener);
1510
+ this._sessionList.push(session);
1511
+ logger.info(`[RCCallClient call] successfully created session object, sessionId: ${session.getSessionId()}`);
1512
+ return { code, session };
1513
+ }
1514
+ else {
1515
+ logger.error(`[RCCallClient call] call failed code ->: ${code}`);
1516
+ localTracks.forEach(track => {
1517
+ // 禁用视频
1518
+ track.mute();
1519
+ // 关闭摄像头
1520
+ track.destroy();
1521
+ });
1522
+ return { code };
1523
+ }
1524
+ });
1525
+ }
1526
+ /**
1527
+ * 发起群组呼叫
1528
+ * @param params.targetId 群组 Id 必填
1529
+ * @param params.userIds 被呼叫的群内成员 Id 必填
1530
+ * @param params.mediaType 音频呼叫 or 音视频呼叫 必填
1531
+ * @param params.listener (session上的监听) 必填
1532
+ * @param params.channelId 组织 Id 可选
1533
+ * @param params.constraints 获取音频或音视频资源时的参数 可选
1534
+ */
1535
+ callInGroup({ targetId, userIds, mediaType = pluginCallEngine.RCCallMediaType.AUDIO, listener, constraints, channelId = '' }) {
1536
+ return __awaiter(this, void 0, void 0, function* () {
1537
+ const conclusion = [validateTargetId(targetId), validateUserIds(userIds), validateMediaType(mediaType), validateListener(listener)];
1538
+ const messages = [];
1539
+ const result = conclusion.every((obj) => {
1540
+ !obj.result && messages.push(obj.msg);
1541
+ return obj.result;
1542
+ });
1543
+ if (!result) {
1544
+ throw new Error(`[RCCallClient callInGroup] ${messages.join('\n')}`);
1545
+ }
1546
+ let localTracks = [];
1547
+ const { code: _code, tracks } = yield this._getLocalTrack(mediaType, constraints);
1548
+ if (_code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
1549
+ return { code: _code };
1550
+ }
1551
+ localTracks = tracks;
1552
+ localTracks.forEach(track => {
1553
+ // 向外抛出本地流
1554
+ listener.onTrackReady(track);
1555
+ });
1556
+ // 往组里发消息
1557
+ const { code, stateMachine } = yield this._callEngine.callInGroup(channelId, targetId, mediaType, userIds);
1558
+ if (code === pluginCallEngine.RCCallErrorCode.SUCCESS && stateMachine) {
1559
+ logger.info('[RCCallClient callInGroup] successfully created state machine');
1560
+ const session = new RCCallSession(stateMachine, this._rtcClient, {
1561
+ localTracks,
1562
+ // 是否允许订阅重试
1563
+ isAllowSubscribeRetry: this._options.isAllowSubscribeRetry,
1564
+ // 是否允许发布重试
1565
+ isAllowPublishRetry: this._options.isAllowPublishRetry,
1566
+ /**
1567
+ * 禁用视频时关摄像头
1568
+ */
1569
+ isOffCameraWhenVideoDisable: this._options.isOffCameraWhenVideoDisable,
1570
+ /**
1571
+ * RTC 房间加入类型
1572
+ */
1573
+ joinType: this._options.joinType,
1574
+ // 允许降级获得流,获得音视频不成功 ,降级获得音频, 默认不允许
1575
+ isAllowDemotionGetStream: this._options.isAllowDemotionGetStream,
1576
+ // 标明是主叫产生的session
1577
+ produceType: ProduceTypes.CALLER
1578
+ });
1579
+ // session上注册监听事件
1580
+ session.registerSessionListener(listener);
1581
+ this._sessionList.push(session);
1582
+ logger.info(`[RCCallClient callInGroup] successfully created session object, sessionId: ${session.getSessionId()}`);
1583
+ return { code, session };
1584
+ }
1585
+ else {
1586
+ logger.info(`[RCCallClient callInGroup] callInGroup failed code -> ${code}`);
1587
+ localTracks.forEach(track => {
1588
+ // 禁用视频
1589
+ track.mute();
1590
+ // 关闭摄像头
1591
+ track.destroy();
1592
+ });
1593
+ return { code };
1594
+ }
1595
+ });
1596
+ }
1597
+ /**
1598
+ * 调RTC API 获得本地流
1599
+ */
1600
+ _getLocalTrackCore(mediaType, constraints) {
1601
+ return __awaiter(this, void 0, void 0, function* () {
1602
+ // 检测是否能够获得本地流
1603
+ if (mediaType === pluginCallEngine.RCCallMediaType.AUDIO) {
1604
+ const { code, track } = yield this._rtcClient.createMicrophoneAudioTrack('RongCloudRTC', constraints && constraints.audio && Object.assign({}, constraints.audio));
1605
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
1606
+ logger.error(`[RCCallClient _getTrack] get Audio local tracks failed RCT code -> ${code}`);
1607
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_AUDIO_TRACK_ERROR };
1608
+ }
1609
+ logger.info('[RCCallClient _getTrack] successfully get Audio local tracks');
1610
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS, tracks: [track] };
1611
+ }
1612
+ else {
1613
+ const { code, tracks } = yield this._rtcClient.createMicrophoneAndCameraTracks('RongCloudRTC', constraints && Object.assign({}, constraints));
1614
+ if (code !== pluginRtc.RCRTCCode.SUCCESS) {
1615
+ logger.error(`[RCCallClient _getTrack] get Audio and Video local tracks failed RCT code -> ${code}`);
1616
+ return { code: pluginCallEngine.RCCallErrorCode.GET_LOCAL_AUDIO_AND_VIDEO_TRACK_ERROR };
1617
+ }
1618
+ logger.info('[RCCallClient _getTrack] successfully get audio and video local tracks');
1619
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS, tracks };
1620
+ }
1621
+ });
1622
+ }
1623
+ _getLocalTrack(mediaType, constraints) {
1624
+ return __awaiter(this, void 0, void 0, function* () {
1625
+ // 如果是允许降级获得流,并且是获得音视频
1626
+ if (this._options.isAllowDemotionGetStream && mediaType === pluginCallEngine.RCCallMediaType.AUDIO_VIDEO) {
1627
+ const { code, tracks } = yield this._getLocalTrackCore(pluginCallEngine.RCCallMediaType.AUDIO_VIDEO, constraints);
1628
+ // 如果音视频不能获得,就降组获得音频
1629
+ if (code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
1630
+ const { code, tracks } = yield this._getLocalTrackCore(pluginCallEngine.RCCallMediaType.AUDIO, constraints);
1631
+ if (code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
1632
+ return { code };
1633
+ }
1634
+ return { code, tracks: tracks };
1635
+ }
1636
+ return { code, tracks: tracks };
1637
+ }
1638
+ else {
1639
+ const { code: _code, tracks } = yield this._getLocalTrackCore(mediaType, constraints);
1640
+ if (_code !== pluginCallEngine.RCCallErrorCode.SUCCESS) {
1641
+ return { code: _code };
1642
+ }
1643
+ return { code: _code, tracks: tracks };
1644
+ }
1645
+ });
1646
+ }
1647
+ /**
1648
+ * 从sessionList删除某个session
1649
+ */
1650
+ _removeSession(session) {
1651
+ const id = session.getSessionId();
1652
+ this._sessionList = this._sessionList.filter(session => session.getSessionId() !== id);
1653
+ }
1654
+ /**
1655
+ * 获取加入通话(已加入 RTC 房间)的用户信息
1656
+ * @param userId 用户 ID
1657
+ */
1658
+ getJoinedUserInfo(userId) {
1659
+ return __awaiter(this, void 0, void 0, function* () {
1660
+ if (typeof userId !== 'string') {
1661
+ throw new Error('[RCCallClient getJoinedUserInfo] parameter error');
1662
+ }
1663
+ const { code, data } = yield this._context.getRTCJoinedUserInfo(userId);
1664
+ if (code !== engine.ErrorCode.SUCCESS) {
1665
+ logger.error('getJoinedUserInfo error', code);
1666
+ return { code: pluginCallEngine.RCCallErrorCode.QUERY_JOINED_USER_INFO_ERROR };
1667
+ }
1668
+ return { code: pluginCallEngine.RCCallErrorCode.SUCCESS, data };
1669
+ });
1670
+ }
1671
+ }
1672
+
1673
+ const installer = {
1674
+ tag: 'RCCall',
1675
+ verify(runtime) {
1676
+ return runtime.tag === 'browser';
1677
+ },
1678
+ setup(context, runtime, options) {
1679
+ // 先校验参数
1680
+ const conclusion = validateCallInitOptions(options);
1681
+ if (!conclusion.result) {
1682
+ throw new Error(`[RCCallLib installer steup]${conclusion.msg}`);
1683
+ }
1684
+ logger.setLogLevel(options.logLevel);
1685
+ logger.setLogStdout(options.logStdout);
1686
+ logger.warn(`RCCall Version: ${"5.0.5-alpha.1"}, Commit: ${"92132946bea607c42e2d1fe5fe641bed05997834"}`);
1687
+ return new RCCallClient(context, runtime, options);
1688
+ }
1689
+ };
1690
+
1691
+ Object.defineProperty(exports, 'RCCallEndReason', {
1692
+ enumerable: true,
1693
+ get: function () { return pluginCallEngine.RCCallEndReason; }
1694
+ });
1695
+ Object.defineProperty(exports, 'RCCallErrorCode', {
1696
+ enumerable: true,
1697
+ get: function () { return pluginCallEngine.RCCallErrorCode; }
1698
+ });
1699
+ Object.defineProperty(exports, 'RCCallLanguage', {
1700
+ enumerable: true,
1701
+ get: function () { return pluginCallEngine.RCCallLanguage; }
1702
+ });
1703
+ Object.defineProperty(exports, 'RCCallMediaType', {
1704
+ enumerable: true,
1705
+ get: function () { return pluginCallEngine.RCCallMediaType; }
1706
+ });
1707
+ Object.defineProperty(exports, 'RCCallSessionState', {
1708
+ enumerable: true,
1709
+ get: function () { return pluginCallEngine.RCCallSessionState; }
1710
+ });
1711
+ Object.defineProperty(exports, 'RCCallUserState', {
1712
+ enumerable: true,
1713
+ get: function () { return pluginCallEngine.RCCallUserState; }
1714
+ });
1715
+ exports.RCCallClient = RCCallClient;
1716
+ exports.RCCallSession = RCCallSession;
1717
+ exports.installer = installer;