diff options
Diffstat (limited to 'src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp')
-rw-r--r-- | src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp | 191 |
1 files changed, 122 insertions, 69 deletions
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp index 6156c97be..1a8c6976c 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp @@ -2,116 +2,169 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <common/qgstreameraudiooutput_p.h> -#include <audio/qgstreameraudiodevice_p.h> +#include <QtCore/qloggingcategory.h> #include <QtMultimedia/qaudiodevice.h> #include <QtMultimedia/qaudiooutput.h> -#include <QtCore/qloggingcategory.h> -#include <utility> +#include <common/qgstpipeline_p.h> +#include <audio/qgstreameraudiodevice_p.h> -static Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput") QT_BEGIN_NAMESPACE -QMaybe<QPlatformAudioOutput *> QGstreamerAudioOutput::create(QAudioOutput *parent) +namespace { + +Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput") + +constexpr QLatin1String defaultSinkName = [] { + using namespace Qt::Literals; + + if constexpr (QT_CONFIG(pulseaudio)) + return "pulsesink"_L1; + else if constexpr (QT_CONFIG(alsa)) + return "alsasink"_L1; + else + return "autoaudiosink"_L1; +}(); + +bool hasDeviceProperty(const QGstElement &element) { - QGstElement audioconvert = QGstElement::createFromFactory("audioconvert", "audioConvert"); - if (!audioconvert) - return errorMessageCannotFindElement("audioconvert"); + using namespace Qt::Literals; + QLatin1String elementType = element.typeName(); + + if constexpr (QT_CONFIG(pulseaudio)) + return elementType == "GstPulseSink"_L1; + if constexpr (0 && QT_CONFIG(alsa)) // alsasrc has a "device" property, but it cannot be changed + // during playback + return elementType == "GstAlsaSink"_L1; - QGstElement audioresample = QGstElement::createFromFactory("audioresample", "audioResample"); - if (!audioresample) - return errorMessageCannotFindElement("audioresample"); + return false; +} - QGstElement volume = QGstElement::createFromFactory("volume", "volume"); - if (!volume) - return errorMessageCannotFindElement("volume"); +} // namespace - QGstElement autoaudiosink = QGstElement::createFromFactory("autoaudiosink", "autoAudioSink"); - if (!autoaudiosink) - return errorMessageCannotFindElement("autoaudiosink"); +QMaybe<QPlatformAudioOutput *> QGstreamerAudioOutput::create(QAudioOutput *parent) +{ + static const auto error = qGstErrorMessageIfElementsNotAvailable( + "audioconvert", "audioresample", "volume", "autoaudiosink"); + if (error) + return *error; - return new QGstreamerAudioOutput(audioconvert, audioresample, volume, autoaudiosink, parent); + return new QGstreamerAudioOutput(parent); } -QGstreamerAudioOutput::QGstreamerAudioOutput(QGstElement audioconvert, QGstElement audioresample, - QGstElement volume, QGstElement autoaudiosink, - QAudioOutput *parent) +QGstreamerAudioOutput::QGstreamerAudioOutput(QAudioOutput *parent) : QObject(parent), QPlatformAudioOutput(parent), - gstAudioOutput(QGstBin::create("audioOutput")), - audioConvert(std::move(audioconvert)), - audioResample(std::move(audioresample)), - audioVolume(std::move(volume)), - audioSink(std::move(autoaudiosink)) + m_audioOutputBin(QGstBin::create("audioOutput")), + m_audioQueue{ + QGstElement::createFromFactory("queue", "audioQueue"), + }, + m_audioConvert{ + QGstElement::createFromFactory("audioconvert", "audioConvert"), + }, + m_audioResample{ + QGstElement::createFromFactory("audioresample", "audioResample"), + }, + m_audioVolume{ + QGstElement::createFromFactory("volume", "volume"), + }, + m_audioSink{ + QGstElement::createFromFactory(defaultSinkName.constData(), "audiosink"), + } { - audioQueue = QGstElement::createFromFactory("queue", "audioQueue"); - gstAudioOutput.add(audioQueue, audioConvert, audioResample, audioVolume, audioSink); - qLinkGstElements(audioQueue, audioConvert, audioResample, audioVolume, audioSink); + m_audioOutputBin.add(m_audioQueue, m_audioConvert, m_audioResample, m_audioVolume, m_audioSink); + qLinkGstElements(m_audioQueue, m_audioConvert, m_audioResample, m_audioVolume, m_audioSink); - gstAudioOutput.addGhostPad(audioQueue, "sink"); + m_audioOutputBin.addGhostPad(m_audioQueue, "sink"); +} + +QGstElement QGstreamerAudioOutput::createGstElement() +{ + const auto *customDeviceInfo = + dynamic_cast<const QGStreamerCustomAudioDeviceInfo *>(m_audioDevice.handle()); + + if (customDeviceInfo) { + qCDebug(qLcMediaAudioOutput) + << "requesting custom audio sink element: " << customDeviceInfo->id; + + QGstElement element = + QGstBin::createFromPipelineDescription(customDeviceInfo->id, /*name=*/nullptr, + /*ghostUnlinkedPads=*/true); + if (element) + return element; + + qCWarning(qLcMediaAudioOutput) + << "Cannot create audio sink element:" << customDeviceInfo->id; + } + + const QByteArray &id = m_audioDevice.id(); + if constexpr (QT_CONFIG(pulseaudio) || QT_CONFIG(alsa)) { + QGstElement newSink = + QGstElement::createFromFactory(defaultSinkName.constData(), "audiosink"); + if (newSink) { + newSink.set("device", id.constData()); + return newSink; + } + + qWarning() << "Cannot create" << defaultSinkName; + } else { + auto *deviceInfo = dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle()); + if (deviceInfo && deviceInfo->gstDevice) { + QGstElement element = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosink"); + if (element) + return element; + } + } + qCWarning(qLcMediaAudioOutput) << "Invalid audio device:" << m_audioDevice.id(); + qCWarning(qLcMediaAudioOutput) + << "Failed to create a gst element for the audio device, using a default audio sink"; + return QGstElement::createFromFactory("autoaudiosink", "audiosink"); } QGstreamerAudioOutput::~QGstreamerAudioOutput() { - gstAudioOutput.setStateSync(GST_STATE_NULL); + m_audioOutputBin.setStateSync(GST_STATE_NULL); } void QGstreamerAudioOutput::setVolume(float volume) { - audioVolume.set("volume", volume); + m_audioVolume.set("volume", volume); } void QGstreamerAudioOutput::setMuted(bool muted) { - audioVolume.set("mute", muted); + m_audioVolume.set("mute", muted); } -void QGstreamerAudioOutput::setAudioDevice(const QAudioDevice &info) +void QGstreamerAudioOutput::setAudioDevice(const QAudioDevice &device) { - if (info == m_audioOutput) + if (device == m_audioDevice) return; - qCDebug(qLcMediaAudioOutput) << "setAudioOutput" << info.description() << info.isNull(); + qCDebug(qLcMediaAudioOutput) << "setAudioDevice" << device.description() << device.isNull(); - m_audioOutput = info; - const QByteArray &id = m_audioOutput.id(); + m_audioDevice = device; - QGstElement newSink; - if constexpr (QT_CONFIG(pulseaudio)) { - newSink = QGstElement::createFromFactory("pulsesink", "audiosink"); - if (newSink) - newSink.set("device", id.constData()); - else - qWarning() << "Cannot create pulsesink"; - } else if constexpr (QT_CONFIG(alsa)) { - newSink = QGstElement::createFromFactory("alsasink", "audiosink"); - if (newSink) - newSink.set("device", id.constData()); - else - qWarning() << "Cannot create alsasink"; - } else { - auto *deviceInfo = dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle()); - if (deviceInfo && deviceInfo->gstDevice) - newSink = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosink"); - else - qWarning() << "Invalid audio device"; + if (hasDeviceProperty(m_audioSink) && !isCustomAudioDevice(m_audioDevice)) { + m_audioSink.set("device", m_audioDevice.id().constData()); + return; } - if (newSink.isNull()) { - qWarning() << "Failed to create a gst element for the audio " - "device using a default audio sink"; - newSink = QGstElement::createFromFactory("autoaudiosink", "audiosink"); - } + QGstElement newSink = createGstElement(); - QGstPipeline::modifyPipelineWhileNotRunning(gstAudioOutput.getPipeline(), [&] { - qUnlinkGstElements(audioVolume, audioSink); - gstAudioOutput.stopAndRemoveElements(audioSink); - audioSink = std::move(newSink); - gstAudioOutput.add(audioSink); - audioSink.syncStateWithParent(); - qLinkGstElements(audioVolume, audioSink); + QGstPipeline::modifyPipelineWhileNotRunning(m_audioOutputBin.getPipeline(), [&] { + qUnlinkGstElements(m_audioVolume, m_audioSink); + m_audioOutputBin.stopAndRemoveElements(m_audioSink); + m_audioSink = std::move(newSink); + m_audioOutputBin.add(m_audioSink); + m_audioSink.syncStateWithParent(); + qLinkGstElements(m_audioVolume, m_audioSink); }); + + // we need to flush the pipeline, otherwise, the new sink doesn't always reach the new state + if (m_audioOutputBin.getPipeline()) + m_audioOutputBin.getPipeline().flush(); } QT_END_NAMESPACE |