summaryrefslogtreecommitdiffstats
path: root/src/multimedia/windows
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2022-04-07 15:15:58 +0200
committerLars Knoll <lars.knoll@qt.io>2022-04-29 07:16:05 +0200
commit75658f288f80513e8b81fab077eed082667f6216 (patch)
tree897a1c924336118946d7f5052a521499a5e97a64 /src/multimedia/windows
parentf0282b86889faa3ac4c2531e2c09abd10d4c87a2 (diff)
Move the low level audio integration back into Qt Multimedia
Those integrations are selected at compile time and not dependent on native vs ffmpeg backend. Because of that we can move them back into Qt Multimedia directly. This will simplify and improve a couple of things: * We don't need to build the audio code twice (once per plugins) * Low level audio is always available even if you choose not to ship a large backend for audio/video decoding and recording * We can use Qt Multimedia as a backend for other modules requiring audio only without bloating things there (e.g. for Qt Speech) * We can directly integrate support for 3D audio and headphone spatialization into Qt Multimedia without requiring an additional module for that. To do so, we need to remove the camera handling from QPlatformMediaDevices. This is only partially done in this commit (by adding a QPlatformVideoDevices class), and will be further cleaned up in followup commits. Change-Id: Ib4a33d7255faaf26dd825527786eae10ed283490 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/multimedia/windows')
-rw-r--r--src/multimedia/windows/qwindowsaudiodevice.cpp233
-rw-r--r--src/multimedia/windows/qwindowsaudiodevice_p.h96
-rw-r--r--src/multimedia/windows/qwindowsaudiosink.cpp436
-rw-r--r--src/multimedia/windows/qwindowsaudiosink_p.h145
-rw-r--r--src/multimedia/windows/qwindowsaudiosource.cpp698
-rw-r--r--src/multimedia/windows/qwindowsaudiosource_p.h170
-rw-r--r--src/multimedia/windows/qwindowsaudioutils.cpp157
-rw-r--r--src/multimedia/windows/qwindowsaudioutils_p.h76
-rw-r--r--src/multimedia/windows/qwindowsiupointer_p.h93
-rw-r--r--src/multimedia/windows/qwindowsmediadevices.cpp532
-rw-r--r--src/multimedia/windows/qwindowsmediadevices_p.h96
-rw-r--r--src/multimedia/windows/qwindowsmfdefs.cpp62
-rw-r--r--src/multimedia/windows/qwindowsmfdefs_p.h128
-rw-r--r--src/multimedia/windows/qwindowsmultimediautils.cpp240
-rw-r--r--src/multimedia/windows/qwindowsmultimediautils_p.h80
-rw-r--r--src/multimedia/windows/qwindowsresampler.cpp273
-rw-r--r--src/multimedia/windows/qwindowsresampler_p.h104
17 files changed, 3619 insertions, 0 deletions
diff --git a/src/multimedia/windows/qwindowsaudiodevice.cpp b/src/multimedia/windows/qwindowsaudiodevice.cpp
new file mode 100644
index 000000000..1942dd8a6
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudiodevice.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+
+#include <QtCore/qt_windows.h>
+#include <QtCore/QDataStream>
+#include <QtCore/QIODevice>
+#include <utility>
+#include <mmsystem.h>
+#include <mmdeviceapi.h>
+#include "qwindowsaudiodevice_p.h"
+#include "qwindowsaudioutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, QWindowsIUPointer<IMMDevice> immdev, int waveID, const QString &description, QAudioDevice::Mode mode)
+ : QAudioDevicePrivate(dev, mode),
+ devId(waveID),
+ immdev(std::move(immdev))
+{
+ this->description = description;
+ preferredFormat.setSampleRate(44100);
+ preferredFormat.setChannelCount(2);
+ preferredFormat.setSampleFormat(QAudioFormat::Int16);
+
+ DWORD fmt = 0;
+
+ if(mode == QAudioDevice::Output) {
+ WAVEOUTCAPS woc;
+ if (waveOutGetDevCaps(devId, &woc, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
+ fmt = woc.dwFormats;
+ } else {
+ WAVEINCAPS woc;
+ if (waveInGetDevCaps(devId, &woc, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
+ fmt = woc.dwFormats;
+ }
+
+ if (!fmt)
+ return;
+
+ // Check sample size
+ if ((fmt & WAVE_FORMAT_1M08)
+ || (fmt & WAVE_FORMAT_1S08)
+ || (fmt & WAVE_FORMAT_2M08)
+ || (fmt & WAVE_FORMAT_2S08)
+ || (fmt & WAVE_FORMAT_4M08)
+ || (fmt & WAVE_FORMAT_4S08)
+ || (fmt & WAVE_FORMAT_48M08)
+ || (fmt & WAVE_FORMAT_48S08)
+ || (fmt & WAVE_FORMAT_96M08)
+ || (fmt & WAVE_FORMAT_96S08)) {
+ supportedSampleFormats.append(QAudioFormat::UInt8);
+ }
+ if ((fmt & WAVE_FORMAT_1M16)
+ || (fmt & WAVE_FORMAT_1S16)
+ || (fmt & WAVE_FORMAT_2M16)
+ || (fmt & WAVE_FORMAT_2S16)
+ || (fmt & WAVE_FORMAT_4M16)
+ || (fmt & WAVE_FORMAT_4S16)
+ || (fmt & WAVE_FORMAT_48M16)
+ || (fmt & WAVE_FORMAT_48S16)
+ || (fmt & WAVE_FORMAT_96M16)
+ || (fmt & WAVE_FORMAT_96S16)) {
+ supportedSampleFormats.append(QAudioFormat::Int16);
+ }
+
+ minimumSampleRate = std::numeric_limits<int>::max();
+ maximumSampleRate = 0;
+ // Check sample rate
+ if ((fmt & WAVE_FORMAT_1M08)
+ || (fmt & WAVE_FORMAT_1S08)
+ || (fmt & WAVE_FORMAT_1M16)
+ || (fmt & WAVE_FORMAT_1S16)) {
+ minimumSampleRate = qMin(minimumSampleRate, 11025);
+ maximumSampleRate = qMax(maximumSampleRate, 11025);
+ }
+ if ((fmt & WAVE_FORMAT_2M08)
+ || (fmt & WAVE_FORMAT_2S08)
+ || (fmt & WAVE_FORMAT_2M16)
+ || (fmt & WAVE_FORMAT_2S16)) {
+ minimumSampleRate = qMin(minimumSampleRate, 22050);
+ maximumSampleRate = qMax(maximumSampleRate, 22050);
+ }
+ if ((fmt & WAVE_FORMAT_4M08)
+ || (fmt & WAVE_FORMAT_4S08)
+ || (fmt & WAVE_FORMAT_4M16)
+ || (fmt & WAVE_FORMAT_4S16)) {
+ minimumSampleRate = qMin(minimumSampleRate, 44100);
+ maximumSampleRate = qMax(maximumSampleRate, 44100);
+ }
+ if ((fmt & WAVE_FORMAT_48M08)
+ || (fmt & WAVE_FORMAT_48S08)
+ || (fmt & WAVE_FORMAT_48M16)
+ || (fmt & WAVE_FORMAT_48S16)) {
+ minimumSampleRate = qMin(minimumSampleRate, 48000);
+ maximumSampleRate = qMax(maximumSampleRate, 48000);
+ }
+ if ((fmt & WAVE_FORMAT_96M08)
+ || (fmt & WAVE_FORMAT_96S08)
+ || (fmt & WAVE_FORMAT_96M16)
+ || (fmt & WAVE_FORMAT_96S16)) {
+ minimumSampleRate = qMin(minimumSampleRate, 96000);
+ maximumSampleRate = qMax(maximumSampleRate, 96000);
+ }
+ if (minimumSampleRate == std::numeric_limits<int>::max())
+ minimumSampleRate = 0;
+
+ minimumChannelCount = std::numeric_limits<int>::max();
+ maximumChannelCount = 0;
+ // Check channel count
+ if (fmt & WAVE_FORMAT_1M08
+ || fmt & WAVE_FORMAT_1M16
+ || fmt & WAVE_FORMAT_2M08
+ || fmt & WAVE_FORMAT_2M16
+ || fmt & WAVE_FORMAT_4M08
+ || fmt & WAVE_FORMAT_4M16
+ || fmt & WAVE_FORMAT_48M08
+ || fmt & WAVE_FORMAT_48M16
+ || fmt & WAVE_FORMAT_96M08
+ || fmt & WAVE_FORMAT_96M16) {
+ minimumChannelCount = 1;
+ maximumChannelCount = 1;
+ }
+ if (fmt & WAVE_FORMAT_1S08
+ || fmt & WAVE_FORMAT_1S16
+ || fmt & WAVE_FORMAT_2S08
+ || fmt & WAVE_FORMAT_2S16
+ || fmt & WAVE_FORMAT_4S08
+ || fmt & WAVE_FORMAT_4S16
+ || fmt & WAVE_FORMAT_48S08
+ || fmt & WAVE_FORMAT_48S16
+ || fmt & WAVE_FORMAT_96S08
+ || fmt & WAVE_FORMAT_96S16) {
+ minimumChannelCount = qMin(minimumChannelCount, 2);
+ maximumChannelCount = qMax(maximumChannelCount, 2);
+ }
+
+ if (minimumChannelCount == std::numeric_limits<int>::max())
+ minimumChannelCount = 0;
+
+ // WAVEOUTCAPS and WAVEINCAPS contains information only for the previously tested parameters.
+ // WaveOut and WaveInt might actually support more formats, the only way to know is to try
+ // opening the device with it.
+ QAudioFormat testFormat;
+ testFormat.setChannelCount(maximumChannelCount);
+ testFormat.setSampleRate(maximumSampleRate);
+ const QAudioFormat defaultTestFormat(testFormat);
+
+ // Check if float samples are supported
+ testFormat.setSampleFormat(QAudioFormat::Float);
+ if (testSettings(testFormat))
+ supportedSampleFormats.append(QAudioFormat::Float);
+
+ // Check channel counts > 2
+ testFormat = defaultTestFormat;
+ for (int i = 18; i > 2; --i) { // <mmreg.h> defines 18 different channels
+ testFormat.setChannelCount(i);
+ if (testSettings(testFormat)) {
+ maximumChannelCount = i;
+ break;
+ }
+ }
+}
+
+QWindowsAudioDeviceInfo::~QWindowsAudioDeviceInfo()
+{
+}
+
+bool QWindowsAudioDeviceInfo::testSettings(const QAudioFormat& format) const
+{
+ WAVEFORMATEXTENSIBLE wfx;
+ if (QWindowsAudioUtils::formatToWaveFormatExtensible(format, wfx)) {
+ // query only, do not open device
+ if (mode == QAudioDevice::Output) {
+ return (waveOutOpen(NULL, UINT_PTR(devId), &wfx.Format, 0, 0,
+ WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR);
+ } else { // AudioInput
+ return (waveInOpen(NULL, UINT_PTR(devId), &wfx.Format, 0, 0,
+ WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR);
+ }
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsaudiodevice_p.h b/src/multimedia/windows/qwindowsaudiodevice_p.h
new file mode 100644
index 000000000..990cbc46f
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudiodevice_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#ifndef QWINDOWSAUDIODEVICEINFO_H
+#define QWINDOWSAUDIODEVICEINFO_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+
+#include <QtMultimedia/qaudiodevice.h>
+#include <private/qaudiosystem_p.h>
+#include <private/qaudiodevice_p.h>
+#include <qwindowsiupointer_p.h>
+
+struct IMMDevice;
+
+QT_BEGIN_NAMESPACE
+
+const unsigned int MAX_SAMPLE_RATES = 5;
+const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 };
+
+class QWindowsAudioDeviceInfo : public QAudioDevicePrivate
+{
+public:
+ QWindowsAudioDeviceInfo(QByteArray dev, QWindowsIUPointer<IMMDevice> immdev, int waveID, const QString &description, QAudioDevice::Mode mode);
+ ~QWindowsAudioDeviceInfo();
+
+ bool open();
+ void close();
+
+ bool testSettings(const QAudioFormat& format) const;
+
+ int waveId() const { return devId; }
+ QWindowsIUPointer<IMMDevice> immDev() const { return immdev; }
+
+private:
+ quint32 devId;
+ QWindowsIUPointer<IMMDevice> immdev;
+};
+
+
+
+QT_END_NAMESPACE
+
+
+#endif // QWINDOWSAUDIODEVICEINFO_H
diff --git a/src/multimedia/windows/qwindowsaudiosink.cpp b/src/multimedia/windows/qwindowsaudiosink.cpp
new file mode 100644
index 000000000..5d41e6ed6
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudiosink.cpp
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+#include "qwindowsaudiosink_p.h"
+#include "qwindowsaudiodevice_p.h"
+#include "qwindowsaudioutils_p.h"
+#include <QtEndian>
+#include <QtCore/QDataStream>
+#include <QtCore/qtimer.h>
+
+#include <private/qaudiohelpers_p.h>
+
+#include <qloggingcategory.h>
+
+#include <system_error>
+#include <audioclient.h>
+#include <mmdeviceapi.h>
+
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcAudioOutput, "qt.multimedia.audiooutput")
+
+const IID IID_IAudioClient = __uuidof(IAudioClient);
+const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
+
+class OutputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ OutputPrivate(QWindowsAudioSink& audio) : audioDevice(audio) {}
+ ~OutputPrivate() override = default;
+
+ qint64 readData(char *, qint64) override { return 0; }
+ qint64 writeData(const char *data, qint64 len) override { return audioDevice.push(data, len); }
+
+private:
+ QWindowsAudioSink &audioDevice;
+};
+
+std::optional<quint32> audioClientFramesInUse(IAudioClient *client)
+{
+ Q_ASSERT(client);
+ UINT32 framesPadding = 0;
+ if (SUCCEEDED(client->GetCurrentPadding(&framesPadding)))
+ return framesPadding;
+ return {};
+}
+
+std::optional<quint32> audioClientFramesAllocated(IAudioClient *client)
+{
+ Q_ASSERT(client);
+ UINT32 bufferFrameCount = 0;
+ if (SUCCEEDED(client->GetBufferSize(&bufferFrameCount)))
+ return bufferFrameCount;
+ return {};
+}
+
+std::optional<quint32> audioClientFramesAvailable(IAudioClient *client)
+{
+ auto framesAllocated = audioClientFramesAllocated(client);
+ auto framesInUse = audioClientFramesInUse(client);
+
+ if (framesAllocated && framesInUse)
+ return *framesAllocated - *framesInUse;
+ return {};
+}
+
+QWindowsAudioSink::QWindowsAudioSink(QWindowsIUPointer<IMMDevice> device) :
+ m_pushSource(new OutputPrivate(*this)),
+ m_device(std::move(device))
+{
+ m_pushSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
+ m_timer.setSingleShot(true);
+ m_timer.setTimerType(Qt::PreciseTimer);
+}
+
+QWindowsAudioSink::~QWindowsAudioSink()
+{
+ close();
+}
+
+qint64 QWindowsAudioSink::remainingPlayTimeUs()
+{
+ auto framesInUse = audioClientFramesInUse(m_audioClient.get());
+ return m_resampler.outputFormat().durationForFrames(framesInUse ? *framesInUse : 0);
+}
+
+void QWindowsAudioSink::deviceStateChange(QAudio::State state, QAudio::Error error)
+{
+ if (state != deviceState) {
+ if (state == QAudio::ActiveState) {
+ m_audioClient->Start();
+ qCDebug(qLcAudioOutput) << "Audio client started";
+
+ } else if (deviceState == QAudio::ActiveState) {
+ m_timer.stop();
+ m_audioClient->Stop();
+ qCDebug(qLcAudioOutput) << "Audio client stopped";
+ }
+
+ deviceState = state;
+ emit stateChanged(deviceState);
+ }
+
+ if (error != errorState) {
+ errorState = error;
+ emit errorChanged(error);
+ }
+}
+
+QAudioFormat QWindowsAudioSink::format() const
+{
+ return m_format;
+}
+
+void QWindowsAudioSink::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ m_format = fmt;
+}
+
+void QWindowsAudioSink::pullSource()
+{
+ qCDebug(qLcAudioOutput) << "Pull source";
+ if (!m_pullSource)
+ return;
+
+ auto bytesAvailable = m_pullSource->isOpen() ? qsizetype(m_pullSource->bytesAvailable()) : 0;
+ auto readLen = qMin(bytesFree(), bytesAvailable);
+ if (readLen > 0) {
+ QByteArray samples = m_pullSource->read(readLen);
+ if (samples.size() == 0) {
+ deviceStateChange(QAudio::IdleState, QAudio::IOError);
+ return;
+ } else {
+ write(samples.data(), samples.size());
+ }
+ }
+
+ auto playTimeUs = remainingPlayTimeUs();
+ if (playTimeUs == 0) {
+ deviceStateChange(QAudio::IdleState, m_pullSource->atEnd() ? QAudio::NoError : QAudio::UnderrunError);
+ } else {
+ deviceStateChange(QAudio::ActiveState, QAudio::NoError);
+ m_timer.start(playTimeUs / 2000);
+ }
+}
+
+void QWindowsAudioSink::start(QIODevice* device)
+{
+ qCDebug(qLcAudioOutput) << "start(ioDevice)" << deviceState;
+ if (deviceState != QAudio::StoppedState)
+ close();
+
+ if (!open()) {
+ errorState = QAudio::OpenError;
+ emit errorChanged(QAudio::OpenError);
+ return;
+ }
+
+ m_pullSource = device;
+
+ connect(device, &QIODevice::readyRead, this, &QWindowsAudioSink::pullSource);
+ m_timer.disconnect();
+ m_timer.callOnTimeout(this, &QWindowsAudioSink::pullSource);
+ m_timer.start(0);
+}
+
+qint64 QWindowsAudioSink::push(const char *data, qint64 len)
+{
+ if (deviceState == QAudio::StoppedState)
+ return -1;
+
+ qint64 written = write(data, len);
+ if (written > 0) {
+ deviceStateChange(QAudio::ActiveState, QAudio::NoError);
+ m_timer.start(remainingPlayTimeUs() /1000);
+ }
+
+ return written;
+}
+
+QIODevice* QWindowsAudioSink::start()
+{
+ qCDebug(qLcAudioOutput) << "start()";
+ if (deviceState != QAudio::StoppedState)
+ close();
+
+ if (!open()) {
+ errorState = QAudio::OpenError;
+ emit errorChanged(QAudio::OpenError);
+ return nullptr;
+ }
+
+ deviceStateChange(QAudio::IdleState, QAudio::NoError);
+
+ m_timer.disconnect();
+ m_timer.callOnTimeout([&](){
+ deviceStateChange(QAudio::IdleState, QAudio::UnderrunError);
+ });
+
+ return m_pushSource.get();
+}
+
+bool QWindowsAudioSink::open()
+{
+ HRESULT hr = m_device->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER,
+ nullptr, (void**)m_audioClient.address());
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioOutput) << "Failed to activate audio device";
+ return false;
+ }
+
+ WAVEFORMATEX *pwfx = nullptr;
+ hr = m_audioClient->GetMixFormat(&pwfx);
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioOutput) << "Format unsupported" << hr;
+ return false;
+ }
+
+ if (!m_resampler.setup(m_format, QWindowsAudioUtils::waveFormatExToFormat(*pwfx))) {
+ qCWarning(qLcAudioOutput) << "Failed to setup resampler";
+ CoTaskMemFree(pwfx);
+ return false;
+ }
+
+ if (m_bufferSize == 0)
+ m_bufferSize = m_format.sampleRate() * m_format.bytesPerFrame() / 2;
+
+ REFERENCE_TIME requestedDuration = m_format.durationForBytes(m_bufferSize) * 10;
+
+ hr = m_audioClient->Initialize(
+ AUDCLNT_SHAREMODE_SHARED,
+ 0,
+ requestedDuration,
+ 0,
+ pwfx,
+ nullptr);
+
+ CoTaskMemFree(pwfx);
+
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioOutput) << "Failed to initialize audio client" << hr;
+ return false;
+ }
+
+ auto framesAllocated = audioClientFramesAllocated(m_audioClient.get());
+ if (!framesAllocated) {
+ qCWarning(qLcAudioOutput) << "Failed to get audio client buffer size";
+ return false;
+ }
+
+ m_bufferSize = m_format.bytesForDuration(
+ m_resampler.outputFormat().durationForFrames(*framesAllocated));
+
+ hr = m_audioClient->GetService(IID_IAudioRenderClient, (void**)m_renderClient.address());
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioOutput) << "Failed to obtain audio client rendering service" << hr;
+ return false;
+ }
+
+ return true;
+}
+
+void QWindowsAudioSink::close()
+{
+ qCDebug(qLcAudioOutput) << "close()";
+ if (deviceState == QAudio::StoppedState)
+ return;
+
+ deviceStateChange(QAudio::StoppedState, QAudio::NoError);
+
+ if (m_pullSource)
+ disconnect(m_pullSource, &QIODevice::readyRead, this, &QWindowsAudioSink::pullSource);
+ m_audioClient.reset();
+ m_renderClient.reset();
+ m_pullSource = nullptr;
+}
+
+qsizetype QWindowsAudioSink::bytesFree() const
+{
+ if (!m_audioClient)
+ return 0;
+
+ auto framesAvailable = audioClientFramesAvailable(m_audioClient.get());
+ if (framesAvailable)
+ return m_resampler.inputBufferSize(*framesAvailable * m_resampler.outputFormat().bytesPerFrame());
+ return 0;
+}
+
+void QWindowsAudioSink::setBufferSize(qsizetype value)
+{
+ if (!m_audioClient)
+ m_bufferSize = value;
+}
+
+qint64 QWindowsAudioSink::processedUSecs() const
+{
+ if (!m_audioClient)
+ return 0;
+
+ return m_format.durationForBytes(m_resampler.totalInputBytes());
+}
+
+qint64 QWindowsAudioSink::write(const char *data, qint64 len)
+{
+ Q_ASSERT(m_audioClient);
+ Q_ASSERT(m_renderClient);
+
+ qCDebug(qLcAudioOutput) << "write()" << len;
+
+ auto framesAvailable = audioClientFramesAvailable(m_audioClient.get());
+ if (!framesAvailable)
+ return -1;
+
+ auto maxBytesCanWrite = m_format.bytesForDuration(
+ m_resampler.outputFormat().durationForFrames(*framesAvailable));
+ qsizetype writeSize = qMin(maxBytesCanWrite, len);
+
+ QByteArray writeBytes = m_resampler.resample({data, writeSize});
+ qint32 writeFramesNum = m_resampler.outputFormat().framesForBytes(writeBytes.size());
+
+ quint8 *buffer = nullptr;
+ HRESULT hr = m_renderClient->GetBuffer(writeFramesNum, &buffer);
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioOutput) << "Failed to get buffer"
+ << std::system_category().message(hr).c_str();
+ return -1;
+ }
+
+ if (m_volume < qreal(1.0))
+ QAudioHelperInternal::qMultiplySamples(m_volume, m_resampler.outputFormat(), writeBytes.data(), buffer, writeBytes.size());
+ else
+ std::memcpy(buffer, writeBytes.data(), writeBytes.size());
+
+ DWORD flags = writeBytes.isEmpty() ? AUDCLNT_BUFFERFLAGS_SILENT : 0;
+ hr = m_renderClient->ReleaseBuffer(writeFramesNum, flags);
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioOutput) << "Failed to return buffer"
+ << std::system_category().message(hr).c_str();
+ return -1;
+ }
+
+ return writeSize;
+}
+
+void QWindowsAudioSink::resume()
+{
+ qCDebug(qLcAudioOutput) << "resume()";
+ if (deviceState == QAudio::SuspendedState) {
+ if (m_pullSource) {
+ pullSource();
+ } else {
+ // FIXME: Set IdleState to be consistent with implementations on other platforms
+ // even when playing the audio part that was in the buffer when suspended
+ deviceStateChange(QAudio::IdleState, QAudio::NoError);
+ if (remainingPlayTimeUs() > 0)
+ m_audioClient->Start();
+ }
+ }
+}
+
+void QWindowsAudioSink::suspend()
+{
+ qCDebug(qLcAudioOutput) << "suspend()";
+ if (deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState)
+ deviceStateChange(QAudio::SuspendedState, QAudio::NoError);
+}
+
+void QWindowsAudioSink::setVolume(qreal v)
+{
+ if (qFuzzyCompare(m_volume, v))
+ return;
+
+ m_volume = qBound(qreal(0), v, qreal(1));
+}
+
+void QWindowsAudioSink::reset()
+{
+ if (m_audioClient) {
+ m_audioClient->Stop();
+ m_audioClient->Reset();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qwindowsaudiosink.moc"
diff --git a/src/multimedia/windows/qwindowsaudiosink_p.h b/src/multimedia/windows/qwindowsaudiosink_p.h
new file mode 100644
index 000000000..6910a1385
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudiosink_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QWINDOWSAUDIOOUTPUT_H
+#define QWINDOWSAUDIOOUTPUT_H
+
+#include "qwindowsaudioutils_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qtimer.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodevice.h>
+#include <private/qaudiosystem_p.h>
+#include <qwindowsiupointer_p.h>
+#include <qwindowsresampler_p.h>
+
+#include <queue>
+#include <utility>
+
+#include <audioclient.h>
+#include <mmdeviceapi.h>
+
+// For compat with 4.6
+#if !defined(QT_WIN_CALLBACK)
+# if defined(Q_CC_MINGW)
+# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer))
+# else
+# define QT_WIN_CALLBACK CALLBACK
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsResampler;
+
+class QWindowsAudioSink : public QPlatformAudioSink
+{
+ Q_OBJECT
+public:
+ QWindowsAudioSink(QWindowsIUPointer<IMMDevice> device);
+ ~QWindowsAudioSink();
+
+ void setFormat(const QAudioFormat& fmt) override;
+ QAudioFormat format() const override;
+ QIODevice* start() override;
+ void start(QIODevice* device) override;
+ void stop() override { close(); }
+ void reset() override;
+ void suspend() override;
+ void resume() override;
+ qsizetype bytesFree() const override;
+ void setBufferSize(qsizetype value) override;
+ qsizetype bufferSize() const override { return m_bufferSize; }
+ qint64 processedUSecs() const override;
+ QAudio::Error error() const override { return errorState; }
+ QAudio::State state() const override { return deviceState; }
+ void setVolume(qreal) override;
+ qreal volume() const override { return m_volume; }
+
+private:
+ friend class OutputPrivate;
+ qint64 write(const char *data, qint64 len);
+ qint64 push(const char *data, qint64 len);
+
+ bool open();
+ void close();
+
+ void deviceStateChange(QAudio::State, QAudio::Error);
+
+ void pullSource();
+ qint64 remainingPlayTimeUs();
+
+ QAudioFormat m_format;
+ QAudio::Error errorState = QAudio::NoError;
+ QAudio::State deviceState = QAudio::StoppedState;
+
+ qsizetype m_bufferSize = 0;
+ qreal m_volume = 1.0;
+ QTimer m_timer;
+ QScopedPointer<QIODevice> m_pushSource;
+ QIODevice *m_pullSource = nullptr;
+ QWindowsIUPointer<IMMDevice> m_device;
+ QWindowsIUPointer<IAudioClient> m_audioClient;
+ QWindowsIUPointer<IAudioRenderClient> m_renderClient;
+ QWindowsResampler m_resampler;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QWINDOWSAUDIOOUTPUT_H
diff --git a/src/multimedia/windows/qwindowsaudiosource.cpp b/src/multimedia/windows/qwindowsaudiosource.cpp
new file mode 100644
index 000000000..d86b27157
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudiosource.cpp
@@ -0,0 +1,698 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+
+#include "qwindowsaudiosource_p.h"
+
+#include <QtCore/QDataStream>
+#include <QtCore/qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG_AUDIO 1
+
+QWindowsAudioSource::QWindowsAudioSource(int deviceId)
+{
+ bytesAvailable = 0;
+ buffer_size = 0;
+ period_size = 0;
+ m_deviceId = deviceId;
+ totalTimeValue = 0;
+ errorState = QAudio::NoError;
+ deviceState = QAudio::StoppedState;
+ audioSource = 0;
+ pullMode = true;
+ resuming = false;
+ finished = false;
+ waveBlockOffset = 0;
+
+ mixerID = 0;
+ cachedVolume = -1.0f;
+ memset(&mixerLineControls, 0, sizeof(mixerLineControls));
+}
+
+QWindowsAudioSource::~QWindowsAudioSource()
+{
+ stop();
+}
+
+void QT_WIN_CALLBACK QWindowsAudioSource::waveInProc( HWAVEIN hWaveIn, UINT uMsg,
+ DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
+{
+ Q_UNUSED(dwParam1);
+ Q_UNUSED(dwParam2);
+ Q_UNUSED(hWaveIn);
+
+ QWindowsAudioSource* qAudio;
+ qAudio = (QWindowsAudioSource*)(dwInstance);
+ if(!qAudio)
+ return;
+
+ QMutexLocker locker(&qAudio->mutex);
+
+ switch(uMsg) {
+ case WIM_OPEN:
+ break;
+ case WIM_DATA:
+ if(qAudio->waveFreeBlockCount > 0)
+ qAudio->waveFreeBlockCount--;
+ qAudio->feedback();
+ break;
+ case WIM_CLOSE:
+ qAudio->finished = true;
+ break;
+ default:
+ return;
+ }
+}
+
+WAVEHDR* QWindowsAudioSource::allocateBlocks(int size, int count)
+{
+ int i;
+ unsigned char* buffer;
+ WAVEHDR* blocks;
+ DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
+
+ if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
+ totalBufferSize)) == 0) {
+ qWarning("QAudioSource: Memory allocation error");
+ return 0;
+ }
+ blocks = (WAVEHDR*)buffer;
+ buffer += sizeof(WAVEHDR)*count;
+ for(i = 0; i < count; i++) {
+ blocks[i].dwBufferLength = size;
+ blocks[i].lpData = (LPSTR)buffer;
+ blocks[i].dwBytesRecorded=0;
+ blocks[i].dwUser = 0L;
+ blocks[i].dwFlags = 0L;
+ blocks[i].dwLoops = 0L;
+ result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioSource: Can't prepare block %d",i);
+ return 0;
+ }
+ buffer += size;
+ }
+ return blocks;
+}
+
+void QWindowsAudioSource::freeBlocks(WAVEHDR* blockArray)
+{
+ WAVEHDR* blocks = blockArray;
+
+ int count = buffer_size/period_size;
+
+ for(int i = 0; i < count; i++) {
+ waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR));
+ blocks++;
+ }
+ HeapFree(GetProcessHeap(), 0, blockArray);
+}
+
+QAudio::Error QWindowsAudioSource::error() const
+{
+ return errorState;
+}
+
+QAudio::State QWindowsAudioSource::state() const
+{
+ return deviceState;
+}
+
+#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
+ #ifndef DRVM_MAPPER
+ #define DRVM_MAPPER 0x2000
+ #endif
+ #ifndef DRVM_MAPPER_STATUS
+ #define DRVM_MAPPER_STATUS (DRVM_MAPPER+0)
+ #endif
+ #define DRVM_USER 0x4000
+ #define DRVM_MAPPER_RECONFIGURE (DRVM_MAPPER+1)
+ #define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21)
+ #define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23)
+#endif
+
+void QWindowsAudioSource::setVolume(qreal volume)
+{
+ cachedVolume = volume;
+ for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
+
+ MIXERCONTROLDETAILS controlDetails;
+ controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
+ controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID;
+ controlDetails.cChannels = 1;
+
+ if ((mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_FADER) ||
+ (mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) {
+ MIXERCONTROLDETAILS_UNSIGNED controlDetailsUnsigned;
+ controlDetailsUnsigned.dwValue = qBound(DWORD(0), DWORD(65535.0 * volume + 0.5), DWORD(65535));
+ controlDetails.cMultipleItems = 0;
+ controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
+ controlDetails.paDetails = &controlDetailsUnsigned;
+ mixerSetControlDetails(mixerID, &controlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
+ }
+ }
+}
+
+qreal QWindowsAudioSource::volume() const
+{
+ for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
+ if ((mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_FADER) &&
+ (mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_VOLUME)) {
+ continue;
+ }
+
+ MIXERCONTROLDETAILS controlDetails;
+ controlDetails.cbStruct = sizeof(controlDetails);
+ controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID;
+ controlDetails.cChannels = 1;
+ controlDetails.cMultipleItems = 0;
+ controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
+ MIXERCONTROLDETAILS_UNSIGNED detailsUnsigned;
+ controlDetails.paDetails = &detailsUnsigned;
+ memset(controlDetails.paDetails, 0, controlDetails.cbDetails);
+
+ MMRESULT result = mixerGetControlDetails(mixerID, &controlDetails, MIXER_GETCONTROLDETAILSF_VALUE);
+ if (result != MMSYSERR_NOERROR)
+ continue;
+ if (controlDetails.cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED))
+ continue;
+ return detailsUnsigned.dwValue / 65535.0;
+ }
+
+ return qFuzzyCompare(cachedVolume, qreal(-1.0f)) ? 1.0f : cachedVolume;
+}
+
+void QWindowsAudioSource::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ settings = fmt;
+}
+
+QAudioFormat QWindowsAudioSource::format() const
+{
+ return settings;
+}
+
+void QWindowsAudioSource::start(QIODevice* device)
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = true;
+ audioSource = device;
+
+ deviceState = QAudio::ActiveState;
+
+ if(!open())
+ return;
+
+ emit stateChanged(deviceState);
+}
+
+QIODevice* QWindowsAudioSource::start()
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = false;
+ audioSource = new InputPrivate(this);
+ audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+
+ deviceState = QAudio::IdleState;
+
+ if(!open())
+ return 0;
+
+ emit stateChanged(deviceState);
+
+ return audioSource;
+}
+
+void QWindowsAudioSource::stop()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ close();
+ emit stateChanged(deviceState);
+}
+
+bool QWindowsAudioSource::open()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
+#endif
+ header = 0;
+
+ period_size = 0;
+
+ if (!QWindowsAudioUtils::formatToWaveFormatExtensible(settings, wfx)) {
+ qWarning("QAudioSource: open error, invalid format.");
+ } else if (buffer_size == 0) {
+ buffer_size
+ = (settings.sampleRate()
+ * settings.channelCount()
+ * settings.bytesPerSample()
+ + 39) / 5;
+ period_size = buffer_size / 5;
+ } else {
+ period_size = buffer_size / 5;
+ }
+
+ if (period_size == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+
+ elapsedTimeOffset = 0;
+
+ if (waveInOpen(&hWaveIn, UINT_PTR(m_deviceId), &wfx.Format,
+ (DWORD_PTR)&waveInProc,
+ (DWORD_PTR) this,
+ CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ qWarning("QAudioSource: failed to open audio device");
+ return false;
+ }
+ waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
+ waveBlockOffset = 0;
+
+ if(waveBlocks == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ qWarning("QAudioSource: failed to allocate blocks. open failed");
+ return false;
+ }
+
+ mutex.lock();
+ waveFreeBlockCount = buffer_size/period_size;
+ mutex.unlock();
+
+ for(int i=0; i<buffer_size/period_size; i++) {
+ result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioSource: failed to setup block %d,err=%d",i,result);
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+ }
+ result = waveInStart(hWaveIn);
+ if(result) {
+ qWarning("QAudioSource: failed to start audio input");
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+ elapsedTimeOffset = 0;
+ totalTimeValue = 0;
+ errorState = QAudio::NoError;
+ initMixer();
+ return true;
+}
+
+void QWindowsAudioSource::close()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ deviceState = QAudio::StoppedState;
+ waveInReset(hWaveIn);
+
+ mutex.lock();
+ for (int i=0; i<waveFreeBlockCount; i++)
+ waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
+ freeBlocks(waveBlocks);
+ mutex.unlock();
+
+ waveInClose(hWaveIn);
+ closeMixer();
+
+ int count = 0;
+ while(!finished && count < 500) {
+ count++;
+ Sleep(10);
+ }
+}
+
+void QWindowsAudioSource::initMixer()
+{
+ // Get the Mixer ID from the Sound Device ID
+ UINT mixerIntID = 0;
+ if (mixerGetID(reinterpret_cast<HMIXEROBJ>(hWaveIn),
+ &mixerIntID, MIXER_OBJECTF_HWAVEIN) != MMSYSERR_NOERROR)
+ return;
+ mixerID = reinterpret_cast<HMIXEROBJ>(quintptr(mixerIntID));
+
+ // Get the Destination (Recording) Line Information
+ MIXERLINE mixerLine;
+ mixerLine.cbStruct = sizeof(MIXERLINE);
+ mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
+ if (mixerGetLineInfo(mixerID, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR)
+ return;
+
+ // Set all the Destination (Recording) Line Controls
+ if (mixerLine.cControls > 0) {
+ mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
+ mixerLineControls.dwLineID = mixerLine.dwLineID;
+ mixerLineControls.cControls = mixerLine.cControls;
+ mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
+ mixerLineControls.pamxctrl = new MIXERCONTROL[mixerLineControls.cControls];
+ if (mixerGetLineControls(mixerID, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL) != MMSYSERR_NOERROR)
+ closeMixer();
+ else if (!qFuzzyCompare(cachedVolume, qreal(-1.0f)))
+ setVolume(cachedVolume);
+ }
+}
+
+void QWindowsAudioSource::closeMixer()
+{
+ delete[] mixerLineControls.pamxctrl;
+ memset(&mixerLineControls, 0, sizeof(mixerLineControls));
+}
+
+qsizetype QWindowsAudioSource::bytesReady() const
+{
+ if (period_size == 0 || buffer_size == 0)
+ return 0;
+ if (deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
+ return 0;
+
+ int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size;
+ if(buf < 0)
+ buf = 0;
+ return buf;
+}
+
+qint64 QWindowsAudioSource::read(char* data, qint64 len)
+{
+ bool done = false;
+
+ char* p = data;
+ qint64 l = 0;
+ qint64 written = 0;
+ while(!done) {
+ // Read in some audio data
+ if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) {
+ if(pullMode) {
+ l = audioSource->write(waveBlocks[header].lpData + waveBlockOffset,
+ waveBlocks[header].dwBytesRecorded - waveBlockOffset);
+#ifdef DEBUG_AUDIO
+ qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
+#endif
+ if(l < 0) {
+ // error
+ qWarning("QAudioSource: IOError");
+ errorState = QAudio::IOError;
+
+ } else if(l == 0) {
+ // cant write to IODevice
+ qWarning("QAudioSource: IOError, can't write to QIODevice");
+ errorState = QAudio::IOError;
+
+ } else {
+ totalTimeValue += l;
+ errorState = QAudio::NoError;
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ resuming = false;
+ }
+ } else {
+ l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded - waveBlockOffset);
+ // push mode
+ memcpy(p, waveBlocks[header].lpData + waveBlockOffset, l);
+
+ len -= l;
+
+#ifdef DEBUG_AUDIO
+ qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
+#endif
+ totalTimeValue += l;
+ errorState = QAudio::NoError;
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ resuming = false;
+ }
+ } else {
+ //no data, not ready yet, next time
+ break;
+ }
+
+ if (l < waveBlocks[header].dwBytesRecorded - waveBlockOffset) {
+ waveBlockOffset += l;
+ done = true;
+ } else {
+ waveBlockOffset = 0;
+
+ waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
+
+ mutex.lock();
+ waveFreeBlockCount++;
+ mutex.unlock();
+
+ waveBlocks[header].dwBytesRecorded=0;
+ waveBlocks[header].dwFlags = 0L;
+ result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
+ qWarning("QAudioSource: failed to prepare block %d,err=%d",header,result);
+ errorState = QAudio::IOError;
+
+ mutex.lock();
+ waveFreeBlockCount--;
+ mutex.unlock();
+
+ return 0;
+ }
+ result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioSource: failed to setup block %d,err=%d",header,result);
+ errorState = QAudio::IOError;
+
+ mutex.lock();
+ waveFreeBlockCount--;
+ mutex.unlock();
+
+ return 0;
+ }
+ header++;
+ if(header >= buffer_size/period_size)
+ header = 0;
+ p+=l;
+
+ mutex.lock();
+ if(!pullMode) {
+ if(len < period_size || waveFreeBlockCount == buffer_size/period_size)
+ done = true;
+ } else {
+ if(waveFreeBlockCount == buffer_size/period_size)
+ done = true;
+ }
+ mutex.unlock();
+ }
+
+ written+=l;
+ }
+#ifdef DEBUG_AUDIO
+ qDebug()<<"read in len="<<written;
+#endif
+ return written;
+}
+
+void QWindowsAudioSource::resume()
+{
+ if (deviceState == QAudio::SuspendedState) {
+ deviceState = QAudio::ActiveState;
+ for(int i=0; i<buffer_size/period_size; i++) {
+ result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioSource: failed to setup block %d,err=%d",i,result);
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return;
+ }
+ }
+
+ mutex.lock();
+ waveFreeBlockCount = buffer_size/period_size;
+ mutex.unlock();
+
+ header = 0;
+ resuming = true;
+ waveBlockOffset = 0;
+ waveInStart(hWaveIn);
+ QTimer::singleShot(20,this,SLOT(feedback()));
+ emit stateChanged(deviceState);
+ }
+}
+
+void QWindowsAudioSource::setBufferSize(qsizetype value)
+{
+ buffer_size = value;
+}
+
+qsizetype QWindowsAudioSource::bufferSize() const
+{
+ return buffer_size;
+}
+
+qint64 QWindowsAudioSource::processedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+ qint64 result = qint64(1000000) * totalTimeValue /
+ settings.bytesPerFrame() / settings.sampleRate();
+
+ return result;
+}
+
+void QWindowsAudioSource::suspend()
+{
+ if(deviceState == QAudio::ActiveState) {
+ deviceState = QAudio::SuspendedState;
+ waveInReset(hWaveIn);
+ emit stateChanged(deviceState);
+ }
+}
+
+void QWindowsAudioSource::feedback()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this;
+#endif
+ if(deviceState != QAudio::StoppedState && deviceState != QAudio::SuspendedState)
+ QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
+}
+
+bool QWindowsAudioSource::deviceReady()
+{
+ bytesAvailable = bytesReady();
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT" << bytesReady();
+#endif
+ if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
+ return true;
+
+ if(pullMode) {
+ // reads some audio data and writes it to QIODevice
+ read(0, buffer_size);
+ } else {
+ // emits readyRead() so user will call read() on QIODevice to get some audio data
+ InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
+ a->trigger();
+ }
+
+ return true;
+}
+
+void QWindowsAudioSource::reset()
+{
+ stop();
+ if (period_size > 0)
+ waveFreeBlockCount = buffer_size / period_size;
+}
+
+InputPrivate::InputPrivate(QWindowsAudioSource* audio)
+{
+ audioDevice = qobject_cast<QWindowsAudioSource*>(audio);
+}
+
+InputPrivate::~InputPrivate() {}
+
+qint64 InputPrivate::readData( char* data, qint64 len)
+{
+ // push mode, user read() called
+ if(audioDevice->deviceState != QAudio::ActiveState &&
+ audioDevice->deviceState != QAudio::IdleState)
+ return 0;
+ // Read in some audio data
+ return audioDevice->read(data,len);
+}
+
+qint64 InputPrivate::writeData(const char* data, qint64 len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+
+ emit readyRead();
+ return 0;
+}
+
+void InputPrivate::trigger()
+{
+ emit readyRead();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowsaudiosource_p.cpp"
diff --git a/src/multimedia/windows/qwindowsaudiosource_p.h b/src/multimedia/windows/qwindowsaudiosource_p.h
new file mode 100644
index 000000000..17bbdaaee
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudiosource_p.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QWINDOWSAUDIOINPUT_H
+#define QWINDOWSAUDIOINPUT_H
+
+#include "qwindowsaudioutils_p.h"
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmutex.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodevice.h>
+#include <private/qaudiosystem_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+// For compat with 4.6
+#if !defined(QT_WIN_CALLBACK)
+# if defined(Q_CC_MINGW)
+# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer))
+# else
+# define QT_WIN_CALLBACK CALLBACK
+# endif
+#endif
+
+class QWindowsAudioSource : public QPlatformAudioSource
+{
+ Q_OBJECT
+public:
+ QWindowsAudioSource(int deviceId);
+ ~QWindowsAudioSource();
+
+ qint64 read(char* data, qint64 len);
+
+ void setFormat(const QAudioFormat& fmt) override;
+ QAudioFormat format() const override;
+ QIODevice* start() override;
+ void start(QIODevice* device) override;
+ void stop() override;
+ void reset() override;
+ void suspend() override;
+ void resume() override;
+ qsizetype bytesReady() const override;
+ void setBufferSize(qsizetype value) override;
+ qsizetype bufferSize() const override;
+ qint64 processedUSecs() const override;
+ QAudio::Error error() const override;
+ QAudio::State state() const override;
+ void setVolume(qreal volume) override;
+ qreal volume() const override;
+
+ QIODevice* audioSource;
+ QAudioFormat settings;
+ QAudio::Error errorState;
+ QAudio::State deviceState;
+
+private:
+ qint32 buffer_size;
+ qint32 period_size;
+ qint32 header;
+ int m_deviceId;
+ int bytesAvailable;
+ qint64 elapsedTimeOffset;
+ qint64 totalTimeValue;
+ bool pullMode;
+ bool resuming;
+ WAVEFORMATEXTENSIBLE wfx;
+ HWAVEIN hWaveIn;
+ MMRESULT result;
+ WAVEHDR* waveBlocks;
+ volatile bool finished;
+ volatile int waveFreeBlockCount;
+ int waveBlockOffset;
+
+ QMutex mutex;
+ static void QT_WIN_CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg,
+ DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 );
+
+ WAVEHDR* allocateBlocks(int size, int count);
+ void freeBlocks(WAVEHDR* blockArray);
+ bool open();
+ void close();
+
+ void initMixer();
+ void closeMixer();
+ HMIXEROBJ mixerID;
+ MIXERLINECONTROLS mixerLineControls;
+ qreal cachedVolume;
+
+private slots:
+ void feedback();
+ bool deviceReady();
+
+signals:
+ void processMore();
+};
+
+class InputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ InputPrivate(QWindowsAudioSource* audio);
+ ~InputPrivate();
+
+ qint64 readData( char* data, qint64 len) override;
+ qint64 writeData(const char* data, qint64 len) override;
+
+ void trigger();
+private:
+ QWindowsAudioSource *audioDevice;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/windows/qwindowsaudioutils.cpp b/src/multimedia/windows/qwindowsaudioutils.cpp
new file mode 100644
index 000000000..41c27a28f
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudioutils.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsaudioutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+bool QWindowsAudioUtils::formatToWaveFormatExtensible(const QAudioFormat &format, WAVEFORMATEXTENSIBLE &wfx)
+{
+ if (!format.isValid())
+ return false;
+
+ wfx.Format.nSamplesPerSec = format.sampleRate();
+ wfx.Format.wBitsPerSample = wfx.Samples.wValidBitsPerSample = format.bytesPerSample()*8;
+ wfx.Format.nChannels = format.channelCount();
+ wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample / 8) * wfx.Format.nChannels;
+ wfx.Format.nAvgBytesPerSec = wfx.Format.nBlockAlign * wfx.Format.nSamplesPerSec;
+ wfx.Format.cbSize = 0;
+
+ if (format.sampleFormat() == QAudioFormat::Float) {
+ wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+ wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ } else {
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ }
+
+ if (format.channelCount() > 2) {
+ wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfx.Format.cbSize = 22;
+ wfx.dwChannelMask = 0xFFFFFFFF >> (32 - format.channelCount());
+ }
+
+ return true;
+}
+
+QAudioFormat QWindowsAudioUtils::waveFormatExToFormat(const WAVEFORMATEX &in)
+{
+ QAudioFormat out;
+ out.setSampleRate(in.nSamplesPerSec);
+ out.setChannelCount(in.nChannels);
+ if (in.wFormatTag == WAVE_FORMAT_PCM) {
+ if (in.wBitsPerSample == 8)
+ out.setSampleFormat(QAudioFormat::UInt8);
+ else if (in.wBitsPerSample == 16)
+ out.setSampleFormat(QAudioFormat::Int16);
+ else if (in.wBitsPerSample == 32)
+ out.setSampleFormat(QAudioFormat::Int32);
+ } else if (in.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+ if (in.cbSize >= 22) {
+ auto wfe = reinterpret_cast<const WAVEFORMATEXTENSIBLE &>(in);
+ if (wfe.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ out.setSampleFormat(QAudioFormat::Float);
+ }
+ } else if (in.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
+ out.setSampleFormat(QAudioFormat::Float);
+ }
+
+ return out;
+}
+
+QAudioFormat QWindowsAudioUtils::mediaTypeToFormat(IMFMediaType *mediaType)
+{
+ QAudioFormat format;
+ if (!mediaType)
+ return format;
+
+ UINT32 val = 0;
+ if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &val))) {
+ format.setChannelCount(int(val));
+ }
+ if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &val))) {
+ format.setSampleRate(int(val));
+ }
+ UINT32 bitsPerSample = 0;
+ mediaType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bitsPerSample);
+
+ GUID subType;
+ if (SUCCEEDED(mediaType->GetGUID(MF_MT_SUBTYPE, &subType))) {
+ if (subType == MFAudioFormat_Float) {
+ format.setSampleFormat(QAudioFormat::Float);
+ } else if (bitsPerSample == 8) {
+ format.setSampleFormat(QAudioFormat::UInt8);
+ } else if (bitsPerSample == 16) {
+ format.setSampleFormat(QAudioFormat::Int16);
+ } else if (bitsPerSample == 32){
+ format.setSampleFormat(QAudioFormat::Int32);
+ }
+ }
+ return format;
+}
+
+QWindowsIUPointer<IMFMediaType> QWindowsAudioUtils::formatToMediaType(const QAudioFormat &format)
+{
+ QWindowsIUPointer<IMFMediaType> mediaType;
+
+ if (!format.isValid())
+ return mediaType;
+
+ MFCreateMediaType(mediaType.address());
+
+ mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
+ if (format.sampleFormat() == QAudioFormat::Float) {
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
+ } else {
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
+ }
+
+ mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, UINT32(format.channelCount()));
+ mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, UINT32(format.sampleRate()));
+ auto alignmentBlock = UINT32(format.bytesPerFrame());
+ mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, alignmentBlock);
+ auto avgBytesPerSec = UINT32(format.sampleRate() * format.bytesPerFrame());
+ mediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, avgBytesPerSec);
+ mediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, UINT32(format.bytesPerSample()*8));
+ mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
+
+ return mediaType;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsaudioutils_p.h b/src/multimedia/windows/qwindowsaudioutils_p.h
new file mode 100644
index 000000000..d6ce262df
--- /dev/null
+++ b/src/multimedia/windows/qwindowsaudioutils_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSAUDIOUTILS_H
+#define QWINDOWSAUDIOUTILS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qaudioformat.h>
+#include <QtCore/qt_windows.h>
+#include <private/qwindowsiupointer_p.h>
+#include <mfapi.h>
+#include <mmsystem.h>
+#include <mmreg.h>
+
+struct IMFMediaType;
+
+QT_BEGIN_NAMESPACE
+
+namespace QWindowsAudioUtils
+{
+ bool formatToWaveFormatExtensible(const QAudioFormat &format, WAVEFORMATEXTENSIBLE &wfx);
+ QAudioFormat waveFormatExToFormat(const WAVEFORMATEX &in);
+ Q_MULTIMEDIA_EXPORT QAudioFormat mediaTypeToFormat(IMFMediaType *mediaType);
+ QWindowsIUPointer<IMFMediaType> formatToMediaType(const QAudioFormat &format);
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSAUDIOUTILS_H
diff --git a/src/multimedia/windows/qwindowsiupointer_p.h b/src/multimedia/windows/qwindowsiupointer_p.h
new file mode 100644
index 000000000..9e9cf6894
--- /dev/null
+++ b/src/multimedia/windows/qwindowsiupointer_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSIUPOINTER_H
+#define QWINDOWSIUPOINTER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+template <class T>
+class QWindowsIUPointer
+{
+public:
+ explicit QWindowsIUPointer(T *ptr = nullptr) : m_ptr(ptr) {}
+ QWindowsIUPointer(const QWindowsIUPointer<T> &uiPtr) : m_ptr(uiPtr.m_ptr) { if (m_ptr) m_ptr->AddRef(); }
+ QWindowsIUPointer(QWindowsIUPointer<T> &&uiPtr) : m_ptr(uiPtr.m_ptr) { uiPtr.m_ptr = nullptr; }
+ ~QWindowsIUPointer() { if (m_ptr) m_ptr->Release(); }
+
+ QWindowsIUPointer& operator=(const QWindowsIUPointer<T> &rhs) {
+ if (this != &rhs) {
+ if (m_ptr)
+ m_ptr->Release();
+ m_ptr = rhs.m_ptr;
+ m_ptr->AddRef();
+ }
+ return *this;
+ }
+
+ QWindowsIUPointer& operator=(QWindowsIUPointer<T> &&rhs) noexcept {
+ if (m_ptr)
+ m_ptr->Release();
+ m_ptr = rhs.m_ptr;
+ rhs.m_ptr = nullptr;
+ return *this;
+ }
+
+ explicit operator bool() const { return m_ptr != nullptr; }
+ T *operator->() const { return m_ptr; }
+
+ T **address() { Q_ASSERT(m_ptr == nullptr); return &m_ptr; }
+ void reset(T *ptr = nullptr) { if (m_ptr) m_ptr->Release(); m_ptr = ptr; }
+ T *release() { T *ptr = m_ptr; m_ptr = nullptr; return ptr; }
+ T *get() const { return m_ptr; }
+
+private:
+ T *m_ptr;
+};
+
+#endif
diff --git a/src/multimedia/windows/qwindowsmediadevices.cpp b/src/multimedia/windows/qwindowsmediadevices.cpp
new file mode 100644
index 000000000..464ae1d3d
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmediadevices.cpp
@@ -0,0 +1,532 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsmediadevices_p.h"
+#include "qmediadevices.h"
+#include "private/qcameradevice_p.h"
+#include "qvarlengtharray.h"
+
+#include "qwindowsaudiosource_p.h"
+#include "qwindowsaudiosink_p.h"
+#include "qwindowsaudiodevice_p.h"
+#include "qwindowsmultimediautils_p.h"
+
+#include <Dbt.h>
+#include <ks.h>
+
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <mfapi.h>
+#include <mfobjects.h>
+#include <mfidl.h>
+#include <mfreadwrite.h>
+#include <Mferror.h>
+#include <mmdeviceapi.h>
+#include <Functiondiscoverykeys_devpkey.h>
+#include <qwindowsaudioutils_p.h>
+#include <qwindowsmfdefs_p.h>
+
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+class CMMNotificationClient : public IMMNotificationClient
+{
+ LONG m_cRef;
+ QWindowsIUPointer<IMMDeviceEnumerator> m_enumerator;
+ QWindowsMediaDevices *m_windowsMediaDevices;
+ QMap<QString, DWORD> m_deviceState;
+
+public:
+ CMMNotificationClient(QWindowsMediaDevices *windowsMediaDevices,
+ QWindowsIUPointer<IMMDeviceEnumerator> enumerator,
+ QMap<QString, DWORD> &&deviceState) :
+ m_cRef(1),
+ m_enumerator(enumerator),
+ m_windowsMediaDevices(windowsMediaDevices),
+ m_deviceState(deviceState)
+ {}
+
+ virtual ~CMMNotificationClient() {}
+
+ // IUnknown methods -- AddRef, Release, and QueryInterface
+ ULONG STDMETHODCALLTYPE AddRef() override
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ ULONG STDMETHODCALLTYPE Release() override
+ {
+ ULONG ulRef = InterlockedDecrement(&m_cRef);
+ if (0 == ulRef) {
+ delete this;
+ }
+ return ulRef;
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override
+ {
+ if (IID_IUnknown == riid) {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ } else if (__uuidof(IMMNotificationClient) == riid) {
+ AddRef();
+ *ppvInterface = (IMMNotificationClient*)this;
+ } else {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) override
+ {
+ if (role == ERole::eMultimedia)
+ emitAudioDevicesChanged(flow);
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR deviceID) override
+ {
+ auto it = m_deviceState.find(QString::fromWCharArray(deviceID));
+ if (it == std::end(m_deviceState)) {
+ m_deviceState.insert(QString::fromWCharArray(deviceID), DEVICE_STATE_ACTIVE);
+ emitAudioDevicesChanged(deviceID);
+ }
+
+ return S_OK;
+ };
+
+ HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR deviceID) override
+ {
+ auto key = QString::fromWCharArray(deviceID);
+ auto it = m_deviceState.find(key);
+ if (it != std::end(m_deviceState)) {
+ if (it.value() == DEVICE_STATE_ACTIVE)
+ emitAudioDevicesChanged(deviceID);
+ m_deviceState.remove(key);
+ }
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR deviceID, DWORD newState) override
+ {
+ if (auto it = m_deviceState.find(QString::fromWCharArray(deviceID)); it != std::end(m_deviceState)) {
+ // If either the old state or the new state is active emit device change
+ if ((it.value() == DEVICE_STATE_ACTIVE) != (newState == DEVICE_STATE_ACTIVE)) {
+ emitAudioDevicesChanged(deviceID);
+ }
+ it.value() = newState;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) override
+ {
+ return S_OK;
+ }
+
+ void emitAudioDevicesChanged(EDataFlow flow)
+ {
+ // windowsMediaDevice may be deleted as we are executing the callback
+ if (flow == EDataFlow::eCapture) {
+ m_windowsMediaDevices->audioInputsChanged();
+ } else if (flow == EDataFlow::eRender) {
+ m_windowsMediaDevices->audioOutputsChanged();
+ }
+ }
+
+ void emitAudioDevicesChanged(LPCWSTR deviceID)
+ {
+ QWindowsIUPointer<IMMDevice> device;
+ QWindowsIUPointer<IMMEndpoint> endpoint;
+ EDataFlow flow;
+
+ if (SUCCEEDED(m_enumerator->GetDevice(deviceID, device.address()))
+ && SUCCEEDED(device->QueryInterface(__uuidof(IMMEndpoint), (void**)endpoint.address()))
+ && SUCCEEDED(endpoint->GetDataFlow(&flow)))
+ {
+ emitAudioDevicesChanged(flow);
+ }
+ }
+};
+
+LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (message == WM_DEVICECHANGE) {
+ auto b = (PDEV_BROADCAST_HDR)lParam;
+ if (b && b->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
+ auto wmd = reinterpret_cast<QWindowsMediaDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
+
+ if (wmd) {
+ if (wParam == DBT_DEVICEARRIVAL) {
+ wmd->videoInputsChanged();
+ } else if (wParam == DBT_DEVICEREMOVECOMPLETE) {
+ wmd->videoInputsChanged();
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static const auto windowClassName = TEXT("QWindowsMediaDevicesMessageWindow");
+
+HWND createMessageOnlyWindow()
+{
+ WNDCLASSEX wx = {};
+ wx.cbSize = sizeof(WNDCLASSEX);
+ wx.lpfnWndProc = deviceNotificationWndProc;
+ wx.hInstance = GetModuleHandle(nullptr);
+ wx.lpszClassName = windowClassName;
+
+ if (!RegisterClassEx(&wx))
+ return nullptr;
+
+ auto hwnd = CreateWindowEx(0, windowClassName, TEXT("Message"),
+ 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
+ if (!hwnd) {
+ UnregisterClass(windowClassName, GetModuleHandle(nullptr));
+ return nullptr;
+ }
+
+ return hwnd;
+}
+
+QWindowsMediaDevices::QWindowsMediaDevices()
+ : QPlatformMediaDevices(),
+ m_videoDeviceMsgWindow(nullptr),
+ m_videoDeviceNotification(nullptr)
+
+{
+ CoInitialize(nullptr);
+
+ auto hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
+ CLSCTX_INPROC_SERVER,__uuidof(IMMDeviceEnumerator),
+ (void**)&m_deviceEnumerator);
+
+ if (SUCCEEDED(hr)) {
+ QMap<QString, DWORD> devState;
+ QWindowsIUPointer<IMMDeviceCollection> devColl;
+ UINT count = 0;
+
+ if (SUCCEEDED(m_deviceEnumerator->EnumAudioEndpoints(EDataFlow::eAll, DEVICE_STATEMASK_ALL, devColl.address()))
+ && SUCCEEDED(devColl->GetCount(&count)))
+ {
+ for (UINT i = 0; i < count; i++) {
+ QWindowsIUPointer<IMMDevice> device;
+ DWORD state = 0;
+ LPWSTR id = nullptr;
+
+ if (SUCCEEDED(devColl->Item(i, device.address()))
+ && SUCCEEDED(device->GetState(&state))
+ && SUCCEEDED(device->GetId(&id)))
+ {
+ devState.insert(QString::fromWCharArray(id), state);
+ CoTaskMemFree(id);
+ }
+ }
+ }
+
+
+ m_notificationClient.reset(new CMMNotificationClient(this, m_deviceEnumerator, std::move(devState)));
+ m_deviceEnumerator->RegisterEndpointNotificationCallback(m_notificationClient.get());
+
+ } else {
+ qWarning() << "Audio device change notification disabled";
+ }
+
+ m_videoDeviceMsgWindow = createMessageOnlyWindow();
+ if (m_videoDeviceMsgWindow) {
+ SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this);
+
+ DEV_BROADCAST_DEVICEINTERFACE di = {};
+ di.dbcc_size = sizeof(di);
+ di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ di.dbcc_classguid = QMM_KSCATEGORY_VIDEO_CAMERA;
+
+ m_videoDeviceNotification =
+ RegisterDeviceNotification(m_videoDeviceMsgWindow, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
+ if (!m_videoDeviceNotification) {
+ DestroyWindow(m_videoDeviceMsgWindow);
+ m_videoDeviceMsgWindow = nullptr;
+
+ UnregisterClass(windowClassName, GetModuleHandle(nullptr));
+ }
+ }
+
+ if (!m_videoDeviceNotification) {
+ qWarning() << "Video device change notification disabled";
+ }
+}
+
+QWindowsMediaDevices::~QWindowsMediaDevices()
+{
+ if (m_deviceEnumerator) {
+ m_deviceEnumerator->UnregisterEndpointNotificationCallback(m_notificationClient.get());
+ }
+
+ m_deviceEnumerator.reset();
+ m_notificationClient.reset();
+
+ if (m_videoDeviceNotification) {
+ UnregisterDeviceNotification(m_videoDeviceNotification);
+ }
+
+ if (m_videoDeviceMsgWindow) {
+ DestroyWindow(m_videoDeviceMsgWindow);
+ UnregisterClass(windowClassName, GetModuleHandle(nullptr));
+ }
+
+ CoUninitialize();
+}
+
+QList<QAudioDevice> QWindowsMediaDevices::availableDevices(QAudioDevice::Mode mode) const
+{
+ const auto audioOut = mode == QAudioDevice::Output;
+
+ const auto defaultAudioDeviceID = [this, audioOut]{
+ const auto dataFlow = audioOut ? EDataFlow::eRender : EDataFlow::eCapture;
+ QWindowsIUPointer<IMMDevice> dev;
+ LPWSTR id = nullptr;
+ QString sid;
+
+ if (SUCCEEDED(m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, ERole::eMultimedia, dev.address()))
+ && SUCCEEDED(dev->GetId(&id))) {
+ sid = QString::fromWCharArray(id);
+ CoTaskMemFree(id);
+ }
+ return sid.toUtf8();
+ }();
+
+ QList<QAudioDevice> devices;
+
+ auto waveDevices = audioOut ? waveOutGetNumDevs() : waveInGetNumDevs();
+
+ for (auto waveID = 0u; waveID < waveDevices; waveID++) {
+ auto wave = IntToPtr(waveID);
+ auto waveMessage = [wave, audioOut](UINT msg, auto p0, auto p1) {
+ return audioOut ? waveOutMessage((HWAVEOUT)wave, msg, (DWORD_PTR)p0, (DWORD_PTR)p1)
+ : waveInMessage((HWAVEIN)wave, msg, (DWORD_PTR)p0, (DWORD_PTR)p1);
+ };
+
+ size_t len = 0;
+ if (waveMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE, &len, 0) != MMSYSERR_NOERROR)
+ continue;
+
+ QVarLengthArray<WCHAR> id(len);
+ if (waveMessage(DRV_QUERYFUNCTIONINSTANCEID, id.data(), len) != MMSYSERR_NOERROR)
+ continue;
+
+ QWindowsIUPointer<IMMDevice> device;
+ QWindowsIUPointer<IPropertyStore> props;
+ if (FAILED(m_deviceEnumerator->GetDevice(id.data(), device.address()))
+ || FAILED(device->OpenPropertyStore(STGM_READ, props.address()))) {
+ continue;
+ }
+
+ PROPVARIANT varName;
+ PropVariantInit(&varName);
+
+ if (SUCCEEDED(props->GetValue(QMM_PKEY_Device_FriendlyName, &varName))) {
+ auto description = QString::fromWCharArray(varName.pwszVal);
+ auto strID = QString::fromWCharArray(id.data()).toUtf8();
+
+ auto dev = new QWindowsAudioDeviceInfo(strID, device, waveID, description, mode);
+ dev->isDefault = strID == defaultAudioDeviceID;
+
+ devices.append(dev->create());
+ }
+ PropVariantClear(&varName);
+ }
+
+ return devices;
+}
+
+QList<QAudioDevice> QWindowsMediaDevices::audioInputs() const
+{
+ return availableDevices(QAudioDevice::Input);
+}
+
+QList<QAudioDevice> QWindowsMediaDevices::audioOutputs() const
+{
+ return availableDevices(QAudioDevice::Output);
+}
+
+QList<QCameraDevice> QWindowsMediaDevices::videoInputs() const
+{
+ QList<QCameraDevice> cameras;
+
+ IMFAttributes *pAttributes = NULL;
+ IMFActivate **ppDevices = NULL;
+
+ // Create an attribute store to specify the enumeration parameters.
+ HRESULT hr = MFCreateAttributes(&pAttributes, 1);
+ if (SUCCEEDED(hr)) {
+ // Source type: video capture devices
+ hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
+ MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
+
+ if (SUCCEEDED(hr)) {
+ // Enumerate devices.
+ UINT32 count;
+ hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count);
+ if (SUCCEEDED(hr)) {
+ // Iterate through devices.
+ for (int index = 0; index < int(count); index++) {
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+
+ IMFMediaSource *pSource = NULL;
+ IMFSourceReader *reader = NULL;
+
+ WCHAR *deviceName = NULL;
+ UINT32 deviceNameLength = 0;
+ UINT32 deviceIdLength = 0;
+ WCHAR *deviceId = NULL;
+
+ hr = ppDevices[index]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
+ &deviceName, &deviceNameLength);
+ if (SUCCEEDED(hr))
+ info->description = QString::fromWCharArray(deviceName);
+ CoTaskMemFree(deviceName);
+
+ hr = ppDevices[index]->GetAllocatedString(
+ MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &deviceId,
+ &deviceIdLength);
+ if (SUCCEEDED(hr))
+ info->id = QString::fromWCharArray(deviceId).toUtf8();
+ CoTaskMemFree(deviceId);
+
+ // Create the media source object.
+ hr = ppDevices[index]->ActivateObject(
+ IID_PPV_ARGS(&pSource));
+ // Create the media source reader.
+ hr = MFCreateSourceReaderFromMediaSource(pSource, NULL, &reader);
+ if (SUCCEEDED(hr)) {
+ QList<QSize> photoResolutions;
+ QList<QCameraFormat> videoFormats;
+
+ DWORD dwMediaTypeIndex = 0;
+ IMFMediaType *mediaFormat = NULL;
+ GUID subtype = GUID_NULL;
+ HRESULT mediaFormatResult = S_OK;
+
+ UINT32 frameRateMin = 0u;
+ UINT32 frameRateMax = 0u;
+ UINT32 denominator = 0u;
+ UINT32 width = 0u;
+ UINT32 height = 0u;
+
+ while (SUCCEEDED(mediaFormatResult)) {
+ // Loop through the supported formats for the video device
+ mediaFormatResult = reader->GetNativeMediaType(
+ (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, dwMediaTypeIndex,
+ &mediaFormat);
+ if (mediaFormatResult == MF_E_NO_MORE_TYPES)
+ break;
+ else if (SUCCEEDED(mediaFormatResult)) {
+ QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid;
+ QSize resolution;
+ float minFr = .0;
+ float maxFr = .0;
+
+ if (SUCCEEDED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
+ pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
+
+ if (SUCCEEDED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width,
+ &height))) {
+ resolution.rheight() = (int)height;
+ resolution.rwidth() = (int)width;
+ photoResolutions << resolution;
+ }
+
+ if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN,
+ &frameRateMin, &denominator)))
+ minFr = qreal(frameRateMin) / denominator;
+ if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX,
+ &frameRateMax, &denominator)))
+ maxFr = qreal(frameRateMax) / denominator;
+
+ auto *f = new QCameraFormatPrivate { QSharedData(), pixelFormat,
+ resolution, minFr, maxFr };
+ videoFormats << f->create();
+ }
+ ++dwMediaTypeIndex;
+ }
+ if (mediaFormat)
+ mediaFormat->Release();
+
+ info->videoFormats = videoFormats;
+ info->photoResolutions = photoResolutions;
+ }
+ if (reader)
+ reader->Release();
+ cameras.append(info->create());
+ }
+ }
+ for (DWORD i = 0; i < count; i++) {
+ if (ppDevices[i])
+ ppDevices[i]->Release();
+ }
+ CoTaskMemFree(ppDevices);
+ }
+ }
+ if (pAttributes)
+ pAttributes->Release();
+
+ return cameras;
+}
+
+QPlatformAudioSource *QWindowsMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+{
+ const auto *devInfo = static_cast<const QWindowsAudioDeviceInfo *>(deviceInfo.handle());
+ return new QWindowsAudioSource(devInfo->waveId());
+}
+
+QPlatformAudioSink *QWindowsMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+{
+ const auto *devInfo = static_cast<const QWindowsAudioDeviceInfo *>(deviceInfo.handle());
+ return new QWindowsAudioSink(devInfo->immDev());
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsmediadevices_p.h b/src/multimedia/windows/qwindowsmediadevices_p.h
new file mode 100644
index 000000000..a1a377309
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmediadevices_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMEDIADEVICES_H
+#define QWINDOWSMEDIADEVICES_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformmediadevices_p.h>
+#include <private/qwindowsiupointer_p.h>
+#include <qset.h>
+#include <qaudio.h>
+#include <qaudiodevice.h>
+#include <QtCore/qt_windows.h>
+
+struct IMMDeviceEnumerator;
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsEngine;
+class CMMNotificationClient;
+
+LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND, UINT, WPARAM, LPARAM);
+
+class QWindowsMediaDevices : public QPlatformMediaDevices
+{
+public:
+ QWindowsMediaDevices();
+ virtual ~QWindowsMediaDevices();
+
+ QList<QAudioDevice> audioInputs() const override;
+ QList<QAudioDevice> audioOutputs() const override;
+ QList<QCameraDevice> videoInputs() const override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+
+private:
+ QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode) const;
+
+ QWindowsIUPointer<IMMDeviceEnumerator> m_deviceEnumerator;
+ QWindowsIUPointer<CMMNotificationClient> m_notificationClient;
+ HWND m_videoDeviceMsgWindow;
+ HDEVNOTIFY m_videoDeviceNotification;
+
+ friend CMMNotificationClient;
+ friend LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND, UINT, WPARAM, LPARAM);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/windows/qwindowsmfdefs.cpp b/src/multimedia/windows/qwindowsmfdefs.cpp
new file mode 100644
index 000000000..97eae9743
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmfdefs.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsmfdefs_p.h"
+
+const GUID QMM_MFTranscodeContainerType_ADTS = {0x132fd27d, 0x0f02, 0x43de, {0xa3, 0x01, 0x38, 0xfb, 0xbb, 0xb3, 0x83, 0x4e}};
+const GUID QMM_MFTranscodeContainerType_ASF = {0x430f6f6e, 0xb6bf, 0x4fc1, {0xa0, 0xbd, 0x9e, 0xe4, 0x6e, 0xee, 0x2a, 0xfb}};
+const GUID QMM_MFTranscodeContainerType_AVI = {0x7edfe8af, 0x402f, 0x4d76, {0xa3, 0x3c, 0x61, 0x9f, 0xd1, 0x57, 0xd0, 0xf1}};
+const GUID QMM_MFTranscodeContainerType_FLAC = {0x31344aa3, 0x05a9, 0x42b5, {0x90, 0x1b, 0x8e, 0x9d, 0x42, 0x57, 0xf7, 0x5e}};
+const GUID QMM_MFTranscodeContainerType_MP3 = {0xe438b912, 0x83f1, 0x4de6, {0x9e, 0x3a, 0x9f, 0xfb, 0xc6, 0xdd, 0x24, 0xd1}};
+const GUID QMM_MFTranscodeContainerType_MPEG4 = {0xdc6cd05d, 0xb9d0, 0x40ef, {0xbd, 0x35, 0xfa, 0x62, 0x2c, 0x1a, 0xb2, 0x8a}};
+const GUID QMM_MFTranscodeContainerType_WAVE = {0x64c3453c, 0x0f26, 0x4741, {0xbe, 0x63, 0x87, 0xbd, 0xf8, 0xbb, 0x93, 0x5b}};
+
+const GUID QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID = {0x8ac3587a, 0x4ae7, 0x42d8, {0x99, 0xe0, 0x0a, 0x60, 0x13, 0xee, 0xf9, 0x0f}};
+const GUID QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID = {0x14dd9a1c, 0x7cff, 0x41be, {0xb1, 0xb9, 0xba, 0x1a, 0xc6, 0xec, 0xb5, 0x71}};
+const GUID QMM_MF_TRANSCODE_CONTAINERTYPE = {0x150ff23f, 0x4abc, 0x478b, {0xac, 0x4f, 0xe1, 0x91, 0x6f, 0xba, 0x1c, 0xca}};
+
+const GUID QMM_MF_SD_STREAM_NAME = {0x4f1b099d, 0xd314, 0x41e5, {0xa7, 0x81, 0x7f, 0xef, 0xaa, 0x4c, 0x50, 0x1f}};
+const GUID QMM_MF_SD_LANGUAGE = {0xaf2180, 0xbdc2, 0x423c, {0xab, 0xca, 0xf5, 0x3, 0x59, 0x3b, 0xc1, 0x21}};
+
+const GUID QMM_KSCATEGORY_VIDEO_CAMERA = {0xe5323777, 0xf976, 0x4f5b, {0x9b, 0x55, 0xb9, 0x46, 0x99, 0xc4, 0x6e, 0x44}};
+
+const GUID QMM_MR_POLICY_VOLUME_SERVICE = {0x1abaa2ac, 0x9d3b, 0x47c6, {0xab, 0x48, 0xc5, 0x95, 0x6, 0xde, 0x78, 0x4d}};
+
+const PROPERTYKEY QMM_PKEY_Device_FriendlyName = {{0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}}, 14};
+
diff --git a/src/multimedia/windows/qwindowsmfdefs_p.h b/src/multimedia/windows/qwindowsmfdefs_p.h
new file mode 100644
index 000000000..5bae90d18
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmfdefs_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMFDEFS_P_H
+#define QWINDOWSMFDEFS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtmultimediaexports.h>
+#include <d3d9.h>
+#include <dxva2api.h>
+#include <mfidl.h>
+
+// Stuff that is missing or incorrecty defined in MinGW.
+
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_ADTS;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_ASF;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_AVI;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_FLAC;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_MP3;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_MPEG4;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MFTranscodeContainerType_WAVE;
+
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_TRANSCODE_CONTAINERTYPE;
+
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_SD_STREAM_NAME;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_SD_LANGUAGE;
+
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_KSCATEGORY_VIDEO_CAMERA;
+
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_MR_POLICY_VOLUME_SERVICE;
+
+Q_MULTIMEDIA_EXPORT extern const PROPERTYKEY QMM_PKEY_Device_FriendlyName;
+
+extern "C" HRESULT WINAPI MFCreateDeviceSource(IMFAttributes *pAttributes, IMFMediaSource **ppSource);
+
+#define QMM_MFSESSION_GETFULLTOPOLOGY_CURRENT 1
+#define QMM_PRESENTATION_CURRENT_POSITION 0x7fffffffffffffff
+
+#ifndef __IMFVideoProcessor_INTERFACE_DEFINED__
+#define __IMFVideoProcessor_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0x57,0x35,0x30,0x65,0x6E);
+MIDL_INTERFACE("6AB0000C-FECE-4d1f-A2AC-A9573530656E")
+IMFVideoProcessor : public IUnknown
+{
+ virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoProcessorModes(UINT *, GUID **) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetVideoProcessorCaps(LPGUID, DXVA2_VideoProcessorCaps *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetVideoProcessorMode(LPGUID) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetVideoProcessorMode(LPGUID) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetProcAmpRange(DWORD, DXVA2_ValueRange *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetProcAmpValues(DWORD, DXVA2_ProcAmpValues *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetProcAmpValues(DWORD, DXVA2_ProcAmpValues *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetFilteringRange(DWORD, DXVA2_ValueRange *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetFilteringValue(DWORD, DXVA2_Fixed32 *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetFilteringValue(DWORD, DXVA2_Fixed32 *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetBackgroundColor(COLORREF *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetBackgroundColor(COLORREF) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0x57,0x35,0x30,0x65,0x6E)
+#endif
+#endif // __IMFVideoProcessor_INTERFACE_DEFINED__
+
+#ifndef __IMFSimpleAudioVolume_INTERFACE_DEFINED__
+#define __IMFSimpleAudioVolume_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFSimpleAudioVolume, 0x089EDF13, 0xCF71, 0x4338, 0x8D,0x13, 0x9E,0x56,0x9D,0xBD,0xC3,0x19);
+MIDL_INTERFACE("089EDF13-CF71-4338-8D13-9E569DBDC319")
+IMFSimpleAudioVolume : public IUnknown
+{
+ virtual HRESULT STDMETHODCALLTYPE SetMasterVolume(float) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetMasterVolume(float *) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetMute(BOOL) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetMute(BOOL *) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFSimpleAudioVolume, 0x089EDF13, 0xCF71, 0x4338, 0x8D,0x13, 0x9E,0x56,0x9D,0xBD,0xC3,0x19)
+#endif
+#endif // __IMFSimpleAudioVolume_INTERFACE_DEFINED__
+
+#endif // QWINDOWSMFDEFS_P_H
+
diff --git a/src/multimedia/windows/qwindowsmultimediautils.cpp b/src/multimedia/windows/qwindowsmultimediautils.cpp
new file mode 100644
index 000000000..b83233893
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmultimediautils.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(WINVER) && WINVER < _WIN32_WINNT_WIN10
+# undef WINVER
+#endif
+#if !defined(WINVER)
+# define WINVER _WIN32_WINNT_WIN10 // Enables newer audio formats.
+#endif
+
+#include "qwindowsmultimediautils_p.h"
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <qwindowsmfdefs_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QVideoFrameFormat::PixelFormat QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(const GUID &subtype)
+{
+ if (subtype == MFVideoFormat_ARGB32)
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return QVideoFrameFormat::Format_BGRA8888;
+#else
+ return QVideoFrameFormat::Format_ARGB8888;
+#endif
+ if (subtype == MFVideoFormat_RGB32)
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return QVideoFrameFormat::Format_BGRX8888;
+#else
+ return QVideoFrameFormat::Format_XRGB8888;
+#endif
+ if (subtype == MFVideoFormat_AYUV)
+ return QVideoFrameFormat::Format_AYUV;
+ if (subtype == MFVideoFormat_I420)
+ return QVideoFrameFormat::Format_YUV420P;
+ if (subtype == MFVideoFormat_UYVY)
+ return QVideoFrameFormat::Format_UYVY;
+ if (subtype == MFVideoFormat_YV12)
+ return QVideoFrameFormat::Format_YV12;
+ if (subtype == MFVideoFormat_NV12)
+ return QVideoFrameFormat::Format_NV12;
+ if (subtype == MFVideoFormat_YUY2)
+ return QVideoFrameFormat::Format_YUYV;
+ if (subtype == MFVideoFormat_P010)
+ return QVideoFrameFormat::Format_P010;
+ if (subtype == MFVideoFormat_P016)
+ return QVideoFrameFormat::Format_P016;
+ if (subtype == MFVideoFormat_L8)
+ return QVideoFrameFormat::Format_Y8;
+ if (subtype == MFVideoFormat_L16)
+ return QVideoFrameFormat::Format_Y16;
+
+ return QVideoFrameFormat::Format_Invalid;
+}
+
+GUID QWindowsMultimediaUtils::videoFormatForCodec(QMediaFormat::VideoCodec codec)
+{
+ switch (codec) {
+ case QMediaFormat::VideoCodec::MPEG1:
+ return MFVideoFormat_MPG1;
+ case QMediaFormat::VideoCodec::MPEG2:
+ return MFVideoFormat_MPEG2;
+ case QMediaFormat::VideoCodec::MPEG4:
+ return MFVideoFormat_MP4V;
+ case QMediaFormat::VideoCodec::H264:
+ return MFVideoFormat_H264;
+ case QMediaFormat::VideoCodec::H265:
+ return MFVideoFormat_H265;
+ case QMediaFormat::VideoCodec::VP8:
+ return MFVideoFormat_VP80;
+ case QMediaFormat::VideoCodec::VP9:
+ return MFVideoFormat_VP90;
+ case QMediaFormat::VideoCodec::AV1:
+ return MFVideoFormat_AV1;
+ case QMediaFormat::VideoCodec::WMV:
+ return MFVideoFormat_WMV3;
+ case QMediaFormat::VideoCodec::MotionJPEG:
+ return MFVideoFormat_MJPG;
+ default:
+ return MFVideoFormat_H264;
+ }
+}
+
+QMediaFormat::VideoCodec QWindowsMultimediaUtils::codecForVideoFormat(GUID format)
+{
+ if (format == MFVideoFormat_MPG1)
+ return QMediaFormat::VideoCodec::MPEG1;
+ if (format == MFVideoFormat_MPEG2)
+ return QMediaFormat::VideoCodec::MPEG2;
+ if (format == MFVideoFormat_MP4V
+ || format == MFVideoFormat_M4S2
+ || format == MFVideoFormat_MP4S
+ || format == MFVideoFormat_MP43)
+ return QMediaFormat::VideoCodec::MPEG4;
+ if (format == MFVideoFormat_H264)
+ return QMediaFormat::VideoCodec::H264;
+ if (format == MFVideoFormat_H265)
+ return QMediaFormat::VideoCodec::H265;
+ if (format == MFVideoFormat_VP80)
+ return QMediaFormat::VideoCodec::VP8;
+ if (format == MFVideoFormat_VP90)
+ return QMediaFormat::VideoCodec::VP9;
+ if (format == MFVideoFormat_AV1)
+ return QMediaFormat::VideoCodec::AV1;
+ if (format == MFVideoFormat_WMV1
+ || format == MFVideoFormat_WMV2
+ || format == MFVideoFormat_WMV3)
+ return QMediaFormat::VideoCodec::WMV;
+ if (format == MFVideoFormat_MJPG)
+ return QMediaFormat::VideoCodec::MotionJPEG;
+ return QMediaFormat::VideoCodec::Unspecified;
+}
+
+GUID QWindowsMultimediaUtils::audioFormatForCodec(QMediaFormat::AudioCodec codec)
+{
+ switch (codec) {
+ case QMediaFormat::AudioCodec::MP3:
+ return MFAudioFormat_MP3;
+ case QMediaFormat::AudioCodec::AAC:
+ return MFAudioFormat_AAC;
+ case QMediaFormat::AudioCodec::ALAC:
+ return MFAudioFormat_ALAC;
+ case QMediaFormat::AudioCodec::FLAC:
+ return MFAudioFormat_FLAC;
+ case QMediaFormat::AudioCodec::Vorbis:
+ return MFAudioFormat_Vorbis;
+ case QMediaFormat::AudioCodec::Wave:
+ return MFAudioFormat_PCM;
+ case QMediaFormat::AudioCodec::Opus:
+ return MFAudioFormat_Opus;
+ case QMediaFormat::AudioCodec::AC3:
+ return MFAudioFormat_Dolby_AC3;
+ case QMediaFormat::AudioCodec::EAC3:
+ return MFAudioFormat_Dolby_DDPlus;
+ case QMediaFormat::AudioCodec::WMA:
+ return MFAudioFormat_WMAudioV9;
+ default:
+ return MFAudioFormat_AAC;
+ }
+}
+
+QMediaFormat::AudioCodec QWindowsMultimediaUtils::codecForAudioFormat(GUID format)
+{
+ if (format == MFAudioFormat_MP3)
+ return QMediaFormat::AudioCodec::MP3;
+ if (format == MFAudioFormat_AAC)
+ return QMediaFormat::AudioCodec::AAC;
+ if (format == MFAudioFormat_ALAC)
+ return QMediaFormat::AudioCodec::ALAC;
+ if (format == MFAudioFormat_FLAC)
+ return QMediaFormat::AudioCodec::FLAC;
+ if (format == MFAudioFormat_Vorbis)
+ return QMediaFormat::AudioCodec::Vorbis;
+ if (format == MFAudioFormat_PCM)
+ return QMediaFormat::AudioCodec::Wave;
+ if (format == MFAudioFormat_Opus)
+ return QMediaFormat::AudioCodec::Opus;
+ if (format == MFAudioFormat_Dolby_AC3)
+ return QMediaFormat::AudioCodec::AC3;
+ if (format == MFAudioFormat_Dolby_DDPlus)
+ return QMediaFormat::AudioCodec::EAC3;
+ if (format == MFAudioFormat_WMAudioV8
+ || format == MFAudioFormat_WMAudioV9
+ || format == MFAudioFormat_WMAudio_Lossless)
+ return QMediaFormat::AudioCodec::WMA;
+ return QMediaFormat::AudioCodec::Unspecified;
+}
+
+GUID QWindowsMultimediaUtils::containerForVideoFileFormat(QMediaFormat::FileFormat format)
+{
+ switch (format) {
+ case QMediaFormat::FileFormat::MPEG4:
+ return QMM_MFTranscodeContainerType_MPEG4;
+ case QMediaFormat::FileFormat::WMV:
+ return QMM_MFTranscodeContainerType_ASF;
+ case QMediaFormat::FileFormat::AVI:
+ return QMM_MFTranscodeContainerType_AVI;
+ default:
+ return QMM_MFTranscodeContainerType_MPEG4;
+ }
+}
+
+GUID QWindowsMultimediaUtils::containerForAudioFileFormat(QMediaFormat::FileFormat format)
+{
+ switch (format) {
+ case QMediaFormat::FileFormat::MP3:
+ return QMM_MFTranscodeContainerType_MP3;
+ case QMediaFormat::FileFormat::AAC:
+ return QMM_MFTranscodeContainerType_ADTS;
+ case QMediaFormat::FileFormat::Mpeg4Audio:
+ return QMM_MFTranscodeContainerType_MPEG4;
+ case QMediaFormat::FileFormat::WMA:
+ return QMM_MFTranscodeContainerType_ASF;
+ case QMediaFormat::FileFormat::FLAC:
+ return QMM_MFTranscodeContainerType_FLAC;
+ case QMediaFormat::FileFormat::Wave:
+ return QMM_MFTranscodeContainerType_WAVE;
+ default:
+ return QMM_MFTranscodeContainerType_MPEG4;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsmultimediautils_p.h b/src/multimedia/windows/qwindowsmultimediautils_p.h
new file mode 100644
index 000000000..3821a058e
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmultimediautils_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMULTIMEDIATUTILS_P_H
+#define QWINDOWSMULTIMEDIATUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtmultimediaglobal_p.h>
+#include <private/qplatformmediaformatinfo_p.h>
+#include <qvideoframeformat.h>
+#include <guiddef.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QWindowsMultimediaUtils {
+
+ Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormatFromMediaSubtype(const GUID &subtype);
+
+ Q_MULTIMEDIA_EXPORT GUID videoFormatForCodec(QMediaFormat::VideoCodec codec);
+
+ Q_MULTIMEDIA_EXPORT QMediaFormat::VideoCodec codecForVideoFormat(GUID format);
+
+ Q_MULTIMEDIA_EXPORT GUID audioFormatForCodec(QMediaFormat::AudioCodec codec);
+
+ Q_MULTIMEDIA_EXPORT QMediaFormat::AudioCodec codecForAudioFormat(GUID format);
+
+ Q_MULTIMEDIA_EXPORT GUID containerForVideoFileFormat(QMediaFormat::FileFormat format);
+
+ Q_MULTIMEDIA_EXPORT GUID containerForAudioFileFormat(QMediaFormat::FileFormat format);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/windows/qwindowsresampler.cpp b/src/multimedia/windows/qwindowsresampler.cpp
new file mode 100644
index 000000000..5b09830cc
--- /dev/null
+++ b/src/multimedia/windows/qwindowsresampler.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsresampler_p.h"
+#include <qwindowsaudioutils_p.h>
+#include <qloggingcategory.h>
+
+#include <Wmcodecdsp.h>
+#include <mftransform.h>
+#include <mfapi.h>
+#include <mferror.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcAudioResampler, "qt.multimedia.audioresampler")
+
+QWindowsResampler::QWindowsResampler()
+{
+ CoCreateInstance(CLSID_CResamplerMediaObject, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IMFTransform, (LPVOID*)(m_resampler.address()));
+ if (m_resampler)
+ m_resampler->AddInputStreams(1, &m_inputStreamID);
+}
+
+QWindowsResampler::~QWindowsResampler() = default;
+
+quint64 QWindowsResampler::outputBufferSize(quint64 inputBufferSize) const
+{
+ if (m_inputFormat.isValid() && m_outputFormat.isValid())
+ return m_outputFormat.bytesForDuration(m_inputFormat.durationForBytes(inputBufferSize));
+ else
+ return 0;
+}
+
+quint64 QWindowsResampler::inputBufferSize(quint64 outputBufferSize) const
+{
+ if (m_inputFormat.isValid() && m_outputFormat.isValid())
+ return m_inputFormat.bytesForDuration(m_outputFormat.durationForBytes(outputBufferSize));
+ else
+ return 0;
+}
+
+HRESULT QWindowsResampler::processInput(const QByteArrayView &in)
+{
+ QWindowsIUPointer<IMFSample> sample;
+ HRESULT hr = MFCreateSample(sample.address());
+ if (FAILED(hr))
+ return hr;
+
+ QWindowsIUPointer<IMFMediaBuffer> buffer;
+ hr = MFCreateMemoryBuffer(in.size(), buffer.address());
+ if (FAILED(hr))
+ return hr;
+
+ BYTE *data = nullptr;
+ DWORD maxLen = 0;
+ DWORD currentLen = 0;
+ hr = buffer->Lock(&data, &maxLen, &currentLen);
+ if (FAILED(hr))
+ return hr;
+
+ memcpy(data, in.data(), in.size());
+
+ hr = buffer->Unlock();
+ if (FAILED(hr))
+ return hr;
+
+ hr = buffer->SetCurrentLength(in.size());
+ if (FAILED(hr))
+ return hr;
+
+ hr = sample->AddBuffer(buffer.get());
+ if (FAILED(hr))
+ return hr;
+
+ return m_resampler->ProcessInput(m_inputStreamID, sample.get(), 0);
+}
+
+HRESULT QWindowsResampler::processOutput(QByteArray &out)
+{
+ QWindowsIUPointer<IMFSample> sample;
+ QWindowsIUPointer<IMFMediaBuffer> buffer;
+
+ if (m_resamplerNeedsSampleBuffer) {
+ HRESULT hr = MFCreateSample(sample.address());
+ if (FAILED(hr))
+ return hr;
+
+ auto expectedOutputSize = outputBufferSize(m_totalInputBytes) - m_totalOutputBytes;
+ hr = MFCreateMemoryBuffer(expectedOutputSize, buffer.address());
+ if (FAILED(hr))
+ return hr;
+
+ hr = sample->AddBuffer(buffer.get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ HRESULT hr = S_OK;
+
+ MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
+ outputDataBuffer.dwStreamID = 0;
+ do {
+ outputDataBuffer.pEvents = nullptr;
+ outputDataBuffer.dwStatus = 0;
+ outputDataBuffer.pSample = m_resamplerNeedsSampleBuffer ? sample.get() : nullptr;
+ DWORD status = 0;
+ hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
+ if (SUCCEEDED(hr)) {
+ QWindowsIUPointer<IMFMediaBuffer> outputBuffer;
+ outputDataBuffer.pSample->ConvertToContiguousBuffer(outputBuffer.address());
+ DWORD len = 0;
+ BYTE *data = nullptr;
+ hr = outputBuffer->Lock(&data, nullptr, &len);
+ if (SUCCEEDED(hr))
+ out.push_back(QByteArray(reinterpret_cast<char *>(data), len));
+ outputBuffer->Unlock();
+ }
+ } while (SUCCEEDED(hr));
+
+ if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
+ hr = S_OK;
+
+ return hr;
+}
+
+QByteArray QWindowsResampler::resample(const QByteArrayView &in)
+{
+ m_totalInputBytes += in.size();
+
+ if (m_inputFormat == m_outputFormat) {
+ m_totalOutputBytes += in.size();
+ return {in.data(), in.size()};
+
+ } else {
+ Q_ASSERT(m_resampler);
+
+ QByteArray out;
+ HRESULT hr = processInput(in);
+ if (SUCCEEDED(hr))
+ hr = processOutput(out);
+
+ if (FAILED(hr))
+ qCWarning(qLcAudioResampler) << "Resampling failed" << hr;
+
+ m_totalOutputBytes += out.size();
+ return out;
+ }
+}
+
+QByteArray QWindowsResampler::resample(IMFSample *sample)
+{
+ Q_ASSERT(sample);
+
+ DWORD totalLength = 0;
+ HRESULT hr = sample->GetTotalLength(&totalLength);
+ if (FAILED(hr))
+ return {};
+
+ m_totalInputBytes += totalLength;
+
+ QByteArray out;
+
+ if (m_inputFormat == m_outputFormat) {
+ QWindowsIUPointer<IMFMediaBuffer> outputBuffer;
+ sample->ConvertToContiguousBuffer(outputBuffer.address());
+ DWORD len = 0;
+ BYTE *data = nullptr;
+ hr = outputBuffer->Lock(&data, nullptr, &len);
+ if (SUCCEEDED(hr))
+ out.push_back(QByteArray(reinterpret_cast<char *>(data), len));
+ outputBuffer->Unlock();
+
+ } else {
+ Q_ASSERT(m_resampler);
+
+ hr = m_resampler->ProcessInput(m_inputStreamID, sample, 0);
+ if (SUCCEEDED(hr))
+ hr = processOutput(out);
+
+ if (FAILED(hr))
+ qCWarning(qLcAudioResampler) << "Resampling failed" << hr;
+ }
+
+ m_totalOutputBytes += out.size();
+
+ return out;
+}
+
+bool QWindowsResampler::setup(const QAudioFormat &fin, const QAudioFormat &fout)
+{
+ qCDebug(qLcAudioResampler) << "Setup audio resampler" << fin << "->" << fout;
+
+ m_totalInputBytes = 0;
+ m_totalOutputBytes = 0;
+
+ if (fin == fout) {
+ qCDebug(qLcAudioResampler) << "Pass through mode";
+ m_inputFormat = fin;
+ m_outputFormat = fout;
+ return true;
+ }
+
+ Q_ASSERT(m_resampler);
+
+ QWindowsIUPointer<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(fin);
+ QWindowsIUPointer<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(fout);
+
+ HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.get(), 0);
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioResampler) << "Failed to set input type" << hr;
+ return false;
+ }
+
+ hr = m_resampler->SetOutputType(0, mout.get(), 0);
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioResampler) << "Failed to set output type" << hr;
+ return false;
+ }
+
+ MFT_OUTPUT_STREAM_INFO streamInfo;
+ hr = m_resampler->GetOutputStreamInfo(0, &streamInfo);
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioResampler) << "Could not obtain stream info" << hr;
+ return false;
+ }
+
+ m_resamplerNeedsSampleBuffer = (streamInfo.dwFlags
+ & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0;
+
+ m_inputFormat = fin;
+ m_outputFormat = fout;
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsresampler_p.h b/src/multimedia/windows/qwindowsresampler_p.h
new file mode 100644
index 000000000..ce2be58ac
--- /dev/null
+++ b/src/multimedia/windows/qwindowsresampler_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QT_QWINDOWSRESAMPLER_H
+#define QT_QWINDOWSRESAMPLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qbytearray.h>
+#include <qbytearrayview.h>
+#include <qaudioformat.h>
+#include <private/qwindowsiupointer_p.h>
+#include <qt_windows.h>
+#include <mftransform.h>
+
+struct IMFSample;
+struct IMFTransform;
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QWindowsResampler
+{
+public:
+ QWindowsResampler();
+ ~QWindowsResampler();
+
+ bool setup(const QAudioFormat &in, const QAudioFormat &out);
+
+ QByteArray resample(const QByteArrayView &in);
+ QByteArray resample(IMFSample *sample);
+
+ QAudioFormat inputFormat() const { return m_inputFormat; }
+ QAudioFormat outputFormat() const { return m_outputFormat; }
+
+ quint64 outputBufferSize(quint64 inputBufferSize) const;
+ quint64 inputBufferSize(quint64 outputBufferSize) const;
+
+ quint64 totalInputBytes() const { return m_totalInputBytes; }
+ quint64 totalOutputBytes() const { return m_totalOutputBytes; }
+
+private:
+ HRESULT processInput(const QByteArrayView &in);
+ HRESULT processOutput(QByteArray &out);
+
+ QWindowsIUPointer<IMFTransform> m_resampler;
+
+ bool m_resamplerNeedsSampleBuffer = false;
+ quint64 m_totalInputBytes = 0;
+ quint64 m_totalOutputBytes = 0;
+ QAudioFormat m_inputFormat;
+ QAudioFormat m_outputFormat;
+
+ DWORD m_inputStreamID = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_QWINDOWSRESAMPLER_H