4#include <common/qgstreameraudiooutput_p.h>
6#include <QtCore/qloggingcategory.h>
7#include <QtMultimedia/qaudiodevice.h>
8#include <QtMultimedia/qaudiooutput.h>
10#include <common/qgstpipeline_p.h>
11#include <audio/qgstreameraudiodevice_p.h>
13#if QT_CONFIG(pulseaudio)
14# include <pulse/version.h>
21Q_STATIC_LOGGING_CATEGORY(qLcMediaAudioOutput,
"qt.multimedia.audiooutput")
24 using namespace Qt::Literals;
26 if constexpr (QT_CONFIG(pulseaudio))
27 return "pulsesink"_L1;
28 else if constexpr (QT_CONFIG(alsa))
31 return "autoaudiosink"_L1;
36 using namespace Qt::Literals;
37 QLatin1String elementType = element.typeName();
39 if constexpr (QT_CONFIG(pulseaudio))
40 return elementType ==
"GstPulseSink"_L1;
41 if constexpr (0 && QT_CONFIG(alsa))
43 return elementType ==
"GstAlsaSink"_L1;
50#if QT_CONFIG(pulseaudio)
51 static std::once_flag versionCheckGuard;
53 std::call_once(versionCheckGuard, [] {
54 QVersionNumber paVersion = QVersionNumber::fromString(pa_get_library_version());
55 QVersionNumber firstBadVersion(15, 99);
56 QVersionNumber firstGoodVersion(16, 2);
57 if (paVersion >= firstBadVersion && paVersion < firstGoodVersion) {
58 qWarning() <<
"Pulseaudio v16 detected. It has known issues, that can cause GStreamer "
72 static const auto error = qGstErrorMessageIfElementsNotAvailable(
73 "audioconvert",
"audioresample",
"volume",
"autoaudiosink");
82 QPlatformAudioOutput(parent),
83 m_audioOutputBin(QGstBin::create(
"audioOutput")),
85 QGstElement::createFromFactory(
"queue",
"audioQueue"),
88 QGstElement::createFromFactory(
"audioconvert",
"audioConvert"),
91 QGstElement::createFromFactory(
"audioresample",
"audioResample"),
94 QGstElement::createFromFactory(
"volume",
"volume"),
97 QGstElement::createFromFactory(defaultSinkName.constData(),
"audiosink"),
100 m_audioOutputBin.add(m_audioQueue, m_audioConvert, m_audioResample, m_audioVolume, m_audioSink);
101 qLinkGstElements(m_audioQueue, m_audioConvert, m_audioResample, m_audioVolume, m_audioSink);
103 m_audioOutputBin.addGhostPad(m_audioQueue,
"sink");
110 const auto *customDeviceInfo =
111 dynamic_cast<
const QGStreamerCustomAudioDeviceInfo *>(m_audioDevice.handle());
113 if (customDeviceInfo) {
114 qCDebug(qLcMediaAudioOutput)
115 <<
"requesting custom audio sink element: " << customDeviceInfo->id;
123 qCWarning(qLcMediaAudioOutput)
124 <<
"Cannot create audio sink element:" << customDeviceInfo->id;
127 const QByteArray &id = m_audioDevice.id();
128 if constexpr (QT_CONFIG(pulseaudio) || QT_CONFIG(alsa)) {
132 newSink.set(
"device", id.constData());
134 newSink.set(
"async",
false);
138 qWarning() <<
"Cannot create" << defaultSinkName;
140 auto *deviceInfo =
dynamic_cast<
const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle());
141 if (deviceInfo && deviceInfo->gstDevice) {
142 QGstElement element = QGstElement::createFromDevice(deviceInfo->gstDevice,
"audiosink");
144 element.set(
"async",
false);
149 qCWarning(qLcMediaAudioOutput) <<
"Invalid audio device:" << m_audioDevice.id();
150 qCWarning(qLcMediaAudioOutput)
151 <<
"Failed to create a gst element for the audio device, using a default audio sink";
157 m_audioOutputBin.setStateSync(GST_STATE_NULL);
162 m_audioVolume.set(
"volume", volume);
167 m_audioVolume.set(
"mute", muted);
172 m_sinkIsAsync = isAsync;
174 m_audioSink.set(
"async", m_sinkIsAsync);
179 if (device == m_audioDevice)
181 qCDebug(qLcMediaAudioOutput) <<
"setAudioDevice" << device.description() << device.isNull();
183 m_audioDevice = device;
188 constexpr bool forceNewSinkCreation =
true;
189 if constexpr (!forceNewSinkCreation) {
190 if (sinkHasDeviceProperty(m_audioSink) && !isCustomAudioDevice(m_audioDevice)) {
191 m_audioSink.set(
"device", m_audioDevice.id().constData());
198 m_audioVolume.src().modifyPipelineInIdleProbe([&] {
199 qUnlinkGstElements(m_audioVolume, m_audioSink);
200 m_audioOutputBin.stopAndRemoveElements(m_audioSink);
201 m_audioSink = std::move(newSink);
202 m_audioOutputBin.add(m_audioSink);
203 m_audioSink.syncStateWithParent();
204 qLinkGstElements(m_audioVolume, m_audioSink);
static QGstBin createFromPipelineDescription(const char *pipelineDescription, const char *name=nullptr, bool ghostUnlinkedPads=false)
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
void setAudioDevice(const QAudioDevice &) override
void setMuted(bool) override
void setVolume(float) override
Combined button and popup list for selecting options.
void pulseVersionSanityCheck()
constexpr QLatin1String defaultSinkName
bool sinkHasDeviceProperty(const QGstElement &element)