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
qffmpegaudiodecoder.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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
5#include "qaudiobuffer.h"
6
8#include "playbackengine/qffmpegrenderer_p.h"
9#include <QtMultimedia/qplaybackoptions.h>
10#include <qloggingcategory.h>
11
12Q_STATIC_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
13
14QT_BEGIN_NAMESPACE
15
16namespace QFFmpeg
17{
18
20
22{
24public:
29
43
46
47private:
48 QAudioFormat m_format;
49 std::unique_ptr<QFFmpegResampler> m_resampler;
50};
51
53{
55public:
60
74
76 {
79
81 // updateObjectsPausedState();
82 }
83
86
87private:
88 QPointer<Renderer> m_audioRenderer;
89 QAudioFormat m_format;
90};
91} // namespace QFFmpeg
92
93QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
94 : QPlatformAudioDecoder(parent)
95{
96}
97
99
101{
102 return m_url;
103}
104
105void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
106{
107 stop();
108 m_sourceDevice = nullptr;
109
110 if (std::exchange(m_url, fileName) != fileName)
111 sourceChanged();
112}
113
115{
116 return m_sourceDevice;
117}
118
119void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
120{
121 stop();
122 m_url.clear();
123 if (std::exchange(m_sourceDevice, device) != device)
124 sourceChanged();
125}
126
128{
129 qCDebug(qLcAudioDecoder) << "start";
130 auto checkNoError = [this]() {
131 if (error() == QAudioDecoder::NoError)
132 return true;
133
134 durationChanged(kInvalidDuration);
135 positionChanged(kInvalidPosition);
136
137 m_decoder.reset();
138
139 return false;
140 };
141
142 QPlaybackOptions defaultOptions;
143 m_decoder = std::make_unique<AudioDecoder>(m_audioFormat, defaultOptions);
144 connect(m_decoder.get(), &AudioDecoder::errorOccured, this, &QFFmpegAudioDecoder::errorSignal);
145 connect(m_decoder.get(), &AudioDecoder::endOfStream, this, &QFFmpegAudioDecoder::done);
146 connect(m_decoder.get(), &AudioDecoder::newAudioBuffer, this,
147 &QFFmpegAudioDecoder::newAudioBuffer);
148
149 QFFmpeg::MediaDataHolder::Maybe media =
150 QFFmpeg::MediaDataHolder::create(m_url, m_sourceDevice, defaultOptions, nullptr);
151
152 if (media) {
153 Q_ASSERT(media.value());
154 if (media.value()->streamInfo(QFFmpeg::TrackType::AudioStream).isEmpty())
155 error(QAudioDecoder::FormatError,
156 QLatin1String("The media doesn't contain an audio stream"));
157 else
158 m_decoder->setMedia(std::move(*media.value()));
159 } else {
160 auto [code, description] = media.error();
161 errorSignal(code, description);
162 }
163
164 if (!checkNoError())
165 return;
166
167 m_decoder->setState(QMediaPlayer::PausedState);
168 if (!checkNoError())
169 return;
170
171 m_decoder->nextBuffer();
172 if (!checkNoError())
173 return;
174
175 durationChanged(std::chrono::milliseconds{ QFFmpeg::toUserDuration(m_decoder->duration()).get() });
176 setIsDecoding(true);
177}
178
180{
181 qCDebug(qLcAudioDecoder) << ">>>>> stop";
182 if (m_decoder) {
183 m_decoder.reset();
184 done();
185 }
186}
187
189{
190 return m_audioFormat;
191}
192
193void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
194{
195 if (std::exchange(m_audioFormat, format) != format)
196 formatChanged(m_audioFormat);
197}
198
200{
201 auto buffer = std::exchange(m_audioBuffer, QAudioBuffer{});
202 if (!buffer.isValid())
203 return buffer;
204 qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
205 bufferAvailableChanged(false);
206 if (m_decoder)
207 m_decoder->nextBuffer();
208 return buffer;
209}
210
211void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b)
212{
213 Q_ASSERT(b.isValid());
214 Q_ASSERT(!m_audioBuffer.isValid());
215 Q_ASSERT(!bufferAvailable());
216
217 qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
218 m_audioBuffer = b;
219 // QAudioBuffer::startTime is in microseconds; convert to milliseconds
220 const qint64 pos = b.startTime();
221 positionChanged(
222 std::chrono::round<std::chrono::milliseconds>(std::chrono::microseconds{ pos }));
223 bufferAvailableChanged(b.isValid());
224 bufferReady();
225}
226
228{
229 qCDebug(qLcAudioDecoder) << ">>>>> DONE!";
230 finished();
231}
232
233void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString)
234{
235 // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
236 // Map them.
237 switch (QMediaPlayer::Error(err)) {
238 case QMediaPlayer::NoError:
239 error(QAudioDecoder::NoError, errorString);
240 break;
241 case QMediaPlayer::ResourceError:
242 error(QAudioDecoder::ResourceError, errorString);
243 break;
244 case QMediaPlayer::FormatError:
245 error(QAudioDecoder::FormatError, errorString);
246 break;
247 case QMediaPlayer::NetworkError:
248 // fall through, Network error doesn't exist in QAudioDecoder
249 case QMediaPlayer::AccessDeniedError:
250 error(QAudioDecoder::AccessDeniedError, errorString);
251 break;
252 }
253}
254
255QT_END_NAMESPACE
256
257#include "moc_qffmpegaudiodecoder_p.cpp"
258
259#include "qffmpegaudiodecoder.moc"
void setSource(const QUrl &fileName) override
QIODevice * sourceDevice() const override
~QFFmpegAudioDecoder() override
void errorSignal(int err, const QString &errorString)
QAudioFormat audioFormat() const override
void setAudioFormat(const QAudioFormat &format) override
QAudioBuffer read() override
void setSourceDevice(QIODevice *device) override
QUrl source() const override
The QPlaybackOptions class enables low-level control of media playback options.
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)