From c3c0bb446ffff8f82dd18e724c4a54b89533a12c Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Mon, 29 Apr 2024 17:11:18 +0800 Subject: GStreamer: QGstreamerImageCapture - emit signals from app thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When emitting signals on QPlatformImageCapture, we need to make sure they are invoked on the thread. So we defer the invocations when emitting from a worker thread to the thread owning `QGstreamerImageCapture`. This also makes sure that we don't emit signals while holding a mutex. Pick-to: 6.5 6.7 Change-Id: If53ae5622d2a3d62a146afa3f9b6585909967069 Reviewed-by: Jøger Hansegård Reviewed-by: Artem Dyomin --- .../mediacapture/qgstreamerimagecapture.cpp | 91 +++++++++++----------- .../mediacapture/qgstreamerimagecapture_p.h | 6 ++ 2 files changed, 52 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp index dca64e265..f1f336200 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp @@ -111,44 +111,40 @@ int QGstreamerImageCapture::doCapture(const QString &fileName) { qCDebug(qLcImageCaptureGst) << "do capture"; - // emit error in the next event loop, - // so application can associate it with returned request id. - auto invokeDeferred = [&](auto &&fn) { - QMetaObject::invokeMethod(this, std::forward(fn), Qt::QueuedConnection); - }; - - QMutexLocker guard(&m_mutex); - if (!m_session) { - invokeDeferred([this] { - emit error(-1, QImageCapture::ResourceError, - QPlatformImageCapture::msgImageCaptureNotSet()); - }); + { + QMutexLocker guard(&m_mutex); + if (!m_session) { + invokeDeferred([this] { + emit error(-1, QImageCapture::ResourceError, + QPlatformImageCapture::msgImageCaptureNotSet()); + }); - qCDebug(qLcImageCaptureGst) << "error 1"; - return -1; - } - if (!m_session->camera()) { - invokeDeferred([this] { - emit error(-1, QImageCapture::ResourceError, tr("No camera available.")); - }); + qCDebug(qLcImageCaptureGst) << "error 1"; + return -1; + } + if (!m_session->camera()) { + invokeDeferred([this] { + emit error(-1, QImageCapture::ResourceError, tr("No camera available.")); + }); - qCDebug(qLcImageCaptureGst) << "error 2"; - return -1; - } - if (passImage) { - invokeDeferred([this] { - emit error(-1, QImageCapture::NotReadyError, - QPlatformImageCapture::msgCameraNotReady()); - }); + qCDebug(qLcImageCaptureGst) << "error 2"; + return -1; + } + if (passImage) { + invokeDeferred([this] { + emit error(-1, QImageCapture::NotReadyError, + QPlatformImageCapture::msgCameraNotReady()); + }); + + qCDebug(qLcImageCaptureGst) << "error 3"; + return -1; + } + m_lastId++; - qCDebug(qLcImageCaptureGst) << "error 3"; - return -1; + pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} }); + // let one image pass the pipeline + passImage = true; } - m_lastId++; - - pendingImages.enqueue({m_lastId, fileName, QMediaMetaData{}}); - // let one image pass the pipeline - passImage = true; emit readyForCaptureChanged(false); return m_lastId; @@ -192,7 +188,10 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) passImage = false; - emit readyForCaptureChanged(isReadyForCapture()); + bool ready = isReadyForCapture(); + invokeDeferred([this, ready] { + emit readyForCaptureChanged(ready); + }); QGstCaps caps = bin.staticPad("sink").currentCaps(); auto memoryFormat = caps.memoryFormat(); @@ -223,24 +222,26 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) std::move(bufferHandle), previewInfo, sink, fmt, memoryFormat, }; QVideoFrame frame(gstBuffer, fmt); + QImage img = frame.toImage(); if (img.isNull()) { qDebug() << "received a null image"; return; } - auto &imageData = pendingImages.head(); - - emit imageExposed(imageData.id); - qCDebug(qLcImageCaptureGst) << "Image available!"; - emit imageAvailable(imageData.id, frame); - emit imageCaptured(imageData.id, img); - QMediaMetaData imageMetaData = metaData(); imageMetaData.insert(QMediaMetaData::Resolution, frame.size()); - imageData.metaData = imageMetaData; - - emit imageMetadataAvailable(imageData.id, imageMetaData); + pendingImages.head().metaData = std::move(imageMetaData); + PendingImage pendingImage = pendingImages.head(); + + invokeDeferred([this, pendingImage = std::move(pendingImage), frame = std::move(frame), + img = std::move(img)]() mutable { + emit imageExposed(pendingImage.id); + qCDebug(qLcImageCaptureGst) << "Image available!"; + emit imageAvailable(pendingImage.id, frame); + emit imageCaptured(pendingImage.id, img); + emit imageMetadataAvailable(pendingImage.id, pendingImage.metaData); + }); }); m_pendingFutures.insert(futureId, future); diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h index 0ca3bd9b9..8dfceb969 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h @@ -97,6 +97,12 @@ private: QMap> m_pendingFutures; int futureIDAllocator = 0; + + template + void invokeDeferred(Functor &&fn) + { + QMetaObject::invokeMethod(this, std::forward(fn), Qt::QueuedConnection); + }; }; QT_END_NAMESPACE -- cgit v1.2.3