Skip to content

Commit

Permalink
Merge pull request #62 from whereby/nandor/pan-251-handle-recording-a…
Browse files Browse the repository at this point in the history
…nd-streaming-participants

Handle recording and streaming states
  • Loading branch information
nandito authored Sep 4, 2023
2 parents d81b761 + 6a6087d commit 6452085
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 3 deletions.
69 changes: 69 additions & 0 deletions src/lib/RoomConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ServerSocket, {
NewClientEvent,
RoomJoinedEvent as SignalRoomJoinedEvent,
RoomKnockedEvent as SignalRoomKnockedEvent,
SignalClient,
SocketManager,
} from "@whereby/jslib-media/src/utils/ServerSocket";
import { sdkVersion } from "./index";
Expand Down Expand Up @@ -54,6 +55,16 @@ export type RoomConnectionStatus =
| "accepted"
| "rejected";

export type CloudRecordingState = {
status: "" | "recording";
startedAt: number | null;
};

export type StreamingState = {
status: "" | "streaming";
startedAt: number | null;
};

type RoomJoinedEvent = {
localParticipant: LocalParticipant;
remoteParticipants: RemoteParticipant[];
Expand Down Expand Up @@ -103,6 +114,7 @@ type WaitingParticipantLeftEvent = {

interface RoomEventsMap {
chat_message: CustomEvent<ChatMessage>;
cloud_recording_started: CustomEvent<CloudRecordingState>;
participant_audio_enabled: CustomEvent<ParticipantAudioEnabledEvent>;
participant_joined: CustomEvent<ParticipantJoinedEvent>;
participant_left: CustomEvent<ParticipantLeftEvent>;
Expand All @@ -111,6 +123,7 @@ interface RoomEventsMap {
participant_video_enabled: CustomEvent<ParticipantVideoEnabledEvent>;
room_connection_status_changed: CustomEvent<RoomConnectionStatusChangedEvent>;
room_joined: CustomEvent<RoomJoinedEvent>;
streaming_started: CustomEvent<StreamingState>;
waiting_participant_joined: CustomEvent<WaitingParticipantJoinedEvent>;
waiting_participant_left: CustomEvent<WaitingParticipantLeftEvent>;
}
Expand Down Expand Up @@ -252,6 +265,8 @@ export default class RoomConnection extends TypedEventTarget {
this.signalSocket.on("knocker_left", this._handleKnockerLeft.bind(this));
this.signalSocket.on("room_joined", this._handleRoomJoined.bind(this));
this.signalSocket.on("room_knocked", this._handleRoomKnocked.bind(this));
this.signalSocket.on("cloud_recording_stopped", this._handleCloudRecordingStopped.bind(this));
this.signalSocket.on("streaming_stopped", this._handleStreamingStopped.bind(this));
this.signalSocket.on("disconnect", this._handleDisconnect.bind(this));
this.signalSocket.on("connect_error", this._handleDisconnect.bind(this));

Expand Down Expand Up @@ -303,7 +318,41 @@ export default class RoomConnection extends TypedEventTarget {
this.dispatchEvent(new CustomEvent("chat_message", { detail: message }));
}

private _handleCloudRecordingStarted({ client }: { client: SignalClient }) {
this.dispatchEvent(
new CustomEvent("cloud_recording_started", {
detail: {
status: "recording",
startedAt: client.startedCloudRecordingAt
? new Date(client.startedCloudRecordingAt).getTime()
: new Date().getTime(),
},
})
);
}

private _handleStreamingStarted() {
this.dispatchEvent(
new CustomEvent("streaming_started", {
detail: {
status: "streaming",
// We don't have the streaming start time stored on the
// server, so we use the current time instead. This gives
// an invalid timestamp for "Client B" if "Client A" has
// been streaming for a while before "Client B" joins.
startedAt: new Date().getTime(),
},
})
);
}

private _handleNewClient({ client }: NewClientEvent) {
if (client.role.roleName === "recorder") {
this._handleCloudRecordingStarted({ client });
}
if (client.role.roleName === "streamer") {
this._handleStreamingStarted();
}
if (NON_PERSON_ROLES.includes(client.role.roleName)) {
return;
}
Expand Down Expand Up @@ -421,8 +470,20 @@ export default class RoomConnection extends TypedEventTarget {
...localClient,
stream: this.localMedia.stream || undefined,
});

const recorderClient = clients.find((c) => c.role.roleName === "recorder");
if (recorderClient) {
this._handleCloudRecordingStarted({ client: recorderClient });
}

const streamerClient = clients.find((c) => c.role.roleName === "streamer");
if (streamerClient) {
this._handleStreamingStarted();
}

this.remoteParticipants = clients
.filter((c) => c.id !== selfId)
.filter((c) => !NON_PERSON_ROLES.includes(c.role.roleName))
.map((c) => new RemoteParticipant({ ...c, newJoiner: false }));

this.roomConnectionStatus = "connected";
Expand Down Expand Up @@ -470,6 +531,14 @@ export default class RoomConnection extends TypedEventTarget {
);
}

private _handleCloudRecordingStopped() {
this.dispatchEvent(new CustomEvent("cloud_recording_stopped"));
}

private _handleStreamingStopped() {
this.dispatchEvent(new CustomEvent("streaming_stopped"));
}

private _handleRtcEvent<K extends keyof RtcEvents>(eventName: K, data: RtcEvents[K]) {
if (eventName === "rtc_manager_created") {
return this._handleRtcManagerCreated(data as RtcManagerCreatedPayload);
Expand Down
86 changes: 83 additions & 3 deletions src/lib/react/useRoomConnection.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
import { useEffect, useReducer, useState } from "react";
import VideoView from "./VideoView";
import { LocalMediaRef } from "./useLocalMedia";
import RoomConnection, { ChatMessage, RoomConnectionOptions, RoomConnectionStatus } from "../RoomConnection";
import RoomConnection, {
ChatMessage,
CloudRecordingState,
RoomConnectionOptions,
RoomConnectionStatus,
StreamingState,
} from "../RoomConnection";
import { LocalParticipant, RemoteParticipant, WaitingParticipant } from "../RoomParticipant";

type RemoteParticipantState = Omit<RemoteParticipant, "updateStreamState">;

export interface RoomConnectionState {
chatMessages: ChatMessage[];
cloudRecording: CloudRecordingState;
isJoining: boolean;
joinError: unknown;
localParticipant?: LocalParticipant;
mostRecentChatMessage: ChatMessage | null;
roomConnectionStatus: RoomConnectionStatus;
remoteParticipants: RemoteParticipantState[];
roomConnectionStatus: RoomConnectionStatus;
streaming: StreamingState;
waitingParticipants: WaitingParticipant[];
}

const initialState: RoomConnectionState = {
chatMessages: [],
roomConnectionStatus: "",
cloudRecording: {
status: "",
startedAt: null,
},
isJoining: false,
joinError: null,
mostRecentChatMessage: null,
remoteParticipants: [],
roomConnectionStatus: "",
streaming: {
status: "",
startedAt: null,
},
waitingParticipants: [],
};

Expand All @@ -32,6 +48,13 @@ type RoomConnectionEvents =
type: "CHAT_MESSAGE";
payload: ChatMessage;
}
| {
type: "CLOUD_RECORDING_STARTED";
payload: CloudRecordingState;
}
| {
type: "CLOUD_RECORDING_STOPPED";
}
| {
type: "ROOM_JOINED";
payload: {
Expand Down Expand Up @@ -92,6 +115,13 @@ type RoomConnectionEvents =
displayName: string;
};
}
| {
type: "STREAMING_STARTED";
payload: StreamingState;
}
| {
type: "STREAMING_STOPPED";
}
| {
type: "WAITING_PARTICIPANT_JOINED";
payload: {
Expand Down Expand Up @@ -132,6 +162,22 @@ function reducer(state: RoomConnectionState, action: RoomConnectionEvents): Room
chatMessages: [...state.chatMessages, action.payload],
mostRecentChatMessage: action.payload,
};
case "CLOUD_RECORDING_STARTED":
return {
...state,
cloudRecording: {
status: action.payload.status,
startedAt: action.payload.startedAt,
},
};
case "CLOUD_RECORDING_STOPPED":
return {
...state,
cloudRecording: {
status: "",
startedAt: null,
},
};
case "ROOM_JOINED":
return {
...state,
Expand Down Expand Up @@ -191,6 +237,22 @@ function reducer(state: RoomConnectionState, action: RoomConnectionEvents): Room
...state,
localParticipant: { ...state.localParticipant, displayName: action.payload.displayName },
};
case "STREAMING_STARTED":
return {
...state,
streaming: {
status: action.payload.status,
startedAt: action.payload.startedAt,
},
};
case "STREAMING_STOPPED":
return {
...state,
streaming: {
status: "",
startedAt: null,
},
};
case "WAITING_PARTICIPANT_JOINED":
return {
...state,
Expand Down Expand Up @@ -253,6 +315,15 @@ export default function useRoomConnection(
dispatch({ type: "CHAT_MESSAGE", payload: chatMessage });
});

roomConnection.addEventListener("cloud_recording_started", (e) => {
const { status, startedAt } = e.detail;
dispatch({ type: "CLOUD_RECORDING_STARTED", payload: { status, startedAt } });
});

roomConnection.addEventListener("cloud_recording_stopped", () => {
dispatch({ type: "CLOUD_RECORDING_STOPPED" });
});

roomConnection.addEventListener("participant_audio_enabled", (e) => {
const { participantId, isAudioEnabled } = e.detail;
dispatch({ type: "PARTICIPANT_AUDIO_ENABLED", payload: { participantId, isAudioEnabled } });
Expand Down Expand Up @@ -293,6 +364,15 @@ export default function useRoomConnection(
dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
});

roomConnection.addEventListener("streaming_started", (e) => {
const { status, startedAt } = e.detail;
dispatch({ type: "STREAMING_STARTED", payload: { status, startedAt } });
});

roomConnection.addEventListener("streaming_stopped", () => {
dispatch({ type: "STREAMING_STOPPED" });
});

roomConnection.addEventListener("waiting_participant_joined", (e) => {
const { participantId, displayName } = e.detail;
dispatch({ type: "WAITING_PARTICIPANT_JOINED", payload: { participantId, displayName } });
Expand Down
4 changes: 4 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ declare module "@whereby/jslib-media/src/utils/ServerSocket" {
isAudioEnabled: boolean;
isVideoEnabled: boolean;
role: ClientRole;
startedCloudRecordingAt: string | null;
}

interface AudioEnabledEvent {
Expand Down Expand Up @@ -191,6 +192,8 @@ declare module "@whereby/jslib-media/src/utils/ServerSocket" {
chat_message: ChatMessage;
client_left: ClientLeftEvent;
client_metadata_received: ClientMetadataReceivedEvent;
cloud_recording_stopped: void;
chat_message: ChatMessage;
connect: void;
connect_error: void;
device_identified: void;
Expand All @@ -201,6 +204,7 @@ declare module "@whereby/jslib-media/src/utils/ServerSocket" {
room_joined: RoomJoinedEvent;
room_knocked: RoomKnockedEvent;
room_left: void;
streaming_stopped: void;
video_enabled: VideoEnabledEvent;
}

Expand Down

0 comments on commit 6452085

Please sign in to comment.