Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
mfaudiodecodercontrol.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5
6#include <QtMultimedia/private/qwindowsaudioutils_p.h>
7#include <QtMultimedia/private/qwmf_support_p.h>
8
9#include <QtCore/qdebug.h>
10#include <QtCore/qglobal.h>
11
12#include <chrono>
13#include <mferror.h>
14
15#include <system_error>
16
17QT_BEGIN_NAMESPACE
18
19MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent)
20 : QPlatformAudioDecoder(parent)
21 , m_sourceResolver(makeComObject<SourceResolver>())
22{
23 connect(m_sourceResolver.Get(), &SourceResolver::mediaSourceReady, this, &MFAudioDecoderControl::handleMediaSourceReady);
24 connect(m_sourceResolver.Get(), &SourceResolver::error, this, &MFAudioDecoderControl::handleMediaSourceError);
25}
26
28{
29 m_sourceResolver->shutdown();
30}
31
32void MFAudioDecoderControl::setSource(const QUrl &fileName)
33{
34 if (!m_device && m_source == fileName)
35 return;
36 stop();
37 m_sourceResolver->cancel();
38 m_sourceResolver->shutdown();
39 m_device = nullptr;
40 m_source = fileName;
41 sourceChanged();
42
43 if (!m_source.isEmpty()) {
44 m_sourceResolver->load(m_source, 0);
45 m_loadingSource = true;
46 }
47}
48
49void MFAudioDecoderControl::setSourceDevice(QIODevice *device)
50{
51 if (m_device == device && m_source.isEmpty())
52 return;
53 stop();
54 m_sourceResolver->cancel();
55 m_sourceResolver->shutdown();
56 m_source.clear();
57 m_device = device;
58 sourceChanged();
59
60 if (m_device) {
61 if (m_device->isOpen() && m_device->isReadable()) {
62 m_sourceResolver->load(QUrl(), m_device);
63 m_loadingSource = true;
64 }
65 }
66}
67
68void MFAudioDecoderControl::handleMediaSourceReady()
69{
70 m_loadingSource = false;
71 if (m_deferredStart) {
72 m_deferredStart = false;
73 startReadingSource(m_sourceResolver->mediaSource());
74 }
75}
76
77void MFAudioDecoderControl::handleMediaSourceError(long hr)
78{
79 m_loadingSource = false;
80 m_deferredStart = false;
81 if (hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE) {
82 error(QAudioDecoder::FormatError, tr("Unsupported media type"));
83 } else if (hr == ERROR_FILE_NOT_FOUND) {
84 error(QAudioDecoder::ResourceError, tr("Media not found"));
85 } else {
86 error(QAudioDecoder::ResourceError, tr("Unable to load specified URL")
87 + QString::fromStdString(std::system_category().message(hr)));
88 }
89}
90
91void MFAudioDecoderControl::startReadingSource(const ComPtr<IMFMediaSource> &source)
92{
93 Q_ASSERT(source);
94 using namespace QWindowsAudioUtils;
95 using namespace std::chrono;
96
97 m_decoderSourceReader = std::make_unique<MFDecoderSourceReader>();
98 if (!m_decoderSourceReader) {
99 error(QAudioDecoder::ResourceError, tr("Could not instantiate MFDecoderSourceReader"));
100 return;
101 }
102
103 auto mediaType = m_decoderSourceReader->setSource(source, m_outputFormat.sampleFormat());
104 QAudioFormat mediaFormat = QWindowsAudioUtils::mediaTypeToFormat(mediaType.Get());
105 if (!mediaFormat.isValid()) {
106 error(QAudioDecoder::FormatError, tr("Invalid media format"));
107 m_decoderSourceReader.reset();
108 return;
109 }
110
111 ComPtr<IMFPresentationDescriptor> pd;
112 if (SUCCEEDED(source->CreatePresentationDescriptor(pd.GetAddressOf()))) {
113 UINT64 durationHns = 0;
114 pd->GetUINT64(MF_PD_DURATION, &durationHns);
115 durationChanged(round<milliseconds>(QWMF::reference_time{ static_cast<long long>(durationHns) }));
116 }
117
118 if (!m_resampler.setup(mediaFormat, m_outputFormat.isValid() ? m_outputFormat : mediaFormat)) {
119 qWarning() << "Failed to set up resampler";
120 return;
121 }
122
123 connect(m_decoderSourceReader.get(), &MFDecoderSourceReader::finished, this, &MFAudioDecoderControl::handleSourceFinished);
124 connect(m_decoderSourceReader.get(), &MFDecoderSourceReader::newSample, this, &MFAudioDecoderControl::handleNewSample);
125
126 setIsDecoding(true);
127
128 m_decoderSourceReader->readNextSample();
129}
130
132{
133 if (isDecoding())
134 return;
135
136 if (m_loadingSource) {
137 m_deferredStart = true;
138 } else {
139 ComPtr<IMFMediaSource> source = m_sourceResolver->mediaSource();
140 if (!source) {
141 if (m_device)
142 error(QAudioDecoder::ResourceError, tr("Unable to read from specified device"));
143 else if (m_source.isValid())
144 error(QAudioDecoder::ResourceError, tr("Unable to load specified URL"));
145 else
146 error(QAudioDecoder::ResourceError, tr("No media source specified"));
147 return;
148 } else {
149 startReadingSource(source);
150 }
151 }
152}
153
155{
156 m_deferredStart = false;
157 if (!isDecoding())
158 return;
159
160 disconnect(m_decoderSourceReader.get());
161 m_decoderSourceReader->clearSource();
162 m_decoderSourceReader.reset();
163
164 if (bufferAvailable()) {
165 QAudioBuffer buffer;
166 m_audioBuffer.swap(buffer);
167 bufferAvailableChanged(false);
168 }
169 setIsDecoding(false);
170
171 finished();
172}
173
174void MFAudioDecoderControl::handleNewSample(ComPtr<IMFSample> sample)
175{
176 Q_ASSERT(sample);
177
178 qint64 sampleStartTimeUs = m_resampler.outputFormat().durationForBytes(m_resampler.totalOutputBytes());
179 QByteArray out = m_resampler.resample(sample);
180
181 if (out.isEmpty()) {
182 error(QAudioDecoder::Error::ResourceError, tr("Failed processing a sample"));
183
184 } else {
185 m_audioBuffer = QAudioBuffer(out, m_resampler.outputFormat(), sampleStartTimeUs);
186
187 bufferAvailableChanged(true);
188 bufferReady();
189 }
190}
191
192void MFAudioDecoderControl::handleSourceFinished()
193{
194 stop();
195}
196
197void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
198{
199 if (m_outputFormat == format)
200 return;
201 m_outputFormat = format;
202 formatChanged(m_outputFormat);
203}
204
206{
207 QAudioBuffer buffer;
208
209 if (bufferAvailable()) {
210 buffer.swap(m_audioBuffer);
211 // buffer.startTime() is microseconds; convert to milliseconds
212 positionChanged(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::microseconds{ buffer.startTime() }));
213 bufferAvailableChanged(false);
214 m_decoderSourceReader->readNextSample();
215 }
216
217 return buffer;
218}
219
220QT_END_NAMESPACE
221
222#include "moc_mfaudiodecodercontrol_p.cpp"
void setAudioFormat(const QAudioFormat &format) override
bool bufferAvailable() const override
void setSourceDevice(QIODevice *device) override
QAudioBuffer read() override
void setSource(const QUrl &fileName) override