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
3#include "qffmpegaudiodecoder_p.h"
4#include "qffmpegresampler_p.h"
5#include "qaudiobuffer.h"
6
7#include "qffmpegplaybackengine_p.h"
8#include "playbackengine/qffmpegrenderer_p.h"
9
10#include <qloggingcategory.h>
11
12static Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
13
14QT_BEGIN_NAMESPACE
15
16namespace QFFmpeg
17{
18
19class SteppingAudioRenderer : public Renderer
20{
21 Q_OBJECT
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>(args: frame.codecContext(), args&: m_format);
32
33 emit newAudioBuffer(m_resampler->resample(frame: frame.avFrame()));
34
35 return {};
36 }
37
38signals:
39 void newAudioBuffer(QAudioBuffer);
40
41private:
42 QAudioFormat m_format;
43 std::unique_ptr<QFFmpegResampler> m_resampler;
44};
45
46class AudioDecoder : public PlaybackEngine
47{
48 Q_OBJECT
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>(args&: m_format);
58 m_audioRenderer = result.get();
59
60 connect(sender: result.get(), signal: &SteppingAudioRenderer::newAudioBuffer, context: this,
61 slot: &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
75signals:
76 void newAudioBuffer(QAudioBuffer);
77
78private:
79 QPointer<Renderer> m_audioRenderer;
80 QAudioFormat m_format;
81};
82} // namespace QFFmpeg
83
84QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
85 : QPlatformAudioDecoder(parent)
86{
87}
88
89QFFmpegAudioDecoder::~QFFmpegAudioDecoder() = default;
90
91QUrl QFFmpegAudioDecoder::source() const
92{
93 return m_url;
94}
95
96void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
97{
98 stop();
99 m_sourceDevice = nullptr;
100
101 if (std::exchange(obj&: m_url, new_val: fileName) != fileName)
102 sourceChanged();
103}
104
105QIODevice *QFFmpegAudioDecoder::sourceDevice() const
106{
107 return m_sourceDevice;
108}
109
110void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
111{
112 stop();
113 m_url.clear();
114 if (std::exchange(obj&: m_sourceDevice, new_val&: device) != device)
115 sourceChanged();
116}
117
118void QFFmpegAudioDecoder::start()
119{
120 qCDebug(qLcAudioDecoder) << "start";
121 auto checkNoError = [this]() {
122 if (error() == QAudioDecoder::NoError)
123 return true;
124
125 durationChanged(duration: -1);
126 positionChanged(position: -1);
127
128 m_decoder.reset();
129
130 return false;
131 };
132
133 m_decoder = std::make_unique<AudioDecoder>(args&: m_audioFormat);
134 connect(sender: m_decoder.get(), signal: &AudioDecoder::errorOccured, context: this, slot: &QFFmpegAudioDecoder::errorSignal);
135 connect(sender: m_decoder.get(), signal: &AudioDecoder::endOfStream, context: this, slot: &QFFmpegAudioDecoder::done);
136 connect(sender: m_decoder.get(), signal: &AudioDecoder::newAudioBuffer, context: this,
137 slot: &QFFmpegAudioDecoder::newAudioBuffer);
138
139 QFFmpeg::MediaDataHolder::Maybe media = QFFmpeg::MediaDataHolder::create(url: m_url, stream: m_sourceDevice, cancelToken: nullptr);
140
141 if (media) {
142 Q_ASSERT(media.value());
143 if (media.value()->streamInfo(trackType: QPlatformMediaPlayer::AudioStream).isEmpty())
144 error(error: QAudioDecoder::FormatError,
145 errorString: QLatin1String("The media doesn't contain an audio stream"));
146 else
147 m_decoder->setMedia(std::move(*media.value()));
148 } else {
149 auto [code, description] = media.error();
150 errorSignal(err: code, errorString: description);
151 }
152
153 if (!checkNoError())
154 return;
155
156 m_decoder->setState(QMediaPlayer::PausedState);
157 if (!checkNoError())
158 return;
159
160 m_decoder->nextBuffer();
161 if (!checkNoError())
162 return;
163
164 durationChanged(duration: QFFmpeg::toUserDuration(trackDuration: m_decoder->duration()).get());
165 setIsDecoding(true);
166}
167
168void QFFmpegAudioDecoder::stop()
169{
170 qCDebug(qLcAudioDecoder) << ">>>>> stop";
171 if (m_decoder) {
172 m_decoder.reset();
173 done();
174 }
175}
176
177QAudioFormat QFFmpegAudioDecoder::audioFormat() const
178{
179 return m_audioFormat;
180}
181
182void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
183{
184 if (std::exchange(obj&: m_audioFormat, new_val: format) != format)
185 formatChanged(format: m_audioFormat);
186}
187
188QAudioBuffer QFFmpegAudioDecoder::read()
189{
190 auto buffer = std::exchange(obj&: m_audioBuffer, new_val: QAudioBuffer{});
191 if (!buffer.isValid())
192 return buffer;
193 qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
194 bufferAvailableChanged(available: false);
195 if (m_decoder)
196 m_decoder->nextBuffer();
197 return buffer;
198}
199
200void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b)
201{
202 Q_ASSERT(b.isValid());
203 Q_ASSERT(!m_audioBuffer.isValid());
204 Q_ASSERT(!bufferAvailable());
205
206 qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
207 m_audioBuffer = b;
208 const qint64 pos = b.startTime();
209 positionChanged(position: pos/1000);
210 bufferAvailableChanged(available: b.isValid());
211 bufferReady();
212}
213
214void QFFmpegAudioDecoder::done()
215{
216 qCDebug(qLcAudioDecoder) << ">>>>> DONE!";
217 finished();
218}
219
220void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString)
221{
222 // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
223 // Map them.
224 switch (QMediaPlayer::Error(err)) {
225 case QMediaPlayer::NoError:
226 error(error: QAudioDecoder::NoError, errorString);
227 break;
228 case QMediaPlayer::ResourceError:
229 error(error: QAudioDecoder::ResourceError, errorString);
230 break;
231 case QMediaPlayer::FormatError:
232 error(error: QAudioDecoder::FormatError, errorString);
233 break;
234 case QMediaPlayer::NetworkError:
235 // fall through, Network error doesn't exist in QAudioDecoder
236 case QMediaPlayer::AccessDeniedError:
237 error(error: QAudioDecoder::AccessDeniedError, errorString);
238 break;
239 }
240}
241
242QT_END_NAMESPACE
243
244#include "moc_qffmpegaudiodecoder_p.cpp"
245
246#include "qffmpegaudiodecoder.moc"
247

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp