6#include <QtCore/qloggingcategory.h>
7#include <QtCore/private/qfunctions_win_p.h>
8#include <QtCore/private/qsystemerror_p.h>
9#include <QtMultimedia/private/qaudio_alignment_support_p.h>
10#include <QtMultimedia/private/qwindowsaudioutils_p.h>
11#include <QtMultimedia/private/qwmf_support_p.h>
13#include <wmcodecdsp.h>
14#include <mftransform.h>
19Q_STATIC_LOGGING_CATEGORY(qLcAudioResampler,
"qt.multimedia.audioresampler");
23HRESULT replaceBuffer(
const ComPtr<IMFSample> &sample,
const ComPtr<IMFMediaBuffer> &buffer)
25 HRESULT hr = sample->RemoveAllBuffers();
29 return sample->AddBuffer(buffer.Get());
34bool QWindowsResampler::isAvailable()
36 return QWindowsMediaFoundation::instance();
39QWindowsResampler::QWindowsResampler()
41 qt_win_ensureComInitializedOnThisThread();
43 CoCreateInstance(__uuidof(CResamplerMediaObject),
nullptr, CLSCTX_INPROC_SERVER,
44 IID_PPV_ARGS(&m_resampler));
46 m_resampler->AddInputStreams(1, &m_inputStreamID);
48 for (ComPtr<IMFSample> &sample : { std::ref(m_inputSample), std::ref(m_outputSample) }) {
49 HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
51 qCWarning(qLcAudioResampler) <<
"Failed to create sample for resampling:"
52 << QSystemError::windowsComString(hr);
53 m_resampler =
nullptr;
59QWindowsResampler::~QWindowsResampler() =
default;
61quint64 QWindowsResampler::outputBufferSize(quint64 inputBufferSize)
const
63 if (m_inputFormat.isValid() && m_outputFormat.isValid())
64 return m_outputFormat.bytesForDuration(m_inputFormat.durationForBytes(inputBufferSize));
69quint64 QWindowsResampler::inputBufferSize(quint64 outputBufferSize)
const
71 if (m_inputFormat.isValid() && m_outputFormat.isValid())
72 return m_inputFormat.bytesForDuration(m_outputFormat.durationForBytes(outputBufferSize));
77qsizetype QWindowsResampler::overAllocatedOutputBufferSize()
79 auto expectedOutputSize = outputBufferSize(m_totalInputBytes) - m_totalOutputBytes;
81 expectedOutputSize += m_outputFormat.bytesForDuration(10000);
82 expectedOutputSize = QtMultimediaPrivate::alignUp(expectedOutputSize, 1024);
83 return expectedOutputSize;
86template <
typename Functor>
87auto QWindowsResampler::processOutput(ComPtr<IMFMediaBuffer> buffer, Functor &&f)
88 -> std::invoke_result_t<Functor,
const ComPtr<IMFMediaBuffer> &>
90 HRESULT hr = replaceBuffer(m_outputSample, buffer);
92 return q23::unexpected{ hr };
94 MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
95 outputDataBuffer.dwStreamID = 0;
96 outputDataBuffer.pEvents =
nullptr;
97 outputDataBuffer.dwStatus = 0;
98 outputDataBuffer.pSample = m_outputSample.Get();
100 hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
102 return q23::unexpected{ hr };
107q23::expected<QByteArray, HRESULT> QWindowsResampler::processOutput()
109 using namespace QWMF;
111 ComPtr<IMFMediaBuffer> buffer;
112 HRESULT hr = QByteArrayMFMediaBuffer::CreateInstance(overAllocatedOutputBufferSize(),
113 buffer.GetAddressOf());
115 return q23::unexpected{ hr };
117 return processOutput(std::move(buffer), [&](
const ComPtr<IMFMediaBuffer> &buffer) {
118 return withLockedBuffer(buffer, [&](QSpan<BYTE> data, QSpan<BYTE> ) {
119 auto *byteArrayBuffer =
static_cast<QByteArrayMFMediaBuffer *>(buffer.Get());
120 QByteArray out = byteArrayBuffer->takeByteArray();
121 out.truncate(data.size());
127QByteArray QWindowsResampler::resample(QByteArray in)
129 m_totalInputBytes += in.size();
131 if (m_inputFormat == m_outputFormat) {
132 m_totalOutputBytes += in.size();
136 Q_ASSERT(m_resampler && m_wmf);
139 ComPtr<IMFMediaBuffer> buffer;
141 QWMF::QByteArrayMFMediaBuffer::CreateInstance(std::move(in), buffer.GetAddressOf(),
144 HRESULT hr = replaceBuffer(m_inputSample, buffer);
148 hr = m_resampler->ProcessInput(m_inputStreamID, m_inputSample.Get(), 0);
150 qCWarning(qLcAudioResampler)
151 <<
"Failed to process input" << QSystemError::windowsComString(hr);
156 auto result = processOutput();
158 m_totalOutputBytes += result.value().size();
159 return result.value();
162 qCWarning(qLcAudioResampler) <<
"Resampling failed"
163 << QSystemError::windowsComString(result.error());
167QByteArray QWindowsResampler::resample(
const QByteArrayView &in)
169 return QWindowsResampler::resample(QByteArray(in));
172QByteArray QWindowsResampler::resample(
const ComPtr<IMFSample> &sample)
174 using namespace QWMF;
178 DWORD totalLength = 0;
179 HRESULT hr = sample->GetTotalLength(&totalLength);
183 m_totalInputBytes += totalLength;
185 if (m_inputFormat == m_outputFormat) {
186 ComPtr<IMFMediaBuffer> outputBuffer;
187 sample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
189 auto result = withLockedBuffer(outputBuffer, [&](QSpan<BYTE> data, QSpan<BYTE> ) {
190 return QByteArray(data);
193 m_totalOutputBytes += result.value().size();
194 return result.value();
196 qCWarning(qLcAudioResampler) <<
"Failed to convert sample to contiguous buffer"
197 << QSystemError::windowsComString(result.error());
201 Q_ASSERT(m_resampler && m_wmf);
204 hr = m_resampler->ProcessInput(m_inputStreamID, sample.Get(), 0);
206 qCWarning(qLcAudioResampler)
207 <<
"Failed to process input sample" << QSystemError::windowsComString(hr);
212 auto result = processOutput();
214 m_totalOutputBytes += result.value().size();
215 return result.value();
217 qCWarning(qLcAudioResampler) <<
"Resampling failed" << QSystemError::windowsComString(hr);
221QAudioBuffer QWindowsResampler::resample(
const char *data, size_t size)
223 quint64 elapsedBytesAtStart = m_totalOutputBytes;
225 QByteArray resampled = resample(QSpan{
226 reinterpret_cast<
const std::byte *>(data),
230 if (resampled.isEmpty())
234 std::move(resampled),
236 m_outputFormat.durationForBytes(elapsedBytesAtStart) + m_startTimeOffset.count(),
240std::pmr::vector<std::byte> QWindowsResampler::resample(QSpan<
const std::byte> in,
241 std::pmr::memory_resource *mr)
243 using namespace QWMF;
245 m_totalInputBytes += in.size_bytes();
246 if (m_inputFormat == m_outputFormat) {
247 m_totalOutputBytes += in.size();
248 return std::pmr::vector<std::byte>{ in.begin(), in.end(), mr };
252 ComPtr<IMFMediaBuffer> inputBuffer;
253 HRESULT hr = QPmrMediaBuffer::CreateInstance(in, mr, inputBuffer.GetAddressOf());
257 hr = replaceBuffer(m_inputSample, inputBuffer);
261 hr = m_resampler->ProcessInput(m_inputStreamID, m_inputSample.Get(), 0);
266 ComPtr<IMFMediaBuffer> outputBuffer;
267 hr = QPmrMediaBuffer::CreateInstance(overAllocatedOutputBufferSize(), mr,
268 outputBuffer.GetAddressOf());
270 qCWarning(qLcAudioResampler)
271 <<
"Failed to create output buffer" << QSystemError::windowsComString(hr);
275 auto result = processOutput(std::move(outputBuffer), [&](
const ComPtr<IMFMediaBuffer> &buffer) {
276 return withLockedBuffer(buffer, [&](QSpan<BYTE> data, QSpan<BYTE> ) {
277 return std::pmr::vector<std::byte>{
278 reinterpret_cast<std::byte *>(data.begin()),
279 reinterpret_cast<std::byte *>(data.end()),
286 qCWarning(qLcAudioResampler)
287 <<
"Resampling failed" << QSystemError::windowsComString(result.error());
290 m_totalOutputBytes += result->size();
291 return result.value();
294bool QWindowsResampler::setup(
const QAudioFormat &fin,
const QAudioFormat &fout)
296 qCDebug(qLcAudioResampler) <<
"Setup audio resampler" << fin <<
"->" << fout;
298 m_totalInputBytes = 0;
299 m_totalOutputBytes = 0;
302 qCDebug(qLcAudioResampler) <<
"Pass through mode";
304 m_outputFormat = fout;
308 if (!m_resampler || !m_wmf)
311 ComPtr<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(*m_wmf, fin);
312 ComPtr<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(*m_wmf, fout);
314 HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.Get(), 0);
316 qCWarning(qLcAudioResampler)
317 <<
"Failed to set input type" << QSystemError::windowsComString(hr);
321 hr = m_resampler->SetOutputType(0, mout.Get(), 0);
323 qCWarning(qLcAudioResampler)
324 <<
"Failed to set output type" << QSystemError::windowsComString(hr);
328 MFT_OUTPUT_STREAM_INFO streamInfo;
329 hr = m_resampler->GetOutputStreamInfo(0, &streamInfo);
331 qCWarning(qLcAudioResampler)
332 <<
"Could not obtain stream info" << QSystemError::windowsComString(hr);
337 m_outputFormat = fout;
342void QWindowsResampler::setStartTimeOffset(std::chrono::microseconds startTime)
344 m_startTimeOffset = startTime;
Combined button and popup list for selecting options.