Skip to content

Commit

Permalink
added support for http streaming via GET request
Browse files Browse the repository at this point in the history
  • Loading branch information
psy0rz committed Dec 25, 2024
1 parent 7f8e4d7 commit 84ca73c
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 81 deletions.
2 changes: 1 addition & 1 deletion ledder/Display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export default abstract class Display {
abstract setPixel(x: number, y: number, color: ColorInterface);

//should send the current rendered frame buffer and clear the buffer
//Parameter is time in uS when the frame should be rendered.
//Parameter is absolute time in uS when the frame should be rendered.
//For pre-rendeers this starts counting at 0, for live renders this is the systemtime.
abstract frame(displayTimeMicros: number)

Expand Down
6 changes: 3 additions & 3 deletions ledder/server/DisplayQOIS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export abstract class DisplayQOIS extends Display {
}

//encodes current pixels by adding bytes to bytes array.
encode(bytes: Array<number>, displayTime): boolean {
encode(bytes: Array<number>, displayTimeMS): boolean {
let prevPixel = colorBlack
let run = 0
let pixelCount = 1
Expand All @@ -92,8 +92,8 @@ export abstract class DisplayQOIS extends Display {
bytes.push((this.pixelsPerChannel >> 8) & 0xff)

//time when to display frame
bytes.push(displayTime & 0xff)
bytes.push((displayTime >> 8) & 0xff)
bytes.push(displayTimeMS & 0xff)
bytes.push((displayTimeMS >> 8) & 0xff)


this.statsBytes -= bytes.length //substract header overhead
Expand Down
40 changes: 0 additions & 40 deletions ledder/server/RenderStatic.ts

This file was deleted.

52 changes: 52 additions & 0 deletions ledder/server/RenderStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {Render} from "./Render.js"
import {Writable} from "node:stream"

/** Non-realtime rendering , for use in tcp streaming for example.
* This renders as fast as the display driver allows, letting the driver/client handle the fps rates.
* This means buffers are hopefully filled and displaying will be smooth and stutterless.
*/
export class RenderStream extends Render {

/*
Renders and sends one frame.
Caller should keep calling us as fast as possible, as long as the external writable is draining.
*/
async render(fh: Writable, encodedFrame: number[]) {


//render as fast as possible, filling writable buffers
let frameTime = 0

const _this = this

async function fillFh() {

console.log("FILLING")
do {
//NOTE: await is needed, to allow microtasks to run!
frameTime = frameTime + await _this.scheduler.__step(false)

frameTime = ~~(frameTime / _this.display.frameRoundingMicros) * _this.display.frameRoundingMicros

encodedFrame.length = 0
_this.display.render(_this.box)
_this.display.frame(frameTime)
} while (fh.write(new Uint8Array(encodedFrame)))
console.log("FULL")
}

fh.on('drain', async () => {
console.log("CONTINUE")
await fillFh()
})

fh.on('close', () => {
console.log("CLOSED")
this.animationManager.stop(false)
})

await fillFh()

}

}
Original file line number Diff line number Diff line change
@@ -1,43 +1,35 @@
import {DisplayQOIS} from "../DisplayQOIS.js"
import OffsetMapper from "./OffsetMapper.js"

import {createWriteStream, WriteStream} from "fs"

//Renders a animation to a writestream. Used to do static rendering/uploading
//Note: user of this class is responsible for creating fh.
export class DisplayQOISstream extends DisplayQOIS {
export class DisplayQOISbuffer extends DisplayQOIS {

public writeCallback: (Uint8Array)=>void
private buffer:number[]

/**
* QOIS file driver, used in for https://github.com/psy0rz/ledstream
* Instead of streaming via UDP like DisplayQOISudp, this one writes frames to a writestream.
Adds QOIS frames to specified buffer. (never cleans it)
* @param mapper Offset mapper that determines the width and height, and which coordinate belongs to with offset in the display buffer.
@param pixelsPerChannel Number of pixels per channel you want to use. Doesnt have to correspondent with how you compiled ledder, leds will be skipped/cropped. Or 0 to use all the leds available in the firmware.
*/
constructor(mapper: OffsetMapper, pixelsPerChannel,maxFps=60) {
constructor(buffer:number[], mapper: OffsetMapper, pixelsPerChannel,maxFps=60) {
super(mapper, pixelsPerChannel)

this.minFrameTimeMicros = ~~(1000000 / maxFps)
this.defaultFrameTimeMicros = this.minFrameTimeMicros

this.buffer=buffer


}

}

//QOIS FRAME:
// [display time (2 bytes)][QOIS encoded bytes]

frame(displayTime: number) {

displayTime = displayTime / 1000

const frameBytes = []

this.encode(frameBytes, displayTime)

this.writeCallback(new Uint8Array(frameBytes))
this.encode(this.buffer, displayTime)


}
Expand Down
1 change: 1 addition & 0 deletions ledder/server/drivers/DisplayQOISudp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export class DisplayQOISudp extends DisplayQOIS {

displayTime = displayTime / 1000


const frameBytes = []

const maxFramesLag = 24
Expand Down
40 changes: 18 additions & 22 deletions ledder/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import GammaMapper from "./drivers/GammaMapper.js"
import {config} from "./config.js"
import {presetStore} from "./PresetStore.js"
import {previewStore} from "./PreviewStore.js"
import {RenderStatic} from "./RenderStatic.js"
import {DisplayQOISstream} from "./drivers/DisplayQOISstream.js"
import {RenderStream} from "./RenderStream.js"
import {DisplayQOISbuffer} from "./drivers/DisplayQOISbuffer.js"
import {displayDeviceStore} from "./DisplayDeviceStore.js"
import OffsetMapper from "./drivers/OffsetMapper.js"


const settingsControl = new ControlGroup('Global settings')
Expand Down Expand Up @@ -137,35 +138,30 @@ rpc.app.get('/get/status/:id', async (req, resp) => {

})

rpc.app.get('/get/render/:id', async (req, resp) => {
//work in progress
//Stream QOIS frames via a http get request.
//Rendering is as fast as possible, to fill buffers up.
//Client display decides how much data is buffered and consumed.
rpc.app.get('/get/stream/:id', async (req, resp) => {

let deviceInfo = await displayDeviceStore.get(req.params.id)

//XXX:fix me, now only supports first display
const display: DisplayQOISstream = config.staticDisplayList[0]
display.writeCallback = (data) => {
resp.write(data)
}
display.gammaMapper = gammaMapper

const renderer = new RenderStatic(display)
await renderer.animationManager.select(deviceInfo.animation, false)
await renderer.render(deviceInfo.frames)

resp.end()
let matrixzigzag8x32 = new OffsetMapper(32, 8, false)
matrixzigzag8x32.zigZagY()
matrixzigzag8x32.flipY()


})
const encodedFrameBuffer=[]
const display=new DisplayQOISbuffer(encodedFrameBuffer,matrixzigzag8x32, 256)
display.gammaMapper = gammaMapper

rpc.app.get('/get/stream/:id', async (req, resp) => {
const renderer=new RenderStream(display)
await renderer.animationManager.select("Tests/TestMatrix/default", false)

let deviceInfo = await displayDeviceStore.get(req.params.id)

await renderer.render(resp, encodedFrameBuffer)


resp.once('finish', () => {
console.log("CLOSE")
})


})

0 comments on commit 84ca73c

Please sign in to comment.