Skip to content

Commit 8cefe0b

Browse files
committed
Test QQuickView
1 parent 16dcd0f commit 8cefe0b

3 files changed

Lines changed: 424 additions & 0 deletions

File tree

SerialPrograms/Source/CommonFramework/VideoPipeline/Backends/CameraImplementations.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "CameraWidgetQt6.h"
1919
#include "CameraWidgetQt6.5.h"
2020
#include "CameraWidgetQt6_QQuickWidget.h"
21+
#include "CameraWidgetQt6_QQuickView.h"
2122

2223

2324
namespace PokemonAutomation{
@@ -60,6 +61,10 @@ struct CameraBackends{
6061
"qt6-QQuickWidget", "Qt6: QQuickWidget",
6162
std::make_unique<CameraQt6_QQuickWidget::CameraBackend>()
6263
);
64+
m_backends.emplace_back(
65+
"qt6-QQuickView", "Qt6: QQuickView",
66+
std::make_unique<CameraQt6_QQuickView::CameraBackend>()
67+
);
6368
}
6469

6570
size_t items = 0;
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/* Camera Widget (Qt6 QQuickView)
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
*/
6+
7+
#include <QDir>
8+
#include <QTemporaryFile>
9+
#include <QVBoxLayout>
10+
#include <QCamera>
11+
#include <QPainter>
12+
#include <QVideoSink>
13+
#include <QQuickItem>
14+
#include <QQuickView>
15+
#include <QQmlContext>
16+
#include <QQmlEngine>
17+
#include "Common/Qt/Redispatch.h"
18+
#include "VideoFrameQt.h"
19+
#include "QFormatAggregator.h"
20+
#include "MediaServicesQt6.h"
21+
#include "CameraWidgetQt6_QQuickView.h"
22+
23+
// REMOVE
24+
#include <iostream>
25+
using std::cout;
26+
using std::endl;
27+
28+
namespace PokemonAutomation{
29+
namespace CameraQt6_QQuickView{
30+
31+
32+
33+
34+
35+
36+
std::vector<CameraInfo> CameraBackend::get_all_cameras() const{
37+
const auto cameras = GlobalMediaServices::instance().get_all_cameras();
38+
std::vector<CameraInfo> ret;
39+
for (const auto& info : cameras){
40+
ret.emplace_back(info.id().toStdString());
41+
}
42+
return ret;
43+
}
44+
std::string CameraBackend::get_camera_name(const CameraInfo& info) const{
45+
const auto cameras = GlobalMediaServices::instance().get_all_cameras();
46+
for (const auto& camera : cameras){
47+
if (camera.id().toStdString() == info.device_name()){
48+
return camera.description().toStdString();
49+
}
50+
}
51+
std::cout << "Error: no such camera for CameraInfo: " << info.device_name() << std::endl;
52+
return "";
53+
}
54+
std::unique_ptr<VideoSource> CameraBackend::make_video_source(
55+
Logger& logger,
56+
const CameraInfo& info,
57+
Resolution resolution,
58+
VideoFormat format,
59+
FramesPerSecond fps
60+
) const{
61+
return std::make_unique<CameraVideoSource>(logger, info, resolution, format, fps);
62+
}
63+
64+
65+
66+
67+
68+
69+
CameraVideoSource::~CameraVideoSource(){
70+
if (!m_capture){
71+
return;
72+
}
73+
try{
74+
m_logger.log("Stopping Camera...");
75+
}catch (...){}
76+
77+
// cout << "Stopping Camera..." << endl;
78+
79+
run_on_main_thread_and_wait([&]{
80+
m_metaobject.reset();
81+
// m_camera->stop();
82+
m_capture.reset();
83+
m_video_sink.reset();
84+
m_camera.reset();
85+
});
86+
}
87+
CameraVideoSource::CameraVideoSource(
88+
Logger& logger,
89+
const CameraInfo& info,
90+
Resolution desired_resolution,
91+
VideoFormat desired_format,
92+
FramesPerSecond desired_fps
93+
)
94+
: VideoSource(logger, true)
95+
, m_logger(logger)
96+
, m_last_frame(logger)
97+
, m_snapshot_manager(logger, m_last_frame)
98+
{
99+
if (!info){
100+
return;
101+
}
102+
m_logger.log("Starting Camera: Backend = CameraQt6QVideoSink");
103+
104+
run_on_main_thread_and_wait([&]{
105+
init(info, desired_resolution, desired_format, desired_fps);
106+
});
107+
}
108+
void CameraVideoSource::init(
109+
const CameraInfo& info,
110+
Resolution desired_resolution,
111+
VideoFormat desired_format,
112+
FramesPerSecond desired_fps
113+
){
114+
m_metaobject.reset(new QObject());
115+
116+
auto cameras = QMediaDevices::videoInputs();
117+
const QCameraDevice* device = nullptr;
118+
for (const auto& camera : cameras){
119+
if (camera.id().toStdString() == info.device_name()){
120+
device = &camera;
121+
break;
122+
}
123+
}
124+
if (device == nullptr){
125+
m_logger.log("Camera not found: " + info.device_name(), COLOR_RED);
126+
return;
127+
}
128+
129+
QCameraFormat format = build_format_set(
130+
m_logger,
131+
m_formats,
132+
*device,
133+
desired_resolution,
134+
desired_format,
135+
desired_fps
136+
);
137+
if (format.isNull()){
138+
return;
139+
}
140+
141+
get_format(format, m_resolution, m_format, m_fps);
142+
m_logger.log(
143+
"Resolution: " + m_resolution.to_string() +
144+
", Format: " + VideoFormat_database().find(m_format)->display +
145+
", FPS: " + std::to_string(m_fps)
146+
);
147+
148+
m_camera.reset(new QCameraThread(m_logger, *device, format));
149+
m_video_sink.reset(new QVideoSink());
150+
m_capture.reset(new QMediaCaptureSession());
151+
m_capture->setCamera(&m_camera->camera());
152+
m_capture->setVideoSink(m_video_sink.get());
153+
154+
#if 0
155+
connect(m_camera.get(), &QCamera::errorOccurred, this, [&](){
156+
if (m_camera->error() != QCamera::NoError){
157+
m_logger.log("QCamera error: " + m_camera->errorString().toStdString());
158+
}
159+
});
160+
#endif
161+
m_metaobject->connect(
162+
m_video_sink.get(), &QVideoSink::videoFrameChanged,
163+
&m_camera->camera(), [&](const QVideoFrame& frame){
164+
// This runs on the QCamera's thread. So it is off the critical path.
165+
166+
WallClock now = current_time();
167+
if (!m_last_frame.push_frame(frame, now)){
168+
return;
169+
}
170+
report_source_frame(std::make_shared<VideoFrame>(now, frame));
171+
}
172+
);
173+
}
174+
175+
176+
QWidget* CameraVideoSource::make_display_QtWidget(QWidget* parent){
177+
return new CameraVideoDisplay(parent, *this);
178+
}
179+
180+
181+
182+
CameraVideoDisplay::~CameraVideoDisplay(){
183+
m_source.remove_source_frame_listener(*this);
184+
}
185+
CameraVideoDisplay::CameraVideoDisplay(QWidget* parent, CameraVideoSource& source)
186+
: QWidget(parent)
187+
, m_source(source)
188+
{
189+
cout << "CameraVideoDisplay()" << endl;
190+
191+
const char QML_CONTENTS[] =
192+
"import QtQuick\n"
193+
"import QtMultimedia\n"
194+
"Item {\n"
195+
" id: root\n"
196+
" anchors.fill: parent\n"
197+
" VideoOutput {\n"
198+
" id: videoOutput\n"
199+
" anchors.fill: parent\n"
200+
" fillMode: VideoOutput.PreserveAspectFit\n"
201+
" }\n"
202+
" Component.onCompleted: {\n"
203+
" if (typeof videoSourceBackend !== \"undefined\") {\n"
204+
" videoSourceBackend.registerQmlSink(videoOutput.videoSink)\n"
205+
" }\n"
206+
" }\n"
207+
"}\n";
208+
209+
210+
QQuickView* view = new QQuickView();
211+
view->setResizeMode(QQuickView::SizeRootObjectToView);
212+
213+
// Pass your sink/source data directly to the QML context root properties
214+
view->rootContext()->setContextProperty("videoSourceBackend", this);
215+
216+
QQmlComponent component(view->engine());
217+
component.setData(QByteArray(QML_CONTENTS), QUrl());
218+
219+
QObject* rootObject = component.create(view->rootContext());
220+
view->setContent(QUrl(), &component, rootObject);
221+
222+
// Wrap the native QML window inside a C++ QWidget container!
223+
QWidget* container = QWidget::createWindowContainer(view, this);
224+
container->setMinimumSize(80, 45);
225+
container->setFocusPolicy(Qt::NoFocus);
226+
227+
this->setLayout(new QVBoxLayout(nullptr));
228+
this->layout()->setContentsMargins(0, 0, 0, 0);
229+
this->layout()->addWidget(container);
230+
231+
m_source.add_source_frame_listener(*this);
232+
}
233+
void CameraVideoDisplay::on_frame(std::shared_ptr<const VideoFrame> frame){
234+
if (m_sink == nullptr){
235+
return;
236+
}
237+
// cout << "setVideoFrame()" << endl;
238+
m_sink->setVideoFrame(frame->frame);
239+
}
240+
Q_INVOKABLE void CameraVideoDisplay::registerQmlSink(QObject* qml_sink){
241+
cout << "registerQmlSink(): " << qml_sink << endl;
242+
if (!qml_sink){
243+
return;
244+
}
245+
246+
// Safely cast the generic QML object back to a functional QVideoSink pointer
247+
QVideoSink* target_sink = qobject_cast<QVideoSink*>(qml_sink);
248+
cout << "target_sink = " << target_sink << endl;
249+
if (target_sink) {
250+
// Forward frames arriving from your QCamera capture device right into this QML sink!
251+
// Now, every time your capture thread hooks frame details, it routes them to targetSink.
252+
m_sink = target_sink;
253+
}
254+
}
255+
256+
257+
258+
259+
260+
261+
262+
263+
264+
265+
}
266+
}

0 commit comments

Comments
 (0)