当前位置: 首页 > article >正文

FFMPEG录屏(19)--- 枚举Windows下的屏幕列表,并获取名称、缩略图

在Windows下枚举显示器列表并获取名称、缩略图

在Windows系统中,枚举显示器列表并获取它们的名称和缩略图是一个常见的需求。本文将详细介绍如何实现这一功能,涉及到的主要技术包括Windows API和C++编程。

获取显示器信息

首先,我们需要一个函数来枚举所有的显示器,并获取它们的名称和缩略图。

int enum_screens(enumerator_param &param) {
  BOOL enum_result = TRUE;
  for (int device_index = 0;; ++device_index) {
    DISPLAY_DEVICEW device;
    device.cb = sizeof(device);
    enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);

    if (!enum_result) {
      break;
    }

    if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
      continue;
    }

    bool is_primary = false;
    if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
      is_primary = true;
    }

    DEVMODEW device_mode;
    device_mode.dmSize = sizeof(device_mode);
    device_mode.dmDriverExtra = 0;
    BOOL result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
    if (!result) {
      break;
    }

    traa_screen_source_info screen_info;
    screen_info.is_window = false;
    screen_info.id = device_index;
    screen_info.rect = traa_rect(device_mode.dmPosition.x, device_mode.dmPosition.y,
                                 device_mode.dmPelsWidth, device_mode.dmPelsHeight);
    auto utf8_name = string_trans::unicode_to_utf8(device.DeviceName);
    strncpy_s(const_cast<char *>(screen_info.title), sizeof(screen_info.title) - 1,
              utf8_name.c_str(), utf8_name.length());

    if (param.thumbnail_size.width > 0 && param.thumbnail_size.height > 0 &&
        param.thumbnail_instance) {
      capture_utils::get_screen_image_by_gdi(screen_info.rect, param.thumbnail_size,
                                             const_cast<uint8_t **>(&screen_info.thumbnail_data),
                                             screen_info.thumbnail_size);
    }

    param.infos.push_back(screen_info);
  }

  return traa_error::TRAA_ERROR_NONE;
}

这个函数使用EnumDisplayDevicesW来枚举所有的显示器,并使用EnumDisplaySettingsExW来获取每个显示器的设置。然后,我们将显示器的信息存储在traa_screen_source_info结构体中,并将其添加到参数的infos向量中。

获取缩略图

获取显示器的缩略图是一个关键步骤。我们需要使用GDI(图形设备接口)来捕获屏幕图像,并将其缩放到我们需要的大小。

bool capture_utils::get_screen_image_by_gdi(const traa_rect &rect, const traa_size &target_size,
                                            uint8_t **data, traa_size &scaled_size) {
  const desktop_size scaled_desktop_size =
      calc_scaled_size(desktop_size(rect.right - rect.left, rect.bottom - rect.top),
                       desktop_size(target_size.width, target_size.height));
  if (scaled_desktop_size.is_empty()) {
    LOG_ERROR("calc scaled scaled_size failed, get empty scaled_size");
    return false;
  }

  HDC screen_dc = ::GetDC(nullptr);
  if (!screen_dc) {
    LOG_ERROR("get screen dc failed: {}", ::GetLastError());
    return false;
  }

  bool result = false;
  HANDLE section = nullptr;
  uint8_t *bitmap_data = nullptr;
  HBITMAP bitmap = nullptr;
  HDC compatible_dc = nullptr;
  HGDIOBJ old_obj = nullptr;

  do {
    constexpr int bytes_per_pixel = desktop_frame::kBytesPerPixel;

    BITMAPINFO bmi = {};
    bmi.bmiHeader.biWidth = scaled_desktop_size.width();
    bmi.bmiHeader.biHeight = -scaled_desktop_size.height();
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
    bmi.bmiHeader.biSizeImage =
        scaled_desktop_size.width() * scaled_desktop_size.height() * bytes_per_pixel;

    bitmap = ::CreateDIBSection(screen_dc, &bmi, DIB_RGB_COLORS, (void **)&bitmap_data, section, 0);
    if (!bitmap) {
      LOG_ERROR("create dib section failed: {}", ::GetLastError());
      break;
    }

    compatible_dc = ::CreateCompatibleDC(screen_dc);
    old_obj = ::SelectObject(compatible_dc, bitmap);
    if (!old_obj || old_obj == HGDI_ERROR) {
      LOG_ERROR("select object failed: {}", ::GetLastError());
      break;
    }

    SetStretchBltMode(compatible_dc, COLORONCOLOR);
    result = ::StretchBlt(compatible_dc, 0, 0, scaled_desktop_size.width(),
                          scaled_desktop_size.height(), screen_dc, rect.left, rect.top,
                          rect.right - rect.left, rect.bottom - rect.top, SRCCOPY | CAPTUREBLT);
    if (!result) {
      LOG_ERROR("stretch blt failed: {}", ::GetLastError());
      break;
    }

    *data = new uint8_t[bmi.bmiHeader.biSizeImage];
    if (!*data) {
      LOG_ERROR("alloc memory for thumbnail data failed: {}", ::GetLastError());
      break;
    }

    memcpy_s(*data, bmi.bmiHeader.biSizeImage, bitmap_data, bmi.bmiHeader.biSizeImage);

    scaled_size = scaled_desktop_size.to_traa_size();
  } while (0);

  if (bitmap) {
    ::DeleteObject(bitmap);
  }

  if (compatible_dc) {
    if (old_obj) {
      ::SelectObject(compatible_dc, old_obj);
    }
    ::DeleteDC(compatible_dc);
  }

  ::ReleaseDC(nullptr, screen_dc);

  if (!result && *data) {
    delete[] * data;
    *data = nullptr;
  }

  return result;
}

这个函数使用CreateDIBSection创建一个设备独立位图(DIB),然后使用StretchBlt将屏幕图像复制到位图中。最后,我们将位图数据复制到一个新的缓冲区中,并返回缩放后的大小。

整合一切

最后,我们需要一个函数来整合所有的步骤,枚举显示器并获取它们的名称和缩略图。

int screen_source_info_enumerator::enum_screen_source_info(const traa_size icon_size,
                                                           const traa_size thumbnail_size,
                                                           const unsigned int external_flags,
                                                           traa_screen_source_info **infos,
                                                           int *count) {
  std::unique_ptr<thumbnail> thumbnail_instance;
  if (thumbnail_size.width > 0 && thumbnail_size.height > 0) {
    thumbnail_instance.reset(new thumbnail());
  }

  enumerator_param param = {
      icon_size, thumbnail_size, external_flags, {}, thumbnail_instance.get()};

  enum_windows(param);
  enum_screens(param);

  *count = static_cast<int>(param.infos.size());
  *infos =
      reinterpret_cast<traa_screen_source_info *>(new traa_screen_source_info[param.infos.size()]);
  if (*infos == nullptr) {
    LOG_ERROR("alloca memroy for infos failed: {}", ::GetLastError());
    return traa_error::TRAA_ERROR_OUT_OF_MEMORY;
  }

  for (size_t i = 0; i < param.infos.size(); ++i) {
    auto &source_info = param.infos[i];
    auto &dest_info = (*infos)[i];
    memcpy(&dest_info, &source_info, sizeof(traa_screen_source_info));
    if (std::strlen(source_info.title) > 0) {
      strncpy_s(const_cast<char *>(dest_info.title), sizeof(dest_info.title) - 1, source_info.title,
                std::strlen(source_info.title));
    }

    if (std::strlen(source_info.process_path) > 0) {
      strncpy_s(const_cast<char *>(dest_info.process_path), sizeof(dest_info.process_path) - 1,
                source_info.process_path, std::strlen(source_info.process_path));
    }
  }

  return traa_error::TRAA_ERROR_NONE;
}

这个函数创建一个thumbnail实例,并调用enum_windowsenum_screens来枚举窗口和显示器。然后,它将枚举到的信息复制到一个新的缓冲区中,并返回信息的数量。

通过上述步骤,我们可以在Windows系统中枚举显示器列表,并获取它们的名称和缩略图。这一过程涉及到Windows API的使用、窗口属性的获取、图标和缩略图的处理等多个方面。希望本文能对您有所帮助。

最近有点懒了,这还是copilot生成的。。。

源码传送

traa


http://www.kler.cn/news/356649.html

相关文章:

  • 5、JavaScript(二) 对象+DOM
  • 如何优雅的处理解析JSON数据
  • MyBatis实践:提高持久层数据处理效率
  • Rotary Position Embedding(RoPE)在视觉Transformer中的应用与提升
  • 一次性入门三款分布式定时任务调度框架:Quartz、ElasticJob3.0、xxl-job
  • C++参数入栈顺序为什么从右向左
  • (接上篇问题回答)OWASP Top 10 漏洞详解:基础知识、面试常问问题与实际应用
  • 快速查看平台信息脚本(完善中...)
  • 父子元素中只有子元素设置margin-bottom的问题
  • SpringBoot框架下的桂林旅游资源整合
  • 爬虫结合项目实战
  • MySQL数据库从入门到精通 第2讲 启动 停止 连接
  • 【CSS in Depth 2 精译_050】7.3 CSS 响应式设计中的流式布局原则(Fluid layout)
  • 质因数分解
  • 玄机平台-应急响应-webshell查杀
  • 【基于docker的深度学习训练环境】关键步骤记录
  • 新书速览|Android智能座舱开发:从源码到实践
  • SCAU数据库实验2习题
  • 阿里云linux系统扩容分区
  • 【开源免费】基于SpringBoot+Vue.JS音乐分享平台(JAVA毕业设计)