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{
22public:
27
41
44
45private:
46 QAudioFormat m_format;
47 std::unique_ptr<QFFmpegResampler> m_resampler;
48};
49
51{
53public:
58
72
74 {
77
79 // updateObjectsPausedState();
80 }
81
84
85private:
86 QPointer<Renderer> m_audioRenderer;
87 QAudioFormat m_format;
88};
89} // namespace QFFmpeg
90
91QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
92 : QPlatformAudioDecoder(parent)
93{
94}
95
97
99{
100 return m_url;
101}
102
103void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
104{
105 stop();
106 m_sourceDevice = nullptr;
107
108 if (std::exchange(m_url, fileName) != fileName)
109 sourceChanged();
110}
111
113{
114 return m_sourceDevice;
115}
116
117void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
118{
119 stop();
120 m_url.clear();
121 if (std::exchange(m_sourceDevice, device) != device)
122 sourceChanged();
123}
124
126{
127 qCDebug(qLcAudioDecoder) << "start";
128 auto checkNoError = [this]() {
129 if (error() == QAudioDecoder::NoError)
130 return true;
131
132 durationChanged(-1);
133 positionChanged(-1);
134
135 m_decoder.reset();
136
137 return false;
138 };
139
140 QPlaybackOptions defaultOptions;
141 m_decoder = std::make_unique<AudioDecoder>(m_audioFormat, defaultOptions);
142 connect(m_decoder.get(), &AudioDecoder::errorOccured, this, &QFFmpegAudioDecoder::errorSignal);
143 connect(m_decoder.get(), &AudioDecoder::endOfStream, this, &QFFmpegAudioDecoder::done);
144 connect(m_decoder.get(), &AudioDecoder::newAudioBuffer, this,
145 &QFFmpegAudioDecoder::newAudioBuffer);
146
147 QFFmpeg::MediaDataHolder::Maybe media =
148 QFFmpeg::MediaDataHolder::create(m_url, m_sourceDevice, defaultOptions, nullptr);
149
150 if (media) {
151 Q_ASSERT(media.value());
152 if (media.value()->streamInfo(QPlatformMediaPlayer::AudioStream).isEmpty())
153 error(QAudioDecoder::FormatError,
154 QLatin1String("The media doesn't contain an audio stream"));
155 else
156 m_decoder->setMedia(std::move(*media.value()));
157 } else {
158 auto [code, description] = media.error();
159 errorSignal(code, description);
160 }
161
162 if (!checkNoError())
163 return;
164
165 m_decoder->setState(QMediaPlayer::PausedState);
166 if (!checkNoError())
167 return;
168
169 m_decoder->nextBuffer();
170 if (!checkNoError())
171 return;
172
173 durationChanged(QFFmpeg::toUserDuration(m_decoder->duration()).get());
174 setIsDecoding(true);
175}
176
178{
179 qCDebug(qLcAudioDecoder) << ">>>>> stop";
180 if (m_decoder) {
181 m_decoder.reset();
182 done();
183 }
184}
185
187{
188 return m_audioFormat;
189}
190
191void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
192{
193 if (std::exchange(m_audioFormat, format) != format)
194 formatChanged(m_audioFormat);
195}
196
198{
199 auto buffer = std::exchange(m_audioBuffer, QAudioBuffer{});
200 if (!buffer.isValid())
201 return buffer;
202 qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
203 bufferAvailableChanged(false);
204 if (m_decoder)
205 m_decoder->nextBuffer();
206 return buffer;
207}
208
209void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b)
210{
211 Q_ASSERT(b.isValid());
212 Q_ASSERT(!m_audioBuffer.isValid());
213 Q_ASSERT(!bufferAvailable());
214
215 qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
216 m_audioBuffer = b;
217 const qint64 pos = b.startTime();
218 positionChanged(pos/1000);
219 bufferAvailableChanged(b.isValid());
220 bufferReady();
221}
222
224{
225 qCDebug(qLcAudioDecoder) << ">>>>> DONE!";
226 finished();
227}
228
229void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString)
230{
231 // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
232 // Map them.
233 switch (QMediaPlayer::Error(err)) {
234 case QMediaPlayer::NoError:
235 error(QAudioDecoder::NoError, errorString);
236 break;
237 case QMediaPlayer::ResourceError:
238 error(QAudioDecoder::ResourceError, errorString);
239 break;
240 case QMediaPlayer::FormatError:
241 error(QAudioDecoder::FormatError, errorString);
242 break;
243 case QMediaPlayer::NetworkError:
244 // fall through, Network error doesn't exist in QAudioDecoder
245 case QMediaPlayer::AccessDeniedError:
246 error(QAudioDecoder::AccessDeniedError, errorString);
247 break;
248 }
249}
250
251QT_END_NAMESPACE
252
253#include "moc_qffmpegaudiodecoder_p.cpp"
254
255#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.
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)