summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/windows/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/windows/decoder')
-rw-r--r--src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp225
-rw-r--r--src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h75
-rw-r--r--src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp103
-rw-r--r--src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h63
4 files changed, 466 insertions, 0 deletions
diff --git a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
new file mode 100644
index 000000000..912ab5e94
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
@@ -0,0 +1,225 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <system_error>
+#include <mferror.h>
+#include <qglobal.h>
+#include "wmcodecdsp.h"
+#include "mfaudiodecodercontrol_p.h"
+#include <private/qwindowsaudioutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent)
+ , m_sourceResolver(new SourceResolver)
+{
+ connect(m_sourceResolver, &SourceResolver::mediaSourceReady, this, &MFAudioDecoderControl::handleMediaSourceReady);
+ connect(m_sourceResolver, &SourceResolver::error, this, &MFAudioDecoderControl::handleMediaSourceError);
+}
+
+MFAudioDecoderControl::~MFAudioDecoderControl()
+{
+ m_sourceResolver->shutdown();
+ m_sourceResolver->Release();
+}
+
+void MFAudioDecoderControl::setSource(const QUrl &fileName)
+{
+ if (!m_device && m_source == fileName)
+ return;
+ stop();
+ m_sourceResolver->cancel();
+ m_sourceResolver->shutdown();
+ m_device = nullptr;
+ m_source = fileName;
+ sourceChanged();
+
+ if (!m_source.isEmpty()) {
+ m_sourceResolver->load(m_source, 0);
+ m_loadingSource = true;
+ }
+}
+
+void MFAudioDecoderControl::setSourceDevice(QIODevice *device)
+{
+ if (m_device == device && m_source.isEmpty())
+ return;
+ stop();
+ m_sourceResolver->cancel();
+ m_sourceResolver->shutdown();
+ m_source.clear();
+ m_device = device;
+ sourceChanged();
+
+ if (m_device) {
+ if (m_device->isOpen() && m_device->isReadable()) {
+ m_sourceResolver->load(QUrl(), m_device);
+ m_loadingSource = true;
+ }
+ }
+}
+
+void MFAudioDecoderControl::handleMediaSourceReady()
+{
+ m_loadingSource = false;
+ if (m_deferredStart) {
+ m_deferredStart = false;
+ startReadingSource(m_sourceResolver->mediaSource());
+ }
+}
+
+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::startReadingSource(IMFMediaSource *source)
+{
+ Q_ASSERT(source);
+
+ m_decoderSourceReader = makeComObject<MFDecoderSourceReader>();
+ if (!m_decoderSourceReader) {
+ error(QAudioDecoder::ResourceError, tr("Could not instantiate MFDecoderSourceReader"));
+ return;
+ }
+
+ auto mediaType = m_decoderSourceReader->setSource(source, m_outputFormat.sampleFormat());
+ QAudioFormat mediaFormat = QWindowsAudioUtils::mediaTypeToFormat(mediaType.Get());
+ if (!mediaFormat.isValid()) {
+ error(QAudioDecoder::FormatError, tr("Invalid media format"));
+ m_decoderSourceReader.Reset();
+ return;
+ }
+
+ ComPtr<IMFPresentationDescriptor> pd;
+ if (SUCCEEDED(source->CreatePresentationDescriptor(pd.GetAddressOf()))) {
+ UINT64 duration = 0;
+ pd->GetUINT64(MF_PD_DURATION, &duration);
+ duration /= 10000;
+ m_duration = qint64(duration);
+ durationChanged(m_duration);
+ }
+
+ if (!m_resampler.setup(mediaFormat, m_outputFormat.isValid() ? m_outputFormat : mediaFormat)) {
+ qWarning() << "Failed to set up resampler";
+ return;
+ }
+
+ connect(m_decoderSourceReader.Get(), &MFDecoderSourceReader::finished, this, &MFAudioDecoderControl::handleSourceFinished);
+ connect(m_decoderSourceReader.Get(), &MFDecoderSourceReader::newSample, this, &MFAudioDecoderControl::handleNewSample);
+
+ setIsDecoding(true);
+
+ m_decoderSourceReader->readNextSample();
+}
+
+void MFAudioDecoderControl::start()
+{
+ if (isDecoding())
+ return;
+
+ if (m_loadingSource) {
+ 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);
+ }
+ }
+}
+
+void MFAudioDecoderControl::stop()
+{
+ m_deferredStart = false;
+ if (!isDecoding())
+ return;
+
+ disconnect(m_decoderSourceReader.Get());
+ m_decoderSourceReader->clearSource();
+ m_decoderSourceReader.Reset();
+
+ if (bufferAvailable()) {
+ QAudioBuffer buffer;
+ m_audioBuffer.swap(buffer);
+ 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::handleNewSample(ComPtr<IMFSample> sample)
+{
+ Q_ASSERT(sample);
+
+ qint64 sampleStartTimeUs = m_resampler.outputFormat().durationForBytes(m_resampler.totalOutputBytes());
+ QByteArray out = m_resampler.resample(sample.Get());
+
+ if (out.isEmpty()) {
+ error(QAudioDecoder::Error::ResourceError, tr("Failed processing a sample"));
+
+ } else {
+ m_audioBuffer = QAudioBuffer(out, m_resampler.outputFormat(), sampleStartTimeUs);
+
+ bufferAvailableChanged(true);
+ bufferReady();
+ }
+}
+
+void MFAudioDecoderControl::handleSourceFinished()
+{
+ stop();
+ finished();
+}
+
+void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
+{
+ if (m_outputFormat == format)
+ return;
+ m_outputFormat = format;
+ formatChanged(m_outputFormat);
+}
+
+QAudioBuffer MFAudioDecoderControl::read()
+{
+ QAudioBuffer buffer;
+
+ if (bufferAvailable()) {
+ buffer.swap(m_audioBuffer);
+ m_position = buffer.startTime() / 1000;
+ positionChanged(m_position);
+ bufferAvailableChanged(false);
+ m_decoderSourceReader->readNextSample();
+ }
+
+ return buffer;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_mfaudiodecodercontrol_p.cpp"
diff --git a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h
new file mode 100644
index 000000000..9bb2371ec
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef MFAUDIODECODERCONTROL_H
+#define MFAUDIODECODERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "mfdecodersourcereader_p.h"
+#include <private/qplatformaudiodecoder_p.h>
+#include <sourceresolver_p.h>
+#include <private/qcomptr_p.h>
+#include <private/qwindowsresampler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class MFAudioDecoderControl : public QPlatformAudioDecoder
+{
+ Q_OBJECT
+public:
+ MFAudioDecoderControl(QAudioDecoder *parent);
+ ~MFAudioDecoderControl() override;
+
+ QUrl source() const override { return m_source; }
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice* sourceDevice() const override { return m_device; }
+ void setSourceDevice(QIODevice *device) override;
+
+ void start() override;
+ void stop() override;
+
+ QAudioFormat audioFormat() const override { return m_outputFormat; }
+ void setAudioFormat(const QAudioFormat &format) override;
+
+ QAudioBuffer read() override;
+ bool bufferAvailable() const override { return m_audioBuffer.sampleCount() > 0; }
+
+ qint64 position() const override { return m_position; }
+ qint64 duration() const override { return m_duration; }
+
+private Q_SLOTS:
+ void handleMediaSourceReady();
+ void handleMediaSourceError(long hr);
+ void handleNewSample(ComPtr<IMFSample>);
+ void handleSourceFinished();
+
+private:
+ void startReadingSource(IMFMediaSource *source);
+
+ ComPtr<MFDecoderSourceReader> m_decoderSourceReader;
+ SourceResolver *m_sourceResolver;
+ QWindowsResampler m_resampler;
+ QUrl m_source;
+ QIODevice *m_device = nullptr;
+ QAudioFormat m_outputFormat;
+ QAudioBuffer m_audioBuffer;
+ qint64 m_duration = -1;
+ qint64 m_position = -1;
+ bool m_loadingSource = false;
+ bool m_deferredStart = false;
+};
+
+QT_END_NAMESPACE
+
+#endif//MFAUDIODECODERCONTROL_H
diff --git a/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp
new file mode 100644
index 000000000..097f83437
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <system_error>
+#include <mferror.h>
+#include <qlogging.h>
+#include <qdebug.h>
+#include "mfdecodersourcereader_p.h"
+
+QT_BEGIN_NAMESPACE
+
+ComPtr<IMFMediaType> MFDecoderSourceReader::setSource(IMFMediaSource *source, QAudioFormat::SampleFormat sampleFormat)
+{
+ ComPtr<IMFMediaType> mediaType;
+ m_sourceReader.Reset();
+
+ if (!source)
+ return mediaType;
+
+ ComPtr<IMFAttributes> attr;
+ MFCreateAttributes(attr.GetAddressOf(), 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;
+
+ HRESULT hr = MFCreateSourceReaderFromMediaSource(source, attr.Get(), m_sourceReader.GetAddressOf());
+ if (FAILED(hr)) {
+ qWarning() << "MFDecoderSourceReader: failed to set up source reader: "
+ << std::system_category().message(hr).c_str();
+ return mediaType;
+ }
+
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE);
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
+
+ ComPtr<IMFMediaType> pPartialType;
+ MFCreateMediaType(pPartialType.GetAddressOf());
+ 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.Get());
+ m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), mediaType.GetAddressOf());
+ // Ensure the stream is selected.
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
+
+ return mediaType;
+}
+
+void MFDecoderSourceReader::readNextSample()
+{
+ if (m_sourceReader)
+ m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, NULL, NULL, NULL);
+}
+
+//from IUnknown
+STDMETHODIMP MFDecoderSourceReader::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFSourceReaderCallback) {
+ *ppvObject = static_cast<IMFSourceReaderCallback*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) MFDecoderSourceReader::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MFDecoderSourceReader::Release()
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ this->deleteLater();
+ }
+ return cRef;
+}
+
+//from IMFSourceReaderCallback
+STDMETHODIMP MFDecoderSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
+ DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
+{
+ Q_UNUSED(hrStatus);
+ Q_UNUSED(dwStreamIndex);
+ Q_UNUSED(llTimestamp);
+ if (pSample) {
+ emit newSample(ComPtr<IMFSample>{pSample});
+ } else if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) {
+ emit finished();
+ }
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_mfdecodersourcereader_p.cpp"
diff --git a/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h
new file mode 100644
index 000000000..dee6f8bf5
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef MFDECODERSOURCEREADER_H
+#define MFDECODERSOURCEREADER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <mfreadwrite.h>
+
+#include <QtCore/qobject.h>
+#include "qaudioformat.h"
+#include <private/qcomptr_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class MFDecoderSourceReader : public QObject, public IMFSourceReaderCallback
+{
+ Q_OBJECT
+public:
+ MFDecoderSourceReader() {}
+ ~MFDecoderSourceReader() override {}
+
+ void clearSource() { m_sourceReader.Reset(); }
+ ComPtr<IMFMediaType> setSource(IMFMediaSource *source, QAudioFormat::SampleFormat);
+
+ void readNextSample();
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) 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) override { return S_OK; }
+ STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
+
+Q_SIGNALS:
+ void newSample(ComPtr<IMFSample>);
+ void finished();
+
+private:
+ long m_cRef = 1;
+ ComPtr<IMFSourceReader> m_sourceReader;
+
+};
+
+QT_END_NAMESPACE
+
+#endif//MFDECODERSOURCEREADER_H