在经过多种内核的洗礼以后,逐渐对不同内核的不同音视频文件和视频流进行大量的对比测试,比如测试对各种格式的支持性,对各种网络流的支持程度,在同一个地址下占用的CPU/GPU资源比对,最终发现播放器这块mpv优于vlc(可能vlc的插件太过于庞大臃肿),尤其是对8K视频的支持,Qt自身的QMediaplayer也是非常吃力,哪怕用的是性能暴增的Qt6,估计还是没有单独对这种超大分辨率进行友好的处理,可能用的极少吧。ffmpeg在开启了硬解码的情况下,也是挺吃力,主要的耗时操作大量的停顿在使用 av_hwframe_transfer_data 函数将解码后的数据从GPU拷贝的CPU,后期这块还要想办法优化一下,尽量不用这个转换处理就直接GPU那边绘制掉。有个朋友的处理是转换后再将nv12的数据转换成yuv再绘制,这就更要不得了,相当于两次触发了CPU运算,不如直接将取出来的nv12数据用opengl绘制。
对8K视频的支持,vlc和mpv都做得很好,前提是开启了硬件加速,比如vlc要选择any,mpv选择auto,不开启的话也是卡顿,总体对比测试下来发现,对于小分辨率比如2K以下的,主要的耗时操作在绘制,如果是采用opengl绘制则可以大大降低CPU占用,而对于大分辨率比如4K及以上,硬解码的占比更大,尽管绘制那边用的opengl如果没有开启硬解码也是很容易卡顿,一旦开启性能指数级暴增。
公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。
公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。


bool MpvThread::openVideo()
{
//先检查地址是否正常(文件是否存在或者网络地址是否可达)
if (!VideoHelper::checkUrl(this, videoType, videoUrl, connectTimeout)) {
return false;
}
//启动计时
timer.start();
//创建实例
if (!mpvPlayer) {
mpvPlayer = mpv_create();
}
//创建失败则返回
if (!mpvPlayer) {
return false;
}
#if 0
//osd-level=2 显示当前播放时间 osd-level=3 显示播放时间和总时间
setValue("osd-level", 1);
setValue("osd-color", "#FF0000");
setValue("osd-font-size", 30);
setValue("osd-msg1", "通道名称");
//setValue("osd-msg2", "当前时间");
//setValue("osd-width", 10);
//setValue("osd-height", 100);
#endif
#if 0
QVariantList list;
list << "overlay_add" << "test1" << 100 << 100 << "xxx" << 10 << "gbra" << 10 << 10 << 0 << NULL;
command(list);
//QByteArray tmp = QString::number(id).toUtf8();
//const char *args[] = {"overlay_remove", tmp.constData(), NULL};
//const char *args[] = {"sub-add", tmp.constData(), NULL};
//const char *args[] = {"set", "sub-visibility", b ? "yes" : "no", NULL};
#endif
//std::setlocale(LC_NUMERIC, "C");
//通过句柄的方式设置播放
#ifdef Q_OS_WIN
HWND wid = (HWND)hwndWidget->winId();
#else
void *wid = (void *)hwndWidget->winId();
#endif
if (mpv_set_option(mpvPlayer, "wid", MPV_FORMAT_INT64, &wid) < 0) {
return false;
}
//请求级别日志消息
mpv_request_log_messages(mpvPlayer, "none");
//启用默认绑定
//setValue("input-default-bindings", "yes");
//启用键盘输入
//setValue("input-vo-keyboard", "yes");
//设置控制台打印
setOption("terminal", "false");
//设置消息级别
setOption("msg-level", "all=v");
//设置硬件加速(none auto any dxva2 d3d11va)
setOption("hwdec", hardware);
//设置通信协议(tcp udp)
setOption("rtsp-transport", transport);
//设置网络超时时间(单位秒)
setOption("network-timeout", 3);
//初始化实例
if (mpv_initialize(mpvPlayer) < 0) {
return false;
}
//创建事件管理器
MpvHelper::attachEvents(mpvPlayer);
mpv_set_wakeup_callback(mpvPlayer, MpvHelper::wakeup, this);
//打开视频地址播放
QByteArray data = videoUrl.toUtf8();
//command(QVariantList() << "loadfile" << data.data());
const char *args[] = {"loadfile", data.data(), NULL};
//if (mpv_command(mpvPlayer, args) < 0) {
if (mpv_command_async(mpvPlayer, 0, args) < 0) {
return false;
}
//打印支持的属性列表和命令列表
//qDebug() << TIMEMS << getValue("property-list") << getValue("command-list");
//打印组件的版本
//qDebug() << TIMEMS << getValue("mpv-version") << getValue("ffmpeg-version");
lastTime = QDateTime::currentDateTime();
int time = timer.elapsed();
debug("打开成功", QString("用时: %1 毫秒").arg(time));
//只有获取到了宽高信息才算真正打开完成
//emit receivePlayStart(time);
emit recorderStateChanged(RecorderState_Stopped, fileName);
isOk = true;
return isOk;
}
void MpvThread::closeVideo()
{
//先停止录制
recordStop();
//搞个标志位判断是否需要调用父类的释放(可以防止重复调用)
bool needClose = (mpvPlayer);
//释放对象
if (mpvPlayer) {
MpvHelper::detachEvents(mpvPlayer);
mpv_terminate_destroy(mpvPlayer);
mpvPlayer = NULL;
}
if (needClose) {
VideoThread::closeVideo();
}
}