diff --git a/examples/demo/demo.ts b/examples/demo/demo.ts index 9d1bcb5988..b3b7cf7172 100644 --- a/examples/demo/demo.ts +++ b/examples/demo/demo.ts @@ -996,6 +996,10 @@ function handleRoomDisconnect(reason?: DisconnectReason) { if (container) { container.innerHTML = ''; } + const selfView = $('self-view-area'); + if (selfView) { + selfView.innerHTML = ''; + } // clear the chat area on disconnect const chat = $('chat'); @@ -1026,7 +1030,7 @@ function appendLog(...args: any[]) { // updates participant UI function renderParticipant(participant: Participant, remove: boolean = false) { - const container = getParticipantsAreaElement(); + const container = getParticipantContainer(participant); if (!container) return; const { identity } = participant; let div = container.querySelector(`#participant-${identity}`); @@ -1236,9 +1240,9 @@ function renderBitrate() { } const participants: Participant[] = [...currentRoom.remoteParticipants.values()]; participants.push(currentRoom.localParticipant); - const container = getParticipantsAreaElement(); for (const p of participants) { + const container = getParticipantContainer(p); const elm = container.querySelector(`#bitrate-${p.identity}`); let totalBitrate = 0; for (const t of p.trackPublications.values()) { @@ -1755,6 +1759,15 @@ function getParticipantsAreaElement(): HTMLElement { ); } +// the local participant's camera is rendered in its own floating box instead of +// the regular participants grid +function getParticipantContainer(participant: Participant): HTMLElement { + if (isLocalParticipant(participant)) { + return $('self-view-area') ?? getParticipantsAreaElement(); + } + return getParticipantsAreaElement(); +} + function updateVideoSize(element: HTMLVideoElement, target: Element) { target.innerHTML = `(${element.videoWidth}x${element.videoHeight})`; } diff --git a/examples/demo/index.html b/examples/demo/index.html index 83f04068d2..77fc71e1b3 100644 --- a/examples/demo/index.html +++ b/examples/demo/index.html @@ -299,6 +299,8 @@

Livekit Sample App

+
+
diff --git a/examples/demo/styles.css b/examples/demo/styles.css index cd2ea7f551..1cf0341351 100644 --- a/examples/demo/styles.css +++ b/examples/demo/styles.css @@ -53,21 +53,46 @@ } #participants-area { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 10; + display: flex; + flex-direction: row; + flex-wrap: nowrap; gap: 20px; + width: 100%; + padding: 10px; + background-color: rgba(0, 0, 0, 0.6); + overflow-x: auto; } #participants-area > .participant { - width: 100%; + flex: 1 1 0; + min-width: 240px; + aspect-ratio: 16 / 9; +} + +#self-view-area { + position: fixed; + right: 20px; + bottom: 20px; + z-index: 20; + width: 280px; + aspect-ratio: 16 / 9; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); +} + +#self-view-area:empty { + display: none; } -#participants-area > .participant::before { - content: ''; - display: inline-block; - width: 1px; - height: 0; - padding-bottom: calc(100% / (16 / 9)); +#self-view-area > .participant { + width: 100%; + height: 100%; } #log-area { diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index 1444f18e00..ac7b615d36 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -1221,7 +1221,7 @@ export default class LocalParticipant extends Participant { this.emit(ParticipantEvent.LocalSenderCreated, track.sender, track); if (isLocalVideoTrack(track)) { - opts.degradationPreference ??= getDefaultDegradationPreference(track); + opts.degradationPreference = 'maintain-resolution'; track.setDegradationPreference(opts.degradationPreference); }