diff options
20 files changed, 495 insertions, 228 deletions
diff --git a/src/gsttools/qgstreamerbushelper.cpp b/src/gsttools/qgstreamerbushelper.cpp index bd35d4b0a..bad1d210a 100644 --- a/src/gsttools/qgstreamerbushelper.cpp +++ b/src/gsttools/qgstreamerbushelper.cpp @@ -143,8 +143,10 @@ static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstre QMutexLocker lock(&d->filterMutex); for (QGstreamerSyncMessageFilter *filter : qAsConst(d->syncFilters)) { - if (filter->processSyncMessage(QGstreamerMessage(message))) + if (filter->processSyncMessage(QGstreamerMessage(message))) { + gst_message_unref(message); return GST_BUS_DROP; + } } return GST_BUS_PASS; diff --git a/src/gsttools/qgstreamervideooverlay.cpp b/src/gsttools/qgstreamervideooverlay.cpp index 6ad70fd6a..de4f255d5 100644 --- a/src/gsttools/qgstreamervideooverlay.cpp +++ b/src/gsttools/qgstreamervideooverlay.cpp @@ -64,59 +64,258 @@ static const ElementMap elementMap[] = { "xcb", "ximagesink" } }; -QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent, const QByteArray &elementName) - : QObject(parent) - , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) - , m_videoSink(0) - , m_isActive(false) - , m_hasForceAspectRatio(false) - , m_hasBrightness(false) - , m_hasContrast(false) - , m_hasHue(false) - , m_hasSaturation(false) - , m_hasShowPrerollFrame(false) - , m_windowId(0) - , m_aspectRatioMode(Qt::KeepAspectRatio) - , m_brightness(0) - , m_contrast(0) - , m_hue(0) - , m_saturation(0) +class QGstreamerSinkProperties { - if (!elementName.isEmpty()) - m_videoSink = gst_element_factory_make(elementName.constData(), NULL); - else - m_videoSink = findBestVideoSink(); - - if (m_videoSink) { - qt_gst_object_ref_sink(GST_OBJECT(m_videoSink)); //Take ownership +public: + virtual ~QGstreamerSinkProperties() + { + } - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - addProbeToPad(pad); - gst_object_unref(GST_OBJECT(pad)); + virtual bool hasShowPrerollFrame() const = 0; + virtual void reset() = 0; + virtual int brightness() const = 0; + virtual bool setBrightness(int brightness) = 0; + virtual int contrast() const = 0; + virtual bool setContrast(int contrast) = 0; + virtual int hue() const = 0; + virtual bool setHue(int hue) = 0; + virtual int saturation() const = 0; + virtual bool setSaturation(int saturation) = 0; + virtual Qt::AspectRatioMode aspectRatioMode() const = 0; + virtual void setAspectRatioMode(Qt::AspectRatioMode mode) = 0; +}; +class QXVImageSinkProperties : public QGstreamerSinkProperties +{ +public: + QXVImageSinkProperties(GstElement *sink) + : m_videoSink(sink) + { m_hasForceAspectRatio = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "force-aspect-ratio"); m_hasBrightness = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness"); m_hasContrast = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast"); m_hasHue = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue"); m_hasSaturation = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation"); m_hasShowPrerollFrame = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame"); + } - if (m_hasShowPrerollFrame) { - g_signal_connect(m_videoSink, "notify::show-preroll-frame", - G_CALLBACK(showPrerollFrameChanged), this); + bool hasShowPrerollFrame() const override + { + return m_hasShowPrerollFrame; + } + + void reset() override + { + setAspectRatioMode(m_aspectRatioMode); + setBrightness(m_brightness); + setContrast(m_contrast); + setHue(m_hue); + setSaturation(m_saturation); + } + + int brightness() const override + { + int brightness = 0; + if (m_hasBrightness) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); + + return brightness / 10; + } + + bool setBrightness(int brightness) override + { + m_brightness = brightness; + if (m_hasBrightness) + g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, NULL); + + return m_hasBrightness; + } + + int contrast() const override + { + int contrast = 0; + if (m_hasContrast) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); + + return contrast / 10; + } + + bool setContrast(int contrast) override + { + m_contrast = contrast; + if (m_hasContrast) + g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, NULL); + + return m_hasContrast; + } + + int hue() const override + { + int hue = 0; + if (m_hasHue) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); + + return hue / 10; + } + + bool setHue(int hue) override + { + m_hue = hue; + if (m_hasHue) + g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, NULL); + + return m_hasHue; + } + + int saturation() const override + { + int saturation = 0; + if (m_hasSaturation) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); + + return saturation / 10; + } + + bool setSaturation(int saturation) override + { + m_saturation = saturation; + if (m_hasSaturation) + g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, NULL); + + return m_hasSaturation; + } + + Qt::AspectRatioMode aspectRatioMode() const override + { + Qt::AspectRatioMode mode = Qt::KeepAspectRatio; + if (m_hasForceAspectRatio) { + gboolean forceAR = false; + g_object_get(G_OBJECT(m_videoSink), "force-aspect-ratio", &forceAR, NULL); + if (!forceAR) + mode = Qt::IgnoreAspectRatio; } + + return mode; } -} -QGstreamerVideoOverlay::~QGstreamerVideoOverlay() + void setAspectRatioMode(Qt::AspectRatioMode mode) override + { + m_aspectRatioMode = mode; + if (m_hasForceAspectRatio) { + g_object_set(G_OBJECT(m_videoSink), + "force-aspect-ratio", + (mode == Qt::KeepAspectRatio), + (const char*)NULL); + } + } + +protected: + + GstElement *m_videoSink = nullptr; + bool m_hasForceAspectRatio = false; + bool m_hasBrightness = false; + bool m_hasContrast = false; + bool m_hasHue = false; + bool m_hasSaturation = false; + bool m_hasShowPrerollFrame = false; + Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio; + int m_brightness = 0; + int m_contrast = 0; + int m_hue = 0; + int m_saturation = 0; +}; + +class QVaapiSinkProperties : public QXVImageSinkProperties { - if (m_videoSink) { - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - removeProbeFromPad(pad); - gst_object_unref(GST_OBJECT(pad)); - gst_object_unref(GST_OBJECT(m_videoSink)); +public: + QVaapiSinkProperties(GstElement *sink) + : QXVImageSinkProperties(sink) + { + // Set default values. + m_contrast = 1; + m_saturation = 1; + } + + int brightness() const override + { + gfloat brightness = 0; + if (m_hasBrightness) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); + + return brightness * 100; // [-1,1] -> [-100,100] + } + + bool setBrightness(int brightness) override + { + m_brightness = brightness; + if (m_hasBrightness) { + gfloat v = brightness / 100.0; // [-100,100] -> [-1,1] + g_object_set(G_OBJECT(m_videoSink), "brightness", v, NULL); + } + + return m_hasBrightness; + } + + int contrast() const override + { + gfloat contrast = 1; + if (m_hasContrast) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); + + return (contrast - 1) * 100; // [0,2] -> [-100,100] + } + + bool setContrast(int contrast) override + { + m_contrast = contrast; + if (m_hasContrast) { + gfloat v = (contrast / 100.0) + 1; // [-100,100] -> [0,2] + g_object_set(G_OBJECT(m_videoSink), "contrast", v, NULL); + } + + return m_hasContrast; + } + + int hue() const override + { + gfloat hue = 0; + if (m_hasHue) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); + + return hue / 180 * 100; // [-180,180] -> [-100,100] + } + + bool setHue(int hue) override + { + m_hue = hue; + if (m_hasHue) { + gfloat v = hue / 100.0 * 180; // [-100,100] -> [-180,180] + g_object_set(G_OBJECT(m_videoSink), "hue", v, NULL); + } + + return m_hasHue; } -} + + int saturation() const override + { + gfloat saturation = 1; + if (m_hasSaturation) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); + + return (saturation - 1) * 100; // [0,2] -> [-100,100] + } + + bool setSaturation(int saturation) override + { + m_saturation = saturation; + if (m_hasSaturation) { + gfloat v = (saturation / 100.0) + 1; // [-100,100] -> [0,2] + g_object_set(G_OBJECT(m_videoSink), "saturation", v, NULL); + } + + return m_hasSaturation; + } +}; static bool qt_gst_element_is_functioning(GstElement *element) { @@ -129,7 +328,7 @@ static bool qt_gst_element_is_functioning(GstElement *element) return false; } -GstElement *QGstreamerVideoOverlay::findBestVideoSink() const +static GstElement *findBestVideoSink() { GstElement *choice = 0; QString platform = QGuiApplication::platformName(); @@ -176,6 +375,44 @@ GstElement *QGstreamerVideoOverlay::findBestVideoSink() const return choice; } +QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent, const QByteArray &elementName) + : QObject(parent) + , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) +{ + if (!elementName.isEmpty()) + m_videoSink = gst_element_factory_make(elementName.constData(), NULL); + else + m_videoSink = findBestVideoSink(); + + if (m_videoSink) { + qt_gst_object_ref_sink(GST_OBJECT(m_videoSink)); //Take ownership + + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + + QString sinkName(QLatin1String(GST_OBJECT_NAME(m_videoSink))); + bool isVaapi = sinkName.startsWith(QLatin1String("vaapisink")); + m_sinkProperties = isVaapi ? new QVaapiSinkProperties(m_videoSink) : new QXVImageSinkProperties(m_videoSink); + + if (m_sinkProperties->hasShowPrerollFrame()) { + g_signal_connect(m_videoSink, "notify::show-preroll-frame", + G_CALLBACK(showPrerollFrameChanged), this); + } + } +} + +QGstreamerVideoOverlay::~QGstreamerVideoOverlay() +{ + if (m_videoSink) { + delete m_sinkProperties; + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); + gst_object_unref(GST_OBJECT(m_videoSink)); + } +} + GstElement *QGstreamerVideoOverlay::videoSink() const { return m_videoSink; @@ -209,11 +446,7 @@ void QGstreamerVideoOverlay::setWindowHandle_helper(WId id) #endif // Properties need to be reset when changing the winId. - setAspectRatioMode(m_aspectRatioMode); - setBrightness(m_brightness); - setContrast(m_contrast); - setHue(m_hue); - setSaturation(m_saturation); + m_sinkProperties->reset(); } } @@ -313,7 +546,7 @@ void QGstreamerVideoOverlay::updateIsActive() GstState state = GST_STATE(m_videoSink); gboolean showPreroll = true; - if (m_hasShowPrerollFrame) + if (m_sinkProperties->hasShowPrerollFrame()) g_object_get(G_OBJECT(m_videoSink), "show-preroll-frame", &showPreroll, NULL); bool newIsActive = (state == GST_STATE_PLAYING || (state == GST_STATE_PAUSED && showPreroll)); @@ -331,108 +564,56 @@ void QGstreamerVideoOverlay::showPrerollFrameChanged(GObject *, GParamSpec *, QG Qt::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const { - Qt::AspectRatioMode mode = Qt::KeepAspectRatio; - - if (m_hasForceAspectRatio) { - gboolean forceAR = false; - g_object_get(G_OBJECT(m_videoSink), "force-aspect-ratio", &forceAR, NULL); - if (!forceAR) - mode = Qt::IgnoreAspectRatio; - } - - return mode; + return m_sinkProperties->aspectRatioMode(); } void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) { - if (m_hasForceAspectRatio) { - g_object_set(G_OBJECT(m_videoSink), - "force-aspect-ratio", - (mode == Qt::KeepAspectRatio), - (const char*)NULL); - } - - m_aspectRatioMode = mode; + m_sinkProperties->setAspectRatioMode(mode); } int QGstreamerVideoOverlay::brightness() const { - int brightness = 0; - - if (m_hasBrightness) - g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); - - return brightness / 10; + return m_sinkProperties->brightness(); } void QGstreamerVideoOverlay::setBrightness(int brightness) { - if (m_hasBrightness) { - g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, NULL); + if (m_sinkProperties->setBrightness(brightness)) emit brightnessChanged(brightness); - } - - m_brightness = brightness; } int QGstreamerVideoOverlay::contrast() const { - int contrast = 0; - - if (m_hasContrast) - g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); - - return contrast / 10; + return m_sinkProperties->contrast(); } void QGstreamerVideoOverlay::setContrast(int contrast) { - if (m_hasContrast) { - g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, NULL); + if (m_sinkProperties->setContrast(contrast)) emit contrastChanged(contrast); - } - - m_contrast = contrast; } int QGstreamerVideoOverlay::hue() const { - int hue = 0; - - if (m_hasHue) - g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); - - return hue / 10; + return m_sinkProperties->hue(); } void QGstreamerVideoOverlay::setHue(int hue) { - if (m_hasHue) { - g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, NULL); + if (m_sinkProperties->setHue(hue)) emit hueChanged(hue); - } - - m_hue = hue; } int QGstreamerVideoOverlay::saturation() const { - int saturation = 0; - - if (m_hasSaturation) - g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); - - return saturation / 10; + return m_sinkProperties->saturation(); } void QGstreamerVideoOverlay::setSaturation(int saturation) { - if (m_hasSaturation) { - g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, NULL); + if (m_sinkProperties->setSaturation(saturation)) emit saturationChanged(saturation); - } - - m_saturation = saturation; } QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiohelpers.cpp b/src/multimedia/audio/qaudiohelpers.cpp index 8aa4c090b..fae591477 100644 --- a/src/multimedia/audio/qaudiohelpers.cpp +++ b/src/multimedia/audio/qaudiohelpers.cpp @@ -43,6 +43,49 @@ QT_BEGIN_NAMESPACE +// Base implementation of 24 bits number. +// Used to adjust 3 bytes values by a factor. +// TODO: Uses little-endian only. +class Int24 +{ +public: + quint8 data[3]; + Int24(qint32 v) { + data[0] = v & 0xFF; + data[1] = (v & 0xFF00) >> 8; + data[2] = (v & 0xFF0000) >> 16; + } + template<class T> + T multiply(qreal factor, T v = 0) const { + v |= data[0]; + v |= data[1] << 8; + v |= data[2] << 16; + v *= factor; + return v; + } +}; + +class qint24: public Int24 +{ +public: + qint24(qint32 v): Int24(v) {} + qint24 operator*(qreal factor) const { + // Checks if it is a signed value. + qint32 v = (data[2] & 0x80) ? 0xFF000000 : 0; + return multiply(factor, v); + } +}; + +class quint24: public Int24 +{ +public: + quint24(quint32 v): Int24(v) {} + quint24 operator*(qreal factor) const { + return multiply<quint32>(factor); + } +}; + + namespace QAudioHelperInternal { @@ -101,6 +144,12 @@ void qMultiplySamples(qreal factor, const QAudioFormat &format, const void* src, else if (format.sampleType() == QAudioFormat::UnSignedInt) QAudioHelperInternal::adjustUnsignedSamples<quint16>(factor,src,dest,samplesCount); break; + case 24: + if (format.sampleType() == QAudioFormat::SignedInt) + QAudioHelperInternal::adjustSamples<qint24>(factor,src,dest,samplesCount); + else if (format.sampleType() == QAudioFormat::UnSignedInt) + QAudioHelperInternal::adjustSamples<quint24>(factor,src,dest,samplesCount); + break; default: if (format.sampleType() == QAudioFormat::SignedInt) QAudioHelperInternal::adjustSamples<qint32>(factor,src,dest,samplesCount); diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.cpp b/src/multimedia/audio/qsoundeffect_pulse_p.cpp index 981d4c01f..77f8607a6 100644 --- a/src/multimedia/audio/qsoundeffect_pulse_p.cpp +++ b/src/multimedia/audio/qsoundeffect_pulse_p.cpp @@ -164,9 +164,9 @@ private Q_SLOTS: if (m_prepared) return; - m_context = 0; + m_context = nullptr; m_mainLoop = pa_threaded_mainloop_new(); - if (m_mainLoop == 0) { + if (m_mainLoop == nullptr) { qWarning("PulseAudioService: unable to create pulseaudio mainloop"); return; } @@ -182,11 +182,11 @@ private Q_SLOTS: lock(); m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtPulseAudio:%1")).arg(::getpid()).toLatin1().constData()); - if (m_context == 0) { + if (m_context == nullptr) { qWarning("PulseAudioService: Unable to create new pulseaudio context"); unlock(); pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = 0; + m_mainLoop = nullptr; onContextFailed(); return; } @@ -194,16 +194,16 @@ private Q_SLOTS: pa_context_set_state_callback(m_context, context_state_callback, this); const QByteArray srvStrEnv = qgetenv("QT_PULSE_SERVER_STRING"); - const char *srvStr = srvStrEnv.isNull() ? 0 : srvStrEnv.constData(); + const char *srvStr = srvStrEnv.isNull() ? nullptr : srvStrEnv.constData(); pa_context_flags_t flags = qEnvironmentVariableIsSet("QT_PULSE_NOAUTOSPAWN") ? PA_CONTEXT_NOAUTOSPAWN : (pa_context_flags_t)0; - if (pa_context_connect(m_context, srvStr, flags, 0) < 0) { + if (pa_context_connect(m_context, srvStr, flags, nullptr) < 0) { qWarning("PulseAudioService: pa_context_connect() failed"); pa_context_unref(m_context); unlock(); pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = 0; - m_context = 0; + m_mainLoop = nullptr; + m_context = nullptr; return; } unlock(); @@ -226,12 +226,12 @@ private: if (m_mainLoop) { pa_threaded_mainloop_stop(m_mainLoop); pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = 0; + m_mainLoop = nullptr; } if (m_context) { pa_context_unref(m_context); - m_context = 0; + m_context = nullptr; } m_prepared = false; @@ -338,7 +338,7 @@ public: qDebug() << "QSoundEffectRef(" << this << ") notifyDeleted"; #endif QMutexLocker locker(&m_mutex); - m_target = NULL; + m_target = nullptr; } private: @@ -349,7 +349,7 @@ private: QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent): QObject(parent), - m_pulseStream(0), + m_pulseStream(nullptr), m_sinkInputId(-1), m_emptying(false), m_sampleReady(false), @@ -362,7 +362,7 @@ QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent): m_loopCount(1), m_runningCount(0), m_reloadCategory(false), - m_sample(0), + m_sample(nullptr), m_position(0), m_resourcesAvailable(false) { @@ -397,7 +397,7 @@ void QSoundEffectPrivate::release() unloadPulseStream(); if (m_sample) { m_sample->release(); - m_sample = 0; + m_sample = nullptr; } this->deleteLater(); @@ -435,7 +435,7 @@ void QSoundEffectPrivate::setCategory(const QString &category) QSoundEffectPrivate::~QSoundEffectPrivate() { QMediaResourcePolicy::destroyResourceSet(m_resources); - m_resources = 0; + m_resources = nullptr; m_ref->release(); pulseDaemon()->deref(); @@ -474,7 +474,7 @@ void QSoundEffectPrivate::setSource(const QUrl &url) disconnect(m_sample, SIGNAL(ready()), this, SLOT(sampleReady())); } m_sample->release(); - m_sample = 0; + m_sample = nullptr; } m_source = url; @@ -482,9 +482,9 @@ void QSoundEffectPrivate::setSource(const QUrl &url) setLoopsRemaining(0); if (m_pulseStream && !pa_stream_is_corked(m_pulseStream)) { - pa_stream_set_write_callback(m_pulseStream, 0, 0); - pa_stream_set_underflow_callback(m_pulseStream, 0, 0); - pa_operation *op = pa_stream_cork(m_pulseStream, 1, 0, 0); + pa_stream_set_write_callback(m_pulseStream, nullptr, nullptr); + pa_stream_set_underflow_callback(m_pulseStream, nullptr, nullptr); + pa_operation *op = pa_stream_cork(m_pulseStream, 1, nullptr, nullptr); if (op) pa_operation_unref(op); else @@ -676,8 +676,8 @@ void QSoundEffectPrivate::emptyStream(EmptyStreamOptions options) PulseDaemonLocker locker; m_emptying = true; - pa_stream_set_write_callback(m_pulseStream, 0, 0); - pa_stream_set_underflow_callback(m_pulseStream, 0, 0); + pa_stream_set_write_callback(m_pulseStream, nullptr, nullptr); + pa_stream_set_underflow_callback(m_pulseStream, nullptr, nullptr); pa_operation *op = pa_stream_flush(m_pulseStream, flushCompleteCb, m_ref->getRef()); if (op) pa_operation_unref(op); @@ -696,7 +696,8 @@ void QSoundEffectPrivate::emptyComplete(void *stream, bool reload) m_emptying = false; if ((pa_stream *)stream == m_pulseStream) { - pa_operation *op = pa_stream_cork(m_pulseStream, 1, reload ? stream_cork_callback : 0, m_ref->getRef()); + pa_operation *op = pa_stream_cork(m_pulseStream, 1, + reload ? stream_cork_callback : nullptr, m_ref->getRef()); if (op) pa_operation_unref(op); else @@ -780,13 +781,13 @@ void QSoundEffectPrivate::unloadPulseStream() m_sinkInputId = -1; PulseDaemonLocker locker; if (m_pulseStream) { - pa_stream_set_state_callback(m_pulseStream, 0, 0); - pa_stream_set_write_callback(m_pulseStream, 0, 0); - pa_stream_set_underflow_callback(m_pulseStream, 0, 0); + pa_stream_set_state_callback(m_pulseStream, nullptr, nullptr); + pa_stream_set_write_callback(m_pulseStream, nullptr, nullptr); + pa_stream_set_underflow_callback(m_pulseStream, nullptr, nullptr); pa_stream_disconnect(m_pulseStream); pa_stream_unref(m_pulseStream); disconnect(pulseDaemon(), SIGNAL(contextFailed()), this, SLOT(contextFailed())); - m_pulseStream = 0; + m_pulseStream = nullptr; m_reloadCategory = false; // category will be reloaded when we connect anyway } } @@ -896,7 +897,7 @@ int QSoundEffectPrivate::writeToStream(const void *data, int size) if (volume < 1.0f) { // Don't use PulseAudio volume, as it might affect all other streams of the same category // or even affect the system volume if flat volumes are enabled - void *dest = NULL; + void *dest = nullptr; size_t nbytes = size; if (pa_stream_begin_write(m_pulseStream, &dest, &nbytes) < 0) { qWarning("QSoundEffect(pulseaudio): pa_stream_begin_write, error = %s", @@ -907,7 +908,7 @@ int QSoundEffectPrivate::writeToStream(const void *data, int size) size = int(nbytes); QAudioHelperInternal::qMultiplySamples(volume, m_sample->format(), data, dest, size); data = dest; - writeDoneCb = NULL; + writeDoneCb = nullptr; } if (pa_stream_write(m_pulseStream, data, size, writeDoneCb, 0, PA_SEEK_RELATIVE) < 0) { @@ -928,7 +929,7 @@ void QSoundEffectPrivate::playSample() #endif Q_ASSERT(m_pulseStream); Q_ASSERT(pa_stream_get_state(m_pulseStream) == PA_STREAM_READY); - pa_operation *o = pa_stream_cork(m_pulseStream, 0, 0, 0); + pa_operation *o = pa_stream_cork(m_pulseStream, 0, nullptr, nullptr); if (o) pa_operation_unref(o); } @@ -999,14 +1000,15 @@ void QSoundEffectPrivate::createPulseStream() pa_proplist *propList = pa_proplist_new(); if (!m_category.isNull()) pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, m_category.toLatin1().constData()); - pa_stream *stream = pa_stream_new_with_proplist(pulseDaemon()->context(), m_name.constData(), &m_pulseSpec, 0, propList); + pa_stream *stream = pa_stream_new_with_proplist(pulseDaemon()->context(), m_name.constData(), + &m_pulseSpec, nullptr, propList); pa_proplist_free(propList); connect(pulseDaemon(), SIGNAL(contextFailed()), this, SLOT(contextFailed())); - if (stream == 0) { + if (stream == nullptr) { qWarning("QSoundEffect(pulseaudio): Failed to create stream"); - m_pulseStream = 0; + m_pulseStream = nullptr; setStatus(QSoundEffect::Error); setPlaying(false); return; @@ -1018,8 +1020,8 @@ void QSoundEffectPrivate::createPulseStream() } m_pulseStream = stream; - if (pa_stream_connect_playback(m_pulseStream, 0, 0, - PA_STREAM_START_CORKED, 0, 0) < 0) { + if (pa_stream_connect_playback(m_pulseStream, nullptr, nullptr, + PA_STREAM_START_CORKED, nullptr, nullptr) < 0) { qWarning("QSoundEffect(pulseaudio): Failed to connect stream, error = %s", pa_strerror(pa_context_errno(pulseDaemon()->context()))); } diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp index 7dcbf934a..bf96f16ae 100644 --- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp +++ b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp @@ -129,7 +129,7 @@ void QSoundEffectPrivate::setSource(const QUrl &url) disconnect(d->m_sample, SIGNAL(ready()), d, SLOT(sampleReady())); } d->m_sample->release(); - d->m_sample = 0; + d->m_sample = nullptr; } setStatus(QSoundEffect::Loading); @@ -308,8 +308,8 @@ PrivateSoundSource::PrivateSoundSource(QSoundEffectPrivate* s): m_runningCount(0), m_playing(false), m_status(QSoundEffect::Null), - m_audioOutput(0), - m_sample(0), + m_audioOutput(nullptr), + m_sample(nullptr), m_muted(false), m_volume(1.0), m_sampleReady(false), diff --git a/src/multimedia/gsttools_headers/qgstreamervideooverlay_p.h b/src/multimedia/gsttools_headers/qgstreamervideooverlay_p.h index f3066fad8..4228f0fd0 100644 --- a/src/multimedia/gsttools_headers/qgstreamervideooverlay_p.h +++ b/src/multimedia/gsttools_headers/qgstreamervideooverlay_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE +class QGstreamerSinkProperties; class QGstreamerVideoOverlay : public QObject , public QGstreamerSyncMessageFilter @@ -106,29 +107,17 @@ Q_SIGNALS: void saturationChanged(int saturation); private: - GstElement *findBestVideoSink() const; void setWindowHandle_helper(WId id); void updateIsActive(); void probeCaps(GstCaps *caps) override; static void showPrerollFrameChanged(GObject *, GParamSpec *, QGstreamerVideoOverlay *); - GstElement *m_videoSink; + GstElement *m_videoSink = nullptr; QSize m_nativeVideoSize; - bool m_isActive; - - bool m_hasForceAspectRatio; - bool m_hasBrightness; - bool m_hasContrast; - bool m_hasHue; - bool m_hasSaturation; - bool m_hasShowPrerollFrame; - - WId m_windowId; - Qt::AspectRatioMode m_aspectRatioMode; - int m_brightness; - int m_contrast; - int m_hue; - int m_saturation; + bool m_isActive = false; + + QGstreamerSinkProperties *m_sinkProperties = nullptr; + WId m_windowId = 0; }; QT_END_NAMESPACE diff --git a/src/multimediawidgets/qpaintervideosurface.cpp b/src/multimediawidgets/qpaintervideosurface.cpp index 0147690e1..0396f9fc0 100644 --- a/src/multimediawidgets/qpaintervideosurface.cpp +++ b/src/multimediawidgets/qpaintervideosurface.cpp @@ -202,12 +202,13 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::paint( if (m_scanLineDirection == QVideoSurfaceFormat::BottomToTop) { transform.scale(1, -1); transform.translate(0, -target.bottom()); - targetRect.setY(0); + targetRect = QRectF(target.x(), 0, target.width(), target.height()); } + if (m_mirrored) { transform.scale(-1, 1); transform.translate(-target.right(), 0); - targetRect.setX(0); + targetRect = QRectF(0, targetRect.y(), target.width(), target.height()); } painter->setTransform(transform); painter->drawImage(targetRect, image, source); diff --git a/src/multimediawidgets/qvideowidget.cpp b/src/multimediawidgets/qvideowidget.cpp index 9a01c73aa..e9977c5a8 100644 --- a/src/multimediawidgets/qvideowidget.cpp +++ b/src/multimediawidgets/qvideowidget.cpp @@ -76,7 +76,9 @@ QVideoWidgetControlBackend::QVideoWidgetControlBackend( layout->setMargin(0); layout->setSpacing(0); - layout->addWidget(control->videoWidget()); + QWidget *videoWidget = control->videoWidget(); + videoWidget->setMouseTracking(widget->hasMouseTracking()); + layout->addWidget(videoWidget); widget->setLayout(layout); } @@ -321,6 +323,9 @@ QWindowVideoWidgetBackend::QWindowVideoWidgetBackend( connect(control, SIGNAL(nativeSizeChanged()), m_widget, SLOT(_q_dimensionsChanged())); control->setWinId(widget->winId()); +#if defined(Q_OS_WIN) + m_widget->setUpdatesEnabled(false); +#endif } QWindowVideoWidgetBackend::~QWindowVideoWidgetBackend() @@ -390,16 +395,13 @@ void QWindowVideoWidgetBackend::showEvent() m_windowControl->setWinId(m_widget->winId()); updateDisplayRect(); -#if defined(Q_WS_WIN) - m_widget->setUpdatesEnabled(false); +#if defined(Q_OS_WIN) + m_windowControl->repaint(); #endif } void QWindowVideoWidgetBackend::hideEvent(QHideEvent *) { -#if defined(Q_WS_WIN) - m_widget->setUpdatesEnabled(true); -#endif } void QWindowVideoWidgetBackend::moveEvent(QMoveEvent *) @@ -425,16 +427,6 @@ void QWindowVideoWidgetBackend::paintEvent(QPaintEvent *event) event->accept(); } -#if defined(Q_WS_WIN) -bool QWindowVideoWidgetBackend::winEvent(MSG *message, long *) -{ - if (message->message == WM_PAINT) - m_windowControl->repaint(); - - return false; -} -#endif - void QVideoWidgetPrivate::setCurrentControl(QVideoWidgetControlInterface *control) { if (currentControl != control) { @@ -1009,20 +1001,6 @@ void QVideoWidget::paintEvent(QPaintEvent *event) } } - -#if defined(Q_WS_WIN) -/*! - \internal -*/ -bool QVideoWidget::winEvent(MSG *message, long *result) -{ - return d_func()->windowBackend && d_func()->windowBackend->winEvent(message, result) - ? true - : QWidget::winEvent(message, result); -} -#endif - - #include "moc_qvideowidget.cpp" #include "moc_qvideowidget_p.cpp" QT_END_NAMESPACE diff --git a/src/multimediawidgets/qvideowidget.h b/src/multimediawidgets/qvideowidget.h index 00b160821..b1e2da46b 100644 --- a/src/multimediawidgets/qvideowidget.h +++ b/src/multimediawidgets/qvideowidget.h @@ -107,10 +107,6 @@ protected: bool setMediaObject(QMediaObject *object) override; -#if defined(Q_WS_WIN) - bool winEvent(MSG *message, long *result); -#endif - QVideoWidget(QVideoWidgetPrivate &dd, QWidget *parent); QVideoWidgetPrivate *d_ptr; diff --git a/src/multimediawidgets/qvideowidget_p.h b/src/multimediawidgets/qvideowidget_p.h index 48b08e093..ef417b222 100644 --- a/src/multimediawidgets/qvideowidget_p.h +++ b/src/multimediawidgets/qvideowidget_p.h @@ -206,10 +206,6 @@ public: void moveEvent(QMoveEvent *event) override; void paintEvent(QPaintEvent *event) override; -#if defined(Q_WS_WIN) - bool winEvent(MSG *message, long *result); -#endif - private: void updateDisplayRect(); diff --git a/src/plugins/audiocapture/audioencodercontrol.cpp b/src/plugins/audiocapture/audioencodercontrol.cpp index d8a83bf26..6fc519cef 100644 --- a/src/plugins/audiocapture/audioencodercontrol.cpp +++ b/src/plugins/audiocapture/audioencodercontrol.cpp @@ -54,13 +54,12 @@ static QAudioFormat audioSettingsToAudioFormat(const QAudioEncoderSettings &sett fmt.setCodec(settings.codec()); fmt.setChannelCount(settings.channelCount()); fmt.setSampleRate(settings.sampleRate()); - if (settings.sampleRate() == 8000 && settings.bitRate() == 8000) { - fmt.setSampleType(QAudioFormat::UnSignedInt); - fmt.setSampleSize(8); - } else { - fmt.setSampleSize(16); - fmt.setSampleType(QAudioFormat::SignedInt); - } + int sampleSize = 16; + if (settings.bitRate() && settings.channelCount() && settings.sampleRate()) + sampleSize = settings.bitRate() / settings.channelCount() / settings.sampleRate(); + fmt.setSampleSize(sampleSize); + fmt.setSampleType(sampleSize == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt); + fmt.setByteOrder(QAudioDeviceInfo::defaultInputDevice().preferredFormat().byteOrder()); return fmt; } diff --git a/src/plugins/directshow/camera/dscameracontrol.cpp b/src/plugins/directshow/camera/dscameracontrol.cpp index a6824976d..8d923da9f 100644 --- a/src/plugins/directshow/camera/dscameracontrol.cpp +++ b/src/plugins/directshow/camera/dscameracontrol.cpp @@ -53,6 +53,8 @@ DSCameraControl::DSCameraControl(QObject *parent) m_session = qobject_cast<DSCameraSession*>(parent); connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SIGNAL(statusChanged(QCamera::Status))); + connect(m_session, &DSCameraSession::cameraError, + this, &DSCameraControl::error); } DSCameraControl::~DSCameraControl() diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp index 9b642872a..ca3c47cb8 100644 --- a/src/plugins/directshow/camera/dscamerasession.cpp +++ b/src/plugins/directshow/camera/dscamerasession.cpp @@ -441,30 +441,33 @@ bool DSCameraSession::startPreview() setStatus(QCamera::StartingStatus); + QString errorString; HRESULT hr = S_OK; IMediaControl* pControl = 0; if (!configurePreviewFormat()) { - qWarning() << "Failed to configure preview format"; + errorString = tr("Failed to configure preview format"); goto failed; } - if (!connectGraph()) + if (!connectGraph()) { + errorString = tr("Failed to connect graph"); goto failed; + } if (m_surface) m_surface->start(m_previewSurfaceFormat); hr = m_filterGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); if (FAILED(hr)) { - qWarning() << "failed to get stream control"; + errorString = tr("Failed to get stream control"); goto failed; } hr = pControl->Run(); pControl->Release(); if (FAILED(hr)) { - qWarning() << "failed to start"; + errorString = tr("Failed to start"); goto failed; } @@ -477,7 +480,7 @@ failed: if (m_surface && m_surface->isActive()) m_surface->stop(); disconnectGraph(); - setStatus(QCamera::LoadedStatus); + setError(QCamera::CameraError, errorString); return false; } @@ -491,17 +494,18 @@ bool DSCameraSession::stopPreview() if (m_previewSampleGrabber) m_previewSampleGrabber->stop(); + QString errorString; IMediaControl* pControl = 0; HRESULT hr = m_filterGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); if (FAILED(hr)) { - qWarning() << "failed to get stream control"; + errorString = tr("Failed to get stream control"); goto failed; } hr = pControl->Stop(); pControl->Release(); if (FAILED(hr)) { - qWarning() << "failed to stop"; + errorString = tr("Failed to stop"); goto failed; } @@ -514,10 +518,16 @@ bool DSCameraSession::stopPreview() return true; failed: - setStatus(QCamera::ActiveStatus); + setError(QCamera::CameraError, errorString); return false; } +void DSCameraSession::setError(int error, const QString &errorString) +{ + emit cameraError(error, errorString); + setStatus(QCamera::UnloadedStatus); +} + void DSCameraSession::setStatus(QCamera::Status status) { if (m_status == status) @@ -668,6 +678,7 @@ bool DSCameraSession::createFilterGraph() // Previously containered in <qedit.h>. static const CLSID cLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + QString errorString; HRESULT hr; IMoniker* pMoniker = NULL; ICreateDevEnum* pDevEnum = NULL; @@ -677,7 +688,7 @@ bool DSCameraSession::createFilterGraph() hr = CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC, IID_IGraphBuilder, (void**)&m_filterGraph); if (FAILED(hr)) { - qWarning() << "failed to create filter graph"; + errorString = tr("Failed to create filter graph"); goto failed; } @@ -685,14 +696,14 @@ bool DSCameraSession::createFilterGraph() hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void**)&m_graphBuilder); if (FAILED(hr)) { - qWarning() << "failed to create graph builder"; + errorString = tr("Failed to create graph builder"); goto failed; } // Attach the filter graph to the capture graph hr = m_graphBuilder->SetFiltergraph(m_filterGraph); if (FAILED(hr)) { - qWarning() << "failed to connect capture graph and filter graph"; + errorString = tr("Failed to connect capture graph and filter graph"); goto failed; } @@ -762,7 +773,7 @@ bool DSCameraSession::createFilterGraph() } if (!m_sourceFilter) { - qWarning() << "No capture device found"; + errorString = tr("No capture device found"); goto failed; } @@ -779,7 +790,7 @@ bool DSCameraSession::createFilterGraph() hr = CoCreateInstance(cLSID_NullRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&m_nullRendererFilter); if (FAILED(hr)) { - qWarning() << "failed to create null renderer"; + errorString = tr("Failed to create null renderer"); goto failed; } @@ -793,6 +804,7 @@ failed: SAFE_RELEASE(m_nullRendererFilter); SAFE_RELEASE(m_filterGraph); SAFE_RELEASE(m_graphBuilder); + setError(QCamera::CameraError, errorString); return false; } diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h index e28015534..07c3d9ef9 100644 --- a/src/plugins/directshow/camera/dscamerasession.h +++ b/src/plugins/directshow/camera/dscamerasession.h @@ -138,6 +138,7 @@ Q_SIGNALS: void readyForCaptureChanged(bool); void captureError(int id, int error, const QString &errorString); void captureDestinationChanged(QCameraImageCapture::CaptureDestinations); + void cameraError(int error, const QString &errorString); private Q_SLOTS: void presentFrame(); @@ -176,6 +177,7 @@ private: void updateSourceCapabilities(); bool configurePreviewFormat(); void updateImageProcessingParametersInfos(); + void setError(int error, const QString &errorString); // These static functions are used for scaling of adjustable parameters, // which have the ranges from -1.0 to +1.0 in the QCameraImageProcessing API. diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 8ee5d67a1..63f738bbe 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -1071,8 +1071,11 @@ void DirectShowPlayerService::doSetRate(QMutexLocker *locker) locker->relock(); if (!SUCCEEDED(hr)) { + qWarning("%s: Audio device or filter does not support rate: %.2f. " \ + "Falling back to previous value.", __FUNCTION__, m_rate); + double rate = 0.0; - m_rate = seeking->GetRate(&rate) + m_rate = SUCCEEDED(seeking->GetRate(&rate)) ? rate : 1.0; } diff --git a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp index 2ccc1b0bc..89be2ac5d 100644 --- a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp @@ -102,8 +102,6 @@ QCamera::LockStatus CameraBinLocks::lockStatus(QCamera::LockType lock) const default: return QCamera::Unlocked; } - - return lock == QCamera::LockFocus ? m_focus->focusStatus() : QCamera::Unlocked; } void CameraBinLocks::searchAndLock(QCamera::LockTypes locks) diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index 39f97e989..3bb6ebffb 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -767,7 +767,11 @@ void CameraBinSession::setStateHelper(QCamera::State state) void CameraBinSession::setError(int err, const QString &errorString) { - m_pendingState = QCamera::UnloadedState; + // Emit only first error + if (m_pendingState == QCamera::UnloadedState) + return; + + setState(QCamera::UnloadedState); emit error(err, errorString); setStatus(QCamera::UnloadedStatus); } @@ -990,10 +994,14 @@ bool CameraBinSession::processBusMessage(const QGstreamerMessage &message) if (err && err->message) { message = QString::fromUtf8(err->message); qWarning() << "CameraBin error:" << message; +#if CAMERABIN_DEBUG + qWarning() << QString::fromUtf8(debug); +#endif } - //only report error messager from camerabin - if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_camerabin)) { + // Only report error messages from camerabin or video source + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_camerabin) + || GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSrc)) { if (message.isEmpty()) message = tr("Camera error"); diff --git a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp index 5035cb6b1..b568f38fc 100644 --- a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp +++ b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp @@ -76,6 +76,7 @@ private slots: void testCtorWithPosition(); void testCameraStates(); + void testCameraStartError(); void testCaptureMode(); void testCameraCapture(); void testCaptureToBuffer(); @@ -251,6 +252,27 @@ void tst_QCameraBackend::testCameraStates() QCOMPARE(errorSignal.count(), 0); } +void tst_QCameraBackend::testCameraStartError() +{ + QCamera camera1(QCameraInfo::defaultCamera()); + QCamera camera2(QCameraInfo::defaultCamera()); + QSignalSpy errorSpy1(&camera1, QOverload<QCamera::Error>::of(&QCamera::error)); + QSignalSpy errorSpy2(&camera2, QOverload<QCamera::Error>::of(&QCamera::error)); + + camera1.start(); + camera2.start(); + + QCOMPARE(camera1.state(), QCamera::ActiveState); + QTRY_COMPARE(camera1.status(), QCamera::ActiveStatus); + QCOMPARE(camera1.error(), QCamera::NoError); + QCOMPARE(camera2.state(), QCamera::UnloadedState); + QCOMPARE(camera2.status(), QCamera::UnloadedStatus); + QCOMPARE(camera2.error(), QCamera::CameraError); + + QCOMPARE(errorSpy1.count(), 0); + QCOMPARE(errorSpy2.count(), 1); +} + void tst_QCameraBackend::testCaptureMode() { QCamera camera; diff --git a/tests/auto/integration/qsoundeffect/test24.wav b/tests/auto/integration/qsoundeffect/test24.wav Binary files differnew file mode 100644 index 000000000..9575aaaee --- /dev/null +++ b/tests/auto/integration/qsoundeffect/test24.wav diff --git a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp index 5be889096..fa98c8b16 100644 --- a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp +++ b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp @@ -62,12 +62,14 @@ private slots: void testSetSourceWhilePlaying(); void testSupportedMimeTypes(); void testCorruptFile(); + void testPlaying24Bits(); private: QSoundEffect* sound; QUrl url; // test.wav: pcm_s16le, 48000 Hz, stereo, s16 QUrl url2; // test_tone.wav: pcm_s16le, 44100 Hz, mono QUrl urlCorrupted; // test_corrupted.wav: corrupted + QUrl url24Bits; // test24.wav pcm_s24le, 44100 Hz, mono }; void tst_QSoundEffect::init() @@ -105,6 +107,11 @@ void tst_QSoundEffect::initTestCase() QVERIFY2(!fullPath.isEmpty(), qPrintable(QStringLiteral("Unable to locate ") + testFileName)); urlCorrupted = QUrl::fromLocalFile(fullPath); + testFileName = QStringLiteral("test24.wav"); + fullPath = QFINDTESTDATA(testFileName); + QVERIFY2(!fullPath.isEmpty(), qPrintable(QStringLiteral("Unable to locate ") + testFileName)); + url24Bits = QUrl::fromLocalFile(fullPath); + sound = new QSoundEffect(this); QVERIFY(sound->source().isEmpty()); @@ -409,6 +416,26 @@ void tst_QSoundEffect::testCorruptFile() } } +void tst_QSoundEffect::testPlaying24Bits() +{ + sound->setLoopCount(QSoundEffect::Infinite); + sound->setSource(url24Bits); + QTestEventLoop::instance().enterLoop(1); + sound->play(); + QTestEventLoop::instance().enterLoop(1); + QTRY_COMPARE(sound->isPlaying(), true); + sound->stop(); + + QSignalSpy readSignal(sound, SIGNAL(volumeChanged())); + sound->setVolume(0.5); + QCOMPARE(sound->volume(), 0.5); + sound->play(); + QTestEventLoop::instance().enterLoop(1); + QTRY_COMPARE(sound->isPlaying(), true); + QCOMPARE(readSignal.count(), 1); + sound->stop(); +} + QTEST_MAIN(tst_QSoundEffect) #include "tst_qsoundeffect.moc" |