UI: Add pause support

Adds support for pausing recordings.  When settings are eligible for
recordings, a pause button will appear next to the recording button.  If
the settings are not eligible, it will warn the user in the output
settings that they cannot pause recordings if those settings are used.
master
jp9000 2019-07-07 15:47:29 -07:00
parent 942ca6f709
commit eab10d48b2
22 changed files with 385 additions and 42 deletions

View File

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

View File

@ -21,6 +21,7 @@ void EnumSceneCollections(function<bool(const char *, const char *)> &&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");

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8" fill="#d2d2d2">
<path d="M0 0v6h2v-6h-2zm4 0v6h2v-6h-2z" transform="translate(1 1)" />
</svg>

After

Width:  |  Height:  |  Size: 154 B

View File

@ -507,6 +507,10 @@ QToolButton:pressed {
qproperty-icon: url(./Dark/down.svg);
}
* [themeID="pauseIconSmall"] {
qproperty-icon: url(./Dark/media-pause.svg);
}
/***********************/
/* --- Combo boxes --- */
/***********************/

View File

@ -39,6 +39,10 @@
qproperty-icon: url(:/res/images/down.svg);
}
* [themeID="pauseIconSmall"] {
qproperty-icon: url(:/res/images/media-pause.svg);
}
MuteCheckBox {
outline: none;
}

View File

@ -116,7 +116,7 @@
<x>0</x>
<y>0</y>
<width>1079</width>
<height>22</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@ -656,7 +656,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>64</width>
<width>80</width>
<height>16</height>
</rect>
</property>
@ -843,7 +843,7 @@
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/add.png</normaloff>:/res/images/add.png</iconset>
</property>
<property name="flat">
@ -878,7 +878,7 @@
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/list_remove.png</normaloff>:/res/images/list_remove.png</iconset>
</property>
<property name="flat">
@ -913,7 +913,7 @@
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/configuration21_16.png</normaloff>:/res/images/configuration21_16.png</iconset>
</property>
<property name="flat">
@ -1000,7 +1000,7 @@
<attribute name="dockWidgetArea">
<number>8</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_3">
<widget class="QWidget" name="controlsDockContents">
<layout class="QVBoxLayout" name="buttonsVLayout">
<property name="spacing">
<number>2</number>
@ -1037,29 +1037,48 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="recordButton">
<property name="enabled">
<bool>true</bool>
<layout class="QHBoxLayout" name="recordingLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="leftMargin">
<number>0</number>
</property>
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
<property name="topMargin">
<number>0</number>
</property>
<property name="text">
<string>Basic.Main.StartRecording</string>
<property name="rightMargin">
<number>0</number>
</property>
<property name="checkable">
<bool>true</bool>
<property name="bottomMargin">
<number>0</number>
</property>
</widget>
<item>
<widget class="RecordButton" name="recordButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Main.StartRecording</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="modeSwitch">
@ -1115,7 +1134,7 @@
</widget>
<action name="actionAddScene">
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/add.png</normaloff>:/res/images/add.png</iconset>
</property>
<property name="text">
@ -1127,7 +1146,7 @@
</action>
<action name="actionAddSource">
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/add.png</normaloff>:/res/images/add.png</iconset>
</property>
<property name="text">
@ -1139,7 +1158,7 @@
</action>
<action name="actionRemoveScene">
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/list_remove.png</normaloff>:/res/images/list_remove.png</iconset>
</property>
<property name="text">
@ -1157,7 +1176,7 @@
</action>
<action name="actionRemoveSource">
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/list_remove.png</normaloff>:/res/images/list_remove.png</iconset>
</property>
<property name="text">
@ -1178,7 +1197,7 @@
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/properties.png</normaloff>:/res/images/properties.png</iconset>
</property>
<property name="text">
@ -1190,7 +1209,7 @@
</action>
<action name="actionSceneUp">
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/up.png</normaloff>:/res/images/up.png</iconset>
</property>
<property name="text">
@ -1205,7 +1224,7 @@
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/up.png</normaloff>:/res/images/up.png</iconset>
</property>
<property name="text">
@ -1217,7 +1236,7 @@
</action>
<action name="actionSceneDown">
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/down.png</normaloff>:/res/images/down.png</iconset>
</property>
<property name="text">
@ -1232,7 +1251,7 @@
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<iconset>
<normaloff>:/res/images/down.png</normaloff>:/res/images/down.png</iconset>
</property>
<property name="text">
@ -1733,6 +1752,11 @@
<header>window-dock.hpp</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RecordButton</class>
<extends>QPushButton</extends>
<header>record-button.hpp</header>
</customwidget>
</customwidgets>
<resources>
<include location="obs.qrc"/>

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8" fill="#000000">
<path d="M0 0v6h2v-6h-2zm4 0v6h2v-6h-2z" transform="translate(1 1)" />
</svg>

After

Width:  |  Height:  |  Size: 154 B

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/res">
<file>images/media-pause.svg</file>
<file>images/mute.svg</file>
<file>images/refresh.svg</file>
<file>images/no_sources.svg</file>

View File

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

View File

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

View File

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

18
UI/record-button.cpp Normal file
View File

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

12
UI/record-button.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <QPushButton>
class RecordButton : public QPushButton {
Q_OBJECT
public:
inline RecordButton(QWidget *parent = nullptr) : QPushButton(parent) {}
virtual void resizeEvent(QResizeEvent *event) override;
};

View File

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

View File

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

View File

@ -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<QPushButton> transitionButton;
QPointer<QPushButton> replayBufferButton;
QScopedPointer<QPushButton> pause;
QScopedPointer<QSystemTrayIcon> trayIcon;
QPointer<QAction> 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();

View File

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

View File

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

View File

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