blob: ba26d8df8329a2408599455a17b9ab6ed8790964 [file] [log] [blame]
/****************************************************************************
**
** 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 "qgstreamerrecordercontrol.h"
#include "qgstreameraudioencode.h"
#include "qgstreamervideoencode.h"
#include "qgstreamermediacontainercontrol.h"
#include <QtCore/QDebug>
#include <QtGui/qdesktopservices.h>
QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session)
:QMediaRecorderControl(session),
m_session(session),
m_state(QMediaRecorder::StoppedState),
m_status(QMediaRecorder::UnloadedStatus)
{
connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateStatus()));
connect(m_session, SIGNAL(error(int,QString)), SLOT(handleSessionError(int,QString)));
connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64)));
connect(m_session, SIGNAL(mutedChanged(bool)), SIGNAL(mutedChanged(bool)));
connect(m_session, SIGNAL(volumeChanged(qreal)), SIGNAL(volumeChanged(qreal)));
m_hasPreviewState = m_session->captureMode() != QGstreamerCaptureSession::Audio;
}
QGstreamerRecorderControl::~QGstreamerRecorderControl()
{
}
QUrl QGstreamerRecorderControl::outputLocation() const
{
return m_session->outputLocation();
}
bool QGstreamerRecorderControl::setOutputLocation(const QUrl &sink)
{
m_outputLocation = sink;
m_session->setOutputLocation(sink);
return true;
}
QMediaRecorder::State QGstreamerRecorderControl::state() const
{
return m_state;
}
QMediaRecorder::Status QGstreamerRecorderControl::status() const
{
static QMediaRecorder::Status statusTable[3][3] = {
//Stopped recorder state:
{ QMediaRecorder::LoadedStatus, QMediaRecorder::FinalizingStatus, QMediaRecorder::FinalizingStatus },
//Recording recorder state:
{ QMediaRecorder::StartingStatus, QMediaRecorder::RecordingStatus, QMediaRecorder::PausedStatus },
//Paused recorder state:
{ QMediaRecorder::StartingStatus, QMediaRecorder::RecordingStatus, QMediaRecorder::PausedStatus }
};
QMediaRecorder::State sessionState = QMediaRecorder::StoppedState;
switch ( m_session->state() ) {
case QGstreamerCaptureSession::RecordingState:
sessionState = QMediaRecorder::RecordingState;
break;
case QGstreamerCaptureSession::PausedState:
sessionState = QMediaRecorder::PausedState;
break;
case QGstreamerCaptureSession::PreviewState:
case QGstreamerCaptureSession::StoppedState:
sessionState = QMediaRecorder::StoppedState;
break;
}
return statusTable[m_state][sessionState];
}
void QGstreamerRecorderControl::updateStatus()
{
QMediaRecorder::Status newStatus = status();
if (m_status != newStatus) {
m_status = newStatus;
emit statusChanged(m_status);
// If stop has been called and session state became stopped.
if (m_status == QMediaRecorder::LoadedStatus)
emit stateChanged(m_state);
}
}
void QGstreamerRecorderControl::handleSessionError(int code, const QString &description)
{
emit error(code, description);
stop();
}
qint64 QGstreamerRecorderControl::duration() const
{
return m_session->duration();
}
void QGstreamerRecorderControl::setState(QMediaRecorder::State state)
{
switch (state) {
case QMediaRecorder::StoppedState:
stop();
break;
case QMediaRecorder::PausedState:
pause();
break;
case QMediaRecorder::RecordingState:
record();
break;
}
}
void QGstreamerRecorderControl::record()
{
if (m_state == QMediaRecorder::RecordingState)
return;
m_state = QMediaRecorder::RecordingState;
if (m_outputLocation.isEmpty()) {
QString container = m_session->mediaContainerControl()->containerExtension();
if (container.isEmpty())
container = "raw";
m_session->setOutputLocation(QUrl(generateFileName(defaultDir(), container)));
}
m_session->dumpGraph("before-record");
if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
m_session->setState(QGstreamerCaptureSession::RecordingState);
} else
emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
m_session->dumpGraph("after-record");
emit stateChanged(m_state);
updateStatus();
emit actualLocationChanged(m_session->outputLocation());
}
void QGstreamerRecorderControl::pause()
{
if (m_state == QMediaRecorder::PausedState)
return;
m_state = QMediaRecorder::PausedState;
m_session->dumpGraph("before-pause");
if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
m_session->setState(QGstreamerCaptureSession::PausedState);
} else
emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
emit stateChanged(m_state);
updateStatus();
}
void QGstreamerRecorderControl::stop()
{
if (m_state == QMediaRecorder::StoppedState)
return;
m_state = QMediaRecorder::StoppedState;
if (!m_hasPreviewState) {
m_session->setState(QGstreamerCaptureSession::StoppedState);
} else {
if (m_session->state() != QGstreamerCaptureSession::StoppedState)
m_session->setState(QGstreamerCaptureSession::PreviewState);
}
updateStatus();
}
void QGstreamerRecorderControl::applySettings()
{
//Check the codecs are compatible with container,
//and choose the compatible codecs/container if omitted
QGstreamerAudioEncode *audioEncodeControl = m_session->audioEncodeControl();
QGstreamerVideoEncode *videoEncodeControl = m_session->videoEncodeControl();
QGstreamerMediaContainerControl *mediaContainerControl = m_session->mediaContainerControl();
bool needAudio = m_session->captureMode() & QGstreamerCaptureSession::Audio;
bool needVideo = m_session->captureMode() & QGstreamerCaptureSession::Video;
QStringList containerCandidates;
if (mediaContainerControl->containerFormat().isEmpty())
containerCandidates = mediaContainerControl->supportedContainers();
else
containerCandidates << mediaContainerControl->containerFormat();
QStringList audioCandidates;
if (needAudio) {
QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
if (audioSettings.codec().isEmpty())
audioCandidates = audioEncodeControl->supportedAudioCodecs();
else
audioCandidates << audioSettings.codec();
}
QStringList videoCandidates;
if (needVideo) {
QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
if (videoSettings.codec().isEmpty())
videoCandidates = videoEncodeControl->supportedVideoCodecs();
else
videoCandidates << videoSettings.codec();
}
QString container;
QString audioCodec;
QString videoCodec;
for (const QString &containerCandidate : qAsConst(containerCandidates)) {
QSet<QString> supportedTypes = mediaContainerControl->supportedStreamTypes(containerCandidate);
audioCodec.clear();
videoCodec.clear();
if (needAudio) {
bool found = false;
for (const QString &audioCandidate : qAsConst(audioCandidates)) {
QSet<QString> audioTypes = audioEncodeControl->supportedStreamTypes(audioCandidate);
if (audioTypes.intersects(supportedTypes)) {
found = true;
audioCodec = audioCandidate;
break;
}
}
if (!found)
continue;
}
if (needVideo) {
bool found = false;
for (const QString &videoCandidate : qAsConst(videoCandidates)) {
QSet<QString> videoTypes = videoEncodeControl->supportedStreamTypes(videoCandidate);
if (videoTypes.intersects(supportedTypes)) {
found = true;
videoCodec = videoCandidate;
break;
}
}
if (!found)
continue;
}
container = containerCandidate;
break;
}
if (container.isEmpty()) {
emit error(QMediaRecorder::FormatError, tr("Not compatible codecs and container format."));
} else {
mediaContainerControl->setContainerFormat(container);
if (needAudio) {
QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
audioSettings.setCodec(audioCodec);
audioEncodeControl->setAudioSettings(audioSettings);
}
if (needVideo) {
QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
videoSettings.setCodec(videoCodec);
videoEncodeControl->setVideoSettings(videoSettings);
}
}
}
bool QGstreamerRecorderControl::isMuted() const
{
return m_session->isMuted();
}
qreal QGstreamerRecorderControl::volume() const
{
return m_session->volume();
}
void QGstreamerRecorderControl::setMuted(bool muted)
{
m_session->setMuted(muted);
}
void QGstreamerRecorderControl::setVolume(qreal volume)
{
m_session->setVolume(volume);
}
QDir QGstreamerRecorderControl::defaultDir() const
{
QStringList dirCandidates;
if (m_session->captureMode() & QGstreamerCaptureSession::Video)
dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
else
dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
dirCandidates << QDir::home().filePath("Documents");
dirCandidates << QDir::home().filePath("My Documents");
dirCandidates << QDir::homePath();
dirCandidates << QDir::currentPath();
dirCandidates << QDir::tempPath();
for (const QString &path : qAsConst(dirCandidates)) {
QDir dir(path);
if (dir.exists() && QFileInfo(path).isWritable())
return dir;
}
return QDir();
}
QString QGstreamerRecorderControl::generateFileName(const QDir &dir, const QString &ext) const
{
int lastClip = 0;
const auto list = dir.entryList(QStringList() << QString("clip_*.%1").arg(ext));
for (const QString &fileName : list) {
int imgNumber = fileName.midRef(5, fileName.size()-6-ext.length()).toInt();
lastClip = qMax(lastClip, imgNumber);
}
QString name = QString("clip_%1.%2").arg(lastClip+1,
4, //fieldWidth
10,
QLatin1Char('0')).arg(ext);
return dir.absoluteFilePath(name);
}