diff --git a/README.md b/README.md index 19e7977..2bd2e9f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,173 @@ # x11stream -Shell script on Ubuntu to auto-stream on boot using x11grab and ffmpeg + +Shell script on Ubuntu to auto-stream X11 display on boot using x11grab and ffmpeg. + +## Features + +- Captures X11 display using ffmpeg with low-latency settings +- Hosts an HTTP server for direct browser/VLC access +- Auto-starts on boot via systemd service +- **Interactive mode** for easy configuration +- **Audio streaming support** with multiple quality presets +- Configurable resolution, framerate, bitrate, and audio settings +- Bandwidth estimation for all configurations + +## Requirements + +- Ubuntu (or other Linux with X11) +- ffmpeg with x11grab support +- PulseAudio or ALSA (for audio streaming) +- systemd (for auto-start on boot) + +Install ffmpeg: +```bash +sudo apt update +sudo apt install ffmpeg +``` + +## Installation + +1. Clone this repository: +```bash +git clone https://github.com/maple-underscore/x11stream.git +cd x11stream +``` + +2. Install the script: +```bash +sudo cp x11stream.sh /usr/local/bin/ +sudo chmod +x /usr/local/bin/x11stream.sh +``` + +3. Install and enable the systemd service: +```bash +sudo cp x11stream.service /etc/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl enable x11stream.service +sudo systemctl start x11stream.service +``` + +## Usage + +### Interactive Mode +Run in interactive mode to configure all settings through a menu: +```bash +./x11stream.sh --interactive +# or +./x11stream.sh -i +``` + +### Non-Interactive Mode +```bash +./x11stream.sh +``` + +### Access the Stream + +Once the script is running, access the stream via: + +- **Browser**: `http://:8080/stream` +- **VLC**: Open Network Stream → `http://:8080/stream` + +### Service Management +```bash +# Start the service +sudo systemctl start x11stream.service + +# Stop the service +sudo systemctl stop x11stream.service + +# Check status +sudo systemctl status x11stream.service + +# View logs +sudo journalctl -u x11stream.service -f +``` + +## Configuration + +### Environment Variables + +The following environment variables can be set to customize the stream: + +| Variable | Default | Description | +|-------------------|--------------|--------------------------------| +| DISPLAY | :0.0 | X11 display to capture | +| RESOLUTION | 1920x1080 | Capture resolution | +| FRAMERATE | 60 | Frames per second | +| BITRATE | 6M | Video bitrate | +| HTTP_PORT | 8080 | HTTP server port | +| AUDIO_ENABLED | false | Enable audio streaming | +| AUDIO_BITRATE | 128 | Audio bitrate (kbps) | +| AUDIO_CODEC | aac | Audio codec (aac, mp3, pcm) | +| AUDIO_SAMPLE_RATE | 44100 | Sample rate for PCM audio | +| AUDIO_BIT_DEPTH | 16 | Bit depth for PCM audio | + +### Audio Quality Presets + +When using interactive mode, you can choose from these audio presets: + +#### Lossy Compression (AAC/MP3) +| Preset | Bitrate | Quality | Bandwidth | +|--------|-----------|-----------------|---------------| +| 1 | 64 kbps | Voice/Low | ~8 KB/s | +| 2 | 128 kbps | Standard | ~16 KB/s | +| 3 | 192 kbps | Good | ~24 KB/s | +| 4 | 256 kbps | High | ~32 KB/s | +| 5 | 320 kbps | Maximum | ~40 KB/s | + +#### Lossless PCM (16-bit) +| Preset | Sample Rate | Quality | Bandwidth | +|--------|-------------|---------------|---------------| +| 6 | 44.1 kHz | CD quality | ~172 KB/s | +| 7 | 48 kHz | DVD quality | ~188 KB/s | +| 8 | 96 kHz | Hi-Res | ~375 KB/s | +| 9 | 192 kHz | Ultra Hi-Res | ~750 KB/s | + +#### Lossless PCM (24-bit) +| Preset | Sample Rate | Quality | Bandwidth | +|--------|-------------|--------------------|---------------| +| 10 | 44.1 kHz | Studio | ~258 KB/s | +| 11 | 48 kHz | Professional | ~281 KB/s | +| 12 | 96 kHz | Hi-Res Studio | ~563 KB/s | +| 13 | 192 kHz | Ultra Hi-Res Studio| ~1125 KB/s | + +### Video Quality Presets + +| Bitrate | Quality | Bandwidth | +|---------|-------------------|---------------| +| 2M | Low bandwidth | ~250 KB/s | +| 4M | Medium quality | ~500 KB/s | +| 6M | Good quality | ~750 KB/s | +| 10M | High quality | ~1.25 MB/s | +| 15M | Very high | ~1.9 MB/s | +| 20M | Excellent | ~2.5 MB/s | + +To modify these settings permanently, edit the service file: +```bash +sudo systemctl edit x11stream.service +``` + +Or edit `/etc/systemd/system/x11stream.service` directly. + +## Troubleshooting + +### "Display not found" error +Ensure X11 is running and the `DISPLAY` variable is set correctly. On systems with multiple displays, try `:1.0` or `:0.1`. + +### "Permission denied" error +The user running the script needs access to the X11 display. Run with appropriate permissions or add the user to the video group. + +### Stream not accessible +- Check if ffmpeg is running: `ps aux | grep ffmpeg` +- Ensure the port is not blocked by firewall: `sudo ufw allow 8080/tcp` +- Verify the IP address and port in the startup output + +### Audio not working +- Ensure PulseAudio or ALSA is running +- Check audio source: `pactl list short sources` (PulseAudio) +- Try different audio source in interactive mode + +## License + +MIT License diff --git a/x11stream.service b/x11stream.service new file mode 100644 index 0000000..383d3b7 --- /dev/null +++ b/x11stream.service @@ -0,0 +1,21 @@ +[Unit] +Description=X11 Display Stream Server +After=network.target display-manager.service +Wants=graphical.target + +[Service] +Type=simple +Environment="DISPLAY=:0.0" +Environment="RESOLUTION=1920x1080" +Environment="FRAMERATE=60" +Environment="BITRATE=6M" +Environment="HTTP_PORT=8080" +ExecStart=/usr/local/bin/x11stream.sh +Restart=on-failure +RestartSec=5 +# Note: Running as root for X11 access. For better security, create a dedicated +# user in the 'video' group with X11 display access and update this line. +User=root + +[Install] +WantedBy=graphical.target diff --git a/x11stream.sh b/x11stream.sh new file mode 100755 index 0000000..9624332 --- /dev/null +++ b/x11stream.sh @@ -0,0 +1,365 @@ +#!/bin/bash +# +# x11stream.sh - Stream X11 display via HTTP using ffmpeg +# +# This script captures the X11 display and makes it available via HTTP +# Access the stream at: http://{ip}:8080/stream +# +# Run with --interactive or -i for interactive mode +# Run without arguments for non-interactive mode using environment variables +# + +set -e + +# ============================================ +# Audio Presets with Bandwidth Estimates +# ============================================ +# MP3/AAC Bitrate Options: +# 64 kbps - Low quality voice (~8 KB/s) +# 128 kbps - Standard quality (~16 KB/s) +# 192 kbps - Good quality (~24 KB/s) +# 256 kbps - High quality (~32 KB/s) +# 320 kbps - Maximum MP3 quality (~40 KB/s) +# +# PCM/Lossless Options (sample rate @ bit depth): +# 16-bit @ 44.1 kHz - CD quality (~172 KB/s stereo) +# 16-bit @ 48 kHz - DVD quality (~188 KB/s stereo) +# 16-bit @ 96 kHz - Hi-Res (~375 KB/s stereo) +# 16-bit @ 192 kHz - Ultra Hi-Res (~750 KB/s stereo) +# 24-bit @ 44.1 kHz - Studio quality (~258 KB/s stereo) +# 24-bit @ 48 kHz - Professional (~281 KB/s stereo) +# 24-bit @ 96 kHz - Hi-Res Studio (~563 KB/s stereo) +# 24-bit @ 192 kHz - Ultra Hi-Res Studio (~1125 KB/s stereo) +# ============================================ + +# Default configuration +DISPLAY_NUM="${DISPLAY:-:0.0}" +RESOLUTION="${RESOLUTION:-1920x1080}" +FRAMERATE="${FRAMERATE:-60}" +VIDEO_BITRATE="${BITRATE:-6M}" +HTTP_PORT="${HTTP_PORT:-8080}" +AUDIO_ENABLED="${AUDIO_ENABLED:-false}" +AUDIO_BITRATE="${AUDIO_BITRATE:-128}" +AUDIO_SAMPLE_RATE="${AUDIO_SAMPLE_RATE:-44100}" +AUDIO_BIT_DEPTH="${AUDIO_BIT_DEPTH:-16}" +AUDIO_CODEC="${AUDIO_CODEC:-aac}" +AUDIO_SOURCE="${AUDIO_SOURCE:-default}" +INTERACTIVE_MODE=false + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -i|--interactive) + INTERACTIVE_MODE=true + shift + ;; + -h|--help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -i, --interactive Run in interactive mode" + echo " -h, --help Show this help message" + echo "" + echo "Environment variables (non-interactive mode):" + echo " DISPLAY X11 display (default: :0.0)" + echo " RESOLUTION Video resolution (default: 1920x1080)" + echo " FRAMERATE Frames per second (default: 60)" + echo " BITRATE Video bitrate (default: 6M)" + echo " HTTP_PORT HTTP server port (default: 8080)" + echo " AUDIO_ENABLED Enable audio (default: false)" + echo " AUDIO_BITRATE Audio bitrate in kbps (default: 128)" + echo " AUDIO_CODEC Audio codec: aac, mp3, pcm (default: aac)" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Detect the machine's IP address +# Uses 'ip route get 1' to determine the primary interface IP via the default route +get_ip() { + ip route get 1 2>/dev/null | awk '{print $7; exit}' || hostname -I | awk '{print $1}' +} + +IP_ADDRESS=$(get_ip) + +# Validate IP address +if [ -z "$IP_ADDRESS" ]; then + echo "Error: Unable to determine IP address for this host." >&2 + echo "Please ensure that 'ip route' or 'hostname -I' is available, or set IP_ADDRESS manually." >&2 + exit 1 +fi + +# Calculate bandwidth estimate +calculate_bandwidth() { + local video_bps audio_bps total_kbps + + # Parse video bitrate (e.g., "6M" -> 6000 kbps) + case "$VIDEO_BITRATE" in + *M) video_bps=$((${VIDEO_BITRATE%M} * 1000)) ;; + *k) video_bps=${VIDEO_BITRATE%k} ;; + *) video_bps=$VIDEO_BITRATE ;; # Assume kbps if no suffix + esac + + # Calculate audio bandwidth in kbps + if [ "$AUDIO_ENABLED" = "true" ]; then + if [ "$AUDIO_CODEC" = "pcm" ]; then + # PCM: sample_rate * bit_depth * channels / 1000 = kbps + audio_bps=$((AUDIO_SAMPLE_RATE * AUDIO_BIT_DEPTH * 2 / 1000)) + else + audio_bps=$AUDIO_BITRATE + fi + else + audio_bps=0 + fi + + total_kbps=$((video_bps + audio_bps)) + echo "$total_kbps kbps (~$((total_kbps / 8)) KB/s)" +} + +# Show audio presets menu +show_audio_presets() { + echo "" + echo "Audio Quality Presets:" + echo "============================================" + echo "Lossy Compression (AAC/MP3):" + echo " 1) 64 kbps - Voice/Low quality (~8 KB/s)" + echo " 2) 128 kbps - Standard quality (~16 KB/s)" + echo " 3) 192 kbps - Good quality (~24 KB/s)" + echo " 4) 256 kbps - High quality (~32 KB/s)" + echo " 5) 320 kbps - Maximum quality (~40 KB/s)" + echo "" + echo "Lossless PCM (16-bit):" + echo " 6) 16-bit @ 44.1 kHz - CD quality (~172 KB/s)" + echo " 7) 16-bit @ 48 kHz - DVD quality (~188 KB/s)" + echo " 8) 16-bit @ 96 kHz - Hi-Res (~375 KB/s)" + echo " 9) 16-bit @ 192 kHz - Ultra Hi-Res (~750 KB/s)" + echo "" + echo "Lossless PCM (24-bit):" + echo " 10) 24-bit @ 44.1 kHz - Studio (~258 KB/s)" + echo " 11) 24-bit @ 48 kHz - Professional (~281 KB/s)" + echo " 12) 24-bit @ 96 kHz - Hi-Res Studio (~563 KB/s)" + echo " 13) 24-bit @ 192 kHz - Ultra Hi-Res (~1125 KB/s)" + echo "============================================" +} + +# Apply audio preset +apply_audio_preset() { + case $1 in + 1) AUDIO_CODEC="aac"; AUDIO_BITRATE=64 ;; + 2) AUDIO_CODEC="aac"; AUDIO_BITRATE=128 ;; + 3) AUDIO_CODEC="aac"; AUDIO_BITRATE=192 ;; + 4) AUDIO_CODEC="aac"; AUDIO_BITRATE=256 ;; + 5) AUDIO_CODEC="aac"; AUDIO_BITRATE=320 ;; + 6) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=16; AUDIO_SAMPLE_RATE=44100 ;; + 7) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=16; AUDIO_SAMPLE_RATE=48000 ;; + 8) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=16; AUDIO_SAMPLE_RATE=96000 ;; + 9) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=16; AUDIO_SAMPLE_RATE=192000 ;; + 10) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=24; AUDIO_SAMPLE_RATE=44100 ;; + 11) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=24; AUDIO_SAMPLE_RATE=48000 ;; + 12) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=24; AUDIO_SAMPLE_RATE=96000 ;; + 13) AUDIO_CODEC="pcm"; AUDIO_BIT_DEPTH=24; AUDIO_SAMPLE_RATE=192000 ;; + *) echo "Invalid selection, using default (128 kbps AAC)"; AUDIO_CODEC="aac"; AUDIO_BITRATE=128 ;; + esac +} + +# Interactive configuration +interactive_setup() { + echo "============================================" + echo "X11 Stream Server - Interactive Setup" + echo "============================================" + echo "" + + # Display selection + echo "Current X11 display: $DISPLAY_NUM" + read -p "Enter display (press Enter for default): " input + [ -n "$input" ] && DISPLAY_NUM="$input" + + # Resolution selection + echo "" + echo "Resolution Options:" + echo " 1) 1920x1080 (Full HD)" + echo " 2) 2560x1440 (2K/QHD)" + echo " 3) 3840x2160 (4K/UHD)" + echo " 4) 1280x720 (HD)" + echo " 5) 1366x768 (HD+)" + echo " 6) Custom" + read -p "Select resolution [1-6] (default: 1): " res_choice + case $res_choice in + 2) RESOLUTION="2560x1440" ;; + 3) RESOLUTION="3840x2160" ;; + 4) RESOLUTION="1280x720" ;; + 5) RESOLUTION="1366x768" ;; + 6) read -p "Enter custom resolution (e.g., 1920x1080): " RESOLUTION ;; + *) RESOLUTION="1920x1080" ;; + esac + + # Framerate selection + echo "" + echo "Framerate Options:" + echo " 1) 24 fps (Film)" + echo " 2) 30 fps (Standard)" + echo " 3) 60 fps (Smooth)" + echo " 4) 120 fps (High refresh)" + echo " 5) 144 fps (Gaming)" + echo " 6) Custom" + read -p "Select framerate [1-6] (default: 3): " fps_choice + case $fps_choice in + 1) FRAMERATE=24 ;; + 2) FRAMERATE=30 ;; + 4) FRAMERATE=120 ;; + 5) FRAMERATE=144 ;; + 6) read -p "Enter custom framerate: " FRAMERATE ;; + *) FRAMERATE=60 ;; + esac + + # Video quality/bitrate selection + echo "" + echo "Video Quality Options:" + echo " 1) 2M - Low bandwidth (~250 KB/s)" + echo " 2) 4M - Medium quality (~500 KB/s)" + echo " 3) 6M - Good quality (~750 KB/s)" + echo " 4) 10M - High quality (~1.25 MB/s)" + echo " 5) 15M - Very high (~1.9 MB/s)" + echo " 6) 20M - Excellent (~2.5 MB/s)" + echo " 7) Custom" + read -p "Select video quality [1-7] (default: 3): " quality_choice + case $quality_choice in + 1) VIDEO_BITRATE="2M" ;; + 2) VIDEO_BITRATE="4M" ;; + 4) VIDEO_BITRATE="10M" ;; + 5) VIDEO_BITRATE="15M" ;; + 6) VIDEO_BITRATE="20M" ;; + 7) read -p "Enter custom bitrate (e.g., 6M or 6000k): " VIDEO_BITRATE ;; + *) VIDEO_BITRATE="6M" ;; + esac + + # Audio configuration + echo "" + read -p "Enable audio streaming? [y/N]: " audio_choice + if [[ "$audio_choice" =~ ^[Yy]$ ]]; then + AUDIO_ENABLED="true" + + show_audio_presets + read -p "Select audio preset [1-13] (default: 2): " audio_preset + [ -z "$audio_preset" ] && audio_preset=2 + apply_audio_preset "$audio_preset" + + # Audio source + echo "" + echo "Audio Source:" + echo " 1) PulseAudio default" + echo " 2) ALSA default" + echo " 3) Custom" + read -p "Select audio source [1-3] (default: 1): " source_choice + case $source_choice in + 2) AUDIO_SOURCE="hw:0" ;; + 3) read -p "Enter audio source: " AUDIO_SOURCE ;; + *) AUDIO_SOURCE="default" ;; + esac + else + AUDIO_ENABLED="false" + fi + + # HTTP Port + echo "" + read -p "HTTP port (default: 8080): " port_input + [ -n "$port_input" ] && HTTP_PORT="$port_input" + + echo "" + echo "============================================" + echo "Configuration Summary" + echo "============================================" +} + +# Run interactive setup if requested +if [ "$INTERACTIVE_MODE" = "true" ]; then + interactive_setup +fi + +# Build audio arguments +build_audio_args() { + if [ "$AUDIO_ENABLED" = "true" ]; then + local audio_input audio_codec_args + + # Input source + if [ "$AUDIO_SOURCE" = "default" ]; then + audio_input="-f pulse -i default" + else + audio_input="-f alsa -i $AUDIO_SOURCE" + fi + + # Codec settings + if [ "$AUDIO_CODEC" = "pcm" ]; then + local fmt + if [ "$AUDIO_BIT_DEPTH" = "24" ]; then + fmt="s24le" + else + fmt="s16le" + fi + audio_codec_args="-c:a pcm_${fmt} -ar $AUDIO_SAMPLE_RATE" + else + audio_codec_args="-c:a $AUDIO_CODEC -b:a ${AUDIO_BITRATE}k" + fi + + echo "$audio_input $audio_codec_args" + fi +} + +# Display configuration +echo "Display: $DISPLAY_NUM" +echo "Resolution: $RESOLUTION" +echo "Framerate: $FRAMERATE fps" +echo "Video: $VIDEO_BITRATE" +if [ "$AUDIO_ENABLED" = "true" ]; then + if [ "$AUDIO_CODEC" = "pcm" ]; then + echo "Audio: PCM ${AUDIO_BIT_DEPTH}-bit @ ${AUDIO_SAMPLE_RATE} Hz" + else + audio_codec_upper=$(echo "$AUDIO_CODEC" | tr '[:lower:]' '[:upper:]') + echo "Audio: ${audio_codec_upper} @ ${AUDIO_BITRATE} kbps" + fi +else + echo "Audio: Disabled" +fi +echo "HTTP Port: $HTTP_PORT" +echo "" +echo "Estimated bandwidth: $(calculate_bandwidth)" +echo "" +echo "Access your stream at:" +echo " http://${IP_ADDRESS}:${HTTP_PORT}/stream" +echo " http://localhost:${HTTP_PORT}/stream" +echo "============================================" + +# Build and execute ffmpeg command +AUDIO_ARGS=$(build_audio_args) + +# Run ffmpeg with x11grab and output to HTTP via mpegts +# The -listen 1 flag makes ffmpeg act as an HTTP server +# Low-latency settings: +# -probesize 32768: Minimal probe size (32KB) for faster stream start +# -analyzeduration 0: Skip analysis delay for immediate processing +if [ "$AUDIO_ENABLED" = "true" ]; then + exec ffmpeg -fflags nobuffer -flags low_delay -probesize 32768 -analyzeduration 0 \ + -f x11grab -video_size "$RESOLUTION" -framerate "$FRAMERATE" -i "$DISPLAY_NUM" \ + $AUDIO_ARGS \ + -c:v libx264 -preset ultrafast -tune zerolatency \ + -x264-params "bframes=0:rc-lookahead=0:sync-lookahead=0:sliced-threads=1" \ + -g 15 -keyint_min 15 -sc_threshold 0 \ + -b:v "$VIDEO_BITRATE" -maxrate "$VIDEO_BITRATE" -bufsize 256k \ + -pix_fmt yuv420p \ + -f mpegts \ + -listen 1 "http://0.0.0.0:${HTTP_PORT}/stream" +else + exec ffmpeg -fflags nobuffer -flags low_delay -probesize 32768 -analyzeduration 0 \ + -f x11grab -video_size "$RESOLUTION" -framerate "$FRAMERATE" -i "$DISPLAY_NUM" \ + -c:v libx264 -preset ultrafast -tune zerolatency \ + -x264-params "bframes=0:rc-lookahead=0:sync-lookahead=0:sliced-threads=1" \ + -g 15 -keyint_min 15 -sc_threshold 0 \ + -b:v "$VIDEO_BITRATE" -maxrate "$VIDEO_BITRATE" -bufsize 256k \ + -pix_fmt yuv420p \ + -f mpegts \ + -listen 1 "http://0.0.0.0:${HTTP_PORT}/stream" +fi