summaryrefslogtreecommitdiffstats
path: root/src/multimedia
diff options
context:
space:
mode:
authorJøger Hansegård <joger.hansegard@qt.io>2024-03-10 18:43:54 +0100
committerJøger Hansegård <joger.hansegard@qt.io>2024-03-28 14:54:50 +0100
commit7efc9f9b6d058b6e7e6c768b94abf602719b31b4 (patch)
tree975b13191d6644cfc33f30aa8b05c22c7b0416d1 /src/multimedia
parent6351b52b24ec5c8fa37abb4d394a4ca40d6fcf51 (diff)
Tie Qt Multimedia backend lifetime to Qt Application lifetime
This patch uses a specialized application static variable to tie backend lifetime to the Qt application lifetime if an application is present. This ensures that system resources are released before static destruction, and prevents abrupt termination on Windows during static destruction. This is according to Windows Media Foundation's documentation that states that WMF classes shall not be released during static destruction. If Qt Multimedia features are used without a Qt Application object, the lifetime of the multimedia backend remains as before, and destruction takes place as part of static destruction. As a side effect, this change fixes a crash if Qt Multimedia features are used after recreating the Qt Application. Fixes: QTBUG-120198 Pick-to: 6.7 6.6 6.5 Change-Id: I570743d6462e27630d8f29dc60cfa414c8cbc17d Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
Diffstat (limited to 'src/multimedia')
-rw-r--r--src/multimedia/audio/qsoundeffect.cpp9
-rw-r--r--src/multimedia/platform/qplatformmediaintegration.cpp61
2 files changed, 59 insertions, 11 deletions
diff --git a/src/multimedia/audio/qsoundeffect.cpp b/src/multimedia/audio/qsoundeffect.cpp
index 935777cc6..c12114672 100644
--- a/src/multimedia/audio/qsoundeffect.cpp
+++ b/src/multimedia/audio/qsoundeffect.cpp
@@ -114,6 +114,14 @@ void QSoundEffectPrivate::sampleReady()
if (!m_audioSink) {
const auto audioDevice =
m_audioDevice.isNull() ? QMediaDevices::defaultAudioOutput() : m_audioDevice;
+
+ if (audioDevice.isNull()) {
+ // We are likely on a virtual machine, for example in CI
+ qCCritical(qLcSoundEffect) << "Failed to play sound. No audio devices present.";
+ setStatus(QSoundEffect::Error);
+ return;
+ }
+
const auto &sampleFormat = m_sample->format();
const auto sampleChannelConfig =
sampleFormat.channelConfig() == QAudioFormat::ChannelConfigUnknown
@@ -141,6 +149,7 @@ void QSoundEffectPrivate::sampleReady()
m_audioBuffer = QAudioBuffer(m_sample->data(), m_sample->format());
m_audioSink.reset(new QAudioSink(audioDevice, m_audioBuffer.format()));
+
connect(m_audioSink.get(), &QAudioSink::stateChanged, this, &QSoundEffectPrivate::stateChanged);
if (!m_muted)
m_audioSink->setVolume(m_volume);
diff --git a/src/multimedia/platform/qplatformmediaintegration.cpp b/src/multimedia/platform/qplatformmediaintegration.cpp
index dfa248c56..2a563417a 100644
--- a/src/multimedia/platform/qplatformmediaintegration.cpp
+++ b/src/multimedia/platform/qplatformmediaintegration.cpp
@@ -13,6 +13,7 @@
#include <qcameradevice.h>
#include <qloggingcategory.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qapplicationstatic.h>
#include "qplatformcapturablewindows_p.h"
#include "qplatformmediadevices_p.h"
@@ -74,7 +75,7 @@ struct InstanceHolder
if (backend.isEmpty() && !backends.isEmpty())
backend = defaultBackend(backends);
- qCDebug(qLcMediaPlugin) << "loading backend" << backend;
+ qCDebug(qLcMediaPlugin) << "Loading media backend" << backend;
instance.reset(
qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(), backend));
@@ -84,10 +85,54 @@ struct InstanceHolder
}
}
+ ~InstanceHolder()
+ {
+ instance.reset();
+ qCDebug(qLcMediaPlugin) << "Released media backend";
+ }
+
+ // Play nice with QtGlobalStatic::ApplicationHolder
+ using QAS_Type = InstanceHolder;
+ static void innerFunction(void *pointer)
+ {
+ new (pointer) InstanceHolder();
+ }
+
std::unique_ptr<QPlatformMediaIntegration> instance;
};
-Q_GLOBAL_STATIC(InstanceHolder, instanceHolder);
+// Specialized implementation of Q_APPLICATION_STATIC which behaves as
+// an application static if a Qt application is present, otherwise as a Q_GLOBAL_STATIC.
+// By doing this, and we have a Qt application, all system resources allocated by the
+// backend is released when application lifetime ends. This is important on Windows,
+// where Windows Media Foundation instances should not be released during static destruction.
+//
+// If we don't have a Qt application available when instantiating the instance holder,
+// it will be created once, and not destroyed until static destruction. This can cause
+// abrupt termination of Windows applications during static destruction. This is not a
+// supported use case, but we keep this as a fallback to keep old applications functional.
+// See also QTBUG-120198
+struct ApplicationHolder : QtGlobalStatic::ApplicationHolder<InstanceHolder>
+{
+ // Replace QtGlobalStatic::ApplicationHolder::pointer to prevent crash if
+ // no application is present
+ static InstanceHolder* pointer()
+ {
+ if (guard.loadAcquire() == QtGlobalStatic::Initialized)
+ return realPointer();
+
+ QMutexLocker locker(&mutex);
+ if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {
+ InstanceHolder::innerFunction(&storage);
+
+ if (const QCoreApplication *app = QCoreApplication::instance())
+ QObject::connect(app, &QObject::destroyed, app, reset, Qt::DirectConnection);
+
+ guard.storeRelease(QtGlobalStatic::Initialized);
+ }
+ return realPointer();
+ }
+};
} // namespace
@@ -95,7 +140,8 @@ QT_BEGIN_NAMESPACE
QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
{
- return instanceHolder->instance.get();
+ static QGlobalStatic<ApplicationHolder> s_instanceHolder;
+ return s_instanceHolder->instance.get();
}
QList<QCameraDevice> QPlatformMediaIntegration::videoInputs()
@@ -146,19 +192,12 @@ QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo()
return new QPlatformMediaFormatInfo;
}
-// clang-format off
std::unique_ptr<QPlatformMediaDevices> QPlatformMediaIntegration::createMediaDevices()
{
- // Avoid releasing WMF resources and uninitializing WMF during static
- // destruction, QTBUG-120198
- if (QCoreApplication::instance())
- connect(qApp, &QObject::destroyed, this, [this] {
- m_mediaDevices = nullptr;
- });
-
return QPlatformMediaDevices::create();
}
+// clang-format off
QPlatformVideoDevices *QPlatformMediaIntegration::videoDevices()
{
std::call_once(m_videoDevicesOnceFlag,