citra_qt/dumping: Add dumping dialog
This is the main dialog of video dumping. This dialog allows the user to set output format, output path, video/audio encoder and video/audio bitrate. When a format is selected, the list of video and audio encoders are updated. Only encoders of codecs that can be contained in the format is shown.
This commit is contained in:
parent
e769d90aa8
commit
f82ba41fe0
|
@ -0,0 +1,210 @@
|
||||||
|
// Copyright 2020 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include "citra_qt/dumping/dumping_dialog.h"
|
||||||
|
#include "citra_qt/dumping/options_dialog.h"
|
||||||
|
#include "citra_qt/uisettings.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
#include "ui_dumping_dialog.h"
|
||||||
|
|
||||||
|
DumpingDialog::DumpingDialog(QWidget* parent)
|
||||||
|
: QDialog(parent), ui(std::make_unique<Ui::DumpingDialog>()) {
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->pathExplore, &QToolButton::clicked, this, &DumpingDialog::OnToolButtonClicked);
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this] {
|
||||||
|
ApplyConfiguration();
|
||||||
|
accept();
|
||||||
|
});
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &DumpingDialog::reject);
|
||||||
|
connect(ui->formatOptionsButton, &QPushButton::clicked, [this] {
|
||||||
|
OpenOptionsDialog(formats.at(ui->formatComboBox->currentData().toUInt()).options,
|
||||||
|
format_options);
|
||||||
|
});
|
||||||
|
connect(ui->videoEncoderOptionsButton, &QPushButton::clicked, [this] {
|
||||||
|
OpenOptionsDialog(
|
||||||
|
video_encoders.at(ui->videoEncoderComboBox->currentData().toUInt()).options,
|
||||||
|
video_encoder_options);
|
||||||
|
});
|
||||||
|
connect(ui->audioEncoderOptionsButton, &QPushButton::clicked, [this] {
|
||||||
|
OpenOptionsDialog(
|
||||||
|
audio_encoders.at(ui->audioEncoderComboBox->currentData().toUInt()).options,
|
||||||
|
audio_encoder_options);
|
||||||
|
});
|
||||||
|
|
||||||
|
SetConfiguration();
|
||||||
|
|
||||||
|
connect(ui->formatComboBox, qOverload<int>(&QComboBox::currentIndexChanged), [this] {
|
||||||
|
ui->pathLineEdit->setText(QString{});
|
||||||
|
format_options.clear();
|
||||||
|
PopulateEncoders();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->videoEncoderComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||||
|
[this] { video_encoder_options.clear(); });
|
||||||
|
connect(ui->audioEncoderComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||||
|
[this] { audio_encoder_options.clear(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
DumpingDialog::~DumpingDialog() = default;
|
||||||
|
|
||||||
|
QString DumpingDialog::GetFilePath() const {
|
||||||
|
return ui->pathLineEdit->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpingDialog::Populate() {
|
||||||
|
formats = VideoDumper::ListFormats();
|
||||||
|
video_encoders = VideoDumper::ListEncoders(AVMEDIA_TYPE_VIDEO);
|
||||||
|
audio_encoders = VideoDumper::ListEncoders(AVMEDIA_TYPE_AUDIO);
|
||||||
|
|
||||||
|
// Check that these are not empty
|
||||||
|
QString missing;
|
||||||
|
if (formats.empty()) {
|
||||||
|
missing = tr("output formats");
|
||||||
|
}
|
||||||
|
if (video_encoders.empty()) {
|
||||||
|
missing = tr("video encoders");
|
||||||
|
}
|
||||||
|
if (audio_encoders.empty()) {
|
||||||
|
missing = tr("audio encoders");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missing.isEmpty()) {
|
||||||
|
QMessageBox::critical(this, tr("Citra"),
|
||||||
|
tr("Could not find any available %1.\nPlease check your FFmpeg "
|
||||||
|
"installation used for compilation.")
|
||||||
|
.arg(missing));
|
||||||
|
reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate formats
|
||||||
|
for (std::size_t i = 0; i < formats.size(); ++i) {
|
||||||
|
const auto& format = formats[i];
|
||||||
|
|
||||||
|
// Check format: only formats that have video encoders and audio encoders are displayed
|
||||||
|
bool has_video = false;
|
||||||
|
for (const auto& video_encoder : video_encoders) {
|
||||||
|
if (format.supported_video_codecs.count(video_encoder.codec)) {
|
||||||
|
has_video = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!has_video)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool has_audio = false;
|
||||||
|
for (const auto& audio_encoder : audio_encoders) {
|
||||||
|
if (format.supported_audio_codecs.count(audio_encoder.codec)) {
|
||||||
|
has_audio = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!has_audio)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ui->formatComboBox->addItem(tr("%1 (%2)").arg(QString::fromStdString(format.long_name),
|
||||||
|
QString::fromStdString(format.name)),
|
||||||
|
static_cast<unsigned long long>(i));
|
||||||
|
if (format.name == Settings::values.output_format) {
|
||||||
|
ui->formatComboBox->setCurrentIndex(ui->formatComboBox->count() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PopulateEncoders();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpingDialog::PopulateEncoders() {
|
||||||
|
const auto& format = formats.at(ui->formatComboBox->currentData().toUInt());
|
||||||
|
|
||||||
|
ui->videoEncoderComboBox->clear();
|
||||||
|
for (std::size_t i = 0; i < video_encoders.size(); ++i) {
|
||||||
|
const auto& video_encoder = video_encoders[i];
|
||||||
|
if (!format.supported_video_codecs.count(video_encoder.codec)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->videoEncoderComboBox->addItem(
|
||||||
|
tr("%1 (%2)").arg(QString::fromStdString(video_encoder.long_name),
|
||||||
|
QString::fromStdString(video_encoder.name)),
|
||||||
|
static_cast<unsigned long long>(i));
|
||||||
|
if (video_encoder.name == Settings::values.video_encoder) {
|
||||||
|
ui->videoEncoderComboBox->setCurrentIndex(ui->videoEncoderComboBox->count() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->audioEncoderComboBox->clear();
|
||||||
|
for (std::size_t i = 0; i < audio_encoders.size(); ++i) {
|
||||||
|
const auto& audio_encoder = audio_encoders[i];
|
||||||
|
if (!format.supported_audio_codecs.count(audio_encoder.codec)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->audioEncoderComboBox->addItem(
|
||||||
|
tr("%1 (%2)").arg(QString::fromStdString(audio_encoder.long_name),
|
||||||
|
QString::fromStdString(audio_encoder.name)),
|
||||||
|
static_cast<unsigned long long>(i));
|
||||||
|
if (audio_encoder.name == Settings::values.audio_encoder) {
|
||||||
|
ui->audioEncoderComboBox->setCurrentIndex(ui->audioEncoderComboBox->count() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpingDialog::OnToolButtonClicked() {
|
||||||
|
const auto& format = formats.at(ui->formatComboBox->currentData().toUInt());
|
||||||
|
|
||||||
|
QString extensions;
|
||||||
|
for (const auto& ext : format.extensions) {
|
||||||
|
if (!extensions.isEmpty()) {
|
||||||
|
extensions.append(QLatin1Char{' '});
|
||||||
|
}
|
||||||
|
extensions.append(QStringLiteral("*.%1").arg(QString::fromStdString(ext)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto path = QFileDialog::getSaveFileName(
|
||||||
|
this, tr("Select Video Output Path"), last_path,
|
||||||
|
tr("%1 (%2)").arg(QString::fromStdString(format.long_name), extensions));
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
last_path = QFileInfo(ui->pathLineEdit->text()).path();
|
||||||
|
ui->pathLineEdit->setText(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpingDialog::OpenOptionsDialog(const std::vector<VideoDumper::OptionInfo>& options,
|
||||||
|
std::string& current_value) {
|
||||||
|
OptionsDialog dialog(this, options, current_value);
|
||||||
|
if (dialog.exec() != QDialog::DialogCode::Accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_value = dialog.GetCurrentValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpingDialog::SetConfiguration() {
|
||||||
|
Populate();
|
||||||
|
|
||||||
|
format_options = Settings::values.format_options;
|
||||||
|
video_encoder_options = Settings::values.video_encoder_options;
|
||||||
|
audio_encoder_options = Settings::values.audio_encoder_options;
|
||||||
|
last_path = UISettings::values.video_dumping_path;
|
||||||
|
ui->videoBitrateSpinBox->setValue(static_cast<int>(Settings::values.video_bitrate));
|
||||||
|
ui->audioBitrateSpinBox->setValue(static_cast<int>(Settings::values.audio_bitrate));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpingDialog::ApplyConfiguration() {
|
||||||
|
Settings::values.output_format = formats.at(ui->formatComboBox->currentData().toUInt()).name;
|
||||||
|
Settings::values.format_options = format_options;
|
||||||
|
Settings::values.video_encoder =
|
||||||
|
video_encoders.at(ui->videoEncoderComboBox->currentData().toUInt()).name;
|
||||||
|
Settings::values.video_encoder_options = video_encoder_options;
|
||||||
|
Settings::values.video_bitrate = ui->videoBitrateSpinBox->value();
|
||||||
|
Settings::values.audio_encoder =
|
||||||
|
audio_encoders.at(ui->audioEncoderComboBox->currentData().toUInt()).name;
|
||||||
|
Settings::values.audio_encoder_options = audio_encoder_options;
|
||||||
|
Settings::values.audio_bitrate = ui->audioBitrateSpinBox->value();
|
||||||
|
UISettings::values.video_dumping_path = last_path;
|
||||||
|
Settings::Apply();
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2020 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QDialog>
|
||||||
|
#include "core/dumping/ffmpeg_backend.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class DumpingDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DumpingDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DumpingDialog(QWidget* parent);
|
||||||
|
~DumpingDialog() override;
|
||||||
|
|
||||||
|
QString GetFilePath() const;
|
||||||
|
void ApplyConfiguration();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Populate();
|
||||||
|
void PopulateEncoders();
|
||||||
|
void SetConfiguration();
|
||||||
|
void OnToolButtonClicked();
|
||||||
|
void OpenOptionsDialog(const std::vector<VideoDumper::OptionInfo>& options,
|
||||||
|
std::string& current_value);
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::DumpingDialog> ui;
|
||||||
|
std::string format_options;
|
||||||
|
std::string video_encoder_options;
|
||||||
|
std::string audio_encoder_options;
|
||||||
|
|
||||||
|
QString last_path;
|
||||||
|
|
||||||
|
std::vector<VideoDumper::FormatInfo> formats;
|
||||||
|
std::vector<VideoDumper::EncoderInfo> video_encoders;
|
||||||
|
std::vector<VideoDumper::EncoderInfo> audio_encoders;
|
||||||
|
};
|
|
@ -0,0 +1,183 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DumpingDialog</class>
|
||||||
|
<widget class="QDialog" name="DumpingDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>360</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dump Video</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Output</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Format:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="formatComboBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="formatOptionsButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Options...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Path:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="pathLineEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QToolButton" name="pathExplore">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Video</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Encoder:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="videoEncoderComboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="videoEncoderOptionsButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Options...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bitrate:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="videoBitrateSpinBox">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000000</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>bps</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Audio</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Encoder:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="audioEncoderComboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="audioEncoderOptionsButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Options...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bitrate:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="audioBitrateSpinBox">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>1000000</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>bps</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</ui>
|
Reference in New Issue