make kestrel a tak server, so that it can send and receive pois as cots data
Some checks failed
ci/woodpecker/pr/pr Pipeline failed

This commit is contained in:
Madison Grubb
2026-02-17 10:42:53 -05:00
parent b18283d3b3
commit b0e8dd7ad9
96 changed files with 5767 additions and 500 deletions

View File

@@ -104,9 +104,9 @@ const hasStream = ref(false)
const error = ref('')
const connectionState = ref('') // '', 'connecting', 'connected', 'failed'
const failureReason = ref(null) // { wrongHost: { serverHostname, clientHostname } | null }
let device = null
let recvTransport = null
let consumer = null
const device = ref(null)
const recvTransport = ref(null)
const consumer = ref(null)
async function runFailureReasonCheck() {
failureReason.value = await getWebRTCFailureReason()
@@ -135,16 +135,16 @@ async function setupWebRTC() {
const rtpCapabilities = await $fetch(`/api/live/webrtc/router-rtp-capabilities?sessionId=${props.session.id}`, {
credentials: 'include',
})
device = await createMediasoupDevice(rtpCapabilities)
recvTransport = await createRecvTransport(device, props.session.id)
device.value = await createMediasoupDevice(rtpCapabilities)
recvTransport.value = await createRecvTransport(device.value, props.session.id)
recvTransport.on('connectionstatechange', () => {
const state = recvTransport.connectionState
recvTransport.value.on('connectionstatechange', () => {
const state = recvTransport.value.connectionState
if (state === 'connected') connectionState.value = 'connected'
else if (state === 'failed' || state === 'disconnected' || state === 'closed') {
logWarn('LiveSessionPanel: Receive transport connection state changed', {
state,
transportId: recvTransport.id,
transportId: recvTransport.value.id,
sessionId: props.session.id,
})
if (state === 'failed') {
@@ -154,8 +154,8 @@ async function setupWebRTC() {
}
})
const connectionPromise = waitForConnectionState(recvTransport, 10000)
consumer = await consumeProducer(recvTransport, device, props.session.id)
const connectionPromise = waitForConnectionState(recvTransport.value, 10000)
consumer.value = await consumeProducer(recvTransport.value, device.value, props.session.id)
const finalConnectionState = await connectionPromise
if (finalConnectionState !== 'connected') {
@@ -163,8 +163,8 @@ async function setupWebRTC() {
runFailureReasonCheck()
logWarn('LiveSessionPanel: Transport not fully connected', {
state: finalConnectionState,
transportId: recvTransport.id,
consumerId: consumer.id,
transportId: recvTransport.value.id,
consumerId: consumer.value.id,
})
}
else {
@@ -182,14 +182,14 @@ async function setupWebRTC() {
attempts++
}
if (!consumer.track) {
if (!consumer.value.track) {
logError('LiveSessionPanel: No video track available', {
consumerId: consumer.id,
consumerKind: consumer.kind,
consumerPaused: consumer.paused,
consumerClosed: consumer.closed,
consumerProducerId: consumer.producerId,
transportConnectionState: recvTransport?.connectionState,
consumerId: consumer.value.id,
consumerKind: consumer.value.kind,
consumerPaused: consumer.value.paused,
consumerClosed: consumer.value.closed,
consumerProducerId: consumer.value.producerId,
transportConnectionState: recvTransport.value?.connectionState,
})
error.value = 'No video track available - consumer may not be receiving data from producer'
return
@@ -197,14 +197,14 @@ async function setupWebRTC() {
if (!videoRef.value) {
logError('LiveSessionPanel: Video ref not available', {
consumerId: consumer.id,
hasTrack: !!consumer.track,
consumerId: consumer.value.id,
hasTrack: !!consumer.value.track,
})
error.value = 'Video element not available'
return
}
const stream = new MediaStream([consumer.track])
const stream = new MediaStream([consumer.value.track])
videoRef.value.srcObject = stream
hasStream.value = true
@@ -227,7 +227,7 @@ async function setupWebRTC() {
if (resolved) return
resolved = true
videoRef.value.removeEventListener('loadedmetadata', handler)
logWarn('LiveSessionPanel: Video metadata timeout', { consumerId: consumer.id })
logWarn('LiveSessionPanel: Video metadata timeout', { consumerId: consumer.value.id })
resolve()
}, 5000)
})
@@ -239,7 +239,7 @@ async function setupWebRTC() {
}
catch (playErr) {
logWarn('LiveSessionPanel: Video play() failed (may need user interaction)', {
consumerId: consumer.id,
consumerId: consumer.value.id,
error: playErr.message || String(playErr),
errorName: playErr.name,
videoPaused: videoRef.value.paused,
@@ -248,7 +248,7 @@ async function setupWebRTC() {
// Don't set error - video might still work, just needs user interaction
}
consumer.track.addEventListener('ended', () => {
consumer.value.track.addEventListener('ended', () => {
error.value = 'Video track ended'
hasStream.value = false
})
@@ -274,15 +274,15 @@ async function setupWebRTC() {
}
function cleanup() {
if (consumer) {
consumer.close()
consumer = null
if (consumer.value) {
consumer.value.close()
consumer.value = null
}
if (recvTransport) {
recvTransport.close()
recvTransport = null
if (recvTransport.value) {
recvTransport.value.close()
recvTransport.value = null
}
device = null
device.value = null
if (videoRef.value) {
videoRef.value.srcObject = null
}
@@ -308,7 +308,7 @@ watch(
watch(
() => props.session?.hasStream,
(hasStream) => {
if (hasStream && props.session?.id && !device) {
if (hasStream && props.session?.id && !device.value) {
setupWebRTC()
}
else if (!hasStream) {