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:
zhupengfei 2020-02-01 12:38:13 +08:00
parent e769d90aa8
commit f82ba41fe0
No known key found for this signature in database
GPG key ID: DD129E108BD09378
3 changed files with 434 additions and 0 deletions

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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>