Add remote video overlay

Fix remote audio not working when viewing another conversation.
Add a remote video overlay when in a call, but viewing a different page.

New components:
- VideoOverlay
- RemoteVideoOverlay: Video overlay for the remote stream when in a
  different page. Clicking it will redirect to the current call page.
- VideoStream: renders a video from a MediaStream and can
  output audio to the device with id `audioOutDeviceId`

Misc changes:
- Can click local video to make bigger

Change-Id: If1bb0b10c137944c405d540f041ebfe8005f9251
diff --git a/client/src/components/VideoStream.tsx b/client/src/components/VideoStream.tsx
new file mode 100644
index 0000000..4740e81
--- /dev/null
+++ b/client/src/components/VideoStream.tsx
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { forwardRef, useEffect, useRef, VideoHTMLAttributes } from 'react';
+
+import { VideoElementWithSinkId } from '../utils/utils';
+
+export type VideoStreamProps = Partial<VideoHTMLAttributes<VideoElementWithSinkId>> & {
+  stream: MediaStream | undefined;
+  audioOutDeviceId?: string;
+};
+
+const VideoStream = forwardRef<VideoElementWithSinkId, VideoStreamProps>(
+  ({ stream, audioOutDeviceId, ...props }, ref) => {
+    const videoRef = useRef<VideoElementWithSinkId | null>(null);
+
+    useEffect(() => {
+      if (!ref) {
+        return;
+      }
+
+      if (typeof ref === 'function') {
+        ref(videoRef.current);
+      } else {
+        ref.current = videoRef.current;
+      }
+    }, [ref]);
+
+    useEffect(() => {
+      if (stream && videoRef.current) {
+        videoRef.current.srcObject = stream;
+      }
+    }, [stream, videoRef]);
+
+    useEffect(() => {
+      if (!audioOutDeviceId) {
+        return;
+      }
+
+      if (videoRef.current?.setSinkId) {
+        // This only work on chrome and other browsers that support `setSinkId`
+        // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId#browser_compatibility
+        videoRef.current.setSinkId(audioOutDeviceId);
+      }
+    }, [audioOutDeviceId, videoRef]);
+
+    return <video ref={videoRef} autoPlay {...props} />;
+  }
+);
+export default VideoStream;