6#include <QtCore/qloggingcategory.h>
7#include <QtCore/private/qsystemerror_p.h>
8#include <QtMultimedia/private/qaudio_alignment_support_p.h>
9#include <QtMultimedia/private/qwindowsaudioutils_p.h>
10#include <QtMultimedia/private/qwmf_support_p.h>
12#include <wmcodecdsp.h>
13#include <mftransform.h>
18Q_STATIC_LOGGING_CATEGORY(qLcAudioResampler,
"qt.multimedia.audioresampler");
22HRESULT replaceBuffer(
const ComPtr<IMFSample> &sample,
const ComPtr<IMFMediaBuffer> &buffer)
24 HRESULT hr = sample->RemoveAllBuffers();
28 return sample->AddBuffer(buffer.Get());
33bool QWindowsResampler::isAvailable()
35 return QWindowsMediaFoundation::instance();
38QWindowsResampler::QWindowsResampler()
40 CoCreateInstance(__uuidof(CResamplerMediaObject),
nullptr, CLSCTX_INPROC_SERVER,
41 IID_PPV_ARGS(&m_resampler));
43 m_resampler->AddInputStreams(1, &m_inputStreamID);
45 for (ComPtr<IMFSample> &sample : { std::ref(m_inputSample), std::ref(m_outputSample) }) {
46 HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
48 qCWarning(qLcAudioResampler) <<
"Failed to create sample for resampling:"
49 << QSystemError::windowsComString(hr);
50 m_resampler =
nullptr;
56QWindowsResampler::~QWindowsResampler() =
default;
58quint64 QWindowsResampler::outputBufferSize(quint64 inputBufferSize)
const
60 if (m_inputFormat.isValid() && m_outputFormat.isValid())
61 return m_outputFormat.bytesForDuration(m_inputFormat.durationForBytes(inputBufferSize));
66quint64 QWindowsResampler::inputBufferSize(quint64 outputBufferSize)
const
68 if (m_inputFormat.isValid() && m_outputFormat.isValid())
69 return m_inputFormat.bytesForDuration(m_outputFormat.durationForBytes(outputBufferSize));
74qsizetype QWindowsResampler::overAllocatedOutputBufferSize()
76 auto expectedOutputSize = outputBufferSize(m_totalInputBytes) - m_totalOutputBytes;
78 expectedOutputSize += m_outputFormat.bytesForDuration(10000);
79 expectedOutputSize = QtMultimediaPrivate::alignUp(expectedOutputSize, 1024);
80 return expectedOutputSize;
83template <
typename Functor>
84auto QWindowsResampler::processOutput(ComPtr<IMFMediaBuffer> buffer, Functor &&f)
85 -> std::invoke_result_t<Functor,
const ComPtr<IMFMediaBuffer> &>
87 HRESULT hr = replaceBuffer(m_outputSample, buffer);
89 return q23::unexpected{ hr };
91 MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
92 outputDataBuffer.dwStreamID = 0;
93 outputDataBuffer.pEvents =
nullptr;
94 outputDataBuffer.dwStatus = 0;
95 outputDataBuffer.pSample = m_outputSample.Get();
97 hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
99 return q23::unexpected{ hr };
104q23::expected<QByteArray, HRESULT> QWindowsResampler::processOutput()
106 using namespace QWMF;
108 ComPtr<IMFMediaBuffer> buffer;
109 HRESULT hr = QByteArrayMFMediaBuffer::CreateInstance(overAllocatedOutputBufferSize(),
110 buffer.GetAddressOf());
112 return q23::unexpected{ hr };
114 return processOutput(std::move(buffer), [&](
const ComPtr<IMFMediaBuffer> &buffer) {
115 return withLockedBuffer(buffer, [&](QSpan<BYTE> data, QSpan<BYTE> ) {
116 auto *byteArrayBuffer =
static_cast<QByteArrayMFMediaBuffer *>(buffer.Get());
117 QByteArray out = byteArrayBuffer->takeByteArray();
118 out.truncate(data.size());
124QByteArray QWindowsResampler::resample(QByteArray in)
126 m_totalInputBytes += in.size();
128 if (m_inputFormat == m_outputFormat) {
129 m_totalOutputBytes += in.size();
133 Q_ASSERT(m_resampler && m_wmf);
136 ComPtr<IMFMediaBuffer> buffer;
138 QWMF::QByteArrayMFMediaBuffer::CreateInstance(std::move(in), buffer.GetAddressOf(),
141 HRESULT hr = replaceBuffer(m_inputSample, buffer);
145 hr = m_resampler->ProcessInput(m_inputStreamID, m_inputSample.Get(), 0);
147 qCWarning(qLcAudioResampler)
148 <<
"Failed to process input" << QSystemError::windowsComString(hr);
153 auto result = processOutput();
155 m_totalOutputBytes += result.value().size();
156 return result.value();
159 qCWarning(qLcAudioResampler) <<
"Resampling failed"
160 << QSystemError::windowsComString(result.error());
164QByteArray QWindowsResampler::resample(
const QByteArrayView &in)
166 return QWindowsResampler::resample(QByteArray(in));
169QByteArray QWindowsResampler::resample(
const ComPtr<IMFSample> &sample)
171 using namespace QWMF;
175 DWORD totalLength = 0;
176 HRESULT hr = sample->GetTotalLength(&totalLength);
180 m_totalInputBytes += totalLength;
182 if (m_inputFormat == m_outputFormat) {
183 ComPtr<IMFMediaBuffer> outputBuffer;
184 sample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
186 auto result = withLockedBuffer(outputBuffer, [&](QSpan<BYTE> data, QSpan<BYTE> ) {
187 return QByteArray(data);
190 m_totalOutputBytes += result.value().size();
191 return result.value();
193 qCWarning(qLcAudioResampler) <<
"Failed to convert sample to contiguous buffer"
194 << QSystemError::windowsComString(result.error());
198 Q_ASSERT(m_resampler && m_wmf);
201 hr = m_resampler->ProcessInput(m_inputStreamID, sample.Get(), 0);
203 qCWarning(qLcAudioResampler)
204 <<
"Failed to process input sample" << QSystemError::windowsComString(hr);
209 auto result = processOutput();
211 m_totalOutputBytes += result.value().size();
212 return result.value();
214 qCWarning(qLcAudioResampler) <<
"Resampling failed" << QSystemError::windowsComString(hr);
218QAudioBuffer QWindowsResampler::resample(
const char *data, size_t size)
220 quint64 elapsedBytesAtStart = m_totalOutputBytes;
222 QByteArray resampled = resample(QSpan{
223 reinterpret_cast<
const std::byte *>(data),
227 if (resampled.isEmpty())
231 std::move(resampled),
233 m_outputFormat.durationForBytes(elapsedBytesAtStart) + m_startTimeOffset.count(),
237std::pmr::vector<std::byte> QWindowsResampler::resample(QSpan<
const std::byte> in,
238 std::pmr::memory_resource *mr)
240 using namespace QWMF;
242 m_totalInputBytes += in.size_bytes();
243 if (m_inputFormat == m_outputFormat) {
244 m_totalOutputBytes += in.size();
245 return std::pmr::vector<std::byte>{ in.begin(), in.end(), mr };
249 ComPtr<IMFMediaBuffer> inputBuffer;
250 HRESULT hr = QPmrMediaBuffer::CreateInstance(in, mr, inputBuffer.GetAddressOf());
254 hr = replaceBuffer(m_inputSample, inputBuffer);
258 hr = m_resampler->ProcessInput(m_inputStreamID, m_inputSample.Get(), 0);
263 ComPtr<IMFMediaBuffer> outputBuffer;
264 hr = QPmrMediaBuffer::CreateInstance(overAllocatedOutputBufferSize(), mr,
265 outputBuffer.GetAddressOf());
267 qCWarning(qLcAudioResampler)
268 <<
"Failed to create output buffer" << QSystemError::windowsComString(hr);
272 auto result = processOutput(std::move(outputBuffer), [&](
const ComPtr<IMFMediaBuffer> &buffer) {
273 return withLockedBuffer(buffer, [&](QSpan<BYTE> data, QSpan<BYTE> ) {
274 return std::pmr::vector<std::byte>{
275 reinterpret_cast<std::byte *>(data.begin()),
276 reinterpret_cast<std::byte *>(data.end()),
283 qCWarning(qLcAudioResampler)
284 <<
"Resampling failed" << QSystemError::windowsComString(result.error());
287 m_totalOutputBytes += result->size();
288 return result.value();
291bool QWindowsResampler::setup(
const QAudioFormat &fin,
const QAudioFormat &fout)
293 qCDebug(qLcAudioResampler) <<
"Setup audio resampler" << fin <<
"->" << fout;
295 m_totalInputBytes = 0;
296 m_totalOutputBytes = 0;
299 qCDebug(qLcAudioResampler) <<
"Pass through mode";
301 m_outputFormat = fout;
305 if (!m_resampler || !m_wmf)
308 ComPtr<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(*m_wmf, fin);
309 ComPtr<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(*m_wmf, fout);
311 HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.Get(), 0);
313 qCWarning(qLcAudioResampler)
314 <<
"Failed to set input type" << QSystemError::windowsComString(hr);
318 hr = m_resampler->SetOutputType(0, mout.Get(), 0);
320 qCWarning(qLcAudioResampler)
321 <<
"Failed to set output type" << QSystemError::windowsComString(hr);
325 MFT_OUTPUT_STREAM_INFO streamInfo;
326 hr = m_resampler->GetOutputStreamInfo(0, &streamInfo);
328 qCWarning(qLcAudioResampler)
329 <<
"Could not obtain stream info" << QSystemError::windowsComString(hr);
334 m_outputFormat = fout;
339void QWindowsResampler::setStartTimeOffset(std::chrono::microseconds startTime)
341 m_startTimeOffset = startTime;