Qt6 qml 相机预览与处理
本文章适用于 qt6 的 quick程序,不适用于qt5!!
本文章在qt6.4.3版本下运行,windows及安卓都运行通过。
1. Main.qml
import QtQuick
import QtQuick.Window
import QtMultimedia
Window {
width: 375
height: 667
visible: true
title: qsTr("Hello World")
objectName: "rootObject"
// 屏幕适配, 以宽为基准方案
function dp(pixel){
if(Qt.platform.os === 'android'){
return pixel / 375.0 * Screen.width
}
return pixel
}
Column {
MediaDevices {
id: mediaDevices
}
Item {
id: itm
// 0: android下是后置摄像头,1:前置摄像头
// windows下很奇怪,所以我在videoOutput中用defaultVideoInput兜底
property int cIndex: 0
property int cWidth: (camera.cameraDevice.videoFormats[0]
&& camera.cameraDevice.videoFormats[0].resolution
? camera.cameraDevice.videoFormats[0].resolution.width
: 1280)
property int cHeight: (camera.cameraDevice.videoFormats[0]
&& camera.cameraDevice.videoFormats[0].resolution
? camera.cameraDevice.videoFormats[0].resolution.height
: 768)
width: dp(375)
height: dp(375) / cWidth * cHeight
// 声明视频输出组件
VideoOutput {
id: videoOutput
objectName: "videoOutput"
anchors.fill: parent
fillMode: VideoOutput.Stretch
}
// 创建视频会话
CaptureSession {
// 创建并选择相机
// android底层用的应该是camera2,部分root型号安卓机可以同时打开多个摄像头
// 大部分只能打开1个摄像头
// 不可能是cameraX,因为原生cameraX所有机型仅支持1个相机
camera: Camera {
id: camera
cameraDevice: mediaDevices.videoInputs[itm.cIndex] || mediaDevices.defaultVideoInput
// TODO 加各种配置,如自动缩放,焦点等,官方文档有说明
}
// 与声明的视频输出组件作绑定
videoOutput: videoOutput
// 绑定完成启动相机
onVideoOutputChanged: {
camera.start()
}
}
}
Text {
// 相机视频流的宽高,很可能系统不给你
text: `cWidth: ${itm.cWidth}, cHeight: ${itm.cHeight}`
font.pixelSize: dp(21)
}
}
}
2. main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QVideoSink>
#include <QVideoFrame>
#include <QDebug>
#include "VideoHandler.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/demo1/Main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
&app, []() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.load(url);
// 视频流处理对象
VideoHandler videoHandler;
// 查找 qml VideoOutput
int rootSize = engine.rootObjects().size();
for(int i=0; i< rootSize; i++){
auto item = engine.rootObjects().at(i);
if(item->objectName() == "rootObject"){
QObject * root = item;
// 找到后,进行信号与槽连接
auto videoOutput = root->findChild<QObject *>("videoOutput");
// 官方文档说 videoOutput 有个属性叫 videoSink,它是个 QVideoSink 指针对象
QObject * videoSink = qvariant_cast<QObject*>(videoOutput->property("videoSink"));
// 打印分析是否获取到 QVideoSink 指针对象
qDebug() << videoSink->objectName() << " " << videoSink << Qt::endl;
QVideoSink * vsPtr = qobject_cast<QVideoSink *>(videoSink);
// 官方文档还说,它有个信号,能获取到视频帧
QObject::connect(vsPtr, &QVideoSink::videoFrameChanged, &videoHandler, &VideoHandler::videoFrameChanged);
break;
}
}
return app.exec();
}
3. VideoHandler.hpp
#ifndef VIDEOHANDLER_HPP
#define VIDEOHANDLER_HPP
#include <QObject>
#include <QVideoFrame>
#include <QDebug>
class VideoHandler: public QObject {
private :
int num = 0;
public slots:
// 进行逐帧视频流处理
void videoFrameChanged(const QVideoFrame &frame) {
// 首帧打印帧格式
if(num == 0){
qDebug() << "format: " << frame.pixelFormat() << Qt::endl;
++num;
}
// TODO 逐帧分析,如人脸识别
}
};
#endif // VIDEOHANDLER_HPP