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 | |
12 | static Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder" ) |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace QFFmpeg |
17 | { |
18 | |
19 | class SteppingAudioRenderer : public Renderer |
20 | { |
21 | Q_OBJECT |
22 | public: |
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 | |
38 | signals: |
39 | void newAudioBuffer(QAudioBuffer); |
40 | |
41 | private: |
42 | QAudioFormat m_format; |
43 | std::unique_ptr<QFFmpegResampler> m_resampler; |
44 | }; |
45 | |
46 | class AudioDecoder : public PlaybackEngine |
47 | { |
48 | Q_OBJECT |
49 | public: |
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 | |
75 | signals: |
76 | void newAudioBuffer(QAudioBuffer); |
77 | |
78 | private: |
79 | QPointer<Renderer> m_audioRenderer; |
80 | QAudioFormat m_format; |
81 | }; |
82 | } |
83 | |
84 | |
85 | QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent) |
86 | : QPlatformAudioDecoder(parent) |
87 | { |
88 | } |
89 | |
90 | QFFmpegAudioDecoder::~QFFmpegAudioDecoder() = default; |
91 | |
92 | QUrl QFFmpegAudioDecoder::source() const |
93 | { |
94 | return m_url; |
95 | } |
96 | |
97 | void 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 | |
106 | QIODevice *QFFmpegAudioDecoder::sourceDevice() const |
107 | { |
108 | return m_sourceDevice; |
109 | } |
110 | |
111 | void 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 | |
119 | void 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 | |
169 | void QFFmpegAudioDecoder::stop() |
170 | { |
171 | qCDebug(qLcAudioDecoder) << ">>>>> stop" ; |
172 | if (m_decoder) { |
173 | m_decoder.reset(); |
174 | done(); |
175 | } |
176 | } |
177 | |
178 | QAudioFormat QFFmpegAudioDecoder::audioFormat() const |
179 | { |
180 | return m_audioFormat; |
181 | } |
182 | |
183 | void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format) |
184 | { |
185 | if (std::exchange(obj&: m_audioFormat, new_val: format) != format) |
186 | formatChanged(format: m_audioFormat); |
187 | } |
188 | |
189 | QAudioBuffer 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 | |
201 | void 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 | |
215 | void QFFmpegAudioDecoder::done() |
216 | { |
217 | qCDebug(qLcAudioDecoder) << ">>>>> DONE!" ; |
218 | finished(); |
219 | } |
220 | |
221 | void 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 | |
243 | QT_END_NAMESPACE |
244 | |
245 | #include "moc_qffmpegaudiodecoder_p.cpp" |
246 | |
247 | #include "qffmpegaudiodecoder.moc" |
248 | |