summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTim Blechmann <tim@klingt.org>2024-04-24 14:19:43 +0800
committerTim Blechmann <tim@klingt.org>2024-05-17 14:32:41 +0800
commitc3655553e0c9e1a77fe54252a9b897fd9fa943db (patch)
tree8b0edaa1f7db48af1378391a54152eaf152e0d6b /src
parent264b7e8d7d5683252102b5e5149685c8b8a70c2d (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.cpp63
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);