Skip to content

Commit

Permalink
Call Features Stream Util Implementation and Call client state for To…
Browse files Browse the repository at this point in the history
…gether mode feature (#5362)

* Change files

* Included a common util method that set call features renderer info

* Addressed comments

* Created a new type for togetherMode seating property

* Fix pr build failure

* Fix failing build
  • Loading branch information
cn0151 authored Nov 5, 2024
1 parent ff7dee7 commit 639e504
Show file tree
Hide file tree
Showing 18 changed files with 890 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "togetherMode",
"comment": "Stream utils implementation for call feature streams. This also included the client state for together mode feature",
"packageName": "@azure/communication-react",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "togetherMode",
"comment": "Included a common util method that set call features renderer info",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
84 changes: 55 additions & 29 deletions packages/calling-stateful-client/src/CallClientState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,45 +272,70 @@ export interface RaiseHandCallFeatureState {

/* @conditional-compile-remove(together-mode) */
/**
* State only version of {@link @azure/communication-calling#TogetherModeCallFeature}. {@link StatefulCallClient} will
* automatically listen for raised hands on the call and update the state exposed by {@link StatefulCallClient} accordingly.
* @alpha
* @beta
*/
export interface TogetherModeCallFeatureState {
/**
* Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.togetherModeStream}.
*/
stream: TogetherModeStreamState[];
export type CallFeatureStreamName = 'togetherMode';

/* @conditional-compile-remove(together-mode) */
/**
* @beta
*/
export interface CallFeatureStreamState extends RemoteVideoStreamState {
feature?: CallFeatureStreamName;
}

/* @conditional-compile-remove(together-mode) */
/**
* State only version of {@link @azure/communication-calling#TogetherModeVideoStream}.
* @alpha
* State only version of {@link @azure/communication-calling#TogetherModeSeatingMap}.
* @beta
*
* Represents the seating position of a participant in Together Mode.
*/
export interface TogetherModeStreamState {
/**
* Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.id}.
*/
id: number;
/**
* Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.mediaStreamType}.
*/
mediaStreamType: MediaStreamType;
/**
* Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.isReceiving}.
* @public
*/
isReceiving: boolean;
export interface TogetherModeSeatingPositionState {
// The top left offset from the top of the together mode view.
top: number;
// The left offset position from the left of the together mode view.
left: number;
// The width of the seating area
width: number;
// The height of the seating area.
height: number;
}

/* @conditional-compile-remove(together-mode) */
/**
* Represents the seating positions of participants in Together Mode.
*
* @beta
*/
export type TogetherModeParticipantSeatingState = Record<string, TogetherModeSeatingPositionState>;

/* @conditional-compile-remove(together-mode) */
/**
* Interface representing the streams in Together Mode.
*
* @beta
*/
export interface TogetherModeStreamsState {
mainVideoStream?: CallFeatureStreamState;
}

/* @conditional-compile-remove(together-mode) */
/**
* State only version of {@link @azure/communication-calling#TogetherModeCallFeature}. {@link StatefulCallClient} will
* automatically listen for raised hands on the call and update the state exposed by {@link StatefulCallClient} accordingly.
* @beta
*/
export interface TogetherModeCallFeatureState {
isActive: boolean;
/**
* {@link VideoStreamRendererView} that is managed by createView/disposeView in {@link StatefulCallClient}
* API. This can be undefined if the stream has not yet been rendered and defined after createView creates the view.
* Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.togetherModeStream}.
*/
view?: VideoStreamRendererViewState;
streams: TogetherModeStreamsState;
/**
* Proxy of {@link @azure/communication-calling#RemoteVideoStream.size}.
* Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.TogetherModeSeatingMap}.
*/
streamSize?: { width: number; height: number };
seatingPositions: TogetherModeParticipantSeatingState;
}

/**
Expand Down Expand Up @@ -621,6 +646,7 @@ export interface CallState {
/* @conditional-compile-remove(together-mode) */
/**
* Proxy of {@link @azure/communication-calling#TogetherModeCallFeature}.
* @beta
*/
togetherMode: TogetherModeCallFeatureState;
/**
Expand Down
114 changes: 108 additions & 6 deletions packages/calling-stateful-client/src/CallContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { TeamsCaptionsInfo } from '@azure/communication-calling';
import { CaptionsKind, CaptionsInfo as AcsCaptionsInfo } from '@azure/communication-calling';
import { EnvironmentInfo } from '@azure/communication-calling';
/* @conditional-compile-remove(together-mode) */
import { TogetherModeVideoStream } from '@azure/communication-calling';
import { TogetherModeVideoStream, TogetherModeSeatingMap } from '@azure/communication-calling';
import { AzureLogger, createClientLogger, getLogLevel } from '@azure/logger';
import { EventEmitter } from 'events';
import { enableMapSet, enablePatches, Patch, produce } from 'immer';
Expand Down Expand Up @@ -65,6 +65,8 @@ import { SpotlightedParticipant } from '@azure/communication-calling';
import { LocalRecordingInfo } from '@azure/communication-calling';
/* @conditional-compile-remove(local-recording-notification) */
import { RecordingInfo } from '@azure/communication-calling';
/* @conditional-compile-remove(together-mode) */
import { CallFeatureStreamState, TogetherModeSeatingPositionState } from './CallClientState';

enableMapSet();
// Needed to generate state diff for verbose logging.
Expand Down Expand Up @@ -455,11 +457,78 @@ export class CallContext {
}

/* @conditional-compile-remove(together-mode) */
public setTogetherModeVideoStream(callId: string, addedStream: TogetherModeVideoStream[]): void {
public setTogetherModeVideoStreams(
callId: string,
addedStreams: CallFeatureStreamState[],
removedStreams: CallFeatureStreamState[]
): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
for (const stream of removedStreams) {
if (stream.mediaStreamType === 'Video') {
call.togetherMode.streams.mainVideoStream = undefined;
call.togetherMode.isActive = false;
call.togetherMode.seatingPositions = {};
}
}

for (const newStream of addedStreams) {
// This should only be called by the subscriber and some properties are add by other components so if the
// stream already exists, only update the values that subscriber knows about.
const mainVideoStream = call.togetherMode.streams.mainVideoStream;
if (mainVideoStream && mainVideoStream.id === newStream.id) {
mainVideoStream.mediaStreamType = newStream.mediaStreamType;
mainVideoStream.isAvailable = newStream.isAvailable;
mainVideoStream.isReceiving = newStream.isReceiving;
} else {
call.togetherMode.streams.mainVideoStream = newStream;
}
call.togetherMode.isActive = true;
}
}
});
}

/* @conditional-compile-remove(together-mode) */
public setTogetherModeVideoStreamIsAvailable(callId: string, streamId: number, isAvailable: boolean): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
const stream = call.togetherMode.streams.mainVideoStream;
if (stream && stream?.id === streamId) {
stream.isReceiving = isAvailable;
}
}
});
}

/* @conditional-compile-remove(together-mode) */
public setTogetherModeVideoStreamIsReceiving(callId: string, streamId: number, isReceiving: boolean): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
const stream = call.togetherMode.streams.mainVideoStream;
if (stream && stream?.id === streamId) {
stream.isReceiving = isReceiving;
}
}
});
}

/* @conditional-compile-remove(together-mode) */
public setTogetherModeVideoStreamSize(
callId: string,
streamId: number,
size: { width: number; height: number }
): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
call.togetherMode = { stream: addedStream };
const stream = call.togetherMode.streams.mainVideoStream;
if (stream && stream?.id === streamId) {
stream.streamSize = size;
}
}
});
}
Expand All @@ -470,15 +539,29 @@ export class CallContext {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
for (const stream of removedStream) {
if (stream.mediaStreamType in call.togetherMode.stream) {
// Temporary lint fix: Remove the stream from the list
call.togetherMode.stream = [];
if (stream.mediaStreamType === 'Video') {
call.togetherMode.streams.mainVideoStream = undefined;
call.togetherMode.isActive = false;
}
}
}
});
}

/* @conditional-compile-remove(together-mode) */
public setTogetherModeSeatingCoordinates(callId: string, seatingMap: TogetherModeSeatingMap): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
const seatingPositions: Record<string, TogetherModeSeatingPositionState> = {};
for (const [userId, seatingPosition] of seatingMap.entries()) {
seatingPositions[userId] = seatingPosition;
}
call.togetherMode.seatingPositions = seatingPositions;
}
});
}

public setCallRaisedHands(callId: string, raisedHands: RaisedHand[]): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
Expand Down Expand Up @@ -718,6 +801,25 @@ export class CallContext {
});
}

/* @conditional-compile-remove(together-mode) */
public setTogetherModeVideoStreamRendererView(
callId: string,
togetherModeStreamType: string,
view: VideoStreamRendererViewState | undefined
): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
if (call) {
if (togetherModeStreamType === 'Video') {
const togetherModeStream = call.togetherMode.streams.mainVideoStream;
if (togetherModeStream) {
togetherModeStream.view = view;
}
}
}
});
}

public setParticipantState(callId: string, participantKey: string, state: RemoteParticipantStatus): void {
this.modifyState((draft: CallClientState) => {
const call = draft.calls[this._callIdHistory.latestCallId(callId)];
Expand Down
Loading

0 comments on commit 639e504

Please sign in to comment.