FFMPEG录屏(22)--- Linux 下基于X11枚举所有显示屏,并获取大小和截图等信息

众人拾柴火焰高,github给个star行不行?
open-traa/traa
traa is a versatile project aimed at recording anything, anywhere. The primary focus is to provide robust solutions for various recording scenarios, making it a highly adaptable tool for multiple use cases.

在 Linux X11 窗口系统下枚举显示屏

在 Linux 系统中,X11 是一个广泛使用的窗口系统,它提供了丰富的 API 用于管理和操作显示屏。在这篇博客中,我们将介绍如何使用 X11 枚举系统中的显示屏,并获取显示屏的 ID、坐标和截图等信息。

前置条件

在开始之前,请确保你已经安装了 X11 相关的开发库,例如 libX11libXrandr。你可以使用以下命令进行安装:

bash 复制代码
sudo apt-get install libx11-dev libxrandr-dev

额外的说明

现在的Ubuntu系统,默认是Wayland的,所以一些X11功能可能会报错,尤其是 获取屏幕截图 , 作为LInux新手...我搞了好久,切换X11环境后,一次就成了,可以在Login界面选择

枚举显示屏的基本步骤

我们将通过以下几个步骤来实现枚举显示屏的功能:

  1. 打开 X11 显示连接。
  2. 检查 Xrandr 扩展是否可用。
  3. 获取根窗口。
  4. 获取显示屏信息。
  5. 获取显示屏的截图。

代码实现

以下是一个完整的代码示例,展示了如何实现上述功能:

cpp 复制代码
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <cstring>

// 获取显示屏的截图
bool get_screen_image_data(Display *display, ::Window root, const XRRMonitorInfo &monitor, const int thumbnail_width, const int thumbnail_height, std::vector<uint8_t> &data) {
    XImage *image = XGetImage(display, root, monitor.x, monitor.y, monitor.width, monitor.height, AllPlanes, ZPixmap);
    if (!image) {
        std::cerr << "Failed to get image for screen" << std::endl;
        return false;
    }

    int scaled_width = thumbnail_width;
    int scaled_height = thumbnail_height;
    data.resize(scaled_width * scaled_height * 4);

    // 使用 libyuv 进行图像缩放
    libyuv::ARGBScale(reinterpret_cast<uint8_t *>(image->data), image->bytes_per_line, image->width, image->height, data.data(), scaled_width * 4, scaled_width, scaled_height, libyuv::kFilterBox);

    XDestroyImage(image);
    return true;
}

// 枚举显示屏
void enum_screens(int thumbnail_width, int thumbnail_height) {
    Display *display = XOpenDisplay(NULL);
    if (!display) {
        std::cerr << "Failed to open display" << std::endl;
        return;
    }

    // 检查 Xrandr 扩展是否可用
    int event_base, error_base;
    if (!XRRQueryExtension(display, &event_base, &error_base)) {
        std::cerr << "Xrandr extension is not available" << std::endl;
        XCloseDisplay(display);
        return;
    }

    // 获取根窗口
    ::Window root = DefaultRootWindow(display);

    // 获取显示屏信息
    int monitor_count = 0;
    XRRMonitorInfo *monitors = XRRGetMonitors(display, root, True, &monitor_count);
    if (!monitors) {
        std::cerr << "Failed to get monitors" << std::endl;
        XCloseDisplay(display);
        return;
    }

    for (int i = 0; i < monitor_count; ++i) {
        std::cout << "Monitor ID: " << i << std::endl;
        std::cout << "Position: (" << monitors[i].x << ", " << monitors[i].y << ")" << std::endl;
        std::cout << "Size: " << monitors[i].width << "x" << monitors[i].height << std::endl;

        // 获取显示屏的截图
        std::vector<uint8_t> image_data;
        if (get_screen_image_data(display, root, monitors[i], thumbnail_width, thumbnail_height, image_data)) {
            std::cout << "Captured screen image" << std::endl;
        }
    }

    XRRFreeMonitors(monitors);
    XCloseDisplay(display);
}

int main() {
    int thumbnail_width = 200;  // 缩略图宽度
    int thumbnail_height = 150; // 缩略图高度
    enum_screens(thumbnail_width, thumbnail_height);
    return 0;
}

代码说明

  1. 获取显示屏的截图:使用

XGetImage

函数获取显示屏的截图数据,并使用 libyuv 进行图像缩放。

  1. 枚举显示屏:使用

XRRGetMonitors

函数获取显示屏信息,并遍历每个显示屏获取其详细信息。

总结

通过上述步骤,我们可以在 Linux X11 窗口系统下枚举系统中的显示屏,并获取显示屏的 ID、坐标和截图等信息。这些功能可以用于开发桌面管理工具、屏幕录制软件等应用。希望这篇博客对你有所帮助!

更多细节请前往 TRAA

额外的实现

真实的获取图片的代码是用了一个额外的辅助类,参考了webrtc的代码,本系列会大量借鉴webrtc代码,比较大树底下好乘凉?XServerPixelBuffer

这个东西,有一个坑点,搞了许久,也不知道为什么,望大佬赐教,我们是利用Xrandr插件来获取每个显示屏的坐标的,一旦你先调用了XRRGetMonitors 枚举函数,XServerPixelBuffer::Init 中的XGetWindowAttributes 必然失败,错误是什么BadMatch。。。你懂的,X11的错误我至今不知道应该如何调查,所以一定要在XRRGetMonitors 之前把pixelBuffer初始化掉

如下

cpp 复制代码
  std::unique_ptr<basic_desktop_frame> full_screen_frame;
  if (thumbnail_size.width > 0 && thumbnail_size.height > 0) {
    // must init the pixel buffer before calling XRRGetMonitors, ohterwise init will raise an
    // BadMatch error.I don't know why, just do it.
    x_server_pixel_buffer pixel_buffer;
    if (!pixel_buffer.init(&atom_cache, root)) {
      LOG_ERROR("failed to init pixel buffer for window {}", root);
      return false;
    }

    // capture the whole screen
    full_screen_frame = std::make_unique<basic_desktop_frame>(pixel_buffer.window_size());
    pixel_buffer.synchronize();
    if (!pixel_buffer.capture_rect(desktop_rect::make_size(full_screen_frame->size()),
                                   full_screen_frame.get())) {
      LOG_ERROR("failed to capture rect for screen {}", root);
      return false;
    }

#if 0
    // create a file to save the full screen
    save_pixel_to_ppm("full_screen.ppm", full_screen_frame->data(), full_screen_frame->size().width(),
                      full_screen_frame->size().height());
#endif
  }

  int monitor_count = 0;
  XRRMonitorInfo *monitors = XRRGetMonitors(display, root, True, &monitor_count);
  if (!monitors) {
    LOG_ERROR("failed to get monitors");
    XCloseDisplay(display);
    return traa_error::TRAA_ERROR_UNKNOWN;
  }
相关推荐
想见感3 小时前
自定义集成ESXI网卡驱动
linux
IT19954 小时前
Linux笔记-对Linux环境变量的进一步认识(2024-08-09)
linux·运维·笔记·运维开发
Yanbin_Q5 小时前
Vagrant 没了 VirtualBox 的话可以配 Qemu
运维·ruby·vagrant
zkf01000076 小时前
ISAAC SIM踩坑记录--ROS2相机影像发布
linux
fivestar20097 小时前
一台服务器最大能打开的文件数
运维·服务器
看山还是山,看水还是。7 小时前
Oracle的字符串函数
运维·数据库·安全·oracle
很楠不爱7 小时前
Linux网络——自定义协议与序列化
linux·服务器·网络
姝孟7 小时前
LInux——环境基础开发工具使用(正在更新中...)
linux·运维·centos
Genius Kim7 小时前
Knife4j调试全局对象参数自动化
运维·python·自动化
大耳朵土土垚7 小时前
【Linux】ELF可执行程序和动态库加载
linux·运维·服务器