Skip to content

ForkbombEu/avdstream

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AVD Stream

Real-time Android device screen streaming to browser using scrcpy, v4l2loopback, and MJPEG.

Quick Start

# Using Taskfile (recommended)
task run

# Or build and run manually
go build -o avdstream .
./avdstream -device emulator-5554

# Open http://localhost:8000

Current Implementation

This project uses a modern Go-based streaming solution:

  1. scrcpy captures Android screen and pushes YUV420 frames to /dev/video20 (v4l2loopback virtual camera)
  2. ffmpeg reads from /dev/video20 and encodes to MJPEG
  3. Go HTTP server with broadcaster pattern distributes frames to multiple clients
  4. Browser displays MJPEG stream with low latency (~30fps)

Key Features

  • Low latency: ~100-300ms delay (much better than HLS)
  • Multi-client support: Broadcaster pattern allows multiple viewers without restarting the stream
  • Automatic lifecycle: ffmpeg starts on first client, stops 5s after last client disconnects
  • Embedded web UI: No external HTTP server needed
  • Continuous streaming: No 3-minute limit like adb screenrecord

Architecture

┌─────────────┐
│   Android   │
│   Device    │
└──────┬──────┘
       │ scrcpy (YUV420 30fps)
       ▼
┌─────────────┐
│ /dev/video20│ v4l2loopback virtual camera
└──────┬──────┘
       │ reads frames
       ▼
┌─────────────┐
│   ffmpeg    │ Encode to MJPEG
└──────┬──────┘
       │ stdout pipe
       ▼
┌─────────────┐
│ Broadcaster │ Parse JPEG frames, distribute to clients
│  (main.go)  │
└──────┬──────┘
       │ HTTP /mjpeg (multipart/x-mixed-replace)
       ▼
┌─────────────┐
│   Browser   │ Native MJPEG support
│ (index.html)│
└─────────────┘

Requirements

System Dependencies

  • Go 1.25.3 or later
  • scrcpy (for Android screen mirroring)
  • ffmpeg with H.264 and MJPEG support
  • v4l2loopback kernel module (Linux only)
  • adb (Android Debug Bridge)
  • Running Android device or emulator

Setup v4l2loopback

# Install v4l2loopback (Ubuntu/Debian)
sudo apt install v4l2loopback-dkms

# Configure and load module
task setup

# Or manually:
sudo modprobe v4l2loopback video_nr=20 card_label="LoopbackCam" exclusive_caps=0

Usage

With Taskfile

# Run with default device (emulator-5554)
task run

# Run with specific device
task run DEVICE=emulator-5580

# Run without starting scrcpy (if already running)
task run-no-scrcpy

# Development mode (go run)
task dev

# List available tasks
task --list

Direct Binary Usage

# Build
go build -o avdstream .

# Run with default device
./avdstream

# Specify device
./avdstream -device emulator-5580

# Run without auto-starting scrcpy
./avdstream -no-scrcpy

Configuration

The application accepts the following flags:

  • -device: Android device serial (default: emulator-5554)
  • -no-scrcpy: Don't start scrcpy automatically

Default port is :8000. Access the stream at http://localhost:8000.

Project Structure

avdstream/
├── main.go           # Go server with broadcaster and ffmpeg integration
├── go.mod            # Go module definition
├── Taskfile.yml      # Task runner configuration
├── mise.toml         # Tool version management
├── www/
│   ├── index.html    # MJPEG player with status monitoring
│   ├── old.html      # Legacy HLS player
│   └── brutto.html   # Alternative player
└── README.md

How It Works

Broadcaster Pattern (main.go:20-210)

The Broadcaster struct manages:

  • Single ffmpeg instance for all clients
  • Frame parsing from MJPEG stream (JPEG SOI 0xFFD8 to EOI 0xFFD9)
  • Distribution to subscribed clients via channels
  • Automatic start/stop based on client count

MJPEG Handler (main.go:212-251)

Serves multipart/x-mixed-replace stream:

  1. Client connects to /mjpeg
  2. Broadcaster starts ffmpeg if not running
  3. Client subscribes to frame channel
  4. Each JPEG frame sent with MIME boundary
  5. Client disconnects, broadcaster stops after 5s if no other clients

Web Interface (www/index.html)

Simple MJPEG player with:

  • Connection status indicator
  • FPS counter
  • Automatic reconnection on errors
  • Low-latency display

Troubleshooting

No video appears:

# Check Android device is connected
adb devices

# Verify /dev/video20 exists
ls -l /dev/video20

# Test v4l2loopback
task test-stream

# Check scrcpy works
scrcpy -s emulator-5554 --no-playback --v4l2-sink=/dev/video20

Stream is black or frozen:

# Restart v4l2loopback module
sudo modprobe -r v4l2loopback
sudo modprobe v4l2loopback video_nr=20 card_label="LoopbackCam" exclusive_caps=0

# Check ffmpeg can read from video20
ffmpeg -f v4l2 -i /dev/video20 -frames:v 1 test.jpg

Port already in use:

# Check what's using port 8000
sudo lsof -i :8000

# Kill existing process
pkill avdstream

v4l2loopback not available (macOS/Windows):

This solution currently requires Linux with v4l2loopback. For other platforms, consider:

  • Running in a Linux VM or container
  • Using the legacy bash/HLS solution (see git history)

Performance Notes

  • Resolution: 1080x2400 downscaled to 540x1200 for streaming
  • Frame rate: 30fps (configurable in main.go:52)
  • Latency: ~100-300ms (vs 2-4s for HLS)
  • MJPEG quality: q:v 3 (configurable in main.go:59)
  • Multi-client: No performance impact, same ffmpeg instance shared

Development

# Run in development mode
task dev

# Build binary
task build

# Clean build artifacts
task clean

# Check available devices
task check-device

License

MIT

About

Real-time Android device screen streaming to browser using scrcpy, v4l2loopback, and MJPEG.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published