3#include <QtCore/QMutexLocker>
4#include <QtCore/QThread>
5#include <QtGui/QWindow>
6#include <QtMultimedia/QVideoSink>
7#include <QtMultimediaQuick/private/qquickvideooutput_p.h>
8#include <QtQuick/QQuickWindow>
17 : QObject(parent), _element(element ? GST_ELEMENT(gst_object_ref(element)) :
nullptr)
19 _emitTimer.setInterval(1000);
20 _emitTimer.setSingleShot(
false);
21 QObject::connect(&_emitTimer, &QTimer::timeout,
this, &QGCQVideoSinkController::_onEmitTimer);
27 _releaseElementBinding();
29 gst_object_unref(_element);
38 : QList<QGCQVideoSinkController*>{};
43 if (!receiver || !videoOutput)
46 auto applyVisibility = [receiver](QWindow* win) {
47 const QWindow::Visibility v = win ? win->visibility() : QWindow::Hidden;
48 const bool active = win && (v != QWindow::Hidden && v != QWindow::Minimized);
54 auto prevConn = std::make_shared<QMetaObject::Connection>();
55 auto wireWindow = [applyVisibility, prevConn, receiver](QQuickWindow* qw) {
57 QObject::disconnect(*prevConn);
58 *prevConn = QMetaObject::Connection{};
61 applyVisibility(
nullptr);
65 *prevConn = QObject::connect(qw, &QWindow::visibilityChanged, receiver,
66 [applyVisibility, qw](QWindow::Visibility) { applyVisibility(qw); });
68 wireWindow(videoOutput->window());
69 QObject::connect(videoOutput, &QQuickVideoOutput::windowChanged, receiver, wireWindow);
79 if (thread() != QThread::currentThread()) {
80 qCCritical(QGCQVideoSinkControllerLog) <<
"called from wrong thread";
87 QMutexLocker locker(&_stateMutex);
88 if (format != _negotiatedFormat || resolution != _negotiatedResolution) {
89 _negotiatedFormat = format;
90 _negotiatedResolution = resolution;
95 qCDebug(QGCQVideoSinkControllerLog).noquote()
96 <<
"Negotiation update: format=" << format <<
"size=" << resolution;
103 if (thread() != QThread::currentThread()) {
104 qCCritical(QGCQVideoSinkControllerLog) <<
"called from wrong thread";
107 if (!_element || _bindingReleased)
112 if (!gst_element_query(_element, query.get())) {
113 qCDebug(QGCQVideoSinkControllerLog) <<
"Latency query not handled by sink element";
119 if (thread() != QThread::currentThread()) {
120 qCCritical(QGCQVideoSinkControllerLog) <<
"called from wrong thread";
123 if (!_element || _bindingReleased)
125 g_object_set(_element,
"active", active ? TRUE : FALSE,
nullptr);
130 if (thread() != QThread::currentThread()) {
131 qCCritical(QGCQVideoSinkControllerLog) <<
"called from wrong thread";
136 if (_sinkDestroyedConnection) {
137 QObject::disconnect(_sinkDestroyedConnection);
138 _sinkDestroyedConnection = {};
140 QVideoSink* raw = sink.data();
144 g_object_set(_element,
"active", FALSE,
"qvideosink",
static_cast<gpointer
>(
nullptr),
nullptr);
145 _bindingReleased =
true;
148 g_object_set(_element,
"qvideosink",
static_cast<gpointer
>(raw),
nullptr);
149 _sinkDestroyedConnection = QObject::connect(raw, &QObject::destroyed,
this, [
this]() {
152 _bindingReleased =
false;
157 if (thread() != QThread::currentThread()) {
158 qCCritical(QGCQVideoSinkControllerLog) <<
"called from wrong thread";
161 _releaseElementBinding();
167 if (!_element || _bindingReleased)
170 g_object_get(_element,
"frames-delivered", &
delivered,
nullptr);
176 QMutexLocker locker(&_stateMutex);
177 return _negotiatedFormat;
182 QMutexLocker locker(&_stateMutex);
183 return _negotiatedResolution;
186void QGCQVideoSinkController::_releaseElementBinding() noexcept
188 if (!_element || _bindingReleased)
191 if (_sinkDestroyedConnection) {
192 QObject::disconnect(_sinkDestroyedConnection);
193 _sinkDestroyedConnection = {};
195 g_object_set(_element,
"active", FALSE,
"qvideosink",
static_cast<gpointer
>(
nullptr),
nullptr);
196 _bindingReleased =
true;
199void QGCQVideoSinkController::_onEmitTimer()
201 if (!_element || _bindingReleased)
204 g_object_get(_element,
"frames-delivered", &
delivered,
nullptr);
205 if (
delivered != _lastEmittedFrameTotal) {
std::atomic< quint64 > delivered
struct _GstElement GstElement
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static QList< QGCQVideoSinkController * > controllersOf(const QObject *receiver)
A receiver's owning controllers — direct children only, never a deep QObject-tree walk.
~QGCQVideoSinkController() override
void negotiationChanged()
const GstElement * element() const noexcept
QSize negotiatedResolution() const
void updateNegotiation(const QString &format, const QSize &resolution)
void setActive(bool active)
void setVideoSink(QPointer< QVideoSink > sink)
QString negotiatedFormat() const
void frameCountsChanged()
quint64 frameCount() const noexcept
static void syncActiveToWindowVisibility(QObject *receiver, QQuickVideoOutput *videoOutput)
GstQueryPtr adoptQuery(GstQuery *query) noexcept
std::unique_ptr< GstQuery, GstQueryDeleter > GstQueryPtr