diff options
author | Tim Blechmann <tim@klingt.org> | 2024-04-24 14:19:43 +0800 |
---|---|---|
committer | Tim Blechmann <tim@klingt.org> | 2024-05-17 14:32:41 +0800 |
commit | c3655553e0c9e1a77fe54252a9b897fd9fa943db (patch) | |
tree | 8b0edaa1f7db48af1378391a54152eaf152e0d6b /src | |
parent | 264b7e8d7d5683252102b5e5149685c8b8a70c2d (diff) |
GStreamer: QImageCapture - prevent QRhi access from gstreamer thread
We defer the access to QRhi to a worker thread to prevent
deinitialisation failures when joining gstreamer threads. This can
happen after the QApplication has been destroyed, leading to
use-after-free errors.
Fixes: QTBUG-124189
Pick-to: 6.5 6.7
Change-Id: Icfd060fdc695ff39f0a9cd67b28392a61145d6ad
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp | 63 |
1 files changed, 44 insertions, 19 deletions
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp index 4c8e190be..dca64e265 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp @@ -170,6 +170,13 @@ void QGstreamerImageCapture::setResolution(const QSize &resolution) filter.set("caps", caps); } +// HACK: gcc-10 and earlier reject [=,this] when building with c++17 +#if __cplusplus >= 202002L +# define EQ_THIS_CAPTURE =, this +#else +# define EQ_THIS_CAPTURE = +#endif + bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) { QMutexLocker guard(&m_mutex); @@ -196,35 +203,53 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) if (optionalFormatAndVideoInfo) std::tie(fmt, previewInfo) = std::move(*optionalFormatAndVideoInfo); - auto *sink = m_session->gstreamerVideoSink(); - auto *gstBuffer = new QGstVideoBuffer{ - std::move(bufferHandle), previewInfo, sink, fmt, memoryFormat, - }; - QVideoFrame frame(gstBuffer, fmt); - QImage img = frame.toImage(); - if (img.isNull()) { - qDebug() << "received a null image"; - return true; - } + int futureId = futureIDAllocator += 1; - auto &imageData = pendingImages.head(); + // ensure QVideoFrame::toImage is executed on a worker thread that is joined before the + // qApplication is destroyed + QFuture<void> future = QtConcurrent::run([EQ_THIS_CAPTURE]() mutable { + QMutexLocker guard(&m_mutex); + auto scopeExit = qScopeGuard([&] { + m_pendingFutures.remove(futureId); + }); - emit imageExposed(imageData.id); + if (!m_session) { + qDebug() << "QGstreamerImageCapture::probeBuffer: no session"; + return; + } - qCDebug(qLcImageCaptureGst) << "Image available!"; - emit imageAvailable(imageData.id, frame); + auto *sink = m_session->gstreamerVideoSink(); + auto *gstBuffer = new QGstVideoBuffer{ + 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 imageCaptured(imageData.id, img); + emit imageExposed(imageData.id); + qCDebug(qLcImageCaptureGst) << "Image available!"; + emit imageAvailable(imageData.id, frame); + emit imageCaptured(imageData.id, img); - QMediaMetaData metaData = this->metaData(); - metaData.insert(QMediaMetaData::Resolution, frame.size()); - imageData.metaData = metaData; + QMediaMetaData imageMetaData = metaData(); + imageMetaData.insert(QMediaMetaData::Resolution, frame.size()); + imageData.metaData = imageMetaData; - emit imageMetadataAvailable(imageData.id, metaData); + emit imageMetadataAvailable(imageData.id, imageMetaData); + }); + + m_pendingFutures.insert(futureId, future); return true; } +#undef EQ_THIS_CAPTURE + void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session) { QMutexLocker guard(&m_mutex); |