Qt/C++音视频开发52采集本地屏幕桌面的终极设计
一、前言
最开始设计的时候,只考虑了一个屏幕的情况,这种当然是最理想的情况,实际上双屏或者多屏的用户也不在少数,比如我这两个屏幕,屏幕1是1080P,屏幕2是2K分辨率,打印两个屏幕的区域是 QRect(0,0 1920x1030), QRect(1920,-208 2560x1390),可以看到有个负数值(可以在操作系统中的排列显示器拖动调整),而且如果屏幕左右的顺序调换下,比如2K的分辨率在前面,打印的屏幕区域是 QRect(0,0 1920x1030), QRect(-2560,-185 2560x1390),可以看到2K的这个屏幕XY坐标都是负数,你以为这就是所有的情况了吗?那就想错了,还有可能是上下屏幕排列的,2K屏幕在下面打印区域 QRect(0,0 1920x1030), QRect(-639,1080 2560x1390),2K屏幕在上面打印区域是 QRect(0,0 1920x1030), QRect(-270,-1440 2560x1390),这还支持两个屏幕的情况,如果是4个或者更多呢,如果要用户获取到对应屏幕的区域然后填入桌面录制参数中,无异于难于上青天,这肯定是不可能的事情,而ffmpeg默认的参数就是要传入真实的偏移值坐标和分辨率,而用户呢又习惯于在哪个屏幕打开的程序就以当前屏幕的分辨率为基准,偏移值以左上角(0, 0)为基准,所以约定用户只需要填入分辨率和相对偏移值就行,不填入就以当前屏幕整体分辨率为准,这就需要搞一个专门的转换函数,专门获取当前屏幕区域并计算各种情况。
经过上面大费周折的计算,以为可以关机回家吃饭加鸡腿了,又想多了,用户可能输入了超过当前分辨率的区域,或者偏移值加上采集分辨率超过了当前屏幕的分辨率,这样是无法打开的,无法正常采集,程序不会执行,为了能够增强健壮性兼容性,有需要做一些调整,比如计算后发现设定的采集区域尺寸超过了屏幕的真实分辨率尺寸,就以设定的偏移值开始到右下角为准裁剪,这样无论用户怎么错,程序就是不会错,都能正常采集,以合理的方式进行调整,这才是一个好的程序设计。
演示视频:www.bilibili.com/video/BV1D8…
至此已实现的采集桌面的功能:
- 支持多屏幕,可以指定屏幕索引。
- 支持左右排列和上下排列以及自由调整屏幕位置。
- 支持指定采集区域。
- 自动校正超过屏幕区域的参数设定。
- 指定相对偏移值采集,以桌面左上角为基准。
- 支持指定采集帧率。
- 不填写分辨率和各种参数,自动计算默认值。
- 不指定屏幕则以鼠标所在当前屏幕为准。
- 还有更多的细节在代码中体现。
格式说明:
二、效果图
三、体验地址
四、功能特点
五、相关代码
void AbstractVideoThread::checkDeviceUrl(const QString &url, QString &deviceName, QString &resolution, int &frameRate, int &offsetX, int &offsetY, QString &encodeScale) { //无论是否带分隔符第一个约定是设备名称 QStringList list = url.split("|"); int count = list.count(); deviceName = list.at(0); //默认不指定屏幕索引 int screenIndex = -1; //用一个无用的参数作为是否是本地摄像头的标志位 bool isCamera = (encodeScale == "camera"); //带分隔符说明还指定了分辨率或帧率 if (count > 1) { QStringList sizes = WidgetHelper::getSizes(list.at(1)); if (sizes.count() == 2) { int width = sizes.at(0).toInt(); int height = sizes.at(1).toInt(); resolution = QString("%1x%2").arg(width).arg(height); } else { resolution = "0x0"; } //第三个参数是帧率 if (count >= 3) { frameRate = list.at(2).toInt(); } //桌面采集还需要取出其他几个参数 if (!isCamera) { //XY坐标偏移值 if (count >= 5) { offsetX = list.at(3).toInt(); offsetY = list.at(4).toInt(); } //屏幕索引 if (count >= 6) { screenIndex = list.at(5).toInt(); } //视频缩放 if (count >= 7) { encodeScale = list.at(6); } WidgetHelper::checkRect(screenIndex, resolution, offsetX, offsetY); } } //没有设置分辨率则重新处理 if (resolution == "0x0") { if (isCamera) { resolution = "640x480"; } else { WidgetHelper::checkRect(screenIndex, resolution, offsetX, offsetY); } } } QList WidgetHelper::getScreenRects() { QList rects; #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) int screenCount = qApp->screens().count(); QList screens = qApp->screens(); for (int i = 0; i desktop()->screenCount(); QDesktopWidget *desk = qApp->desktop(); for (int i = 0; i = 0 && screenIndex rect.width()) { offsetX = 0; } if (offsetY > rect.height()) { offsetY = 0; } //判断设定的偏移值加上设定的分辨率是否超出了真实的分辨率 QStringList sizes = WidgetHelper::getSizes(resolution); int width = sizes.at(0).toInt(); int height = sizes.at(1).toInt(); if (offsetX + width > rect.width()) { width = rect.width() - offsetX; } if (offsetY + height > rect.height()) { height = rect.height() - offsetY; } //如果超出了分辨率则重新设置采集的分辨率 resolution = WidgetHelper::getResolution(width, height); //多个屏幕需要加上屏幕起始坐标 if (offsetX == 0) { offsetX = rect.x(); } else { offsetX += rect.x(); } if (offsetY == 0) { offsetY = rect.y(); } else { offsetY += rect.y(); } //qDebug()