Marble

TourCaptureDialog.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2014 Sanjiban Bairagya <sanjiban22393@gmail.com>
4//
5
6#include "TourCaptureDialog.h"
7#include "ui_TourCaptureDialog.h"
8
9#include <QFileDialog>
10#include <QMessageBox>
11#include <QTimer>
12
13#include "MarbleWidget.h"
14#include "MovieCapture.h"
15#include "TourPlayback.h"
16
17namespace Marble
18{
19
20TourCaptureDialog::TourCaptureDialog(MarbleWidget *widget, QWidget *parent)
21 : QDialog(parent)
22 , ui(new Ui::TourCaptureDialog)
23 , m_recorder(new MovieCapture(widget, parent))
24 , m_playback(nullptr)
25 , m_writingPossible(true)
26 , m_current_position(0.0)
27{
28 ui->setupUi(this);
29 m_recorder->setSnapshotMethod(MovieCapture::DataDriven);
30 ui->progressBar->setValue(0);
31
32 connect(ui->fpsSlider, &QAbstractSlider::valueChanged, ui->fpsSpin, &QSpinBox::setValue);
33
34 connect(ui->fpsSpin, &QSpinBox::valueChanged, ui->fpsSlider, &QAbstractSlider::setValue);
35
36 connect(ui->fpsSlider, &QAbstractSlider::valueChanged, m_recorder, &MovieCapture::setFps);
37
38 connect(ui->closeButton, &QAbstractButton::clicked, this, &QWidget::close);
39
40 connect(ui->startButton, &QAbstractButton::clicked, this, &TourCaptureDialog::startRecording);
41
42 connect(ui->openButton, &QAbstractButton::clicked, this, &TourCaptureDialog::loadDestinationFile);
43
44 connect(m_recorder, &MovieCapture::rateCalculated, this, &TourCaptureDialog::setRate);
45
46 connect(m_recorder, &MovieCapture::errorOccured, this, &TourCaptureDialog::handleError);
47}
48
49TourCaptureDialog::~TourCaptureDialog()
50{
51 delete ui;
52}
53
54void TourCaptureDialog::loadDestinationFile()
55{
56 const QList<MovieFormat> formats = m_recorder->availableFormats();
57 if (formats.isEmpty()) {
58 QMessageBox::warning(this, tr("Codecs are unavailable"), tr("Supported codecs are not found. (ffmpeg/avconv needs to be installed and in PATH.)"));
59 return;
60 }
61 QString filter = formats.first().name() + QLatin1StringView(" (*.") + formats.first().extension() + QLatin1Char(')');
62 for (int i = 1; i < formats.size(); i++) {
63 filter += QLatin1StringView(";;") + formats.at(i).name() + QLatin1StringView(" (*.") + formats.at(i).extension() + QLatin1Char(')');
64 }
65 const QString defaultFileName = ui->destinationEdit->text().isEmpty() ? m_defaultFileName : ui->destinationEdit->text();
66
67 const QString destination = QFileDialog::getSaveFileName(this, tr("Save video file"), defaultFileName, filter);
68
69 if (destination.isEmpty()) {
70 return;
71 }
72
73 bool supported = false;
74 for (const MovieFormat &format : formats) {
75 if (destination.endsWith(QLatin1Char('.') + format.extension())) {
76 supported = true;
77 break;
78 }
79 }
80
81 if (!supported) {
82 QString formatsExtensions = QLatin1Char('.') + formats.at(0).extension();
83 for (int i = 1; i < formats.size(); ++i) {
84 formatsExtensions += QLatin1StringView(", .") + formats.at(i).extension();
85 }
87 tr("Filename is not valid"),
88 tr("This file format is not supported. "
89 "Please, use %1 instead")
90 .arg(formatsExtensions));
91 return;
92 }
93
94 ui->destinationEdit->setText(destination);
95 m_recorder->setFilename(destination);
96}
97
98void TourCaptureDialog::startRecording()
99{
100 if (ui->startButton->text() == tr("Start")) {
101 ui->startButton->setText(tr("Cancel"));
102 ui->closeButton->setDisabled(true);
103 const QString path = ui->destinationEdit->text();
104
105 if (path.isEmpty()) {
107 tr("Missing filename"),
108 tr("Destination video file is not set. "
109 "I don't know where to save recorded "
110 "video. Please, specify one."));
111 return;
112 }
113
114 m_recorder->setSnapshotMethod(MovieCapture::DataDriven);
115 m_recorder->setFps(ui->fpsSlider->value());
116 m_recorder->startRecording();
117 m_current_position = 0.0;
118 recordNextFrame();
119 } else {
120 ui->startButton->setText(tr("Start"));
121 m_recorder->cancelRecording();
122 ui->progressBar->setValue(0);
123 ui->closeButton->setEnabled(true);
124 }
125}
126
127void TourCaptureDialog::updateProgress(double position)
128{
129 ui->progressBar->setValue(position);
130}
131
132void TourCaptureDialog::recordNextFrame()
133{
134 double const shift = 1.0 / (ui->fpsSlider->value());
135 double const duration = m_playback->duration();
136
137 if (!m_writingPossible) {
138 ui->rate->setText(QStringLiteral("<font color=\"red\">%1</font>").arg(tr("Video writing failed.")));
139 return;
140 }
141
142 if (ui->startButton->text() == QLatin1StringView("Start")) {
143 return;
144 }
145
146 if (m_current_position <= duration) {
147 m_playback->seek(m_current_position);
148 m_recorder->recordFrame();
149 updateProgress(m_current_position * 100);
150 m_current_position += shift;
151 QTimer::singleShot(1, this, SLOT(recordNextFrame()));
152 } else {
153 m_recorder->stopRecording();
154 ui->progressBar->setValue(duration * 100);
155 ui->startButton->setText(tr("Start"));
156 ui->rate->setText(QStringLiteral("<font color=\"green\">%1</font>").arg(tr("Video export completed.")));
157 ui->closeButton->setEnabled(true);
158 }
159}
160
161void TourCaptureDialog::setRate(double rate)
162{
163 ui->rate->setText(QStringLiteral("%1 KBytes/sec").arg(rate));
164}
165
166void TourCaptureDialog::stopRecording()
167{
168 m_recorder->stopRecording();
169}
170
171void TourCaptureDialog::setTourPlayback(TourPlayback *playback)
172{
173 m_playback = playback;
174 ui->progressBar->setMaximum(playback->duration() * 100);
175}
176
177void TourCaptureDialog::setDefaultFilename(const QString &filename)
178{
179 m_defaultFileName = filename;
180}
181
182void TourCaptureDialog::handleError()
183{
184 m_writingPossible = false;
185}
186
187} // namespace Marble
188
189#include "moc_TourCaptureDialog.cpp"
This file contains the headers for MarbleWidget.
QString path(const QString &relativePath)
Binds a QML item to a specific geodetic location in screen coordinates.
void clicked(bool checked)
void setValue(int)
void valueChanged(int value)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QLatin1Char at(qsizetype pos) const const
const_reference at(qsizetype i) const const
T & first()
bool isEmpty() const const
qsizetype size() const const
StandardButton warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
void setValue(int val)
void valueChanged(int i)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool close()
void setupUi(QWidget *widget)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:37:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.