Skip to content

Instantly share code, notes, and snippets.

@companje
Created February 23, 2025 13:38
Show Gist options
  • Save companje/114b3d5d54fdfa1129e76527ad6ce8b1 to your computer and use it in GitHub Desktop.
Save companje/114b3d5d54fdfa1129e76527ad6ce8b1 to your computer and use it in GitHub Desktop.
Render to screen and stream on a different resolution and framerate to Websocket
Streamer streamer;
void setup() {
size(640, 600, P3D);
frameRate(60);
background(0);
streamer = new Streamer(400, 400, 8080);
}
void draw() {
render(this.getGraphics()); //render to screen
if (frameCount%4==0) {
streamer.renderFrame(); //must be called from draw() when using P3D/OPENGL
}
stroke(255, 255, 0);
point(frameCount % width, height-frameRate);
}
void render(PGraphics pg) {
pg.beginDraw();
pg.background(pg==streamer.pg ? 150 : 0);
pg.stroke(255);
pg.fill(255, 0, 0);
pg.ellipse(frameCount % pg.width, pg.height/2, 50, 50);
pg.translate(pg.width/2, pg.height/2);
pg.rotateY(frameCount*.01);
pg.sphere(100);
pg.endDraw();
}
import org.java_websocket.server.WebSocketServer;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.WebSocket;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import javax.imageio.ImageIO;
import java.util.Base64;
import java.awt.Graphics2D;
class Streamer {
PGraphics pg;
PImage pimg;
Thread render;
boolean frameReady = false;
ProcessingWebSocketServer server;
BufferedImage img;
Graphics2D g2d;
ByteArrayOutputStream baos;
Streamer(int w, int h, int port) {
pg = createGraphics(w, h, P3D);
img = new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
g2d = img.createGraphics();
baos = new ByteArrayOutputStream();
server = new ProcessingWebSocketServer(port);
server.start();
render = new Thread(()->runner());
render.start();
}
void renderFrame() { //must be called from draw()
render(pg);
pimg = pg.get();
frameReady = true;
}
void runner() {
while (!Thread.currentThread().isInterrupted()) {
if (frameReady) {
if (server!=null && !server.getClients().isEmpty()) {
try {
g2d.drawImage((java.awt.Image) pimg.getNative(), 0, 0, null);
ImageIO.write(img, "jpg", baos);
String base64Image = Base64.getEncoder().encodeToString(baos.toByteArray());
server.broadcast(base64Image);
baos.reset(); //!!
}
catch (Exception e) {
e.printStackTrace();
}
}
frameReady = false;
}
}
}
}
// WebSocket-serverklasse
class ProcessingWebSocketServer extends WebSocketServer {
public Set<WebSocket> clients = Collections.synchronizedSet(new HashSet<>());
ProcessingWebSocketServer(int port) {
super(new InetSocketAddress("0.0.0.0", port));
this.setReuseAddr(true); //!!
}
//@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
clients.add(conn);
println("Client connected: " + conn.getRemoteSocketAddress());
}
//@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
clients.remove(conn);
println("Client disconnected: " + conn.getRemoteSocketAddress());
}
//@Override
public void onMessage(WebSocket conn, String message) {
println("Received message: " + message);
}
//@Override
public void onError(WebSocket conn, Exception ex) {
ex.printStackTrace();
}
//@Override
public void onStart() {
println("WebSocket server started on port " + getPort());
}
public Set<WebSocket> getClients() {
return clients;
}
public void broadcast(String message) {
synchronized (clients) {
for (WebSocket client : clients) {
if (client.isOpen()) {
client.send(message);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment