1. Overview
In this tutorial, we’ll look at practical examples of displaying images and videos on a Linux system without a traditional display server such as X11 or Wayland. In such a terminal-only environment, we’ll also see how to browse the web using a primarily text-only browser that can also display images.
We’ll focus on using the framebuffer and the Direct Rendering Manager (DRM), which are well suited for taking advantage of the video card in embedded systems or in traditional Linux distributions that are minimally configured without a graphical user interface (GUI). We’ll also explore text-only methods for viewing images and video from a remote SSH terminal.
As sample files, we’ll have img-lowres.jpg, img-hires.jpg, and video.mp4.
Our demonstration platform is a VirtualBox machine running Ubuntu Server 22.04 without a graphical environment. However, the techniques and commands we present are applicable to various Linux distributions.
2. Take Preventive Measures
We’ll focus on tools that are mostly only intended for environments without display servers. If we want to try them in a classic desktop distribution with GNOME, KDE, Cinnamon or other desktop environments running, we should avoid using a terminal emulator in such environments. There are other methods for proper testing:
- Use a headless distribution inside a virtual machine → safer and suggested approach
- Open a virtual console (TTY) with CTRL+ALT+F[1-6] → simplest solution, but may have unforeseen effects
- Boot Linux with init=/bin/bash → drastic and limited approach
- Boot Linux with runlevel 3 → our system will run in command line mode instead of GUI
A virtual machine is a good testing platform because it’s isolated enough from the real hardware. In any case, while the tools we’ll see are safe, it’s strongly discouraged trying to write directly to the framebuffer using a custom script in Bash, Python, or other languages. We won’t discuss this possibility to avoid crashes and other side effects.
Another point to consider is that switching from our desktop to a virtual console using CTRL+ALT+F[1-6] and then returning to the desktop using CTRL+ALT+F7 can freeze Google Chrome and prevent it from even closing and reopening. A known workaround for this issue is to start Chrome with google-chrome –disable-gpu.
3. Framebuffer Tools
The framebuffer is a designated area of RAM that the computer’s video hardware uses to store the current image shown on the screen. It’s typically accessible to applications through the /dev/fb0 character device, which allows direct interaction with the display hardware. This is a simple, low-level method that enables the generation of images or video without relying on a display server.
To get information about the current framebuffer settings, we can use fbset, which usually doesn’t come with the operating system by default. We’ll install this and the next upcoming tools using apt:
$ sudo apt install fbset
$ sudo fbset -fb /dev/fb0 -i
[...]
geometry 1024 768 [...] 32
[...]
Visual : TRUECOLOR
[...]
Accelerator : No
The shown portion of this output indicates a screen resolution of 1024 x 768 pixels, 32-bit color depth, no graphics acceleration, and true color support. The full output is more extensive, but not relevant for our purposes.
3.1. Use Restrictions
Due to the framebuffer’s direct interaction with critical hardware resources, we usually need elevated privileges, typically accessed via sudo. An exception is Links2, as we’ll see in a moment. If using sudo isn’t feasible or poses security risks, we should consider the DRM as an alternative, which provides similar functionality without the need for elevated privileges.
Also, the framebuffer and DRM are local technologies that work with the host system’s hardware. They aren’t suitable for remote tasks such as viewing images or videos over SSH. For such remote applications, it’s better to use tools that rely on a color Unicode video output driver.
We’ll talk about DRM and SSH-based options later.
3.2. Framebuffer Graphical Web Browser
Links2 is a web browser compatible with different operating systems. It offers both text and graphic modes, works on different terminals, and supports mouse functions in some environments. It has a user-friendly pull-down menu in 33 languages and can display images directly in graphics mode. In addition, Links2 handles animated GIFs, bookmarks, file downloads, and integrates with external MIME-type programs. It also offers high-quality image processing, font anti-aliasing, and support for Unicode fonts in graphic mode. Key features include user-adjustable settings, single- and dual-wheel mouse support, and automatic aspect ratio correction.
Let’s install it. In our test distribution, we also need to use chown and chmod to make it work:
$ sudo apt install links2
$ sudo chown root:video /usr/bin/links2
$ sudo chmod g+s /usr/bin/links2
This way, chown changes the owner of the Links2 executable to the root user and sets its group to video. Further, chmod applies the Set Group ID (SGID) bit on the Links2 executable so that it runs with the group privileges of its group owner, which, in this case, is the video group. Our intent is to allow Links2 to access the video hardware without needing full root privileges. In our tests, we’ve seen that this strategy only works in this case, while it doesn’t work with the other framebuffer tools we’ll see.
The -g option starts Links2 in graphical mode, which allows it to render images, and the -driver fb option specifies the use of the framebuffer for rendering graphics. Let’s try to browse the Baeldung homepage:
$ links2 -g -driver fb https://www.baeldung.com/
The main limitation is that it can render complex pages, but only with partial HTML 4.0 support:
To close the browser, we press the q key. We can also use Links2 to view locally stored images:
$ links2 -g -driver fb file://img-lowres.jpg
$ links2 -g -driver fb file://img-hires.jpg
When we press Enter, Links2 automatically resizes the displayed image:
If our goal is just to view images or browse simple web pages, Links2 is definitely a suitable solution.
3.3. Framebuffer Image Viewers
fbi, an image viewer designed specifically for Linux framebuffer devices, was a notable tool in the past. It’s still an option, but it may not work properly on newer systems.
fim, which stands for fbi improved, is a lightweight image viewer successor to fbi. It’s especially useful for users familiar with vim or mutt, offering a scriptable interface and keyboard control. fim automatically detects and selects the appropriate video mode for displaying images, including the framebuffer device.
Let’s give it a try:
$ sudo apt install fim
$ sudo fim -a img-lowres.jpg
$ sudo fim -a img-hires.jpg
The -a option ensures that the image is automatically resized to fit the screen. To exit, let’s press q. If, as in our case, part of the image is still on the screen after the program is closed, we can use the clear command to wipe it:
As a side note, this viewer can convert images to ASCII art with the -o aa option.
3.4. Framebuffer Video Player
MPlayer is a well-known popular media player with wide compatibility with numerous multimedia formats. Its framebuffer support is facilitated by the -vo (video output) option.
Let’s give it a try:
$ sudo apt install mplayer
$ sudo mplayer -vo fbdev2:/dev/fb0 -zoom -x 1024 -y 768 video.mp4
When we use the -vo fbdev2:/dev/fb0 option, we’re instructing MPlayer to output the video to the first framebuffer device, which is usually the primary display. The -zoom option scales the video, and the -x 1024 -y 768 options specify the resolution to which the video should be scaled:
We can stop playback and close the player by pressing q.
Although MPlayer’s framebuffer support allows it to run in environments without a graphical display server, this doesn’t affect its ability to handle audio. So we can enjoy uninterrupted audio playback alongside our framebuffer-rendered videos.
4. Direct Rendering Manager (DRM)
The DRM, a key subsystem of the Linux kernel that interfaces with GPUs and manages graphics memory, plays a critical role in rendering images and video. Essential for 3D acceleration and complex graphics, it goes beyond the primitive pixel-based screen representation of the traditional framebuffer by providing advanced GPU access. This facilitates hardware-accelerated rendering, efficient memory usage, and multi-buffering, all of which are critical to modern graphics applications.
As we mentioned earlier about the limitations of using the framebuffer, DRM is very different from the framebuffer in terms of user permissions and security. The framebuffer, which is a low-level interface, often requires root privileges due to the potential security risks of unrestricted access to video memory. In contrast, DRM, which operates at a higher level with more sophisticated access controls, allows non-root users to safely utilize GPU capabilities through a structured API. This approach is critical in multi-user environments and for applications that require secure, efficient use of graphics resources without elevated privileges.
4.1. DRM Video Player
The mpv media player stands out for its extensive support for various video drivers. We can take advantage of its compatibility with DRM by using the –vo=drm option.
As in the previous case of MPlayer, mpv also has no problem playing the audio associated with the video. In addition, to close the player before the end of the video, we can press q. Let’s give it a try:
$ sudo apt install mpv
$ mpv --vo=drm video.mp4
The video automatically scales to full screen without any additional options:
The documentation for the keyboard controls in mpv reveals a comprehensive set of features. For example, we can go forward or backward five seconds, one minute or 10 minutes, or to the previous or next subtitle. Other controls include adjusting playback speed, managing subtitles and audio tracks, switching to full screen or quitting, taking screenshots, displaying progress bar or advanced video info.
4.2. DRM Image Viewer
It’s worth noting that mpv isn’t just limited to playing video. With the –keep-open=yes option, we can also use it to display images:
$ mpv --keep-open=yes --vo=drm img-lowres.jpg
$ mpv --keep-open=yes --vo=drm img-hires.jpg
As with videos, images are automatically scaled to fit the screen. We still have to press q to close the viewer:
This way, mpv can efficiently handle both still images and video content.
5. SSH Text-Only Communication Tools
The core functionality and most common use of SSH is text-based remote command execution and file transfer via scp. However, using scp to copy a remote image or video file for local viewing is beyond the scope of this discussion. Similarly, using sshfs to locally mount a remote filesystem via SSH is another viable option, but it’s not the focus of our current topic. For clarity, our goal is direct interaction with multimedia content using a text-only SSH interface.
For the purposes of our demonstration, we’ve set up our host machine to connect to our virtualized guest machine via SSH. This type of connection isn’t possible with VirtualBox’s default NAT network, so we need to use a bridged adapter and get the guest’s IP with ifconfig:
In our case, as shown in the screenshot, the guest IP is 172.20.10.2 and the user is francesco. We need to use these values to adapt the ssh command given in a host terminal:
$ ssh [email protected]
In addition, this bridged configuration requires the virtual machine to be powered off and on each time the host loses its Internet connection. In such cases, a reboot isn’t sufficient.
5.1. mpv
The –vo=tct option in mpv is quite unique. It stands for true color terminal and is a video output driver that allows mpv to play video directly in a terminal window, using ANSI escape codes to render video frames in the terminal as colored ASCII characters.
The resolution of the video played back will therefore be very low, because it’s given by the number of characters available per line, multiplied by the number of lines. We can easily check this resolution with tput:
$ echo "$(tput cols) x $(tput lines)"
146 x 36
The played video will therefore be pixelated. Let’s give it a try:
$ mpv --vo=tct video.mp4
Obviously, there is no way to transmit the audio stream using only characters:
The result is imperfect, but if all we want is to be able to identify the video without downloading it, just by viewing it in very low resolution, then we can be satisfied.
We can repeat the same test with our two example images:
$ mpv --vo=tct --keep-open=yes img-lowres.jpg
$ mpv --vo=tct --keep-open=yes img-hires.jpg
The images remain displayed until we press the q key:
Again, the result is approximate but sufficient to identify the images.
5.2. cacaview
cacaview is a lightweight ASCII art viewer. While it offers a more basic approach and generally lower quality results compared to mpv‘s tct video output driver, it’s still a noteworthy option for those interested in ASCII art and retro computing aesthetics.
Let’s give it a try:
$ sudo apt install caca-utils
$ cacaview img-lowres.jpg
$ cacaview img-hires.jpg
Unfortunately, the result is poor:
There are other ASCII art viewers, such as jp2a and the already discussed fim. In general, however, their functionality is mainly artistic rather than aimed at reproducing the original image as faithfully as possible.
6. Conclusion
In this article, we’ve explored the unconventional but fascinating approach to managing multimedia content in a Linux system without a graphical interface. We’ve seen how to use framebuffers, DRM, and text-only communication channels with versatile tools that can be adapted to different contexts:
- links2 → text-based web browser supporting graphics in text mode
- fbi → framebuffer image viewer with basic functionality
- mplayer → powerful multimedia player supporting a wide range of formats
- mpv → advanced media player with focus on high quality video playback
- cacaview → displays images using ASCII characters for artistic, text-based viewing
By using these specialized tools, we’ve demonstrated that we can effectively handle images and video even in the absence of a traditional display server.