Qt: Clean up video recording for GB

This commit is contained in:
Jeffrey Pfau 2016-09-06 20:41:55 -07:00
parent c0689783da
commit 8c8361477d
6 changed files with 307 additions and 254 deletions

View File

@ -16,7 +16,6 @@ struct NoIntroDB;
extern "C" { extern "C" {
#include "core/log.h" #include "core/log.h"
#include "gba/sio.h"
} }
mLOG_DECLARE_CATEGORY(QT); mLOG_DECLARE_CATEGORY(QT);

View File

@ -12,6 +12,12 @@
#include <QMap> #include <QMap>
#ifdef M_CORE_GB
extern "C" {
#include "gb/video.h"
}
#endif
using namespace QGBA; using namespace QGBA;
QMap<QString, QString> VideoView::s_acodecMap; QMap<QString, QString> VideoView::s_acodecMap;
@ -34,10 +40,10 @@ bool VideoView::Preset::compatible(const Preset& other) const {
if (other.vbr && vbr && other.vbr != vbr) { if (other.vbr && vbr && other.vbr != vbr) {
return false; return false;
} }
if (other.width && width && other.width != width) { if (other.dims.width() && dims.width() && other.dims.width() != dims.width()) {
return false; return false;
} }
if (other.height && height && other.height != height) { if (other.dims.height() && dims.height() && other.dims.height() != dims.height()) {
return false; return false;
} }
return true; return true;
@ -48,6 +54,10 @@ VideoView::VideoView(QWidget* parent)
, m_audioCodecCstr(nullptr) , m_audioCodecCstr(nullptr)
, m_videoCodecCstr(nullptr) , m_videoCodecCstr(nullptr)
, m_containerCstr(nullptr) , m_containerCstr(nullptr)
, m_nativeWidth(0)
, m_nativeHeight(0)
, m_width(1)
, m_height(1)
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
@ -96,67 +106,80 @@ VideoView::VideoView(QWidget* parent)
FFmpegEncoderInit(&m_encoder); FFmpegEncoderInit(&m_encoder);
addPreset(m_ui.preset1080, (Preset) { updatePresets();
setPreset({
.container = "MKV",
.vcodec = "PNG",
.acodec = "FLAC",
.vbr = 0,
.abr = 0,
.dims = QSize(),
});
showAdvanced(false);
}
void VideoView::updatePresets() {
m_presets.clear();
addPreset(m_ui.preset1080, {
.container = QString(), .container = QString(),
.vcodec = QString(), .vcodec = QString(),
.acodec = QString(), .acodec = QString(),
.vbr = 0, .vbr = 0,
.abr = 0, .abr = 0,
.width = 1620, .dims = maintainAspect(QSize(1920, 1080))
.height = 1080
}); });
addPreset(m_ui.preset720, (Preset) { addPreset(m_ui.preset720, {
.container = QString(), .container = QString(),
.vcodec = QString(), .vcodec = QString(),
.acodec = QString(), .acodec = QString(),
.vbr = 0, .vbr = 0,
.abr = 0, .abr = 0,
.width = 1080, .dims = maintainAspect(QSize(1280, 720))
.height = 720
}); });
addPreset(m_ui.preset480, (Preset) { addPreset(m_ui.preset480, {
.container = QString(), .container = QString(),
.vcodec = QString(), .vcodec = QString(),
.acodec = QString(), .acodec = QString(),
.vbr = 0, .vbr = 0,
.abr = 0, .abr = 0,
.width = 720, .dims = maintainAspect(QSize(720, 480))
.height = 480
}); });
addPreset(m_ui.preset160, (Preset) { if (m_nativeWidth && m_nativeHeight) {
.container = QString(), addPreset(m_ui.presetNative, {
.vcodec = QString(), .container = QString(),
.acodec = QString(), .vcodec = QString(),
.vbr = 0, .acodec = QString(),
.abr = 0, .vbr = 0,
.width = VIDEO_HORIZONTAL_PIXELS, .abr = 0,
.height = VIDEO_VERTICAL_PIXELS .dims = QSize(m_nativeWidth, m_nativeHeight)
}); });
m_ui.presetNative->setEnabled(true);
}
addPreset(m_ui.presetHQ, (Preset) { addPreset(m_ui.presetHQ, {
.container = "MP4", .container = "MP4",
.vcodec = "h.264", .vcodec = "h.264",
.acodec = "AAC", .acodec = "AAC",
.vbr = 8000, .vbr = 8000,
.abr = 384, .abr = 384,
.width = 1620, .dims = maintainAspect(QSize(1920, 1080))
.height = 1080
}); });
addPreset(m_ui.presetYoutube, (Preset) { addPreset(m_ui.presetYoutube, {
.container = "MP4", .container = "MP4",
.vcodec = "h.264", .vcodec = "h.264",
.acodec = "AAC", .acodec = "AAC",
.vbr = 5000, .vbr = 5000,
.abr = 256, .abr = 256,
.width = 1080, .dims = maintainAspect(QSize(1280, 720))
.height = 720
}); });
addPreset(m_ui.presetWebM, (Preset) { addPreset(m_ui.presetWebM, {
.container = "WebM", .container = "WebM",
.vcodec = "VP8", .vcodec = "VP8",
.acodec = "Vorbis", .acodec = "Vorbis",
@ -164,27 +187,16 @@ VideoView::VideoView(QWidget* parent)
.abr = 128 .abr = 128
}); });
addPreset(m_ui.presetLossless, (Preset) { if (m_nativeWidth && m_nativeHeight) {
.container = "MKV", addPreset(m_ui.presetLossless, {
.vcodec = "PNG", .container = "MKV",
.acodec = "FLAC", .vcodec = "PNG",
.vbr = 0, .acodec = "FLAC",
.abr = 0, .vbr = 0,
.width = VIDEO_HORIZONTAL_PIXELS, .abr = 0,
.height = VIDEO_VERTICAL_PIXELS, .dims = QSize(m_nativeWidth, m_nativeHeight)
}); });
}
setPreset((Preset) {
.container = "MKV",
.vcodec = "PNG",
.acodec = "FLAC",
.vbr = 0,
.abr = 0,
.width = VIDEO_HORIZONTAL_PIXELS,
.height = VIDEO_VERTICAL_PIXELS,
});
showAdvanced(false);
} }
VideoView::~VideoView() { VideoView::~VideoView() {
@ -214,6 +226,23 @@ void VideoView::stopRecording() {
validateSettings(); validateSettings();
} }
void VideoView::setNativeResolution(const QSize& dims) {
m_nativeWidth = dims.width();
m_nativeHeight = dims.height();
m_ui.presetNative->setText(tr("Native (%0x%1)").arg(m_nativeWidth).arg(m_nativeHeight));
QSize newSize = maintainAspect(QSize(m_width, m_height));
m_width = newSize.width();
m_height = newSize.height();
updateAspectRatio(m_nativeWidth, m_nativeHeight, false);
updatePresets();
for (auto iterator = m_presets.constBegin(); iterator != m_presets.constEnd(); ++iterator) {
if (iterator.key()->isChecked()) {
setPreset(*iterator);
break;
}
}
}
void VideoView::selectFile() { void VideoView::selectFile() {
QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file")); QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
@ -295,7 +324,7 @@ void VideoView::setVideoBitrate(int br, bool manual) {
void VideoView::setWidth(int width, bool manual) { void VideoView::setWidth(int width, bool manual) {
m_width = width; m_width = width;
updateAspectRatio(width, 0); updateAspectRatio(width, 0, false);
FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height); FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height);
if (manual) { if (manual) {
uncheckIncompatible(); uncheckIncompatible();
@ -304,7 +333,7 @@ void VideoView::setWidth(int width, bool manual) {
void VideoView::setHeight(int height, bool manual) { void VideoView::setHeight(int height, bool manual) {
m_height = height; m_height = height;
updateAspectRatio(0, height); updateAspectRatio(0, height, false);
FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height); FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height);
if (manual) { if (manual) {
uncheckIncompatible(); uncheckIncompatible();
@ -401,8 +430,7 @@ void VideoView::uncheckIncompatible() {
.acodec = m_audioCodec, .acodec = m_audioCodec,
.vbr = m_vbr / 1000, .vbr = m_vbr / 1000,
.abr = m_abr / 1000, .abr = m_abr / 1000,
.width = m_width, .dims = QSize(m_width, m_height)
.height = m_height
}; };
m_ui.presets->setExclusive(false); m_ui.presets->setExclusive(false);
@ -419,8 +447,8 @@ void VideoView::uncheckIncompatible() {
m_ui.presets->setExclusive(true); m_ui.presets->setExclusive(true);
m_ui.resolutions->setExclusive(true); m_ui.resolutions->setExclusive(true);
if (current.compatible(m_presets[m_ui.preset160])) { if (current.compatible(m_presets[m_ui.presetNative])) {
safelyCheck(m_ui.preset160); safelyCheck(m_ui.presetNative);
} }
if (current.compatible(m_presets[m_ui.preset480])) { if (current.compatible(m_presets[m_ui.preset480])) {
safelyCheck(m_ui.preset480); safelyCheck(m_ui.preset480);
@ -465,6 +493,7 @@ void VideoView::safelySet(QComboBox* box, const QString& value) {
void VideoView::addPreset(QAbstractButton* button, const Preset& preset) { void VideoView::addPreset(QAbstractButton* button, const Preset& preset) {
m_presets[button] = preset; m_presets[button] = preset;
button->disconnect();
connect(button, &QAbstractButton::pressed, [this, preset]() { connect(button, &QAbstractButton::pressed, [this, preset]() {
setPreset(preset); setPreset(preset);
}); });
@ -491,17 +520,27 @@ void VideoView::setPreset(const Preset& preset) {
setVideoBitrate(preset.vbr, false); setVideoBitrate(preset.vbr, false);
safelySet(m_ui.vbr, preset.vbr); safelySet(m_ui.vbr, preset.vbr);
} }
if (preset.width) { if (preset.dims.width() > 0) {
setWidth(preset.width, false); setWidth(preset.dims.width(), false);
safelySet(m_ui.width, preset.width); safelySet(m_ui.width, preset.dims.width());
} }
if (preset.height) { if (preset.dims.height() > 0) {
setHeight(preset.height, false); setHeight(preset.dims.height(), false);
safelySet(m_ui.height, preset.height); safelySet(m_ui.height, preset.dims.height());
} }
uncheckIncompatible(); uncheckIncompatible();
validateSettings(); validateSettings();
} }
QSize VideoView::maintainAspect(const QSize& size) {
QSize ds = size;
if (ds.width() * m_nativeHeight > ds.height() * m_nativeWidth) {
ds.setWidth(ds.height() * m_nativeWidth / m_nativeHeight);
} else if (ds.width() * m_nativeHeight < ds.height() * m_nativeWidth) {
ds.setHeight(ds.width() * m_nativeHeight / m_nativeWidth);
}
return ds;
}
#endif #endif

View File

@ -22,18 +22,6 @@ class VideoView : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
struct Preset {
QString container;
QString vcodec;
QString acodec;
int vbr;
int abr;
int width;
int height;
bool compatible(const Preset&) const;
};
VideoView(QWidget* parent = nullptr); VideoView(QWidget* parent = nullptr);
virtual ~VideoView(); virtual ~VideoView();
@ -42,6 +30,7 @@ public:
public slots: public slots:
void startRecording(); void startRecording();
void stopRecording(); void stopRecording();
void setNativeResolution(const QSize&);
signals: signals:
void recordingStarted(mAVStream*); void recordingStarted(mAVStream*);
@ -65,8 +54,20 @@ private slots:
void showAdvanced(bool); void showAdvanced(bool);
void uncheckIncompatible(); void uncheckIncompatible();
void updatePresets();
private: private:
struct Preset {
QString container;
QString vcodec;
QString acodec;
int vbr;
int abr;
QSize dims;
bool compatible(const Preset&) const;
};
bool validateSettings(); bool validateSettings();
void updateAspectRatio(int width, int height, bool force = false); void updateAspectRatio(int width, int height, bool force = false);
static QString sanitizeCodec(const QString&, const QMap<QString, QString>& mapping); static QString sanitizeCodec(const QString&, const QMap<QString, QString>& mapping);
@ -77,6 +78,8 @@ private:
void addPreset(QAbstractButton*, const Preset&); void addPreset(QAbstractButton*, const Preset&);
void setPreset(const Preset&); void setPreset(const Preset&);
QSize maintainAspect(const QSize&);
Ui::VideoView m_ui; Ui::VideoView m_ui;
FFmpegEncoder m_encoder; FFmpegEncoder m_encoder;
@ -95,6 +98,9 @@ private:
int m_width; int m_width;
int m_height; int m_height;
int m_nativeWidth;
int m_nativeHeight;
QMap<QAbstractButton*, Preset> m_presets; QMap<QAbstractButton*, Preset> m_presets;
static QMap<QString, QString> s_acodecMap; static QMap<QString, QString> s_acodecMap;

View File

@ -181,9 +181,12 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QRadioButton" name="preset160"> <widget class="QRadioButton" name="presetNative">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>GBA (240x160)</string> <string>Native</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
@ -417,7 +420,7 @@
<item row="0" column="3"> <item row="0" column="3">
<widget class="QSpinBox" name="height"> <widget class="QSpinBox" name="height">
<property name="minimum"> <property name="minimum">
<number>160</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>3160</number> <number>3160</number>
@ -427,7 +430,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QSpinBox" name="width"> <widget class="QSpinBox" name="width">
<property name="minimum"> <property name="minimum">
<number>240</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>3840</number> <number>3840</number>
@ -442,9 +445,6 @@
<property name="maximum"> <property name="maximum">
<number>9999</number> <number>9999</number>
</property> </property>
<property name="value">
<number>2</number>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
@ -455,9 +455,6 @@
<property name="maximum"> <property name="maximum">
<number>9999</number> <number>9999</number>
</property> </property>
<property name="value">
<number>3</number>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="4"> <item row="1" column="4">
@ -505,7 +502,7 @@
<tabstop>preset1080</tabstop> <tabstop>preset1080</tabstop>
<tabstop>preset720</tabstop> <tabstop>preset720</tabstop>
<tabstop>preset480</tabstop> <tabstop>preset480</tabstop>
<tabstop>preset160</tabstop> <tabstop>presetNative</tabstop>
<tabstop>container</tabstop> <tabstop>container</tabstop>
<tabstop>video</tabstop> <tabstop>video</tabstop>
<tabstop>audio</tabstop> <tabstop>audio</tabstop>

View File

@ -492,6 +492,12 @@ void Window::openVideoWindow() {
connect(m_videoView, SIGNAL(recordingStopped()), m_controller, SLOT(clearAVStream()), Qt::DirectConnection); connect(m_videoView, SIGNAL(recordingStopped()), m_controller, SLOT(clearAVStream()), Qt::DirectConnection);
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_videoView, SLOT(stopRecording())); connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_videoView, SLOT(stopRecording()));
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_videoView, SLOT(close())); connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_videoView, SLOT(close()));
connect(m_controller, &GameController::gameStarted, [this]() {
m_videoView->setNativeResolution(m_controller->screenDimensions());
});
if (m_controller->isLoaded()) {
m_videoView->setNativeResolution(m_controller->screenDimensions());
}
connect(this, SIGNAL(shutdown()), m_videoView, SLOT(close())); connect(this, SIGNAL(shutdown()), m_videoView, SLOT(close()));
} }
m_videoView->show(); m_videoView->show();
@ -1253,6 +1259,7 @@ void Window::setupMenu(QMenuBar* menubar) {
QAction* recordOutput = new QAction(tr("Record output..."), avMenu); QAction* recordOutput = new QAction(tr("Record output..."), avMenu);
connect(recordOutput, SIGNAL(triggered()), this, SLOT(openVideoWindow())); connect(recordOutput, SIGNAL(triggered()), this, SLOT(openVideoWindow()));
addControlledAction(avMenu, recordOutput, "recordOutput"); addControlledAction(avMenu, recordOutput, "recordOutput");
m_gameActions.append(recordOutput);
#endif #endif
#ifdef USE_MAGICK #ifdef USE_MAGICK

File diff suppressed because it is too large Load Diff