rendering web app calling tracks

This commit is contained in:
balzack 2025-02-01 22:36:45 -08:00
parent 8a5e634479
commit 666f2cbab0
3 changed files with 75 additions and 49 deletions

View File

@ -37,13 +37,13 @@
.calling { .calling {
height: 100%; height: 100%;
width: 100%; width: 100%;
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
display: flex;
flex-direction: column;
.title { .title {
flex: 1; position: absolute;
top: 32px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -54,17 +54,17 @@
} }
.logo { .logo {
flex: 4; position: absolute;
} max-width: 50%;
height: 50%;
.control { aspect-ratio: 1;
flex: 2; top: 92px;
display: flex; border-radius: 16px;
align-items: center;
justify-content: center;
} }
.buttons { .buttons {
position: absolute;
bottom: 64px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 16px; padding: 16px;
@ -74,7 +74,8 @@
} }
.status { .status {
flex: 1; position: absolute;
bottom: 32px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import classes from './Calling.module.css' import classes from './Calling.module.css'
import { useCalling } from './useCalling.hook'; import { useCalling } from './useCalling.hook';
import { Card } from '../card/Card'; import { Card } from '../card/Card';
@ -15,6 +15,8 @@ export function Calling({ callCard }: { callCard: string }) {
const [ignoring, setIgnoring] = useState(false); const [ignoring, setIgnoring] = useState(false);
const [declining, setDeclining] = useState(false); const [declining, setDeclining] = useState(false);
const [accepting, setAccepting] = useState(false); const [accepting, setAccepting] = useState(false);
const remote = useRef();
const local = useRef();
const { state, actions } = useCalling(); const { state, actions } = useCalling();
const showError = () => { const showError = () => {
@ -148,6 +150,22 @@ export function Calling({ callCard }: { callCard: string }) {
} }
}, [callCard]); }, [callCard]);
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]);
const calls = state.calls.map((contact, index) => { const calls = state.calls.map((contact, index) => {
const { callId, card } = contact; const { callId, card } = contact;
const { name, handle, node, imageUrl } = card; const { name, handle, node, imageUrl } = card;
@ -164,28 +182,39 @@ export function Calling({ callCard }: { callCard: string }) {
return ( return (
<div className={state.calls.length > 0 || connecting || state.calling ? classes.active : classes.inactive}> <div className={state.calls.length > 0 || connecting || state.calling ? classes.active : classes.inactive}>
{ !state.calling && !connecting && state.calls.length > 0 && ( <div>
<div> { !state.calling && !connecting && state.calls.length > 0 && (
{ calls } <div>
</div> { calls }
)}
{ !state.calling && connecting && (
<Loader size={48} />
)}
{ state.calling && (
<div className={classes.calling}>
<div className={classes.title}>
{ state.calling.name && (
<Text className={classes.name}>{ state.calling.name }</Text>
)}
{ !state.calling.name && (
<Text className={classes.name}>{ `${state.calling.handle}/${state.calling.node}` }</Text>
)}
</div> </div>
<div className={classes.logo}> )}
<Image radius="sm" height="100%" width="auto" fit="container" src={state.calling.imageUrl} /> { !state.calling && connecting && (
</div> <Loader size={48} />
<div className={classes.control}> )}
{ state.calling && (
<div className={classes.calling}>
<div className={classes.title}>
{ state.calling.name && (
<Text className={classes.name}>{ state.calling.name }</Text>
)}
{ !state.calling.name && (
<Text className={classes.name}>{ `${state.calling.handle}/${state.calling.node}` }</Text>
)}
</div>
<div className={classes.logo}>
<Image radius="lg" height="100%" width="auto" fit="contain" src={state.calling.imageUrl} />
</div>
<div className={classes.status}>
{ !state.connected && (
<Text className={classes.label}>{ state.strings.connecting }</Text>
)}
</div>
<div style={{ position: 'absolute', width: '100%', height: '100%', top: 0, left: 0, backgroundColor: '#888888', display: state.remoteVideo ? 'block' : 'none' }}>
<video ref={remote} disablePictureInPicture playsInline autoPlay style={{ width: '100%', height: '100%' }} />
</div>
<div style={{ position: 'absolute', width: state.remoteVideo ? '20%' : '100%', height: state.remoteVideo ? '20%' : '100%', top: 0, left: 0, backgroundColor: '#888888', display: state.localVideo ? 'block' : 'none' }}>
<video ref={local} disablePictureInPicture playsInline autoPlay style={{ width: '100%', height: '100%' }} />
</div>
<div className={classes.buttons}> <div className={classes.buttons}>
<ActionIcon onClick={toggleAudio} disabled={!state.connected} loading={applyingAudio} color={Colors.primary} size="xl"> <ActionIcon onClick={toggleAudio} disabled={!state.connected} loading={applyingAudio} color={Colors.primary} size="xl">
{ state.audioEnabled && ( { state.audioEnabled && (
@ -206,13 +235,8 @@ export function Calling({ callCard }: { callCard: string }) {
<ActionIcon onClick={end} color={Colors.offsync} size="xl"><IconPhone className={classes.off} /></ActionIcon> <ActionIcon onClick={end} color={Colors.offsync} size="xl"><IconPhone className={classes.off} /></ActionIcon>
</div> </div>
</div> </div>
<div className={classes.status}> )}
{ !state.connected && ( </div>
<Text className={classes.label}>{ state.strings.connecting }</Text>
)}
</div>
</div>
)}
</div> </div>
) )
} }

View File

@ -81,15 +81,15 @@ export function useCalling() {
const audioStream = await getAudioStream(null); const audioStream = await getAudioStream(null);
const audioTrack = audioStream.getTracks().find((track: MediaStreamTrack) => track.kind === 'audio'); const audioTrack = audioStream.getTracks().find((track: MediaStreamTrack) => track.kind === 'audio');
if (audioTrack) { if (audioTrack) {
localAudio.current = null; localAudio.current = audioTrack;
} }
localVideo.current = null; localVideo.current = null;
localStream.current = new MediaStream(); localStream.current = null;
remoteStream.current = new MediaStream(); remoteStream.current = new MediaStream();
if (localAudio.current) { if (localAudio.current) {
localAudio.current.enabled = true; localAudio.current.enabled = true;
await updatePeer('local_track', localAudio.current); await updatePeer('local_track', { track: audioTrack, stream: audioStream });
} }
updateState({ localStream: localStream.current, remoteStream: remoteStream.current, updateState({ localStream: localStream.current, remoteStream: remoteStream.current,
audioEnabled: true, videoEnabled: false, localVideo: false, remoteVideo: false, connected: true }); audioEnabled: true, videoEnabled: false, localVideo: false, remoteVideo: false, connected: true });
@ -157,11 +157,11 @@ export function useCalling() {
} }
} }
const peerTrack = async (track: MediaStreamTrack) => { const peerTrack = async (track: MediaStreamTrack, stream: MediaStream) => {
if (call.current && localStream.current) { if (call.current && localStream.current) {
try { try {
const { peer } = call.current; const { peer } = call.current;
peer.addTrack(track, localStream.current); peer.addTrack(track, stream);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -189,7 +189,7 @@ export function useCalling() {
} }
} }
} else if (type === 'local_track') { } else if (type === 'local_track') {
await peerTrack(data); await peerTrack(data.track, data.stream);
} else if (type === 'close' && call.current) { } else if (type === 'close' && call.current) {
peerUpdate.current = []; peerUpdate.current = [];
const { peer, link } = call.current; const { peer, link } = call.current;
@ -339,8 +339,9 @@ export function useCalling() {
const videoTrack = videoStream.getTracks().find((track: MediaStreamTrack) => track.kind === 'video'); const videoTrack = videoStream.getTracks().find((track: MediaStreamTrack) => track.kind === 'video');
if (videoTrack) { if (videoTrack) {
localVideo.current = videoTrack; localVideo.current = videoTrack;
updatePeer('local_track', videoTrack); localStream.current = videoStream;
updateState({ localVideo: true }); updatePeer('local_track', { track: videoTrack, stream: videoStream });
updateState({ localVideo: true, localStream: videoStream });
} }
} }
if (localVideo.current) { if (localVideo.current) {