Skip to content

Commit

Permalink
Merge branch 'cherry-pick-08042024' of github.com:mkesavan13/webex-js…
Browse files Browse the repository at this point in the history
…-sdk into cherry-pick-08042024
  • Loading branch information
Catalin committed Apr 15, 2024
2 parents 4c0217c + 0cfa952 commit 51236db
Show file tree
Hide file tree
Showing 15 changed files with 551 additions and 196 deletions.
51 changes: 40 additions & 11 deletions docs/samples/browser-plugin-meetings/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,8 @@ const stopShareBtn = document.querySelector('#ts-stop-screenshare');
const toggleAudioButton = document.querySelector('#ts-toggle-audio');
const stopVideoButton = document.querySelector('#ts-stop-video');
const stopAudioButton = document.querySelector('#ts-stop-audio');
const muteVideoMessage = document.querySelector('#ts-mute-video-message');
const muteAudioMessage = document.querySelector('#ts-mute-audio-message');
const modeBtn = document.getElementById('mode-type');

/**
Expand Down Expand Up @@ -1320,6 +1322,15 @@ async function loadCamera(constraints) {

meetingStreamsLocalVideo.srcObject = localMedia.cameraStream.outputStream;

localMedia.cameraStream.on('user-mute-state-change', (muted) => {
console.log('MeetingControls#loadCamera() :: local camera stream user mute state changed to', muted);
});

localMedia.cameraStream.on('system-mute-state-change', (muted) => {
console.log('MeetingControls#loadCamera() :: local camera stream system mute state changed to', muted);
handleMuteVideoMessage();
});

localMedia.cameraStream.on('stream-ended', () => {
console.log('MeetingControls#loadCamera() :: local camera stream ended');

Expand All @@ -1332,6 +1343,7 @@ async function loadCamera(constraints) {
clearVideoResolutionCheckInterval(localVideoResElm, localVideoResolutionInterval);
});

handleMuteVideoMessage();
handleEffectsButton(toggleVbgBtn, VBG);
loadCameraBtn.disabled = true;
stopVideoButton.disabled = false;
Expand Down Expand Up @@ -1392,6 +1404,16 @@ async function loadMicrophone(constraints) {

meetingStreamsLocalAudio.srcObject = localMedia.microphoneStream.outputStream;

localMedia.microphoneStream.on('user-mute-state-change', (muted) => {
console.log('MeetingControls#loadMicrophone() :: local microphone stream user mute state changed to', muted);
handleAudioButton();
});

localMedia.microphoneStream.on('system-mute-state-change', (muted) => {
console.log('MeetingControls#loadMicrophone() :: local microphone stream system mute state changed to', muted);
handleMuteAudioMessage();
});

localMedia.microphoneStream.on('stream-ended', () => {
console.log('MeetingControls#loadMicrophone() :: local microphone stream ended');

Expand All @@ -1402,6 +1424,7 @@ async function loadMicrophone(constraints) {
loadMicrophoneBtn.disabled = false;
});

handleMuteAudioMessage();
handleEffectsButton(toggleBNRBtn, BNR);
loadMicrophoneBtn.disabled = true;
stopAudioButton.disabled = false;
Expand Down Expand Up @@ -1642,12 +1665,12 @@ function setVideoInputDevice() {
const {video} = getAudioVideoInput();

if (meeting) {
const isMuted = localMedia.cameraStream?.muted;
const isMuted = localMedia.cameraStream?.userMuted;
localMedia.cameraStream?.stop();

return getUserMedia({video})
.then(() => {
localMedia.cameraStream.setMuted(!!isMuted);
localMedia.cameraStream.setUserMuted(!!isMuted);
localVideoResolutionCheckInterval();
meeting.publishStreams({camera: localMedia.cameraStream});
});
Expand All @@ -1662,12 +1685,12 @@ function setAudioInputDevice() {
const {audio} = getAudioVideoInput();

if (meeting) {
const isMuted = localMedia.microphoneStream?.muted;
const isMuted = localMedia.microphoneStream?.userMuted;
localMedia.microphoneStream?.stop();

return getUserMedia({audio})
.then(() => {
localMedia.microphoneStream.setMuted(!!isMuted);
localMedia.microphoneStream.setUserMuted(!!isMuted);
meeting.publishStreams({microphone: localMedia.microphoneStream});
});
}
Expand All @@ -1690,31 +1713,38 @@ function setAudioOutputDevice() {
}

function handleAudioButton() {
const audioButtonTitle = localMedia.microphoneStream.muted ? 'Unmute' : 'Mute';
const audioButtonTitle = localMedia.microphoneStream.userMuted ? 'Unmute' : 'Mute';
toggleAudioButton.innerHTML = `${audioButtonTitle} Audio`;
}

function handleMuteAudioMessage() {
muteAudioMessage.innerHTML = localMedia.microphoneStream.systemMuted ? "Warning: microphone may be muted by the system" : "";
}

function toggleSendAudio() {
console.log('MeetingControls#toggleSendAudio()');

if (localMedia.microphoneStream) {
const newMuteValue = !localMedia.microphoneStream.muted;
const newMuteValue = !localMedia.microphoneStream.userMuted;

localMedia.microphoneStream.setMuted(newMuteValue);
handleAudioButton();
localMedia.microphoneStream.setUserMuted(newMuteValue);

console.log(`MeetingControls#toggleSendAudio() :: Successfully ${newMuteValue ? 'muted': 'unmuted'} audio!`);
return;
}
}

function handleMuteVideoMessage() {
muteVideoMessage.innerHTML = localMedia.cameraStream.systemMuted ? "Warning: camera may be muted by the system" : "";
}

function toggleSendVideo() {
console.log('MeetingControls#toggleSendVideo()');

if (localMedia.cameraStream) {
const newMuteValue = !localMedia.cameraStream.muted;
const newMuteValue = !localMedia.cameraStream.userMuted;

localMedia.cameraStream.setMuted(newMuteValue);
localMedia.cameraStream.setUserMuted(newMuteValue);

console.log(`MeetingControls#toggleSendVideo() :: Successfully ${newMuteValue ? 'muted': 'unmuted'} video!`);

Expand Down Expand Up @@ -3083,7 +3113,6 @@ function muteMember(muteButton) {
if (meeting) {
meeting.mute(participantID, newMuteStatus).then((res) => {
console.log(res, `participant is ${newMuteStatus ? 'muted' : 'unmuted'}`);
handleAudioButton();
}).catch((err) => {
console.log('error', err);
});
Expand Down
2 changes: 2 additions & 0 deletions docs/samples/browser-plugin-meetings/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ <h2 class="collapsible">
<!--mute-unmute-video -->
<button id="ts-toggle-video" type="button" onclick="toggleSendVideo()" class="btn-code">Toggle Video</button>
<button id="ts-stop-video" type="button" disabled onclick="stopStartVideo()" class="btn-code">Stop Video</button>
<span id="ts-mute-video-message" class="webex-warning"></span>
</div>
</div>

Expand All @@ -209,6 +210,7 @@ <h2 class="collapsible">
<!--mute-unmute audio -->
<button id="ts-toggle-audio" type="button" onclick="toggleSendAudio()" class="btn-code">Mute Audio</button>
<button id="ts-stop-audio" type="button" disabled onclick="stopStartAudio()" class="btn-code">Stop Audio</button>
<span id="ts-mute-audio-message" class="webex-warning"></span>
</div>
</div>

Expand Down
4 changes: 4 additions & 0 deletions docs/samples/browser-plugin-meetings/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,10 @@ legend {
padding: 0.3rem;
}

.webex-warning {
color: #7d4705;
}

.webex-error {
color: #de3434;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/@webex/media-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"deploy:npm": "yarn npm publish"
},
"dependencies": {
"@webex/internal-media-core": "2.2.9",
"@webex/internal-media-core": "2.3.0",
"@webex/ts-events": "^1.1.0",
"@webex/web-media-effects": "^2.15.6"
},
Expand All @@ -41,6 +41,7 @@
"@webex/test-helper-chai": "workspace:*",
"@webex/test-helper-mock-webex": "workspace:*",
"eslint": "^8.24.0",
"jsdom-global": "3.0.2",
"sinon": "^9.2.4"
}
}
1 change: 1 addition & 0 deletions packages/@webex/media-helpers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {
LocalStreamEventNames,
StreamEventNames,
RemoteStream,
RemoteStreamEventNames,
type ServerMuteReason,
LocalMicrophoneStreamEventNames,
LocalCameraStreamEventNames,
Expand Down
19 changes: 10 additions & 9 deletions packages/@webex/media-helpers/src/webrtc-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ export {
LocalStreamEventNames,
StreamEventNames,
RemoteStream,
RemoteStreamEventNames,
type VideoContentHint,
} from '@webex/internal-media-core';

export type ServerMuteReason =
| 'remotelyMuted' // other user has remotely muted us
| 'clientRequestFailed' // client called setMuted() but server request failed
| 'clientRequestFailed' // client called setUserMuted() but server request failed
| 'localUnmuteRequired'; // server forced the client to be unmuted

// these events are in addition to WCME events. This will be properly typed once webrtc-core event types inheritance is fixed
Expand Down Expand Up @@ -74,22 +75,22 @@ class _LocalMicrophoneStream extends WcmeLocalMicrophoneStream {
return this.unmuteAllowed;
}

setMuted(muted: boolean): void {
setUserMuted(muted: boolean): void {
if (!muted) {
if (!this.isUnmuteAllowed()) {
throw new Error('Unmute is not allowed');
}
}

return super.setMuted(muted);
return super.setUserMuted(muted);
}

/**
* @internal
*/
setServerMuted(muted: boolean, reason: ServerMuteReason) {
if (muted !== this.muted) {
this.setMuted(muted);
if (muted !== this.userMuted) {
this.setUserMuted(muted);
this[LocalMicrophoneStreamEventNames.ServerMuted].emit(muted, reason);
}
}
Expand All @@ -116,22 +117,22 @@ class _LocalCameraStream extends WcmeLocalCameraStream {
return this.unmuteAllowed;
}

setMuted(muted: boolean): void {
setUserMuted(muted: boolean): void {
if (!muted) {
if (!this.isUnmuteAllowed()) {
throw new Error('Unmute is not allowed');
}
}

return super.setMuted(muted);
return super.setUserMuted(muted);
}

/**
* @internal
*/
setServerMuted(muted: boolean, reason: ServerMuteReason) {
if (muted !== this.muted) {
this.setMuted(muted);
if (muted !== this.userMuted) {
this.setUserMuted(muted);
this[LocalCameraStreamEventNames.ServerMuted].emit(muted, reason);
}
}
Expand Down
21 changes: 11 additions & 10 deletions packages/@webex/media-helpers/test/unit/spec/webrtc-core.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'jsdom-global/register';
import {assert, expect} from '@webex/test-helper-chai';
import sinon from 'sinon';
import {
Expand Down Expand Up @@ -53,22 +54,22 @@ describe('media-helpers', () => {

it('by default allows unmuting', async () => {
assert.equal(stream.isUnmuteAllowed(), true);
await stream.setMuted(false);
await stream.setUserMuted(false);
});

it('rejects setMute(false) if unmute is not allowed', async () => {
await stream.setUnmuteAllowed(false);
it('rejects setUserMuted(false) if unmute is not allowed', async () => {
stream.setUnmuteAllowed(false);

assert.equal(stream.isUnmuteAllowed(), false);
const fn = () => stream.setMuted(false);
const fn = () => stream.setUserMuted(false);
expect(fn).to.throw(/Unmute is not allowed/);
});

it('resolves setMute(false) if unmute is allowed', async () => {
await stream.setUnmuteAllowed(true);
it('resolves setUserMuted(false) if unmute is allowed', async () => {
stream.setUnmuteAllowed(true);

assert.equal(stream.isUnmuteAllowed(), true);
await stream.setMuted(false);
await stream.setUserMuted(false);
});

it('returns a reasonable length string from JSON.stringify()', () => {
Expand All @@ -81,16 +82,16 @@ describe('media-helpers', () => {
});

const checkSetServerMuted = async (startMute, setMute, expectedCalled) => {
await stream.setMuted(startMute);
await stream.setUserMuted(startMute);

assert.equal(stream.muted, startMute);
assert.equal(stream.userMuted, startMute);

const handler = sinon.fake();
stream.on(event.ServerMuted, handler);

await stream.setServerMuted(setMute, 'remotelyMuted');

assert.equal(stream.muted, setMute);
assert.equal(stream.userMuted, setMute);
if (expectedCalled) {
assert.calledOnceWithExactly(handler, setMute, 'remotelyMuted');
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/@webex/plugin-meetings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
},
"dependencies": {
"@webex/common": "workspace:*",
"@webex/internal-media-core": "2.2.9",
"@webex/internal-media-core": "2.3.0",
"@webex/internal-plugin-conversation": "workspace:*",
"@webex/internal-plugin-device": "workspace:*",
"@webex/internal-plugin-llm": "workspace:*",
Expand Down
1 change: 1 addition & 0 deletions packages/@webex/plugin-meetings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export {
LocalSystemAudioStream,
LocalStreamEventNames,
StreamEventNames,
RemoteStreamEventNames,
type ServerMuteReason,
LocalMicrophoneStreamEventNames,
LocalCameraStreamEventNames,
Expand Down
Loading

0 comments on commit 51236db

Please sign in to comment.