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