@webex/plugin-meetings 3.0.0-beta.85 → 3.0.0-beta.87
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/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/index.js +63 -1
- package/dist/index.js.map +1 -1
- package/dist/media/index.js +7 -4
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +139 -88
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +169 -26
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +20 -2
- package/dist/meetings/index.js.map +1 -1
- package/dist/types/controls-options-manager/util.d.ts +0 -102
- package/dist/types/index.d.ts +6 -5
- package/dist/types/media/properties.d.ts +1 -1
- package/dist/types/meeting/index.d.ts +8 -5
- package/dist/types/meeting/muteState.d.ts +58 -5
- package/dist/types/meetings/index.d.ts +1 -0
- package/dist/types/multistream/remoteMedia.d.ts +1 -29
- package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
- package/package.json +19 -18
- package/src/{index.js → index.ts} +15 -0
- package/src/media/index.ts +17 -18
- package/src/media/properties.ts +3 -7
- package/src/meeting/index.ts +108 -51
- package/src/meeting/muteState.ts +158 -15
- package/src/meeting/util.ts +1 -1
- package/src/meetings/index.ts +14 -0
- package/test/integration/spec/converged-space-meetings.js +4 -3
- package/test/integration/spec/journey.js +4 -3
- package/test/integration/spec/space-meeting.js +4 -3
- package/test/unit/spec/media/index.ts +30 -2
- package/test/unit/spec/meeting/index.js +24 -28
- package/test/unit/spec/meeting/muteState.js +226 -13
- package/test/utils/integrationTestUtils.js +64 -0
- package/test/utils/testUtils.js +0 -57
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { ServerMuteReason } from '@webex/media-helpers';
|
|
2
|
+
export declare const createMuteState: (type: any, meeting: any, mediaDirection: any, sdkOwnsLocalTrack: boolean) => MuteState;
|
|
2
3
|
/** The purpose of this class is to manage the local and remote mute state and make sure that the server state always matches
|
|
3
4
|
the last requested state by the client.
|
|
4
5
|
|
|
@@ -9,13 +10,42 @@ declare class MuteState {
|
|
|
9
10
|
pendingPromiseResolve: any;
|
|
10
11
|
state: any;
|
|
11
12
|
type: any;
|
|
13
|
+
sdkOwnsLocalTrack: boolean;
|
|
14
|
+
ignoreMuteStateChange: boolean;
|
|
12
15
|
/**
|
|
13
16
|
* Constructor
|
|
14
17
|
*
|
|
15
18
|
* @param {String} type - audio or video
|
|
16
19
|
* @param {Object} meeting - the meeting object (used for reading current remote mute status)
|
|
20
|
+
* @param {boolean} sdkOwnsLocalTrack - if false, then client app owns the local track (for now that's the case only for multistream meetings)
|
|
17
21
|
*/
|
|
18
|
-
constructor(type: string, meeting: any);
|
|
22
|
+
constructor(type: string, meeting: any, sdkOwnsLocalTrack: boolean);
|
|
23
|
+
/**
|
|
24
|
+
* Starts the mute state machine. Needs to be called after a new MuteState instance is created.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} meeting - the meeting object
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
init(meeting: any): void;
|
|
30
|
+
/**
|
|
31
|
+
* This method needs to be called whenever the local audio/video track has changed.
|
|
32
|
+
* It reapplies the remote mute state onto the new track and also reads the current
|
|
33
|
+
* local mute state from the track and updates the internal state machine and sends
|
|
34
|
+
* any required requests to the server.
|
|
35
|
+
*
|
|
36
|
+
* @param {Object} meeting - the meeting object
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
handleLocalTrackChange(meeting: any): void;
|
|
40
|
+
/**
|
|
41
|
+
* Mutes/unmutes local track
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} meeting - the meeting object
|
|
44
|
+
* @param {Boolean} mute - true to mute the track, false to unmute it
|
|
45
|
+
* @param {ServerMuteReason} reason - reason for muting/unmuting
|
|
46
|
+
* @returns {void}
|
|
47
|
+
*/
|
|
48
|
+
private muteLocalTrack;
|
|
19
49
|
/**
|
|
20
50
|
* Handles mute/unmute request from the client/user. Returns a promise that's resolved once the server update is completed or
|
|
21
51
|
* at the point that this request becomese superseded by another client request.
|
|
@@ -30,16 +60,26 @@ declare class MuteState {
|
|
|
30
60
|
* @param {Boolean} [mute] true for muting, false for unmuting request
|
|
31
61
|
* @returns {Promise}
|
|
32
62
|
*/
|
|
33
|
-
handleClientRequest(meeting
|
|
63
|
+
handleClientRequest(meeting: object, mute?: boolean): Promise<unknown>;
|
|
64
|
+
/**
|
|
65
|
+
* This method should be called when the local track mute state is changed
|
|
66
|
+
* @public
|
|
67
|
+
* @memberof MuteState
|
|
68
|
+
* @param {Object} [meeting] the meeting object
|
|
69
|
+
* @param {Boolean} [mute] true for muting, false for unmuting request
|
|
70
|
+
* @returns {void}
|
|
71
|
+
*/
|
|
72
|
+
handleLocalTrackMuteStateChange(meeting?: object, mute?: boolean): void;
|
|
34
73
|
/**
|
|
35
74
|
* Applies the current mute state to the local track (by enabling or disabling it accordingly)
|
|
36
75
|
*
|
|
37
76
|
* @public
|
|
38
77
|
* @param {Object} [meeting] the meeting object
|
|
78
|
+
* @param {ServerMuteReason} reason - reason why we're applying our client state to the local track
|
|
39
79
|
* @memberof MuteState
|
|
40
80
|
* @returns {void}
|
|
41
81
|
*/
|
|
42
|
-
applyClientStateLocally(meeting?: any): void;
|
|
82
|
+
applyClientStateLocally(meeting?: any, reason?: ServerMuteReason): void;
|
|
43
83
|
/**
|
|
44
84
|
* Updates the server local and remote mute values so that they match the current client desired state.
|
|
45
85
|
*
|
|
@@ -67,16 +107,29 @@ declare class MuteState {
|
|
|
67
107
|
* @returns {Promise}
|
|
68
108
|
*/
|
|
69
109
|
private sendRemoteMuteRequestToServer;
|
|
110
|
+
/** Sets the mute state of the local track according to what server thinks is our state
|
|
111
|
+
* @param {Object} meeting - the meeting object
|
|
112
|
+
* @param {ServerMuteReason} serverMuteReason - reason why we're applying server mute to the local track
|
|
113
|
+
* @returns {void}
|
|
114
|
+
*/
|
|
115
|
+
private applyServerMuteToLocalTrack;
|
|
116
|
+
/** Applies the current value for unmute allowed to the underlying track
|
|
117
|
+
*
|
|
118
|
+
* @param {Meeting} meeting
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
private applyUnmuteAllowedToTrack;
|
|
70
122
|
/**
|
|
71
123
|
* This method should be called whenever the server remote mute state is changed
|
|
72
124
|
*
|
|
73
125
|
* @public
|
|
74
126
|
* @memberof MuteState
|
|
127
|
+
* @param {Meeting} meeting
|
|
75
128
|
* @param {Boolean} [muted] true if user is remotely muted, false otherwise
|
|
76
129
|
* @param {Boolean} [unmuteAllowed] indicates if user is allowed to unmute self (false when "hard mute" feature is used)
|
|
77
130
|
* @returns {undefined}
|
|
78
131
|
*/
|
|
79
|
-
handleServerRemoteMuteUpdate(muted?: boolean, unmuteAllowed?: boolean): void;
|
|
132
|
+
handleServerRemoteMuteUpdate(meeting: any, muted?: boolean, unmuteAllowed?: boolean): void;
|
|
80
133
|
/**
|
|
81
134
|
* This method should be called whenever we receive from the server a requirement to locally unmute
|
|
82
135
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MediaType, SourceState } from '@webex/internal-media-core';
|
|
2
2
|
import EventsScope from '../common/events/events-scope';
|
|
3
3
|
import { MediaRequestManager } from './mediaRequestManager';
|
|
4
|
-
import {
|
|
4
|
+
import { ReceiveSlot } from './receiveSlot';
|
|
5
5
|
export declare const RemoteMediaEvents: {
|
|
6
6
|
SourceUpdate: string;
|
|
7
7
|
Stopped: string;
|
|
@@ -44,29 +44,6 @@ export declare class RemoteMedia extends EventsScope {
|
|
|
44
44
|
* @param height height of the video element
|
|
45
45
|
*/
|
|
46
46
|
setSizeHint(width: any, height: any): void;
|
|
47
|
-
/**
|
|
48
|
-
* Invalidates the remote media by clearing the reference to a receive slot and
|
|
49
|
-
* cancelling the media request.
|
|
50
|
-
* After this call the remote media is unusable.
|
|
51
|
-
*
|
|
52
|
-
* @param {boolean} commit - whether to commit the cancellation of the media request
|
|
53
|
-
* @internal
|
|
54
|
-
*/
|
|
55
|
-
stop(commit?: boolean): void;
|
|
56
|
-
/**
|
|
57
|
-
* Sends a new media request. This method can only be used for receiver-selected policy,
|
|
58
|
-
* because only in that policy we have a 1-1 relationship between RemoteMedia and MediaRequest
|
|
59
|
-
* and the request id is then stored in this RemoteMedia instance.
|
|
60
|
-
* For active-speaker policy, the same request is shared among many RemoteMedia instances,
|
|
61
|
-
* so it's managed through RemoteMediaGroup
|
|
62
|
-
*
|
|
63
|
-
* @internal
|
|
64
|
-
*/
|
|
65
|
-
sendMediaRequest(csi: CSI, commit: boolean): void;
|
|
66
|
-
/**
|
|
67
|
-
* @internal
|
|
68
|
-
*/
|
|
69
|
-
cancelMediaRequest(commit: boolean): void;
|
|
70
47
|
/**
|
|
71
48
|
* registers event listeners on the receive slot and forwards all the events
|
|
72
49
|
*/
|
|
@@ -91,10 +68,5 @@ export declare class RemoteMedia extends EventsScope {
|
|
|
91
68
|
* Getter for remote media stream
|
|
92
69
|
*/
|
|
93
70
|
get stream(): MediaStream;
|
|
94
|
-
/**
|
|
95
|
-
* @internal
|
|
96
|
-
* @returns {ReceiveSlot}
|
|
97
|
-
*/
|
|
98
|
-
getUnderlyingReceiveSlot(): ReceiveSlot;
|
|
99
71
|
}
|
|
100
72
|
export {};
|
|
@@ -35,15 +35,6 @@ export declare class RemoteMediaGroup {
|
|
|
35
35
|
isPinned(remoteMedia: RemoteMedia): boolean;
|
|
36
36
|
private sendActiveSpeakerMediaRequest;
|
|
37
37
|
private cancelActiveSpeakerMediaRequest;
|
|
38
|
-
/**
|
|
39
|
-
* Invalidates the remote media group by clearing the references to the receive slots
|
|
40
|
-
* used by all remote media from that group and cancelling all media requests.
|
|
41
|
-
* After this call the remote media group is unusable.
|
|
42
|
-
*
|
|
43
|
-
* @param{boolean} commit whether to commit the cancellation of media requests
|
|
44
|
-
* @internal
|
|
45
|
-
*/
|
|
46
|
-
stop(commit?: boolean): void;
|
|
47
38
|
/**
|
|
48
39
|
* Checks if a given RemoteMedia instance belongs to this group.
|
|
49
40
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.87",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
|
|
6
6
|
"contributors": [
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"build": "yarn run -T tsc --declaration true --declarationDir ./dist/types"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@webex/plugin-meetings": "3.0.0-beta.
|
|
36
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
37
|
-
"@webex/test-helper-mocha": "3.0.0-beta.
|
|
38
|
-
"@webex/test-helper-mock-webex": "3.0.0-beta.
|
|
39
|
-
"@webex/test-helper-retry": "3.0.0-beta.
|
|
40
|
-
"@webex/test-helper-test-users": "3.0.0-beta.
|
|
35
|
+
"@webex/plugin-meetings": "3.0.0-beta.87",
|
|
36
|
+
"@webex/test-helper-chai": "3.0.0-beta.87",
|
|
37
|
+
"@webex/test-helper-mocha": "3.0.0-beta.87",
|
|
38
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.87",
|
|
39
|
+
"@webex/test-helper-retry": "3.0.0-beta.87",
|
|
40
|
+
"@webex/test-helper-test-users": "3.0.0-beta.87",
|
|
41
41
|
"chai": "^4.3.4",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
43
|
"jsdom-global": "3.0.2",
|
|
@@ -46,18 +46,19 @@
|
|
|
46
46
|
"typescript": "^4.7.4"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@webex/common": "3.0.0-beta.
|
|
49
|
+
"@webex/common": "3.0.0-beta.87",
|
|
50
50
|
"@webex/internal-media-core": "1.36.0",
|
|
51
|
-
"@webex/internal-plugin-conversation": "3.0.0-beta.
|
|
52
|
-
"@webex/internal-plugin-device": "3.0.0-beta.
|
|
53
|
-
"@webex/internal-plugin-llm": "3.0.0-beta.
|
|
54
|
-
"@webex/internal-plugin-mercury": "3.0.0-beta.
|
|
55
|
-
"@webex/internal-plugin-metrics": "3.0.0-beta.
|
|
56
|
-
"@webex/internal-plugin-support": "3.0.0-beta.
|
|
57
|
-
"@webex/internal-plugin-user": "3.0.0-beta.
|
|
58
|
-
"@webex/
|
|
59
|
-
"@webex/plugin-
|
|
60
|
-
"@webex/
|
|
51
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.87",
|
|
52
|
+
"@webex/internal-plugin-device": "3.0.0-beta.87",
|
|
53
|
+
"@webex/internal-plugin-llm": "3.0.0-beta.87",
|
|
54
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.87",
|
|
55
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.87",
|
|
56
|
+
"@webex/internal-plugin-support": "3.0.0-beta.87",
|
|
57
|
+
"@webex/internal-plugin-user": "3.0.0-beta.87",
|
|
58
|
+
"@webex/media-helpers": "3.0.0-beta.87",
|
|
59
|
+
"@webex/plugin-people": "3.0.0-beta.87",
|
|
60
|
+
"@webex/plugin-rooms": "3.0.0-beta.87",
|
|
61
|
+
"@webex/webex-core": "3.0.0-beta.87",
|
|
61
62
|
"ampersand-collection": "^2.0.2",
|
|
62
63
|
"bowser": "^2.11.0",
|
|
63
64
|
"btoa": "^1.2.1",
|
|
@@ -8,6 +8,21 @@ registerPlugin('meetings', Meetings, {
|
|
|
8
8
|
config,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
+
export {
|
|
12
|
+
LocalTrack,
|
|
13
|
+
LocalDisplayTrack,
|
|
14
|
+
LocalTrackEvents,
|
|
15
|
+
type TrackMuteEvent,
|
|
16
|
+
type ServerMuteReason,
|
|
17
|
+
LocalMicrophoneTrackEvents,
|
|
18
|
+
LocalCameraTrackEvents,
|
|
19
|
+
LocalMicrophoneTrack,
|
|
20
|
+
LocalCameraTrack,
|
|
21
|
+
createMicrophoneTrack,
|
|
22
|
+
createCameraTrack,
|
|
23
|
+
createDisplayTrack,
|
|
24
|
+
} from '@webex/media-helpers';
|
|
25
|
+
|
|
11
26
|
export default Meetings;
|
|
12
27
|
|
|
13
28
|
export * as CONSTANTS from './constants';
|
package/src/media/index.ts
CHANGED
|
@@ -3,13 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/* globals navigator */
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
LocalDisplayTrack,
|
|
9
|
-
LocalMicrophoneTrack,
|
|
10
|
-
RoapMediaConnection,
|
|
11
|
-
MultistreamRoapMediaConnection,
|
|
12
|
-
} from '@webex/internal-media-core';
|
|
6
|
+
import {RoapMediaConnection, MultistreamRoapMediaConnection} from '@webex/internal-media-core';
|
|
7
|
+
import {LocalCameraTrack, LocalDisplayTrack, LocalMicrophoneTrack} from '@webex/media-helpers';
|
|
13
8
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
14
9
|
import {AUDIO_INPUT, VIDEO_INPUT, MEDIA_TRACK_CONSTRAINT} from '../constants';
|
|
15
10
|
import Config from '../config';
|
|
@@ -19,6 +14,8 @@ import BrowserDetection from '../common/browser-detection';
|
|
|
19
14
|
|
|
20
15
|
const {isBrowser} = BrowserDetection();
|
|
21
16
|
|
|
17
|
+
type MultistreamConnectionConfig = ConstructorParameters<typeof MultistreamRoapMediaConnection>[0];
|
|
18
|
+
|
|
22
19
|
export type BundlePolicy = ConstructorParameters<
|
|
23
20
|
typeof MultistreamRoapMediaConnection
|
|
24
21
|
>[0]['bundlePolicy'];
|
|
@@ -166,17 +163,19 @@ Media.createMediaConnection = (
|
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
if (isMultistream) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
166
|
+
const config: MultistreamConnectionConfig = {
|
|
167
|
+
iceServers,
|
|
168
|
+
enableMainAudio:
|
|
169
|
+
mediaProperties.mediaDirection?.sendAudio || mediaProperties.mediaDirection?.receiveAudio,
|
|
170
|
+
enableMainVideo:
|
|
171
|
+
mediaProperties.mediaDirection?.sendVideo || mediaProperties.mediaDirection?.receiveVideo,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (bundlePolicy) {
|
|
175
|
+
config.bundlePolicy = bundlePolicy;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return new MultistreamRoapMediaConnection(config, debugId);
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
if (!mediaProperties) {
|
package/src/media/properties.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
LocalCameraTrack,
|
|
5
|
-
LocalMicrophoneTrack,
|
|
6
|
-
LocalDisplayTrack,
|
|
7
|
-
} from '@webex/internal-media-core';
|
|
1
|
+
import {ConnectionState, Event} from '@webex/internal-media-core';
|
|
2
|
+
|
|
3
|
+
import {LocalCameraTrack, LocalMicrophoneTrack, LocalDisplayTrack} from '@webex/media-helpers';
|
|
8
4
|
|
|
9
5
|
import {MEETINGS, PC_BAIL_TIMEOUT, QUALITY_LEVELS} from '../constants';
|
|
10
6
|
import LoggerProxy from '../common/logs/logger-proxy';
|
package/src/meeting/index.ts
CHANGED
|
@@ -7,13 +7,18 @@ import {
|
|
|
7
7
|
Errors,
|
|
8
8
|
ErrorType,
|
|
9
9
|
Event,
|
|
10
|
+
MediaType,
|
|
11
|
+
RemoteTrackType,
|
|
12
|
+
} from '@webex/internal-media-core';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
LocalTrack,
|
|
10
16
|
LocalCameraTrack,
|
|
11
17
|
LocalDisplayTrack,
|
|
12
18
|
LocalMicrophoneTrack,
|
|
13
19
|
LocalTrackEvents,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from '@webex/internal-media-core';
|
|
20
|
+
TrackMuteEvent,
|
|
21
|
+
} from '@webex/media-helpers';
|
|
17
22
|
|
|
18
23
|
import {
|
|
19
24
|
MeetingNotActiveError,
|
|
@@ -519,6 +524,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
519
524
|
resourceUrl: string;
|
|
520
525
|
selfId: string;
|
|
521
526
|
state: any;
|
|
527
|
+
localAudioTrackMuteStateHandler: (event: TrackMuteEvent) => void;
|
|
528
|
+
localVideoTrackMuteStateHandler: (event: TrackMuteEvent) => void;
|
|
522
529
|
webexMeetingId: string;
|
|
523
530
|
|
|
524
531
|
namespace = MEETINGS;
|
|
@@ -1163,6 +1170,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1163
1170
|
* helper class for managing remote streams
|
|
1164
1171
|
*/
|
|
1165
1172
|
this.remoteMediaManager = null;
|
|
1173
|
+
|
|
1174
|
+
this.localAudioTrackMuteStateHandler = (event) => {
|
|
1175
|
+
this.audio.handleLocalTrackMuteStateChange(this, event.trackState.muted);
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
this.localVideoTrackMuteStateHandler = (event) => {
|
|
1179
|
+
this.video.handleLocalTrackMuteStateChange(this, event.trackState.muted);
|
|
1180
|
+
};
|
|
1166
1181
|
}
|
|
1167
1182
|
|
|
1168
1183
|
/**
|
|
@@ -2056,14 +2071,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2056
2071
|
this.selfId === contentShare.beneficiaryId &&
|
|
2057
2072
|
contentShare.disposition === FLOOR_ACTION.GRANTED
|
|
2058
2073
|
) {
|
|
2059
|
-
// @ts-ignore originalTrack is private - this will be fixed when SPARK-
|
|
2074
|
+
// @ts-ignore originalTrack is private - this will be fixed when SPARK-399695 is done
|
|
2060
2075
|
const localShareTrack = this.mediaProperties.shareTrack?.originalTrack;
|
|
2061
2076
|
|
|
2062
2077
|
// todo: remove this block of code and instead make sure we have LocalTrackEvents.Ended listener always registered (SPARK-399695)
|
|
2063
2078
|
if (localShareTrack?.readyState === 'ended') {
|
|
2064
2079
|
try {
|
|
2065
2080
|
if (this.isMultistream) {
|
|
2066
|
-
await this.unpublishTracks([
|
|
2081
|
+
await this.unpublishTracks([this.mediaProperties.shareTrack]); // todo screen share audio (SPARK-399690)
|
|
2067
2082
|
} else {
|
|
2068
2083
|
await this.stopShare({
|
|
2069
2084
|
skipSignalingCheck: true,
|
|
@@ -2173,8 +2188,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2173
2188
|
oldShareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE
|
|
2174
2189
|
) {
|
|
2175
2190
|
if (this.isMultistream) {
|
|
2176
|
-
|
|
2177
|
-
await this.unpublishTracks([this.mediaProperties.shareTrack?.originalTrack]); // todo screen share audio (SPARK-399690)
|
|
2191
|
+
await this.unpublishTracks([this.mediaProperties.shareTrack]); // todo screen share audio (SPARK-399690)
|
|
2178
2192
|
} else {
|
|
2179
2193
|
await this.updateShare({
|
|
2180
2194
|
sendShare: false,
|
|
@@ -2491,7 +2505,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2491
2505
|
if (this.video) {
|
|
2492
2506
|
payload.muted = payload.muted ?? this.video.isRemotelyMuted();
|
|
2493
2507
|
payload.unmuteAllowed = payload.unmuteAllowed ?? this.video.isUnmuteAllowed();
|
|
2494
|
-
this.video.handleServerRemoteMuteUpdate(payload.muted, payload.unmuteAllowed);
|
|
2508
|
+
this.video.handleServerRemoteMuteUpdate(this, payload.muted, payload.unmuteAllowed);
|
|
2495
2509
|
}
|
|
2496
2510
|
Trigger.trigger(
|
|
2497
2511
|
this,
|
|
@@ -2512,7 +2526,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2512
2526
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED, (payload) => {
|
|
2513
2527
|
if (payload) {
|
|
2514
2528
|
if (this.audio) {
|
|
2515
|
-
this.audio.handleServerRemoteMuteUpdate(payload.muted, payload.unmuteAllowed);
|
|
2529
|
+
this.audio.handleServerRemoteMuteUpdate(this, payload.muted, payload.unmuteAllowed);
|
|
2516
2530
|
}
|
|
2517
2531
|
// with "mute on entry" server will send us remote mute even if we don't have media configured,
|
|
2518
2532
|
// so if being muted by others, always send the notification,
|
|
@@ -3223,6 +3237,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3223
3237
|
* @memberof Meeting
|
|
3224
3238
|
*/
|
|
3225
3239
|
private setLocalAudioTrack(rawAudioTrack: MediaStreamTrack | null, emitEvent = true) {
|
|
3240
|
+
if (this.isMultistream) {
|
|
3241
|
+
throw new Error('this method is only supposed to be used for transcoded meetings');
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3226
3244
|
if (rawAudioTrack) {
|
|
3227
3245
|
const settings = rawAudioTrack.getSettings();
|
|
3228
3246
|
|
|
@@ -3259,6 +3277,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3259
3277
|
* @memberof Meeting
|
|
3260
3278
|
*/
|
|
3261
3279
|
private setLocalVideoTrack(rawVideoTrack: MediaStreamTrack | null, emitEvent = true) {
|
|
3280
|
+
if (this.isMultistream) {
|
|
3281
|
+
throw new Error('this method is only supposed to be used for transcoded meetings');
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3262
3284
|
if (rawVideoTrack) {
|
|
3263
3285
|
const {aspectRatio, frameRate, height, width, deviceId} = rawVideoTrack.getSettings();
|
|
3264
3286
|
|
|
@@ -3372,7 +3394,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3372
3394
|
);
|
|
3373
3395
|
} else if (this.mediaProperties.shareTrack) {
|
|
3374
3396
|
this.mediaProperties.shareTrack.off(LocalTrackEvents.Ended, this.handleShareTrackEnded);
|
|
3375
|
-
this.mediaProperties.shareTrack.stop(); // todo: this line should be removed once SPARK-
|
|
3397
|
+
this.mediaProperties.shareTrack.stop(); // todo: this line should be removed once SPARK-399695 is done
|
|
3376
3398
|
this.mediaProperties.setLocalShareTrack(null);
|
|
3377
3399
|
}
|
|
3378
3400
|
}
|
|
@@ -5775,7 +5797,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5775
5797
|
|
|
5776
5798
|
// audio state could be undefined if you have not sent audio before
|
|
5777
5799
|
this.audio =
|
|
5778
|
-
this.audio || createMuteState(AUDIO, this, this.mediaProperties.mediaDirection);
|
|
5800
|
+
this.audio || createMuteState(AUDIO, this, this.mediaProperties.mediaDirection, true);
|
|
5779
5801
|
});
|
|
5780
5802
|
}
|
|
5781
5803
|
|
|
@@ -5831,7 +5853,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5831
5853
|
|
|
5832
5854
|
// video state could be undefined if you have not sent video before
|
|
5833
5855
|
this.video =
|
|
5834
|
-
this.video || createMuteState(VIDEO, this, this.mediaProperties.mediaDirection);
|
|
5856
|
+
this.video || createMuteState(VIDEO, this, this.mediaProperties.mediaDirection, true);
|
|
5835
5857
|
});
|
|
5836
5858
|
}
|
|
5837
5859
|
|
|
@@ -5947,10 +5969,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5947
5969
|
// TODO wire into default config. There's currently an issue with the stateless plugin or how we register
|
|
5948
5970
|
// @ts-ignore - config coming from registerPlugin
|
|
5949
5971
|
this.mediaProperties.setMediaDirection(Object.assign(this.config.mediaSettings, mediaSettings));
|
|
5950
|
-
|
|
5951
|
-
//
|
|
5952
|
-
|
|
5953
|
-
|
|
5972
|
+
|
|
5973
|
+
// for multistream, this.audio and this.video are created when publishTracks() is called
|
|
5974
|
+
if (!this.isMultistream) {
|
|
5975
|
+
this.audio =
|
|
5976
|
+
this.audio || createMuteState(AUDIO, this, this.mediaProperties.mediaDirection, true);
|
|
5977
|
+
this.video =
|
|
5978
|
+
this.video || createMuteState(VIDEO, this, this.mediaProperties.mediaDirection, true);
|
|
5979
|
+
}
|
|
5954
5980
|
// Validation is already done in addMedia so no need to check if the lenght is greater then 0
|
|
5955
5981
|
this.setLocalTracks(localStream);
|
|
5956
5982
|
if (this.isMultistream && localShare) {
|
|
@@ -6802,7 +6828,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6802
6828
|
error
|
|
6803
6829
|
);
|
|
6804
6830
|
} finally {
|
|
6805
|
-
this.setLocalShareTrack(null);
|
|
6831
|
+
// todo: once SPARK-399695 is done, we will be able to just call this.setLocalShareTrack(null); here instead of the next 2 lines:
|
|
6832
|
+
this.mediaProperties.shareTrack?.off(LocalTrackEvents.Ended, this.handleShareTrackEnded);
|
|
6833
|
+
this.mediaProperties.setLocalShareTrack(null);
|
|
6834
|
+
|
|
6806
6835
|
this.mediaProperties.mediaDirection.sendShare = false;
|
|
6807
6836
|
}
|
|
6808
6837
|
} else {
|
|
@@ -7265,18 +7294,29 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7265
7294
|
* @returns {Promise}
|
|
7266
7295
|
*/
|
|
7267
7296
|
async publishTracks(tracks: {
|
|
7268
|
-
microphone?:
|
|
7269
|
-
camera?:
|
|
7297
|
+
microphone?: LocalMicrophoneTrack;
|
|
7298
|
+
camera?: LocalCameraTrack;
|
|
7270
7299
|
screenShare: {
|
|
7271
|
-
audio?:
|
|
7272
|
-
video?:
|
|
7300
|
+
audio?: LocalTrack; // todo: for now screen share audio is not supported (will be done in SPARK-399690)
|
|
7301
|
+
video?: LocalDisplayTrack;
|
|
7273
7302
|
};
|
|
7274
7303
|
}): Promise<void> {
|
|
7275
7304
|
this.checkMediaConnection();
|
|
7276
7305
|
|
|
7306
|
+
if (!this.isMultistream) {
|
|
7307
|
+
throw new Error('publishTracks() only supported with multistream');
|
|
7308
|
+
}
|
|
7309
|
+
|
|
7277
7310
|
if (tracks.screenShare?.video) {
|
|
7311
|
+
const oldTrack = this.mediaProperties.shareTrack;
|
|
7312
|
+
const localDisplayTrack = tracks.screenShare?.video;
|
|
7313
|
+
|
|
7314
|
+
oldTrack?.off(LocalTrackEvents.Ended, this.handleShareTrackEnded);
|
|
7315
|
+
|
|
7278
7316
|
// we are starting a screen share
|
|
7279
|
-
this.setLocalShareTrack(
|
|
7317
|
+
this.mediaProperties.setLocalShareTrack(localDisplayTrack);
|
|
7318
|
+
|
|
7319
|
+
localDisplayTrack.on(LocalTrackEvents.Ended, this.handleShareTrackEnded);
|
|
7280
7320
|
|
|
7281
7321
|
await this.requestScreenShareFloor();
|
|
7282
7322
|
this.mediaProperties.mediaDirection.sendShare = true;
|
|
@@ -7287,11 +7327,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7287
7327
|
}
|
|
7288
7328
|
|
|
7289
7329
|
if (tracks.microphone) {
|
|
7290
|
-
this.
|
|
7330
|
+
const oldTrack = this.mediaProperties.audioTrack;
|
|
7331
|
+
const localTrack = tracks.microphone;
|
|
7332
|
+
|
|
7333
|
+
oldTrack?.off(LocalTrackEvents.Muted, this.localAudioTrackMuteStateHandler);
|
|
7334
|
+
|
|
7335
|
+
this.mediaProperties.setLocalAudioTrack(localTrack);
|
|
7291
7336
|
this.mediaProperties.mediaDirection.sendAudio = true;
|
|
7292
7337
|
|
|
7293
7338
|
// audio mute state could be undefined if you have not sent audio before
|
|
7294
|
-
|
|
7339
|
+
if (!this.audio) {
|
|
7340
|
+
this.audio = createMuteState(AUDIO, this, this.mediaProperties.mediaDirection, false);
|
|
7341
|
+
} else {
|
|
7342
|
+
this.audio.handleLocalTrackChange(this);
|
|
7343
|
+
}
|
|
7344
|
+
|
|
7345
|
+
localTrack.on(LocalTrackEvents.Muted, this.localAudioTrackMuteStateHandler);
|
|
7295
7346
|
|
|
7296
7347
|
await this.mediaProperties.webrtcMediaConnection.publishTrack(
|
|
7297
7348
|
this.mediaProperties.audioTrack
|
|
@@ -7299,11 +7350,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7299
7350
|
}
|
|
7300
7351
|
|
|
7301
7352
|
if (tracks.camera) {
|
|
7302
|
-
this.
|
|
7353
|
+
const oldTrack = this.mediaProperties.videoTrack;
|
|
7354
|
+
const localTrack = tracks.camera;
|
|
7355
|
+
|
|
7356
|
+
oldTrack?.off(LocalTrackEvents.Muted, this.localVideoTrackMuteStateHandler);
|
|
7357
|
+
|
|
7358
|
+
this.mediaProperties.setLocalVideoTrack(localTrack);
|
|
7303
7359
|
this.mediaProperties.mediaDirection.sendVideo = true;
|
|
7304
7360
|
|
|
7305
7361
|
// video state could be undefined if you have not sent video before
|
|
7306
|
-
|
|
7362
|
+
if (!this.video) {
|
|
7363
|
+
this.video = createMuteState(VIDEO, this, this.mediaProperties.mediaDirection, false);
|
|
7364
|
+
} else {
|
|
7365
|
+
this.video.handleLocalTrackChange(this);
|
|
7366
|
+
}
|
|
7367
|
+
|
|
7368
|
+
localTrack.on(LocalTrackEvents.Muted, this.localVideoTrackMuteStateHandler);
|
|
7307
7369
|
|
|
7308
7370
|
await this.mediaProperties.webrtcMediaConnection.publishTrack(
|
|
7309
7371
|
this.mediaProperties.videoTrack
|
|
@@ -7317,48 +7379,43 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7317
7379
|
* @param {Array<MediaStreamTrack>} tracks
|
|
7318
7380
|
* @returns {Promise}
|
|
7319
7381
|
*/
|
|
7320
|
-
async unpublishTracks(tracks:
|
|
7382
|
+
async unpublishTracks(tracks: LocalTrack[]): Promise<void> {
|
|
7321
7383
|
this.checkMediaConnection();
|
|
7322
7384
|
|
|
7385
|
+
if (!this.isMultistream) {
|
|
7386
|
+
throw new Error('unpublishTracks() is only supported with multistream');
|
|
7387
|
+
}
|
|
7388
|
+
|
|
7323
7389
|
const unpublishPromises = [];
|
|
7324
7390
|
|
|
7325
|
-
for (const track of tracks) {
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
const localTrackToUnpublish = this.mediaProperties.shareTrack;
|
|
7391
|
+
for (const track of tracks.filter((t) => !!t)) {
|
|
7392
|
+
if (track === this.mediaProperties.shareTrack) {
|
|
7393
|
+
this.mediaProperties.setLocalShareTrack(null);
|
|
7329
7394
|
|
|
7330
|
-
this.
|
|
7395
|
+
track.off(LocalTrackEvents.Ended, this.handleShareTrackEnded);
|
|
7331
7396
|
|
|
7332
7397
|
this.releaseScreenShareFloor(); // we ignore the returned promise here on purpose
|
|
7333
7398
|
this.mediaProperties.mediaDirection.sendShare = false;
|
|
7334
7399
|
|
|
7335
|
-
unpublishPromises.push(
|
|
7336
|
-
this.mediaProperties.webrtcMediaConnection.unpublishTrack(localTrackToUnpublish)
|
|
7337
|
-
);
|
|
7400
|
+
unpublishPromises.push(this.mediaProperties.webrtcMediaConnection.unpublishTrack(track));
|
|
7338
7401
|
}
|
|
7339
7402
|
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
const localTrackToUnpublish = this.mediaProperties.audioTrack;
|
|
7343
|
-
|
|
7344
|
-
this.setLocalAudioTrack(null);
|
|
7403
|
+
if (track === this.mediaProperties.audioTrack) {
|
|
7404
|
+
this.mediaProperties.setLocalAudioTrack(null);
|
|
7345
7405
|
this.mediaProperties.mediaDirection.sendAudio = false;
|
|
7346
7406
|
|
|
7347
|
-
|
|
7348
|
-
this.mediaProperties.webrtcMediaConnection.unpublishTrack(localTrackToUnpublish)
|
|
7349
|
-
);
|
|
7350
|
-
}
|
|
7407
|
+
track.off(LocalTrackEvents.Muted, this.localAudioTrackMuteStateHandler);
|
|
7351
7408
|
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
const localTrackToUnpublish = this.mediaProperties.videoTrack;
|
|
7409
|
+
unpublishPromises.push(this.mediaProperties.webrtcMediaConnection.unpublishTrack(track));
|
|
7410
|
+
}
|
|
7355
7411
|
|
|
7356
|
-
|
|
7412
|
+
if (track === this.mediaProperties.videoTrack) {
|
|
7413
|
+
this.mediaProperties.setLocalVideoTrack(null);
|
|
7357
7414
|
this.mediaProperties.mediaDirection.sendVideo = false;
|
|
7358
7415
|
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
);
|
|
7416
|
+
track.off(LocalTrackEvents.Muted, this.localVideoTrackMuteStateHandler);
|
|
7417
|
+
|
|
7418
|
+
unpublishPromises.push(this.mediaProperties.webrtcMediaConnection.unpublishTrack(track));
|
|
7362
7419
|
}
|
|
7363
7420
|
}
|
|
7364
7421
|
|