| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qaudiodecoder.h" |
| |
| #include "qmediaobject_p.h" |
| #include <qmediaservice.h> |
| #include "qaudiodecodercontrol.h" |
| #include <private/qmediaserviceprovider_p.h> |
| |
| #include <QtCore/qcoreevent.h> |
| #include <QtCore/qmetaobject.h> |
| #include <QtCore/qtimer.h> |
| #include <QtCore/qdebug.h> |
| #include <QtCore/qpointer.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \class QAudioDecoder |
| \brief The QAudioDecoder class allows decoding audio. |
| \inmodule QtMultimedia |
| \ingroup multimedia |
| \ingroup multimedia_audio |
| |
| \preliminary |
| |
| The QAudioDecoder class is a high level class for decoding local |
| audio media files. It is similar to the QMediaPlayer class except |
| that audio is provided back through this API rather than routed |
| directly to audio hardware, and playlists and network and streaming |
| based media is not supported. |
| |
| \sa QAudioBuffer |
| */ |
| |
| static void qRegisterAudioDecoderMetaTypes() |
| { |
| qRegisterMetaType<QAudioDecoder::State>("QAudioDecoder::State"); |
| qRegisterMetaType<QAudioDecoder::Error>("QAudioDecoder::Error"); |
| } |
| |
| Q_CONSTRUCTOR_FUNCTION(qRegisterAudioDecoderMetaTypes) |
| |
| class QAudioDecoderPrivate : public QMediaObjectPrivate |
| { |
| Q_DECLARE_NON_CONST_PUBLIC(QAudioDecoder) |
| |
| public: |
| QAudioDecoderPrivate() |
| : provider(nullptr) |
| , control(nullptr) |
| , state(QAudioDecoder::StoppedState) |
| , error(QAudioDecoder::NoError) |
| {} |
| |
| QMediaServiceProvider *provider; |
| QAudioDecoderControl *control; |
| QAudioDecoder::State state; |
| QAudioDecoder::Error error; |
| QString errorString; |
| |
| void _q_stateChanged(QAudioDecoder::State state); |
| void _q_error(int error, const QString &errorString); |
| }; |
| |
| void QAudioDecoderPrivate::_q_stateChanged(QAudioDecoder::State ps) |
| { |
| Q_Q(QAudioDecoder); |
| |
| if (ps != state) { |
| state = ps; |
| |
| emit q->stateChanged(ps); |
| } |
| } |
| |
| void QAudioDecoderPrivate::_q_error(int error, const QString &errorString) |
| { |
| Q_Q(QAudioDecoder); |
| |
| this->error = QAudioDecoder::Error(error); |
| this->errorString = errorString; |
| |
| emit q->error(this->error); |
| } |
| |
| /*! |
| Construct an QAudioDecoder instance |
| parented to \a parent. |
| */ |
| QAudioDecoder::QAudioDecoder(QObject *parent) |
| : QMediaObject(*new QAudioDecoderPrivate, |
| parent, |
| QMediaServiceProvider::defaultServiceProvider()->requestService(Q_MEDIASERVICE_AUDIODECODER)) |
| { |
| Q_D(QAudioDecoder); |
| |
| d->provider = QMediaServiceProvider::defaultServiceProvider(); |
| if (d->service) { |
| d->control = qobject_cast<QAudioDecoderControl*>(d->service->requestControl(QAudioDecoderControl_iid)); |
| if (d->control != nullptr) { |
| connect(d->control, SIGNAL(stateChanged(QAudioDecoder::State)), SLOT(_q_stateChanged(QAudioDecoder::State))); |
| connect(d->control, SIGNAL(error(int,QString)), SLOT(_q_error(int,QString))); |
| |
| connect(d->control, SIGNAL(formatChanged(QAudioFormat)), SIGNAL(formatChanged(QAudioFormat))); |
| connect(d->control, SIGNAL(sourceChanged()), SIGNAL(sourceChanged())); |
| connect(d->control, SIGNAL(bufferReady()), this, SIGNAL(bufferReady())); |
| connect(d->control ,SIGNAL(bufferAvailableChanged(bool)), this, SIGNAL(bufferAvailableChanged(bool))); |
| connect(d->control ,SIGNAL(finished()), this, SIGNAL(finished())); |
| connect(d->control ,SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64))); |
| connect(d->control ,SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); |
| } |
| } |
| if (!d->control) { |
| d->error = ServiceMissingError; |
| d->errorString = tr("The QAudioDecoder object does not have a valid service"); |
| } |
| } |
| |
| |
| /*! |
| Destroys the audio decoder object. |
| */ |
| QAudioDecoder::~QAudioDecoder() |
| { |
| Q_D(QAudioDecoder); |
| |
| if (d->service) { |
| if (d->control) |
| d->service->releaseControl(d->control); |
| |
| d->provider->releaseService(d->service); |
| } |
| } |
| |
| QAudioDecoder::State QAudioDecoder::state() const |
| { |
| return d_func()->state; |
| } |
| |
| /*! |
| Returns the current error state. |
| */ |
| |
| QAudioDecoder::Error QAudioDecoder::error() const |
| { |
| return d_func()->error; |
| } |
| |
| QString QAudioDecoder::errorString() const |
| { |
| return d_func()->errorString; |
| } |
| |
| /*! |
| Starts decoding the audio resource. |
| |
| As data gets decoded, the \l bufferReady() signal will be emitted |
| when enough data has been decoded. Calling \l read() will then return |
| an audio buffer without blocking. |
| |
| If you call read() before a buffer is ready, an invalid buffer will |
| be returned, again without blocking. |
| |
| \sa read() |
| */ |
| void QAudioDecoder::start() |
| { |
| Q_D(QAudioDecoder); |
| |
| if (d->control == nullptr) { |
| QMetaObject::invokeMethod(this, "_q_error", Qt::QueuedConnection, |
| Q_ARG(int, QAudioDecoder::ServiceMissingError), |
| Q_ARG(QString, tr("The QAudioDecoder object does not have a valid service"))); |
| return; |
| } |
| |
| // Reset error conditions |
| d->error = NoError; |
| d->errorString.clear(); |
| |
| d->control->start(); |
| } |
| |
| /*! |
| Stop decoding audio. Calling \l start() again will resume decoding from the beginning. |
| */ |
| void QAudioDecoder::stop() |
| { |
| Q_D(QAudioDecoder); |
| |
| if (d->control != nullptr) |
| d->control->stop(); |
| } |
| |
| /*! |
| Returns the current file name to decode. |
| If \l setSourceDevice was called, this will |
| be empty. |
| */ |
| QString QAudioDecoder::sourceFilename() const |
| { |
| Q_D(const QAudioDecoder); |
| if (d->control) |
| return d->control->sourceFilename(); |
| return QString(); |
| } |
| |
| /*! |
| Sets the current audio file name to \a fileName. |
| |
| When this property is set any current decoding is stopped, |
| and any audio buffers are discarded. |
| |
| You can only specify either a source filename or |
| a source QIODevice. Setting one will unset the other. |
| */ |
| void QAudioDecoder::setSourceFilename(const QString &fileName) |
| { |
| Q_D(QAudioDecoder); |
| |
| if (d->control != nullptr) |
| d_func()->control->setSourceFilename(fileName); |
| } |
| |
| /*! |
| Returns the current source QIODevice, if one was set. |
| If \l setSourceFilename() was called, this will be 0. |
| */ |
| QIODevice *QAudioDecoder::sourceDevice() const |
| { |
| Q_D(const QAudioDecoder); |
| if (d->control) |
| return d->control->sourceDevice(); |
| return nullptr; |
| } |
| |
| /*! |
| Sets the current audio QIODevice to \a device. |
| |
| When this property is set any current decoding is stopped, |
| and any audio buffers are discarded. |
| |
| You can only specify either a source filename or |
| a source QIODevice. Setting one will unset the other. |
| */ |
| void QAudioDecoder::setSourceDevice(QIODevice *device) |
| { |
| Q_D(QAudioDecoder); |
| |
| if (d->control != nullptr) |
| d_func()->control->setSourceDevice(device); |
| } |
| |
| /*! |
| Returns the current audio format of the decoded stream. |
| |
| Any buffers returned should have this format. |
| |
| \sa setAudioFormat(), formatChanged() |
| */ |
| QAudioFormat QAudioDecoder::audioFormat() const |
| { |
| Q_D(const QAudioDecoder); |
| if (d->control) |
| return d->control->audioFormat(); |
| return QAudioFormat(); |
| } |
| |
| /*! |
| Set the desired audio format for decoded samples to \a format. |
| |
| This property can only be set while the decoder is stopped. |
| Setting this property at other times will be ignored. |
| |
| If the decoder does not support this format, \l error() will |
| be set to \c FormatError. |
| |
| If you do not specify a format, the format of the decoded |
| audio itself will be used. Otherwise, some format conversion |
| will be applied. |
| |
| If you wish to reset the decoded format to that of the original |
| audio file, you can specify an invalid \a format. |
| */ |
| void QAudioDecoder::setAudioFormat(const QAudioFormat &format) |
| { |
| Q_D(QAudioDecoder); |
| |
| if (state() != QAudioDecoder::StoppedState) |
| return; |
| |
| if (d->control != nullptr) |
| d_func()->control->setAudioFormat(format); |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| bool QAudioDecoder::bind(QObject *obj) |
| { |
| return QMediaObject::bind(obj); |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QAudioDecoder::unbind(QObject *obj) |
| { |
| QMediaObject::unbind(obj); |
| } |
| |
| /*! |
| Returns the level of support an audio decoder has for a \a mimeType and a set of \a codecs. |
| */ |
| QMultimedia::SupportEstimate QAudioDecoder::hasSupport(const QString &mimeType, |
| const QStringList& codecs) |
| { |
| return QMediaServiceProvider::defaultServiceProvider()->hasSupport(QByteArray(Q_MEDIASERVICE_AUDIODECODER), |
| mimeType, |
| codecs); |
| } |
| |
| /*! |
| Returns true if a buffer is available to be read, |
| and false otherwise. If there is no buffer available, calling |
| the \l read() function will return an invalid buffer. |
| */ |
| bool QAudioDecoder::bufferAvailable() const |
| { |
| Q_D(const QAudioDecoder); |
| if (d->control) |
| return d->control->bufferAvailable(); |
| return false; |
| } |
| |
| /*! |
| Returns position (in milliseconds) of the last buffer read from |
| the decoder or -1 if no buffers have been read. |
| */ |
| |
| qint64 QAudioDecoder::position() const |
| { |
| Q_D(const QAudioDecoder); |
| if (d->control) |
| return d->control->position(); |
| return -1; |
| } |
| |
| /*! |
| Returns total duration (in milliseconds) of the audio stream or -1 |
| if not available. |
| */ |
| |
| qint64 QAudioDecoder::duration() const |
| { |
| Q_D(const QAudioDecoder); |
| if (d->control) |
| return d->control->duration(); |
| return -1; |
| } |
| |
| /*! |
| Read a buffer from the decoder, if one is available. Returns an invalid buffer |
| if there are no decoded buffers currently available, or on failure. In both cases |
| this function will not block. |
| |
| You should either respond to the \l bufferReady() signal or check the |
| \l bufferAvailable() function before calling read() to make sure |
| you get useful data. |
| */ |
| |
| QAudioBuffer QAudioDecoder::read() const |
| { |
| Q_D(const QAudioDecoder); |
| |
| if (d->control) { |
| return d->control->read(); |
| } else { |
| return QAudioBuffer(); |
| } |
| } |
| |
| // Enums |
| /*! |
| \enum QAudioDecoder::State |
| |
| Defines the current state of a media player. |
| |
| \value StoppedState The decoder is not decoding. Decoding will |
| start at the start of the media. |
| \value DecodingState The audio player is currently decoding media. |
| */ |
| |
| /*! |
| \enum QAudioDecoder::Error |
| |
| Defines a media player error condition. |
| |
| \value NoError No error has occurred. |
| \value ResourceError A media resource couldn't be resolved. |
| \value FormatError The format of a media resource isn't supported. |
| \value AccessDeniedError There are not the appropriate permissions to play a media resource. |
| \value ServiceMissingError A valid playback service was not found, playback cannot proceed. |
| */ |
| |
| // Signals |
| /*! |
| \fn QAudioDecoder::error(QAudioDecoder::Error error) |
| |
| Signals that an \a error condition has occurred. |
| |
| \sa errorString() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::stateChanged(State state) |
| |
| Signal the \a state of the decoder object has changed. |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::sourceChanged() |
| |
| Signals that the current source of the decoder has changed. |
| |
| \sa sourceFilename(), sourceDevice() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::formatChanged(const QAudioFormat &format) |
| |
| Signals that the current audio format of the decoder has changed to \a format. |
| |
| \sa audioFormat(), setAudioFormat() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::bufferReady() |
| |
| Signals that a new decoded audio buffer is available to be read. |
| |
| \sa read(), bufferAvailable() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::bufferAvailableChanged(bool available) |
| |
| Signals the availability (if \a available is true) of a new buffer. |
| |
| If \a available is false, there are no buffers available. |
| |
| \sa bufferAvailable(), bufferReady() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::finished() |
| |
| Signals that the decoding has finished successfully. |
| If decoding fails, error signal is emitted instead. |
| |
| \sa start(), stop(), error() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::positionChanged(qint64 position) |
| |
| Signals that the current \a position of the decoder has changed. |
| |
| \sa durationChanged() |
| */ |
| |
| /*! |
| \fn void QAudioDecoder::durationChanged(qint64 duration) |
| |
| Signals that the estimated \a duration of the decoded data has changed. |
| |
| \sa positionChanged() |
| */ |
| |
| |
| // Properties |
| /*! |
| \property QAudioDecoder::state |
| \brief the audio decoder's playback state. |
| |
| By default this property is QAudioDecoder::Stopped |
| |
| \sa start(), stop() |
| */ |
| |
| /*! |
| \property QAudioDecoder::error |
| \brief a string describing the last error condition. |
| |
| \sa error() |
| */ |
| |
| /*! |
| \property QAudioDecoder::sourceFilename |
| \brief the active filename being decoded by the decoder object. |
| */ |
| |
| /*! |
| \property QAudioDecoder::bufferAvailable |
| \brief whether there is a decoded audio buffer available |
| */ |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qaudiodecoder.cpp" |