diff --git a/frontend/src/routes/stream/Stream.svelte b/frontend/src/routes/stream/Stream.svelte index bf1560d..311fa95 100644 --- a/frontend/src/routes/stream/Stream.svelte +++ b/frontend/src/routes/stream/Stream.svelte @@ -12,13 +12,19 @@ let loading = $state(true); let fullscreen = $state(false); let gameplayView: HTMLDivElement; + let gameplayCanvas: HTMLCanvasElement; async function startStream() { console.log(`Connecting to stream at ${url} with cert_hash ${certHash}`); - await streamUrl(url, certHash); + await streamUrl(url, certHash, gameplayCanvas); } async function requestFullscreen() { + // Update fullscreen var if fullscreen was exited outside our control + if (document.fullscreenElement == null) { + fullscreen = false; + } + if (fullscreen) { await document.exitFullscreen(); fullscreen = false; @@ -36,7 +42,7 @@
- +
diff --git a/frontend/src/routes/stream/stream.ts b/frontend/src/routes/stream/stream.ts index d447fcc..7423171 100644 --- a/frontend/src/routes/stream/stream.ts +++ b/frontend/src/routes/stream/stream.ts @@ -26,92 +26,89 @@ type DecodeUnitPacket = { DecodeUnit: DecodeUnit } +async function connectToUrl(url: string, certHash: Uint8Array): Promise { + console.log('Hash: ', certHash); + console.log(`Connecting to stream`); + // Check if WebTransport is supported + if (!window.WebTransport) { + throw new Error('WebTransport is not supported in this browser'); + } -export async function streamUrl(url: string, cert_hash: Array) { - const buffer = new Uint8Array(cert_hash); - console.log('Hash: ', buffer); - try { - console.log(`Connecting to stream`); - - // Check if WebTransport is supported - if (!window.WebTransport) { - throw new Error('WebTransport is not supported in this browser'); - } - - //const url = new URL(); - const transport = new WebTransport(url, { - serverCertificateHashes: [ - { - algorithm: "sha-256", - value: buffer, - } - ] - }); - - console.log('Connecting to WebTransport at ', url); - // Wait for the connection to be ready - await transport.ready; - console.log('WebTransport connection established'); - - - console.log('Creating WebTransport bidirectional stream'); - const stream = await transport.createBidirectionalStream(); - console.log('Bidirectional stream created'); - - const reader = stream.readable.getReader(); - - function parseData(newBuffer: Uint8Array, oldBuffer: Uint8Array): [Array, Uint8Array] { - let packets = new Array(); - let unparsedData = new Uint8Array(); - - let data = new Uint8Array([...oldBuffer, ...newBuffer]); - let index = 0; - while (true) { - if (index >= data.length) { - break - } - const view = new DataView(data.buffer.slice(index, index + 4)); - const dataLength = view.getUint32(0, true); - - const slice_start_index = index + 4; - const slice_end_index = index + 4 + dataLength; - - if (data.length < slice_end_index) { - unparsedData = new Uint8Array(data.buffer.slice(index, data.length)); - break; - } - - const dataToParse = data.buffer.slice(slice_start_index, slice_end_index); - const decoder = new TextDecoder('utf-8'); - const jsonString = decoder.decode(dataToParse); - - packets.push(JSON.parse(jsonString)); - - index += 4 + dataLength; + const transport = new WebTransport(url, { + serverCertificateHashes: [ + { + algorithm: "sha-256", + value: certHash, } - return [packets, unparsedData]; + ] + }); + + console.log('Connecting to WebTransport at ', url); + // Wait for the connection to be ready + await transport.ready; + console.log('WebTransport connection established'); + + return transport; +} + +function parseData(newBuffer: Uint8Array, oldBuffer: Uint8Array): [Array, Uint8Array] { + let packets = new Array(); + let unparsedData = new Uint8Array(); + + let data = new Uint8Array([...oldBuffer, ...newBuffer]); + let index = 0; + while (true) { + if (index >= data.length) { + break + } + const view = new DataView(data.buffer.slice(index, index + 4)); + const dataLength = view.getUint32(0, true); + + const slice_start_index = index + 4; + const slice_end_index = index + 4 + dataLength; + + if (data.length < slice_end_index) { + unparsedData = new Uint8Array(data.buffer.slice(index, data.length)); + break; } + const dataToParse = data.buffer.slice(slice_start_index, slice_end_index); + const decoder = new TextDecoder('utf-8'); + const jsonString = decoder.decode(dataToParse); + + packets.push(JSON.parse(jsonString)); + + index += 4 + dataLength; + } + return [packets, unparsedData]; +} + +export async function streamUrl(url: string, certHash: Array, canvasElement: HTMLCanvasElement) { + const certHashArray = new Uint8Array(certHash); + + const transport = await connectToUrl(url, certHashArray); + console.log('Creating WebTransport bidirectional stream'); + const stream = await transport.createBidirectionalStream(); + console.log('Bidirectional stream created'); + + const canvasCtx: CanvasRenderingContext2D | null = canvasElement.getContext('2d'); + if (canvasCtx == null) { + throw new Error(`Could not get 2d canvas context`); + } + + const reader = stream.readable.getReader(); + try { let unparsedData = new Uint8Array(); - const canvas: HTMLCanvasElement | null = document.getElementById('gamestream-canvas'); - if (canvas == null) { - throw new Error(`Could not find canvas`); - } - const ctx: CanvasRenderingContext2D | null = canvas.getContext('2d'); - if (ctx == null) { - throw new Error(`Could not get 2d canvas context`); - } - const videoDecoder = new VideoDecoder({ output: (frame) => { // Set canvas dimensions to match the frame - canvas.width = frame.displayWidth; - canvas.height = frame.displayHeight; + canvasElement.width = frame.displayWidth; + canvasElement.height = frame.displayHeight; // Draw the decoded frame to canvas - ctx.drawImage(frame, 0, 0); + canvasCtx.drawImage(frame, 0, 0); // Important: close the frame to free memory frame.close(); @@ -174,9 +171,6 @@ export async function streamUrl(url: string, cert_hash: Array) { } - - - // Handle connection close transport.closed.then(() => { console.log('WebTransport connection closed'); @@ -187,7 +181,8 @@ export async function streamUrl(url: string, cert_hash: Array) { // You can add more WebTransport handling logic here // For example, handling incoming streams, sending data, etc. - } catch (error) { + } catch (e) { + var error = e; console.error('Error connecting to stream:', error); alert('Failed to connect to stream: ' + error.message); }