Linux(含麒麟操作系统)如何实现多显示器屏幕采集录制

技术背景

在操作系统领域,很多核心技术掌握在国外企业手中。如果过度依赖国外技术,在国际形势变化、贸易摩擦等情况下,可能面临技术封锁和断供风险。开发国产操作系统可以降低这种风险,确保国家关键信息基础设施的稳定运行。在一些敏感行业如国防、金融等,对技术的自主可控要求极高。

音视频信息在很多场合涉及国家安全和敏感内容。如果操作系统的音视频模块依赖国外技术,可能存在安全漏洞被利用的风险,导致国家机密信息泄露。自主开发的音视频模块可以进行更严格的安全管控,增强国家信息安全防护能力。

在这样的背景下,我们实现了Linux平台下的以屏幕采集、摄像头采集、麦克风扬声器采集为数据源的RTMP推送模块、轻量级RTSP服务模块,和RTMP播放器和RTSP播放器模块,并同时覆盖了x86-64架构和aarch64架构。

技术实现

xrandr

本文我们要讨论的是,如何在Linux平台实现多显示器的屏幕采集录制。我们知道,Linux下,X Window Sysem支持多显示器的配置和显示器列表获取。可以使用xrandr查看显示器列表:

xrandr --listactivemonitors” 可在Linux 系统中用于显示当前活动监视器信息的命令。

命令作用

  1. 显示连接状态

    • 该命令可以列出当前连接到系统的所有活动监视器,包括其名称、分辨率、刷新率以及位置信息等。通过查看这些信息,你可以了解到每个监视器的连接状态和基本参数。
    • 例如,如果你连接了多个显示器,这个命令可以帮助你确定哪些显示器是处于活动状态的,以及它们的具体配置。
  2. 帮助配置多显示器

    • 对于使用多显示器的用户来说,这个命令非常有用。它可以让你了解当前的显示器布局,以便更好地进行配置和调整。
    • 你可以根据命令输出的信息,使用其他 xrandr 命令来设置显示器的分辨率、位置、旋转等参数,实现个性化的多显示器设置。

如何使用libXrandr获取显示器列表

先看看Xrandr.h

/** Copyright © 2000 Compaq Computer Corporation, Inc.* Copyright © 2002 Hewlett-Packard Company, Inc.* Copyright © 2006 Intel Corporation* Copyright © 2008 Red Hat, Inc.** Permission to use, copy, modify, distribute, and sell this software and its* documentation for any purpose is hereby granted without fee, provided that* the above copyright notice appear in all copies and that both that copyright* notice and this permission notice appear in supporting documentation, and* that the name of the copyright holders not be used in advertising or* publicity pertaining to distribution of the software without specific,* written prior permission.  The copyright holders make no representations* about the suitability of this software for any purpose.  It is provided "as* is" without express or implied warranty.** THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE* OF THIS SOFTWARE.** Author:  Jim Gettys, HP Labs, Hewlett-Packard, Inc.*	    Keith Packard, Intel Corporation*/#ifndef _XRANDR_H_
#define _XRANDR_H_#include <X11/extensions/randr.h>
#include <X11/extensions/Xrender.h>#include <X11/Xfuncproto.h>_XFUNCPROTOBEGINtypedef struct _XRRMonitorInfo {Atom name;Bool primary;Bool automatic;int noutput;int x;int y;int width;int height;int mwidth;int mheight;RROutput *outputs;
} XRRMonitorInfo;XRRMonitorInfo *
XRRAllocateMonitor(Display *dpy, int noutput);XRRMonitorInfo *
XRRGetMonitors(Display *dpy, Window window, Bool get_active, int *nmonitors);void
XRRSetMonitor(Display *dpy, Window window, XRRMonitorInfo *monitor);void
XRRDeleteMonitor(Display *dpy, Window window, Atom name);void
XRRFreeMonitors(XRRMonitorInfo *monitors);_XFUNCPROTOEND#endif /* _XRANDR_H_ */

我们Linux平台RTMP推送、轻量级RTSP服务设计的选择接口如下:

/** nt_liunx_smart_publisher_sdk.h* Author: daniusdk.com* WeChat: xinsheng120*/
/*
* 获取active XRR-Monitor列表, X RandR 1.5及以上版本支持(可以用:"xrandr --version"查看版本)
* x_display_name:传入实际使用的名称或者NULL,调用XOpenDisplay时使用.
* monitors: 预分配的XRR-Monitor数组
* monitors_array_size: 预分配的XRR-Monitor数组长度
* out_count: 返回实际的XRR-Monitor数量
* 成功返回:NT_ERC_OK, 如果返回值是:NT_ERC_MORE_DATA, 说明预分配的数组长度不够, 请分配更大的数组再尝试.
*/
NT_UINT32 NT_API NT_PB_GetXRRMonitors(NT_PCSTR x_display_name, NT_PB_XRR_MonitorBaseInfo* monitors, NT_INT32 monitors_array_size, NT_INT32* out_count);/*
* 设置要采集的XRRMonitor id, 采集X屏幕时使用
* xrr_monitor_id: -1:采集所有屏幕, SDK默认为-1.
*/
NT_UINT32 NT_API NT_PB_SetCaptureXRRMonitor(NT_HANDLE handle, NT_INT64 xrr_monitor_id);

 NT_PB_GetXRRMonitors()可以获取active XRR-Monitor列表。

NT_PB_SetCaptureXRRMonitor()设置要采集的XRRMonitor id, 采集X屏幕时使用。

以RTMP推送模块为例,目前我们的功能设计如下:

Linux平台x64_64架构|aarch64架构RTMP直播推送模块

  • 音频编码:AAC/SPEEX;
  • 视频编码:H.264;
  • 推流协议:RTMP;
  • [音视频]支持纯音频/纯视频/音视频推送;
  • 支持X11屏幕采集;
  • 支持部分V4L2摄像头设备采集;
  • [屏幕/V4L2摄像头]支持帧率、关键帧间隔(GOP)、码率(bit-rate)设置;
  • [V4L2摄像头]支持V4L2摄像头设备选择(设备文件名范围:[/dev/video0, /dev/video63])、分辨率设置、帧率设置;
  • [V4L2摄像头]支持水平反转、垂直反转、0° 90° 180° 270°旋转;
  • [音频]支持基于alsa-lib接口的音频采集;
  • [音频]支持基于libpulse接口采集本机PulseAudio服务音频;
  • [预览]支持推送端实时预览;
  • [对接服务器]支持自建标准RTMP服务器或CDN;
  • 支持断网自动重连、网络状态回调;
  • 屏幕和摄像头合成/多层合成;
  • 支持窗口采集(一般不建议使用);
  • 支持实时快照;
  • 支持降噪处理、自动增益控制、VAD端点检测;
  • 支持扬声器和麦克风混音;
  • 支持外部编码前音视频数据对接;
  • 支持外部编码后音视频数据对接;
  • 支持实时音量调节;
  • 支持扩展录像模块;
  • 支持Unity接口;
  • 支持H.264扩展SEI发送模块;
  • 支持x64_64架构、aarch64架构(需要glibc-2.21及以上版本的Linux系统, 需要libX11.so.6, 需要GLib–2.0, 需安装 libstdc++.so.6.0.21、GLIBCXX_3.4.21、 CXXABI_1.3.9)。

RTMP推送调用示例

以大牛直播SDK的Linux平台RTMP直播推送模块为例,本Demo实现的是Linux上实现桌面和系统声音采集,然后使用RTMP协议推出去的一个SDK. 集成调用非常简单。

int main(int argc, char *argv[])
{signal(SIGINT, &OnSigIntHandler);//printf("sizeof(NT_SmartPublisherSDKAPI)=%d\n", sizeof(NT_SmartPublisherSDKAPI));LogInit();NT_SmartPublisherSDKAPI push_api;if (!PushSDKInit(push_api)){return 0;}auto push_handle = StartPush(&push_api, "rtmp://192.168.0.154:1935/live/test1", 30);if (!push_handle){fprintf(stderr, "start push failed.\n");push_api.UnInit();return 0;}while (!g_is_exit){sleep(2);}fprintf(stdout, "Skip run loop, is_exit:%d\n", g_is_exit);push_api.StopPublisher(push_handle);push_api.Close(push_handle);push_handle = nullptr;push_api.UnInit();fprintf(stdout, "SDK UnInit..\n");return 0;
}

相关初始化

	void OnSigIntHandler(int sig){if (SIGINT == sig){g_is_exit = true;}}void LogInit(){SmartLogAPI log_api;memset(&log_api, 0, sizeof(log_api));GetSmartLogAPI(&log_api);log_api.SetLevel(SL_INFO_LEVEL);log_api.SetPath((NT_PVOID)"./");}bool PushSDKInit(NT_SmartPublisherSDKAPI& push_api){memset(&push_api, 0, sizeof(push_api));NT_GetSmartPublisherSDKAPI(&push_api);auto ret = push_api.Init(0, nullptr);if (NT_ERC_OK != ret){fprintf(stderr, "push_api.Init failed!\n");return false;}else{fprintf(stdout, "push_api.Init ok!\n");}return true;}

推送接口封装

	NT_HANDLE StartPush(NT_SmartPublisherSDKAPI* push_api, const std::string& rtmp_url, int dst_fps){NT_INT32 pulse_device_number = 0;if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(2, &pulse_device_number)){fprintf(stdout, "Pulse device num:%d\n", pulse_device_number);char device_name[512];for (auto i = 0; i < pulse_device_number; ++i){if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(2, i, device_name, 512)){fprintf(stdout, "index:%d name:%s\n", i, device_name);}}}NT_INT32 alsa_device_number = 0;if (pulse_device_number < 1){if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(1, &alsa_device_number)){fprintf(stdout, "Alsa device num:%d\n", alsa_device_number);char device_name[512];for (auto i = 0; i < alsa_device_number; ++i){if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(1, i, device_name, 512)){fprintf(stdout, "index:%d name:%s\n", i, device_name);}}}}NT_INT32 capture_speaker_flag = 0;if ( NT_ERC_OK == push_api->IsCanCaptureSpeaker(2, &capture_speaker_flag) ){if (capture_speaker_flag)fprintf(stdout, "Support speaker capture\n");elsefprintf(stdout, "UnSupport speaker capture\n");}NT_INT32 is_support_window_capture = 0;if (NT_ERC_OK == push_api->IsCaptureWindowSupported(NULL, &is_support_window_capture)){if (is_support_window_capture)fprintf(stdout, "Support window capture\n");elsefprintf(stdout, "UnSupport window capture\n");}NT_HANDLE push_handle = nullptr;// if (NT_ERC_OK != push_api->Open(&push_handle, NT_PB_E_VIDEO_OPTION_LAYER, NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER, 0, NULL))if (NT_ERC_OK != push_api->Open(&push_handle, NT_PB_E_VIDEO_OPTION_SCREEN, NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER, 0, NULL)){return nullptr;}//push_api->SetXDisplayName(push_handle, ":0");//push_api->SetXDisplayName(push_handle, NULL);// 视频层配置方式//std::vector<std::shared_ptr<nt_pb_sdk::layer_conf_wrapper_base> > layer_confs;//auto index = 0;第0层填充RGBA矩形, 目的是保证帧率, 颜色就填充全黑//auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);//rgba_layer_c0->conf_.red_ = 0;//rgba_layer_c0->conf_.green_ = 0;//rgba_layer_c0->conf_.blue_ = 0;//rgba_layer_c0->conf_.alpha_ = 255;//layer_confs.push_back(rgba_layer_c0);第一层为桌面层//auto screen_layer_c1 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);////screen_layer_c1->conf_.scale_filter_mode_ = 3;//layer_confs.push_back(screen_layer_c1);//std::vector<const NT_PB_LayerBaseConfig* > layer_base_confs;//for (const auto& i : layer_confs)//{//	layer_base_confs.push_back(i->getBase());//}//if (NT_ERC_OK != push_api->SetLayersConfig(push_handle, 0, layer_base_confs.data(),//	layer_base_confs.size(), 0, nullptr))//{//	push_api->Close(push_handle);//	push_handle = nullptr;//	return nullptr;//}// push_api->SetScreenClip(push_handle, 0, 0, 1280, 720);push_api->SetFrameRate(push_handle, dst_fps); // 帧率设置push_api->SetVideoBitRate(push_handle, 2000);  // 平均码率2000kbpspush_api->SetVideoQualityV2(push_handle, 26); push_api->SetVideoMaxBitRate(push_handle, 4000); // 最大码率4000kbpspush_api->SetVideoKeyFrameInterval(push_handle, dst_fps*2); // 关键帧间隔push_api->SetVideoEncoderProfile(push_handle, 3); // h264 baselinepush_api->SetVideoEncoderSpeed(push_handle, 3); // 编码速度设置到3if (pulse_device_number > 0){push_api->SetAudioInputLayer(push_handle, 2);push_api->SetAuidoInputDeviceId(push_handle, 0);}else if (alsa_device_number > 0){push_api->SetAudioInputLayer(push_handle, 1);push_api->SetAuidoInputDeviceId(push_handle, 0);}// 音频配置push_api->SetPublisherAudioCodecType(push_handle, 1);//push_api->SetMute(push_handle, 1);if ( NT_ERC_OK != push_api->SetURL(push_handle, rtmp_url.c_str(), NULL) ){push_api->Close(push_handle);push_handle = nullptr;return nullptr;}if ( NT_ERC_OK != push_api->StartPublisher(push_handle, NULL) ){push_api->Close(push_handle);push_handle = nullptr;return nullptr;}return push_handle;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/54929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++位图】构建灵活的空间效率工具

目录 位图位图的基本概念如何用位图表示数据位图的基本操作setresettest 封装位图的设计 总结 在计算机科学中&#xff0c;位图&#xff08;Bitmap&#xff09;是一种高效的空间管理数据结构&#xff0c;广泛应用于各种场景&#xff0c;如集合操作、图像处理和资源管理。与传统…

一文读懂 Pencils Protocol 近期不可错过的市场活动

Pencils Protocol 是 Scroll 上综合性的 DeFi 协议&#xff0c;自 9 月 18 日开始其陆续在 Tokensoft、Bounce、Coresky 等平台开启 DAPP 通证的销售&#xff0c;并分别在短期内完成售罄。吸引了来自韩国、CIS、土耳其等 70 多个国家的 5 万多名认证用户&#xff0c;反响热烈&a…

Jmeter关联,断言,参数化

一、关联 常用的关联有三种 1.边界提取器 2.JSON提取器 3.正则表达式提取器 接下来就详细讲述一下这三种的用法 这里提供两个接口方便练习 登录接口 接口名称&#xff1a;登录 接口提交方式&#xff1a;POST 接口的url地址&#xff1a;https://admin-api.macrozheng.com/a…

C#常用数据结构栈的介绍

定义 在C#中&#xff0c;Stack<T> 是一个后进先出&#xff08;LIFO&#xff0c;Last-In-First-Out&#xff09;集合类&#xff0c;位于System.Collections.Generic 命名空间中。Stack<T> 允许你将元素压入栈顶&#xff0c;并从栈顶弹出元素。 不难看出&#xff0c;…

Vue引入js脚本问题记录(附解决办法)

目录 一、需求 二、import引入问题记录 三、解决方式 一、需求 我想在我的Vue项目中引入jquery.js和bootstrap.js这种脚本文件&#xff0c;但发现不能单纯的import引入&#xff0c;问题如下。 二、import引入问题记录 我直接这么引入&#xff0c;发现控制台报错TypeError: …

华为HarmonyOS地图服务 11 - 如何在地图上增加点注释?

场景介绍 本章节将向您介绍如何在地图的指定位置添加点注释以标识位置、商家、建筑等&#xff0c;并可以通过信息窗口展示详细信息。 点注释支持功能&#xff1a; 支持设置图标、文字、碰撞规则等。支持添加点击事件。 PointAnnotation有默认风格&#xff0c;同时也支持自定…

《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch9 策略梯度方法 Box 8.1 马尔可夫决策过程的平稳分布

Box 8.1&#xff1a; 马尔可夫决策过程的平稳分布 整理自 链接 分析平稳分布的关键工具是 P π ∈ R n n P_\pi \in {\mathbb R}^{n\times n} Pπ​∈Rnn&#xff0c;它是给定策略 π π π 下的概率转移矩阵。 如果状态被索引为 s 1 , ⋯ , s n s_1,\cdots, s_n s1​,⋯…

idea2021git从dev分支合并到主分支master

1、新建分支 新建一个名称为dev的分支&#xff0c;切换到该分支下面&#xff0c;输入新内容 提交代码到dev分支的仓库 2、切换分支 切换到主分支&#xff0c;因为刚刚提交的分支在dev环境&#xff0c;所以master是没有 3、合并分支 点击push&#xff0c;将dev里面的代码合并到…

图片尺寸不合适?这3款免费好用的AI绘图神器帮你免费无缝拓展!一键扩展画面之外的内容,真的泰裤啦!

大家好&#xff0c;我是灵魂画师向阳 在处理图片素材时&#xff0c;大家有没有遇到过尺寸不合适但又不能裁切的情况&#xff1f;是不是也想过图像要是能自己“长”出一块就好了&#xff1f;这种要求在以前或许很难实现&#xff0c;但生产式 AI 技术出现后它就不再是问题了&…

基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue

基于GIKT深度知识追踪模型的习题推荐系统 目录结构 Flask-BackEnd flask后端 app 后端主体文件 alg 深度学习模块 data 数据集data_process.py 数据预处理gikt.py GIKT模型pebg.py PEBG模型params.py 一些参数train.py 仅模型训练train_test.py 模型训练和测试-五折交叉验证t…

WebGIS开发四大开源框架对比

本篇文章主要介绍GIS开发四大地图框架的差异和特点。 Cesium: Cesium是目前主流的一款三维地图框架&#xff0c;支持桌面端、web端、移动端等多平台。Mapbox&#xff1a;高清经纬度矢量瓦片&#xff0c;个性化前端表达&#xff0c;前端矢量绘制&#xff0c;支持海量地名地址。…

数据库实验2—1

10-1 查询重量在[40,65]之间的产品信息 本题目要求编写SQL语句&#xff0c; 检索出product表中所有符合40 < Weight < 65的记录。 提示&#xff1a;请使用SELECT语句作答。 表结构: CREATE TABLE product (Pid varchar(20), --商品编号PName varchar(50), --商品名称…

仓颉编程入门2,启动HTTP服务

上一篇配置了仓颉sdk编译和运行环境&#xff0c;读取一个配置文件&#xff0c;并把配置文件简单解析了一下。 前面读取配置文件&#xff0c;使用File.readFrom()&#xff0c;这个直接把文件全部读取出来&#xff0c;返回一个字节数组。然后又创建一个字节流&#xff0c;给文件…

C语言实现常见的数据结构

栈 栈是一种后进先出&#xff08;LIFO, Last In First Out&#xff09;的数据结构 #include <stdio.h> #include <stdlib.h>#define MAX 100typedef struct {int data[MAX];int top; } Stack;// 初始化栈 void init(Stack *s) {s->top -1; }// 判断栈是否为空…

VBA技术资料MF204:右键多按钮弹出菜单中使用图标

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【python】【绘制小程序】动态爱心绘制

背景介绍 参考链接&#xff1a;https://blog.csdn.net/Python_HUHU/article/details/139703289点的背景颜色在开始修改&#xff1b;文字的颜色在最后修改。文字内容可以修改。 python 代码 import tkinter as tk import random from math import sin, cos, pi, log from PIL…

Cocos 3.8.3 实现外描边效果(逃课玩法)

本来想着用Cocos 的Shader Graph照搬Unity的思路来加外描边&#xff0c;发现不行&#xff0c;然后我就想弄两个物体不就行了吗&#xff0c;一个是放大的版本&#xff0c;再放大的版本上加一个材质&#xff0c;这个材质面剔除选择前面的面剔除就行了&#xff0c;果不其然还真行。…

如何使用ssm实现基于java web的防疫工作志愿者服务平台的设计与实现

TOC ssm693基于java web的防疫工作志愿者服务平台的设计与实现jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进…

详细解读,F5服务器负载均衡的技术优势

在现代大规模、高流量的网络使用场景中&#xff0c;为应对高并发和海量数据的挑战&#xff0c;服务器负载均衡技术应运而生。但凡知道服务器负载均衡这一名词的&#xff0c;基本都对F5有所耳闻&#xff0c;因为负载均衡正是F5的代表作&#xff0c;换句通俗易懂的话来说&#xf…

makefile和CMakeLists/C++包管理器

make 大家可能会很奇怪&#xff0c;都什么年代了&#xff0c;还学makefile&#xff0c;cmake都有些过时了&#xff0c;为什么还要再学这个呢&#xff1f; 我是这么看待这个问题的&#xff0c;cmake跨平台性还是很有有优势的&#xff0c;有着多年积累的底蕴&#xff0c;借助大模…