diff options
Diffstat (limited to 'src/plugins/multimedia/gstreamer')
24 files changed, 331 insertions, 92 deletions
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp index 1bf4c2021..f793a4537 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp @@ -36,24 +36,29 @@ typedef enum { } GstPlayFlags; - -QGstreamerAudioDecoder::QGstreamerAudioDecoder(QAudioDecoder *parent) - : QPlatformAudioDecoder(parent), - m_playbin(GST_PIPELINE_CAST(QGstElement("playbin", "playbin").element())) +QMaybe<QPlatformAudioDecoder *> QGstreamerAudioDecoder::create(QAudioDecoder *parent) { - if (m_playbin.isNull()) { - // ### set error - return; - } + QGstElement audioconvert("audioconvert", "audioconvert"); + if (!audioconvert) + return errorMessageCannotFindElement("audioconvert"); + + QGstPipeline playbin = GST_PIPELINE_CAST(QGstElement("playbin", "playbin").element()); + if (!playbin) + return errorMessageCannotFindElement("playbin"); + return new QGstreamerAudioDecoder(playbin, audioconvert, parent); +} + +QGstreamerAudioDecoder::QGstreamerAudioDecoder(QGstPipeline playbin, QGstElement audioconvert, + QAudioDecoder *parent) + : QPlatformAudioDecoder(parent), m_playbin(playbin), m_audioConvert(audioconvert) +{ // Sort out messages m_playbin.installMessageFilter(this); // Set the rest of the pipeline up setAudioFlags(true); - m_audioConvert = QGstElement("audioconvert", "audioconvert"); - m_outputBin = QGstBin("audio-output-bin"); m_outputBin.add(m_audioConvert); @@ -61,7 +66,8 @@ QGstreamerAudioDecoder::QGstreamerAudioDecoder(QAudioDecoder *parent) m_outputBin.addGhostPad(m_audioConvert, "sink"); g_object_set(m_playbin.object(), "audio-sink", m_outputBin.element(), NULL); - g_signal_connect(m_playbin.object(), "deep-notify::source", (GCallback) &QGstreamerAudioDecoder::configureAppSrcElement, (gpointer)this); + g_signal_connect(m_playbin.object(), "deep-notify::source", + (GCallback)&QGstreamerAudioDecoder::configureAppSrcElement, (gpointer)this); // Set volume to 100% gdouble volume = 1.0; @@ -285,8 +291,15 @@ void QGstreamerAudioDecoder::start() return; } - if (!m_appSrc) - m_appSrc = new QGstAppSrc(this); + if (!m_appSrc) { + auto maybeAppSrc = QGstAppSrc::create(this); + if (maybeAppSrc) { + m_appSrc = maybeAppSrc.value(); + } else { + processInvalidMedia(QAudioDecoder::ResourceError, maybeAppSrc.error()); + return; + } + } m_playbin.set("uri", "appsrc://"); } else { diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h index ec4ede00d..5a37f8b2a 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h @@ -20,7 +20,8 @@ #include <QtCore/qmutex.h> #include <QtCore/qurl.h> -#include "private/qplatformaudiodecoder_p.h" +#include <private/qplatformaudiodecoder_p.h> +#include <private/qmultimediautils_p.h> #include <qgstpipeline_p.h> #include "qaudiodecoder.h" @@ -42,7 +43,7 @@ class QGstreamerAudioDecoder Q_OBJECT public: - QGstreamerAudioDecoder(QAudioDecoder *parent); + static QMaybe<QPlatformAudioDecoder *> create(QAudioDecoder *parent); virtual ~QGstreamerAudioDecoder(); QUrl source() const override; @@ -77,6 +78,8 @@ private slots: void updateDuration(); private: + QGstreamerAudioDecoder(QGstPipeline playbin, QGstElement audioconvert, QAudioDecoder *parent); + void setAudioFlags(bool wantNativeAudio); void addAppSink(); void removeAppSink(); diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp index ac98e3fac..cf339d7d7 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp @@ -17,23 +17,42 @@ #include <qgstutils_p.h> #include <qgstreamermessage_p.h> +#include <utility> + QT_BEGIN_NAMESPACE -QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device) +QMaybe<QPlatformAudioSink *> QGStreamerAudioSink::create(const QAudioDevice &device) +{ + auto maybeAppSrc = QGstAppSrc::create(); + if (!maybeAppSrc) + return maybeAppSrc.error(); + + QGstElement audioconvert("audioconvert", "conv"); + if (!audioconvert) + return errorMessageCannotFindElement("audioconvert"); + + QGstElement volume("volume", "volume"); + if (!volume) + return errorMessageCannotFindElement("volume"); + + return new QGStreamerAudioSink(device, maybeAppSrc.value(), audioconvert, volume); +} + +QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device, QGstAppSrc *appsrc, + QGstElement audioconvert, QGstElement volume) : m_device(device.id()), - gstPipeline("pipeline") + gstPipeline("pipeline"), + gstVolume(std::move(volume)), + m_appSrc(appsrc) { gstPipeline.installMessageFilter(this); - m_appSrc = new QGstAppSrc; connect(m_appSrc, &QGstAppSrc::bytesProcessed, this, &QGStreamerAudioSink::bytesProcessedByAppSrc); connect(m_appSrc, &QGstAppSrc::noMoreData, this, &QGStreamerAudioSink::needData); gstAppSrc = m_appSrc->element(); - // gstDecodeBin = gst_element_factory_make ("decodebin", "dec"); QGstElement queue("queue", "queue"); - QGstElement conv("audioconvert", "conv"); - gstVolume = QGstElement("volume", "volume"); + if (m_volume != 1.) gstVolume.set("volume", m_volume); @@ -43,8 +62,8 @@ QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device) const auto *audioInfo = static_cast<const QGStreamerAudioDeviceInfo *>(device.handle()); gstOutput = gst_device_create_element(audioInfo->gstDevice, nullptr); - gstPipeline.add(gstAppSrc, queue, /*gstDecodeBin, */ conv, gstVolume, gstOutput); - gstAppSrc.link(queue, conv, gstVolume, gstOutput); + gstPipeline.add(gstAppSrc, queue, /*gstDecodeBin, */ audioconvert, gstVolume, gstOutput); + gstAppSrc.link(queue, audioconvert, gstVolume, gstOutput); } QGStreamerAudioSink::~QGStreamerAudioSink() diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h index 6a034f7e9..26d2c9b28 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h @@ -26,6 +26,7 @@ #include "qaudio.h" #include "qaudiodevice.h" #include <private/qaudiosystem_p.h> +#include <private/qmultimediautils_p.h> #include <qgst_p.h> #include <qgstpipeline_p.h> @@ -42,7 +43,7 @@ class QGStreamerAudioSink Q_OBJECT public: - QGStreamerAudioSink(const QAudioDevice &device); + static QMaybe<QPlatformAudioSink *> create(const QAudioDevice &device); ~QGStreamerAudioSink(); void start(QIODevice *device) override; @@ -68,6 +69,9 @@ private Q_SLOTS: void needData(); private: + QGStreamerAudioSink(const QAudioDevice &device, QGstAppSrc *appsrc, QGstElement audioconvert, + QGstElement volume); + void setState(QAudio::State state); void setError(QAudio::Error error); diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp index d1b00840a..77d09be49 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp @@ -159,6 +159,7 @@ bool QGStreamerAudioSource::open() QGstElement conv("audioconvert", "conv"); gstVolume = QGstElement("volume", "volume"); + Q_ASSERT(gstVolume); if (m_volume != 1.) gstVolume.set("volume", m_volume); diff --git a/src/plugins/multimedia/gstreamer/common/qgst_p.h b/src/plugins/multimedia/gstreamer/common/qgst_p.h index 40897ad9d..ec2f9cb58 100644 --- a/src/plugins/multimedia/gstreamer/common/qgst_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgst_p.h @@ -290,6 +290,8 @@ public: gst_object_unref(m_object); } + explicit operator bool() const { return bool(m_object); } + friend bool operator==(const QGstObject &a, const QGstObject &b) { return a.m_object == b.m_object; } friend bool operator!=(const QGstObject &a, const QGstObject &b) @@ -430,6 +432,10 @@ public: QGstElement(const char *factory, const char *name = nullptr) : QGstElement(gst_element_factory_make(factory, name), NeedsRef) { +#ifndef QT_NO_DEBUG + if (!m_object) + qWarning() << "Failed to make element" << name << "from factory" << factory; +#endif } bool linkFiltered(const QGstElement &next, const QGstMutableCaps &caps) @@ -595,6 +601,11 @@ inline QGstCaps QGValue::toCaps() const return QGstCaps(gst_value_get_caps(value)); } +inline QString errorMessageCannotFindElement(std::string_view element) +{ + return QStringLiteral("Could not find the %1 GStreamer element").arg(element.data()); +} + QT_END_NAMESPACE #endif diff --git a/src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp b/src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp index 053b3ce8e..b3e4837b4 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp @@ -10,12 +10,18 @@ Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc") -QGstAppSrc::QGstAppSrc(QObject *parent) - : QObject(parent) +QMaybe<QGstAppSrc *> QGstAppSrc::create(QObject *parent) +{ + QGstElement appsrc("appsrc", "appsrc"); + if (!appsrc) + return errorMessageCannotFindElement("appsrc"); + + return new QGstAppSrc(appsrc, parent); +} + +QGstAppSrc::QGstAppSrc(QGstElement appsrc, QObject *parent) + : QObject(parent), m_appSrc(std::move(appsrc)) { - m_appSrc = QGstElement("appsrc", "appsrc"); - if (m_appSrc.isNull()) - qWarning() << "Could not create GstAppSrc."; } QGstAppSrc::~QGstAppSrc() diff --git a/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h b/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h index 0266e0312..8d5d2b0c1 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h @@ -16,6 +16,7 @@ // #include <private/qtmultimediaglobal_p.h> +#include <private/qmultimediautils_p.h> #include <qaudioformat.h> #include <QtCore/qobject.h> @@ -34,7 +35,7 @@ class Q_MULTIMEDIA_EXPORT QGstAppSrc : public QObject { Q_OBJECT public: - QGstAppSrc(QObject *parent = 0); + static QMaybe<QGstAppSrc *> create(QObject *parent = nullptr); ~QGstAppSrc(); bool setup(QIODevice *stream = nullptr, qint64 offset = 0); @@ -61,6 +62,8 @@ private Q_SLOTS: void streamDestroyed(); private: + QGstAppSrc(QGstElement appsrc, QObject *parent); + bool setStream(QIODevice *, qint64 offset); bool isStreamValid() const { diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp index 455e98f51..01ea33694 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp @@ -14,17 +14,33 @@ #include <sys/stat.h> #include <fcntl.h> +#include <utility> + Q_LOGGING_CATEGORY(qLcMediaAudioInput, "qt.multimedia.audioInput") QT_BEGIN_NAMESPACE -QGstreamerAudioInput::QGstreamerAudioInput(QAudioInput *parent) - : QObject(parent), - QPlatformAudioInput(parent), - gstAudioInput("audioInput") +QMaybe<QPlatformAudioInput *> QGstreamerAudioInput::create(QAudioInput *parent) +{ + QGstElement autoaudiosrc("autoaudiosrc", "autoaudiosrc"); + if (!autoaudiosrc) + return errorMessageCannotFindElement("autoaudiosrc"); + + QGstElement volume("volume", "volume"); + if (!volume) + return errorMessageCannotFindElement("volume"); + + return new QGstreamerAudioInput(autoaudiosrc, volume, parent); +} + +QGstreamerAudioInput::QGstreamerAudioInput(QGstElement autoaudiosrc, QGstElement volume, + QAudioInput *parent) + : QObject(parent), + QPlatformAudioInput(parent), + gstAudioInput("audioInput"), + audioSrc(std::move(autoaudiosrc)), + audioVolume(std::move(volume)) { - audioSrc = QGstElement("autoaudiosrc", "autoaudiosrc"); - audioVolume = QGstElement("volume", "volume"); gstAudioInput.add(audioSrc, audioVolume); audioSrc.link(audioVolume); diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h index 2586898b2..b74a21718 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h @@ -16,6 +16,7 @@ // #include <private/qtmultimediaglobal_p.h> +#include <private/qmultimediautils_p.h> #include <qaudiodevice.h> #include <QtCore/qobject.h> @@ -34,7 +35,7 @@ class Q_MULTIMEDIA_EXPORT QGstreamerAudioInput : public QObject, public QPlatfor Q_OBJECT public: - QGstreamerAudioInput(QAudioInput *parent); + static QMaybe<QPlatformAudioInput *> create(QAudioInput *parent); ~QGstreamerAudioInput(); int volume() const; @@ -54,6 +55,8 @@ Q_SIGNALS: void volumeChanged(int); private: + QGstreamerAudioInput(QGstElement autoaudiosrc, QGstElement volume, QAudioInput *parent); + float m_volume = 1.; bool m_muted = false; diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp index 9891e0b6c..4d43226d2 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp @@ -14,20 +14,45 @@ #include <sys/stat.h> #include <fcntl.h> +#include <utility> + Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput") QT_BEGIN_NAMESPACE -QGstreamerAudioOutput::QGstreamerAudioOutput(QAudioOutput *parent) - : QObject(parent), - QPlatformAudioOutput(parent), - gstAudioOutput("audioOutput") +QMaybe<QPlatformAudioOutput *> QGstreamerAudioOutput::create(QAudioOutput *parent) +{ + QGstElement audioconvert("audioconvert", "audioConvert"); + if (!audioconvert) + return errorMessageCannotFindElement("audioconvert"); + + QGstElement audioresample("audioresample", "audioResample"); + if (!audioresample) + return errorMessageCannotFindElement("audioresample"); + + QGstElement volume("volume", "volume"); + if (!volume) + return errorMessageCannotFindElement("volume"); + + QGstElement autoaudiosink("autoaudiosink", "autoAudioSink"); + if (!autoaudiosink) + return errorMessageCannotFindElement("autoaudiosink"); + + return new QGstreamerAudioOutput(audioconvert, audioresample, volume, autoaudiosink, parent); +} + +QGstreamerAudioOutput::QGstreamerAudioOutput(QGstElement audioconvert, QGstElement audioresample, + QGstElement volume, QGstElement autoaudiosink, + QAudioOutput *parent) + : QObject(parent), + QPlatformAudioOutput(parent), + gstAudioOutput("audioOutput"), + audioConvert(std::move(audioconvert)), + audioResample(std::move(audioresample)), + audioVolume(std::move(volume)), + audioSink(std::move(autoaudiosink)) { audioQueue = QGstElement("queue", "audioQueue"); - audioConvert = QGstElement("audioconvert", "audioConvert"); - audioResample = QGstElement("audioresample", "audioResample"); - audioVolume = QGstElement("volume", "volume"); - audioSink = QGstElement("autoaudiosink", "autoAudioSink"); gstAudioOutput.add(audioQueue, audioConvert, audioResample, audioVolume, audioSink); audioQueue.link(audioConvert, audioResample, audioVolume, audioSink); diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h index a44e6589a..e9d1e1b72 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h @@ -16,6 +16,7 @@ // #include <private/qtmultimediaglobal_p.h> +#include <private/qmultimediautils_p.h> #include <qaudiodevice.h> #include <QtCore/qobject.h> @@ -34,7 +35,7 @@ class Q_MULTIMEDIA_EXPORT QGstreamerAudioOutput : public QObject, public QPlatfo Q_OBJECT public: - QGstreamerAudioOutput(QAudioOutput *parent); + static QMaybe<QPlatformAudioOutput *> create(QAudioOutput *parent); ~QGstreamerAudioOutput(); void setAudioDevice(const QAudioDevice &) override; @@ -50,6 +51,9 @@ Q_SIGNALS: void volumeChanged(int); private: + QGstreamerAudioOutput(QGstElement audioconvert, QGstElement audioresample, QGstElement volume, + QGstElement autoaudiosink, QAudioOutput *parent); + QAudioDevice m_audioOutput; // Gst elements diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp index 9fd65aef2..eb0e8f427 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp @@ -29,9 +29,8 @@ Q_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player") QT_BEGIN_NAMESPACE -QGstreamerMediaPlayer::TrackSelector::TrackSelector(TrackType type, const char *name) - : selector(QGstElement("input-selector", name)), - type(type) +QGstreamerMediaPlayer::TrackSelector::TrackSelector(TrackType type, QGstElement selector) + : selector(selector), type(type) { selector.set("sync-streams", true); selector.set("sync-mode", 1 /*clock*/); @@ -74,17 +73,49 @@ QGstreamerMediaPlayer::TrackSelector &QGstreamerMediaPlayer::trackSelector(Track return ts; } -QGstreamerMediaPlayer::QGstreamerMediaPlayer(QMediaPlayer *parent) +QMaybe<QPlatformMediaPlayer *> QGstreamerMediaPlayer::create(QMediaPlayer *parent) +{ + auto videoOutput = QGstreamerVideoOutput::create(); + if (!videoOutput) + return videoOutput.error(); + + QGstElement decodebin("decodebin", nullptr); + if (!decodebin) + return errorMessageCannotFindElement("decodebin"); + + QGstElement videoInputSelector("input-selector", "videoInputSelector"); + if (!videoInputSelector) + return errorMessageCannotFindElement("input-selector"); + + QGstElement audioInputSelector("input-selector", "audioInputSelector"); + if (!audioInputSelector) + return errorMessageCannotFindElement("input-selector"); + + QGstElement subTitleInputSelector("input-selector", "subTitleInputSelector"); + if (!subTitleInputSelector) + return errorMessageCannotFindElement("input-selector"); + + return new QGstreamerMediaPlayer(videoOutput.value(), decodebin, videoInputSelector, + audioInputSelector, subTitleInputSelector, parent); +} + +QGstreamerMediaPlayer::QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput, + QGstElement decodebin, + QGstElement videoInputSelector, + QGstElement audioInputSelector, + QGstElement subTitleInputSelector, + QMediaPlayer *parent) : QObject(parent), QPlatformMediaPlayer(parent), - trackSelectors{{{ VideoStream, "videoInputSelector" }, - { AudioStream, "audioInputSelector" }, - { SubtitleStream, "subTitleInputSelector" }}}, - playerPipeline("playerPipeline") + trackSelectors{ { { VideoStream, videoInputSelector }, + { AudioStream, audioInputSelector }, + { SubtitleStream, subTitleInputSelector } } }, + playerPipeline("playerPipeline"), + gstVideoOutput(videoOutput) { playerPipeline.setFlushOnConfigChanges(true); - gstVideoOutput = new QGstreamerVideoOutput(this); + gstVideoOutput->setParent(this); gstVideoOutput->setPipeline(playerPipeline); for (auto &ts : trackSelectors) @@ -100,7 +131,6 @@ QGstreamerMediaPlayer::QGstreamerMediaPlayer(QMediaPlayer *parent) * This is ugly. We get the GType of decodebin so we can quickly detect * when a decodebin is added to uridecodebin so we can set the * post-stream-topology setting to TRUE */ - auto decodebin = QGstElement("decodebin", nullptr); decodebinType = G_OBJECT_TYPE(decodebin.element()); connect(&positionUpdateTimer, &QTimer::timeout, this, &QGstreamerMediaPlayer::updatePosition); } @@ -639,10 +669,21 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream) return; if (m_stream) { - if (!m_appSrc) - m_appSrc = new QGstAppSrc(this); + if (!m_appSrc) { + auto maybeAppSrc = QGstAppSrc::create(this); + if (maybeAppSrc) { + m_appSrc = maybeAppSrc.value(); + } else { + emit error(QMediaPlayer::ResourceError, maybeAppSrc.error()); + return; + } + } src = m_appSrc->element(); decoder = QGstElement("decodebin", "decoder"); + if (!decoder) { + emit error(QMediaPlayer::ResourceError, errorMessageCannotFindElement("decodebin")); + return; + } decoder.set("post-stream-topology", true); playerPipeline.add(src, decoder); src.link(decoder); @@ -652,6 +693,10 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream) } else { // use uridecodebin decoder = QGstElement("uridecodebin", "uridecoder"); + if (!decoder) { + emit error(QMediaPlayer::ResourceError, errorMessageCannotFindElement("uridecodebin")); + return; + } playerPipeline.add(decoder); // can't set post-stream-topology to true, as uridecodebin doesn't have the property. Use a hack decoder.connect("element-added", GCallback(QGstreamerMediaPlayer::uridecodebinElementAddedCallback), this); diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h index 7afad3ba7..3664f34b7 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h @@ -18,6 +18,7 @@ #include <QtCore/qstack.h> #include <private/qplatformmediaplayer_p.h> #include <private/qtmultimediaglobal_p.h> +#include <private/qmultimediautils_p.h> #include <qurl.h> #include <qgst_p.h> #include <qgstpipeline_p.h> @@ -43,7 +44,7 @@ class Q_MULTIMEDIA_EXPORT QGstreamerMediaPlayer Q_OBJECT public: - QGstreamerMediaPlayer(QMediaPlayer *parent = 0); + static QMaybe<QPlatformMediaPlayer *> create(QMediaPlayer *parent = nullptr); ~QGstreamerMediaPlayer(); qint64 position() const override; @@ -86,8 +87,12 @@ public Q_SLOTS: void updatePosition() { positionChanged(position()); } private: + QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput, QGstElement decodebin, + QGstElement videoInputSelector, QGstElement audioInputSelector, + QGstElement subTitleInputSelector, QMediaPlayer *parent); + struct TrackSelector { - TrackSelector(TrackType, const char *name); + TrackSelector(TrackType, QGstElement selector); QGstPad createInputPad(); void removeInputPad(QGstPad pad); void removeAllInputPads(); diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp index 919d43559..9385030e0 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp @@ -13,13 +13,27 @@ Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput") QT_BEGIN_NAMESPACE -QGstreamerVideoOutput::QGstreamerVideoOutput(QObject *parent) +QMaybe<QGstreamerVideoOutput *> QGstreamerVideoOutput::create(QObject *parent) +{ + QGstElement videoConvert("videoconvert", "videoConvert"); + if (!videoConvert) + return errorMessageCannotFindElement("videoconvert"); + + QGstElement videoSink("fakesink", "fakeVideoSink"); + if (!videoSink) + return errorMessageCannotFindElement("fakesink"); + + return new QGstreamerVideoOutput(videoConvert, videoSink, parent); +} + +QGstreamerVideoOutput::QGstreamerVideoOutput(QGstElement convert, QGstElement sink, + QObject *parent) : QObject(parent), - gstVideoOutput("videoOutput") + gstVideoOutput("videoOutput"), + videoConvert(std::move(convert)), + videoSink(std::move(sink)) { videoQueue = QGstElement("queue", "videoQueue"); - videoConvert = QGstElement("videoconvert", "videoConvert"); - videoSink = QGstElement("fakesink", "fakeVideoSink"); videoSink.set("sync", true); gstVideoOutput.add(videoQueue, videoConvert, videoSink); if (!videoQueue.link(videoConvert, videoSink)) @@ -52,6 +66,7 @@ void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink) isFakeSink = false; } else { gstSink = QGstElement("fakesink", "fakevideosink"); + Q_ASSERT(gstSink); gstSink.set("sync", true); isFakeSink = true; } diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h index 74652aa77..a7175dc36 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h @@ -17,6 +17,7 @@ #include <QtCore/qobject.h> #include <private/qtmultimediaglobal_p.h> +#include <private/qmultimediautils_p.h> #include <qgst_p.h> #include <qgstpipeline_p.h> #include <qwaitcondition.h> @@ -33,7 +34,7 @@ class Q_MULTIMEDIA_EXPORT QGstreamerVideoOutput : public QObject Q_OBJECT public: - QGstreamerVideoOutput(QObject *parent = 0); + static QMaybe<QGstreamerVideoOutput *> create(QObject *parent = nullptr); ~QGstreamerVideoOutput(); void setVideoSink(QVideoSink *sink); @@ -49,6 +50,8 @@ public: void flushSubtitles(); private: + QGstreamerVideoOutput(QGstElement videoConvert, QGstElement videoSink, QObject *parent); + void doLinkSubtitleStream(); QPointer<QGstreamerVideoSink> m_videoSink; diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp index bfb3bdf8d..f55038932 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp @@ -16,14 +16,37 @@ #include <QtCore/qdebug.h> -QGstreamerCamera::QGstreamerCamera(QCamera *camera) - : QPlatformCamera(camera) +QMaybe<QPlatformCamera *> QGstreamerCamera::create(QCamera *camera) +{ + QGstElement videotestsrc("videotestsrc"); + if (!videotestsrc) + return errorMessageCannotFindElement("videotestsrc"); + + QGstElement capsFilter("capsfilter", "videoCapsFilter"); + if (!capsFilter) + return errorMessageCannotFindElement("capsfilter"); + + QGstElement videoconvert("videoconvert", "videoConvert"); + if (!videoconvert) + return errorMessageCannotFindElement("videoconvert"); + + QGstElement videoscale("videoscale", "videoScale"); + if (!videoscale) + return errorMessageCannotFindElement("videoscale"); + + return new QGstreamerCamera(videotestsrc, capsFilter, videoconvert, videoscale, camera); +} + +QGstreamerCamera::QGstreamerCamera(QGstElement videotestsrc, QGstElement capsFilter, + QGstElement videoconvert, QGstElement videoscale, + QCamera *camera) + : QPlatformCamera(camera), + gstCamera(std::move(videotestsrc)), + gstCapsFilter(std::move(capsFilter)), + gstVideoConvert(std::move(videoconvert)), + gstVideoScale(std::move(videoscale)) { - gstCamera = QGstElement("videotestsrc"); - gstCapsFilter = QGstElement("capsfilter", "videoCapsFilter"); gstDecode = QGstElement("identity"); - gstVideoConvert = QGstElement("videoconvert", "videoConvert"); - gstVideoScale = QGstElement("videoscale", "videoScale"); gstCameraBin = QGstBin("camerabin"); gstCameraBin.add(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale); gstCamera.link(gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale); diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h index bd2c815ad..bab24d8c6 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - #ifndef QGSTREAMERCAMERACONTROL_H #define QGSTREAMERCAMERACONTROL_H @@ -18,6 +17,7 @@ #include <QHash> #include <private/qplatformcamera_p.h> +#include <private/qmultimediautils_p.h> #include "qgstreamermediacapture_p.h" #include <qgst_p.h> @@ -27,7 +27,8 @@ class QGstreamerCamera : public QPlatformCamera { Q_OBJECT public: - QGstreamerCamera(QCamera *camera); + static QMaybe<QPlatformCamera *> create(QCamera *camera); + virtual ~QGstreamerCamera(); bool isActive() const override; @@ -64,6 +65,9 @@ public: bool isV4L2Camera() const { return !m_v4l2Device.isEmpty(); } private: + QGstreamerCamera(QGstElement videotestsrc, QGstElement capsFilter, QGstElement videoconvert, + QGstElement videoscale, QCamera *camera); + void updateCameraProperties(); #if QT_CONFIG(linux_v4l) void initV4L2Controls(); diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp index 229698289..fcd373f81 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp @@ -12,6 +12,7 @@ #include <QtCore/QDebug> #include <QtCore/QDir> +#include <utility> #include <qstandardpaths.h> #include <qloggingcategory.h> @@ -20,9 +21,30 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture") -QGstreamerImageCapture::QGstreamerImageCapture(QImageCapture *parent) - : QPlatformImageCapture(parent), - QGstreamerBufferProbe(ProbeBuffers) +QMaybe<QPlatformImageCapture *> QGstreamerImageCapture::create(QImageCapture *parent) +{ + QGstElement videoconvert("videoconvert", "imageCaptureConvert"); + if (!videoconvert) + return errorMessageCannotFindElement("videoconvert"); + + QGstElement jpegenc("jpegenc", "jpegEncoder"); + if (!jpegenc) + return errorMessageCannotFindElement("jpegenc"); + + QGstElement jifmux("jifmux", "jpegMuxer"); + if (!jifmux) + return errorMessageCannotFindElement("jifmux"); + + return new QGstreamerImageCapture(videoconvert, jpegenc, jifmux, parent); +} + +QGstreamerImageCapture::QGstreamerImageCapture(QGstElement videoconvert, QGstElement jpegenc, + QGstElement jifmux, QImageCapture *parent) + : QPlatformImageCapture(parent), + QGstreamerBufferProbe(ProbeBuffers), + videoConvert(std::move(videoconvert)), + encoder(std::move(jpegenc)), + muxer(std::move(jifmux)) { bin = QGstBin("imageCaptureBin"); @@ -34,9 +56,6 @@ QGstreamerImageCapture::QGstreamerImageCapture(QImageCapture *parent) queue.set("max-size-bytes", uint(0)); queue.set("max-size-time", quint64(0)); - videoConvert = QGstElement("videoconvert", "imageCaptureConvert"); - encoder = QGstElement("jpegenc", "jpegEncoder"); - muxer = QGstElement("jifmux", "jpegMuxer"); sink = QGstElement("fakesink","imageCaptureSink"); // imageCaptureSink do not wait for a preroll buffer when going READY -> PAUSED // as no buffer will arrive until capture() is called diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h index b76649631..87d805802 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - #ifndef QGSTREAMERIMAGECAPTURECONTROL_H #define QGSTREAMERIMAGECAPTURECONTROL_H @@ -17,6 +16,7 @@ // #include <private/qplatformimagecapture_p.h> +#include <private/qmultimediautils_p.h> #include "qgstreamermediacapture_p.h" #include "qgstreamerbufferprobe_p.h" @@ -32,7 +32,7 @@ class QGstreamerImageCapture : public QPlatformImageCapture, private QGstreamerB { Q_OBJECT public: - QGstreamerImageCapture(QImageCapture *parent); + static QMaybe<QPlatformImageCapture *> create(QImageCapture *parent); virtual ~QGstreamerImageCapture(); bool isReadyForCapture() const override; @@ -53,6 +53,9 @@ public Q_SLOTS: void onCameraChanged(); private: + QGstreamerImageCapture(QGstElement videoconvert, QGstElement jpegenc, QGstElement jifmux, + QImageCapture *parent); + int doCapture(const QString &fileName); static gboolean saveImageFilter(GstElement *element, GstBuffer *buffer, GstPad *pad, void *appdata); diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp index 4a1c7f6b5..be34995ee 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp @@ -38,11 +38,19 @@ static void unlinkTeeFromPad(QGstElement tee, QGstPad sink) tee.releaseRequestPad(source); } +QMaybe<QPlatformMediaCaptureSession *> QGstreamerMediaCapture::create() +{ + auto videoOutput = QGstreamerVideoOutput::create(); + if (!videoOutput) + return videoOutput.error(); + + return new QGstreamerMediaCapture(videoOutput.value()); +} -QGstreamerMediaCapture::QGstreamerMediaCapture() - : gstPipeline("pipeline") +QGstreamerMediaCapture::QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput) + : gstPipeline("pipeline"), gstVideoOutput(videoOutput) { - gstVideoOutput = new QGstreamerVideoOutput(this); + gstVideoOutput->setParent(this); gstVideoOutput->setIsPreview(); gstVideoOutput->setPipeline(gstPipeline); @@ -181,6 +189,7 @@ void QGstreamerMediaCapture::linkEncoder(QGstPad audioSink, QGstPad videoSink) auto caps = gst_pad_get_current_caps(gstVideoTee.sink().pad()); encoderVideoCapsFilter = QGstElement("capsfilter", "encoderVideoCapsFilter"); + Q_ASSERT(encoderVideoCapsFilter); encoderVideoCapsFilter.set("caps", QGstMutableCaps(caps)); gstPipeline.add(encoderVideoCapsFilter); @@ -195,6 +204,7 @@ void QGstreamerMediaCapture::linkEncoder(QGstPad audioSink, QGstPad videoSink) auto caps = gst_pad_get_current_caps(gstAudioTee.sink().pad()); encoderAudioCapsFilter = QGstElement("capsfilter", "encoderAudioCapsFilter"); + Q_ASSERT(encoderAudioCapsFilter); encoderAudioCapsFilter.set("caps", QGstMutableCaps(caps)); gstPipeline.add(encoderAudioCapsFilter); diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h index 245cffe71..3aa6d6c9f 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h @@ -38,7 +38,7 @@ class QGstreamerMediaCapture : public QPlatformMediaCaptureSession Q_OBJECT public: - QGstreamerMediaCapture(); + static QMaybe<QPlatformMediaCaptureSession *> create(); virtual ~QGstreamerMediaCapture(); QPlatformCamera *camera() override; @@ -64,6 +64,8 @@ public: QGstreamerVideoSink *gstreamerVideoSink() const; private: + QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput); + friend QGstreamerMediaEncoder; // Gst elements QGstPipeline gstPipeline; diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp index 43472f657..f88524123 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp @@ -261,11 +261,13 @@ void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings) Q_ASSERT(!actualSink.isEmpty()); gstEncoder = QGstElement("encodebin", "encodebin"); + Q_ASSERT(gstEncoder); auto *encodingProfile = createEncodingProfile(settings); g_object_set (gstEncoder.object(), "profile", encodingProfile, nullptr); gst_encoding_profile_unref(encodingProfile); gstFileSink = QGstElement("filesink", "filesink"); + Q_ASSERT(gstFileSink); gstFileSink.set("location", QFile::encodeName(actualSink.toLocalFile()).constData()); gstFileSink.set("async", false); diff --git a/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp b/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp index 817aae1c2..e84f0301f 100644 --- a/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp +++ b/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp @@ -61,22 +61,22 @@ const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo() const QMaybe<QPlatformAudioDecoder *> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder) { - return new QGstreamerAudioDecoder(decoder); + return QGstreamerAudioDecoder::create(decoder); } QMaybe<QPlatformMediaCaptureSession *> QGstreamerIntegration::createCaptureSession() { - return new QGstreamerMediaCapture(); + return QGstreamerMediaCapture::create(); } QMaybe<QPlatformMediaPlayer *> QGstreamerIntegration::createPlayer(QMediaPlayer *player) { - return new QGstreamerMediaPlayer(player); + return QGstreamerMediaPlayer::create(player); } QMaybe<QPlatformCamera *> QGstreamerIntegration::createCamera(QCamera *camera) { - return new QGstreamerCamera(camera); + return QGstreamerCamera::create(camera); } QMaybe<QPlatformMediaRecorder *> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder) @@ -86,7 +86,7 @@ QMaybe<QPlatformMediaRecorder *> QGstreamerIntegration::createRecorder(QMediaRec QMaybe<QPlatformImageCapture *> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture) { - return new QGstreamerImageCapture(imageCapture); + return QGstreamerImageCapture::create(imageCapture); } QMaybe<QPlatformVideoSink *> QGstreamerIntegration::createVideoSink(QVideoSink *sink) @@ -96,12 +96,12 @@ QMaybe<QPlatformVideoSink *> QGstreamerIntegration::createVideoSink(QVideoSink * QMaybe<QPlatformAudioInput *> QGstreamerIntegration::createAudioInput(QAudioInput *q) { - return new QGstreamerAudioInput(q); + return QGstreamerAudioInput::create(q); } QMaybe<QPlatformAudioOutput *> QGstreamerIntegration::createAudioOutput(QAudioOutput *q) { - return new QGstreamerAudioOutput(q); + return QGstreamerAudioOutput::create(q); } GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id) const |