mirror of https://github.com/mgba-emu/mgba.git
Qt: Clean up video recording for GB
This commit is contained in:
parent
c0689783da
commit
8c8361477d
|
@ -16,7 +16,6 @@ struct NoIntroDB;
|
|||
|
||||
extern "C" {
|
||||
#include "core/log.h"
|
||||
#include "gba/sio.h"
|
||||
}
|
||||
|
||||
mLOG_DECLARE_CATEGORY(QT);
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
|
||||
#include <QMap>
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
extern "C" {
|
||||
#include "gb/video.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
if (other.width && width && other.width != width) {
|
||||
if (other.dims.width() && dims.width() && other.dims.width() != dims.width()) {
|
||||
return false;
|
||||
}
|
||||
if (other.height && height && other.height != height) {
|
||||
if (other.dims.height() && dims.height() && other.dims.height() != dims.height()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -48,6 +54,10 @@ VideoView::VideoView(QWidget* parent)
|
|||
, m_audioCodecCstr(nullptr)
|
||||
, m_videoCodecCstr(nullptr)
|
||||
, m_containerCstr(nullptr)
|
||||
, m_nativeWidth(0)
|
||||
, m_nativeHeight(0)
|
||||
, m_width(1)
|
||||
, m_height(1)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
|
@ -96,67 +106,80 @@ VideoView::VideoView(QWidget* parent)
|
|||
|
||||
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(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.width = 1620,
|
||||
.height = 1080
|
||||
.dims = maintainAspect(QSize(1920, 1080))
|
||||
});
|
||||
|
||||
addPreset(m_ui.preset720, (Preset) {
|
||||
addPreset(m_ui.preset720, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.width = 1080,
|
||||
.height = 720
|
||||
.dims = maintainAspect(QSize(1280, 720))
|
||||
});
|
||||
|
||||
addPreset(m_ui.preset480, (Preset) {
|
||||
addPreset(m_ui.preset480, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.width = 720,
|
||||
.height = 480
|
||||
.dims = maintainAspect(QSize(720, 480))
|
||||
});
|
||||
|
||||
addPreset(m_ui.preset160, (Preset) {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.width = VIDEO_HORIZONTAL_PIXELS,
|
||||
.height = VIDEO_VERTICAL_PIXELS
|
||||
});
|
||||
if (m_nativeWidth && m_nativeHeight) {
|
||||
addPreset(m_ui.presetNative, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = QSize(m_nativeWidth, m_nativeHeight)
|
||||
});
|
||||
m_ui.presetNative->setEnabled(true);
|
||||
}
|
||||
|
||||
addPreset(m_ui.presetHQ, (Preset) {
|
||||
addPreset(m_ui.presetHQ, {
|
||||
.container = "MP4",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "AAC",
|
||||
.vbr = 8000,
|
||||
.abr = 384,
|
||||
.width = 1620,
|
||||
.height = 1080
|
||||
.dims = maintainAspect(QSize(1920, 1080))
|
||||
});
|
||||
|
||||
addPreset(m_ui.presetYoutube, (Preset) {
|
||||
addPreset(m_ui.presetYoutube, {
|
||||
.container = "MP4",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "AAC",
|
||||
.vbr = 5000,
|
||||
.abr = 256,
|
||||
.width = 1080,
|
||||
.height = 720
|
||||
.dims = maintainAspect(QSize(1280, 720))
|
||||
});
|
||||
|
||||
addPreset(m_ui.presetWebM, (Preset) {
|
||||
addPreset(m_ui.presetWebM, {
|
||||
.container = "WebM",
|
||||
.vcodec = "VP8",
|
||||
.acodec = "Vorbis",
|
||||
|
@ -164,27 +187,16 @@ VideoView::VideoView(QWidget* parent)
|
|||
.abr = 128
|
||||
});
|
||||
|
||||
addPreset(m_ui.presetLossless, (Preset) {
|
||||
.container = "MKV",
|
||||
.vcodec = "PNG",
|
||||
.acodec = "FLAC",
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.width = VIDEO_HORIZONTAL_PIXELS,
|
||||
.height = VIDEO_VERTICAL_PIXELS,
|
||||
});
|
||||
|
||||
setPreset((Preset) {
|
||||
.container = "MKV",
|
||||
.vcodec = "PNG",
|
||||
.acodec = "FLAC",
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.width = VIDEO_HORIZONTAL_PIXELS,
|
||||
.height = VIDEO_VERTICAL_PIXELS,
|
||||
});
|
||||
|
||||
showAdvanced(false);
|
||||
if (m_nativeWidth && m_nativeHeight) {
|
||||
addPreset(m_ui.presetLossless, {
|
||||
.container = "MKV",
|
||||
.vcodec = "PNG",
|
||||
.acodec = "FLAC",
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = QSize(m_nativeWidth, m_nativeHeight)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
VideoView::~VideoView() {
|
||||
|
@ -214,6 +226,23 @@ void VideoView::stopRecording() {
|
|||
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() {
|
||||
QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"));
|
||||
if (!filename.isEmpty()) {
|
||||
|
@ -295,7 +324,7 @@ void VideoView::setVideoBitrate(int br, bool manual) {
|
|||
|
||||
void VideoView::setWidth(int width, bool manual) {
|
||||
m_width = width;
|
||||
updateAspectRatio(width, 0);
|
||||
updateAspectRatio(width, 0, false);
|
||||
FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height);
|
||||
if (manual) {
|
||||
uncheckIncompatible();
|
||||
|
@ -304,7 +333,7 @@ void VideoView::setWidth(int width, bool manual) {
|
|||
|
||||
void VideoView::setHeight(int height, bool manual) {
|
||||
m_height = height;
|
||||
updateAspectRatio(0, height);
|
||||
updateAspectRatio(0, height, false);
|
||||
FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height);
|
||||
if (manual) {
|
||||
uncheckIncompatible();
|
||||
|
@ -401,8 +430,7 @@ void VideoView::uncheckIncompatible() {
|
|||
.acodec = m_audioCodec,
|
||||
.vbr = m_vbr / 1000,
|
||||
.abr = m_abr / 1000,
|
||||
.width = m_width,
|
||||
.height = m_height
|
||||
.dims = QSize(m_width, m_height)
|
||||
};
|
||||
|
||||
m_ui.presets->setExclusive(false);
|
||||
|
@ -419,8 +447,8 @@ void VideoView::uncheckIncompatible() {
|
|||
m_ui.presets->setExclusive(true);
|
||||
m_ui.resolutions->setExclusive(true);
|
||||
|
||||
if (current.compatible(m_presets[m_ui.preset160])) {
|
||||
safelyCheck(m_ui.preset160);
|
||||
if (current.compatible(m_presets[m_ui.presetNative])) {
|
||||
safelyCheck(m_ui.presetNative);
|
||||
}
|
||||
if (current.compatible(m_presets[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) {
|
||||
m_presets[button] = preset;
|
||||
button->disconnect();
|
||||
connect(button, &QAbstractButton::pressed, [this, preset]() {
|
||||
setPreset(preset);
|
||||
});
|
||||
|
@ -491,17 +520,27 @@ void VideoView::setPreset(const Preset& preset) {
|
|||
setVideoBitrate(preset.vbr, false);
|
||||
safelySet(m_ui.vbr, preset.vbr);
|
||||
}
|
||||
if (preset.width) {
|
||||
setWidth(preset.width, false);
|
||||
safelySet(m_ui.width, preset.width);
|
||||
if (preset.dims.width() > 0) {
|
||||
setWidth(preset.dims.width(), false);
|
||||
safelySet(m_ui.width, preset.dims.width());
|
||||
}
|
||||
if (preset.height) {
|
||||
setHeight(preset.height, false);
|
||||
safelySet(m_ui.height, preset.height);
|
||||
if (preset.dims.height() > 0) {
|
||||
setHeight(preset.dims.height(), false);
|
||||
safelySet(m_ui.height, preset.dims.height());
|
||||
}
|
||||
|
||||
uncheckIncompatible();
|
||||
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
|
||||
|
|
|
@ -22,18 +22,6 @@ class VideoView : public QWidget {
|
|||
Q_OBJECT
|
||||
|
||||
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);
|
||||
virtual ~VideoView();
|
||||
|
||||
|
@ -42,6 +30,7 @@ public:
|
|||
public slots:
|
||||
void startRecording();
|
||||
void stopRecording();
|
||||
void setNativeResolution(const QSize&);
|
||||
|
||||
signals:
|
||||
void recordingStarted(mAVStream*);
|
||||
|
@ -65,8 +54,20 @@ private slots:
|
|||
void showAdvanced(bool);
|
||||
|
||||
void uncheckIncompatible();
|
||||
void updatePresets();
|
||||
|
||||
private:
|
||||
struct Preset {
|
||||
QString container;
|
||||
QString vcodec;
|
||||
QString acodec;
|
||||
int vbr;
|
||||
int abr;
|
||||
QSize dims;
|
||||
|
||||
bool compatible(const Preset&) const;
|
||||
};
|
||||
|
||||
bool validateSettings();
|
||||
void updateAspectRatio(int width, int height, bool force = false);
|
||||
static QString sanitizeCodec(const QString&, const QMap<QString, QString>& mapping);
|
||||
|
@ -77,6 +78,8 @@ private:
|
|||
void addPreset(QAbstractButton*, const Preset&);
|
||||
void setPreset(const Preset&);
|
||||
|
||||
QSize maintainAspect(const QSize&);
|
||||
|
||||
Ui::VideoView m_ui;
|
||||
|
||||
FFmpegEncoder m_encoder;
|
||||
|
@ -95,6 +98,9 @@ private:
|
|||
int m_width;
|
||||
int m_height;
|
||||
|
||||
int m_nativeWidth;
|
||||
int m_nativeHeight;
|
||||
|
||||
QMap<QAbstractButton*, Preset> m_presets;
|
||||
|
||||
static QMap<QString, QString> s_acodecMap;
|
||||
|
|
|
@ -181,9 +181,12 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="preset160">
|
||||
<widget class="QRadioButton" name="presetNative">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>GBA (240x160)</string>
|
||||
<string>Native</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
@ -417,7 +420,7 @@
|
|||
<item row="0" column="3">
|
||||
<widget class="QSpinBox" name="height">
|
||||
<property name="minimum">
|
||||
<number>160</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>3160</number>
|
||||
|
@ -427,7 +430,7 @@
|
|||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="width">
|
||||
<property name="minimum">
|
||||
<number>240</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>3840</number>
|
||||
|
@ -442,9 +445,6 @@
|
|||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
|
@ -455,9 +455,6 @@
|
|||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
|
@ -505,7 +502,7 @@
|
|||
<tabstop>preset1080</tabstop>
|
||||
<tabstop>preset720</tabstop>
|
||||
<tabstop>preset480</tabstop>
|
||||
<tabstop>preset160</tabstop>
|
||||
<tabstop>presetNative</tabstop>
|
||||
<tabstop>container</tabstop>
|
||||
<tabstop>video</tabstop>
|
||||
<tabstop>audio</tabstop>
|
||||
|
|
|
@ -492,6 +492,12 @@ void Window::openVideoWindow() {
|
|||
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(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()));
|
||||
}
|
||||
m_videoView->show();
|
||||
|
@ -1253,6 +1259,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
QAction* recordOutput = new QAction(tr("Record output..."), avMenu);
|
||||
connect(recordOutput, SIGNAL(triggered()), this, SLOT(openVideoWindow()));
|
||||
addControlledAction(avMenu, recordOutput, "recordOutput");
|
||||
m_gameActions.append(recordOutput);
|
||||
#endif
|
||||
|
||||
#ifdef USE_MAGICK
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue