一、前言
之前用ffmpeg打通了各种视频文件和视频流以及本地摄像头设备的采集,近期有个客户需求要求将整个桌面屏幕采集下来,并可以录制保存成MP4文件,以前也遇到过类似的需求,由于没有搞过,也没有精力去摸索和测试,所以也就一直耽搁着,近期刚好这个需求又来了,定下心来试了试,原来非常简单,模式和采集本地摄像头设备一样,调用av_find_input_format的参数从dshow改成gdigrab即可,然后打开的地址改成desktop,而且采集后保存到MP4文件的整个流程完全一致,根本不用做任何改动,完全可用,可以说ffmpeg封装的非常好,考虑的也非常周到,各种函数接口设计的也非常美妙。
在实现这个功能之前,首先第一步就是用命令行测试,比如 ffplay -f gdigrab desktop -video_size 640x480 -framerate 30 表示开启桌面采集,从左上角00坐标开始分辨率为 640x480 的矩形区域,按照帧率30播放,只有命令行可用的情况下,才表示用ffmpeg代码行得通,如果命令行都不可用,那要仔细思考问题出在哪里,是命令行本身写法有问题还是设备不支持。包括后面有ffmpeg实现推流功能也是这个思路,先上命令行试试行不行,只有可行的情况下才可以大胆的用代码实现,相当于先用命令行探索一番。
不同操作系统对应设备采集格式名称不一样,需要区别处理。
void FFmpegThread::initInputFormat()
{
//本地摄像头/桌面录屏
if (videoType == VideoType_Camera) {
#if defined(Q_OS_WIN)
//ifmt = av_find_input_format("vfwcap");
ifmt = av_find_input_format("dshow");
#elif defined(Q_OS_LINUX)
//可以打开cheese程序查看本地摄像头(如果是在虚拟机中需要设置usb选项3.1)
//ifmt = av_find_input_format("v4l2");
ifmt = av_find_input_format("video4linux2");
#elif defined(Q_OS_MAC)
ifmt = av_find_input_format("avfoundation");
#endif
} else if (videoType == VideoType_Desktop) {
#if defined(Q_OS_WIN)
ifmt = av_find_input_format("gdigrab");
#elif defined(Q_OS_LINUX)
ifmt = av_find_input_format("x11grab");
#elif defined(Q_OS_MAC)
ifmt = av_find_input_format("avfoundation");
#endif
}
}
二、效果图
三、体验地址
- 国内站点:https://gitee.com/feiyangqingyun
- 国际站点:https://github.com/feiyangqingyun
- 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_demo/bin_linux_video。
四、相关代码
void AbstractVideoThread::checkDeviceUrl(const QString &url, QString &deviceName, QString &resolution, int &frameRate)
{
int offsetX, offsetY;
AbstractVideoThread::checkDeviceUrl(url, deviceName, resolution, frameRate, offsetX, offsetY);
}
void AbstractVideoThread::checkDeviceUrl(const QString &url, QString &deviceName, QString &resolution, int &frameRate, int &offsetX, int &offsetY)
{
//无论是否带分隔符第一个约定是设备名称
QStringList list = url.split("|");
deviceName = list.at(0);
//带分隔符说明还指定了分辨率或帧率
if (url.contains("|")) {
QStringList sizes;
QString size = list.at(1);
if (size.contains("*")) {
sizes = size.split("*");
} else if (size.contains("x")) {
sizes = size.split("x");
}
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 (list.count() >= 3) {
frameRate = list.at(2).toInt();
}
//第四第五个参数是XY坐标偏移值
if (list.count() >= 5) {
offsetX = list.at(3).toInt();
offsetY = list.at(4).toInt();
}
}
//设置个默认的分辨率(几乎所有本地设备都支持这个分辨率)
if (resolution == "0x0" && (url.startsWith("video=") || url.startsWith("/dev"))) {
resolution = "640x480";
}
}
void VideoHelper::getDesktopPara(const VideoCore &videoCore, QString &videoUrl, QString &bufferSize, int &frameRate, int &offsetX, int &offsetY)
{
//ffmpeg -f gdigrab -s 800x600 -r 30 -i desktop out.mp4
//ffmpeg -f x11grab -s 800x600 -r 30 -i :0.0+0,0 out.mp4
//ffmpeg -f avfoundation -s 800x600 -r 30 -i 0:0 out.mp4
QString url = videoUrl;
url.replace("desktop=", "");
url.replace("screen://", "");
url.replace("avdevice:gdigrab:", "");
AbstractVideoThread::checkDeviceUrl(url, videoUrl, bufferSize, frameRate, offsetX, offsetY);
//不同内核对应地址不一样
if (videoCore == VideoCore_FFmpeg) {
#if defined(Q_OS_WIN)
url = "desktop";
#elif defined(Q_OS_LINUX)
url = ":0.0+0,0";
#elif defined(Q_OS_MAC)
url = "0:0";
#endif
} else if (videoCore == VideoCore_Vlc) {
url = "screen://";
} else if (videoCore == VideoCore_Qtav) {
url = "avdevice:gdigrab:desktop";
} else if (videoCore == VideoCore_EasyPlayer) {
url = "gdigrab://desktop";
}
videoUrl = url;
}
五、功能特点
5.1 基础功能
- 支持各种音频视频文件格式,比如mp3、wav、mp4、asf、rm、rmvb、mkv等。
- 支持本地摄像头设备,可指定分辨率、帧率。
- 支持各种视频流格式,比如rtp、rtsp、rtmp、http等。
- 本地音视频文件和网络音视频文件,自动识别文件长度、播放进度、音量大小、静音状态等。
- 文件可以指定播放位置、调节音量大小、设置静音状态等。
- 支持倍速播放文件,可选0.5倍、1.0倍、2.5倍、5.0倍等速度,相当于慢放和快放。
- 支持开始播放、停止播放、暂停播放、继续播放。
- 支持抓拍截图,可指定文件路径,可选抓拍完成是否自动显示预览。
- 支持录像存储,手动开始录像、停止录像,部分内核支持暂停录像后继续录像,跳过不需要录像的部分。
- 支持无感知切换循环播放、自动重连等机制。
- 提供播放成功、播放完成、收到解码图片、收到抓拍图片、视频尺寸变化、录像状态变化等信号。
- 多线程处理,一个解码一个线程,不卡主界面。
5.2 特色功能
- 同时支持多种解码内核,包括qmedia内核(Qt4/Qt5/Qt6)、ffmpeg内核(ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5)、vlc内核(vlc2/vlc3)、mpv内核(mpv1/mp2)、海康sdk、easyplayer内核等。
- 非常完善的多重基类设计,新增一种解码内核只需要实现极少的代码量,就可以应用整套机制。
- 同时支持多种画面显示策略,自动调整(原始分辨率小于显示控件尺寸则按照原始分辨率大小显示,否则等比例缩放)、等比例缩放(永远等比例缩放)、拉伸填充(永远拉伸填充)。所有内核和所有视频显示模式下都支持三种画面显示策略。
- 同时支持多种视频显示模式,句柄模式(传入控件句柄交给对方绘制控制)、绘制模式(回调拿到数据后转成QImage用QPainter绘制)、GPU模式(回调拿到数据后转成yuv用QOpenglWidget绘制)。
- 支持多种硬件加速类型,ffmpeg可选dxva2、d3d11va等,mpv可选auto、dxva2、d3d11va,vlc可选any、dxva2、d3d11va。不同的系统环境有不同的类型选择,比如linux系统有vaapi、vdpau,macos系统有videotoolbox。
- 解码线程和显示窗体分离,可指定任意解码内核挂载到任意显示窗体,动态切换。
- 支持共享解码线程,默认开启并且自动处理,当识别到相同的视频地址,共享一个解码线程,在网络视频环境中可以大大节约网络流量以及对方设备的推流压力。国内顶尖视频厂商均采用此策略。这样只要拉一路视频流就可以共享到几十个几百个通道展示。
- 自动识别视频旋转角度并绘制,比如手机上拍摄的视频一般是旋转了90度的,播放的时候要自动旋转处理,不然默认是倒着的。
- 自动识别视频流播放过程中分辨率的变化,在视频控件上自动调整尺寸。比如摄像机可以在使用过程中动态配置分辨率,当分辨率改动后对应视频控件也要做出同步反应。
- 音视频文件无感知自动切换循环播放,不会出现切换期间黑屏等肉眼可见的切换痕迹。
- 视频控件同时支持任意解码内核、任意画面显示策略、任意视频显示模式。
- 视频控件悬浮条同时支持句柄、绘制、GPU三种模式,非绝对坐标移来移去。
- 本地摄像头设备支持指定设备名称、分辨率、帧率进行播放。
- 录像文件同时支持打开的视频文件、本地摄像头、网络视频流等。
- 瞬间响应打开和关闭,无论是打开不存在的视频或者网络流,探测设备是否存在,读取中的超时等待,收到关闭指令立即中断之前的操作并响应。
- 支持打开各种图片文件,支持本地音视频文件拖曳播放。
- 视频控件悬浮条自带开始和停止录像切换、声音静音切换、抓拍截图、关闭视频等功能。
- 音频组件支持声音波形值数据解析,可以根据该值绘制波形曲线和柱状声音条,默认提供了声音振幅信号。
- 各组件中极其详细的打印信息提示,尤其是报错信息提示,封装的统一打印格式。针对现场复杂的设备环境测试极其方便有用,相当于精确定位到具体哪个通道哪个步骤出错。
- 代码框架和结构优化到最优,性能强悍,持续迭代更新升级。
- 源码支持Qt4、Qt5、Qt6,兼容所有版本。
5.3 视频控件
- 可动态添加任意多个osd标签信息,标签信息包括名字、是否可见、字号大小、文本文字、文本颜色、标签图片、标签坐标、标签格式(文本、日期、时间、日期时间、图片)、标签位置(左上角、左下角、右上角、右下角、居中、自定义坐标)。
- 可动态添加任意多个图形信息,这个非常有用,比如人工智能算法解析后的图形区域信息直接发给视频控件即可。图形信息支持任意形状,直接绘制在原始图片上,采用绝对坐标。
- 图形信息包括名字、边框大小、边框颜色、背景颜色、矩形区域、路径集合、点坐标集合等。
- 每个图形信息都可指定三种区域中的一种或者多种,指定了的都会绘制。
- 内置悬浮条控件,悬浮条位置支持顶部、底部、左侧、右侧。
- 悬浮条控件参数包括边距、间距、背景透明度、背景颜色、文本颜色、按下颜色、位置、按钮图标代码集合、按钮名称标识集合、按钮提示信息集合。
- 悬浮条控件一排工具按钮可自定义,通过结构体参数设置,图标可选图形字体还是自定义图片。
- 悬浮条按钮内部实现了录像切换、抓拍截图、静音切换、关闭视频等功能,也可以自行在源码中增加自己对应的功能。
- 悬浮条按钮对应实现了功能的按钮,有对应图标切换处理,比如录像按钮按下后会切换到正在录像中的图标,声音按钮切换后变成静音图标,再次切换还原。
- 悬浮条按钮单击后都用名称唯一标识作为信号发出,可以自行关联响应处理。
- 悬浮条空白区域可以显示提示信息,默认显示当前视频分辨率大小,可以增加帧率、码流大小等信息。
- 视频控件参数包括边框大小、边框颜色、焦点颜色、背景颜色(默认透明)、文字颜色(默认全局文字颜色)、填充颜色(视频外的空白处填充黑色)、背景文字、背景图片(如果设置了图片优先取图片)、是否拷贝图片、缩放显示模式(自动调整、等比例缩放、拉伸填充)、视频显示模式(句柄、绘制、GPU)、启用悬浮条、悬浮条尺寸(横向为高度、纵向为宽度)、悬浮条位置(顶部、底部、左侧、右侧)。