summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Srebrny <piotr.srebrny@qt.io>2021-10-28 10:51:41 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-11-02 12:20:05 +0000
commite54fcdf04ad05fcdad0bfee1dc8b1d42a8edf4d9 (patch)
treeba4405a0d6195187dd91be56681ee52093ef01d4
parentfc25765df381e62b5785fab86d79a64aa6af8ea6 (diff)
downloadqtmultimedia-e54fcdf04ad05fcdad0bfee1dc8b1d42a8edf4d9.tar.gz
Refactor Windows QAudioDecoder to conform to tst_qaudiodecoderbackend
This is a cleanup of the QAudioDecoder backend for Windows. The patch enables the decoder to work without the resampler, if it could not be instantiated. It adds warning messages and error signals to notify a user about issues. The decoder passes tst_qaudiodecoderbackend with minor adjustments that will follow. Fix QWindowsIUPointer self copy operation. Change-Id: Ie962ac7615b957746f85dcc4ec49af38bbda2373 Reviewed-by: André de la Rocha <andre.rocha@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io> (cherry picked from commit 6d908018ec1e8295dbd29e5bc7602219ed9c0dac) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/multimedia/platform/windows/common/qwindowsiupointer_p.h10
-rw-r--r--src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp570
-rw-r--r--src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h57
-rw-r--r--src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp133
-rw-r--r--src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h31
5 files changed, 345 insertions, 456 deletions
diff --git a/src/multimedia/platform/windows/common/qwindowsiupointer_p.h b/src/multimedia/platform/windows/common/qwindowsiupointer_p.h
index a5c08ccc9..9941e110d 100644
--- a/src/multimedia/platform/windows/common/qwindowsiupointer_p.h
+++ b/src/multimedia/platform/windows/common/qwindowsiupointer_p.h
@@ -61,10 +61,12 @@ public:
~QWindowsIUPointer() { if (m_ptr) m_ptr->Release(); }
QWindowsIUPointer& operator=(const QWindowsIUPointer<T> &rhs) {
- if (m_ptr)
- m_ptr->Release();
- m_ptr = rhs.m_ptr;
- m_ptr->AddRef();
+ if (this != &rhs) {
+ if (m_ptr)
+ m_ptr->Release();
+ m_ptr = rhs.m_ptr;
+ m_ptr->AddRef();
+ }
return *this;
}
diff --git a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp b/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp
index 2ee98611c..e850a43d2 100644
--- a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp
+++ b/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp
@@ -37,272 +37,229 @@
**
****************************************************************************/
+#include <system_error>
+#include <mferror.h>
+#include <qglobal.h>
#include "Wmcodecdsp.h"
#include "mfaudiodecodercontrol_p.h"
-MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent)
- : QPlatformAudioDecoder(parent)
- , m_decoderSourceReader(new MFDecoderSourceReader)
- , m_sourceResolver(new SourceResolver)
- , m_resampler(0)
- , m_device(0)
- , m_mfInputStreamID(0)
- , m_mfOutputStreamID(0)
- , m_bufferReady(false)
- , m_duration(-1)
- , m_position(-1)
- , m_loadingSource(false)
- , m_mfOutputType(0)
- , m_convertSample(0)
- , m_sourceReady(false)
- , m_resamplerDirty(false)
+static QWindowsIUPointer<IMFMediaType> formatToMediaType(const QAudioFormat &format)
{
- CoCreateInstance(CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)(&m_resampler));
- if (!m_resampler) {
- qCritical("MFAudioDecoderControl: Failed to create resampler(CLSID_CResamplerMediaObject)!");
- return;
+ 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);
}
- m_resampler->AddInputStreams(1, &m_mfInputStreamID);
- connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady()));
- connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleMediaSourceError(long)));
- connect(m_decoderSourceReader, SIGNAL(finished()), this, SLOT(handleSourceFinished()));
+ 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);
- QAudioFormat defaultFormat;
- setAudioFormat(defaultFormat);
+ return mediaType;
}
-MFAudioDecoderControl::~MFAudioDecoderControl()
+static QAudioFormat mediaTypeToFormat(IMFMediaType *mediaType)
{
- if (m_mfOutputType)
- m_mfOutputType->Release();
- m_decoderSourceReader->shutdown();
- m_decoderSourceReader->Release();
- m_sourceResolver->Release();
- if (m_resampler)
- m_resampler->Release();
+ 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;
}
-QUrl MFAudioDecoderControl::source() const
+MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent)
+ , m_sourceResolver(new SourceResolver)
{
- return m_source;
+ CoCreateInstance(CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)(m_resampler.address()));
+ if (m_resampler)
+ m_resampler->AddInputStreams(1, &m_mfInputStreamID);
+ else
+ qWarning("MFAudioDecoderControl: Failed to create resampler(CLSID_CResamplerMediaObject)");
+
+ connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady()));
+ connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleMediaSourceError(long)));
}
-void MFAudioDecoderControl::onSourceCleared()
+MFAudioDecoderControl::~MFAudioDecoderControl()
{
- bool positionDirty = false;
- bool durationDirty = false;
- if (m_position != -1) {
- m_position = -1;
- positionDirty = true;
- }
- if (m_duration != -1) {
- m_duration = -1;
- durationDirty = true;
- }
- if (positionDirty)
- positionChanged(m_position);
- if (durationDirty)
- durationChanged(m_duration);
+ m_sourceResolver->shutdown();
+ m_sourceResolver->Release();
}
void MFAudioDecoderControl::setSource(const QUrl &fileName)
{
if (!m_device && m_source == fileName)
return;
- m_sourceReady = false;
+ stop();
m_sourceResolver->cancel();
- m_decoderSourceReader->setSource(nullptr, m_audioFormat);
- m_device = 0;
+ m_sourceResolver->shutdown();
+ m_device = nullptr;
m_source = fileName;
+ sourceChanged();
+
if (!m_source.isEmpty()) {
- m_sourceResolver->shutdown();
m_sourceResolver->load(m_source, 0);
m_loadingSource = true;
- } else {
- onSourceCleared();
}
- sourceChanged();
-}
-
-QIODevice* MFAudioDecoderControl::sourceDevice() const
-{
- return m_device;
}
void MFAudioDecoderControl::setSourceDevice(QIODevice *device)
{
if (m_device == device && m_source.isEmpty())
return;
- m_sourceReady = false;
+ stop();
m_sourceResolver->cancel();
- m_decoderSourceReader->setSource(nullptr, m_audioFormat);
+ m_sourceResolver->shutdown();
m_source.clear();
m_device = device;
+ sourceChanged();
+
if (m_device) {
- m_sourceResolver->shutdown();
- m_sourceResolver->load(QUrl(), m_device);
- m_loadingSource = true;
- } else {
- onSourceCleared();
+ if (m_device->isOpen() && m_device->isReadable()) {
+ m_sourceResolver->load(QUrl(), m_device);
+ m_loadingSource = true;
+ }
}
- sourceChanged();
}
-void MFAudioDecoderControl::updateResamplerOutputType()
+HRESULT MFAudioDecoderControl::setResamplerOutputType(IMFMediaType *outputType)
{
- m_resamplerDirty = false;
- HRESULT hr = m_resampler->SetOutputType(m_mfOutputStreamID, m_mfOutputType, 0);
- if (SUCCEEDED(hr)) {
- MFT_OUTPUT_STREAM_INFO streamInfo;
- m_resampler->GetOutputStreamInfo(m_mfOutputStreamID, &streamInfo);
- if ((streamInfo.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
- //if resampler does not allocate output sample memory, we do it here
- if (m_convertSample) {
- m_convertSample->Release();
- m_convertSample = 0;
- }
- if (SUCCEEDED(MFCreateSample(&m_convertSample))) {
- IMFMediaBuffer *mbuf = 0;;
- if (SUCCEEDED(MFCreateMemoryBuffer(streamInfo.cbSize, &mbuf))) {
- m_convertSample->AddBuffer(mbuf);
- mbuf->Release();
- }
- }
+ Q_ASSERT(m_resampler);
+
+ HRESULT hr = m_resampler->SetOutputType(m_mfOutputStreamID, outputType, 0);
+ if (FAILED(hr))
+ return hr;
+
+ MFT_OUTPUT_STREAM_INFO streamInfo;
+ m_resampler->GetOutputStreamInfo(m_mfOutputStreamID, &streamInfo);
+ if ((streamInfo.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
+ //if resampler does not allocate output sample memory, we do it here
+ m_convertSample.reset();
+ hr = MFCreateSample(m_convertSample.address());
+ if (FAILED(hr))
+ return hr;
+
+ QWindowsIUPointer<IMFMediaBuffer> buffer;
+ hr = MFCreateMemoryBuffer(qMax(streamInfo.cbSize, 10000u), buffer.address());
+ if (FAILED(hr)) {
+ m_convertSample.reset();
+ return hr;
}
- } else {
- qWarning() << "MFAudioDecoderControl: failed to SetOutputType of resampler" << hr;
+ m_convertSample->AddBuffer(buffer);
}
+ return S_OK;
}
void MFAudioDecoderControl::handleMediaSourceReady()
{
m_loadingSource = false;
- m_sourceReady = true;
- IMFMediaType *mediaType = m_decoderSourceReader->setSource(m_sourceResolver->mediaSource(), m_audioFormat);
- m_sourceOutputFormat = QAudioFormat();
-
- if (mediaType) {
- m_sourceOutputFormat = m_audioFormat;
-
- UINT32 val = 0;
- if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &val))) {
- m_sourceOutputFormat.setChannelCount(int(val));
- }
- if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &val))) {
- m_sourceOutputFormat.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) {
- m_sourceOutputFormat.setSampleFormat(QAudioFormat::Float);
- } else if (bitsPerSample == 8) {
- m_sourceOutputFormat.setSampleFormat(QAudioFormat::UInt8);
- } else if (bitsPerSample == 16) {
- m_sourceOutputFormat.setSampleFormat(QAudioFormat::Int16);
- } else if (bitsPerSample == 32){
- m_sourceOutputFormat.setSampleFormat(QAudioFormat::Int32);
- }
- }
-
- if (!m_audioFormat.isValid())
- setResamplerOutputFormat(m_sourceOutputFormat);
- else {
- setResamplerOutputFormat(m_audioFormat);
- }
+ if (m_deferredStart) {
+ m_deferredStart = false;
+ startReadingSource(m_sourceResolver->mediaSource());
}
+}
- if (m_sourceResolver->mediaSource()) {
- if (mediaType && m_resampler) {
- HRESULT hr = S_OK;
- hr = m_resampler->SetInputType(m_mfInputStreamID, mediaType, 0);
- if (SUCCEEDED(hr)) {
- updateResamplerOutputType();
- } else {
- qWarning() << "MFAudioDecoderControl: failed to SetInputType of resampler" << hr;
- }
- }
- IMFPresentationDescriptor *pd;
- if (SUCCEEDED(m_sourceResolver->mediaSource()->CreatePresentationDescriptor(&pd))) {
- UINT64 duration = 0;
- pd->GetUINT64(MF_PD_DURATION, &duration);
- pd->Release();
- duration /= 10000;
- if (m_duration != qint64(duration)) {
- m_duration = qint64(duration);
- durationChanged(m_duration);
- }
- }
- if (isDecoding()) {
- activatePipeline();
- }
- } else if (isDecoding()) {
- setIsDecoding(false);
+void MFAudioDecoderControl::handleMediaSourceError(long hr)
+{
+ m_loadingSource = false;
+ m_deferredStart = false;
+ if (hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE) {
+ error(QAudioDecoder::FormatError, tr("Unsupported media type"));
+ } else if (hr == ERROR_FILE_NOT_FOUND) {
+ error(QAudioDecoder::ResourceError, tr("Media not found"));
+ } else {
+ error(QAudioDecoder::ResourceError, tr("Unable to load specified URL")
+ + QString::fromStdString(std::system_category().message(hr)));
}
}
-void MFAudioDecoderControl::setResamplerOutputFormat(const QAudioFormat &format)
+void MFAudioDecoderControl::startReadingSource(IMFMediaSource *source)
{
- if (format.isValid()) {
- IMFMediaType *mediaType = 0;
- MFCreateMediaType(&mediaType);
- 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);
- }
+ Q_ASSERT(source);
- mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, UINT32(format.channelCount()));
- mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, UINT32(format.sampleRate()));
- UINT32 alignmentBlock = UINT32(format.bytesPerFrame());
- mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, alignmentBlock);
- UINT32 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);
-
- if (m_mfOutputType)
- m_mfOutputType->Release();
- m_mfOutputType = mediaType;
- } else {
- if (m_mfOutputType)
- m_mfOutputType->Release();
- m_mfOutputType = NULL;
+ m_decoderSourceReader.reset(new MFDecoderSourceReader());
+ if (!m_decoderSourceReader) {
+ error(QAudioDecoder::ResourceError, tr("Could not instantiate MFDecoderSourceReader"));
+ return;
}
- if (m_sourceReady && !isDecoding()) {
- updateResamplerOutputType();
- } else {
- m_resamplerDirty = true;
+ auto mediaType = m_decoderSourceReader->setSource(source, m_outputFormat.sampleFormat());
+ m_mediaFormat = mediaTypeToFormat(mediaType);
+ if (!m_mediaFormat.isValid()) {
+ error(QAudioDecoder::FormatError, tr("Invalid media format"));
+ m_decoderSourceReader.reset();
+ return;
}
-}
-void MFAudioDecoderControl::handleMediaSourceError(long hr)
-{
- Q_UNUSED(hr);
- m_loadingSource = false;
- m_decoderSourceReader->setSource(nullptr, m_audioFormat);
- setIsDecoding(false);
-}
+ QWindowsIUPointer<IMFPresentationDescriptor> pd;
+ if (SUCCEEDED(source->CreatePresentationDescriptor(pd.address()))) {
+ UINT64 duration = 0;
+ pd->GetUINT64(MF_PD_DURATION, &duration);
+ duration /= 10000;
+ m_duration = qint64(duration);
+ durationChanged(m_duration);
+ }
-void MFAudioDecoderControl::activatePipeline()
-{
- Q_ASSERT(!m_bufferReady);
- setIsDecoding(true);
- connect(m_decoderSourceReader, SIGNAL(sampleAdded()), this, SLOT(handleSampleAdded()));
- if (m_resamplerDirty) {
- updateResamplerOutputType();
+ if (useResampler()) {
+ HRESULT hr = m_resampler->SetInputType(m_mfInputStreamID, mediaType, 0);
+ if (SUCCEEDED(hr)) {
+ if (auto output = formatToMediaType(m_outputFormat); output) {
+ hr = setResamplerOutputType(output);
+ if (FAILED(hr)) {
+ qWarning() << "MFAudioDecoderControl: failed to SetOutputType of resampler: "
+ << std::system_category().message(hr).c_str();
+ }
+ } else {
+ qWarning() << "MFAudioDecoderControl: incorrect audio format";
+ }
+ } else {
+ qWarning() << "MFAudioDecoderControl: failed to SetInputType of resampler: "
+ << std::system_category().message(hr).c_str();
+ }
}
- m_decoderSourceReader->reset();
+
+ connect(m_decoderSourceReader, SIGNAL(finished()), this, SLOT(handleSourceFinished()));
+ connect(m_decoderSourceReader, SIGNAL(newSample(QWindowsIUPointer<IMFSample>)), this, SLOT(handleNewSample(QWindowsIUPointer<IMFSample>)));
+
+ setIsDecoding(true);
+
m_decoderSourceReader->readNextSample();
- if (m_position != -1) {
- m_position = -1;
- positionChanged(-1);
- }
}
void MFAudioDecoderControl::start()
@@ -311,97 +268,115 @@ void MFAudioDecoderControl::start()
return;
if (m_loadingSource) {
- //deferred starting
- setIsDecoding(true);
- return;
+ m_deferredStart = true;
+ } else {
+ IMFMediaSource *source = m_sourceResolver->mediaSource();
+ if (!source) {
+ if (m_device)
+ error(QAudioDecoder::ResourceError, tr("Unable to read from specified device"));
+ else if (m_source.isValid())
+ error(QAudioDecoder::ResourceError, tr("Unable to load specified URL"));
+ else
+ error(QAudioDecoder::ResourceError, tr("No media source specified"));
+ return;
+ } else {
+ startReadingSource(source);
+ }
}
-
- if (!m_decoderSourceReader->mediaSource())
- return;
- activatePipeline();
}
void MFAudioDecoderControl::stop()
{
+ m_deferredStart = false;
if (!isDecoding())
return;
- disconnect(m_decoderSourceReader, SIGNAL(sampleAdded()), this, SLOT(handleSampleAdded()));
- if (m_bufferReady) {
- m_bufferReady = false;
- emit bufferAvailableChanged(m_bufferReady);
+
+ disconnect(m_decoderSourceReader);
+ m_decoderSourceReader->clearSource();
+ m_decoderSourceReader.reset();
+
+ if (bufferAvailable()) {
+ QAudioBuffer buffer;
+ m_audioBuffer.swap(buffer);
+ emit bufferAvailableChanged(false);
}
setIsDecoding(false);
+
+ if (m_position != -1) {
+ m_position = -1;
+ positionChanged(m_position);
+ }
+ if (m_duration != -1) {
+ m_duration = -1;
+ durationChanged(m_duration);
+ }
}
-void MFAudioDecoderControl::handleSampleAdded()
+static HRESULT addDataFromIMFSample(QByteArray &data, IMFSample *s)
{
- QList<IMFSample*> samples = m_decoderSourceReader->takeSamples();
- Q_ASSERT(samples.count() > 0);
- Q_ASSERT(!m_bufferReady);
- Q_ASSERT(m_resampler);
- LONGLONG sampleStartTime = 0;
- IMFSample *firstSample = samples.first();
- firstSample->GetSampleTime(&sampleStartTime);
+ Q_ASSERT(s);
+
+ QWindowsIUPointer<IMFMediaBuffer> mediaBuffer;
+ HRESULT hr = s->ConvertToContiguousBuffer(mediaBuffer.address());
+ if (FAILED(hr))
+ return hr;
+
+ DWORD bufLen = 0;
+ BYTE *buf = nullptr;
+ hr = mediaBuffer->Lock(&buf, nullptr, &bufLen);
+ if (SUCCEEDED(hr))
+ data.push_back(QByteArray(reinterpret_cast<char *>(buf), bufLen));
+
+ mediaBuffer->Unlock();
+
+ return hr;
+}
+
+void MFAudioDecoderControl::handleNewSample(QWindowsIUPointer<IMFSample> sample)
+{
+ Q_ASSERT(sample);
+ LONGLONG sampleStartTime;
QByteArray abuf;
- QAudioFormat bufferFormat;
- if (!m_audioFormat.isValid()) {
- bufferFormat = m_sourceOutputFormat;
- //no need for resampling
- for (IMFSample *s : qAsConst(samples)) {
- IMFMediaBuffer *buffer;
- s->ConvertToContiguousBuffer(&buffer);
- DWORD bufLen = 0;
- BYTE *buf = 0;
- if (SUCCEEDED(buffer->Lock(&buf, NULL, &bufLen))) {
- abuf.push_back(QByteArray(reinterpret_cast<char*>(buf), bufLen));
- buffer->Unlock();
- }
- buffer->Release();
- LONGLONG sampleTime = 0, sampleDuration = 0;
- s->GetSampleTime(&sampleTime);
- s->GetSampleDuration(&sampleDuration);
- m_position = qint64(sampleTime + sampleDuration) / 10000;
- s->Release();
- }
- } else {
- bufferFormat = m_audioFormat;
- for (IMFSample *s : qAsConst(samples)) {
- HRESULT hr = m_resampler->ProcessInput(m_mfInputStreamID, s, 0);
- if (SUCCEEDED(hr)) {
- MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
- outputDataBuffer.dwStreamID = m_mfOutputStreamID;
- while (true) {
- outputDataBuffer.pEvents = 0;
- outputDataBuffer.dwStatus = 0;
- outputDataBuffer.pSample = m_convertSample;
- DWORD status = 0;
- if (SUCCEEDED(m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status))) {
- IMFMediaBuffer *buffer;
- outputDataBuffer.pSample->ConvertToContiguousBuffer(&buffer);
- DWORD bufLen = 0;
- BYTE *buf = 0;
- if (SUCCEEDED(buffer->Lock(&buf, NULL, &bufLen))) {
- abuf.push_back(QByteArray(reinterpret_cast<char*>(buf), bufLen));
- buffer->Unlock();
- }
- buffer->Release();
- } else {
- break;
+
+ HRESULT hr = S_OK;
+ if (useResampler()) {
+ hr = m_resampler->ProcessInput(m_mfInputStreamID, sample, 0);
+ if (SUCCEEDED(hr)) {
+ bool getSampleStartTime = true;
+ MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
+ outputDataBuffer.dwStreamID = m_mfOutputStreamID;
+ do {
+ outputDataBuffer.pEvents = nullptr;
+ outputDataBuffer.dwStatus = 0;
+ outputDataBuffer.pSample = m_convertSample;
+ DWORD status = 0;
+ hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
+ if (SUCCEEDED(hr)) {
+ if (getSampleStartTime) {
+ getSampleStartTime = false;
+ outputDataBuffer.pSample->GetSampleTime(&sampleStartTime);
}
+ hr = addDataFromIMFSample(abuf, outputDataBuffer.pSample);
}
- }
- LONGLONG sampleTime = 0, sampleDuration = 0;
- s->GetSampleTime(&sampleTime);
- s->GetSampleDuration(&sampleDuration);
- m_position = qint64(sampleTime + sampleDuration) / 10000;
- s->Release();
+ } while (SUCCEEDED(hr));
+
+ if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
+ hr = S_OK;
}
+ } else {
+ sample->GetSampleTime(&sampleStartTime);
+ hr = addDataFromIMFSample(abuf, sample);
+ }
+
+ if (FAILED(hr)) {
+ error(QAudioDecoder::Error::ResourceError, tr("Failed processing a sample")
+ + QString::fromStdString(std::system_category().message(hr)));
+ return;
}
// WMF uses 100-nanosecond units, QAudioDecoder uses milliseconds, QAudioBuffer uses microseconds...
- m_cachedAudioBuffer = QAudioBuffer(abuf, bufferFormat, qint64(sampleStartTime / 10));
- m_bufferReady = true;
- emit positionChanged(m_position);
- emit bufferAvailableChanged(m_bufferReady);
+ m_audioBuffer = QAudioBuffer(abuf, useResampler() ? m_outputFormat : m_mediaFormat, qint64(sampleStartTime / 10));
+
+ emit bufferAvailableChanged(true);
emit bufferReady();
}
@@ -411,42 +386,25 @@ void MFAudioDecoderControl::handleSourceFinished()
emit finished();
}
-QAudioFormat MFAudioDecoderControl::audioFormat() const
-{
- return m_audioFormat;
-}
-
void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
{
- if (m_audioFormat == format || !m_resampler)
+ if (m_outputFormat == format || !m_resampler)
return;
- m_audioFormat = format;
- setResamplerOutputFormat(format);
- emit formatChanged(m_audioFormat);
+ m_outputFormat = format;
+ emit formatChanged(m_outputFormat);
}
QAudioBuffer MFAudioDecoderControl::read()
{
- if (!m_bufferReady)
- return QAudioBuffer();
- QAudioBuffer buffer = m_cachedAudioBuffer;
- m_bufferReady = false;
- emit bufferAvailableChanged(m_bufferReady);
- m_decoderSourceReader->readNextSample();
- return buffer;
-}
-
-bool MFAudioDecoderControl::bufferAvailable() const
-{
- return m_bufferReady;
-}
-
-qint64 MFAudioDecoderControl::position() const
-{
- return m_position;
-}
+ QAudioBuffer buffer;
+
+ if (bufferAvailable()) {
+ buffer.swap(m_audioBuffer);
+ m_position = buffer.startTime() / 1000;
+ emit positionChanged(m_position);
+ emit bufferAvailableChanged(false);
+ m_decoderSourceReader->readNextSample();
+ }
-qint64 MFAudioDecoderControl::duration() const
-{
- return m_duration;
+ return buffer;
}
diff --git a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h b/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h
index ae4756c3d..b2399c073 100644
--- a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h
+++ b/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h
@@ -51,9 +51,10 @@
// We mean it.
//
-#include "private/qplatformaudiodecoder_p.h"
#include "mfdecodersourcereader_p.h"
-#include "private/sourceresolver_p.h"
+#include <private/qplatformaudiodecoder_p.h>
+#include <private/sourceresolver_p.h>
+#include <private/qwindowsiupointer_p.h>
QT_USE_NAMESPACE
@@ -62,56 +63,52 @@ class MFAudioDecoderControl : public QPlatformAudioDecoder
Q_OBJECT
public:
MFAudioDecoderControl(QAudioDecoder *parent);
- ~MFAudioDecoderControl();
+ ~MFAudioDecoderControl() override;
- QUrl source() const override;
+ QUrl source() const override { return m_source; }
void setSource(const QUrl &fileName) override;
- QIODevice* sourceDevice() const override;
+ QIODevice* sourceDevice() const override { return m_device; }
void setSourceDevice(QIODevice *device) override;
void start() override;
void stop() override;
- QAudioFormat audioFormat() const override;
+ QAudioFormat audioFormat() const override { return m_outputFormat; }
void setAudioFormat(const QAudioFormat &format) override;
QAudioBuffer read() override;
- bool bufferAvailable() const override;
+ bool bufferAvailable() const override { return m_audioBuffer.sampleCount() > 0; }
- qint64 position() const override;
- qint64 duration() const override;
+ qint64 position() const override { return m_position; }
+ qint64 duration() const override { return m_duration; }
private Q_SLOTS:
void handleMediaSourceReady();
void handleMediaSourceError(long hr);
- void handleSampleAdded();
+ void handleNewSample(QWindowsIUPointer<IMFSample>);
void handleSourceFinished();
private:
- void updateResamplerOutputType();
- void activatePipeline();
- void onSourceCleared();
- void setResamplerOutputFormat(const QAudioFormat &format);
+ HRESULT setResamplerOutputType(IMFMediaType *outputType);
+ void startReadingSource(IMFMediaSource *source);
+ bool useResampler() const { return m_resampler && m_outputFormat.isValid() && m_mediaFormat != m_outputFormat; }
- MFDecoderSourceReader *m_decoderSourceReader;
+ QWindowsIUPointer<MFDecoderSourceReader> m_decoderSourceReader;
SourceResolver *m_sourceResolver;
- IMFTransform *m_resampler;
+ QWindowsIUPointer<IMFTransform> m_resampler;
QUrl m_source;
- QIODevice *m_device;
- QAudioFormat m_audioFormat;
- DWORD m_mfInputStreamID;
- DWORD m_mfOutputStreamID;
- bool m_bufferReady;
- QAudioBuffer m_cachedAudioBuffer;
- qint64 m_duration;
- qint64 m_position;
- bool m_loadingSource;
- IMFMediaType *m_mfOutputType;
- IMFSample *m_convertSample;
- QAudioFormat m_sourceOutputFormat;
- bool m_sourceReady;
- bool m_resamplerDirty;
+ QIODevice *m_device = nullptr;
+ QAudioFormat m_outputFormat;
+ QAudioFormat m_mediaFormat;
+ DWORD m_mfInputStreamID = 0;
+ DWORD m_mfOutputStreamID = 0;
+ QAudioBuffer m_audioBuffer;
+ qint64 m_duration = -1;
+ qint64 m_position = -1;
+ bool m_loadingSource = false;
+ bool m_deferredStart = false;
+ QWindowsIUPointer<IMFSample> m_convertSample;
};
#endif//MFAUDIODECODERCONTROL_H
diff --git a/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp b/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp
index 381c60dc5..3cf0b6df5 100644
--- a/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp
+++ b/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp
@@ -37,103 +37,53 @@
**
****************************************************************************/
+#include <system_error>
+#include <mferror.h>
+#include <qlogging.h>
+#include <qdebug.h>
#include "mfdecodersourcereader_p.h"
-MFDecoderSourceReader::MFDecoderSourceReader(QObject *parent)
- : m_cRef(1)
- , m_sourceReader(0)
- , m_source(0)
+QWindowsIUPointer<IMFMediaType> MFDecoderSourceReader::setSource(IMFMediaSource *source, QAudioFormat::SampleFormat sampleFormat)
{
- Q_UNUSED(parent);
-}
+ QWindowsIUPointer<IMFMediaType> mediaType;
+ m_sourceReader.reset();
-void MFDecoderSourceReader::shutdown()
-{
- if (m_source) {
- m_source->Release();
- m_source = NULL;
- }
- if (m_sourceReader) {
- m_sourceReader->Release();
- m_sourceReader = NULL;
- }
-}
+ if (!source)
+ return mediaType;
-IMFMediaSource* MFDecoderSourceReader::mediaSource()
-{
- return m_source;
-}
+ QWindowsIUPointer<IMFAttributes> attr;
+ MFCreateAttributes(attr.address(), 1);
+ if (FAILED(attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this)))
+ return mediaType;
+ if (FAILED(attr->SetUINT32(MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, TRUE)))
+ return mediaType;
-IMFMediaType* MFDecoderSourceReader::setSource(IMFMediaSource *source, const QAudioFormat &audioFormat)
-{
- IMFMediaType *mediaType = NULL;
- if (m_source == source)
+ HRESULT hr = MFCreateSourceReaderFromMediaSource(source, attr, m_sourceReader.address());
+ if (FAILED(hr)) {
+ qWarning() << "MFDecoderSourceReader: failed to setup source reader: "
+ << std::system_category().message(hr).c_str();
return mediaType;
- if (m_source) {
- m_source->Release();
- m_source = NULL;
}
- if (m_sourceReader) {
- m_sourceReader->Release();
- m_sourceReader = NULL;
- }
- if (!source)
- return mediaType;
- IMFAttributes *attr = NULL;
- MFCreateAttributes(&attr, 1);
- if (SUCCEEDED(attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this))) {
- if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attr, &m_sourceReader))) {
- m_source = source;
- m_source->AddRef();
- m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE);
- m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
- IMFMediaType *pPartialType = NULL;
- MFCreateMediaType(&pPartialType);
- pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
- if (audioFormat.sampleFormat() == QAudioFormat::Float) {
- pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
- } else {
- pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
- }
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE);
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
- m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), NULL, pPartialType);
- pPartialType->Release();
- m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), &mediaType);
- // Ensure the stream is selected.
- m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
- }
- attr->Release();
- }
- return mediaType;
-}
+ QWindowsIUPointer<IMFMediaType> pPartialType;
+ MFCreateMediaType(pPartialType.address());
+ pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
+ pPartialType->SetGUID(MF_MT_SUBTYPE, sampleFormat == QAudioFormat::Float ? MFAudioFormat_Float : MFAudioFormat_PCM);
+ m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), nullptr, pPartialType);
+ m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), mediaType.address());
+ // Ensure the stream is selected.
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
-void MFDecoderSourceReader::reset()
-{
- if (!m_sourceReader)
- return;
- PROPVARIANT vPos;
- PropVariantInit(&vPos);
- vPos.vt = VT_I8;
- vPos.uhVal.QuadPart = 0;
- m_sourceReader->SetCurrentPosition(GUID_NULL, vPos);
+ return mediaType;
}
void MFDecoderSourceReader::readNextSample()
{
- if (!m_sourceReader)
- return;
- m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, NULL, NULL, NULL);
-}
-
-QList<IMFSample*> MFDecoderSourceReader::takeSamples() //internal samples will be cleared after this
-{
- QList<IMFSample*> samples;
- m_samplesMutex.lock();
- samples = m_cachedSamples;
- m_cachedSamples.clear();
- m_samplesMutex.unlock();
- return samples;
+ if (m_sourceReader)
+ m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, NULL, NULL, NULL);
}
//from IUnknown
@@ -153,12 +103,12 @@ STDMETHODIMP MFDecoderSourceReader::QueryInterface(REFIID riid, LPVOID *ppvObjec
return S_OK;
}
-STDMETHODIMP_(ULONG) MFDecoderSourceReader::AddRef(void)
+STDMETHODIMP_(ULONG) MFDecoderSourceReader::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
-STDMETHODIMP_(ULONG) MFDecoderSourceReader::Release(void)
+STDMETHODIMP_(ULONG) MFDecoderSourceReader::Release()
{
LONG cRef = InterlockedDecrement(&m_cRef);
if (cRef == 0) {
@@ -176,22 +126,9 @@ STDMETHODIMP MFDecoderSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStrea
Q_UNUSED(llTimestamp);
if (pSample) {
pSample->AddRef();
- m_samplesMutex.lock();
- m_cachedSamples.push_back(pSample);
- m_samplesMutex.unlock();
- emit sampleAdded();
+ emit newSample(QWindowsIUPointer{pSample});
} else if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) {
emit finished();
}
return S_OK;
}
-
-STDMETHODIMP MFDecoderSourceReader::OnFlush(DWORD)
-{
- return S_OK;
-}
-
-STDMETHODIMP MFDecoderSourceReader::OnEvent(DWORD, IMFMediaEvent*)
-{
- return S_OK;
-}
diff --git a/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h b/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h
index da571153e..da35f466e 100644
--- a/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h
+++ b/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h
@@ -53,11 +53,11 @@
#include <mfapi.h>
#include <mfidl.h>
-#include <Mfreadwrite.h>
+#include <mfreadwrite.h>
#include <QtCore/qobject.h>
-#include <QtCore/qmutex.h>
#include "qaudioformat.h"
+#include <private/qwindowsiupointer_p.h>
QT_USE_NAMESPACE
@@ -65,37 +65,32 @@ class MFDecoderSourceReader : public QObject, public IMFSourceReaderCallback
{
Q_OBJECT
public:
- MFDecoderSourceReader(QObject *parent = 0);
- void shutdown();
+ MFDecoderSourceReader() {}
+ ~MFDecoderSourceReader() override {}
- IMFMediaSource* mediaSource();
- IMFMediaType* setSource(IMFMediaSource *source, const QAudioFormat &audioFormat);
+ void clearSource() { m_sourceReader.reset(); }
+ QWindowsIUPointer<IMFMediaType> setSource(IMFMediaSource *source, QAudioFormat::SampleFormat);
- void reset();
void readNextSample();
- QList<IMFSample*> takeSamples(); //internal samples will be cleared after this
//from IUnknown
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
- STDMETHODIMP_(ULONG) AddRef(void) override;
- STDMETHODIMP_(ULONG) Release(void) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
//from IMFSourceReaderCallback
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) override;
- STDMETHODIMP OnFlush(DWORD dwStreamIndex) override;
- STDMETHODIMP OnEvent(DWORD dwStreamIndex, IMFMediaEvent *pEvent) override;
+ STDMETHODIMP OnFlush(DWORD) override { return S_OK; }
+ STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
Q_SIGNALS:
- void sampleAdded();
+ void newSample(QWindowsIUPointer<IMFSample>);
void finished();
private:
- long m_cRef;
- QList<IMFSample*> m_cachedSamples;
- QMutex m_samplesMutex;
+ long m_cRef = 1;
+ QWindowsIUPointer<IMFSourceReader> m_sourceReader;
- IMFSourceReader *m_sourceReader;
- IMFMediaSource *m_source;
};
#endif//MFDECODERSOURCEREADER_H