QTAV的编译、使用及遇到的问题

2024-01-11 21:04
文章标签 编译 问题 使用 遇到 qtav

本文主要是介绍QTAV的编译、使用及遇到的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近有个功能需求,界面可以直播显示某个特定摄像头的实时画面,通过对Qt本身的 QMiediaPlayer,和其他一些在网上找到的组件做了一些对比,最终选择了QtAv作为我们的组件使用。

QtAV 是一个基于 Qt 和 FFmpeg 的跨平台、高性能多媒体播放框架。

一、QtAV的编译

从 Github 上下载到的是 QtAV 的源码,或者说由于每个开发者使用的 VS 或者 Qt 版本的不同,从而迫使我们不得不自己手动对 QtAV 进行编译,而 QtAV 的编译过程相对来说是非常简单的。

  1. 下载编译依赖组件

    因为 QtAV 是一个基于 Qt 和 FFmpeg 的跨平台多媒体播放器,所以在编译 QtAV 之前,需要我们下载一些依赖的组件。

    对于 FFmpeg, 可以选择下载其源码并自己构建。

    也可以选择直接下载已经构建好的组件。对于 windows 的用户,可以选择下载文件。 QtAV-depends-windows-x86+x64.7z

    为了方便,我这边是选择下载已经编译好的组件版本。

  2. 下载QtAv源码

    QtAV 的源码在 GitHub 上可以直接下载。

  3. 编译

    编译之前,我们需要将下载下来的 QtAV 依赖的组件进行引用,因为我们需要使用 QtCreator 编译 QtAv,所以,首先需要将 QtAV-depends-windows-x86+x64.7z 解压缩之后的文件夹中的 lib 和 include 文件夹拷贝到你本地 Qt 对应的目录下。

    需要注意的是,需要对应编译的版本,比如,我用的 msvc2017_64 编译,所以对应的就需要拷贝到下面的路径 D:\Qt\Qt5.12.5\5.12.5\msvc2017_64\include。也可以不进行拷贝,而是通过修改构建环境里面的INCLUDE和LIB的值进行对应的添加,这边需要注意的是,一定要注意是32位还是64位

    拷贝完成之后,直接用 QtCreator 打开 QtAV 源码中的 QtAV.pro 文件,右键构建即可,等待编译完成。

    编译完成之后,在编译路径下面 lib_win_x86_64 文件夹下面会生成需要的 dll 和 lib 文件,如果以后需要方便的使用,则可以双击编译路径下面的 sdk_install.bat 文件,该文件的作用是,将编译生成的动态库、静态库已经头文件全部复制到编译 QtAV 对应的 Qt 目录下,这样,以后在使用的时候,直接在 pro 文件中引入 QtAV 模块即可。QT += av avwidgets

    如果使用 VS + QT 开发环境,也可以直接使用,或者将对应的库文件拷贝使用。
    相应的 sdk_uninstall.bat 文件会删除拷贝的文件。

二、QtAv的使用

为了方便,我对多媒体播放的部分进行了简单的封装。提供了一些简单的接口,以便外部的调用,而这个程序因为比较简单,所以我用了一个按钮来控制视频的播放和暂停。并且使用了 hideEvent 来作为 stop 的信号。

class AvMediaWidget : public QWidget
{Q_OBJECTpublic:...void setSource(const QString& source);void pause(const bool pause);void stop();void start();protected:virtual void enterEvent(QEvent *event) override;virtual void leaveEvent(QEvent *event) override;virtual void hideEvent(QHideEvent *event) override;
private:void initPage();private:Ui::AvMediaWidget *ui;QtAV::AVPlayer* m_player{ nullptr };QtAV::WidgetRenderer* m_renderer{ nullptr };QString m_source{ QString() };
};

首先,直接 new 一个 AVPlayer 的对象,因为我没有用到Video,所以也就用可一个简单的 WidgetRenderer 作为 player的载体,如果需要做视频播放器的话,可以使用 VideoWidget 类。

void AvMediaWidget::initPage()
{m_player = new AVPlayer();m_renderer = new WidgetRenderer();Widgets::registerRenderers();ui->verticalLayout->addWidget(m_renderer);m_renderer->show();m_player->setRenderer(m_renderer);m_player->setBufferMode(QtAV::BufferBytes);m_player->setBufferValue(1024);...
}

紧接着,我用了一个QPushButton,这个按钮的功能主要是用来控制播放的播放、暂停。样式了,就好比是我们经常看视频时点了暂停的那样。

{auto btn = new QPushButton(this);auto layout = new QHBoxLayout(this);btn->setProperty("type", "player");layout->addWidget(btn);layout->setContentsMargins(15, 0, 0, 0);m_renderer->setLayout(layout);connect(btn, &QPushButton::clicked, this, [this](){if (!m_player->isPlaying()){start();auto btn = static_cast<QPushButton*>(sender());if (btn == nullptr){return;}btn->setVisible(false);return;}m_player->pause(!m_player->isPaused());auto btn = static_cast<QPushButton*>(sender());if (btn == nullptr){return;}btn->setProperty("status", m_player->isPaused() ? "player-start" : "player-stop");this->style()->unpolish(btn);this->style()->polish(btn);});...
}

下面是为了在刚开始或者是停止播放之后,使得界面上会一直存在一个 start 的三角形,所以,对按钮进行了样式表的设置。

 {connect(m_player, &AVPlayer::stopped, this, [this]() {auto btn = m_renderer->findChild<QPushButton*>();if (btn == nullptr){return;}btn->setProperty("status", "player-start");btn->setVisible(true);this->style()->unpolish(btn);this->style()->polish(btn);});
}

而我们希望在播放的过程中,不能有一个按钮影响观感,所以,用了下面两个函数,来控制按钮的显示和隐藏以及根据当前播放器的状态进行样式表的设置。

void AvMediaWidget::enterEvent(QEvent *event)
{if (!m_player->isPlaying()){return;}auto btn = m_renderer->findChild<QPushButton*>();if (btn == nullptr){return;}btn->setVisible(true);btn->setProperty("status", m_player->isPaused() ? "player-start" : "player-stop");this->style()->unpolish(btn);this->style()->polish(btn);
}
void AvMediaWidget::leaveEvent(QEvent *event)
{if (!m_player->isPlaying() || m_player->isPaused()){return;}auto btn = m_renderer->findChild<QPushButton*>();if (btn == nullptr){return;}btn->setVisible(false);
}

三、遇到的问题

1、 VS + QT 环境中报错

因为我使用的开发环境是 VS + QT 的,所以,我在使用的过程中需要将编译好的库文件拷贝到我对应的工程下面,并进行引用。

而在我的测试工程中,发现一个很有意思的事情,就是 Debug 模式下, 一切正常,但只要一切到 Release 版本,通过单步调试,发现,只要执行

m_renderer = new WidgetRenderer();

软件必崩,并且伴随报错 QWidget: Must construct a QApplication before a QWidget

这个错误消息,一般对应着两种情况:

  1. 就是在 main.cpp 执行 QApplication a(argc, argv); 之前就已经有 QWidget 对象被构建了,这主要体现在一下 static QWidget

  2. 很简单,就是 Debug 和 Release 版本对不上,也就是混用了

很明确的是我知道我的整个工程里面是没有静态的 QWidget 的,那么就只剩下第二种一种情况了,就是 Debug 和 Release 版本混用了。为了验证问题,我排查了好几遍,甚至写了有一个 QtCreator 的工程去验证了了下是不是编译库文件的问题,结果没问题。

也找了很多的信息,反正就是只要一起,就崩。debug就是没问题。

也不知道什么时候,突然想起来,既然我现在能保证我所有的 VS 的配置是正确的,但是 VS 是有 Qt 的,那么这个Qt的配置是不是正确的,检查发现,果然,我在VS的release下面配的Qt是debug的。

一个很简单的问题,却耗费了我大半个下午的时候去排查问题。而我们经常会忽略这些很小的细节,以至于多花费更多的精力和时间。

2、rtsp 服务的拉流总是会失败

测试的过程中发现,用 rtmp 服务拉流是没问题的,但是拉取 rtsp 服务的流的时候总是拉不下来,然后我用前面下载下来的 ffmpeg 单独做了一组测试。

ffplay.exe rtsp://.... 
ffplay.exe rtmp://ns8.indexforce.com/home/mystream
ffplay.exe -rtsp_transport tcp rtsp://.... 

发现,后面两种情况是可一的。那么,问题就是该找找要怎样去设置当需要拉取 rtsp 服务流的时候,使用 tcp 去拉。

后来没发现,就改了一下源码,进行重编译。

在 QtAV 的源码中找到 AVDemuxer.cpp 文件,在该文件

void checkNetwork() {// FIXME: is there a good way to check network? now use URLContext.flags == URL_PROTOCOL_FLAG_NETWORK// not network: concat cache pipe avdevice crypto?if (!file.isEmpty()&& file.contains(QLatin1String(":"))&& (file.startsWith(QLatin1String("http")) //http, https, httpproxy|| file.startsWith(QLatin1String("rtmp")) //rtmp{,e,s,te,ts}|| file.startsWith(QLatin1String("mms")) //mms{,h,t}|| file.startsWith(QLatin1String("ffrtmp")) //ffrtmpcrypt, ffrtmphttp|| file.startsWith(QLatin1String("rtp:"))|| file.startsWith(QLatin1String("rtsp:"))|| file.startsWith(QLatin1String("sctp:"))|| file.startsWith(QLatin1String("tcp:"))|| file.startsWith(QLatin1String("tls:"))|| file.startsWith(QLatin1String("udp:"))|| file.startsWith(QLatin1String("gopher:")))) {network = true;}
}

方法末尾增加下面代码,告诉,如果是 rtsp 服务,则使用 TCP 方式。

if(file.startsWith(QLatin1String("rtsp:")))options[QStringLiteral("rtsp_transport")]=QStringLiteral("tcp");

3、rtsp 测试地址太少了

基本上在网上能找到的能够正常测试的 rtsp 地址都已经不能用了,要怎么办呢?

访问 rtsp 的 官网。
拉到最下方,在 free 区域下面点击 Get Started 按钮,在接下来的界面中输入可用的邮箱,rtsp 会发送一封邮件,打开邮件中的地址,会生成两条可测试的 rtsp 流地址,并且每个月有 2G 的流量。

后续在测试的过程中还发现了其他的一些问题,比如花屏问题,也找了一些其他的库做了对比,下一节再来展开看看。

这篇关于QTAV的编译、使用及遇到的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/595688

相关文章

Qt spdlog日志模块的使用详解

《Qtspdlog日志模块的使用详解》在Qt应用程序开发中,良好的日志系统至关重要,本文将介绍如何使用spdlog1.5.0创建满足以下要求的日志系统,感兴趣的朋友一起看看吧... 目录版本摘要例子logmanager.cpp文件main.cpp文件版本spdlog版本:1.5.0采用1.5.0版本主要

Java中使用Hutool进行AES加密解密的方法举例

《Java中使用Hutool进行AES加密解密的方法举例》AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个,下面:本文主要介绍Java中使用Hutool进行AES加密解密的相关资料... 目录前言一、Hutool简介与引入1.1 Hutool简介1.2 引入Hutool二、AES加密解密基础

使用Python将JSON,XML和YAML数据写入Excel文件

《使用Python将JSON,XML和YAML数据写入Excel文件》JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体,本文将介绍如何... 目录如何使用python写入数据到Excel工作表用Python导入jsON数据到Excel工作表用

Pyserial设置缓冲区大小失败的问题解决

《Pyserial设置缓冲区大小失败的问题解决》本文主要介绍了Pyserial设置缓冲区大小失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录问题描述原因分析解决方案问题描述使用set_buffer_size()设置缓冲区大小后,buf

resultMap如何处理复杂映射问题

《resultMap如何处理复杂映射问题》:本文主要介绍resultMap如何处理复杂映射问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录resultMap复杂映射问题Ⅰ 多对一查询:学生——老师Ⅱ 一对多查询:老师——学生总结resultMap复杂映射问题

鸿蒙中@State的原理使用详解(HarmonyOS 5)

《鸿蒙中@State的原理使用详解(HarmonyOS5)》@State是HarmonyOSArkTS框架中用于管理组件状态的核心装饰器,其核心作用是实现数据驱动UI的响应式编程模式,本文给大家介绍... 目录一、@State在鸿蒙中是做什么的?二、@Spythontate的基本原理1. 依赖关系的收集2.

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

java实现延迟/超时/定时问题

《java实现延迟/超时/定时问题》:本文主要介绍java实现延迟/超时/定时问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java实现延迟/超时/定时java 每间隔5秒执行一次,一共执行5次然后结束scheduleAtFixedRate 和 schedu

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

idea maven编译报错Java heap space的解决方法

《ideamaven编译报错Javaheapspace的解决方法》这篇文章主要为大家详细介绍了ideamaven编译报错Javaheapspace的相关解决方法,文中的示例代码讲解详细,感兴趣的... 目录1.增加 Maven 编译的堆内存2. 增加 IntelliJ IDEA 的堆内存3. 优化 Mave