Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwindowsresampler.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7#include <qloggingcategory.h>
8#include <QUuid>
9
10#include <wmcodecdsp.h>
11#include <mftransform.h>
12#include <mferror.h>
13
15
16QUuid qIID_IMFTransform(0xbf94c121, 0x5b05, 0x4e6f, 0x80,0x00, 0xba,0x59,0x89,0x61,0x41,0x4d);
17QUuid qCLSID_CResamplerMediaObject("f447b69e-1884-4a7e-8055-346f74d6edb3");
18
19static Q_LOGGING_CATEGORY(qLcAudioResampler, "qt.multimedia.audioresampler")
20
22 : m_wmf(QWindowsMediaFoundation::instance())
23{
24 HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
25 if (hr == RPC_E_CHANGED_MODE)
26 qWarning() << "Failed attempt to change apartment concurrency model";
27
28 CoCreateInstance(qCLSID_CResamplerMediaObject, nullptr, CLSCTX_INPROC_SERVER,
29 qIID_IMFTransform, (LPVOID*)(m_resampler.GetAddressOf()));
30 if (m_resampler)
31 m_resampler->AddInputStreams(1, &m_inputStreamID);
32}
33
35{
36 CoUninitialize();
37};
38
40{
41 if (m_inputFormat.isValid() && m_outputFormat.isValid())
42 return m_outputFormat.bytesForDuration(m_inputFormat.durationForBytes(inputBufferSize));
43 else
44 return 0;
45}
46
48{
49 if (m_inputFormat.isValid() && m_outputFormat.isValid())
50 return m_inputFormat.bytesForDuration(m_outputFormat.durationForBytes(outputBufferSize));
51 else
52 return 0;
53}
54
55HRESULT QWindowsResampler::processInput(const QByteArrayView &in)
56{
57 ComPtr<IMFSample> sample;
58 HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
59 if (FAILED(hr))
60 return hr;
61
62 ComPtr<IMFMediaBuffer> buffer;
63 hr = m_wmf->mfCreateMemoryBuffer(in.size(), buffer.GetAddressOf());
64 if (FAILED(hr))
65 return hr;
66
67 BYTE *data = nullptr;
68 DWORD maxLen = 0;
69 DWORD currentLen = 0;
70 hr = buffer->Lock(&data, &maxLen, &currentLen);
71 if (FAILED(hr))
72 return hr;
73
74 memcpy(data, in.data(), in.size());
75
76 hr = buffer->Unlock();
77 if (FAILED(hr))
78 return hr;
79
80 hr = buffer->SetCurrentLength(in.size());
81 if (FAILED(hr))
82 return hr;
83
84 hr = sample->AddBuffer(buffer.Get());
85 if (FAILED(hr))
86 return hr;
87
88 return m_resampler->ProcessInput(m_inputStreamID, sample.Get(), 0);
89}
90
91HRESULT QWindowsResampler::processOutput(QByteArray &out)
92{
93 ComPtr<IMFSample> sample;
94 ComPtr<IMFMediaBuffer> buffer;
95
96 if (m_resamplerNeedsSampleBuffer) {
97 HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
98 if (FAILED(hr))
99 return hr;
100
101 auto expectedOutputSize = outputBufferSize(m_totalInputBytes) - m_totalOutputBytes;
102 hr = m_wmf->mfCreateMemoryBuffer(expectedOutputSize, buffer.GetAddressOf());
103 if (FAILED(hr))
104 return hr;
105
106 hr = sample->AddBuffer(buffer.Get());
107 if (FAILED(hr))
108 return hr;
109 }
110
111 HRESULT hr = S_OK;
112
113 MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
114 outputDataBuffer.dwStreamID = 0;
115 do {
116 outputDataBuffer.pEvents = nullptr;
117 outputDataBuffer.dwStatus = 0;
118 outputDataBuffer.pSample = m_resamplerNeedsSampleBuffer ? sample.Get() : nullptr;
119 DWORD status = 0;
120 hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
121 if (SUCCEEDED(hr)) {
122 ComPtr<IMFMediaBuffer> outputBuffer;
123 outputDataBuffer.pSample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
124 DWORD len = 0;
125 BYTE *data = nullptr;
126 hr = outputBuffer->Lock(&data, nullptr, &len);
127 if (SUCCEEDED(hr))
128 out.push_back(QByteArray(reinterpret_cast<char *>(data), len));
129 outputBuffer->Unlock();
130 }
131 } while (SUCCEEDED(hr));
132
133 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
134 hr = S_OK;
135
136 return hr;
137}
138
140{
141 m_totalInputBytes += in.size();
142
143 if (m_inputFormat == m_outputFormat) {
144 m_totalOutputBytes += in.size();
145 return {in.data(), in.size()};
146
147 } else {
148 Q_ASSERT(m_resampler && m_wmf);
149
151 HRESULT hr = processInput(in);
152 if (SUCCEEDED(hr))
153 hr = processOutput(out);
154
155 if (FAILED(hr))
156 qCWarning(qLcAudioResampler) << "Resampling failed" << hr;
157
158 m_totalOutputBytes += out.size();
159 return out;
160 }
161}
162
164{
165 Q_ASSERT(sample);
166
167 DWORD totalLength = 0;
168 HRESULT hr = sample->GetTotalLength(&totalLength);
169 if (FAILED(hr))
170 return {};
171
172 m_totalInputBytes += totalLength;
173
175
176 if (m_inputFormat == m_outputFormat) {
177 ComPtr<IMFMediaBuffer> outputBuffer;
178 sample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
179 DWORD len = 0;
180 BYTE *data = nullptr;
181 hr = outputBuffer->Lock(&data, nullptr, &len);
182 if (SUCCEEDED(hr))
183 out.push_back(QByteArray(reinterpret_cast<char *>(data), len));
184 outputBuffer->Unlock();
185
186 } else {
187 Q_ASSERT(m_resampler && m_wmf);
188
189 hr = m_resampler->ProcessInput(m_inputStreamID, sample, 0);
190 if (SUCCEEDED(hr))
191 hr = processOutput(out);
192
193 if (FAILED(hr))
194 qCWarning(qLcAudioResampler) << "Resampling failed" << hr;
195 }
196
197 m_totalOutputBytes += out.size();
198
199 return out;
200}
201
203{
204 qCDebug(qLcAudioResampler) << "Setup audio resampler" << fin << "->" << fout;
205
206 m_totalInputBytes = 0;
207 m_totalOutputBytes = 0;
208
209 if (fin == fout) {
210 qCDebug(qLcAudioResampler) << "Pass through mode";
211 m_inputFormat = fin;
212 m_outputFormat = fout;
213 return true;
214 }
215
216 if (!m_resampler || !m_wmf)
217 return false;
218
219 ComPtr<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(*m_wmf, fin);
220 ComPtr<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(*m_wmf, fout);
221
222 HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.Get(), 0);
223 if (FAILED(hr)) {
224 qCWarning(qLcAudioResampler) << "Failed to set input type" << hr;
225 return false;
226 }
227
228 hr = m_resampler->SetOutputType(0, mout.Get(), 0);
229 if (FAILED(hr)) {
230 qCWarning(qLcAudioResampler) << "Failed to set output type" << hr;
231 return false;
232 }
233
234 MFT_OUTPUT_STREAM_INFO streamInfo;
235 hr = m_resampler->GetOutputStreamInfo(0, &streamInfo);
236 if (FAILED(hr)) {
237 qCWarning(qLcAudioResampler) << "Could not obtain stream info" << hr;
238 return false;
239 }
240
241 m_resamplerNeedsSampleBuffer = (streamInfo.dwFlags
242 & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0;
243
244 m_inputFormat = fin;
245 m_outputFormat = fout;
246
247 return true;
248}
249
The QAudioFormat class stores audio stream parameter information.
Q_MULTIMEDIA_EXPORT qint32 bytesForDuration(qint64 microseconds) const
Returns the number of bytes required for this audio format for microseconds.
Q_MULTIMEDIA_EXPORT qint64 durationForBytes(qint32 byteCount) const
Returns the number of microseconds represented by bytes in this format.
constexpr bool isValid() const noexcept
Returns true if all of the parameters are valid.
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
Definition quuid.h:31
decltype(&::MFCreateMemoryBuffer) mfCreateMemoryBuffer
decltype(&::MFCreateSample) mfCreateSample
quint64 outputBufferSize(quint64 inputBufferSize) const
bool setup(const QAudioFormat &in, const QAudioFormat &out)
QByteArray resample(const QByteArrayView &in)
quint64 inputBufferSize(quint64 outputBufferSize) const
Combined button and popup list for selecting options.
ComPtr< IMFMediaType > formatToMediaType(QWindowsMediaFoundation &, const QAudioFormat &format)
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLsizei len
GLuint in
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned long long quint64
Definition qtypes.h:56
long HRESULT
QUuid qCLSID_CResamplerMediaObject("f447b69e-1884-4a7e-8055-346f74d6edb3")
QT_BEGIN_NAMESPACE QUuid qIID_IMFTransform(0xbf94c121, 0x5b05, 0x4e6f, 0x80, 0x00, 0xba, 0x59, 0x89, 0x61, 0x41, 0x4d)
QTextStream out(stdout)
[7]