diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp
index 6a5bcc84e..4612100f8 100644
--- a/UI/api-interface.cpp
+++ b/UI/api-interface.cpp
@@ -234,6 +234,21 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return main->outputHandler->RecordingActive();
}
+ void obs_frontend_replay_buffer_start(void) override
+ {
+ QMetaObject::invokeMethod(main, "StartReplayBuffer");
+ }
+
+ void obs_frontend_replay_buffer_stop(void) override
+ {
+ QMetaObject::invokeMethod(main, "StopReplayBuffer");
+ }
+
+ bool obs_frontend_replay_buffer_active(void) override
+ {
+ return main->outputHandler->ReplayBufferActive();
+ }
+
void *obs_frontend_add_tools_menu_qaction(const char *name) override
{
main->ui->menuTools->setEnabled(true);
@@ -286,6 +301,13 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return out;
}
+ obs_output_t *obs_frontend_get_replay_buffer_output(void) override
+ {
+ OBSOutput out = main->outputHandler->replayBuffer;
+ obs_output_addref(out);
+ return out;
+ }
+
config_t *obs_frontend_get_profile_config(void) override
{
return main->basicConfig;
diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
index 8802a12ae..a8ebb2b15 100644
--- a/UI/data/locale/en-US.ini
+++ b/UI/data/locale/en-US.ini
@@ -420,12 +420,14 @@ Basic.Settings.Output.Mode="Output Mode"
Basic.Settings.Output.Mode.Simple="Simple"
Basic.Settings.Output.Mode.Adv="Advanced"
Basic.Settings.Output.Mode.FFmpeg="FFmpeg Output"
-Basic.Settings.Output.UseReplayBuffer="Replay Buffer Mode"
+Basic.Settings.Output.UseReplayBuffer="Enable Replay Buffer"
Basic.Settings.Output.ReplayBuffer.SecondsMax="Maximum Replay Time (Seconds)"
Basic.Settings.Output.ReplayBuffer.MegabytesMax="Maximum Memory (Megabytes)"
Basic.Settings.Output.ReplayBuffer.Estimate="Estimated memory usage: %1 MB"
Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Cannot estimate memory usage. Please set maximum memory limit."
Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(Note: Make sure to set a hotkey for the replay buffer in the hotkeys section)"
+Basic.Settings.Output.ReplayBuffer.Prefix="Replay Buffer Filename Prefix"
+Basic.Settings.Output.ReplayBuffer.Suffix="Suffix"
Basic.Settings.Output.Simple.SavePath="Recording Path"
Basic.Settings.Output.Simple.RecordingQuality="Recording Quality"
Basic.Settings.Output.Simple.RecordingQuality.Stream="Same as stream"
@@ -572,10 +574,6 @@ Basic.Settings.Hotkeys="Hotkeys"
Basic.Settings.Hotkeys.Pair="Key combinations shared with '%1' act as toggles"
# basic mode hotkeys
-Basic.Hotkeys.StartStreaming="Start Streaming"
-Basic.Hotkeys.StopStreaming="Stop Streaming"
-Basic.Hotkeys.StartRecording="Start Recording/Replay Buffer"
-Basic.Hotkeys.StopRecording="Stop Recording/Replay Buffer"
Basic.Hotkeys.SelectScene="Switch to scene"
# system tray
diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui
index 8c9ccdb61..ad11a9dfb 100644
--- a/UI/forms/OBSBasicSettings.ui
+++ b/UI/forms/OBSBasicSettings.ui
@@ -943,13 +943,6 @@
- -
-
-
-
-
-
-
-
@@ -957,6 +950,13 @@
+ -
+
+
+
+
+
+
diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp
index 5d01b64d8..6951bc920 100644
--- a/UI/obs-frontend-api/obs-frontend-api.cpp
+++ b/UI/obs-frontend-api/obs-frontend-api.cpp
@@ -205,6 +205,23 @@ bool obs_frontend_recording_active(void)
: false;
}
+void obs_frontend_replay_buffer_start(void)
+{
+ if (callbacks_valid()) c->obs_frontend_replay_buffer_start();
+}
+
+void obs_frontend_replay_buffer_stop(void)
+{
+ if (callbacks_valid()) c->obs_frontend_replay_buffer_stop();
+}
+
+bool obs_frontend_replay_buffer_active(void)
+{
+ return !!callbacks_valid()
+ ? c->obs_frontend_replay_buffer_active()
+ : false;
+}
+
void *obs_frontend_add_tools_menu_qaction(const char *name)
{
return !!callbacks_valid()
@@ -248,6 +265,13 @@ obs_output_t *obs_frontend_get_recording_output(void)
: nullptr;
}
+obs_output_t *obs_frontend_get_replay_buffer_output(void)
+{
+ return !!callbacks_valid()
+ ? c->obs_frontend_get_replay_buffer_output()
+ : nullptr;
+}
+
config_t *obs_frontend_get_profile_config(void)
{
return !!callbacks_valid()
diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h
index 8a6272a2a..7be113cee 100644
--- a/UI/obs-frontend-api/obs-frontend-api.h
+++ b/UI/obs-frontend-api/obs-frontend-api.h
@@ -70,6 +70,10 @@ 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_replay_buffer_start(void);
+EXPORT void obs_frontend_replay_buffer_stop(void);
+EXPORT bool obs_frontend_replay_buffer_active(void);
+
typedef void (*obs_frontend_cb)(void *private_data);
EXPORT void *obs_frontend_add_tools_menu_qaction(const char *name);
@@ -94,7 +98,12 @@ enum obs_frontend_event {
OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED,
OBS_FRONTEND_EVENT_PROFILE_CHANGED,
OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED,
- OBS_FRONTEND_EVENT_EXIT
+ OBS_FRONTEND_EVENT_EXIT,
+
+ OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING,
+ OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED,
+ OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING,
+ OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED
};
typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event,
@@ -116,6 +125,7 @@ EXPORT void obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
EXPORT obs_output_t *obs_frontend_get_streaming_output(void);
EXPORT obs_output_t *obs_frontend_get_recording_output(void);
+EXPORT obs_output_t *obs_frontend_get_replay_buffer_output(void);
EXPORT config_t *obs_frontend_get_profile_config(void);
EXPORT config_t *obs_frontend_get_global_config(void);
diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp
index cb8da9647..f22d89142 100644
--- a/UI/obs-frontend-api/obs-frontend-internal.hpp
+++ b/UI/obs-frontend-api/obs-frontend-internal.hpp
@@ -39,6 +39,10 @@ struct obs_frontend_callbacks {
virtual void obs_frontend_recording_stop(void)=0;
virtual bool obs_frontend_recording_active(void)=0;
+ virtual void obs_frontend_replay_buffer_start(void)=0;
+ virtual void obs_frontend_replay_buffer_stop(void)=0;
+ virtual bool obs_frontend_replay_buffer_active(void)=0;
+
virtual void *obs_frontend_add_tools_menu_qaction(const char *name)=0;
virtual void obs_frontend_add_tools_menu_item(const char *name,
obs_frontend_cb callback, void *private_data)=0;
@@ -50,6 +54,7 @@ struct obs_frontend_callbacks {
virtual obs_output_t *obs_frontend_get_streaming_output(void)=0;
virtual obs_output_t *obs_frontend_get_recording_output(void)=0;
+ virtual obs_output_t *obs_frontend_get_replay_buffer_output(void)=0;
virtual config_t *obs_frontend_get_profile_config(void)=0;
virtual config_t *obs_frontend_get_global_config(void)=0;
diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp
index c9e4b6cfa..525d0a686 100644
--- a/UI/window-basic-main-outputs.cpp
+++ b/UI/window-basic-main-outputs.cpp
@@ -84,6 +84,36 @@ static void OBSRecordStopping(void *data, calldata_t *params)
UNUSED_PARAMETER(params);
}
+static void OBSStartReplayBuffer(void *data, calldata_t *params)
+{
+ BasicOutputHandler *output = static_cast(data);
+
+ output->replayBufferActive = true;
+ QMetaObject::invokeMethod(output->main, "ReplayBufferStart");
+
+ UNUSED_PARAMETER(params);
+}
+
+static void OBSStopReplayBuffer(void *data, calldata_t *params)
+{
+ BasicOutputHandler *output = static_cast(data);
+ int code = (int)calldata_int(params, "code");
+
+ output->replayBufferActive = false;
+ QMetaObject::invokeMethod(output->main,
+ "ReplayBufferStop", Q_ARG(int, code));
+
+ UNUSED_PARAMETER(params);
+}
+
+static void OBSReplayBufferStopping(void *data, calldata_t *params)
+{
+ BasicOutputHandler *output = static_cast(data);
+ QMetaObject::invokeMethod(output->main, "ReplayBufferStopping");
+
+ UNUSED_PARAMETER(params);
+}
+
static void FindBestFilename(string &strPath, bool noSpace)
{
int num = 2;
@@ -154,6 +184,7 @@ struct SimpleOutput : BasicOutputHandler {
string videoEncoder;
string videoQuality;
bool usingRecordingPreset = false;
+ bool recordingConfigured = false;
bool ffmpegOutput = false;
bool lowCPUx264 = false;
@@ -179,12 +210,18 @@ struct SimpleOutput : BasicOutputHandler {
void LoadStreamingPreset_h264(const char *encoder);
+ void UpdateRecording();
+ bool ConfigureRecording(bool useReplayBuffer);
+
virtual bool StartStreaming(obs_service_t *service) override;
virtual bool StartRecording() override;
+ virtual bool StartReplayBuffer() override;
virtual void StopStreaming(bool force) override;
virtual void StopRecording(bool force) override;
+ virtual void StopReplayBuffer(bool force) override;
virtual bool StreamingActive() const override;
virtual bool RecordingActive() const override;
+ virtual bool ReplayBufferActive() const override;
};
void SimpleOutput::LoadRecordingPreset_Lossless()
@@ -306,21 +343,34 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_)
LoadRecordingPreset();
if (!ffmpegOutput) {
- replayBuffer = config_get_bool(main->Config(),
+ bool useReplayBuffer = config_get_bool(main->Config(),
"SimpleOutput", "RecRB");
- if (replayBuffer) {
+ if (useReplayBuffer) {
const char *str = config_get_string(main->Config(),
"Hotkeys", "ReplayBuffer");
obs_data_t *hotkey = obs_data_create_from_json(str);
- fileOutput = obs_output_create("replay_buffer",
+ replayBuffer = obs_output_create("replay_buffer",
Str("ReplayBuffer"), nullptr, hotkey);
obs_data_release(hotkey);
- } else {
- fileOutput = obs_output_create("ffmpeg_muxer",
- "simple_file_output", nullptr, nullptr);
+ if (!replayBuffer)
+ throw "Failed to create replay buffer output "
+ "(simple output)";
+ obs_output_release(replayBuffer);
+
+ signal_handler_t *signal =
+ obs_output_get_signal_handler(replayBuffer);
+
+ startReplayBuffer.Connect(signal, "start",
+ OBSStartReplayBuffer, this);
+ stopReplayBuffer.Connect(signal, "stop",
+ OBSStopReplayBuffer, this);
+ replayBufferStopping.Connect(signal, "stopping",
+ OBSReplayBufferStopping, this);
}
+ fileOutput = obs_output_create("ffmpeg_muxer",
+ "simple_file_output", nullptr, nullptr);
if (!fileOutput)
throw "Failed to create recording output "
"(simple output)";
@@ -649,8 +699,11 @@ static void ensure_directory_exists(string &path)
os_mkdirs(directory.c_str());
}
-bool SimpleOutput::StartRecording()
+void SimpleOutput::UpdateRecording()
{
+ if (replayBufferActive || recordingActive)
+ return;
+
if (usingRecordingPreset) {
if (!ffmpegOutput)
UpdateRecordingSettings();
@@ -661,6 +714,20 @@ bool SimpleOutput::StartRecording()
if (!Active())
SetupOutputs();
+ if (!ffmpegOutput) {
+ obs_output_set_video_encoder(fileOutput, h264Recording);
+ obs_output_set_audio_encoder(fileOutput, aacRecording, 0);
+ }
+ if (replayBuffer) {
+ obs_output_set_video_encoder(replayBuffer, h264Recording);
+ obs_output_set_audio_encoder(replayBuffer, aacRecording, 0);
+ }
+
+ recordingConfigured = true;
+}
+
+bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
+{
const char *path = config_get_string(main->Config(),
"SimpleOutput", "FilePath");
const char *format = config_get_string(main->Config(),
@@ -706,13 +773,8 @@ bool SimpleOutput::StartRecording()
if (!overwriteIfExists)
FindBestFilename(strPath, noSpace);
- if (!ffmpegOutput) {
- obs_output_set_video_encoder(fileOutput, h264Recording);
- obs_output_set_audio_encoder(fileOutput, aacRecording, 0);
- }
-
obs_data_t *settings = obs_data_create();
- if (replayBuffer) {
+ if (updateReplayBuffer) {
obs_data_set_string(settings, "directory", path);
obs_data_set_string(settings, "format", filenameFormat);
obs_data_set_string(settings, "extension", format);
@@ -723,17 +785,36 @@ bool SimpleOutput::StartRecording()
obs_data_set_string(settings, ffmpegOutput ? "url" : "path",
strPath.c_str());
}
+
obs_data_set_string(settings, "muxer_settings", mux);
- obs_output_update(fileOutput, settings);
+ if (updateReplayBuffer)
+ obs_output_update(replayBuffer, settings);
+ else
+ obs_output_update(fileOutput, settings);
obs_data_release(settings);
+ return true;
+}
- if (obs_output_start(fileOutput)) {
- return true;
- }
+bool SimpleOutput::StartRecording()
+{
+ UpdateRecording();
+ if (!ConfigureRecording(false))
+ return false;
+ if (!obs_output_start(fileOutput))
+ return false;
+ return true;
+}
- return false;
+bool SimpleOutput::StartReplayBuffer()
+{
+ UpdateRecording();
+ if (!ConfigureRecording(true))
+ return false;
+ if (!obs_output_start(replayBuffer))
+ return false;
+ return true;
}
void SimpleOutput::StopStreaming(bool force)
@@ -752,6 +833,14 @@ void SimpleOutput::StopRecording(bool force)
obs_output_stop(fileOutput);
}
+void SimpleOutput::StopReplayBuffer(bool force)
+{
+ if (force)
+ obs_output_force_stop(replayBuffer);
+ else
+ obs_output_stop(replayBuffer);
+}
+
bool SimpleOutput::StreamingActive() const
{
return obs_output_active(streamOutput);
@@ -762,6 +851,11 @@ bool SimpleOutput::RecordingActive() const
return obs_output_active(fileOutput);
}
+bool SimpleOutput::ReplayBufferActive() const
+{
+ return obs_output_active(replayBuffer);
+}
+
/* ------------------------------------------------------------------------ */
struct AdvancedOutput : BasicOutputHandler {
diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp
index e9d442284..ec814a782 100644
--- a/UI/window-basic-main-outputs.hpp
+++ b/UI/window-basic-main-outputs.hpp
@@ -5,19 +5,23 @@ class OBSBasic;
struct BasicOutputHandler {
OBSOutput fileOutput;
OBSOutput streamOutput;
+ OBSOutput replayBuffer;
bool streamingActive = false;
bool recordingActive = false;
bool delayActive = false;
- bool replayBuffer = false;
+ bool replayBufferActive = false;
OBSBasic *main;
OBSSignal startRecording;
OBSSignal stopRecording;
+ OBSSignal startReplayBuffer;
+ OBSSignal stopReplayBuffer;
OBSSignal startStreaming;
OBSSignal stopStreaming;
OBSSignal streamDelayStarting;
OBSSignal streamStopping;
OBSSignal recordStopping;
+ OBSSignal replayBufferStopping;
inline BasicOutputHandler(OBSBasic *main_) : main(main_) {}
@@ -25,16 +29,20 @@ struct BasicOutputHandler {
virtual bool StartStreaming(obs_service_t *service) = 0;
virtual bool StartRecording() = 0;
+ virtual bool StartReplayBuffer() {return false;}
virtual void StopStreaming(bool force = false) = 0;
virtual void StopRecording(bool force = false) = 0;
+ virtual void StopReplayBuffer(bool force = false) {(void)force;}
virtual bool StreamingActive() const = 0;
virtual bool RecordingActive() const = 0;
+ virtual bool ReplayBufferActive() const {return false;}
virtual void Update() = 0;
inline bool Active() const
{
- return streamingActive || recordingActive || delayActive;
+ return streamingActive || recordingActive || delayActive ||
+ replayBufferActive;
}
};
diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
index 4aa21cfe5..72918ddc0 100644
--- a/UI/window-basic-main.cpp
+++ b/UI/window-basic-main.cpp
@@ -1032,6 +1032,14 @@ void OBSBasic::InitPrimitives()
obs_leave_graphics();
}
+void OBSBasic::ReplayBufferClicked()
+{
+ if (outputHandler->ReplayBufferActive())
+ StopReplayBuffer();
+ else
+ StartReplayBuffer();
+};
+
void OBSBasic::ResetOutputs()
{
ProfileScope("OBSBasic::ResetOutputs");
@@ -1045,15 +1053,23 @@ void OBSBasic::ResetOutputs()
CreateAdvancedOutputHandler(this) :
CreateSimpleOutputHandler(this));
- if (outputHandler->replayBuffer)
- ui->recordButton->setText(
- QTStr("Basic.Main.StartReplayBuffer"));
- else
- ui->recordButton->setText(
- QTStr("Basic.Main.StartRecording"));
+ delete replayBufferButton;
- if (sysTrayRecord)
- sysTrayRecord->setText(ui->recordButton->text());
+ if (outputHandler->replayBuffer) {
+ replayBufferButton = new QPushButton(
+ QTStr("Basic.Main.StartReplayBuffer"),
+ this);
+ connect(replayBufferButton,
+ &QPushButton::clicked,
+ this,
+ &OBSBasic::ReplayBufferClicked);
+
+ ui->buttonsVLayout->insertWidget(2, replayBufferButton);
+ }
+
+ if (sysTrayReplayBuffer)
+ sysTrayReplayBuffer->setEnabled(
+ !!outputHandler->replayBuffer);
} else {
outputHandler->Update();
}
@@ -1357,9 +1373,9 @@ void OBSBasic::CreateHotkeys()
streamingHotkeys = obs_hotkey_pair_register_frontend(
"OBSBasic.StartStreaming",
- Str("Basic.Hotkeys.StartStreaming"),
+ Str("Basic.Main.StartStreaming"),
"OBSBasic.StopStreaming",
- Str("Basic.Hotkeys.StopStreaming"),
+ Str("Basic.Main.StopStreaming"),
MAKE_CALLBACK(!basic.outputHandler->StreamingActive(),
basic.StartStreaming),
MAKE_CALLBACK(basic.outputHandler->StreamingActive(),
@@ -1385,9 +1401,9 @@ void OBSBasic::CreateHotkeys()
recordingHotkeys = obs_hotkey_pair_register_frontend(
"OBSBasic.StartRecording",
- Str("Basic.Hotkeys.StartRecording"),
+ Str("Basic.Main.StartRecording"),
"OBSBasic.StopRecording",
- Str("Basic.Hotkeys.StopRecording"),
+ Str("Basic.Main.StopRecording"),
MAKE_CALLBACK(!basic.outputHandler->RecordingActive(),
basic.StartRecording),
MAKE_CALLBACK(basic.outputHandler->RecordingActive(),
@@ -1395,6 +1411,19 @@ void OBSBasic::CreateHotkeys()
this, this);
LoadHotkeyPair(recordingHotkeys,
"OBSBasic.StartRecording", "OBSBasic.StopRecording");
+
+ replayBufHotkeys = obs_hotkey_pair_register_frontend(
+ "OBSBasic.StartReplayBuffer",
+ Str("Basic.Main.StartReplayBuffer"),
+ "OBSBasic.StopReplayBuffer",
+ Str("Basic.Main.StopReplayBuffer"),
+ MAKE_CALLBACK(!basic.outputHandler->ReplayBufferActive(),
+ basic.StartReplayBuffer),
+ MAKE_CALLBACK(basic.outputHandler->ReplayBufferActive(),
+ basic.StopReplayBuffer),
+ this, this);
+ LoadHotkeyPair(replayBufHotkeys,
+ "OBSBasic.StartReplayBuffer", "OBSBasic.StopReplayBuffer");
#undef MAKE_CALLBACK
auto togglePreviewProgram = [] (void *data, obs_hotkey_id,
@@ -1431,6 +1460,7 @@ void OBSBasic::ClearHotkeys()
{
obs_hotkey_pair_unregister(streamingHotkeys);
obs_hotkey_pair_unregister(recordingHotkeys);
+ obs_hotkey_pair_unregister(replayBufHotkeys);
obs_hotkey_unregister(forceStreamingStopHotkey);
obs_hotkey_unregister(togglePreviewProgramHotkey);
obs_hotkey_unregister(transitionHotkey);
@@ -3660,6 +3690,10 @@ void OBSBasic::OpenSceneFilters()
"==== Recording Start ==============================================="
#define RECORDING_STOP \
"==== Recording Stop ================================================"
+#define REPLAY_BUFFER_START \
+ "==== Replay Buffer Start ==========================================="
+#define REPLAY_BUFFER_STOP \
+ "==== Replay Buffer Stop ============================================"
#define STREAMING_START \
"==== Streaming Start ==============================================="
#define STREAMING_STOP \
@@ -3918,31 +3952,11 @@ void OBSBasic::StreamingStop(int code)
}
}
-#define RP_NO_HOTKEY_TITLE QTStr("Output.ReplayBuffer.NoHotkey.Title")
-#define RP_NO_HOTKEY_TEXT QTStr("Output.ReplayBuffer.NoHotkey.Msg")
-
void OBSBasic::StartRecording()
{
if (outputHandler->RecordingActive())
return;
- if (outputHandler->replayBuffer) {
- obs_output_t *output = outputHandler->fileOutput;
- obs_data_t *hotkeys = obs_hotkeys_save_output(output);
- obs_data_array_t *bindings = obs_data_get_array(hotkeys,
- "ReplayBuffer.Save");
- size_t count = obs_data_array_count(bindings);
- obs_data_array_release(bindings);
- obs_data_release(hotkeys);
-
- if (!count) {
- QMessageBox::information(this,
- RP_NO_HOTKEY_TITLE,
- RP_NO_HOTKEY_TEXT);
- return;
- }
- }
-
if (api)
api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING);
@@ -3952,10 +3966,7 @@ void OBSBasic::StartRecording()
void OBSBasic::RecordStopping()
{
- if (outputHandler->replayBuffer)
- ui->recordButton->setText(QTStr("Basic.Main.StoppingReplayBuffer"));
- else
- ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording"));
+ ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording"));
if (sysTrayRecord)
sysTrayRecord->setText(ui->recordButton->text());
@@ -3978,11 +3989,7 @@ void OBSBasic::StopRecording()
void OBSBasic::RecordingStart()
{
ui->statusbar->RecordingStarted(outputHandler->fileOutput);
-
- if (outputHandler->replayBuffer)
- ui->recordButton->setText(QTStr("Basic.Main.StopReplayBuffer"));
- else
- ui->recordButton->setText(QTStr("Basic.Main.StopRecording"));
+ ui->recordButton->setText(QTStr("Basic.Main.StopRecording"));
if (sysTrayRecord)
sysTrayRecord->setText(ui->recordButton->text());
@@ -3999,14 +4006,11 @@ void OBSBasic::RecordingStart()
void OBSBasic::RecordingStop(int code)
{
ui->statusbar->RecordingStopped();
-
- if (outputHandler->replayBuffer)
- ui->recordButton->setText(QTStr("Basic.Main.StartReplayBuffer"));
- else
- ui->recordButton->setText(QTStr("Basic.Main.StartRecording"));
+ ui->recordButton->setText(QTStr("Basic.Main.StartRecording"));
if (sysTrayRecord)
sysTrayRecord->setText(ui->recordButton->text());
+
blog(LOG_INFO, RECORDING_STOP);
if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) {
@@ -4043,6 +4047,131 @@ void OBSBasic::RecordingStop(int code)
OnDeactivate();
}
+#define RP_NO_HOTKEY_TITLE QTStr("Output.ReplayBuffer.NoHotkey.Title")
+#define RP_NO_HOTKEY_TEXT QTStr("Output.ReplayBuffer.NoHotkey.Msg")
+
+void OBSBasic::StartReplayBuffer()
+{
+ if (!outputHandler || !outputHandler->replayBuffer)
+ return;
+ if (outputHandler->ReplayBufferActive())
+ return;
+
+ obs_output_t *output = outputHandler->replayBuffer;
+ obs_data_t *hotkeys = obs_hotkeys_save_output(output);
+ obs_data_array_t *bindings = obs_data_get_array(hotkeys,
+ "ReplayBuffer.Save");
+ size_t count = obs_data_array_count(bindings);
+ obs_data_array_release(bindings);
+ obs_data_release(hotkeys);
+
+ if (!count) {
+ QMessageBox::information(this,
+ RP_NO_HOTKEY_TITLE,
+ RP_NO_HOTKEY_TEXT);
+ return;
+ }
+
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING);
+
+ SaveProject();
+ outputHandler->StartReplayBuffer();
+}
+
+void OBSBasic::ReplayBufferStopping()
+{
+ if (!outputHandler || !outputHandler->replayBuffer)
+ return;
+
+ replayBufferButton->setText(QTStr("Basic.Main.StoppingReplayBuffer"));
+
+ if (sysTrayReplayBuffer)
+ sysTrayReplayBuffer->setText(replayBufferButton->text());
+
+ replayBufferStopping = true;
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING);
+}
+
+void OBSBasic::StopReplayBuffer()
+{
+ if (!outputHandler || !outputHandler->replayBuffer)
+ return;
+
+ SaveProject();
+
+ if (outputHandler->ReplayBufferActive())
+ outputHandler->StopReplayBuffer(replayBufferStopping);
+
+ OnDeactivate();
+}
+
+void OBSBasic::ReplayBufferStart()
+{
+ if (!outputHandler || !outputHandler->replayBuffer)
+ return;
+
+ replayBufferButton->setText(QTStr("Basic.Main.StopReplayBuffer"));
+
+ if (sysTrayReplayBuffer)
+ sysTrayReplayBuffer->setText(replayBufferButton->text());
+
+ replayBufferStopping = false;
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED);
+
+ OnActivate();
+
+ blog(LOG_INFO, REPLAY_BUFFER_START);
+}
+
+void OBSBasic::ReplayBufferStop(int code)
+{
+ if (!outputHandler || !outputHandler->replayBuffer)
+ return;
+
+ replayBufferButton->setText(QTStr("Basic.Main.StartReplayBuffer"));
+
+ if (sysTrayReplayBuffer)
+ sysTrayReplayBuffer->setText(replayBufferButton->text());
+
+ blog(LOG_INFO, REPLAY_BUFFER_STOP);
+
+ if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) {
+ QMessageBox::information(this,
+ QTStr("Output.RecordFail.Title"),
+ QTStr("Output.RecordFail.Unsupported"));
+
+ } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) {
+ QMessageBox::information(this,
+ QTStr("Output.RecordNoSpace.Title"),
+ QTStr("Output.RecordNoSpace.Msg"));
+
+ } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) {
+ QMessageBox::information(this,
+ QTStr("Output.RecordError.Title"),
+ QTStr("Output.RecordError.Msg"));
+
+ } else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) {
+ SysTrayNotify(QTStr("Output.RecordFail.Unsupported"),
+ QSystemTrayIcon::Warning);
+
+ } else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) {
+ SysTrayNotify(QTStr("Output.RecordNoSpace.Msg"),
+ QSystemTrayIcon::Warning);
+
+ } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {
+ SysTrayNotify(QTStr("Output.RecordError.Msg"),
+ QSystemTrayIcon::Warning);
+ }
+
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED);
+
+ OnDeactivate();
+}
+
void OBSBasic::on_streamButton_clicked()
{
if (outputHandler->StreamingActive()) {
@@ -4737,10 +4866,13 @@ void OBSBasic::SystemTrayInit()
trayIcon);
sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"),
trayIcon);
+ sysTrayReplayBuffer = new QAction(QTStr("Basic.Main.StartReplayBuffer"),
+ trayIcon);
exit = new QAction(QTStr("Exit"),
trayIcon);
- sysTrayRecord->setText(ui->recordButton->text());
+ if (outputHandler && !outputHandler->replayBuffer)
+ sysTrayReplayBuffer->setEnabled(false);
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,
@@ -4751,6 +4883,8 @@ void OBSBasic::SystemTrayInit()
this, SLOT(on_streamButton_clicked()));
connect(sysTrayRecord, SIGNAL(triggered()),
this, SLOT(on_recordButton_clicked()));
+ connect(sysTrayReplayBuffer, &QAction::triggered,
+ this, &OBSBasic::ReplayBufferClicked);
connect(exit, SIGNAL(triggered()),
this, SLOT(close()));
@@ -4758,6 +4892,7 @@ void OBSBasic::SystemTrayInit()
trayMenu->addAction(showHide);
trayMenu->addAction(sysTrayStream);
trayMenu->addAction(sysTrayRecord);
+ trayMenu->addAction(sysTrayReplayBuffer);
trayMenu->addAction(exit);
trayIcon->setContextMenu(trayMenu);
}
diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp
index 3de906518..8cc5ef20a 100644
--- a/UI/window-basic-main.hpp
+++ b/UI/window-basic-main.hpp
@@ -131,6 +131,7 @@ private:
std::unique_ptr outputHandler;
bool streamingStopping = false;
bool recordingStopping = false;
+ bool replayBufferStopping = false;
gs_vertbuffer_t *box = nullptr;
gs_vertbuffer_t *boxLeft = nullptr;
@@ -152,9 +153,12 @@ private:
QPointer startStreamMenu;
+ QPointer replayBufferButton;
+
QPointer trayIcon;
QPointer sysTrayStream;
QPointer sysTrayRecord;
+ QPointer sysTrayReplayBuffer;
QPointer showHide;
QPointer exit;
QPointer trayMenu;
@@ -245,7 +249,8 @@ private:
QListWidgetItem *GetTopSelectedSourceItem();
- obs_hotkey_pair_id streamingHotkeys, recordingHotkeys;
+ obs_hotkey_pair_id streamingHotkeys, recordingHotkeys,
+ replayBufHotkeys;
obs_hotkey_id forceStreamingStopHotkey;
void InitDefaultTransitions();
@@ -314,6 +319,8 @@ private:
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
+ void ReplayBufferClicked();
+
public slots:
void StartStreaming();
void StopStreaming();
@@ -333,6 +340,13 @@ public slots:
void RecordStopping();
void RecordingStop(int code);
+ void StartReplayBuffer();
+ void StopReplayBuffer();
+
+ void ReplayBufferStart();
+ void ReplayBufferStopping();
+ void ReplayBufferStop(int code);
+
void SaveProjectDeferred();
void SaveProject();
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index 0c45c65aa..f4b741928 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -2663,10 +2663,13 @@ void OBSBasicSettings::SaveHotkeySettings()
obs_data_array_release(array);
}
- const char *id = obs_obj_get_id(main->outputHandler->fileOutput);
+ if (!main->outputHandler || !main->outputHandler->replayBuffer)
+ return;
+
+ const char *id = obs_obj_get_id(main->outputHandler->replayBuffer);
if (strcmp(id, "replay_buffer") == 0) {
obs_data_t *hotkeys = obs_hotkeys_save_output(
- main->outputHandler->fileOutput);
+ main->outputHandler->replayBuffer);
config_set_string(config, "Hotkeys", "ReplayBuffer",
obs_data_get_json(hotkeys));
obs_data_release(hotkeys);