diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index ac5411056..1ed137860 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -235,6 +235,7 @@ set(obs_SOURCES slider-ignorewheel.cpp combobox-ignorewheel.cpp spinbox-ignorewheel.cpp + record-button.cpp volume-control.cpp adv-audio-control.cpp item-widget-helpers.cpp @@ -289,6 +290,7 @@ set(obs_HEADERS focus-list.hpp menu-button.hpp mute-checkbox.hpp + record-button.hpp volume-control.hpp adv-audio-control.hpp item-widget-helpers.hpp diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index f57f3700d..0d928ac76 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -21,6 +21,7 @@ void EnumSceneCollections(function &&cb); extern volatile bool streaming_active; extern volatile bool recording_active; +extern volatile bool recording_paused; extern volatile bool replaybuf_active; /* ------------------------------------------------------------------------- */ @@ -265,6 +266,17 @@ struct OBSStudioAPI : obs_frontend_callbacks { return os_atomic_load_bool(&recording_active); } + void obs_frontend_recording_pause(bool pause) override + { + QMetaObject::invokeMethod(main, pause ? "PauseRecording" + : "UnpauseRecording"); + } + + bool obs_frontend_recording_paused(void) override + { + return os_atomic_load_bool(&recording_paused); + } + void obs_frontend_replay_buffer_start(void) override { QMetaObject::invokeMethod(main, "StartReplayBuffer"); diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 2400d1fb0..ec9ef1924 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -281,6 +281,9 @@ Output.StartRecordingFailed="Failed to start recording" Output.StartReplayFailed="Failed to start replay buffer" Output.StartFailedGeneric="Starting the output failed. Please check the log for details.\n\nNote: If you are using the NVENC or AMD encoders, make sure your video drivers are up to date." +# replay buffer + pause warning message +Output.ReplayBuffer.PauseWarning.Title="Cannot save replays while paused" +Output.ReplayBuffer.PauseWarning.Text="Warning: Replays cannot be saved while recording is paused." # output connect messages Output.ConnectFail.Title="Failed to connect" @@ -501,6 +504,8 @@ Basic.Main.StartRecording="Start Recording" Basic.Main.StartReplayBuffer="Start Replay Buffer" Basic.Main.StartStreaming="Start Streaming" Basic.Main.StopRecording="Stop Recording" +Basic.Main.PauseRecording="Pause Recording" +Basic.Main.UnpauseRecording="Unpause Recording" Basic.Main.StoppingRecording="Stopping Recording..." Basic.Main.StopReplayBuffer="Stop Replay Buffer" Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..." @@ -678,6 +683,7 @@ Basic.Settings.Output.Simple.RecordingQuality.HQ="Indistinguishable Quality, Lar Basic.Settings.Output.Simple.RecordingQuality.Lossless="Lossless Quality, Tremendously Large File Size" Basic.Settings.Output.Simple.Warn.VideoBitrate="Warning: The streaming video bitrate will be set to %1, which is the upper limit for the current streaming service. If you're sure you want to go above %1, enable advanced encoder options and uncheck \"Enforce streaming service bitrate limits\"." Basic.Settings.Output.Simple.Warn.AudioBitrate="Warning: The streaming audio bitrate will be set to %1, which is the upper limit for the current streaming service. If you're sure you want to go above %1, enable advanced encoder options and uncheck \"Enforce streaming service bitrate limits\"." +Basic.Settings.Output.Simple.Warn.CannotPause="Warning: Recordings cannot be paused if the recording quality is set to \"Same as stream\"." Basic.Settings.Output.Simple.Warn.Encoder="Warning: Recording with a software encoder at a different quality than the stream will require extra CPU usage if you stream and record at the same time." Basic.Settings.Output.Simple.Warn.Lossless="Warning: Lossless quality generates tremendously large file sizes! Lossless quality can use upward of 7 gigabytes of disk space per minute at high resolutions and framerates. Lossless is not recommended for long recordings unless you have a very large amount of disk space available." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Are you sure you want to use lossless quality?" @@ -909,6 +915,7 @@ SceneItemHide="Hide '%1'" OutputWarnings.NoTracksSelected="You must select at least one track" OutputWarnings.MultiTrackRecording="Warning: Certain formats (such as FLV) do not support multiple tracks per recording" OutputWarnings.MP4Recording="Warning: Recordings saved to MP4/MOV will be unrecoverable if the file cannot be finalized (e.g. as a result of BSODs, power losses, etc.). If you want to record multiple audio tracks consider using MKV and remux the recording to MP4/MOV after it is finished (File → Remux Recordings)" +OutputWarnings.CannotPause="Warning: Recordings cannot be paused if the recording encoder is set to \"(Use stream encoder)\"" # deleting final scene FinalScene.Title="Delete Scene" diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index ae2ef8c9a..e98cd8b3d 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -353,6 +353,10 @@ QToolButton:pressed { qproperty-icon: url(./Dark/down.svg); } +* [themeID="pauseIconSmall"] { + qproperty-icon: url(./Dark/media-pause.svg); +} + /* Tab Widget */ QTabWidget::pane { /* The tab widget frame */ diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 693a5561f..9a7c65e74 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -253,6 +253,10 @@ QToolButton:pressed { qproperty-icon: url(./Dark/down.svg); } +* [themeID="pauseIconSmall"] { + qproperty-icon: url(./Dark/media-pause.svg); +} + /* Tab Widget */ @@ -577,6 +581,19 @@ OBSHotkeyLabel[hotkeyPairHover=true] { color: red; } +/* Pause */ +PauseCheckBox { + outline: none; +} + +PauseCheckBox::indicator:checked { + image: url(:/res/images/media-pause.svg); +} + +PauseCheckBox::indicator:unchecked { + image: url(:/res/images/media-play.svg); +} + /* Group Collapse Checkbox */ SourceTreeSubItemCheckBox { diff --git a/UI/data/themes/Dark/media-pause.svg b/UI/data/themes/Dark/media-pause.svg new file mode 100644 index 000000000..6050b9472 --- /dev/null +++ b/UI/data/themes/Dark/media-pause.svg @@ -0,0 +1,3 @@ + + + diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 88bde15e7..1e6a697f2 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -507,6 +507,10 @@ QToolButton:pressed { qproperty-icon: url(./Dark/down.svg); } +* [themeID="pauseIconSmall"] { + qproperty-icon: url(./Dark/media-pause.svg); +} + /***********************/ /* --- Combo boxes --- */ /***********************/ diff --git a/UI/data/themes/System.qss b/UI/data/themes/System.qss index c68e0c462..0eb01cf19 100644 --- a/UI/data/themes/System.qss +++ b/UI/data/themes/System.qss @@ -39,6 +39,10 @@ qproperty-icon: url(:/res/images/down.svg); } +* [themeID="pauseIconSmall"] { + qproperty-icon: url(:/res/images/media-pause.svg); +} + MuteCheckBox { outline: none; } diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index b082729f8..3b72c6295 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -116,7 +116,7 @@ 0 0 1079 - 22 + 21 @@ -656,7 +656,7 @@ 0 0 - 64 + 80 16 @@ -843,7 +843,7 @@ - + :/res/images/add.png:/res/images/add.png @@ -878,7 +878,7 @@ - + :/res/images/list_remove.png:/res/images/list_remove.png @@ -913,7 +913,7 @@ - + :/res/images/configuration21_16.png:/res/images/configuration21_16.png @@ -1000,7 +1000,7 @@ 8 - + 2 @@ -1037,29 +1037,48 @@ - - - true + + + 2 - - - 0 - 0 - + + 0 - - - 130 - 0 - + + 0 - - Basic.Main.StartRecording + + 0 - - true + + 0 - + + + + true + + + + 0 + 0 + + + + + 130 + 0 + + + + Basic.Main.StartRecording + + + true + + + + @@ -1115,7 +1134,7 @@ - + :/res/images/add.png:/res/images/add.png @@ -1127,7 +1146,7 @@ - + :/res/images/add.png:/res/images/add.png @@ -1139,7 +1158,7 @@ - + :/res/images/list_remove.png:/res/images/list_remove.png @@ -1157,7 +1176,7 @@ - + :/res/images/list_remove.png:/res/images/list_remove.png @@ -1178,7 +1197,7 @@ true - + :/res/images/properties.png:/res/images/properties.png @@ -1190,7 +1209,7 @@ - + :/res/images/up.png:/res/images/up.png @@ -1205,7 +1224,7 @@ true - + :/res/images/up.png:/res/images/up.png @@ -1217,7 +1236,7 @@ - + :/res/images/down.png:/res/images/down.png @@ -1232,7 +1251,7 @@ true - + :/res/images/down.png:/res/images/down.png @@ -1733,6 +1752,11 @@
window-dock.hpp
1 + + RecordButton + QPushButton +
record-button.hpp
+
diff --git a/UI/forms/images/media-pause.svg b/UI/forms/images/media-pause.svg new file mode 100644 index 000000000..8e45ee21f --- /dev/null +++ b/UI/forms/images/media-pause.svg @@ -0,0 +1,3 @@ + + + diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index 7c647c7b7..87e11e359 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -1,5 +1,6 @@ + images/media-pause.svg images/mute.svg images/refresh.svg images/no_sources.svg diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 0181a87ab..c5fafeb0c 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -227,6 +227,17 @@ bool obs_frontend_recording_active(void) return !!callbacks_valid() ? c->obs_frontend_recording_active() : false; } +void obs_frontend_recording_pause(bool pause) +{ + if (!!callbacks_valid()) + c->obs_frontend_recording_pause(pause); +} + +bool obs_frontend_recording_paused(void) +{ + return !!callbacks_valid() ? c->obs_frontend_recording_paused() : false; +} + void obs_frontend_replay_buffer_start(void) { if (callbacks_valid()) diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index fb0d8b686..68fa917fd 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -44,6 +44,9 @@ enum obs_frontend_event { OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP, OBS_FRONTEND_EVENT_FINISHED_LOADING, + + OBS_FRONTEND_EVENT_RECORDING_PAUSED, + OBS_FRONTEND_EVENT_RECORDING_UNPAUSED, }; /* ------------------------------------------------------------------------- */ @@ -152,6 +155,8 @@ EXPORT bool obs_frontend_streaming_active(void); EXPORT void obs_frontend_recording_start(void); EXPORT void obs_frontend_recording_stop(void); EXPORT bool obs_frontend_recording_active(void); +EXPORT void obs_frontend_recording_pause(bool pause); +EXPORT bool obs_frontend_recording_paused(void); EXPORT void obs_frontend_replay_buffer_start(void); EXPORT void obs_frontend_replay_buffer_save(void); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index 617b842fc..80051ca79 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -43,6 +43,8 @@ struct obs_frontend_callbacks { virtual void obs_frontend_recording_start(void) = 0; virtual void obs_frontend_recording_stop(void) = 0; virtual bool obs_frontend_recording_active(void) = 0; + virtual void obs_frontend_recording_pause(bool pause) = 0; + virtual bool obs_frontend_recording_paused(void) = 0; virtual void obs_frontend_replay_buffer_start(void) = 0; virtual void obs_frontend_replay_buffer_save(void) = 0; diff --git a/UI/record-button.cpp b/UI/record-button.cpp new file mode 100644 index 000000000..8b82c2b0f --- /dev/null +++ b/UI/record-button.cpp @@ -0,0 +1,18 @@ +#include "record-button.hpp" +#include "window-basic-main.hpp" + +void RecordButton::resizeEvent(QResizeEvent *event) +{ + OBSBasic *main = OBSBasic::Get(); + if (!main->pause) + return; + + QSize newSize = event->size(); + QSize pauseSize = main->pause->size(); + int height = main->ui->recordButton->size().height(); + + if (pauseSize.height() != height || pauseSize.width() != height) { + main->pause->setMinimumSize(height, height); + main->pause->setMaximumSize(height, height); + } +} diff --git a/UI/record-button.hpp b/UI/record-button.hpp new file mode 100644 index 000000000..c1782aafa --- /dev/null +++ b/UI/record-button.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +class RecordButton : public QPushButton { + Q_OBJECT + +public: + inline RecordButton(QWidget *parent = nullptr) : QPushButton(parent) {} + + virtual void resizeEvent(QResizeEvent *event) override; +}; diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index f15bca4ef..93cd37c7c 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -12,6 +12,7 @@ extern bool EncoderAvailable(const char *encoder); volatile bool streaming_active = false; volatile bool recording_active = false; +volatile bool recording_paused = false; volatile bool replaybuf_active = false; static void OBSStreamStarting(void *data, calldata_t *params) @@ -88,6 +89,7 @@ static void OBSStopRecording(void *data, calldata_t *params) output->recordingActive = false; os_atomic_set_bool(&recording_active, false); + os_atomic_set_bool(&recording_paused, false); QMetaObject::invokeMethod(output->main, "RecordingStop", Q_ARG(int, code), Q_ARG(QString, arg_last_error)); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index dabc30a53..f482e33a4 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2112,6 +2112,17 @@ void OBSBasic::CreateHotkeys() LoadHotkeyPair(recordingHotkeys, "OBSBasic.StartRecording", "OBSBasic.StopRecording"); + pauseHotkeys = obs_hotkey_pair_register_frontend( + "OBSBasic.PauseRecording", Str("Basic.Main.PauseRecording"), + "OBSBasic.UnpauseRecording", Str("Basic.Main.UnpauseRecording"), + MAKE_CALLBACK(basic.pause && !basic.pause->isChecked(), + basic.PauseRecording, "Pausing recording"), + MAKE_CALLBACK(basic.pause && basic.pause->isChecked(), + basic.UnpauseRecording, "Unpausing recording"), + this, this); + LoadHotkeyPair(pauseHotkeys, "OBSBasic.PauseRecording", + "OBSBasic.UnpauseRecording"); + replayBufHotkeys = obs_hotkey_pair_register_frontend( "OBSBasic.StartReplayBuffer", Str("Basic.Main.StartReplayBuffer"), @@ -2169,6 +2180,7 @@ void OBSBasic::ClearHotkeys() { obs_hotkey_pair_unregister(streamingHotkeys); obs_hotkey_pair_unregister(recordingHotkeys); + obs_hotkey_pair_unregister(pauseHotkeys); obs_hotkey_pair_unregister(replayBufHotkeys); obs_hotkey_pair_unregister(togglePreviewHotkeys); obs_hotkey_unregister(forceStreamingStopHotkey); @@ -5319,6 +5331,7 @@ void OBSBasic::RecordingStart() api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); OnActivate(); + UpdatePause(); blog(LOG_INFO, RECORDING_START); } @@ -5385,11 +5398,46 @@ void OBSBasic::RecordingStop(int code, QString last_error) AutoRemux(); OnDeactivate(); + UpdatePause(false); } #define RP_NO_HOTKEY_TITLE QTStr("Output.ReplayBuffer.NoHotkey.Title") #define RP_NO_HOTKEY_TEXT QTStr("Output.ReplayBuffer.NoHotkey.Msg") +extern volatile bool recording_paused; +extern volatile bool replaybuf_active; + +void OBSBasic::ShowReplayBufferPauseWarning() +{ + auto msgBox = []() { + QMessageBox msgbox(App()->GetMainWindow()); + msgbox.setWindowTitle(QTStr("Output.ReplayBuffer." + "PauseWarning.Title")); + msgbox.setText(QTStr("Output.ReplayBuffer." + "PauseWarning.Text")); + msgbox.setIcon(QMessageBox::Icon::Information); + msgbox.addButton(QMessageBox::Ok); + + QCheckBox *cb = new QCheckBox(QTStr("DoNotShowAgain")); + msgbox.setCheckBox(cb); + + msgbox.exec(); + + if (cb->isChecked()) { + config_set_bool(App()->GlobalConfig(), "General", + "WarnedAboutReplayBufferPausing", true); + config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + } + }; + + bool warned = config_get_bool(App()->GlobalConfig(), "General", + "WarnedAboutReplayBufferPausing"); + if (!warned) { + QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection, + Q_ARG(VoidFunc, msgBox)); + } +} + void OBSBasic::StartReplayBuffer() { if (!outputHandler || !outputHandler->replayBuffer) @@ -5423,8 +5471,12 @@ void OBSBasic::StartReplayBuffer() api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING); SaveProject(); - if (!outputHandler->StartReplayBuffer()) + + if (!outputHandler->StartReplayBuffer()) { replayBufferButton->setChecked(false); + } else if (os_atomic_load_bool(&recording_paused)) { + ShowReplayBufferPauseWarning(); + } } void OBSBasic::ReplayBufferStopping() @@ -7295,3 +7347,106 @@ void OBSBasic::UpdatePatronJson(const QString &text, const QString &error) patronJson = QT_TO_UTF8(text); } + +void OBSBasic::PauseRecording() +{ + if (!pause || !outputHandler || !outputHandler->fileOutput) + return; + + obs_output_t *output = outputHandler->fileOutput; + + if (obs_output_pause(output, true)) { + pause->setChecked(true); + os_atomic_set_bool(&recording_paused, true); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_RECORDING_PAUSED); + + if (os_atomic_load_bool(&replaybuf_active)) + ShowReplayBufferPauseWarning(); + } +} + +void OBSBasic::UnpauseRecording() +{ + if (!pause || !outputHandler || !outputHandler->fileOutput) + return; + + obs_output_t *output = outputHandler->fileOutput; + + if (obs_output_pause(output, false)) { + pause->setChecked(false); + os_atomic_set_bool(&recording_paused, false); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_RECORDING_UNPAUSED); + } +} + +void OBSBasic::PauseToggled() +{ + if (!pause || !outputHandler || !outputHandler->fileOutput) + return; + + obs_output_t *output = outputHandler->fileOutput; + bool enable = !obs_output_paused(output); + + if (obs_output_pause(output, enable)) { + os_atomic_set_bool(&recording_paused, enable); + + if (api) + api->on_event( + enable ? OBS_FRONTEND_EVENT_RECORDING_PAUSED + : OBS_FRONTEND_EVENT_RECORDING_UNPAUSED); + + if (enable && os_atomic_load_bool(&replaybuf_active)) + ShowReplayBufferPauseWarning(); + } else { + pause->setChecked(!enable); + } +} + +void OBSBasic::UpdatePause(bool activate) +{ + if (!activate || !outputHandler || !outputHandler->RecordingActive()) { + pause.reset(); + return; + } + + const char *mode = config_get_string(basicConfig, "Output", "Mode"); + bool adv = astrcmpi(mode, "Advanced") == 0; + bool shared; + + if (adv) { + const char *recType = + config_get_string(basicConfig, "AdvOut", "RecType"); + + if (astrcmpi(recType, "FFmpeg") == 0) { + shared = config_get_bool(basicConfig, "AdvOut", + "FFOutputToFile"); + } else { + const char *recordEncoder = config_get_string( + basicConfig, "AdvOut", "RecEncoder"); + shared = astrcmpi(recordEncoder, "none") == 0; + } + } else { + const char *quality = config_get_string( + basicConfig, "SimpleOutput", "RecQuality"); + shared = strcmp(quality, "Stream") == 0; + } + + if (!shared) { + pause.reset(new QPushButton()); + pause->setAccessibleName(QTStr("Basic.Main.PauseRecording")); + pause->setToolTip(QTStr("Basic.Main.PauseRecording")); + pause->setCheckable(true); + pause->setChecked(false); + pause->setProperty("themeID", + QVariant(QStringLiteral("pauseIconSmall"))); + connect(pause.data(), &QAbstractButton::clicked, this, + &OBSBasic::PauseToggled); + ui->recordingLayout->addWidget(pause.data()); + } else { + pause.reset(); + } +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 62d65c7dd..287b4957a 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -123,6 +123,7 @@ class OBSBasic : public OBSMainWindow { friend class Auth; friend class AutoConfig; friend class AutoConfigStreamPage; + friend class RecordButton; friend struct OBSStudioAPI; enum class MoveDir { Up, Down, Left, Right }; @@ -204,6 +205,7 @@ private: QPointer transitionButton; QPointer replayBufferButton; + QScopedPointer pause; QScopedPointer trayIcon; QPointer sysTrayStream; @@ -323,8 +325,8 @@ private: int GetTopSelectedSourceItem(); - obs_hotkey_pair_id streamingHotkeys, recordingHotkeys, replayBufHotkeys, - togglePreviewHotkeys; + obs_hotkey_pair_id streamingHotkeys, recordingHotkeys, pauseHotkeys, + replayBufHotkeys, togglePreviewHotkeys; obs_hotkey_id forceStreamingStopHotkey; void InitDefaultTransitions(); @@ -440,6 +442,7 @@ public slots: void RecordStopping(); void RecordingStop(int code, QString last_error); + void ShowReplayBufferPauseWarning(); void StartReplayBuffer(); void StopReplayBuffer(); @@ -465,6 +468,9 @@ public slots: void UpdatePatronJson(const QString &text, const QString &error); + void PauseRecording(); + void UnpauseRecording(); + private slots: void AddSceneItem(OBSSceneItem item); void AddScene(OBSSource source); @@ -557,6 +563,7 @@ private: static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed); void AutoRemux(); + void UpdatePause(bool activate = true); public: OBSSource GetProgramSource(); @@ -760,6 +767,8 @@ private slots: void on_resetUI_triggered(); void on_lockUI_toggled(bool lock); + void PauseToggled(); + void logUploadFinished(const QString &text, const QString &error); void updateCheckFinished(); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 2785b1381..95f8cfa25 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -729,6 +729,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) SLOT(AdvOutRecCheckWarnings())); connect(ui->advOutRecFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(AdvOutRecCheckWarnings())); + connect(ui->advOutRecEncoder, SIGNAL(currentIndexChanged(int)), this, + SLOT(AdvOutRecCheckWarnings())); AdvOutRecCheckWarnings(); ui->buttonBox->button(QDialogButtonBox::Apply)->setIcon(QIcon()); @@ -3929,6 +3931,13 @@ void OBSBasicSettings::AdvOutRecCheckWarnings() warningMsg = QTStr("OutputWarnings.MultiTrackRecording"); } + bool useStreamEncoder = ui->advOutRecEncoder->currentIndex() == 0; + if (useStreamEncoder) { + if (!warningMsg.isEmpty()) + warningMsg += "\n\n"; + warningMsg += QTStr("OutputWarnings.CannotPause"); + } + if (ui->advOutRecFormat->currentText().compare("mp4") == 0 || ui->advOutRecFormat->currentText().compare("mov") == 0) { if (!warningMsg.isEmpty()) @@ -4387,6 +4396,10 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged() warning += "\n\n"; warning += SIMPLE_OUTPUT_WARNING("Encoder"); } + } else { + if (!warning.isEmpty()) + warning += "\n\n"; + warning += SIMPLE_OUTPUT_WARNING("CannotPause"); } if (qual != "Lossless" && diff --git a/UI/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp index b94a8c0bb..74dde3a3c 100644 --- a/UI/window-basic-status-bar.cpp +++ b/UI/window-basic-status-bar.cpp @@ -241,17 +241,28 @@ void OBSBasicStatusBar::UpdateStreamTime() } } +extern volatile bool recording_paused; + void OBSBasicStatusBar::UpdateRecordTime() { - totalRecordSeconds++; + bool paused = os_atomic_load_bool(&recording_paused); - int seconds = totalRecordSeconds % 60; - int totalMinutes = totalRecordSeconds / 60; - int minutes = totalMinutes % 60; - int hours = totalMinutes / 60; + if (!paused) + totalRecordSeconds++; QString text; - text.sprintf("REC: %02d:%02d:%02d", hours, minutes, seconds); + + if (paused) { + text = QStringLiteral("REC: PAUSED"); + } else { + int seconds = totalRecordSeconds % 60; + int totalMinutes = totalRecordSeconds / 60; + int minutes = totalMinutes % 60; + int hours = totalMinutes / 60; + + text.sprintf("REC: %02d:%02d:%02d", hours, minutes, seconds); + } + recordTime->setText(text); recordTime->setMinimumWidth(recordTime->width()); } diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 3b5b86db6..46df5bd63 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -124,6 +124,18 @@ Structures/Enumerations the program is either about to load a new scene collection, or the program is about to exit. + - **OBS_FRONTEND_FINISHED_LOADING** + + Triggered when the program has finished loading. + + - **OBS_FRONTEND_EVENT_RECORDING_PAUSED** + + Triggered when the recording has been paused. + + - **OBS_FRONTEND_EVENT_RECORDING_UNPAUSED** + + Triggered when the recording has been unpaused. + .. type:: struct obs_frontend_source_list - DARRAY(obs_source_t*) **sources** @@ -402,6 +414,18 @@ Functions --------------------------------------- +.. function:: void obs_frontend_recording_pause(bool pause) + + :pause: *true* to pause recording, *false* to unpause. + +--------------------------------------- + +.. function:: bool obs_frontend_recording_paused(void) + + :return: *true* if recording paused, *false* otherwise. + +--------------------------------------- + .. function:: void obs_frontend_replay_buffer_start(void) Starts replay buffer.