fix webrtc video position

This commit is contained in:
balzack 2025-02-06 18:19:13 -08:00
parent 35f2c7d58d
commit 1b73422ab0
7 changed files with 8169 additions and 5439 deletions

View File

@ -45,10 +45,32 @@
padding-top: 16px;
}
}
.fullFrame {
position: absolute;
top: 0;
right: 0;
justify-content: center;
width: 100%;
height: 100%;
}
.boxFrame {
position: absolute;
top: 10%;
right: 5%;
width: 20%;
height: 20%;
}
.full {
width: 100%;
height: 100%;
}
.buttons {
background-color: var(--mantine-color-surface-2);
position: absolute;
bottom: 92px;
bottom: 10%;
opacity: 0.8;
padding: 16px;
border-radius: 8px;

View File

@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import { useCall } from './useCall.hook';
import classes from './Call.module.css'
import { Card as Contact } from '../card/Card';
import { Colors } from '../constants/Colors';
import { modals } from '@mantine/modals'
import { Image, Text, ActionIcon } from '@mantine/core'
import { IconPhone, IconMicrophone, IconMicrophoneOff, IconVideo, IconVideoOff } from '@tabler/icons-react'
import { IconPhone, IconMicrophone, IconMicrophoneOff, IconVideo, IconVideoOff, IconArrowsMinimize } from '@tabler/icons-react'
export function Call() {
const { state, actions } = useCall();
@ -15,6 +15,8 @@ export function Call() {
const [accepting, setAccepting] = useState(null as null|string);
const [ignoring, setIgnoring] = useState(null as null|string);
const [declining, setDeclining] = useState(null as null|string);
const remote = useRef(null as null|HTMLVideoElement);
const local = useRef(null as null|HTMLVideoElement);
const showError = () => {
modals.openConfirmModal({
@ -77,27 +79,53 @@ export function Call() {
}
}
useEffect(() => {
if (local.current) {
local.current.srcObject = state.localStream;
local.current.load();
local.current.play();
}
}, [state.localStream]);
useEffect(() => {
if (remote.current) {
remote.current.srcObject = state.remoteStream;
remote.current.load();
remote.current.play();
}
}, [state.remoteStream]);
return (
<div className={(state.calling && state.fullscreen) ? classes.active : classes.inactive}>
{ state.calling && (
<div className={classes.call}>
<div className={classes.titleView}>
{ state.calling.name && (
<Text className={classes.titleName}>{ state.calling.name }</Text>
)}
{ !state.calling.name && (
<Text className={classes.titleName}>{ `${state.calling.handle}/${state.calling.node}` }</Text>
)}
<div className={classes.image}>
<div className={classes.frame}>
<Image radius="lg" fit="contain" className={classes.logo} src={state.calling.imageUrl} />
{ !state.remoteVideo && !state.localVideo && (
<div className={classes.titleView}>
{ state.calling.name && (
<Text className={classes.titleName}>{ state.calling.name }</Text>
)}
{ !state.calling.name && (
<Text className={classes.titleName}>{ `${state.calling.handle}/${state.calling.node}` }</Text>
)}
<div className={classes.image}>
<div className={classes.frame}>
<Image radius="lg" fit="contain" className={classes.logo} src={state.calling.imageUrl} />
</div>
</div>
<Text className={classes.titleStatus}>{ `${Math.floor(state.duration/60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
</div>
<Text className={classes.titleStatus}>{ `${Math.floor(state.duration/60)}:${(state.duration % 60).toString().padStart(2, '0')}` }</Text>
</div>
)}
<div className={classes.fullFrame} style={{ display: state.remoteVideo ? 'flex' : 'none' }}>
<video ref={remote} disablePictureInPicture playsInline autoPlay className={classes.full} />
</div>
<div className={state.remoteVideo ? classes.boxFrame : classes.fulLFrame} style={{ display: state.localVideo ? 'flex' : 'none' }}>
<video ref={local} disablePictureInPicture playsInline autoPlay className={classes.full} />
</div>
<div className={classes.buttons}>
<ActionIcon onClick={()=>actions.setFullscreen(false)} color={Colors.confirmed} size="xl"><IconArrowsMinimize /></ActionIcon>
<ActionIcon onClick={toggleAudio} disabled={!state.connected} loading={applyingAudio} color={Colors.primary} size="xl">
{ state.audioEnabled && (
<IconMicrophone />

View File

@ -38,7 +38,7 @@ function Action({ icon, color, strings, select }: { icon: ReactNode; color: stri
)
}
export function Contacts({ openRegistry, openContact, textContact }: { openRegistry: ()=>void; openContact: (params: ProfileParams)=>void, textContact: (cardId: string)=>void }) {
export function Contacts({ openRegistry, openContact, textContact, closeContacts }: { openRegistry: ()=>void; openContact: (params: ProfileParams)=>void, textContact: (cardId: string)=>void, closeContacts: ()=>void }) {
const { state, actions } = useContacts()
const cards = state.filtered.map((card, idx) => {
@ -48,7 +48,7 @@ export function Contacts({ openRegistry, openContact, textContact }: { openRegis
const phone = <IconPhone size={24} />
const text = <IconMessage2 size={24} />
return [
<Action key="phone" icon={phone} color={Colors.connected} select={async () => actions.call(card)} strings={state.strings} />,
<Action key="phone" icon={phone} color={Colors.connected} select={async () => { await actions.call(card), closeContacts() }} strings={state.strings} />,
<Action key="text" icon={text} color={Colors.connected} select={async () => textContact(card.cardId)} strings={state.strings} />,
]
} else if (status === 'offsync') {

View File

@ -190,7 +190,6 @@ export function useRingContext() {
}
const cleanup = async () => {
console.log("CLEANUP!");
closing.current = true;
while (updatingPeer.current || connecting.current) {
await new Promise((r) => setTimeout(r, CLOSE_POLL_MS));
@ -214,7 +213,6 @@ console.log("CLEANUP!");
peerUpdate.current = [];
updateState({ calling: null, connected: false, connectedTime: 0, failed: false, localStream: null, remoteStream: null, localVideo: false, remoteVideo: false });
closing.current = false;
console.log("!!");
}
const transmit = (ice: { urls: string; username: string; credential: string }[]) => {

View File

@ -6,7 +6,7 @@ import { Card } from 'databag-client-sdk';
import { Colors } from '../constants/Colors';
import { modals } from '@mantine/modals'
import { Loader, Image, Text, ActionIcon } from '@mantine/core'
import { IconEyeX, IconPhone, IconPhoneOff, IconArrowsMaximize, IconMicrophone, IconMicrophoneOff } from '@tabler/icons-react'
import { IconVideoPlus, IconEyeX, IconPhone, IconPhoneOff, IconArrowsMaximize, IconMicrophone, IconMicrophoneOff } from '@tabler/icons-react'
export function Ring() {
const { state, actions } = useRing();
@ -134,7 +134,12 @@ export function Ring() {
)}
</ActionIcon>
<ActionIcon variant="subtle" disabled={!state.connected} className={classes.circleIcon} color={Colors.confirmed} onClick={()=>actions.setFullscreen(true)}>
<IconArrowsMaximize />
{ (state.localVideo || state.remoteVideo) && (
<IconVideoPlus />
)}
{ !state.localVideo && !state.remoteVideo && (
<IconArrowsMaximize />
)}
</ActionIcon>
<div className={classes.name}>
{ state.calling.name && (

View File

@ -32,7 +32,6 @@ export function Session() {
const [textCard, setTextCard] = useState({ cardId: null} as {cardId: null|string});
const textContact = (cardId: string) => {
console.log("MESSAGE: ", cardId);
setTextCard({ cardId });
closeContacts();
setTab('content');
@ -70,6 +69,7 @@ export function Session() {
<Contacts
textContact={textContact}
openRegistry={openRegistry}
closeContacts={()=>{}}
openContact={(params) => {
setProfileParams(params)
openProfile()
@ -149,6 +149,7 @@ export function Session() {
<Contacts
textContact={textContact}
openRegistry={openRegistry}
closeContacts={closeContacts}
openContact={(params) => {
setProfileParams(params)
openProfile()

File diff suppressed because it is too large Load Diff