Skip to content

Instantly share code, notes, and snippets.

@ElDavoo
Forked from LottieVixen/README.md
Last active February 21, 2021 10:53
Show Gist options
  • Save ElDavoo/6c5c8aa3a3ad9ac13942a734c2bcf044 to your computer and use it in GitHub Desktop.
Save ElDavoo/6c5c8aa3a3ad9ac13942a734c2bcf044 to your computer and use it in GitHub Desktop.
xrandr enable and disable second screen as video display

Use a second laptop or headed computer as another monitor via ffmpeg and mpv

This is much faster than VNC, but obviously does not allow for control.

We use the network to transport the stream - I'm getting excellent results over wi-fi. You can do much better with Ethernet, with Ethernet I guess you can use VNC. I don't think you can use usb, bt or hdmi to do this.

My first laptop has Arch Linux with xfce, my second one has Gentoo Linux with fluxbox.

You need ffmpeg (and maybe compiz) on the first laptop and mpv on the second one (using ffplay is doable i guess)

My first laptop has 1366x768 while my second one has 1024x600.

You first need to check your framerate.

$ Screen 0: minimum 320 x 200, current 1366 x 768, maximum 16384 x 16384
eDP-1 connected primary 1366x768+0+0 (normal left inverted right x axis y axis) 256mm x 144mm
   1366x768      60.00*+
... many others
VGA-1 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
HDMI-1 disconnected (normal left inverted right x axis y axis)

as you can see my laptop's physical screen is eDP-1 and is using 1366x768 resolution with 60.00 framerate. We now need to generate a ModeLine for the second resolution. You must keep the same framerate (this is why we checked.)

$cvt 1024 600 60
...
Modeline "1024x600_60.00"   49.00  1024 1072 1168 1312  600 603 613 624 -hsync +vsync

Now lets add this modeline to xrandr like this

$ xrandr --newmode "1024x600_60.00"   49.00  1024 1072 1168 1312  600 603 613 624 -hsync +vsync

Let's add that mode to the display we want to use

$ xrandr --addmode HDMI-1 1024x600_60.00

I used HDMI-1 because my laptop has an HDMI port, but you can use whatever you want I guess? Experiment.

Now you need to enable the display. My second laptop is right of my first one.

$ xrandr --output HDMI-1 --mode 1024x600_60.00 --right-of eDP-1

You can also use arandr to graphically display and change the screen layout.

Ok, however xrandr will still show the output as disconnected. This can give problems, you'll see later.

A definitive solution would be to use evdi - you can create a program that calls evdi_connect so the driver will report a display is connected, but i'm too lazy for this stuff.

Now let's start ffmpeg

$ ffmpeg -video_size 1024x600 -framerate 30 -f x11grab -i :0.0+1366,0 -c:v libx264 -crf 30 -preset ultrafast -pix_fmt yuv420p -tune zerolatency -level 1.0 -hide_banner -loglevel fatal -f mpegts udp://10.10.11.93:5678 &

When I put framerate=60, I found out my laptop can only encode about 40fps, so to make it standard I put 30.

-i:0.0+1366,0 means that we're capturing display :0.0 and that the upper left point of the rectangle that is captured is the coordinate (1366,0).

-crf : video quality. range from 0 to 55. Higher = worse quality. Lower = more bandwidth. In my case 30 is a very good quality.

the other options make sure encoding is fast and not cpu intensive.

The IP address is the DESTINATION of the stream, which means you need to put your second laptop's IP address!

The & is because I made a script that does everything in one click, see it later.

This uses software encoding which takes up CPU ; Feel free to use hardware encoding if available.

(Trivia: Parsec is a remote desktop/gaming application that uses this principle! :D So hardware encoding + fast p2p network protocol + hardware decoding + fast out-of-band input control. Very good!)

Security warning: This makes the traffic plain. Feel free to use tcp + ssh forwarding if you need to encrypt.

Now you can go to the second laptop and start listening.

mpv ffmpeg://udp://127.0.0.1:5678?listen --cache=no --demuxer-readahead-secs=0 --untimed --really-quiet -fs

You won't see anything for many seconds. This is normal! ffmpeg needs to receive a complete frame (called an I-Frame) to start displaying. You'll need to wait that an I-Frame is sent. You can adjust how many frames an I-frame is sent using the -g option on the ffmpeg line. Lower number = more i-frames sent, more bandwidth used, video starts sooner.

Wait, what if you're doing a script? Why should you physically go to the second laptop? Why don't just use ssh?

ssh dave@10.10.11.93 "export DISPLAY=:0; xset s off -dpms ; mpv ffmpeg://udp://127.0.0.1:5678?listen --cache=no --demuxer-readahead-secs=0 --untimed --really-quiet ; xset dpms"

You can see I didn't put &, so that when i ^C mpv terminates and the script below goes on.

The xset stuff is for disabling automatic display blanking / turning off.

Not using & makes it necessary to keep a terminal window-open, but that's actually useful for me - below there are the reasons.

Of course we need to disable the caches so we decode the frames as fast as possible. Latency is not tolerated.

Please investigate if your video decoding is accelerated with mpv.

Congratulations! You have a fast, fluid and good quality second laptop.

Wait, you're not seeing anything? Only the mouse?

You can't move the windows around? you don't see the panels, the background, the desktop?

Are you by chance using XFCE? Because since the display is not physically connected, GDK won't report it, so xfwm4 (and the rest of xfce) does not know it exists!!!

In particular, xfwm4 will refuse to draw anything in there. I had to replace it with compiz.

However, you'll see that if you put a window in the second screen, than move it back, everything will be glitchy. You will need to open a full-screen window to stop this glitch. A terminal window would be perfect! (Edit: A much simpler solution for me was to enable the compiz "copy to texture" option and reboot it.)

So I just start my script and move the terminal that contains it in the second screen. That will be my "background".

You still will not have panels and desktop. I'm ok with that.

I hope that kde and gnome works without giving these problems.

Cleanup:

$ xrandr --output HDMI-1 --off
killall ffmpeg
xrandr --delmode HDMI-1 "1024x600_60.00"
xrandr --rmmode "1024x600_60.00"

I use secondscreen.sh when I need the second screen. It won't go to background, it will keep the terminal window open, and will cleanup everything when I ^C. I just start it, wait for the screen to appear, then put the terminal window maximized and I can start working. There is a better way to kill ffmpeg, because with "killall fmpeg" you also kill other ffmpeg instances, which means recording the pid somewhere... I don't care.

#Modeline generated with cvt 1024 600 60
xrandr --newmode "1024x600_60.00" 49.00 1024 1072 1168 1312 600 603 613 624 -hsync +vsync
xrandr --addmode HDMI-1 1024x600_60.00
# Enable the second display
xrandr --output HDMI-1 --mode 1024x600_60.00 --right-of eDP-1
#start ffmpeg stream
ffmpeg -video_size 1024x600 -framerate 30 -f x11grab -i :0.0+1366,0 -c:v libx264 -crf 30 -preset ultrafast -pix_fmt yuv420p -tune zerolatency -level 1.0 -hide_banner -loglevel fatal -f mpegts udp://10.10.11.93:5678 &
echo ssh
#start the client
ssh dave@10.10.11.93 "export DISPLAY=:0; xset s off -dpms ; mpv ffmpeg://udp://127.0.0.1:5678?listen --cache=no --demuxer-readahead-secs=0 --untimed --really-quiet -fs ; xset dpms"
# Cleanup
xrandr --output HDMI-1 --off
killall ffmpeg
xrandr --delmode HDMI-1 "1024x600_60.00"
xrandr --rmmode "1024x600_60.00"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment