前言
笔者之前记录的是Qt5.15的,当前Qt6系列无法使用
笔者本次记录下Qt6中,如何创建qml自定义视频源
一、获取视频帧
这个笔者在之前的文档中记录过,本次算是重复了
1.通过videoSink获取
关键代码如下
Camera {
id: camera
}
CaptureSession {
id: captureSession
camera: camera
videoOutput: page.videoOutput
}
Connections {
target: videoOutput.videoSink
function onVideoFrameChanged(frame) {
//设置并处理已获取视频帧
}
}
笔者翻看源码时发现,QMediaCaptureSession中有关于QVideoSink的方法,但没有作为属性导出,不明白官方是什么用意。应该是可以在C++中正常使用的,笔者未测试,感兴趣的小伙伴可以试试
代码很好理解,videoSink的onVideoFrameChanged会将视频帧直接扔出来
2.通过imageCapture获取
正确地说,这是获取静态图片了,关键代码如下
Camera {
id: camera
}
ImageCapture {
id: imageCapture
}
CaptureSession {
id: captureSession
camera: camera
imageCapture: imageCapture
videoOutput: page.videoOutput
}
Timer {
id: timer
interval: 500; running: true; repeat: true
onTriggered: imageCapture.capture()
}
Connections {
target: imageCapture
function onErrorOccurred(requestId, error, message) {//发生错误
console.debug("imageCapture onErrorOccurred",requestId, error, message)
}
function onImageCaptured(requestId, previewImage) {//已经捕获到QImage
console.debug("imageCapture onImageCaptured",requestId, previewImage,imageCapture.preview)
//imageCapture.preview可以直接作为Image的source,以显示图片
//previewImage是QImage对象,可以交给C++处理
}
}
}
代码也很好理解,就是使用定时器,定时获取一个QImage
二、创建视频源
VideoOutput类型的videoSink属性中,有一个setVideoFrame()方法,可以添加新的视频帧,但此方法未注册到qml中,因此需要在C++中实现。笔者定义了一个工具类来实现,关键代码如下
class Tool : public QObject
{
Q_OBJECT
public:
explicit Tool(QObject *parent = nullptr);
//设置并处理已获取视频帧
Q_INVOKABLE void setVideoFrame(const QVideoFrame frame);
Q_INVOKABLE void setVideoImage(const QImage image);
//设置QVideoSink对象实例
Q_INVOKABLE void setVideoSink(QVideoSink *newVideoSink);
signals:
private:
QVideoSink *m_videoSink = nullptr;
};
将此工具类直接注册到qml中,设置好QVideoSink对象实例,然后把已有的视频帧放入就可以了
实现的关键代码如下
/*!
* \brief Tool::setVideoFrame 设置并处理已获取视频帧
* \param frame 已获取视频帧
*/
void Tool::setVideoFrame(const QVideoFrame frame)
{
qDebug()<<Q_FUNC_INFO<<__LINE__<<frame;
if(m_videoSink != nullptr)
m_videoSink->setVideoFrame(frame);
}
void Tool::setVideoImage(const QImage image)
{
qDebug()<<Q_FUNC_INFO<<__LINE__<<image;
QVideoFrameFormat frameFormat(image.size(), QVideoFrameFormat::Format_BGRA8888);
QVideoFrame f(frameFormat);
if (f.map(QVideoFrame::WriteOnly)) {
memcpy(f.bits(0), image.bits(), image.sizeInBytes());
f.setStartTime(0);
f.unmap();
if(m_videoSink != nullptr)
m_videoSink->setVideoFrame(f);
}
}
void Tool::setVideoSink(QVideoSink *newVideoSink)
{
if (m_videoSink == newVideoSink)
return;
m_videoSink = newVideoSink;
}
实现代码也不复杂,但笔者有点疑问,QImage明明显示是RGB32的颜色空间,但在生成QVideoFrame时,却要使用BGRA8888的颜色空间????
三、运行测试
代码不难理解,只是这个运行结果,有空让人难受
1.在win10下测试时,videoSink获取的QVideoFrame是Format_NV12,与Qt5下的完全不同。
若是需要QImage,可以通过QVideoFrame::toImage()方法直接得到
2.在鸿蒙系统下测试时,videoSink获取的QVideoFrame是Format_SamplerExternalOES,与Qt5下的完全不同。
若是需要QImage,无法通过QVideoFrame::toImage()方法得到,而且一用到这个方法,程序就会卡住,界面没响应
3.在win10下,通过imageCapture可是正常获取到QImage的图像,但在鸿蒙系统下获取会很慢
运行结果就不放图片了,笔者建议还是再等等官方修复吧
笔者测试使用的源码
后记
与之前的版本相关,新版本Qt6.3.1中的多媒体模块,已经修复了不少问题。已经可以使用mingw编译器,qml中可以获取正常的Image source。但目前仍然存在很多问题,如设置分辨率无效等。
笔者听说多媒体模块计划使用ffmpeg实现,笔者很期待
接下来,因项目需要,笔者计划暂时使用openCV获取视频帧,并使用VideoOutput显示