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