首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Qt6 QML 中渲染自定义视频帧的改进 2023-05-30 更新

Qt6 QML 中渲染自定义视频帧的改进 2023-05-30 更新

作者头像
我与梦想有个约会
发布于 2023-10-21 07:40:14
发布于 2023-10-21 07:40:14
1.4K20
代码可运行
举报
文章被收录于专栏:jiajia_dengjiajia_deng
运行总次数:0
代码可运行

最近在升级音视频的项目 Qt 版本,从 5.15.0 升级到 6.4.3(6.5 也一样),除了一些 QML 中删除了一些 Qt Quick Controls 1 的控件以外,最重要的就是自定义视频渲染的改进。

QAbstractVideoSurface 变为 QVideoSink

Qt5 中在 QML 上渲染自定义视频帧时需要在 C++ 层实现一个派生于 QObject 的子类,内部使用 QAbstractVideoSurface 来给 VideoOutput 提供数据,具体方法这里就不讨论了,可以参考我之前写的文章 Qt QML VideoOutput 显示自定义的 YUV420P 数据流 在 Qt6 中,QAbstractVideoSurface 被 QVideoSink 替代,提供了更简单的方式来投递一个 QVideoFrame,示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class FrameProvider : public QObject {
    Q_OBJECT
    Q_PROPERTY(QVideoSink* videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged)

public:
    explicit FrameProvider(QObject* parent = nullptr);
    ~FrameProvider();

    QVideoSink* videoSink() const { return m_videoSink; }
    void setVideoSink(QVideoSink* videoSink);

signals:
    void videoSinkChanged();

public slots:
    void deliverFrame(const QVideoFrame& frame);

private:
    QPointer<QVideoSink> m_videoSink;
};

类声明一个槽函数 deliverFrame 提供视频帧提供的模块绑定并投递帧数据。在 cpp 实现中只如果有新的视频流,则直接调用 m_videoSink 的 setVideoFrame 方法就可以了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void FrameProvider::deliverFrame(const QVideoFrame& frame) {
    if (!m_videoSink)
        return;
    m_videoSink->setVideoFrame(frame);
}

将 FrameProvider 按上面文章中的方法一样,注册给到 QML 端,与 VideoOutput 配合使用时也稍微有一些变动:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
FrameProvider {
    id: frameProvider
    videoSink: videoContainer.videoSink
}
VideoOutput {
    id: videoContainer
    anchors.fill: parent
    fillMode: VideoOutput.PreserveAspectFit
}

这样 VideoOutput 与新的 FrameProvider 配合使用就完成了,接下来我们说一下 QVideoFrame 的变动:

QVideoFrame 数据拷贝方式的变动

在 Qt5 中,如拷贝 YUV 数据到 QVideoFrame 的方式非常暴力,通过 videoFrame.bits() 拿到地址算好位置无脑拷贝就可以了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int frameSize = static_cast<int>(frame.width * frame.height * frame.count / 2);
QVideoFrame videoFrame(frameSize, QSize(static_cast<int>(rotationWidth), static_cast<int>(rotationHeight)), static_cast<int>(rotationWidth),
                       QVideoFrame::Format_YUV420P);

if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
    auto src = reinterpret_cast<uint8_t*>(frame.data);
    auto dest = reinterpret_cast<uint8_t*>(videoFrame.bits());

    libyuv::I420Rotate(src + frame.offset[0], static_cast<int>(frame.stride[0]),
                       src + frame.offset[1], static_cast<int>(frame.stride[1]),
                       src + frame.offset[2], static_cast<int>(frame.stride[2]),
                       dest, static_cast<int>(rotationWidth),
                       dest + rotationWidth * rotationHeight, rotationWidth / 2,
                       dest + rotationWidth * rotationHeight + rotationWidth * rotationHeight / 4, rotationWidth / 2,
                       static_cast<int>(frame.width), static_cast<int>(frame.height), rotate_mode);

    videoFrame.setStartTime(0);
    videoFrame.unmap();

    QSize size = QSize(static_cast<int>(rotationWidth), static_cast<int>(rotationHeight));
    emit VideoManager::m_videoFrameDelegate->receivedVideoFrame(QString::fromStdString(accountId), videoFrame, size, bSub);
}

但 Qt6 中出现了较大的变动,首先 bits 函数要求传递目标数据的 plane,比如 Y plane 为 0,U 和 V 依次为 1 和 2。这看起来跟 Qt5 中没有什么太大区别,但如果你按 bits(0)、bits(1)、bits(1) 的地址按原来的逻辑拷贝时会发现部分分辨率的图像会渲染错乱,这基本上是因为原始的 YUV 数据宽度并不是 16 的倍数。而 QVideoFrame 一旦调用了 map 函数,则每个 plane 的 stride(在 Qt 中称为 bytesPerLine) 将会是 16 的倍数,如果你按原始数据宽度拷贝,就会导致画面错乱。 正确的做法是通过 QVideoFrame 提供的 bytesPerLine() 方法算出具体每个 plane 的宽度,按需拷贝,实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
QVideoFrameFormat format(QSize(rotationWidth, rotationHeight), QVideoFrameFormat::Format_YUV420P);
format.setViewport(QRect(0, 0, rotationWidth, rotationHeight));
QVideoFrame videoFrame(format);
if (videoFrame.map(QVideoFrame::WriteOnly)) {
    auto src = reinterpret_cast<uint8_t*>(frame.data);
    // If the aspect ratio of the original data is not a multiple of 16,
    // when mappedBytes(n) is called after frame mapping, the returned size will be expanded to the nearest multiple of 16.
    // When copying the data, bytesPerLine(n) should be used to get the actual stride that needs to be copied.
    libyuv::I420Rotate(src + frame.offset[0], frame.stride[0],
                       src + frame.offset[1], frame.stride[1],
                       src + frame.offset[2], frame.stride[2],
                       videoFrame.bits(0), videoFrame.bytesPerLine(0),
                       videoFrame.bits(1), videoFrame.bytesPerLine(1),
                       videoFrame.bits(2), videoFrame.bytesPerLine(2),
                       frame.width, frame.height, rotate_mode);
    videoFrame.setStartTime(0);
    videoFrame.unmap();
    QSize size = QSize(static_cast<int>(rotationWidth), static_cast<int>(rotationHeight));
    emit VideoManager::m_videoFrameDelegate->receivedVideoFrame(QString::fromStdString(accountId), videoFrame, size, bSub);
}

其中 frame.data 是 YUV 的原始数据。通过改动后的 QVideoFrame API 我们可以看到,Qt 对视频处理数据的要求更加严谨了,虽然处理问题过程中浪费了比较多的时间,但总算总结下了一些宝贵的经验。

2023-05-30 更新

以上拷贝方式当使用 Qt 6.x 版本默认的渲染引擎(OpenGL)时一些奇葩的分辨率会出现花屏的问题。修改 Qt 的渲染引擎为各平台特有引擎后得以解决:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main(int argc, char* argv[]) {
    QGuiApplication app(argc, argv);

#if defined(Q_OS_MACX)
    QQuickWindow::setGraphicsApi(QSGRendererInterface::Metal);
#else
    QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
#endif
    ..... other code
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
楼主你好,可以提供一份这个工程的源码学习吗? 最近在捣鼓Qt 6.3 qml播放视频的东西,但是发现qml这边的MediaPlayer不能设置解码器,感觉需要从c++这边进行解码在放给qml播放。 仅自学!十分感谢!
楼主你好,可以提供一份这个工程的源码学习吗? 最近在捣鼓Qt 6.3 qml播放视频的东西,但是发现qml这边的MediaPlayer不能设置解码器,感觉需要从c++这边进行解码在放给qml播放。 仅自学!十分感谢!
回复回复点赞举报
兄弟我很想学习学习你的这个工程的源码
兄弟我很想学习学习你的这个工程的源码
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
Qt QML VideoOutput 显示自定义的 YUV420P 数据流
在一些传统应用中,如果想使用 Qt 在 QWidget 或者 QML 中显示自定义的视频数据流,需要引入 OpenGL 来实现。而实际 Qt 已经准备了 VideoOutput 类型可以很方便的调用系统摄像头和使用自定义数据流。在 Qt 官网中,VideoOutput 的介绍中说明,source 属性可以是一个自定义派生于 QObject 的子类,并提供一个类型为 QMediaObject 的属性命名为 mediaObject,或者是一个派生与 QObject 的子类并提供一个类型为 QAbstractVideoSurface 的属性命名为 videoSurface。其中任意一个方法都可以实现自定义视频数据流的播放,本文介绍第二种方法。
我与梦想有个约会
2020/01/04
5.7K0
Qt编写自定义控件30-颜色多态按钮
这个控件一开始打算用样式表来实现,经过初步的探索,后面发现还是不够智能以及不能完全满足需求,比如要在此控件设置多个角标,这个用QSS就很难实现,后面才慢慢研究用QPainter来绘制,我记得当时接到这个定制控件任务的时候是2016年,那时候对QPainter的使用还不是很熟悉,也就是从此控件开始,逐步研究QPainter的绘制,把所有的内置函数都使用一遍,最终用的越来越熟悉,使得后来到了心中有坐标,万物皆painter的境界,可能就像武林中所说的打通了任督二脉吧。
feiyangqingyun
2019/08/27
2.2K0
Qt编写自定义控件30-颜色多态按钮
浅析WebRtc中视频数据的接收和渲染流程
本文基于PineAppRtc开源项目https://github.com/thfhongfeng/PineAppRtc
BennuCTech
2021/12/10
1.8K0
浅析WebRtc中视频数据的接收和渲染流程
QT应用编程:基于QMediaPlayer开发音视频播放器
QMediaPlayer是Qt提供的一个跨平台媒体播放器类,它没有自带解码库,而是对平台相关的播放器框架做了封装,提供了平台无关的API。所以使用QMediaPlayer播放视频,需要提前安装解码库。 在win系统下可以下载K-Lite_Codec_Pack或者LAVFilters解码库安装。
DS小龙哥
2022/01/07
3.6K0
QT应用编程:基于QMediaPlayer开发音视频播放器
FFMPEG音频视频开发:QT获取Android、Linux、Windows系统上的摄像头数据帧与声卡音频通过FFMPEG编码为MP4存储(v1.0)
声卡: win10 电脑自带声卡、罗技USB摄像头声卡、Android手机自带声卡都可以获取声音数据 摄像头: 手机摄像头、罗技USB摄像头
DS小龙哥
2022/01/12
1.9K0
FFMPEG音频视频开发:QT获取Android、Linux、Windows系统上的摄像头数据帧与声卡音频通过FFMPEG编码为MP4存储(v1.0)
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
Unity3D可以用于创建各种类型的的应用程序,包括虚拟现实、培训模拟器等。以下是一些可以使用Unity3D全景播放的场景:
音视频牛哥
2023/07/27
3890
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
基于qt的opencv实时图像处理框架FastCvLearn实战
对代码陌生的朋友,可以看看本文。如果熟悉了,可以直接文末到github仓库下载使用。
threeQing
2021/09/29
1.3K0
基于qt的opencv实时图像处理框架FastCvLearn实战
Windows下使用QT+OpenCV完成人脸检测(获取摄像头的数据进行检测)_解决内存释放问题
上一版本的QT+OpenCV例子在这里:https://blog.csdn.net/xiaolong1126626497/article/details/105295367
DS小龙哥
2022/01/12
2K0
Windows下使用QT+OpenCV完成人脸检测(获取摄像头的数据进行检测)_解决内存释放问题
iOS端 TRTC v2 版本自定义采集视频数据实现
实时音视频 SDK 默认会采集摄像头数据,如果开发者想在 TRTC 中集成使用第三方美颜库来实现美颜、滤镜等预处理功能,可以采用自定义采集视频数据接口,然后复用 LiteAVSDK 的编码和推流功能。 目前自定义采集支持的平台包括 iOS 、Android 、Mac OS 、Windows 。接口文档参考:https://cloud.tencent.com/document/product/647/32259#.E8.87.AA.E5.AE.9A.E4.B9.89.E9.87.87.E9.9B.86.E5.92.8C.E6.B8.B2.E6.9F.93
腾讯云-chaoli
2019/07/07
2.1K1
iOS端 TRTC v2 版本自定义采集视频数据实现
Qt编写自定义控件68-IP地址输入框
这个IP地址输入框控件,估计写烂了,网上随便一搜索,保证一大堆,估计也是因为这个控件太容易了,非常适合新手练手,一般的思路都是用4个qlineedit控件拼起来,然后每个输入框设置正则表达式过滤只能输入3位数字,然后安装事件过滤器识别回车自动跳到下一个输入框。关于如何设置正则表达式过滤,这个可以搜索查到,本人也不大懂这个规则,貌似还有专门的书籍专门介绍正则表达式,可能这块非常强大。
feiyangqingyun
2019/10/16
2.2K0
Qt编写自定义控件68-IP地址输入框
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。
福大大架构师每日一题
2023/04/05
1.6K0
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。
FFMPEG音频视频开发: 开发本地视频播放器(单线程解码)
ffmpeg与x264源码开源,可以在任意平台上编译。自行下载源码编译得到库文件即可。
DS小龙哥
2022/01/12
1.5K0
FFMPEG音频视频开发: 开发本地视频播放器(单线程解码)
Qt编写自定义控件35-GIF录屏控件
在平时的写作过程中,经常需要将一些操作动作和效果图截图成gif格式,使得涵盖的信息更全面更生动,有时候可以将整个操作过程和运行效果录制成MP4,但是文件体积比较大,而且很多网站不便于上传,基本上都支持gif动图,一般一个5秒左右的gif,800*600分辨率,可以很好的控制在500KB内,这样就比较完美的支持各大网站上传动图。
feiyangqingyun
2019/08/27
1.5K0
Qt编写自定义控件35-GIF录屏控件
Qt编写自定义控件11-设备防区按钮控件
在很多项目应用中,需要根据数据动态生成对象显示在地图上,比如地图标注,同时还需要可拖动对象到指定位置显示,能有多种状态指示,安防领域一般用来表示防区或者设备,可以直接显示防区号,有多种状态颜色指示,例如布防、撤防、旁路、报警、离线、在线等状态,可以作为一个通用的设备按钮对象使用。
feiyangqingyun
2019/08/23
6840
Qt编写自定义控件11-设备防区按钮控件
QT软件开发-基于FFMPEG设计视频播放器-软解图像(一)
QT软件开发-基于FFMPEG设计视频播放器-CPU软解视频(一) https://xiaolong.blog.csdn.net/article/details/126832537
DS小龙哥
2023/01/30
2.5K1
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。
福大大架构师每日一题
2023/06/08
7330
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频
TRTCSDK自定义采集YUV视频通话
摄像头通话功能,是TRTCSDK对系统摄像头进行了封装,采集摄像头数据,编码传输通话。
ppchao
2020/12/02
3.1K0
TRTCSDK自定义采集YUV视频通话
Qt高级编码约定
  这是我们在编写Qt代码时使用的高级编码约定的概述。有关Qt代码规范,请参见Qt代码风格一文。对于QML,请参阅QML代码规范一文。
Qt君
2019/12/25
2K0
TRTCSDK自定义采集Texture2D视频通话
摄像头通话功能,是TRTCSDK对系统摄像头进行了封装,采集摄像头数据,编码传输通话。
ppchao
2020/12/03
1.4K1
TRTCSDK自定义采集Texture2D视频通话
【Android FFMPEG 开发】FFMPEG 直播功能完整流程 + 源码 ( 源码交叉编译 -> AS工程配置 -> 音视频打开/读取/解码/格式转换 -> 原生绘制播放 -> 音视频同步 )
2 . 效果展示 : 使用的是湖南卫视的直播源 rtmp://58.200.131.2:1935/livetv/hunantv
韩曙亮
2023/03/27
2.5K0
【Android FFMPEG 开发】FFMPEG 直播功能完整流程 + 源码 ( 源码交叉编译 -> AS工程配置 -> 音视频打开/读取/解码/格式转换  -> 原生绘制播放 -> 音视频同步 )
推荐阅读
相关推荐
Qt QML VideoOutput 显示自定义的 YUV420P 数据流
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档