Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add audio output level controls #831

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a59fc73
feat(client): keep track of audio output level in call state and for …
MartinCupela Jul 20, 2023
6735037
feat(react-bindings): add useDefaultAudioOutputLevel hook
MartinCupela Jul 20, 2023
748ae42
feat(react-sdk): enable setting audio output volume to Audio component
MartinCupela Jul 20, 2023
4b0db50
feat(react-sdk): enable toggling master audio output add AudioOutputS…
MartinCupela Jul 20, 2023
f965240
feat(react-sdk): add AudioOutputLevelSlider to ParticipantActionsCont…
MartinCupela Jul 20, 2023
db315cd
feat(styling): add speaker-off icon
MartinCupela Jul 20, 2023
36636a8
feat(styling): add AudioLevelSlider styles
MartinCupela Jul 20, 2023
e0fc04a
feat(react-dogfood): keep showing indicator for local audio input eve…
MartinCupela Jul 20, 2023
8b0d0e6
feat(react-dogfood): add AudioOutputMenu with AudioLevelSlider to Tog…
MartinCupela Jul 20, 2023
bb41135
feat(react-dogfood): add AudioOutputMenu with AudioLevelSlider to Tog…
MartinCupela Jul 20, 2023
1bbbc06
Merge branch 'main' into feat/client/audio-output-level
MartinCupela Jul 20, 2023
c21b9a5
fix(client): validate the default audio output level value before it …
MartinCupela Jul 21, 2023
7128898
refactor(client): pass optionally sessionId to setAudioOutputLevel in…
MartinCupela Jul 21, 2023
a555204
refactor(client): do not update default audio output level once in ac…
MartinCupela Jul 21, 2023
4b581c8
refactor(react-sdk): use local participants audio output level as a m…
MartinCupela Jul 21, 2023
a9a382f
Merge branch 'main' into feat/client/audio-output-level
MartinCupela Jul 21, 2023
e78b263
refactor(client): do not update all participants audio output level i…
MartinCupela Jul 21, 2023
875608d
refactor(react-sdk): do not use local participant's audio output leve…
MartinCupela Jul 21, 2023
b904fec
refactor: rename default audio output level to master audio output level
MartinCupela Jul 21, 2023
49cc7d3
feat(react-sdk): add toggle functionality to AudioOutputLevelSlider
MartinCupela Jul 21, 2023
2fdb4bb
docs: apply suggestions to code documentation
MartinCupela Jul 21, 2023
e1fa904
fix(react-sdk): adjust only master audio output with ToggleAudioOutpu…
MartinCupela Jul 21, 2023
02ef9e8
docs(react-sdk): document the control of audio output level
MartinCupela Jul 21, 2023
4840f21
docs(react-native): document the control of audio output level
MartinCupela Jul 21, 2023
b70f7b7
Merge remote-tracking branch 'origin/feat/client/audio-output-level' …
MartinCupela Jul 21, 2023
0b676f1
style: fix lint issue
MartinCupela Jul 21, 2023
11ecff2
docs: incorrect property definitions
MartinCupela Jul 24, 2023
17cc269
Merge branch 'main' into feat/client/audio-output-level
MartinCupela Jul 24, 2023
44ca940
feat(react-sdk): add dragging functionality to AudioOutputLevelSlider
MartinCupela Jul 28, 2023
68334f0
Merge branch 'main' into feat/client/audio-output-level
MartinCupela Jul 28, 2023
cfec5d6
Merge branch 'main' into feat/client/audio-output-level
MartinCupela Jul 31, 2023
5381953
fix(react-sdk): prevent removal of event listeners when dragging part…
MartinCupela Aug 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/client/src/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,27 @@ export class Call {
});
};

/**
* Updates the audio output level for a given sessionId (participant session) or,
* the master volume if `sessionId` is omitted.
*
* @param level the audio level [0 - 1].
* @param sessionId an optional session id.
*/
setAudioOutputLevel = (level: number, sessionId?: string) => {
if (level < 0 || level > 1) {
throw new Error(`Audio output level must be in the [0-1] range`);
}

if (sessionId) {
this.state.updateParticipant(sessionId, {
audioOutputLevel: level,
});
} else {
this.state.setMasterAudioOutputLevel(level);
}
};

/**
* Sets the `audioDeviceId` property of the [`localParticipant$`](./StreamVideoClient.md/#readonlystatestore)).
*
Expand Down
33 changes: 32 additions & 1 deletion packages/client/src/store/CallState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ export enum CallingState {
* @react You don't have to use this class directly, as we are exposing the state through Hooks.
*/
export class CallState {
/**
* The speaker volume level that is set, if `StreamVideoParticipant.audioOutputLevel` is undefined.
*/
private masterAudioOutputLevelSubject = new BehaviorSubject<number>(1);
/**
* The raw call metadata object, as defined on the backend.
*
Expand Down Expand Up @@ -165,8 +169,12 @@ export class CallState {
*/
private callRecordingListSubject = new BehaviorSubject<CallRecording[]>([]);

// Derived state
/**
* Emits the default audio output level value in form of decimal number in range of 0-1.
*/
masterAudioOutputLevel$: Observable<number>;

// Derived state
/**
* The time the call session actually started.
* Useful for displaying the call duration.
Expand Down Expand Up @@ -303,6 +311,8 @@ export class CallState {
distinctUntilChanged(),
);

this.masterAudioOutputLevel$ =
this.masterAudioOutputLevelSubject.asObservable();
this.startedAt$ = this.startedAtSubject.asObservable();
this.participantCount$ = this.participantCountSubject.asObservable();
this.anonymousParticipantCount$ =
Expand Down Expand Up @@ -473,6 +483,27 @@ export class CallState {
return this.setCurrentValue(this.callingStateSubject, state);
};

/**
* Retrieves the current value of the default audio output level.
*
* @internal
*/
get masterAudioOutputLevel() {
return this.getCurrentValue(this.masterAudioOutputLevel$);
}

/**
* Sets the current value of the default audio output level.
*
* @internal
*/
setMasterAudioOutputLevel = (level: number) => {
if (level < 0 || level > 1) {
throw new Error(`Audio output level must be in the [0-1] range`);
}
return this.setCurrentValue(this.masterAudioOutputLevelSubject, level);
};

/**
* The list of call recordings.
*/
Expand Down
8 changes: 7 additions & 1 deletion packages/client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ export enum DebounceType {

export interface StreamVideoParticipant extends Participant {
/**
* The participant's audio stream, if they are publishing audio and
* The speaker volume in range 0 - 1 set by the participant.
* If not set, then CallState.masterAudioOutputLevel should be reflected.
*/
audioOutputLevel?: number;

/**
* The participant's audio stream, if they are publishing audio, and
* we have subscribed to it.
*/
audioStream?: MediaStream;
Expand Down
10 changes: 10 additions & 0 deletions packages/react-bindings/src/hooks/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,13 @@ export const useCallStartedAt = () => {
const { startedAt$ } = useCallState();
return useObservableValue(startedAt$);
};

/**
* Utility hook providing the default audio output level in a given call.
*
* @category Call State
*/
export const useMasterAudioOutputLevel = () => {
const { masterAudioOutputLevel$ } = useCallState();
return useObservableValue(masterAudioOutputLevel$);
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,43 +44,46 @@ The `StreamCall` component uses the `StreamCallProvider` under the hood.

Here are all the call-related state hooks:

| Name | Description |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `useCall` | The `Call` instance that is registered with `StreamCall`. You need the `Call` instance to initiate API calls. |
| `useIsCallRecordingInProgress` | It's' `true` if the call is being recorded. |
| `useIsCallBroadcastingInProgress` | It's `true` if the call is being broadcasted. |
| `useIsCallLive` | It's `true` if the call is currently live. |
| `useCallMembers` | The list of call members |
| `useCallCallingState` | Provides information about the call state. For example, `RINGING`, `JOINED` or `RECONNECTING`. |
| `useCallStartedAt` | The actual start time of the current call session. |
| `useCallStatsReport` | When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval. |
| `useCallMetadata` | The `CallResponse`, see below for more information. |
| `useCallRecordings` | The latest list of recordings performed during the call. |
| `useHasOngoingScreenShare` | It will return `true` if at least one participant is sharing their screen. |
| `useDominantSpeaker` | The participant that is the current dominant speaker of the call. |
| `useOwnCapabilities` | The capabilities of the local participant. |
| `useHasPermissions` | Returns `true` if the local participant has all the given permissions. |
| `useCallPermissionRequest` | The latest call permission request. |
| Name | Description |
|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `useCall` | The `Call` instance that is registered with `StreamCall`. You need the `Call` instance to initiate API calls. |
| `useCallCallingState` | Provides information about the call state. For example, `RINGING`, `JOINED` or `RECONNECTING`. |
| `useCallMembers` | The list of call members |
| `useCallMetadata` | The `CallResponse`, see below for more information. |
| `useCallRecordings` | The latest list of recordings performed during the call. |
| `useCallStartedAt` | The actual start time of the current call session. |
| `useCallStatsReport` | When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval. |
| `useDominantSpeaker` | The participant that is the current dominant speaker of the call. |
| `useHasOngoingScreenShare` | It will return `true` if at least one participant is sharing their screen. |
| `useHasPermissions` | Returns `true` if the local participant has all the given permissions. |
| `useIsCallRecordingInProgress` | It's' `true` if the call is being recorded. |
| `useIsCallBroadcastingInProgress` | It's `true` if the call is being broadcasted. |
| `useIsCallLive` | It's `true` if the call is currently live. |
| `useMasterAudioOutputLevel` | The audio output level applied to all participants if `StreamVideoParticipant.audioOutputLevel` is not explicitly set. Stored as decimal number in the range of 0 - 1. Managed locally. |
| `useOwnCapabilities` | The capabilities of the local participant. |

The `CallResponse` object contains the following information:

| Name | Description |
| ------------------ | ------------------------------------------------------- |
| `backstage` | It's `true` if the call is in backstage mode. |
| `blocked_user_ids` | The users who are blocked from this call. |
| `created_at` | The time the call was created. |
| `created_by` | The creator of the call. |
| `custom` | Custom data attached to the call. |
| `egress` | Broadcasting related information. |
| `ended_at` | When the call was ended. |
| `ingress` | RTMP publishing related information. |
| `recording` | It's `true` if the call is being recorded. |
| `session` | Information related to the current session of the call. |
| `settings` | The settings for this call. |
| `starts_at` | The scheduled start time of the call. |
| `team` | Team that the call is restricted to. |
| `type` | The type of the call. |
| `updated_at` | When the call was updated. |
| Name | Description |
|----------------------|---------------------------------------------------------|
| `backstage` | It's `true` if the call is in backstage mode. |
| `blocked_user_ids` | The users who are blocked from this call. |
| `cid` | The unique identifier for a call (`<type>:<id>`). |
| `created_at` | The time the call was created. |
| `created_by` | The creator of the call. |
| `current_session_id` | The unique identifier of the current call session. |
| `custom` | Custom data attached to the call. |
| `egress` | Broadcasting related information. |
| `ended_at` | When the call was ended. |
| `id` | The unique call identifier within the given call type. |
| `ingress` | RTMP publishing related information. |
| `recording` | It's `true` if the call is being recorded. |
| `session` | Information related to the current session of the call. |
| `settings` | The settings for this call. |
| `starts_at` | The scheduled start time of the call. |
| `team` | Team that the call is restricted to. |
| `type` | The type of the call. |
| `updated_at` | When the call was updated. |

## Participant state

Expand Down Expand Up @@ -123,21 +126,29 @@ const CallUI = () => {

The `StreamVideoParticipant` object contains the following information:

| Name | Description |
| -------------------- | --------------------------------------------------------------------------- |
| `user` | The user object for this participant. |
| `publishedTracks` | The track types the participant is currently publishing |
| `joinedAt` | The time the participant joined the call. |
| `connectionQuality` | The participant's connection quality. |
| `isSpeaking` | It's `true` if the participant is currently speaking. |
| `isDominantSpeaker` | It's `true` if the participant is the current dominant speaker in the call. |
| `audioLevel` | The audio level of the participant. |
| `audioStream` | The published audio `MediaStream`. |
| `videoStream` | The published video `MediaStream`. |
| `screenShareStream` | The published screen share `MediaStream`. |
| `isLocalParticipant` | It's `true` if the participant is the local participant. |
| `pinnedAt` | The time the participant was pinned. |
| `reaction` | The last reaction this user has sent to this call. |
| Name | Description |
|---------------------------|---------------------------------------------------------------------------------------------|
| `audioLevel` | The audio level of the participant. |
| `audioOutputLevel` | The speaker volume in range 0 - 1 set by the participant. |
| `audioStream` | The published audio `MediaStream`. |
| `connectionQuality` | The participant's connection quality. |
| `image` | The profile image of the user. |
| `isDominantSpeaker` | It's `true` if the participant is the current dominant speaker in the call. |
| `isLocalParticipant` | It's `true` if the participant is the local participant. |
| `isSpeaking` | It's `true` if the participant is currently speaking. |
| `joinedAt` | The time the participant joined the call. |
| `name` | The participant name. |
| `pinnedAt` | The time the participant was pinned. |
| `publishedTracks` | The track types the participant is currently publishing |
| `reaction` | The last reaction this user has sent to this call. |
| `roles` | The array of role names assigned to the participant in the active call. |
| `screenShareStream` | The published screen share `MediaStream`. |
| `screenShareDimension` | The preferred screen share dimensions for this participant. |
| `sessionId` | Participant's session ID. |
| `userId` | Participant's user ID. |
| `videoStream` | The published video `MediaStream`. |
| `videoDimension` | The preferred video dimensions for this participant. |
| `viewportVisibilityState` | The visibility state of the participant's video element within the pre-configured viewport. |

The `StreamVideoLocalParticipant` has these additional properties:

Expand Down Expand Up @@ -199,7 +210,6 @@ The `UserResponse` contains the following properties:
| `created_at` | The time the user was created. |
| `custom` | Custom user data. |
| `deleted_at` | The time the user was deleted. |
| `devices` | The registered push notification devices of the user. |
| `id` | The id of the user. |
| `image` | The profile image of the user. |
| `name` | The name of the user. |
Expand Down
Loading