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;
  }
相关推荐
sszdzq13 分钟前
Docker
运维·docker·容器
book012117 分钟前
MySql数据库运维学习笔记
运维·数据库·mysql
leoufung21 分钟前
VIM FZF 安裝和使用
linux·编辑器·vim
bugtraq20211 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi1 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
VVVVWeiYee1 小时前
BGP配置华为——路径优选验证
运维·网络·华为·信息与通信
陆鳐LuLu2 小时前
日志管理利器:基于 ELK 的日志收集、存储与可视化实战
运维·elk·jenkins
CodeWithMe2 小时前
[ Vim ] 常用命令 and 配置
linux·编辑器·vim
DC_BLOG2 小时前
Linux-GlusterFS进阶分布式卷
linux·运维·服务器·分布式
cookies_s_s3 小时前
Linux--进程(进程虚拟地址空间、页表、进程控制、实现简易shell)
linux·运维·服务器·数据结构·c++·算法·哈希算法