From 8c8361477d8bd34d905368a3b76952aef65ff70d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 6 Sep 2016 20:41:55 -0700 Subject: [PATCH] Qt: Clean up video recording for GB --- src/platform/qt/GBAApp.h | 1 - src/platform/qt/VideoView.cpp | 159 ++++++++++------ src/platform/qt/VideoView.h | 30 +-- src/platform/qt/VideoView.ui | 19 +- src/platform/qt/Window.cpp | 7 + src/platform/qt/ts/mgba-es.ts | 345 +++++++++++++++++----------------- 6 files changed, 307 insertions(+), 254 deletions(-) diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index ea7fa1267..d54712744 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -16,7 +16,6 @@ struct NoIntroDB; extern "C" { #include "core/log.h" -#include "gba/sio.h" } mLOG_DECLARE_CATEGORY(QT); diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index 48b50be50..bb3805a4e 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -12,6 +12,12 @@ #include +#ifdef M_CORE_GB +extern "C" { +#include "gb/video.h" +} +#endif + using namespace QGBA; QMap 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 diff --git a/src/platform/qt/VideoView.h b/src/platform/qt/VideoView.h index 75249fda3..360de48df 100644 --- a/src/platform/qt/VideoView.h +++ b/src/platform/qt/VideoView.h @@ -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& 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 m_presets; static QMap s_acodecMap; diff --git a/src/platform/qt/VideoView.ui b/src/platform/qt/VideoView.ui index c5e7c8b33..ff9dcaed1 100644 --- a/src/platform/qt/VideoView.ui +++ b/src/platform/qt/VideoView.ui @@ -181,9 +181,12 @@ - + + + false + - GBA (240x160) + Native true @@ -417,7 +420,7 @@ - 160 + 1 3160 @@ -427,7 +430,7 @@ - 240 + 1 3840 @@ -442,9 +445,6 @@ 9999 - - 2 - @@ -455,9 +455,6 @@ 9999 - - 3 - @@ -505,7 +502,7 @@ preset1080 preset720 preset480 - preset160 + presetNative container video audio diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index fb8bd6d33..6248f32fd 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -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 diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index bb4cdbfae..19fb787f2 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -2304,12 +2304,17 @@ QGBA::VideoView - + Failed to open output video file: %1 - + + Native (%0x%1) + + + + Select output file @@ -2317,690 +2322,690 @@ QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + Archives (%1) - - - + + + Select ROM - + Game Boy Advance save files (%1) - - - + + + Select save - + Select BIOS - + Select patch - + Patches (*.ips *.ups *.bps) - - + + GameShark saves (*.sps *.xps) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Load - + Could not load game. Are you sure it's in the correct format? - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Load &BIOS... - + Load temporary save... - + Load &patch... - + Boot BIOS - + Replace ROM... - + ROM &info... - + Recent - + Make portable - + &Load state - + F10 - + &Save state - + Shift+F10 - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + F11 - + Undo save state - + Shift+F11 - - + + State &%1 - + F%1 - + Shift+F%1 - + Import GameShark Save - + Export GameShark Save - + New multiplayer window - + About - + E&xit - + &Emulation - + &Reset - + Ctrl+R - + Sh&utdown - + Yank game pak - + &Pause - + Ctrl+P - + &Next frame - + Ctrl+N - + Fast forward (held) - + &Fast forward - + Shift+Tab - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + ` - + Step backwards - + Ctrl+B - + Sync to &video - + Sync to &audio - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Audio/&Video - + Frame size - + %1x - + Toggle fullscreen - + Lock aspect ratio - + Resample video - + Frame&skip - + Shader options... - + Mute - + FPS target - + 15 - + 30 - + 45 - + Native (59.7) - + 60 - + 90 - + 120 - + 240 - + Take &screenshot - + F12 - + Record output... - + Record GIF... - + Video layers - + Background %0 - + OBJ (sprites) - + Audio channels - + Channel %0 - + Channel A - + Channel B - + &Tools - + View &logs... - + Game &overrides... - + Game &Pak sensors... - + &Cheats... - + Start &GDB server... - + Settings... - + View &palette... - + View &tiles... - + View memory... - + View &I/O registers... - + Exit fullscreen - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left @@ -3597,7 +3602,7 @@ - + WebM @@ -3622,122 +3627,122 @@ - - GBA (240x160) + + Native - + Format - + MKV - + AVI - + MP4 - + PNG - + h.264 - + VP8 - + Xvid - + FFV1 - + FLAC - + Opus - + Vorbis - + MP3 - + AAC - + Uncompressed - + Bitrate (kbps) - + VBR - + ABR - + Dimensions - + : - + × - + Lock aspect ratio - + Show advanced