Skip to content

open-game-collective/renderer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

OGS Casting Renderer

The Renderer service is a critical component of the OGS Casting system, responsible for running headless browsers, rendering game content, and streaming it to Chromecast devices via WebRTC. This service is designed to operate as a stateless API while maintaining long-running browser instances for high-quality game streaming.

Architecture Overview

The Renderer service is built on these core technologies:

  • Google Cloud Run: Containerized, auto-scaling serverless platform
  • Puppeteer: Headless Chrome automation for rendering games
  • PeerJS: WebRTC signaling and streaming for TV casting
  • Express.js: HTTP API for receiving commands from Cloudflare Workflows
graph TD
    subgraph "OGS Casting System"
        CastRouter[Cast Router - Cloudflare Worker]
        Workflow[Cloudflare Workflow]
        KV[Cloudflare KV]
    end
    
    subgraph "Renderer Service - Google Cloud Run"
        API[Express API]
        BrowserManager[Browser Manager]
        PuppeteerPool[Puppeteer Pool]
        subgraph "Browser Instances"
            Browser1[Browser 1]
            Browser2[Browser 2]
            BrowserN[Browser N]
        end
        PeerJSClient[PeerJS Client]
    end
    
    subgraph "WebRTC Infrastructure"
        PeerJSServer[PeerJS Server]
        STUN[STUN/TURN Servers]
    end
    
    subgraph "User Devices"
        Chromecast[Chromecast]
    end
    
    Workflow -->|HTTP API Calls| API
    API -->|Manage| BrowserManager
    BrowserManager -->|Launch/Control| PuppeteerPool
    PuppeteerPool -->|Contains| Browser1
    PuppeteerPool -->|Contains| Browser2
    PuppeteerPool -->|Contains| BrowserN
    Browser1 -->|Stream via| PeerJSClient
    PeerJSClient -->|Connect to| PeerJSServer
    PeerJSServer -->|Stream to| Chromecast
Loading

Key Features

  • Stateless API Design: No persistent connections required; operates via standard HTTP requests
  • Long-Running Browser Management: Maintains browser instances for extended periods despite stateless API
  • High-Quality Streaming: Captures and streams browser content at up to 1080p/60fps via WebRTC
  • Efficient Resource Usage: Automatic cleanup of idle browsers to optimize resource consumption
  • Scalable Architecture: Designed to handle multiple concurrent casting sessions
  • Container-Based: Packaged as a Docker container for easy deployment and scaling

Installation and Setup

Prerequisites

  • Google Cloud account with Cloud Run access
  • Docker installed for local development
  • Node.js 18+ and npm/yarn

Environment Variables

PORT=8080                         # Port for the API server
MAX_BROWSERS=50                   # Maximum number of concurrent browser instances
BROWSER_TIMEOUT_MS=3600000        # Browser idle timeout (1 hour default)
PEERJS_SERVER=peerjs.triviajam.tv # PeerJS server for WebRTC
PEERJS_PORT=443                   # PeerJS server port
PEERJS_PATH=/peerjs               # PeerJS server path
PEERJS_SECURE=true                # Use secure WebRTC connections
ALLOWED_ORIGINS=triviajam.tv      # CORS allowed origins
BROWSER_POOL_MIN_SIZE=5           # Minimum browsers to keep warm

Local Development

# Clone the repository
git clone https://github.com/open-game-collective/cast-renderer.git
cd cast-renderer

# Install dependencies
npm install

# Start development server
npm run dev

# Build and run with Docker
docker build -t cast-renderer .
docker run -p 8080:8080 -e PORT=8080 cast-renderer

Google Cloud Run Deployment

# Build and push to Google Container Registry
gcloud builds submit --tag gcr.io/your-project/cast-renderer

# Deploy to Cloud Run
gcloud run deploy cast-renderer \
  --image gcr.io/your-project/cast-renderer \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --memory 2Gi \
  --cpu 2 \
  --min-instances 1 \
  --max-instances 10

API Endpoints

The Renderer service exposes several HTTP endpoints for managing browser instances and streaming:

Browser Management

Create Browser

POST /api/browser

Creates a new browser instance and loads the specified game URL.

Request Body:

{
  "sessionId": "sess_abc123",
  "gameUrl": "https://triviajam.tv/cast?gameId=123&roomCode=ABCD",
  "options": {
    "resolution": "1080p",
    "frameRate": 60
  }
}

Response:

{
  "browserId": "browser_xyz789",
  "status": "launching",
  "createdAt": "2023-05-15T12:34:56.789Z"
}

Get Browser Status

GET /api/browser/{browserId}

Returns the current status of a browser instance.

Response:

{
  "browserId": "browser_xyz789",
  "status": "ready",
  "createdAt": "2023-05-15T12:34:56.789Z",
  "lastActiveAt": "2023-05-15T12:35:10.123Z"
}

Keep Browser Alive

POST /api/browser/{browserId}/heartbeat

Sends a heartbeat to keep the browser instance alive. This endpoint should be called periodically by Cloudflare Workflows.

Response:

{
  "browserId": "browser_xyz789",
  "status": "ready",
  "lastHeartbeatAt": "2023-05-15T12:40:15.678Z"
}

Connect to Receiver

POST /api/browser/{browserId}/receiver

Initiates WebRTC streaming from the browser to a Chromecast receiver.

Request Body:

{
  "receiverPeerId": "peer_abc123"
}

Response:

{
  "browserId": "browser_xyz789",
  "status": "streaming",
  "senderPeerId": "peer_def456",
  "streamStartedAt": "2023-05-15T12:42:30.456Z"
}

Terminate Browser

DELETE /api/browser/{browserId}

Terminates a browser instance and cleans up resources.

Response:

{
  "browserId": "browser_xyz789",
  "status": "terminated",
  "terminatedAt": "2023-05-15T13:15:45.789Z"
}

Service Information

GET /health

Health check endpoint for monitoring service status.

Response:

{
  "status": "healthy",
  "activeBrowsers": 5,
  "availableSlots": 45,
  "version": "1.0.0"
}

Browser Management

The Renderer service uses a sophisticated browser management system to efficiently handle multiple concurrent sessions:

Browser Pool

  • Pre-initialized browser instances for fast startup times
  • Automatic scaling based on demand
  • Resource monitoring to prevent overallocation

Browser Lifecycle

  1. Initialization: Browser is launched with specified options
  2. Loading: Game URL is loaded in the browser
  3. Ready: Browser is ready for streaming
  4. Streaming: Browser is actively streaming to a receiver
  5. Idle: Browser is not actively streaming but remains available
  6. Terminated: Browser is closed and resources are cleaned up

Automatic Cleanup

Browsers are automatically terminated after a configurable idle period (default: 1 hour) to free up resources. The cleanup process:

  1. Checks for browser instances that have not received a heartbeat
  2. Gracefully closes these instances
  3. Releases associated resources
  4. Updates internal state

WebRTC Streaming Implementation

The Renderer service uses PeerJS to establish WebRTC connections for streaming game content:

// Example of WebRTC streaming implementation
async function startStreaming(browser, page, receiverPeerId) {
  // Create a sender peer ID
  const senderPeerId = `sender-${browser.id}-${Date.now()}`;
  
  // Create PeerJS instance
  const peer = new Peer(senderPeerId, {
    host: config.peerjs.host,
    port: config.peerjs.port,
    path: config.peerjs.path,
    secure: config.peerjs.secure,
    debug: 3
  });
  
  return new Promise((resolve, reject) => {
    peer.on('open', async (id) => {
      try {
        // Get media stream from the page
        const stream = await page.evaluate(async () => {
          const canvas = document.querySelector('canvas');
          if (!canvas) {
            throw new Error('No canvas element found for streaming');
          }
          
          // Get stream from canvas
          return canvas.captureStream(60);
        });
        
        // Call the receiver with our stream
        const call = peer.call(receiverPeerId, stream);
        
        // Track connection state
        call.on('stream', () => {
          console.log(`Stream connected to receiver ${receiverPeerId}`);
        });
        
        call.on('close', () => {
          console.log(`Stream to ${receiverPeerId} closed`);
        });
        
        resolve({
          senderPeerId,
          status: 'streaming'
        });
        
      } catch (error) {
        peer.destroy();
        reject(error);
      }
    });
    
    peer.on('error', (error) => {
      reject(error);
    });
  });
}

Integration with Cloudflare Workflows

The Renderer service is designed to work seamlessly with Cloudflare Workflows, which maintain the connection lifecycle:

  1. Browser Creation: Workflow calls the Renderer API to create a browser
  2. Heartbeat Mechanism: Workflow periodically sends heartbeats to keep browsers alive
  3. Command Execution: Workflow forwards commands like receiver connection requests
  4. Termination: Workflow explicitly terminates browsers when sessions end

Example Workflow Integration

// Example of Cloudflare Workflow integration
async function maintainBrowserInstance(browserId) {
  // Send regular heartbeats to keep the browser alive
  const sendHeartbeat = async () => {
    try {
      const response = await fetch(`${RENDERER_HOST}/api/browser/${browserId}/heartbeat`, {
        method: 'POST'
      });
      
      if (!response.ok) {
        throw new Error(`Failed to send heartbeat: ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      console.error('Heartbeat error:', error);
      // Implement retry logic here
    }
  };
  
  // Send heartbeats at regular intervals
  while (true) {
    const result = await sendHeartbeat();
    
    // Check if we should continue
    if (result.status === 'terminated') {
      break;
    }
    
    // Wait before the next heartbeat
    await new Promise(resolve => setTimeout(resolve, 30000)); // 30 seconds
  }
}

Game URL Construction

The Renderer loads game content from a URL like:

https://triviajam.tv/cast?gameId=123&roomCode=ABCD&mode=cast

This URL should load a specialized version of your game optimized for casting:

  • Fullscreen layout suitable for TV display
  • No interactive elements (controlled by the player's device)
  • Connected to your game's state management system
  • Optimized for rendering performance

Browser Extensions

For high-quality tab capture, the Renderer uses custom Chrome extensions:

  • Tab Capture Extension: Enables access to tab content for streaming
  • Media Optimization: Improves video encoding performance
  • Audio Capture: Ensures game audio is properly captured

Performance Optimization

The Renderer service includes several optimizations for performance:

  • Browser Resource Limits: CPU and memory limits for each browser
  • Streaming Quality Adaptation: Adjusts quality based on network conditions
  • Process Isolation: Each browser runs in an isolated process
  • Garbage Collection: Regular cleanup of terminated browsers

Monitoring and Logging

Comprehensive monitoring and logging are built into the service:

  • Browser Instance Metrics: Count, status, resource usage
  • Streaming Performance: FPS, bitrate, latency
  • API Usage: Request rates, errors, latency
  • Resource Utilization: CPU, memory, network usage

Logs are structured in JSON format for easy integration with logging systems.

Security Considerations

The Renderer service implements several security measures:

  • Input Validation: All API requests are validated
  • Resource Limits: Prevents resource exhaustion attacks
  • CORS Protection: Restricts access to allowed origins
  • Sanitized Browser Environment: Restricts browser capabilities
  • Secure WebRTC: Encrypted media streams

Troubleshooting

Common issues and solutions:

Browser Creation Fails

  • Check resource limits in Google Cloud Run
  • Verify Puppeteer configuration
  • Check for errors in container logs

Streaming Issues

  • Verify PeerJS server configuration
  • Check WebRTC compatibility
  • Ensure game content is properly rendered

High Resource Usage

  • Adjust browser pool size
  • Decrease idle timeout
  • Optimize Puppeteer memory usage

Contributing

Contributions to the OGS Casting Renderer are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contact

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published