From e80543144ac992efd1977e919cbaf6de3603d2b5 Mon Sep 17 00:00:00 2001 From: restitux Date: Tue, 12 Aug 2025 02:20:46 -0600 Subject: [PATCH] backend and frontend: support out of order chunks + now it's performant on chrome --- Cargo.lock | 2 + frontend/src/lib/proto/video-update.ts | 3 +- .../proto/video-update/decode-unit-buffer.ts | 100 ++++++ .../proto/video-update/decode-unit-start.ts | 91 ++++++ frontend/src/lib/proto/video-update/update.ts | 20 +- frontend/src/lib/video.ts | 243 +++++++++----- frontend/src/routes/Cover.svelte | 2 + frontend/src/routes/getStreamData.ts | 43 +-- .../src/routes/stores/streamStore.svelte.ts | 2 + frontend/src/routes/stream/+page.svelte | 4 +- frontend/src/routes/stream/Stream.svelte | 14 +- frontend/src/routes/stream/stream.ts | 20 +- gamestream-webtransport-proxy/Cargo.toml | 2 + gamestream-webtransport-proxy/src/certs.rs | 32 +- .../src/gamestream/decoder.rs | 97 +++--- .../src/proxy/handler.rs | 17 +- .../src/proxy/mod.rs | 8 +- .../src/proxy/video/mod.rs | 109 +++++-- .../src/proxy/video/video_generated.rs | 300 ++++++++++++++---- gamestream-webtransport-proxy/src/stream.rs | 5 +- schema/video.fbs | 15 +- 21 files changed, 876 insertions(+), 253 deletions(-) create mode 100644 frontend/src/lib/proto/video-update/decode-unit-buffer.ts create mode 100644 frontend/src/lib/proto/video-update/decode-unit-start.ts diff --git a/Cargo.lock b/Cargo.lock index 0d206dc..df48e85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -680,6 +680,8 @@ dependencies = [ "directories", "flatbuffers", "getrandom 0.3.3", + "h3-datagram", + "h3-quinn", "hex", "hmac-sha256", "http", diff --git a/frontend/src/lib/proto/video-update.ts b/frontend/src/lib/proto/video-update.ts index 4a3fbba..8990853 100644 --- a/frontend/src/lib/proto/video-update.ts +++ b/frontend/src/lib/proto/video-update.ts @@ -2,7 +2,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ -export { DecodeUnit } from './video-update/decode-unit.js'; +export { DecodeUnitBuffer } from './video-update/decode-unit-buffer.js'; +export { DecodeUnitStart } from './video-update/decode-unit-start.js'; export { FrameType } from './video-update/frame-type.js'; export { Setup } from './video-update/setup.js'; export { Update } from './video-update/update.js'; diff --git a/frontend/src/lib/proto/video-update/decode-unit-buffer.ts b/frontend/src/lib/proto/video-update/decode-unit-buffer.ts new file mode 100644 index 0000000..bf0502a --- /dev/null +++ b/frontend/src/lib/proto/video-update/decode-unit-buffer.ts @@ -0,0 +1,100 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class DecodeUnitBuffer { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):DecodeUnitBuffer { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsDecodeUnitBuffer(bb:flatbuffers.ByteBuffer, obj?:DecodeUnitBuffer):DecodeUnitBuffer { + return (obj || new DecodeUnitBuffer()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsDecodeUnitBuffer(bb:flatbuffers.ByteBuffer, obj?:DecodeUnitBuffer):DecodeUnitBuffer { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new DecodeUnitBuffer()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +frameNumber():bigint { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0'); +} + +bufferIndex():bigint { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0'); +} + +bufferOffset():bigint { + const offset = this.bb!.__offset(this.bb_pos, 8); + return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0'); +} + +data(index: number):number|null { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? this.bb!.readUint8(this.bb!.__vector(this.bb_pos + offset) + index) : 0; +} + +dataLength():number { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; +} + +dataArray():Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? new Uint8Array(this.bb!.bytes().buffer, this.bb!.bytes().byteOffset + this.bb!.__vector(this.bb_pos + offset), this.bb!.__vector_len(this.bb_pos + offset)) : null; +} + +static startDecodeUnitBuffer(builder:flatbuffers.Builder) { + builder.startObject(4); +} + +static addFrameNumber(builder:flatbuffers.Builder, frameNumber:bigint) { + builder.addFieldInt64(0, frameNumber, BigInt('0')); +} + +static addBufferIndex(builder:flatbuffers.Builder, bufferIndex:bigint) { + builder.addFieldInt64(1, bufferIndex, BigInt('0')); +} + +static addBufferOffset(builder:flatbuffers.Builder, bufferOffset:bigint) { + builder.addFieldInt64(2, bufferOffset, BigInt('0')); +} + +static addData(builder:flatbuffers.Builder, dataOffset:flatbuffers.Offset) { + builder.addFieldOffset(3, dataOffset, 0); +} + +static createDataVector(builder:flatbuffers.Builder, data:number[]|Uint8Array):flatbuffers.Offset { + builder.startVector(1, data.length, 1); + for (let i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]!); + } + return builder.endVector(); +} + +static startDataVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(1, numElems, 1); +} + +static endDecodeUnitBuffer(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createDecodeUnitBuffer(builder:flatbuffers.Builder, frameNumber:bigint, bufferIndex:bigint, bufferOffset:bigint, dataOffset:flatbuffers.Offset):flatbuffers.Offset { + DecodeUnitBuffer.startDecodeUnitBuffer(builder); + DecodeUnitBuffer.addFrameNumber(builder, frameNumber); + DecodeUnitBuffer.addBufferIndex(builder, bufferIndex); + DecodeUnitBuffer.addBufferOffset(builder, bufferOffset); + DecodeUnitBuffer.addData(builder, dataOffset); + return DecodeUnitBuffer.endDecodeUnitBuffer(builder); +} +} diff --git a/frontend/src/lib/proto/video-update/decode-unit-start.ts b/frontend/src/lib/proto/video-update/decode-unit-start.ts new file mode 100644 index 0000000..f79db1d --- /dev/null +++ b/frontend/src/lib/proto/video-update/decode-unit-start.ts @@ -0,0 +1,91 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +import { FrameType } from '../video-update/frame-type.js'; + + +export class DecodeUnitStart { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):DecodeUnitStart { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsDecodeUnitStart(bb:flatbuffers.ByteBuffer, obj?:DecodeUnitStart):DecodeUnitStart { + return (obj || new DecodeUnitStart()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsDecodeUnitStart(bb:flatbuffers.ByteBuffer, obj?:DecodeUnitStart):DecodeUnitStart { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new DecodeUnitStart()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +frameNumber():bigint { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0'); +} + +frameType():FrameType { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.readInt8(this.bb_pos + offset) : FrameType.PFRAME; +} + +numBuffers():bigint { + const offset = this.bb!.__offset(this.bb_pos, 8); + return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0'); +} + +receiveTimeMs():number { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? this.bb!.readUint16(this.bb_pos + offset) : 0; +} + +fullLength():bigint { + const offset = this.bb!.__offset(this.bb_pos, 12); + return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0'); +} + +static startDecodeUnitStart(builder:flatbuffers.Builder) { + builder.startObject(5); +} + +static addFrameNumber(builder:flatbuffers.Builder, frameNumber:bigint) { + builder.addFieldInt64(0, frameNumber, BigInt('0')); +} + +static addFrameType(builder:flatbuffers.Builder, frameType:FrameType) { + builder.addFieldInt8(1, frameType, FrameType.PFRAME); +} + +static addNumBuffers(builder:flatbuffers.Builder, numBuffers:bigint) { + builder.addFieldInt64(2, numBuffers, BigInt('0')); +} + +static addReceiveTimeMs(builder:flatbuffers.Builder, receiveTimeMs:number) { + builder.addFieldInt16(3, receiveTimeMs, 0); +} + +static addFullLength(builder:flatbuffers.Builder, fullLength:bigint) { + builder.addFieldInt64(4, fullLength, BigInt('0')); +} + +static endDecodeUnitStart(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createDecodeUnitStart(builder:flatbuffers.Builder, frameNumber:bigint, frameType:FrameType, numBuffers:bigint, receiveTimeMs:number, fullLength:bigint):flatbuffers.Offset { + DecodeUnitStart.startDecodeUnitStart(builder); + DecodeUnitStart.addFrameNumber(builder, frameNumber); + DecodeUnitStart.addFrameType(builder, frameType); + DecodeUnitStart.addNumBuffers(builder, numBuffers); + DecodeUnitStart.addReceiveTimeMs(builder, receiveTimeMs); + DecodeUnitStart.addFullLength(builder, fullLength); + return DecodeUnitStart.endDecodeUnitStart(builder); +} +} diff --git a/frontend/src/lib/proto/video-update/update.ts b/frontend/src/lib/proto/video-update/update.ts index be05819..aa544b8 100644 --- a/frontend/src/lib/proto/video-update/update.ts +++ b/frontend/src/lib/proto/video-update/update.ts @@ -2,37 +2,41 @@ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ -import { DecodeUnit } from '../video-update/decode-unit.js'; +import { DecodeUnitBuffer } from '../video-update/decode-unit-buffer.js'; +import { DecodeUnitStart } from '../video-update/decode-unit-start.js'; import { Setup } from '../video-update/setup.js'; export enum Update { NONE = 0, Setup = 1, - DecodeUnit = 2 + DecodeUnitStart = 2, + DecodeUnitBuffer = 3 } export function unionToUpdate( type: Update, - accessor: (obj:DecodeUnit|Setup) => DecodeUnit|Setup|null -): DecodeUnit|Setup|null { + accessor: (obj:DecodeUnitBuffer|DecodeUnitStart|Setup) => DecodeUnitBuffer|DecodeUnitStart|Setup|null +): DecodeUnitBuffer|DecodeUnitStart|Setup|null { switch(Update[type]) { case 'NONE': return null; case 'Setup': return accessor(new Setup())! as Setup; - case 'DecodeUnit': return accessor(new DecodeUnit())! as DecodeUnit; + case 'DecodeUnitStart': return accessor(new DecodeUnitStart())! as DecodeUnitStart; + case 'DecodeUnitBuffer': return accessor(new DecodeUnitBuffer())! as DecodeUnitBuffer; default: return null; } } export function unionListToUpdate( type: Update, - accessor: (index: number, obj:DecodeUnit|Setup) => DecodeUnit|Setup|null, + accessor: (index: number, obj:DecodeUnitBuffer|DecodeUnitStart|Setup) => DecodeUnitBuffer|DecodeUnitStart|Setup|null, index: number -): DecodeUnit|Setup|null { +): DecodeUnitBuffer|DecodeUnitStart|Setup|null { switch(Update[type]) { case 'NONE': return null; case 'Setup': return accessor(index, new Setup())! as Setup; - case 'DecodeUnit': return accessor(index, new DecodeUnit())! as DecodeUnit; + case 'DecodeUnitStart': return accessor(index, new DecodeUnitStart())! as DecodeUnitStart; + case 'DecodeUnitBuffer': return accessor(index, new DecodeUnitBuffer())! as DecodeUnitBuffer; default: return null; } } diff --git a/frontend/src/lib/video.ts b/frontend/src/lib/video.ts index d0efec0..4c0e432 100644 --- a/frontend/src/lib/video.ts +++ b/frontend/src/lib/video.ts @@ -1,105 +1,182 @@ import { VideoUpdate } from "$lib/proto/video"; import { ByteBuffer } from "flatbuffers"; +import { DecodeUnitBuffer } from "./proto/video-update"; +import { DoorClosed, Video } from "lucide-svelte"; -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 = data.slice(index, data.length); - break; - } - - const dataToParse = new ByteBuffer(data.slice(slice_start_index, slice_end_index)); - - const videoUpdate = VideoUpdate.VideoUpdate.getRootAsVideoUpdate(dataToParse); - packets.push(videoUpdate); - - index += 4 + dataLength; - } - return [packets, unparsedData]; -} - - -export async function streamVideoFromReader(reader: ReadableStreamDefaultReader, canvasElement: OffscreenCanvas) { +function getVideoDecoder(canvasElement: OffscreenCanvas): VideoDecoder { const canvasCtx: OffscreenCanvasRenderingContext2D | null = canvasElement.getContext('2d'); if (canvasCtx == null) { throw new Error(`Could not get 2d canvas context`); } + const videoDecoder = new VideoDecoder({ + output: (frame) => { + //console.log(`rendering frame start: ${performance.now()}`); + //canvasElement.width = frame.displayWidth; + //canvasElement.height = frame.displayHeight; + + //console.log(`rendering frame drawImage: ${performance.now()}`); + canvasCtx.drawImage(frame, 0, 0); + + //console.log(`rendering frame end: ${performance.now()}`); + frame.close(); + //console.log(`rendering frame close: ${performance.now()}`); + }, + error: (e) => { + console.error('Decode error:', e); + } + }); + return videoDecoder; +} + +async function configureDecoder(videoDecoder: VideoDecoder, videoFormat: string, width: number, height: number) { + let config: VideoDecoderConfig = { + codec: videoFormat, + codedWidth: width, + codedHeight: height, + optimizeForLatency: true, + //hardwareAcceleration: "prefer-hardware", + }; + + const codecSupport = await VideoDecoder.isConfigSupported(config); + console.log(codecSupport); + if (codecSupport.supported) { + videoDecoder.configure(config); + } else { + throw new Error(`Could not configure decoder`); + } +} + +class Decoder { + videoDecoder: VideoDecoder; + + frameNumber: bigint | undefined; + frameType: EncodedAudioChunkType = "delta"; + fullLength: bigint = 0n; + receiveTimeMs: number = 0; + + //frameBroken: boolean = false; + //lastBufferIndex: bigint = 0n; + frameCollector: boolean[] = new Array(); + dataOffset: number = 0; + data: Uint8Array = new Uint8Array(); + + constructor(videoDecoder: VideoDecoder) { + this.videoDecoder = videoDecoder; + } + + print() { + console.log( + ` + frameNumber: ${this.frameNumber} + frameType: ${this.frameType} + fullLength: ${this.fullLength} + receiveTimeMs: ${this.receiveTimeMs} + frameCollector: ${this.frameCollector} + ts: ${performance.now()} + ` + ); + } + + processStart(decodeUnitStart: VideoUpdate.DecodeUnitStart) { + //this.print(); + const frameCompleted = !this.frameCollector.includes(false); + if (!frameCompleted) { + console.log(`Got setup packet for frame ${decodeUnitStart.frameNumber()} but the last frame has not been completed`); + } + + this.frameNumber = decodeUnitStart.frameNumber(); + this.frameType = "delta"; + if (decodeUnitStart.frameType() == VideoUpdate.FrameType.IDR) { + this.frameType = "key"; + } + this.fullLength = decodeUnitStart.fullLength(); + this.receiveTimeMs = decodeUnitStart.receiveTimeMs(); + + //this.frameBroken = false; + //this.lastBufferIndex = -1n; + //this.dataOffset = 0; + this.frameCollector = new Array(Number(decodeUnitStart.numBuffers())).fill(false); + + this.data = new Uint8Array(Number(this.fullLength)); + + + //this.print(); + //console.log(`start: `, this); + //console.log(performance.now()); + } + + processBuffer(decodeUnitBuffer: VideoUpdate.DecodeUnitBuffer) { + //console.log(`buffer: `, this); + //console.log(performance.now()); + //this.print(); + + + const frameNumber = decodeUnitBuffer.frameNumber(); + if (this.frameNumber === undefined) { + console.log("frameNumber is undefined but we got a buffer, ignoring..."); + return; + } + if (this.frameNumber != frameNumber) { + console.log(`Got buffer for frame ${frameNumber} but we are processing frame ${this.frameNumber}, ignoring...`); + return; + } + + let offset = decodeUnitBuffer.bufferOffset(); + + for (var i = 0; i < decodeUnitBuffer.dataLength(); i++) { + this.data[Number(offset) + i] = decodeUnitBuffer.data(i)!; + } + + this.frameCollector[Number(decodeUnitBuffer.bufferIndex())] = true; + + + const gotAllframes = !this.frameCollector.includes(false); + + if (gotAllframes) { + const chunk = new EncodedVideoChunk({ + //timestamp: this.receiveTimeMs, + timestamp: 0, + type: this.frameType, + data: this.data, + }); + + //console.log(`${ performance.now() }: Enqueing a new decode request, current queue size ${ this.videoDecoder.decodeQueueSize } `); + this.videoDecoder.decode(chunk); + } + //this.print(); + } +} + +export async function streamVideoFromReader(reader: ReadableStreamDefaultReader, canvasElement: OffscreenCanvas) { + const videoDecoder = getVideoDecoder(canvasElement); try { - let unparsedData = new Uint8Array(); + let decodeUnitBuffer: VideoUpdate.DecodeUnitBuffer = new VideoUpdate.DecodeUnitBuffer(); + let decoder = new Decoder(videoDecoder); - const videoDecoder = new VideoDecoder({ - output: (frame) => { - canvasElement.width = frame.displayWidth; - canvasElement.height = frame.displayHeight; - - canvasCtx.drawImage(frame, 0, 0); - - frame.close(); - }, - error: (e) => { - console.error('Decode error:', e); - } - }); - let decodeUnit: VideoUpdate.DecodeUnit = new VideoUpdate.DecodeUnit(); while (true) { const { value, done } = await reader.read(); if (done) break; - let [packets, remainingData] = parseData(value, unparsedData); - unparsedData = remainingData; + const dataToParse = new ByteBuffer(value); + const videoUpdate = VideoUpdate.VideoUpdate.getRootAsVideoUpdate(dataToParse); - for (let i = 0; i < packets.length; i++) { - if (packets[i].updateType() == VideoUpdate.Update.Setup) { - let setup = packets[i].update(new VideoUpdate.Setup()); - let config: VideoDecoderConfig = { - codec: setup.videoFormat(), - codedWidth: setup.width(), - codedHeight: setup.height(), - }; + if (videoUpdate.updateType() == VideoUpdate.Update.Setup) { + let setup = videoUpdate.update(new VideoUpdate.Setup()); + await configureDecoder(videoDecoder, setup.videoFormat(), setup.width(), setup.height()); - const codecSupport = await VideoDecoder.isConfigSupported(config); - if (codecSupport.supported) { - videoDecoder.configure(config); - } else { - throw new Error(`Could not configure decoder`); - } + } else if (videoUpdate.updateType() == VideoUpdate.Update.DecodeUnitStart) { + let decodeUnitStart: VideoUpdate.DecodeUnitStart = new VideoUpdate.DecodeUnitStart(); + videoUpdate.update(decodeUnitStart); + decoder.processStart(decodeUnitStart); - } else if (packets[i].updateType() == VideoUpdate.Update.DecodeUnit) { - packets[i].update(decodeUnit); + } else if (videoUpdate.updateType() == VideoUpdate.Update.DecodeUnitBuffer) { + videoUpdate.update(decodeUnitBuffer); + decoder.processBuffer(decodeUnitBuffer); - let frameType: EncodedAudioChunkType = "delta"; - if (decodeUnit.frameType() == VideoUpdate.FrameType.IDR) { - console.log("GOT KEYFRAME"); - frameType = "key"; - } - const chunk = new EncodedVideoChunk({ - timestamp: decodeUnit.receiveTimeMs(), - type: frameType, - data: decodeUnit.dataArray()!, - }); - - videoDecoder.decode(chunk); - - } else { - throw new Error(`Got packet of unknown type`); - } + } else { + throw new Error(`Got packet of unknown type`); } - } } catch (e) { diff --git a/frontend/src/routes/Cover.svelte b/frontend/src/routes/Cover.svelte index acf054d..264328a 100644 --- a/frontend/src/routes/Cover.svelte +++ b/frontend/src/routes/Cover.svelte @@ -23,6 +23,8 @@ let streamData = await getStreamData(app.id, server_name); streamStore.Url = streamData.Url; streamStore.CertHash = streamData.CertHash; + streamStore.Width = streamData.Width; + streamStore.Height = streamData.Height; console.log(`Stream data retrieved. Navigating to /stream.`); await goto('/stream'); diff --git a/frontend/src/routes/getStreamData.ts b/frontend/src/routes/getStreamData.ts index 33369e4..5e3febf 100644 --- a/frontend/src/routes/getStreamData.ts +++ b/frontend/src/routes/getStreamData.ts @@ -1,51 +1,30 @@ -//Setup { -// video_format: VideoFormat, -// width: u64, -// height: u64, -// redraw_rate: u64, -// dr_flags: i32, -//}, -//DecodeUnit { -// frame_number: u64, -// frame_type: FrameType, - -// host_processing_latency: u16, -// receieve_time_ms: u64, -// enqueue_time_ms: u64, -// presentation_time: u64, - -// full_length: usize, -// //buffers: Vec, -// buffer: Buffer, -// index: u64, - -// hdr_active: bool, -// colorspace: u8, -//}, - - type StreamData = { Url: string, CertHash: Array, + Width: number, + Height: number, } export async function getStreamData(appId: number, server_name: string): Promise { try { // Create the POST request payload + const width = 1920; + const height = 1080; + const payload = { id: appId, server: server_name, server_mode: { fps: 60, - width: 1920, - height: 1080, + width: width, + height: height, }, stream_config: { - bitrate_kbps: 1024 * 10 * 2, + bitrate_kbps: 1024 * 10 * 5, mode: { fps: 60, - width: 1920, - height: 1080, + width: width, + height: height, } } }; @@ -66,7 +45,7 @@ export async function getStreamData(appId: number, server_name: string): Promise const streamDataResp = await response.json(); console.log('Stream started:', streamDataResp); - let streamData: StreamData = { Url: streamDataResp.url, CertHash: streamDataResp.cert_hash }; + let streamData: StreamData = { Url: streamDataResp.url, CertHash: streamDataResp.cert_hash, Width: width, Height: height }; return streamData; diff --git a/frontend/src/routes/stores/streamStore.svelte.ts b/frontend/src/routes/stores/streamStore.svelte.ts index 168e199..5330783 100644 --- a/frontend/src/routes/stores/streamStore.svelte.ts +++ b/frontend/src/routes/stores/streamStore.svelte.ts @@ -1,4 +1,6 @@ export const streamStore = $state({ Url: '', CertHash: [0], + Width: 0, + Height: 0, }); diff --git a/frontend/src/routes/stream/+page.svelte b/frontend/src/routes/stream/+page.svelte index 0c11349..8aaf36f 100644 --- a/frontend/src/routes/stream/+page.svelte +++ b/frontend/src/routes/stream/+page.svelte @@ -4,6 +4,8 @@ $: url = streamStore.Url; $: certHash = streamStore.CertHash; + $: width = streamStore.Width; + $: height = streamStore.Height; @@ -13,7 +15,7 @@ - +