#!/usr/bin/env python3 # pygame_fixed_format.py - Fixed byte order import pygame,os import numpy as np import time import threading from flask import Flask, Response import io # Setup os.environ['SDL_VIDEODRIVER'] = 'dummy' WIDTH, HEIGHT = 400, 300 app = Flask(__name__) # Shared state frame_data = { "jpeg_bytes": None, # Store as JPEG for smaller size "frame_id": 0, "lock": threading.Lock() } @app.route('/') def index(): return ''' PyGame Stream

Auto-Refresh (10 FPS)

Refreshing every 100ms...

''' @app.route('/frame') def get_frame(): """Return frame as JPEG (smaller, faster)""" with frame_data["lock"]: if frame_data["jpeg_bytes"]: return Response( frame_data["jpeg_bytes"], mimetype='image/jpeg', headers={'Cache-Control': 'no-cache'} ) # Return loading image return create_loading_image() def create_loading_image(): """Create a simple loading image""" from PIL import Image, ImageDraw import io as imageio img = Image.new('RGB', (WIDTH, HEIGHT), color='blue') draw = ImageDraw.Draw(img) draw.text((150, 140), 'LOADING...', fill='white') buffer = imageio.BytesIO() img.save(buffer, 'JPEG') return Response(buffer.getvalue(), mimetype='image/jpeg') def pygame_render(): """PyGame render loop with FIXED format""" print("🎬 Starting PyGame with fixed format...") try: pygame.init() surface = pygame.Surface((WIDTH, HEIGHT)) print("✅ PyGame initialized") except Exception as e: print(f"❌ PyGame init failed: {e}") return # Simple bouncing balls (fewer for clarity) balls = [ {'x': 100, 'y': 150, 'dx': 3, 'dy': 2, 'r': 20, 'color': (255, 100, 100)}, {'x': 300, 'y': 100, 'dx': -2, 'dy': 3, 'r': 15, 'color': (100, 255, 100)}, {'x': 200, 'y': 200, 'dx': 4, 'dy': -1, 'r': 25, 'color': (100, 100, 255)}, ] print("✅ Created 3 bouncing balls") frame_id = 0 while True: # Clear surface.fill((30, 30, 50)) # Draw grid background for x in range(0, WIDTH, 40): pygame.draw.line(surface, (50, 50, 70), (x, 0), (x, HEIGHT), 1) for y in range(0, HEIGHT, 40): pygame.draw.line(surface, (50, 50, 70), (0, y), (WIDTH, y), 1) # Update and draw balls for ball in balls: ball['x'] += ball['dx'] ball['y'] += ball['dy'] if ball['x'] < ball['r'] or ball['x'] > WIDTH - ball['r']: ball['dx'] = -ball['dx'] if ball['y'] < ball['r'] or ball['y'] > HEIGHT - ball['r']: ball['dy'] = -ball['dy'] # Draw ball pygame.draw.circle(surface, ball['color'], (int(ball['x']), int(ball['y'])), ball['r']) # Draw outline pygame.draw.circle(surface, (255, 255, 255), (int(ball['x']), int(ball['y'])), ball['r'], 2) # Add text font = pygame.font.Font(None, 24) texts = [ f"Frame: {frame_id}", f"Balls: {len(balls)}", time.strftime("%H:%M:%S") ] for i, text in enumerate(texts): text_surface = font.render(text, True, (255, 255, 255)) surface.blit(text_surface, (10, 10 + i * 25)) # === FIX 1: Convert to JPEG (smaller, browser-friendly) === import io buffer = io.BytesIO() pygame.image.save(surface, buffer, 'JPEG') jpeg_bytes = buffer.getvalue() # Update shared state with frame_data["lock"]: frame_data["jpeg_bytes"] = jpeg_bytes frame_data["frame_id"] = frame_id frame_id += 1 if frame_id % 30 == 0: print(f"📸 Frame {frame_id} ({len(jpeg_bytes)} bytes)") # Fixed 30 FPS time.sleep(1/30) # Start render thread threading.Thread(target=pygame_render, daemon=True).start() # Wait for initialization time.sleep(2) print("🌐 Starting Flask...") app.run(host='0.0.0.0', port=7860, threaded=True, use_reloader=False)