Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
6#include <qloggingcategory.h>
7#include <QUuid>
8
9#include <wmcodecdsp.h>
10#include <mftransform.h>
11#include <mferror.h>
12
14
15QUuid qIID_IMFTransform(0xbf94c121, 0x5b05, 0x4e6f, 0x80,0x00, 0xba,0x59,0x89,0x61,0x41,0x4d);
16QUuid qCLSID_CResamplerMediaObject("f447b69e-1884-4a7e-8055-346f74d6edb3");
17
18static Q_LOGGING_CATEGORY(qLcAudioResampler, "qt.multimedia.audioresampler")
19
21{
22 CoCreateInstance(qCLSID_CResamplerMediaObject, nullptr, CLSCTX_INPROC_SERVER,
23 qIID_IMFTransform, (LPVOID*)(m_resampler.GetAddressOf()));
24 if (m_resampler)
25 m_resampler->AddInputStreams(1, &m_inputStreamID);
26}
27
29
31{
32 if (m_inputFormat.isValid() && m_outputFormat.isValid())
33 return m_outputFormat.bytesForDuration(m_inputFormat.durationForBytes(inputBufferSize));
34 else
35 return 0;
36}
37
39{
40 if (m_inputFormat.isValid() && m_outputFormat.isValid())
41 return m_inputFormat.bytesForDuration(m_outputFormat.durationForBytes(outputBufferSize));
42 else
43 return 0;
44}
45
46HRESULT QWindowsResampler::processInput(const QByteArrayView &in)
47{
48 ComPtr<IMFSample> sample;
49 HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
50 if (FAILED(hr))
51 return hr;
52
53 ComPtr<IMFMediaBuffer> buffer;
54 hr = m_wmf->mfCreateMemoryBuffer(in.size(), buffer.GetAddressOf());
55 if (FAILED(hr))
56 return hr;
57
58 BYTE *data = nullptr;
59 DWORD maxLen = 0;
60 DWORD currentLen = 0;
61 hr = buffer->Lock(&data, &maxLen, &currentLen);
62 if (FAILED(hr))
63 return hr;
64
65 memcpy(data, in.data(), in.size());
66
67 hr = buffer->Unlock();
68 if (FAILED(hr))
69 return hr;
70
71 hr = buffer->SetCurrentLength(in.size());
72 if (FAILED(hr))
73 return hr;
74
75 hr = sample->AddBuffer(buffer.Get());
76 if (FAILED(hr))
77 return hr;
78
79 return m_resampler->ProcessInput(m_inputStreamID, sample.Get(), 0);
80}
81
82HRESULT QWindowsResampler::processOutput(QByteArray &out)
83{
84 ComPtr<IMFSample> sample;
85 ComPtr<IMFMediaBuffer> buffer;
86
87 if (m_resamplerNeedsSampleBuffer) {
88 HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
89 if (FAILED(hr))
90 return hr;
91
92 auto expectedOutputSize = outputBufferSize(m_totalInputBytes) - m_totalOutputBytes;
93 hr = m_wmf->mfCreateMemoryBuffer(expectedOutputSize, buffer.GetAddressOf());
94 if (FAILED(hr))
95 return hr;
96
97 hr = sample->AddBuffer(buffer.Get());
98 if (FAILED(hr))
99 return hr;
100 }
101
102 HRESULT hr = S_OK;
103
104 MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
105 outputDataBuffer.dwStreamID = 0;
106 do {
107 outputDataBuffer.pEvents = nullptr;
108 outputDataBuffer.dwStatus = 0;
109 outputDataBuffer.pSample = m_resamplerNeedsSampleBuffer ? sample.Get() : nullptr;
110 DWORD status = 0;
111 hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
112 if (SUCCEEDED(hr)) {
113 ComPtr<IMFMediaBuffer> outputBuffer;
114 outputDataBuffer.pSample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
115 DWORD len = 0;
116 BYTE *data = nullptr;
117 hr = outputBuffer->Lock(&data, nullptr, &len);
118 if (SUCCEEDED(hr))
119 out.push_back(QByteArray(reinterpret_cast<char *>(data), len));
120 outputBuffer->Unlock();
121 }
122 } while (SUCCEEDED(hr));
123
124 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
125 hr = S_OK;
126
127 return hr;
128}
129
131{
132 m_totalInputBytes += in.size();
133
134 if (m_inputFormat == m_outputFormat) {
135 m_totalOutputBytes += in.size();
136 return {in.data(), in.size()};
137
138 } else {
139 Q_ASSERT(m_resampler && m_wmf);
140
142 HRESULT hr = processInput(in);
143 if (SUCCEEDED(hr))
144 hr = processOutput(out);
145
146 if (FAILED(hr))
147 qCWarning(qLcAudioResampler) << "Resampling failed" << hr;
148
149 m_totalOutputBytes += out.size();
150 return out;
151 }
152}
153
155{
156 Q_ASSERT(sample);
157
158 DWORD totalLength = 0;
159 HRESULT hr = sample->GetTotalLength(&totalLength);
160 if (FAILED(hr))
161 return {};
162
163 m_totalInputBytes += totalLength;
164
166
167 if (m_inputFormat == m_outputFormat) {
168 ComPtr<IMFMediaBuffer> outputBuffer;
169 sample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
170 DWORD len = 0;
171 BYTE *data = nullptr;
172 hr = outputBuffer->Lock(&data, nullptr, &len);
173 if (SUCCEEDED(hr))
174 out.push_back(QByteArray(reinterpret_cast<char *>(data), len));
175 outputBuffer->Unlock();
176
177 } else {
178 Q_ASSERT(m_resampler && m_wmf);
179
180 hr = m_resampler->ProcessInput(m_inputStreamID, sample, 0);
181 if (SUCCEEDED(hr))
182 hr = processOutput(out);
183
184 if (FAILED(hr))
185 qCWarning(qLcAudioResampler) << "Resampling failed" << hr;
186 }
187
188 m_totalOutputBytes += out.size();
189
190 return out;
191}
192
194{
195 qCDebug(qLcAudioResampler) << "Setup audio resampler" << fin << "->" << fout;
196
197 m_totalInputBytes = 0;
198 m_totalOutputBytes = 0;
199
200 if (fin == fout) {
201 qCDebug(qLcAudioResampler) << "Pass through mode";
202 m_inputFormat = fin;
203 m_outputFormat = fout;
204 return true;
205 }
206
207 if (!m_resampler || !m_wmf)
208 return false;
209
210 ComPtr<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(*m_wmf, fin);
211 ComPtr<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(*m_wmf, fout);
212
213 HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.Get(), 0);
214 if (FAILED(hr)) {
215 qCWarning(qLcAudioResampler) << "Failed to set input type" << hr;
216 return false;
217 }
218
219 hr = m_resampler->SetOutputType(0, mout.Get(), 0);
220 if (FAILED(hr)) {
221 qCWarning(qLcAudioResampler) << "Failed to set output type" << hr;
222 return false;
223 }
224
225 MFT_OUTPUT_STREAM_INFO streamInfo;
226 hr = m_resampler->GetOutputStreamInfo(0, &streamInfo);
227 if (FAILED(hr)) {
228 qCWarning(qLcAudioResampler) << "Could not obtain stream info" << hr;
229 return false;
230 }
231
232 m_resamplerNeedsSampleBuffer = (streamInfo.dwFlags
233 & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0;
234
235 m_inputFormat = fin;
236 m_outputFormat = fout;
237
238 return true;
239}
240
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)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLuint in
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned long long quint64
Definition qtypes.h:61
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]