Skip to main content

Overview

Hyperscape captures game music and sound effects for RTMP streams using PulseAudio virtual sinks. This allows viewers to hear the game audio alongside video.

Architecture

Key Components:
  1. PulseAudio - Audio server with virtual sink
  2. chrome_audio sink - Virtual audio device for Chrome output
  3. chrome_audio.monitor - Monitor device for FFmpeg capture
  4. FFmpeg - Captures from monitor and encodes to AAC

PulseAudio Setup

Installation

User Mode Configuration

Why user mode:
  • More reliable than system mode
  • Better permissions handling
  • Easier debugging
Setup:

Virtual Sink Creation

Create chrome_audio sink:
Verify sink exists:

Permissions

Add user to pulse-access group:
Create /run/pulse directory:
Export PULSE_SERVER:

Chrome Configuration

Audio Output

Chrome automatically uses the default PulseAudio sink when launched with PULSE_SERVER set:
No additional Chrome flags needed - PulseAudio integration is automatic.

FFmpeg Configuration

Audio Input

Capture from PulseAudio monitor:
Parameters:
  • thread_queue_size 1024 - Prevent buffer underruns
  • use_wallclock_as_timestamps 1 - Real-time timing
  • -f pulse - PulseAudio input format
  • -ac 2 - Stereo audio (2 channels)
  • -ar 44100 - Sample rate (44.1 kHz)
  • chrome_audio.monitor - Monitor device (captures sink output)

Audio Processing

Async resampling for drift recovery:
Why:
  • Recovers from audio drift when video/audio desync
  • async=1000 - Resample if drift exceeds 1000 samples (22ms at 44.1kHz)
  • first_pts=0 - Reset PTS to prevent timestamp issues

Audio Encoding

Parameters:
  • -c:a aac - AAC codec (required for RTMP)
  • -b:a 128k - Audio bitrate (128 kbps default)
  • -ar 44100 - Output sample rate
  • -flags +global_header - Required for RTMP/FLV

Environment Variables

Graceful Fallback

If PulseAudio is not available, FFmpeg automatically falls back to silent audio:
Why silent fallback:
  • Many RTMP servers require an audio track
  • Silent audio prevents stream rejection
  • Better than failing the entire stream

Troubleshooting

PulseAudio Not Running

Check status:
Start PulseAudio:
Check logs:

chrome_audio Sink Missing

List sinks:
Create sink manually:
Set as default:

No Audio in Stream

Check FFmpeg is capturing:
Test audio capture:
Check Chrome is outputting audio:

Audio Dropouts

Symptoms:
  • Intermittent audio cutting out
  • Audio desync from video
  • Crackling or stuttering
Fixes: 1. Increase buffer size:
2. Check for buffer underruns:
3. Increase thread queue size:
4. Enable async resampling:

Audio/Video Desync

Symptoms:
  • Audio ahead or behind video
  • Lip sync issues
  • Audio drift over time
Fixes: 1. Use wall clock timestamps:
2. Enable async resampling:
3. Match video buffer size:
4. Remove -shortest flag:

Permission Errors

Error:
Fix:
Error:
Fix:

Audio Quality Settings

Bitrate

Default: 128 kbps (good quality, low bandwidth)

Sample Rate

Default: 44.1 kHz (CD quality)

Channels

Default: Stereo (2 channels)

Monitoring

Check PulseAudio Status

Check Audio Levels

FFmpeg Audio Stats

Performance Impact

CPU Usage

PulseAudio: ~1-2% CPU FFmpeg AAC encoding: ~5-10% CPU per stream Total audio overhead: ~10-15% CPU

Memory Usage

PulseAudio: ~50-100 MB RAM FFmpeg audio buffers: ~10-20 MB RAM Total audio overhead: ~100-150 MB RAM

Bandwidth

Audio bitrate: 128 kbps = 16 KB/s = ~1 MB/minute Impact on total stream:
  • Video: 4500 kbps (default)
  • Audio: 128 kbps
  • Total: 4628 kbps (~3% overhead)

Disabling Audio

To disable audio capture and use silent audio:
FFmpeg will use silent audio source:
When to disable:
  • Debugging audio issues
  • Reducing CPU usage
  • Testing video-only streams
  • Copyright concerns (music in game)

Advanced Configuration

Custom PulseAudio Config

Create custom ~/.config/pulse/default.pa:

Audio Filters

Normalize audio levels:
Reduce noise:
Compress dynamic range:

Multiple Audio Sources

Mix game audio + microphone:

Deployment Integration

Vast.ai Deployment

The deploy-vast.sh script automatically configures PulseAudio:

PM2 Environment

Export for PM2:

Audio Stability Improvements

Buffer Configuration (Commit b9d2e41)

Three key changes to prevent audio dropouts: 1. Buffer both audio and video adequately:
2. Use wall clock timestamps:
3. Async resampling for drift recovery:
4. Remove -shortest flag:

Timing Synchronization

Problem: Audio and video can drift over time, causing desync. Solution: Three-layer sync strategy: 1. Wall clock timestamps:
Ensures PulseAudio uses real-time clock for timestamps. 2. Async resampling:
Resamples audio when drift exceeds threshold. 3. Matched buffer sizes:
Prevents one stream from getting ahead of the other.

Testing

Test Audio Capture

Record 5 seconds:
Play back:
Check for audio:

Test Full Pipeline

1. Start PulseAudio:
2. Play test audio to sink:
3. Capture from monitor:
4. Verify audio was captured:

Monitor Live Stream

Check FFmpeg audio stats:
Expected output:

Common Issues

No Audio in Stream

Checklist:
  1. ✅ PulseAudio running: pulseaudio --check
  2. ✅ chrome_audio sink exists: pactl list short sinks | grep chrome_audio
  3. ✅ PULSE_SERVER set: echo $PULSE_SERVER
  4. ✅ FFmpeg using pulse input: pm2 logs | grep pulse
  5. ✅ Chrome outputting audio: pactl list sink-inputs
Debug:

Audio Crackling

Causes:
  • Buffer underruns
  • CPU overload
  • Sample rate mismatch
Fixes:

Audio Ahead of Video

Cause: Audio processing faster than video encoding Fix:

Permission Denied

Error:
Fix: