Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
9
10#include <qloggingcategory.h>
11
12static Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
13
15
16namespace QFFmpeg
17{
18
19class SteppingAudioRenderer : public Renderer
20{
22public:
23 SteppingAudioRenderer(const QAudioFormat &format) : Renderer({}), m_format(format) { }
24
25 RenderingResult renderInternal(Frame frame) override
26 {
27 if (!frame.isValid())
28 return {};
29
30 if (!m_resampler)
31 m_resampler = std::make_unique<QFFmpegResampler>(frame.codec(), m_format);
32
33 emit newAudioBuffer(m_resampler->resample(frame.avFrame()));
34
35 return {};
36 }
37
39 void newAudioBuffer(QAudioBuffer);
40
41private:
42 QAudioFormat m_format;
43 std::unique_ptr<QFFmpegResampler> m_resampler;
44};
45
46class AudioDecoder : public PlaybackEngine
47{
49public:
50 explicit AudioDecoder(const QAudioFormat &format) : m_format(format) { }
51
52 RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType) override
53 {
54 if (trackType != QPlatformMediaPlayer::AudioStream)
55 return RendererPtr{ {}, {} };
56
57 auto result = createPlaybackEngineObject<SteppingAudioRenderer>(m_format);
58 m_audioRenderer = result.get();
59
60 connect(result.get(), &SteppingAudioRenderer::newAudioBuffer, this,
61 &AudioDecoder::newAudioBuffer);
62
63 return result;
64 }
65
66 void nextBuffer()
67 {
68 Q_ASSERT(m_audioRenderer);
69 Q_ASSERT(!m_audioRenderer->isStepForced());
70
71 m_audioRenderer->doForceStep();
72 // updateObjectsPausedState();
73 }
74
76 void newAudioBuffer(QAudioBuffer);
77
78private:
79 QPointer<Renderer> m_audioRenderer;
80 QAudioFormat m_format;
81};
82}
83
84
89
91
93{
94 return m_url;
95}
96
98{
99 stop();
100 m_sourceDevice = nullptr;
101
102 if (std::exchange(m_url, fileName) != fileName)
104}
105
107{
108 return m_sourceDevice;
109}
110
112{
113 stop();
114 m_url.clear();
115 if (std::exchange(m_sourceDevice, device) != device)
117}
118
120{
121 qCDebug(qLcAudioDecoder) << "start";
122 auto checkNoError = [this]() {
124 return true;
125
126 durationChanged(-1);
127 positionChanged(-1);
128
129 m_decoder.reset();
130
131 return false;
132 };
133
134 m_decoder = std::make_unique<AudioDecoder>(m_audioFormat);
135 connect(m_decoder.get(), &AudioDecoder::errorOccured, this, &QFFmpegAudioDecoder::errorSignal);
136 connect(m_decoder.get(), &AudioDecoder::endOfStream, this, &QFFmpegAudioDecoder::done);
137 connect(m_decoder.get(), &AudioDecoder::newAudioBuffer, this,
139
140 QFFmpeg::MediaDataHolder::Maybe media = QFFmpeg::MediaDataHolder::create(m_url, m_sourceDevice, nullptr);
141
142 if (media) {
143 Q_ASSERT(media.value());
144 if (media.value()->streamInfo(QPlatformMediaPlayer::AudioStream).isEmpty())
146 QLatin1String("The media doesn't contain an audio stream"));
147 else
148 m_decoder->setMedia(std::move(*media.value()));
149 } else {
150 auto [code, description] = media.error();
151 errorSignal(code, description);
152 }
153
154 if (!checkNoError())
155 return;
156
157 m_decoder->setState(QMediaPlayer::PausedState);
158 if (!checkNoError())
159 return;
160
161 m_decoder->nextBuffer();
162 if (!checkNoError())
163 return;
164
165 durationChanged(m_decoder->duration() / 1000);
166 setIsDecoding(true);
167}
168
170{
171 qCDebug(qLcAudioDecoder) << ">>>>> stop";
172 if (m_decoder) {
173 m_decoder.reset();
174 done();
175 }
176}
177
179{
180 return m_audioFormat;
181}
182
184{
185 if (std::exchange(m_audioFormat, format) != format)
186 formatChanged(m_audioFormat);
187}
188
190{
191 auto buffer = std::exchange(m_audioBuffer, QAudioBuffer{});
192 if (!buffer.isValid())
193 return buffer;
194 qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
196 if (m_decoder)
197 m_decoder->nextBuffer();
198 return buffer;
199}
200
202{
203 Q_ASSERT(b.isValid());
204 Q_ASSERT(!m_audioBuffer.isValid());
206
207 qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
208 m_audioBuffer = b;
209 const qint64 pos = b.startTime();
210 positionChanged(pos/1000);
211 bufferAvailableChanged(b.isValid());
212 bufferReady();
213}
214
216{
217 qCDebug(qLcAudioDecoder) << ">>>>> DONE!";
218 finished();
219}
220
221void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString)
222{
223 // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
224 // Map them.
225 switch (QMediaPlayer::Error(err)) {
228 break;
231 break;
234 break;
236 // fall through, Network error doesn't exist in QAudioDecoder
239 break;
240 }
241}
242
244
245#include "moc_qffmpegaudiodecoder_p.cpp"
246
247#include "qffmpegaudiodecoder.moc"
IOBluetoothDevice * device
\inmodule QtMultimedia
bool isValid() const noexcept
Returns true if this is a valid buffer.
qint64 startTime() const noexcept
Returns the time in a stream that this buffer starts at (in microseconds).
The QAudioDecoder class implements decoding audio.
The QAudioFormat class stores audio stream parameter information.
void setSource(const QUrl &fileName) override
void newAudioBuffer(const QAudioBuffer &b)
QIODevice * sourceDevice() const override
void errorSignal(int err, const QString &errorString)
QAudioFormat audioFormat() const override
void setAudioFormat(const QAudioFormat &format) override
virtual ~QFFmpegAudioDecoder()
QFFmpegAudioDecoder(QAudioDecoder *parent)
QAudioBuffer read() override
void setSourceDevice(QIODevice *device) override
QUrl source() const override
static Maybe create(const QUrl &url, QIODevice *stream, const std::shared_ptr< ICancelToken > &cancelToken)
\inmodule QtCore \reentrant
Definition qiodevice.h:34
constexpr Value & value()
Definition qmaybe_p.h:69
constexpr const Error & error() const
Definition qmaybe_p.h:87
Error
\qmlproperty enumeration QtMultimedia::MediaPlayer::mediaStatus
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void durationChanged(qint64 duration)
void positionChanged(qint64 position)
void bufferAvailableChanged(bool available)
QAudioDecoder::Error error() const
virtual bool bufferAvailable() const
void formatChanged(const QAudioFormat &format)
void setIsDecoding(bool running=true)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qurl.h:94
void clear()
Resets the content of the QUrl.
Definition qurl.cpp:1909
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLboolean GLboolean GLboolean b
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum format
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_OBJECT
#define signals
#define emit
long long qint64
Definition qtypes.h:60
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QFrame frame
[0]