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
qaudiodecoder.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
6
7#include <QtMultimedia/private/qdrawavaudiodecoder_p.h>
8#include <QtMultimedia/private/qmultimediautils_p.h>
9#include <QtMultimedia/private/qplatformaudiodecoder_p.h>
10#include <QtMultimedia/private/qplatformmediaintegration_p.h>
11#include <QtCore/qfile.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/qtemporaryfile.h>
14#include <QtCore/qurl.h>
15
16#include <chrono>
17
18Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.audiodecoder")
19
20QT_BEGIN_NAMESPACE
21
22/*!
23 \class QAudioDecoder
24 \brief The QAudioDecoder class implements decoding audio.
25 \inmodule QtMultimedia
26 \ingroup multimedia
27 \ingroup multimedia_audio
28
29 \preliminary
30
31 The QAudioDecoder class is a high level class for decoding
32 audio media files. It is similar to the QMediaPlayer class except
33 that audio is provided back through this API rather than routed
34 directly to audio hardware.
35
36 \sa QAudioBuffer
37*/
38
39/*!
40 Construct an QAudioDecoder instance with \a parent.
41*/
42QAudioDecoder::QAudioDecoder(QObject *parent) : QObject{ *new QAudioDecoderPrivate(this), parent }
43{
44}
45
46/*!
47 Destroys the audio decoder object.
48*/
49QAudioDecoder::~QAudioDecoder() = default;
50
51/*!
52 Returns true is audio decoding is supported on this platform.
53*/
54bool QAudioDecoder::isSupported() const
55{
56 return true;
57}
58
59/*!
60 \property QAudioDecoder::isDecoding
61 \brief \c true if the decoder is currently running and decoding audio data.
62*/
63bool QAudioDecoder::isDecoding() const
64{
65 Q_D(const QAudioDecoder);
66 return d->decoder->isDecoding();
67}
68
69/*!
70
71 Returns the current error state of the QAudioDecoder.
72*/
73QAudioDecoder::Error QAudioDecoder::error() const
74{
75 Q_D(const QAudioDecoder);
76 return d->decoder->error();
77}
78
79/*!
80 \property QAudioDecoder::error
81
82 Returns a human readable description of the current error, or
83 an empty string is there is no error.
84*/
85QString QAudioDecoder::errorString() const
86{
87 Q_D(const QAudioDecoder);
88 return d->decoder->errorString();
89}
90
91/*!
92 Starts decoding the audio resource.
93
94 As data gets decoded, the \l bufferReady() signal will be emitted
95 when enough data has been decoded. Calling \l read() will then return
96 an audio buffer without blocking.
97
98 If you call read() before a buffer is ready, an invalid buffer will
99 be returned, again without blocking.
100
101 \sa read()
102*/
103void QAudioDecoder::start()
104{
105 Q_D(QAudioDecoder);
106 // Reset error conditions
107 d->decoder->clearError();
108 d->decoder->start();
109}
110
111/*!
112 Stop decoding audio. Calling \l start() again will resume decoding from the beginning.
113*/
114void QAudioDecoder::stop()
115{
116 Q_D(QAudioDecoder);
117 d->decoder->stop();
118}
119
120/*!
121 Returns the current file name to decode.
122 If \l setSourceDevice was called, this will
123 be empty.
124*/
125QUrl QAudioDecoder::source() const
126{
127 Q_D(const QAudioDecoder);
128 return d->unresolvedUrl;
129}
130
131/*!
132 Sets the current audio file name to \a fileName.
133
134 When this property is set any current decoding is stopped,
135 and any audio buffers are discarded.
136
137 You can only specify either a source filename or
138 a source QIODevice. Setting one will unset the other.
139*/
140void QAudioDecoder::setSource(const QUrl &fileName)
141{
142 using namespace QtMultimediaPrivate;
143
144 Q_D(QAudioDecoder);
145 d->decoder->clearError();
146 d->unresolvedUrl = fileName;
147 d->decoder->setSourceDevice(nullptr);
148 d->qrcFile.reset();
149
150 // Platform decoders generally can't read qrc resources directly; unless the platform
151 // decoder says otherwise, copy the resource to a real file first.
152 if (!fileName.isEmpty() && fileName.scheme() == u"qrc" && !d->decoder->canReadQrc()) {
153 QFile file(u':' + fileName.path());
154 if (!file.open(QFile::ReadOnly)) {
155 d->decoder->error(QAudioDecoder::ResourceError,
156 tr("Attempting to play invalid Qt resource"));
157 return;
158 }
159
160 auto qrcMedia = qCopyQrcToTemporaryFile(file, fileName);
161 if (!qrcMedia) {
162 d->decoder->error(QAudioDecoder::ResourceError, qrcMedia.error());
163 return;
164 }
165
166 d->qrcFile = std::move(qrcMedia->file);
167 d->decoder->setSource(qrcMedia->url);
168 return;
169 }
170
171 QUrl url = qMediaFromUserInput(fileName);
172 d->decoder->setSource(url);
173}
174
175/*!
176 Returns the current source QIODevice, if one was set.
177 If \l setSource() was called, this will be a nullptr.
178*/
179QIODevice *QAudioDecoder::sourceDevice() const
180{
181 Q_D(const QAudioDecoder);
182 return d->decoder->sourceDevice();
183}
184
185/*!
186 Sets the current audio QIODevice to \a device.
187
188 When this property is set any current decoding is stopped,
189 and any audio buffers are discarded.
190
191 You can only specify either a source filename or
192 a source QIODevice. Setting one will unset the other.
193*/
194void QAudioDecoder::setSourceDevice(QIODevice *device)
195{
196 Q_D(QAudioDecoder);
197 d->unresolvedUrl = QUrl{};
198 d->decoder->setSourceDevice(device);
199}
200
201/*!
202 Returns the audio format the decoder is set to.
203
204 \note This may be different than the format of the decoded
205 samples, if the audio format was set to an invalid one.
206
207 \sa setAudioFormat(), formatChanged()
208*/
209QAudioFormat QAudioDecoder::audioFormat() const
210{
211 Q_D(const QAudioDecoder);
212 return d->decoder->audioFormat();
213}
214
215/*!
216 Set the desired audio format for decoded samples to \a format.
217
218 This property can only be set while the decoder is stopped.
219 Setting this property at other times will be ignored.
220
221 If the decoder does not support this format, \l error() will
222 be set to \c FormatError.
223
224 If you do not specify a format, the format of the decoded
225 audio itself will be used. Otherwise, some format conversion
226 will be applied.
227
228 If you wish to reset the decoded format to that of the original
229 audio file, you can specify an invalid \a format.
230
231 \warning Setting a desired audio format is not yet supported
232 on the Android backend. It does work with the default FFMPEG
233 backend.
234*/
235void QAudioDecoder::setAudioFormat(const QAudioFormat &format)
236{
237 if (isDecoding())
238 return;
239
240 Q_D(QAudioDecoder);
241 d->decoder->setAudioFormat(format);
242}
243
244/*!
245 Returns true if a buffer is available to be read,
246 and false otherwise. If there is no buffer available, calling
247 the \l read() function will return an invalid buffer.
248*/
249bool QAudioDecoder::bufferAvailable() const
250{
251 Q_D(const QAudioDecoder);
252 return d->decoder->bufferAvailable();
253}
254
255/*!
256 Returns position (in milliseconds) of the last buffer read from
257 the decoder or -1 if no buffers have been read.
258*/
259
260qint64 QAudioDecoder::position() const
261{
262 Q_D(const QAudioDecoder);
263 return d->decoder->position().count();
264}
265
266/*!
267 Returns total duration (in milliseconds) of the audio stream or -1
268 if not available.
269*/
270
271qint64 QAudioDecoder::duration() const
272{
273 Q_D(const QAudioDecoder);
274 return d->decoder->duration().count();
275}
276
277/*!
278 Read a buffer from the decoder, if one is available. Returns an invalid buffer
279 if there are no decoded buffers currently available, or on failure. In both cases
280 this function will not block.
281
282 You should either respond to the \l bufferReady() signal or check the
283 \l bufferAvailable() function before calling read() to make sure
284 you get useful data.
285*/
286
287QAudioBuffer QAudioDecoder::read() const
288{
289 Q_D(const QAudioDecoder);
290 return d->decoder->read();
291}
292
293// Enums
294/*!
295 \enum QAudioDecoder::Error
296
297 Defines a media player error condition.
298
299 \value NoError No error has occurred.
300 \value ResourceError A media resource couldn't be resolved.
301 \value FormatError The format of a media resource isn't supported.
302 \value AccessDeniedError There are not the appropriate permissions to play a media resource.
303 \value NotSupportedError QAudioDecoder is not supported on this platform
304*/
305
306// Signals
307/*!
308 \fn void QAudioDecoder::error(QAudioDecoder::Error error)
309
310 Signals that an \a error condition has occurred.
311
312 \sa errorString()
313*/
314
315/*!
316 \fn void QAudioDecoder::sourceChanged()
317
318 Signals that the current source of the decoder has changed.
319
320 \sa source(), sourceDevice()
321*/
322
323/*!
324 \fn void QAudioDecoder::formatChanged(const QAudioFormat &format)
325
326 Signals that the current audio format of the decoder has changed to \a format.
327
328 \sa audioFormat(), setAudioFormat()
329*/
330
331/*!
332 \fn void QAudioDecoder::bufferReady()
333
334 Signals that a new decoded audio buffer is available to be read.
335
336 \sa read(), bufferAvailable()
337*/
338
339/*!
340 \fn void QAudioDecoder::bufferAvailableChanged(bool available)
341
342 Signals the availability (if \a available is true) of a new buffer.
343
344 If \a available is false, there are no buffers available.
345
346 \sa bufferAvailable(), bufferReady()
347*/
348
349/*!
350 \fn void QAudioDecoder::finished()
351
352 Signals that the decoding has finished successfully.
353 If decoding fails, error signal is emitted instead.
354
355 \sa start(), stop(), error()
356*/
357
358/*!
359 \fn void QAudioDecoder::positionChanged(qint64 position)
360
361 Signals that the current \a position of the decoder has changed.
362
363 \sa durationChanged()
364*/
365
366/*!
367 \fn void QAudioDecoder::durationChanged(qint64 duration)
368
369 Signals that the estimated \a duration of the decoded data has changed.
370
371 \sa positionChanged()
372*/
373
374// Properties
375/*!
376 \property QAudioDecoder::source
377 \brief the active filename being decoded by the decoder object.
378*/
379
380/*!
381 \property QAudioDecoder::bufferAvailable
382 \brief whether there is a decoded audio buffer available
383*/
384
385namespace {
386std::unique_ptr<QPlatformAudioDecoder> createPlatformDecoder(QAudioDecoder *decoder)
387{
388 // Prefer platform-provided decoder. Allow forcing dr_wav via env var.
389 static const bool forceDrWav = qEnvironmentVariableIsSet("QT_FORCE_DRWAV_AUDIO_DECODER");
390
391 if (!forceDrWav) {
392 auto maybeDecoder = QPlatformMediaIntegration::instance()->createAudioDecoder(decoder);
393 if (maybeDecoder)
394 return std::unique_ptr<QPlatformAudioDecoder>(maybeDecoder.value());
395 qCDebug(qLcAudioDecoder) << "Using dr_wav decoder as fallback";
396 }
397
398 return std::make_unique<QDrWavAudioDecoder>(decoder);
399}
400} // namespace
401
402QAudioDecoderPrivate::QAudioDecoderPrivate(QAudioDecoder *decoder)
403 : decoder(createPlatformDecoder(decoder))
404{
405}
406
407QT_END_NAMESPACE
408
409#include "moc_qaudiodecoder.cpp"
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")