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.codec(), 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}
83
84
85QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
86 : QPlatformAudioDecoder(parent)
87{
88}
89
90QFFmpegAudioDecoder::~QFFmpegAudioDecoder() = default;
91
92QUrl QFFmpegAudioDecoder::source() const
93{
94 return m_url;
95}
96
97void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
98{
99 stop();
100 m_sourceDevice = nullptr;
101
102 if (std::exchange(obj&: m_url, new_val: fileName) != fileName)
103 sourceChanged();
104}
105
106QIODevice *QFFmpegAudioDecoder::sourceDevice() const
107{
108 return m_sourceDevice;
109}
110
111void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
112{
113 stop();
114 m_url.clear();
115 if (std::exchange(obj&: m_sourceDevice, new_val&: device) != device)
116 sourceChanged();
117}
118
119void QFFmpegAudioDecoder::start()
120{
121 qCDebug(qLcAudioDecoder) << "start";
122 auto checkNoError = [this]() {
123 if (error() == QAudioDecoder::NoError)
124 return true;
125
126 durationChanged(duration: -1);
127 positionChanged(position: -1);
128
129 m_decoder.reset();
130
131 return false;
132 };
133
134 m_decoder = std::make_unique<AudioDecoder>(args&: m_audioFormat);
135 connect(sender: m_decoder.get(), signal: &AudioDecoder::errorOccured, context: this, slot: &QFFmpegAudioDecoder::errorSignal);
136 connect(sender: m_decoder.get(), signal: &AudioDecoder::endOfStream, context: this, slot: &QFFmpegAudioDecoder::done);
137 connect(sender: m_decoder.get(), signal: &AudioDecoder::newAudioBuffer, context: this,
138 slot: &QFFmpegAudioDecoder::newAudioBuffer);
139
140 QFFmpeg::MediaDataHolder::Maybe media = QFFmpeg::MediaDataHolder::create(url: m_url, stream: m_sourceDevice, cancelToken: nullptr);
141
142 if (media) {
143 Q_ASSERT(media.value());
144 if (media.value()->streamInfo(trackType: QPlatformMediaPlayer::AudioStream).isEmpty())
145 error(error: QAudioDecoder::FormatError,
146 errorString: 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(err: code, errorString: 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(duration: m_decoder->duration() / 1000);
166 setIsDecoding(true);
167}
168
169void QFFmpegAudioDecoder::stop()
170{
171 qCDebug(qLcAudioDecoder) << ">>>>> stop";
172 if (m_decoder) {
173 m_decoder.reset();
174 done();
175 }
176}
177
178QAudioFormat QFFmpegAudioDecoder::audioFormat() const
179{
180 return m_audioFormat;
181}
182
183void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
184{
185 if (std::exchange(obj&: m_audioFormat, new_val: format) != format)
186 formatChanged(format: m_audioFormat);
187}
188
189QAudioBuffer QFFmpegAudioDecoder::read()
190{
191 auto buffer = std::exchange(obj&: m_audioBuffer, new_val: QAudioBuffer{});
192 if (!buffer.isValid())
193 return buffer;
194 qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
195 bufferAvailableChanged(available: false);
196 if (m_decoder)
197 m_decoder->nextBuffer();
198 return buffer;
199}
200
201void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b)
202{
203 Q_ASSERT(b.isValid());
204 Q_ASSERT(!m_audioBuffer.isValid());
205 Q_ASSERT(!bufferAvailable());
206
207 qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
208 m_audioBuffer = b;
209 const qint64 pos = b.startTime();
210 positionChanged(position: pos/1000);
211 bufferAvailableChanged(available: b.isValid());
212 bufferReady();
213}
214
215void QFFmpegAudioDecoder::done()
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)) {
226 case QMediaPlayer::NoError:
227 error(error: QAudioDecoder::NoError, errorString);
228 break;
229 case QMediaPlayer::ResourceError:
230 error(error: QAudioDecoder::ResourceError, errorString);
231 break;
232 case QMediaPlayer::FormatError:
233 error(error: QAudioDecoder::FormatError, errorString);
234 break;
235 case QMediaPlayer::NetworkError:
236 // fall through, Network error doesn't exist in QAudioDecoder
237 case QMediaPlayer::AccessDeniedError:
238 error(error: QAudioDecoder::AccessDeniedError, errorString);
239 break;
240 }
241}
242
243QT_END_NAMESPACE
244
245#include "moc_qffmpegaudiodecoder_p.cpp"
246
247#include "qffmpegaudiodecoder.moc"
248

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