fciannella's picture
Added the healthcare example
2f49513
raw
history blame
5.71 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebRTC Voice Agent</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; }
#status { font-size: 20px; margin: 20px; }
button { padding: 10px 20px; font-size: 16px; }
</style>
</head>
<body>
<h1>WebRTC Voice Agent</h1>
<p id="status">Disconnected</p>
<button id="connect-btn">Connect</button>
<audio id="audio-el" autoplay></audio>
<script>
const statusEl = document.getElementById("status")
const buttonEl = document.getElementById("connect-btn")
const audioEl = document.getElementById("audio-el")
let connected = false
let peerConnection = null
const waitForIceGatheringComplete = async (pc, timeoutMs = 2000) => {
if (pc.iceGatheringState === 'complete') return;
console.log("Waiting for ICE gathering to complete. Current state:", pc.iceGatheringState);
return new Promise((resolve) => {
let timeoutId;
const checkState = () => {
console.log("icegatheringstatechange:", pc.iceGatheringState);
if (pc.iceGatheringState === 'complete') {
cleanup();
resolve();
}
};
const onTimeout = () => {
console.warn(`ICE gathering timed out after ${timeoutMs} ms.`);
cleanup();
resolve();
};
const cleanup = () => {
pc.removeEventListener('icegatheringstatechange', checkState);
clearTimeout(timeoutId);
};
pc.addEventListener('icegatheringstatechange', checkState);
timeoutId = setTimeout(onTimeout, timeoutMs);
// Checking the state again to avoid any eventual race condition
checkState();
});
};
const createSmallWebRTCConnection = async (audioTrack) => {
const config = {
iceServers: [],
};
const pc = new RTCPeerConnection(config)
addPeerConnectionEventListeners(pc)
pc.ontrack = e => audioEl.srcObject = e.streams[0]
// SmallWebRTCTransport expects to receive both transceivers
pc.addTransceiver(audioTrack, { direction: 'sendrecv' })
pc.addTransceiver('video', { direction: 'sendrecv' })
await pc.setLocalDescription(await pc.createOffer())
await waitForIceGatheringComplete(pc)
const offer = pc.localDescription
try {
const response = await fetch('/api/offer', {
body: JSON.stringify({ sdp: offer.sdp, type: offer.type}),
headers: { 'Content-Type': 'application/json' },
method: 'POST',
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const answer = await response.json()
await pc.setRemoteDescription(answer)
} catch (error) {
console.error('Error during WebRTC connection setup:', error);
_onDisconnected();
throw error;
}
return pc
}
const connect = async () => {
_onConnecting()
const audioStream = await navigator.mediaDevices.getUserMedia({audio: true})
peerConnection= await createSmallWebRTCConnection(audioStream.getAudioTracks()[0])
}
const addPeerConnectionEventListeners = (pc) => {
pc.oniceconnectionstatechange = () => {
console.log("oniceconnectionstatechange", pc?.iceConnectionState)
}
pc.onconnectionstatechange = () => {
console.log("onconnectionstatechange", pc?.connectionState)
let connectionState = pc?.connectionState
if (connectionState === 'connected') {
_onConnected()
} else if (connectionState === 'disconnected') {
_onDisconnected()
}
}
pc.onicecandidate = (event) => {
if (event.candidate) {
console.log("New ICE candidate:", event.candidate);
} else {
console.log("All ICE candidates have been sent.");
}
};
}
const _onConnecting = () => {
statusEl.textContent = "Connecting"
buttonEl.textContent = "Disconnect"
connected = true
}
const _onConnected = () => {
statusEl.textContent = "Connected"
buttonEl.textContent = "Disconnect"
connected = true
}
const _onDisconnected = () => {
statusEl.textContent = "Disconnected"
buttonEl.textContent = "Connect"
connected = false
}
const disconnect = () => {
if (!peerConnection) {
return
}
peerConnection.close()
peerConnection = null
_onDisconnected()
}
buttonEl.addEventListener("click", async () => {
if (!connected) {
await connect()
} else {
disconnect()
}
});
</script>
</body>
</html>