1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qaudiodecoder.h" |
41 | |
42 | #include "qmediaobject_p.h" |
43 | #include <qmediaservice.h> |
44 | #include "qaudiodecodercontrol.h" |
45 | #include <private/qmediaserviceprovider_p.h> |
46 | |
47 | #include <QtCore/qcoreevent.h> |
48 | #include <QtCore/qmetaobject.h> |
49 | #include <QtCore/qtimer.h> |
50 | #include <QtCore/qdebug.h> |
51 | #include <QtCore/qpointer.h> |
52 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | /*! |
56 | \class QAudioDecoder |
57 | \brief The QAudioDecoder class allows decoding audio. |
58 | \inmodule QtMultimedia |
59 | \ingroup multimedia |
60 | \ingroup multimedia_audio |
61 | |
62 | \preliminary |
63 | |
64 | The QAudioDecoder class is a high level class for decoding local |
65 | audio media files. It is similar to the QMediaPlayer class except |
66 | that audio is provided back through this API rather than routed |
67 | directly to audio hardware, and playlists and network and streaming |
68 | based media is not supported. |
69 | |
70 | \sa QAudioBuffer |
71 | */ |
72 | |
73 | static void qRegisterAudioDecoderMetaTypes() |
74 | { |
75 | qRegisterMetaType<QAudioDecoder::State>(typeName: "QAudioDecoder::State" ); |
76 | qRegisterMetaType<QAudioDecoder::Error>(typeName: "QAudioDecoder::Error" ); |
77 | } |
78 | |
79 | Q_CONSTRUCTOR_FUNCTION(qRegisterAudioDecoderMetaTypes) |
80 | |
81 | class QAudioDecoderPrivate : public QMediaObjectPrivate |
82 | { |
83 | Q_DECLARE_NON_CONST_PUBLIC(QAudioDecoder) |
84 | |
85 | public: |
86 | QAudioDecoderPrivate() |
87 | : provider(nullptr) |
88 | , control(nullptr) |
89 | , state(QAudioDecoder::StoppedState) |
90 | , error(QAudioDecoder::NoError) |
91 | {} |
92 | |
93 | QMediaServiceProvider *provider; |
94 | QAudioDecoderControl *control; |
95 | QAudioDecoder::State state; |
96 | QAudioDecoder::Error error; |
97 | QString errorString; |
98 | |
99 | void _q_stateChanged(QAudioDecoder::State state); |
100 | void _q_error(int error, const QString &errorString); |
101 | }; |
102 | |
103 | void QAudioDecoderPrivate::_q_stateChanged(QAudioDecoder::State ps) |
104 | { |
105 | Q_Q(QAudioDecoder); |
106 | |
107 | if (ps != state) { |
108 | state = ps; |
109 | |
110 | emit q->stateChanged(newState: ps); |
111 | } |
112 | } |
113 | |
114 | void QAudioDecoderPrivate::_q_error(int error, const QString &errorString) |
115 | { |
116 | Q_Q(QAudioDecoder); |
117 | |
118 | this->error = QAudioDecoder::Error(error); |
119 | this->errorString = errorString; |
120 | |
121 | emit q->error(error: this->error); |
122 | } |
123 | |
124 | /*! |
125 | Construct an QAudioDecoder instance |
126 | parented to \a parent. |
127 | */ |
128 | QAudioDecoder::QAudioDecoder(QObject *parent) |
129 | : QMediaObject(*new QAudioDecoderPrivate, |
130 | parent, |
131 | QMediaServiceProvider::defaultServiceProvider()->requestService(Q_MEDIASERVICE_AUDIODECODER)) |
132 | { |
133 | Q_D(QAudioDecoder); |
134 | |
135 | d->provider = QMediaServiceProvider::defaultServiceProvider(); |
136 | if (d->service) { |
137 | d->control = qobject_cast<QAudioDecoderControl*>(object: d->service->requestControl(QAudioDecoderControl_iid)); |
138 | if (d->control != nullptr) { |
139 | connect(asender: d->control, SIGNAL(stateChanged(QAudioDecoder::State)), SLOT(_q_stateChanged(QAudioDecoder::State))); |
140 | connect(asender: d->control, SIGNAL(error(int,QString)), SLOT(_q_error(int,QString))); |
141 | |
142 | connect(asender: d->control, SIGNAL(formatChanged(QAudioFormat)), SIGNAL(formatChanged(QAudioFormat))); |
143 | connect(asender: d->control, SIGNAL(sourceChanged()), SIGNAL(sourceChanged())); |
144 | connect(sender: d->control, SIGNAL(bufferReady()), receiver: this, SIGNAL(bufferReady())); |
145 | connect(sender: d->control ,SIGNAL(bufferAvailableChanged(bool)), receiver: this, SIGNAL(bufferAvailableChanged(bool))); |
146 | connect(sender: d->control ,SIGNAL(finished()), receiver: this, SIGNAL(finished())); |
147 | connect(sender: d->control ,SIGNAL(positionChanged(qint64)), receiver: this, SIGNAL(positionChanged(qint64))); |
148 | connect(sender: d->control ,SIGNAL(durationChanged(qint64)), receiver: this, SIGNAL(durationChanged(qint64))); |
149 | } |
150 | } |
151 | if (!d->control) { |
152 | d->error = ServiceMissingError; |
153 | d->errorString = tr(s: "The QAudioDecoder object does not have a valid service" ); |
154 | } |
155 | } |
156 | |
157 | |
158 | /*! |
159 | Destroys the audio decoder object. |
160 | */ |
161 | QAudioDecoder::~QAudioDecoder() |
162 | { |
163 | Q_D(QAudioDecoder); |
164 | |
165 | if (d->service) { |
166 | if (d->control) |
167 | d->service->releaseControl(control: d->control); |
168 | |
169 | d->provider->releaseService(service: d->service); |
170 | } |
171 | } |
172 | |
173 | QAudioDecoder::State QAudioDecoder::state() const |
174 | { |
175 | return d_func()->state; |
176 | } |
177 | |
178 | /*! |
179 | Returns the current error state. |
180 | */ |
181 | |
182 | QAudioDecoder::Error QAudioDecoder::error() const |
183 | { |
184 | return d_func()->error; |
185 | } |
186 | |
187 | QString QAudioDecoder::errorString() const |
188 | { |
189 | return d_func()->errorString; |
190 | } |
191 | |
192 | /*! |
193 | Starts decoding the audio resource. |
194 | |
195 | As data gets decoded, the \l bufferReady() signal will be emitted |
196 | when enough data has been decoded. Calling \l read() will then return |
197 | an audio buffer without blocking. |
198 | |
199 | If you call read() before a buffer is ready, an invalid buffer will |
200 | be returned, again without blocking. |
201 | |
202 | \sa read() |
203 | */ |
204 | void QAudioDecoder::start() |
205 | { |
206 | Q_D(QAudioDecoder); |
207 | |
208 | if (d->control == nullptr) { |
209 | QMetaObject::invokeMethod(obj: this, member: "_q_error" , type: Qt::QueuedConnection, |
210 | Q_ARG(int, QAudioDecoder::ServiceMissingError), |
211 | Q_ARG(QString, tr("The QAudioDecoder object does not have a valid service" ))); |
212 | return; |
213 | } |
214 | |
215 | // Reset error conditions |
216 | d->error = NoError; |
217 | d->errorString.clear(); |
218 | |
219 | d->control->start(); |
220 | } |
221 | |
222 | /*! |
223 | Stop decoding audio. Calling \l start() again will resume decoding from the beginning. |
224 | */ |
225 | void QAudioDecoder::stop() |
226 | { |
227 | Q_D(QAudioDecoder); |
228 | |
229 | if (d->control != nullptr) |
230 | d->control->stop(); |
231 | } |
232 | |
233 | /*! |
234 | Returns the current file name to decode. |
235 | If \l setSourceDevice was called, this will |
236 | be empty. |
237 | */ |
238 | QString QAudioDecoder::sourceFilename() const |
239 | { |
240 | Q_D(const QAudioDecoder); |
241 | if (d->control) |
242 | return d->control->sourceFilename(); |
243 | return QString(); |
244 | } |
245 | |
246 | /*! |
247 | Sets the current audio file name to \a fileName. |
248 | |
249 | When this property is set any current decoding is stopped, |
250 | and any audio buffers are discarded. |
251 | |
252 | You can only specify either a source filename or |
253 | a source QIODevice. Setting one will unset the other. |
254 | */ |
255 | void QAudioDecoder::setSourceFilename(const QString &fileName) |
256 | { |
257 | Q_D(QAudioDecoder); |
258 | |
259 | if (d->control != nullptr) |
260 | d_func()->control->setSourceFilename(fileName); |
261 | } |
262 | |
263 | /*! |
264 | Returns the current source QIODevice, if one was set. |
265 | If \l setSourceFilename() was called, this will be 0. |
266 | */ |
267 | QIODevice *QAudioDecoder::sourceDevice() const |
268 | { |
269 | Q_D(const QAudioDecoder); |
270 | if (d->control) |
271 | return d->control->sourceDevice(); |
272 | return nullptr; |
273 | } |
274 | |
275 | /*! |
276 | Sets the current audio QIODevice to \a device. |
277 | |
278 | When this property is set any current decoding is stopped, |
279 | and any audio buffers are discarded. |
280 | |
281 | You can only specify either a source filename or |
282 | a source QIODevice. Setting one will unset the other. |
283 | */ |
284 | void QAudioDecoder::setSourceDevice(QIODevice *device) |
285 | { |
286 | Q_D(QAudioDecoder); |
287 | |
288 | if (d->control != nullptr) |
289 | d_func()->control->setSourceDevice(device); |
290 | } |
291 | |
292 | /*! |
293 | Returns the current audio format of the decoded stream. |
294 | |
295 | Any buffers returned should have this format. |
296 | |
297 | \sa setAudioFormat(), formatChanged() |
298 | */ |
299 | QAudioFormat QAudioDecoder::audioFormat() const |
300 | { |
301 | Q_D(const QAudioDecoder); |
302 | if (d->control) |
303 | return d->control->audioFormat(); |
304 | return QAudioFormat(); |
305 | } |
306 | |
307 | /*! |
308 | Set the desired audio format for decoded samples to \a format. |
309 | |
310 | This property can only be set while the decoder is stopped. |
311 | Setting this property at other times will be ignored. |
312 | |
313 | If the decoder does not support this format, \l error() will |
314 | be set to \c FormatError. |
315 | |
316 | If you do not specify a format, the format of the decoded |
317 | audio itself will be used. Otherwise, some format conversion |
318 | will be applied. |
319 | |
320 | If you wish to reset the decoded format to that of the original |
321 | audio file, you can specify an invalid \a format. |
322 | */ |
323 | void QAudioDecoder::setAudioFormat(const QAudioFormat &format) |
324 | { |
325 | Q_D(QAudioDecoder); |
326 | |
327 | if (state() != QAudioDecoder::StoppedState) |
328 | return; |
329 | |
330 | if (d->control != nullptr) |
331 | d_func()->control->setAudioFormat(format); |
332 | } |
333 | |
334 | /*! |
335 | \internal |
336 | */ |
337 | |
338 | bool QAudioDecoder::bind(QObject *obj) |
339 | { |
340 | return QMediaObject::bind(obj); |
341 | } |
342 | |
343 | /*! |
344 | \internal |
345 | */ |
346 | |
347 | void QAudioDecoder::unbind(QObject *obj) |
348 | { |
349 | QMediaObject::unbind(obj); |
350 | } |
351 | |
352 | /*! |
353 | Returns the level of support an audio decoder has for a \a mimeType and a set of \a codecs. |
354 | */ |
355 | QMultimedia::SupportEstimate QAudioDecoder::hasSupport(const QString &mimeType, |
356 | const QStringList& codecs) |
357 | { |
358 | return QMediaServiceProvider::defaultServiceProvider()->hasSupport(serviceType: QByteArray(Q_MEDIASERVICE_AUDIODECODER), |
359 | mimeType, |
360 | codecs); |
361 | } |
362 | |
363 | /*! |
364 | Returns true if a buffer is available to be read, |
365 | and false otherwise. If there is no buffer available, calling |
366 | the \l read() function will return an invalid buffer. |
367 | */ |
368 | bool QAudioDecoder::bufferAvailable() const |
369 | { |
370 | Q_D(const QAudioDecoder); |
371 | if (d->control) |
372 | return d->control->bufferAvailable(); |
373 | return false; |
374 | } |
375 | |
376 | /*! |
377 | Returns position (in milliseconds) of the last buffer read from |
378 | the decoder or -1 if no buffers have been read. |
379 | */ |
380 | |
381 | qint64 QAudioDecoder::position() const |
382 | { |
383 | Q_D(const QAudioDecoder); |
384 | if (d->control) |
385 | return d->control->position(); |
386 | return -1; |
387 | } |
388 | |
389 | /*! |
390 | Returns total duration (in milliseconds) of the audio stream or -1 |
391 | if not available. |
392 | */ |
393 | |
394 | qint64 QAudioDecoder::duration() const |
395 | { |
396 | Q_D(const QAudioDecoder); |
397 | if (d->control) |
398 | return d->control->duration(); |
399 | return -1; |
400 | } |
401 | |
402 | /*! |
403 | Read a buffer from the decoder, if one is available. Returns an invalid buffer |
404 | if there are no decoded buffers currently available, or on failure. In both cases |
405 | this function will not block. |
406 | |
407 | You should either respond to the \l bufferReady() signal or check the |
408 | \l bufferAvailable() function before calling read() to make sure |
409 | you get useful data. |
410 | */ |
411 | |
412 | QAudioBuffer QAudioDecoder::read() const |
413 | { |
414 | Q_D(const QAudioDecoder); |
415 | |
416 | if (d->control) { |
417 | return d->control->read(); |
418 | } else { |
419 | return QAudioBuffer(); |
420 | } |
421 | } |
422 | |
423 | // Enums |
424 | /*! |
425 | \enum QAudioDecoder::State |
426 | |
427 | Defines the current state of a media player. |
428 | |
429 | \value StoppedState The decoder is not decoding. Decoding will |
430 | start at the start of the media. |
431 | \value DecodingState The audio player is currently decoding media. |
432 | */ |
433 | |
434 | /*! |
435 | \enum QAudioDecoder::Error |
436 | |
437 | Defines a media player error condition. |
438 | |
439 | \value NoError No error has occurred. |
440 | \value ResourceError A media resource couldn't be resolved. |
441 | \value FormatError The format of a media resource isn't supported. |
442 | \value AccessDeniedError There are not the appropriate permissions to play a media resource. |
443 | \value ServiceMissingError A valid playback service was not found, playback cannot proceed. |
444 | */ |
445 | |
446 | // Signals |
447 | /*! |
448 | \fn QAudioDecoder::error(QAudioDecoder::Error error) |
449 | |
450 | Signals that an \a error condition has occurred. |
451 | |
452 | \sa errorString() |
453 | */ |
454 | |
455 | /*! |
456 | \fn void QAudioDecoder::stateChanged(State state) |
457 | |
458 | Signal the \a state of the decoder object has changed. |
459 | */ |
460 | |
461 | /*! |
462 | \fn void QAudioDecoder::sourceChanged() |
463 | |
464 | Signals that the current source of the decoder has changed. |
465 | |
466 | \sa sourceFilename(), sourceDevice() |
467 | */ |
468 | |
469 | /*! |
470 | \fn void QAudioDecoder::formatChanged(const QAudioFormat &format) |
471 | |
472 | Signals that the current audio format of the decoder has changed to \a format. |
473 | |
474 | \sa audioFormat(), setAudioFormat() |
475 | */ |
476 | |
477 | /*! |
478 | \fn void QAudioDecoder::bufferReady() |
479 | |
480 | Signals that a new decoded audio buffer is available to be read. |
481 | |
482 | \sa read(), bufferAvailable() |
483 | */ |
484 | |
485 | /*! |
486 | \fn void QAudioDecoder::bufferAvailableChanged(bool available) |
487 | |
488 | Signals the availability (if \a available is true) of a new buffer. |
489 | |
490 | If \a available is false, there are no buffers available. |
491 | |
492 | \sa bufferAvailable(), bufferReady() |
493 | */ |
494 | |
495 | /*! |
496 | \fn void QAudioDecoder::finished() |
497 | |
498 | Signals that the decoding has finished successfully. |
499 | If decoding fails, error signal is emitted instead. |
500 | |
501 | \sa start(), stop(), error() |
502 | */ |
503 | |
504 | /*! |
505 | \fn void QAudioDecoder::positionChanged(qint64 position) |
506 | |
507 | Signals that the current \a position of the decoder has changed. |
508 | |
509 | \sa durationChanged() |
510 | */ |
511 | |
512 | /*! |
513 | \fn void QAudioDecoder::durationChanged(qint64 duration) |
514 | |
515 | Signals that the estimated \a duration of the decoded data has changed. |
516 | |
517 | \sa positionChanged() |
518 | */ |
519 | |
520 | |
521 | // Properties |
522 | /*! |
523 | \property QAudioDecoder::state |
524 | \brief the audio decoder's playback state. |
525 | |
526 | By default this property is QAudioDecoder::Stopped |
527 | |
528 | \sa start(), stop() |
529 | */ |
530 | |
531 | /*! |
532 | \property QAudioDecoder::error |
533 | \brief a string describing the last error condition. |
534 | |
535 | \sa error() |
536 | */ |
537 | |
538 | /*! |
539 | \property QAudioDecoder::sourceFilename |
540 | \brief the active filename being decoded by the decoder object. |
541 | */ |
542 | |
543 | /*! |
544 | \property QAudioDecoder::bufferAvailable |
545 | \brief whether there is a decoded audio buffer available |
546 | */ |
547 | |
548 | QT_END_NAMESPACE |
549 | |
550 | #include "moc_qaudiodecoder.cpp" |
551 | |