Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

Add audio/video controls to device request page #58

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 20 additions & 9 deletions www/atoms/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export type Local =
| {
status: "requestingDevices";
name: string;
audioEnabled: boolean;
videoEnabled: boolean;
}
| {
status: "connecting";
Expand All @@ -42,22 +44,31 @@ export const localState = atom<Local>({
const setAudioVideoEnabled =
(audioEnabled: boolean, videoEnabled: boolean) =>
(local: Local): Local => {
assert(local.status === "connecting" || local.status === "connected");
assert(
local.status === "requestingDevices" ||
local.status === "connecting" ||
local.status === "connected"
);
return { ...local, audioEnabled, videoEnabled };
};

const setConnecting =
const setConnecting = (local: Local): Local => {
assert(local.status === "requestingDevices");
return { ...local, status: "connecting" };
};

const setRequestingDevices =
(audioEnabled: boolean, videoEnabled: boolean) =>
(local: Local): Local => {
assert(local.status === "requestingDevices");
return { ...local, status: "connecting", audioEnabled, videoEnabled };
assert(local.status === "requestingPermissions");
return {
...local,
status: "requestingDevices",
audioEnabled,
videoEnabled,
};
};

const setRequestingDevices = (local: Local): Local => {
assert(local.status === "requestingPermissions");
return { ...local, status: "requestingDevices" };
};

const setRequestingPermissions =
(name: string) =>
(local: Local): Local => {
Expand Down
113 changes: 56 additions & 57 deletions www/components/room/controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,94 +36,93 @@ function Control(props: ControlProps) {
);
}

export default function Controls() {
const router = useRouter();
function TrackControl(track: "video" | "audio") {
const [local, setLocal] = useRecoilState(localState);
const peers = useRecoilValue(peersState);
const stream = mapGet(streamMap, LocalStreamKey);

assert(local.status === "connecting" || local.status === "connected");
assert(
local.status === "requestingDevices" ||
local.status === "connecting" ||
local.status === "connected"
);

const { audioEnabled, videoEnabled } = getVideoAudioEnabled(stream);
const enabled = track === "audio" ? audioEnabled : videoEnabled;

const handleLeave = React.useCallback(() => {
router.push(
`/goodbye?left=${router.query.roomCode}/${router.query.roomName}`,
"/goodbye"
);
}, [router]);

const handleToggleAudio = React.useCallback(() => {
const handleToggle = React.useCallback(() => {
peers.forEach((peer) => {
const channel = rtcDataChannelMap.get(peer.sid);

if (channel !== undefined) {
sendMessage(channel, {
type: "peer-state",
name: local.name,
audioEnabled: !audioEnabled,
videoEnabled,
audioEnabled: track === "audio" ? !audioEnabled : audioEnabled,
videoEnabled: track === "video" ? !videoEnabled : videoEnabled,
});
}
});

const audioTracks = stream?.getAudioTracks();
const tracks =
track === "audio" ? stream?.getAudioTracks() : stream?.getVideoTracks();

if (audioTracks !== undefined && audioTracks.length > 0) {
audioTracks[0].enabled = !audioEnabled;
if (tracks !== undefined && tracks.length > 0) {
tracks[0].enabled = !enabled;
}

setLocal(localActions.setAudioVideoEnabled(!audioEnabled, videoEnabled));
setLocal(
localActions.setAudioVideoEnabled(
track === "audio" ? !audioEnabled : audioEnabled,
track === "video" ? !videoEnabled : videoEnabled
)
);
}, [audioEnabled, local.name, peers, setLocal, stream, videoEnabled]);

const handleToggleVideo = React.useCallback(() => {
peers.forEach((peer) => {
const channel = rtcDataChannelMap.get(peer.sid);
const iconClassName = classNames("absolute", {
"text-slate-800": !enabled,
});

if (channel !== undefined) {
sendMessage(channel, {
type: "peer-state",
name: local.name,
audioEnabled,
videoEnabled: !videoEnabled,
});
}
});
return (
<Control disabled={!enabled} text={track === "audio" ? "Mic" : "Cam"}>
<Button
color={enabled ? "slate" : "red"}
icon={
track === "audio" ? (
<MicrophoneIcon width={24} className={iconClassName} />
) : (
<VideoCameraIcon width={24} className={iconClassName} />
)
}
onClick={handleToggle}
square
/>
</Control>
);
}

const videoTracks = stream?.getVideoTracks();
export function AudioControl() {
return TrackControl("audio");
}

if (videoTracks !== undefined && videoTracks.length > 0) {
videoTracks[0].enabled = !videoEnabled;
}
export function VideoControl() {
return TrackControl("video");
}

setLocal(localActions.setAudioVideoEnabled(audioEnabled, !videoEnabled));
}, [audioEnabled, local.name, peers, setLocal, stream, videoEnabled]);
export default function Controls() {
const router = useRouter();

const videoIconClassName = classNames("absolute", {
"text-slate-800": !videoEnabled,
});
const audioIconClassName = classNames("absolute", {
"text-slate-800": !audioEnabled,
});
const handleLeave = React.useCallback(() => {
router.push(
`/goodbye?left=${router.query.roomCode}/${router.query.roomName}`,
"/goodbye"
);
}, [router]);

return (
<div className="flex items-center justify-center m-2 sm:m-4 space-x-8">
<Control disabled={!audioEnabled} text="Mic">
<Button
color={audioEnabled ? "slate" : "red"}
icon={<MicrophoneIcon width={24} className={audioIconClassName} />}
onClick={handleToggleAudio}
square
/>
</Control>
<Control disabled={!videoEnabled} text="Cam">
<Button
color={videoEnabled ? "slate" : "red"}
icon={<VideoCameraIcon width={24} className={videoIconClassName} />}
onClick={handleToggleVideo}
square
/>
</Control>
<AudioControl />
<VideoControl />
<Control text="Leave">
<Button
color="slate"
Expand Down
7 changes: 4 additions & 3 deletions www/components/room/request-devices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
stopStream,
} from "../../lib/mesh/stream";
import Select from "../lib/select";
import { AudioControl, VideoControl } from "./controls";
import LocalPreview from "./local-preview";
import PreForm from "./pre-form";

Expand All @@ -25,9 +26,7 @@ export default function RequestDevices() {
}, []);

const joinRoom = React.useCallback(async () => {
const stream = mapGet(streamMap, LocalStreamKey);
const { audioEnabled, videoEnabled } = getVideoAudioEnabled(stream);
setLocal(localActions.setConnecting(audioEnabled, videoEnabled));
setLocal(localActions.setConnecting);
}, [setLocal]);

const handleDeviceChange = React.useCallback(
Expand Down Expand Up @@ -110,6 +109,7 @@ export default function RequestDevices() {
}
setValue={handleAudioChange}
/>
<AudioControl />
<Select
id="video-select"
fallback="No cameras found"
Expand All @@ -133,6 +133,7 @@ export default function RequestDevices() {
}
setValue={handleVideoChange}
/>
<VideoControl />
</>
}
handleSubmit={joinRoom}
Expand Down
5 changes: 3 additions & 2 deletions www/components/room/request-permission.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { useSetRecoilState } from "recoil";
import { localActions, localState, LocalStreamKey } from "../../atoms/local";
import { streamMap } from "../../lib/mesh/maps";
import { createLocalStream } from "../../lib/mesh/stream";
import { createLocalStream, getVideoAudioEnabled } from "../../lib/mesh/stream";
import PreForm from "./pre-form";

export default function RequestPermission() {
Expand All @@ -11,7 +11,8 @@ export default function RequestPermission() {
const requestPermissions = React.useCallback(async () => {
const stream = await createLocalStream();
streamMap.set(LocalStreamKey, stream);
setLocal(localActions.setRequestingDevices);
const { audioEnabled, videoEnabled } = getVideoAudioEnabled(stream);
setLocal(localActions.setRequestingDevices(audioEnabled, videoEnabled));
}, [setLocal]);

return (
Expand Down