/****************************************************************************
**
** 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 "qmediarecorder.h"
#include "qmediarecorder_p.h"

#include <qmediarecordercontrol.h>
#include "qmediaobject_p.h"
#include <qmediaservice.h>
#include <qmediaserviceprovider_p.h>
#include <qmetadatawritercontrol.h>
#include <qaudioencodersettingscontrol.h>
#include <qvideoencodersettingscontrol.h>
#include <qmediacontainercontrol.h>
#include <qmediaavailabilitycontrol.h>
#include <qcamera.h>
#include <qcameracontrol.h>

#include <QtCore/qdebug.h>
#include <QtCore/qurl.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qmetaobject.h>

#include <qaudioformat.h>

QT_BEGIN_NAMESPACE

/*!
    \class QMediaRecorder
    \inmodule QtMultimedia
    \ingroup multimedia
    \ingroup multimedia_recording

    \brief The QMediaRecorder class is used for the recording of media content.

    The QMediaRecorder class is a high level media recording class.  It's not
    intended to be used alone but for accessing the media recording functions
    of other media objects, like QRadioTuner, or QCamera.

    \snippet multimedia-snippets/media.cpp Media recorder

    \sa QAudioRecorder
*/

static void qRegisterMediaRecorderMetaTypes()
{
    qRegisterMetaType<QMediaRecorder::State>("QMediaRecorder::State");
    qRegisterMetaType<QMediaRecorder::Status>("QMediaRecorder::Status");
    qRegisterMetaType<QMediaRecorder::Error>("QMediaRecorder::Error");
}

Q_CONSTRUCTOR_FUNCTION(qRegisterMediaRecorderMetaTypes)


QMediaRecorderPrivate::QMediaRecorderPrivate():
     mediaObject(nullptr),
     control(nullptr),
     formatControl(nullptr),
     audioControl(nullptr),
     videoControl(nullptr),
     metaDataControl(nullptr),
     availabilityControl(nullptr),
     settingsChanged(false),
     notifyTimer(nullptr),
     state(QMediaRecorder::StoppedState),
     error(QMediaRecorder::NoError)
{
}

#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))

void QMediaRecorderPrivate::_q_stateChanged(QMediaRecorder::State ps)
{
    Q_Q(QMediaRecorder);

    if (ps == QMediaRecorder::RecordingState)
        notifyTimer->start();
    else
        notifyTimer->stop();

//    qDebug() << "Recorder state changed:" << ENUM_NAME(QMediaRecorder,"State",ps);
    if (state != ps) {
        emit q->stateChanged(ps);
    }

    state = ps;
}


void QMediaRecorderPrivate::_q_error(int error, const QString &errorString)
{
    Q_Q(QMediaRecorder);

    this->error = QMediaRecorder::Error(error);
    this->errorString = errorString;

    emit q->error(this->error);
}

void QMediaRecorderPrivate::_q_serviceDestroyed()
{
    mediaObject = nullptr;
    control = nullptr;
    formatControl = nullptr;
    audioControl = nullptr;
    videoControl = nullptr;
    metaDataControl = nullptr;
    availabilityControl = nullptr;
    settingsChanged = true;
}

void QMediaRecorderPrivate::_q_updateActualLocation(const QUrl &location)
{
    if (actualLocation != location) {
        actualLocation = location;
        emit q_func()->actualLocationChanged(actualLocation);
    }
}

void QMediaRecorderPrivate::_q_notify()
{
    emit q_func()->durationChanged(q_func()->duration());
}

void QMediaRecorderPrivate::_q_updateNotifyInterval(int ms)
{
    notifyTimer->setInterval(ms);
}

void QMediaRecorderPrivate::applySettingsLater()
{
    if (control && !settingsChanged) {
        settingsChanged = true;
        QMetaObject::invokeMethod(q_func(), "_q_applySettings", Qt::QueuedConnection);
    }
}

void QMediaRecorderPrivate::_q_applySettings()
{
    if (control && settingsChanged) {
        settingsChanged = false;
        control->applySettings();
    }
}

void QMediaRecorderPrivate::_q_availabilityChanged(QMultimedia::AvailabilityStatus availability)
{
    Q_Q(QMediaRecorder);
    Q_UNUSED(error)
    Q_UNUSED(availability)

    // Really this should not always emit, but
    // we can't really tell from here (isAvailable
    // may not have changed, or the mediaobject's overridden
    // availability() may not have changed).
    q->availabilityChanged(q->availability());
    q->availabilityChanged(q->isAvailable());
}

void QMediaRecorderPrivate::restartCamera()
{
    //restart camera if it can't apply new settings in the Active state
    QCamera *camera = qobject_cast<QCamera*>(mediaObject);
    if (camera && camera->captureMode() == QCamera::CaptureVideo) {
        QMetaObject::invokeMethod(camera,
                                  "_q_preparePropertyChange",
                                  Qt::DirectConnection,
                                  Q_ARG(int, QCameraControl::VideoEncodingSettings));
    }
}


/*!
    Constructs a media recorder which records the media produced by \a mediaObject.

    The \a parent is passed to QMediaObject.
*/

QMediaRecorder::QMediaRecorder(QMediaObject *mediaObject, QObject *parent):
    QObject(parent),
    d_ptr(new QMediaRecorderPrivate)
{
    Q_D(QMediaRecorder);
    d->q_ptr = this;

    d->notifyTimer = new QTimer(this);
    connect(d->notifyTimer, SIGNAL(timeout()), SLOT(_q_notify()));

    setMediaObject(mediaObject);
}

/*!
    \internal
*/
QMediaRecorder::QMediaRecorder(QMediaRecorderPrivate &dd, QMediaObject *mediaObject, QObject *parent):
    QObject(parent),
    d_ptr(&dd)
{
    Q_D(QMediaRecorder);
    d->q_ptr = this;

    d->notifyTimer = new QTimer(this);
    connect(d->notifyTimer, SIGNAL(timeout()), SLOT(_q_notify()));

    setMediaObject(mediaObject);
}

/*!
    Destroys a media recorder object.
*/

QMediaRecorder::~QMediaRecorder()
{
    delete d_ptr;
}

/*!
    Returns the QMediaObject instance that this QMediaRecorder is bound too,
    or 0 otherwise.
*/
QMediaObject *QMediaRecorder::mediaObject() const
{
    return d_func()->mediaObject;
}

/*!
    \internal
*/
bool QMediaRecorder::setMediaObject(QMediaObject *object)
{
    Q_D(QMediaRecorder);

    if (object == d->mediaObject)
        return true;

    if (d->mediaObject) {
        if (d->control) {
            disconnect(d->control, SIGNAL(stateChanged(QMediaRecorder::State)),
                       this, SLOT(_q_stateChanged(QMediaRecorder::State)));

            disconnect(d->control, SIGNAL(statusChanged(QMediaRecorder::Status)),
                       this, SIGNAL(statusChanged(QMediaRecorder::Status)));

            disconnect(d->control, SIGNAL(mutedChanged(bool)),
                       this, SIGNAL(mutedChanged(bool)));

            disconnect(d->control, SIGNAL(volumeChanged(qreal)),
                       this, SIGNAL(volumeChanged(qreal)));

            disconnect(d->control, SIGNAL(durationChanged(qint64)),
                       this, SIGNAL(durationChanged(qint64)));

            disconnect(d->control, SIGNAL(actualLocationChanged(QUrl)),
                       this, SLOT(_q_updateActualLocation(QUrl)));

            disconnect(d->control, SIGNAL(error(int,QString)),
                       this, SLOT(_q_error(int,QString)));
        }

        disconnect(d->mediaObject, SIGNAL(notifyIntervalChanged(int)), this, SLOT(_q_updateNotifyInterval(int)));

        QMediaService *service = d->mediaObject->service();

        if (service) {
            disconnect(service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));

            if (d->control)
                service->releaseControl(d->control);
            if (d->formatControl)
                service->releaseControl(d->formatControl);
            if (d->audioControl)
                service->releaseControl(d->audioControl);
            if (d->videoControl)
                service->releaseControl(d->videoControl);
            if (d->metaDataControl) {
                disconnect(d->metaDataControl, SIGNAL(metaDataChanged()),
                        this, SIGNAL(metaDataChanged()));
                disconnect(d->metaDataControl, SIGNAL(metaDataChanged(QString,QVariant)),
                        this, SIGNAL(metaDataChanged(QString,QVariant)));
                disconnect(d->metaDataControl, SIGNAL(metaDataAvailableChanged(bool)),
                        this, SIGNAL(metaDataAvailableChanged(bool)));
                disconnect(d->metaDataControl, SIGNAL(writableChanged(bool)),
                        this, SIGNAL(metaDataWritableChanged(bool)));

                service->releaseControl(d->metaDataControl);
            }
            if (d->availabilityControl) {
                disconnect(d->availabilityControl, SIGNAL(availabilityChanged(QMultimedia::AvailabilityStatus)),
                           this, SLOT(_q_availabilityChanged(QMultimedia::AvailabilityStatus)));
                service->releaseControl(d->availabilityControl);
            }
        }
    }

    d->control = nullptr;
    d->formatControl = nullptr;
    d->audioControl = nullptr;
    d->videoControl = nullptr;
    d->metaDataControl = nullptr;
    d->availabilityControl = nullptr;

    d->mediaObject = object;

    if (d->mediaObject) {
        QMediaService *service = d->mediaObject->service();

        d->notifyTimer->setInterval(d->mediaObject->notifyInterval());
        connect(d->mediaObject, SIGNAL(notifyIntervalChanged(int)), SLOT(_q_updateNotifyInterval(int)));

        if (service) {
            d->control = qobject_cast<QMediaRecorderControl*>(service->requestControl(QMediaRecorderControl_iid));

            if (d->control) {
                d->formatControl = qobject_cast<QMediaContainerControl *>(service->requestControl(QMediaContainerControl_iid));
                d->audioControl = qobject_cast<QAudioEncoderSettingsControl *>(service->requestControl(QAudioEncoderSettingsControl_iid));
                d->videoControl = qobject_cast<QVideoEncoderSettingsControl *>(service->requestControl(QVideoEncoderSettingsControl_iid));

                QMediaControl *control = service->requestControl(QMetaDataWriterControl_iid);
                if (control) {
                    d->metaDataControl = qobject_cast<QMetaDataWriterControl *>(control);
                    if (!d->metaDataControl) {
                        service->releaseControl(control);
                    } else {
                        connect(d->metaDataControl,
                                SIGNAL(metaDataChanged()),
                                SIGNAL(metaDataChanged()));
                        connect(d->metaDataControl, SIGNAL(metaDataChanged(QString,QVariant)),
                                this, SIGNAL(metaDataChanged(QString,QVariant)));
                        connect(d->metaDataControl,
                                SIGNAL(metaDataAvailableChanged(bool)),
                                SIGNAL(metaDataAvailableChanged(bool)));
                        connect(d->metaDataControl,
                                SIGNAL(writableChanged(bool)),
                                SIGNAL(metaDataWritableChanged(bool)));
                    }
                }

                d->availabilityControl = service->requestControl<QMediaAvailabilityControl*>();
                if (d->availabilityControl) {
                    connect(d->availabilityControl, SIGNAL(availabilityChanged(QMultimedia::AvailabilityStatus)),
                            this, SLOT(_q_availabilityChanged(QMultimedia::AvailabilityStatus)));
                }

                connect(d->control, SIGNAL(stateChanged(QMediaRecorder::State)),
                        this, SLOT(_q_stateChanged(QMediaRecorder::State)));

                connect(d->control, SIGNAL(statusChanged(QMediaRecorder::Status)),
                        this, SIGNAL(statusChanged(QMediaRecorder::Status)));

                connect(d->control, SIGNAL(mutedChanged(bool)),
                        this, SIGNAL(mutedChanged(bool)));

                connect(d->control, SIGNAL(volumeChanged(qreal)),
                        this, SIGNAL(volumeChanged(qreal)));

                connect(d->control, SIGNAL(durationChanged(qint64)),
                        this, SIGNAL(durationChanged(qint64)));

                connect(d->control, SIGNAL(actualLocationChanged(QUrl)),
                        this, SLOT(_q_updateActualLocation(QUrl)));

                connect(d->control, SIGNAL(error(int,QString)),
                        this, SLOT(_q_error(int,QString)));

                connect(service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));


                d->applySettingsLater();

                return true;
            }
        }

        d->mediaObject = nullptr;
        return false;
    }

    return true;
}

/*!
    \property QMediaRecorder::outputLocation
    \brief the destination location of media content.

    Setting the location can fail, for example when the service supports only
    local file system locations but a network URL was passed. If the service
    does not support media recording this setting the output location will
    always fail.

    The \a location can be relative or empty;
    in this case the recorder uses the system specific place and file naming scheme.
    After recording has stated, QMediaRecorder::outputLocation() returns the actual output location.
*/

/*!
    \property QMediaRecorder::actualLocation
    \brief the actual location of the last media content.

    The actual location is usually available after recording starts,
    and reset when new location is set or new recording starts.
*/

/*!
    Returns true if media recorder service ready to use.

    \sa availabilityChanged()
*/
bool QMediaRecorder::isAvailable() const
{
    return availability() == QMultimedia::Available;
}

/*!
    Returns the availability of this functionality.

    \sa availabilityChanged()
*/
QMultimedia::AvailabilityStatus QMediaRecorder::availability() const
{
    if (d_func()->control == nullptr)
        return QMultimedia::ServiceMissing;

    if (d_func()->availabilityControl)
        return d_func()->availabilityControl->availability();

    return QMultimedia::Available;
}

QUrl QMediaRecorder::outputLocation() const
{
    return d_func()->control ? d_func()->control->outputLocation() : QUrl();
}

bool QMediaRecorder::setOutputLocation(const QUrl &location)
{
    Q_D(QMediaRecorder);
    d->actualLocation.clear();
    return d->control ? d->control->setOutputLocation(location) : false;
}

QUrl QMediaRecorder::actualLocation() const
{
    return d_func()->actualLocation;
}

/*!
    Returns the current media recorder state.

    \sa QMediaRecorder::State
*/

QMediaRecorder::State QMediaRecorder::state() const
{
    return d_func()->control ? QMediaRecorder::State(d_func()->control->state()) : StoppedState;
}

/*!
    Returns the current media recorder status.

    \sa QMediaRecorder::Status
*/

QMediaRecorder::Status QMediaRecorder::status() const
{
    return d_func()->control ? QMediaRecorder::Status(d_func()->control->status()) : UnavailableStatus;
}

/*!
    Returns the current error state.

    \sa errorString()
*/

QMediaRecorder::Error QMediaRecorder::error() const
{
    return d_func()->error;
}

/*!
    Returns a string describing the current error state.

    \sa error()
*/

QString QMediaRecorder::errorString() const
{
    return d_func()->errorString;
}

/*!
    \property QMediaRecorder::duration

    \brief the recorded media duration in milliseconds.
*/

qint64 QMediaRecorder::duration() const
{
    return d_func()->control ? d_func()->control->duration() : 0;
}

/*!
    \property QMediaRecorder::muted

    \brief whether a recording audio stream is muted.
*/

bool QMediaRecorder::isMuted() const
{
    return d_func()->control ? d_func()->control->isMuted() : 0;
}

void QMediaRecorder::setMuted(bool muted)
{
    Q_D(QMediaRecorder);

    if (d->control)
        d->control->setMuted(muted);
}

/*!
    \property QMediaRecorder::volume

    \brief the current recording audio volume.

    The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
    range will be clamped.

    The default volume is \c 1.0.

    UI volume controls should usually be scaled nonlinearly. For example, using a logarithmic scale
    will produce linear changes in perceived loudness, which is what a user would normally expect
    from a volume control. See QAudio::convertVolume() for more details.
*/

qreal QMediaRecorder::volume() const
{
    return d_func()->control ? d_func()->control->volume() : 1.0;
}


void QMediaRecorder::setVolume(qreal volume)
{
    Q_D(QMediaRecorder);

    if (d->control) {
        volume = qMax(qreal(0.0), volume);
        d->control->setVolume(volume);
    }
}

/*!
    Returns a list of supported container formats.
*/
QStringList QMediaRecorder::supportedContainers() const
{
    return d_func()->formatControl ?
           d_func()->formatControl->supportedContainers() : QStringList();
}

/*!
    Returns a description of a container \a format.
*/
QString QMediaRecorder::containerDescription(const QString &format) const
{
    return d_func()->formatControl ?
           d_func()->formatControl->containerDescription(format) : QString();
}

/*!
    Returns the selected container format.
*/

QString QMediaRecorder::containerFormat() const
{
    return d_func()->formatControl ?
           d_func()->formatControl->containerFormat() : QString();
}

/*!
    Returns a list of supported audio codecs.
*/
QStringList QMediaRecorder::supportedAudioCodecs() const
{
    return d_func()->audioControl ?
           d_func()->audioControl->supportedAudioCodecs() : QStringList();
}

/*!
    Returns a description of an audio \a codec.
*/
QString QMediaRecorder::audioCodecDescription(const QString &codec) const
{
    return d_func()->audioControl ?
           d_func()->audioControl->codecDescription(codec) : QString();
}

/*!
    Returns a list of supported audio sample rates.

    If non null audio \a settings parameter is passed, the returned list is
    reduced to sample rates supported with partial settings applied.

    This can be used to query the list of sample rates, supported by specific
    audio codec.

    If the encoder supports arbitrary sample rates within the supported rates
    range, *\a continuous is set to true, otherwise *\a continuous is set to
    false.
*/

QList<int> QMediaRecorder::supportedAudioSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
{
    if (continuous)
        *continuous = false;

    return d_func()->audioControl ?
           d_func()->audioControl->supportedSampleRates(settings, continuous) : QList<int>();
}

/*!
    Returns a list of resolutions video can be encoded at.

    If non null video \a settings parameter is passed, the returned list is
    reduced to resolution supported with partial settings like video codec or
    framerate applied.

    If the encoder supports arbitrary resolutions within the supported range,
    *\a continuous is set to true, otherwise *\a continuous is set to false.

    \sa QVideoEncoderSettings::resolution()
*/
QList<QSize> QMediaRecorder::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const
{
    if (continuous)
        *continuous = false;

    return d_func()->videoControl ?
           d_func()->videoControl->supportedResolutions(settings, continuous) : QList<QSize>();
}

/*!
    Returns a list of frame rates video can be encoded at.

    If non null video \a settings parameter is passed, the returned list is
    reduced to frame rates supported with partial settings like video codec or
    resolution applied.

    If the encoder supports arbitrary frame rates within the supported range,
    *\a continuous is set to true, otherwise *\a continuous is set to false.

    \sa QVideoEncoderSettings::frameRate()
*/
QList<qreal> QMediaRecorder::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const
{
    if (continuous)
        *continuous = false;

    return d_func()->videoControl ?
           d_func()->videoControl->supportedFrameRates(settings, continuous) : QList<qreal>();
}

/*!
    Returns a list of supported video codecs.
*/
QStringList QMediaRecorder::supportedVideoCodecs() const
{
    return d_func()->videoControl ?
           d_func()->videoControl->supportedVideoCodecs() : QStringList();
}

/*!
    Returns a description of a video \a codec.

    \sa setEncodingSettings()
*/
QString QMediaRecorder::videoCodecDescription(const QString &codec) const
{
    return d_func()->videoControl ?
           d_func()->videoControl->videoCodecDescription(codec) : QString();
}

/*!
    Returns the audio encoder settings being used.

    \sa setEncodingSettings()
*/

QAudioEncoderSettings QMediaRecorder::audioSettings() const
{
    return d_func()->audioControl ?
           d_func()->audioControl->audioSettings() : QAudioEncoderSettings();
}

/*!
    Returns the video encoder settings being used.

    \sa setEncodingSettings()
*/

QVideoEncoderSettings QMediaRecorder::videoSettings() const
{
    return d_func()->videoControl ?
           d_func()->videoControl->videoSettings() : QVideoEncoderSettings();
}

/*!
    Sets the audio encoder \a settings.

    If some parameters are not specified, or null settings are passed, the
    encoder will choose default encoding parameters, depending on media
    source properties.

    It's only possible to change settings when the encoder is in the
    QMediaEncoder::StoppedState state.

    \sa audioSettings(), videoSettings(), containerFormat()
*/

void QMediaRecorder::setAudioSettings(const QAudioEncoderSettings &settings)
{
    Q_D(QMediaRecorder);

    //restart camera if it can't apply new settings in the Active state
    d->restartCamera();

    if (d->audioControl) {
        d->audioControl->setAudioSettings(settings);
        d->applySettingsLater();
    }
}

/*!
    Sets the video encoder \a settings.

    If some parameters are not specified, or null settings are passed, the
    encoder will choose default encoding parameters, depending on media
    source properties.

    It's only possible to change settings when the encoder is in the
    QMediaEncoder::StoppedState state.

    \sa audioSettings(), videoSettings(), containerFormat()
*/

void QMediaRecorder::setVideoSettings(const QVideoEncoderSettings &settings)
{
    Q_D(QMediaRecorder);

    d->restartCamera();

    if (d->videoControl) {
        d->videoControl->setVideoSettings(settings);
        d->applySettingsLater();
    }
}

/*!
    Sets the media \a container format.

    If the container format is not specified, the
    encoder will choose format, depending on media source properties
    and encoding settings selected.

    It's only possible to change settings when the encoder is in the
    QMediaEncoder::StoppedState state.

    \sa audioSettings(), videoSettings(), containerFormat()
*/

void QMediaRecorder::setContainerFormat(const QString &container)
{
    Q_D(QMediaRecorder);

    d->restartCamera();

    if (d->formatControl) {
        d->formatControl->setContainerFormat(container);
        d->applySettingsLater();
    }
}

/*!
    Sets the \a audio and \a video encoder settings and \a container format.

    If some parameters are not specified, or null settings are passed, the
    encoder will choose default encoding parameters, depending on media
    source properties.

    It's only possible to change settings when the encoder is in the
    QMediaEncoder::StoppedState state.

    \sa audioSettings(), videoSettings(), containerFormat()
*/

void QMediaRecorder::setEncodingSettings(const QAudioEncoderSettings &audio,
                                         const QVideoEncoderSettings &video,
                                         const QString &container)
{
    Q_D(QMediaRecorder);

    d->restartCamera();

    if (d->audioControl)
        d->audioControl->setAudioSettings(audio);

    if (d->videoControl)
        d->videoControl->setVideoSettings(video);

    if (d->formatControl)
        d->formatControl->setContainerFormat(container);

    d->applySettingsLater();
}

/*!
    Start recording.

    While the recorder state is changed immediately to QMediaRecorder::RecordingState,
    recording may start asynchronously, with statusChanged(QMediaRecorder::RecordingStatus)
    signal emitted when recording starts.

    If recording fails error() signal is emitted
    with recorder state being reset back to QMediaRecorder::StoppedState.
*/

void QMediaRecorder::record()
{
    Q_D(QMediaRecorder);

    d->actualLocation.clear();

    if (d->settingsChanged)
        d->_q_applySettings();

    // reset error
    d->error = NoError;
    d->errorString = QString();

    if (d->control)
        d->control->setState(RecordingState);
}

/*!
    Pause recording.

    The recorder state is changed to QMediaRecorder::PausedState.

    Depending on platform recording pause may be not supported,
    in this case the recorder state stays unchanged.
*/

void QMediaRecorder::pause()
{
    Q_D(QMediaRecorder);
    if (d->control)
        d->control->setState(PausedState);
}

/*!
    Stop recording.

    The recorder state is changed to QMediaRecorder::StoppedState.
*/

void QMediaRecorder::stop()
{
    Q_D(QMediaRecorder);
    if (d->control)
        d->control->setState(StoppedState);
}

/*!
    \enum QMediaRecorder::State

    \value StoppedState    The recorder is not active.
        If this is the state after recording then the actual created recording has
        finished being written to the final location and is ready on all platforms
        except on Android. On Android, due to platform limitations, there is no way
        to be certain that the recording has finished writing to the final location.
    \value RecordingState  The recording is requested.
    \value PausedState     The recorder is paused.
*/

/*!
    \enum QMediaRecorder::Status

    \value UnavailableStatus
        The recorder is not available or not supported by connected media object.
    \value UnloadedStatus
        The recorder is avilable but not loaded.
    \value LoadingStatus
        The recorder is initializing.
    \value LoadedStatus
        The recorder is initialized and ready to record media.
    \value StartingStatus
        Recording is requested but not active yet.
    \value RecordingStatus
        Recording is active.
    \value PausedStatus
        Recording is paused.
    \value FinalizingStatus
        Recording is stopped with media being finalized.
*/

/*!
    \enum QMediaRecorder::Error

    \value NoError         No Errors.
    \value ResourceError   Device is not ready or not available.
    \value FormatError     Current format is not supported.
    \value OutOfSpaceError No space left on device.
*/

/*!
    \property QMediaRecorder::state
    \brief The current state of the media recorder.

    The state property represents the user request and is changed synchronously
    during record(), pause() or stop() calls.
    Recorder state may also change asynchronously when recording fails.
*/

/*!
    \property QMediaRecorder::status
    \brief The current status of the media recorder.

    The status is changed asynchronously and represents the actual status
    of media recorder.
*/

/*!
    \fn QMediaRecorder::stateChanged(State state)

    Signals that a media recorder's \a state has changed.
*/

/*!
    \fn QMediaRecorder::durationChanged(qint64 duration)

    Signals that the \a duration of the recorded media has changed.
*/

/*!
    \fn QMediaRecorder::actualLocationChanged(const QUrl &location)

    Signals that the actual \a location of the recorded media has changed.
    This signal is usually emitted when recording starts.
*/

/*!
    \fn QMediaRecorder::error(QMediaRecorder::Error error)

    Signals that an \a error has occurred.
*/

/*!
    \fn QMediaRecorder::availabilityChanged(bool available)

    Signals that the media recorder is now available (if \a available is true), or not.
*/

/*!
    \fn QMediaRecorder::availabilityChanged(QMultimedia::AvailabilityStatus availability)

    Signals that the service availability has changed to \a availability.
*/

/*!
    \fn QMediaRecorder::mutedChanged(bool muted)

    Signals that the \a muted state has changed. If true the recording is being muted.
*/

/*!
    \property QMediaRecorder::metaDataAvailable
    \brief whether access to a media object's meta-data is available.

    If this is true there is meta-data available, otherwise there is no meta-data available.
*/

bool QMediaRecorder::isMetaDataAvailable() const
{
    Q_D(const QMediaRecorder);

    return d->metaDataControl
            ? d->metaDataControl->isMetaDataAvailable()
            : false;
}

/*!
    \fn QMediaRecorder::metaDataAvailableChanged(bool available)

    Signals that the \a available state of a media object's meta-data has changed.
*/

/*!
    \property QMediaRecorder::metaDataWritable
    \brief whether a media object's meta-data is writable.

    If this is true the meta-data is writable, otherwise the meta-data is read-only.
*/

bool QMediaRecorder::isMetaDataWritable() const
{
    Q_D(const QMediaRecorder);

    return d->metaDataControl
            ? d->metaDataControl->isWritable()
            : false;
}

/*!
    \fn QMediaRecorder::metaDataWritableChanged(bool writable)

    Signals that the \a writable state of a media object's meta-data has changed.
*/

/*!
    Returns the value associated with a meta-data \a key.
*/
QVariant QMediaRecorder::metaData(const QString &key) const
{
    Q_D(const QMediaRecorder);

    return d->metaDataControl
            ? d->metaDataControl->metaData(key)
            : QVariant();
}

/*!
    Sets a \a value for a meta-data \a key.

    \note To ensure that meta data is set corretly, it should be set before starting the recording.
    Once the recording is stopped, any meta data set will be attached to the next recording.
*/
void QMediaRecorder::setMetaData(const QString &key, const QVariant &value)
{
    Q_D(QMediaRecorder);

    if (d->metaDataControl)
        d->metaDataControl->setMetaData(key, value);
}

/*!
    Returns a list of keys there is meta-data available for.
*/
QStringList QMediaRecorder::availableMetaData() const
{
    Q_D(const QMediaRecorder);

    return d->metaDataControl
            ? d->metaDataControl->availableMetaData()
            : QStringList();
}

/*!
    \fn QMediaRecorder::metaDataChanged()

    Signals that a media object's meta-data has changed.

    If multiple meta-data elements are changed,
    metaDataChanged(const QString &key, const QVariant &value) signal is emitted
    for each of them with metaDataChanged() changed emitted once.
*/

/*!
    \fn QMediaRecorder::metaDataChanged(const QString &key, const QVariant &value)

    Signal the changes of one meta-data element \a value with the given \a key.
*/

QT_END_NAMESPACE

#include "moc_qmediarecorder.cpp"
