Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 20, 2026

Creates a shell script to capture X11 display via ffmpeg and serve it over HTTP for browser/VLC access, with systemd integration for auto-start on boot. Includes interactive configuration mode and audio streaming support.

Changes

  • x11stream.sh: Captures X11 display using ffmpeg x11grab with low-latency settings, serves via HTTP using ffmpeg's -listen 1 mode. Supports interactive mode (-i) for easy configuration of resolution, framerate, video quality, and audio settings.
  • x11stream.service: systemd unit for auto-start after display manager, configurable via environment variables
  • README.md: Installation steps, configuration options, audio presets with bandwidth estimates, troubleshooting guide

Features

  • Interactive Mode: Run with -i or --interactive to configure settings through menus
  • Audio Streaming: Support for multiple audio quality presets with bandwidth estimates:
    • Lossy (AAC): 64, 128, 192, 256, 320 kbps
    • Lossless 16-bit PCM: 44.1kHz, 48kHz, 96kHz, 192kHz
    • Lossless 24-bit PCM: 44.1kHz, 48kHz, 96kHz, 192kHz
  • Bandwidth Estimation: Displays total estimated bandwidth at startup

Usage

# Install
sudo cp x11stream.sh /usr/local/bin/
sudo cp x11stream.service /etc/systemd/system/
sudo systemctl enable --now x11stream.service

# Run interactively
./x11stream.sh -i

# Access stream
vlc http://<ip>:8080/stream

Configuration

Environment variables: DISPLAY, RESOLUTION, FRAMERATE, BITRATE, HTTP_PORT, AUDIO_ENABLED, AUDIO_BITRATE, AUDIO_CODEC, AUDIO_SAMPLE_RATE, AUDIO_BIT_DEPTH

Original prompt

create a shell script that runs this command


ffmpeg -fflags nobuffer -flags low_delay -probesize 32 -analyzeduration 0 \
  -f x11grab -video_size 1920x1080 -framerate 30 -i :0.0 \
  -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 6M -maxrate 6M -bufsize 256k \
  -pix_fmt yuv420p \
  -f mpegts "udp://{ip}:1234?pkt_size=1316&overrun_nonfatal=1"

on boot, and hosts an http server. the machine should stream to its own http server which can be accessed through browser or vlc media by http://{ip}/stream.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: maple-underscore <102273486+maple-underscore@users.noreply.github.com>
Copilot AI changed the title [WIP] Add shell script for FFmpeg streaming on boot Add x11stream shell script and systemd service for X11 display streaming Jan 20, 2026
@maple-underscore maple-underscore marked this pull request as ready for review January 20, 2026 18:09
Copilot AI review requested due to automatic review settings January 20, 2026 18:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a complete solution for streaming an X11 display over HTTP using ffmpeg. It provides a shell script that captures the X11 display with configurable low-latency settings, a systemd service for automatic startup, and comprehensive documentation.

Changes:

  • Added x11stream.sh: Shell script that uses ffmpeg x11grab to capture the display and serve it via HTTP
  • Added x11stream.service: systemd unit file for auto-starting the stream service on boot
  • Added README.md: Complete documentation with installation, configuration, and troubleshooting instructions

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
x11stream.sh Shell script implementing the X11 capture and HTTP streaming functionality with configurable parameters
x11stream.service systemd service configuration for automatic startup with environment variable overrides
README.md User-facing documentation covering installation, usage, configuration options, and troubleshooting

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

README.md Outdated
|-------------|--------------|--------------------------------|
| DISPLAY | :0.0 | X11 display to capture |
| RESOLUTION | 1920x1080 | Capture resolution |
| FRAMERATE | 30 | Frames per second |
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default framerate documented here is 30, but the actual default in x11stream.sh is 60 (line 14) and in x11stream.service is 60 (line 10). This inconsistency will confuse users about the actual default value.

Suggested change
| FRAMERATE | 30 | Frames per second |
| FRAMERATE | 60 | Frames per second |

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +18
# 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
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running the service as root presents a significant security risk. The X11 server typically allows local users to connect to it, so running as a dedicated user with appropriate group membership (video, or the user running the X session) would be more secure. Consider creating a dedicated user or running as the display owner instead of root.

Suggested change
# 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
# Note: For better security, this service runs as a dedicated user in the 'video'
# group with X11 display access. Ensure the 'x11stream' user and group exist.
User=x11stream
Group=x11stream
SupplementaryGroups=video

Copilot uses AI. Check for mistakes.
x11stream.sh Outdated
-b:v "$BITRATE" -maxrate "$BITRATE" -bufsize 256k \
-pix_fmt yuv420p \
-f mpegts \
-listen 1 "http://0.0.0.0:${HTTP_PORT}/stream"
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTTP server binds to 0.0.0.0, making it accessible from any network interface without authentication. This exposes the X11 display stream to anyone who can reach the port on the network. Consider adding authentication or at minimum binding to localhost (127.0.0.1) if remote access isn't required, or documenting the security implications clearly.

Copilot uses AI. Check for mistakes.
User=root

[Install]
WantedBy=multi-user.target
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service is configured to start in multi-user.target which typically runs before the graphical.target, but the service depends on display-manager.service and wants graphical.target. This can lead to timing issues where the service starts before the X11 display is fully ready. Consider changing WantedBy to graphical.target to ensure proper ordering.

Suggested change
WantedBy=multi-user.target
WantedBy=graphical.target

Copilot uses AI. Check for mistakes.
x11stream.sh Outdated
Comment on lines 43 to 45
# -probesize 32: Minimal probe size (bytes) for faster stream start
# -analyzeduration 0: Skip analysis delay for immediate processing
exec ffmpeg -fflags nobuffer -flags low_delay -probesize 32 -analyzeduration 0 \
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states that probe size is in bytes, but the ffmpeg -probesize parameter actually expects a value in bytes and the value 32 here means 32 bytes, which is extremely small and likely insufficient for proper stream initialization. A more typical low-latency value would be 32768 (32KB) or similar. This could cause stream initialization failures.

Suggested change
# -probesize 32: Minimal probe size (bytes) for faster stream start
# -analyzeduration 0: Skip analysis delay for immediate processing
exec ffmpeg -fflags nobuffer -flags low_delay -probesize 32 -analyzeduration 0 \
# -probesize 32768: Minimal probe size (bytes, 32KB) for faster stream start
# -analyzeduration 0: Skip analysis delay for immediate processing
exec ffmpeg -fflags nobuffer -flags low_delay -probesize 32768 -analyzeduration 0 \

Copilot uses AI. Check for mistakes.
x11stream.sh Outdated
HTTP_PORT="${HTTP_PORT:-8080}"

# Detect the machine's IP address
# Uses 'ip route get 1' to find the default route interface IP (1 is a dummy destination IP)
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IP detection fallback uses 'ip route get 1' which sends a packet to IP address 1.0.0.0. While this is a valid technique, the comment incorrectly describes it as a "dummy destination IP" when it's actually a real IP address owned by APNIC. A more accurate comment would describe it as "uses the default route to determine the primary interface IP" or similar.

Suggested change
# Uses 'ip route get 1' to find the default route interface IP (1 is a dummy destination IP)
# Uses 'ip route get 1' to determine the primary interface IP via the default route

Copilot uses AI. Check for mistakes.
}

IP_ADDRESS=$(get_ip)

Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script uses 'set -e' to exit on errors, but the get_ip function has a fallback pattern using '||' which is good. However, if both 'ip route' and 'hostname -I' fail, the script will continue with an empty IP_ADDRESS variable, which could lead to confusing output and an invalid ffmpeg bind address. Consider adding validation that IP_ADDRESS is not empty before proceeding.

Suggested change
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

Copilot uses AI. Check for mistakes.
@maple-underscore
Copy link
Owner

@copilot Add audio streaming options (64, 128, 192, 256, 320, 16b@44.1kHz, 16b@48kHz, 16b@96kHz, 16b@192kHz, 24b@44.1kHz, 24b@48kHz, 24b@96kHz, 24b@192kHz) with an approximate bandwidth estimate for each. also make the shell script interactive (you can choose framerate, quality, dimensions, etc)

@maple-underscore
Copy link
Owner

@copilot apply changes based on the comments in this thread

Co-authored-by: maple-underscore <102273486+maple-underscore@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 20, 2026

@copilot Add audio streaming options (64, 128, 192, 256, 320, 16b@44.1kHz, 16b@48kHz, 16b@96kHz, 16b@192kHz, 24b@44.1kHz, 24b@48kHz, 24b@96kHz, 24b@192kHz) with an approximate bandwidth estimate for e...

Added in commit 80d2dee:

Interactive mode (./x11stream.sh -i): Menus for resolution (HD/FHD/2K/4K/custom), framerate (24/30/60/120/144/custom), video quality (2M-20M), and audio settings.

Audio presets with bandwidth estimates:

  • Lossy (AAC): 64 (~8 KB/s), 128 (~16 KB/s), 192 (~24 KB/s), 256 (~32 KB/s), 320 kbps (~40 KB/s)
  • 16-bit PCM: 44.1kHz (~172 KB/s), 48kHz (~188 KB/s), 96kHz (~375 KB/s), 192kHz (~750 KB/s)
  • 24-bit PCM: 44.1kHz (~258 KB/s), 48kHz (~281 KB/s), 96kHz (~563 KB/s), 192kHz (~1125 KB/s)

Script displays total estimated bandwidth during startup.

Copilot AI changed the title Add x11stream shell script and systemd service for X11 display streaming Add x11stream shell script with interactive mode, audio streaming, and systemd service Jan 20, 2026
@maple-underscore maple-underscore merged commit b94be61 into main Jan 20, 2026
1 check failed
Copilot stopped work on behalf of maple-underscore due to an error January 20, 2026 18:21
@maple-underscore maple-underscore deleted the copilot/create-ffmpeg-stream-script branch January 20, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants