UI: Enable Replay Buffer in Advanced Mode
Replay buffer is currently only supported in simple mode. This change adds support to advanced mode and is, for the most part, a mirror of the addition of replay buffer to simple mode. Mantis-bug: https://obsproject.com/mantis/view.php?id=792 Closes jp9000/obs-studio#1019
This commit is contained in:
parent
48cc90d5bb
commit
9cba5660e4
5 changed files with 2491 additions and 2046 deletions
|
|
@ -991,6 +991,7 @@ struct AdvancedOutput : BasicOutputHandler {
|
|||
bool ffmpegOutput;
|
||||
bool ffmpegRecording;
|
||||
bool useStreamEncoder;
|
||||
bool usesBitrate = false;
|
||||
|
||||
string aacEncoderID[MAX_AUDIO_MIXES];
|
||||
|
||||
|
|
@ -1009,10 +1010,13 @@ struct AdvancedOutput : BasicOutputHandler {
|
|||
|
||||
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;
|
||||
};
|
||||
|
||||
static OBSData GetDataFromJsonFile(const char *jsonFile)
|
||||
|
|
@ -1050,6 +1054,15 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
|
|||
OBSData streamEncSettings = GetDataFromJsonFile("streamEncoder.json");
|
||||
OBSData recordEncSettings = GetDataFromJsonFile("recordEncoder.json");
|
||||
|
||||
const char *rate_control = obs_data_get_string(
|
||||
useStreamEncoder ? streamEncSettings : recordEncSettings,
|
||||
"rate_control");
|
||||
if (!rate_control)
|
||||
rate_control = "";
|
||||
usesBitrate = astrcmpi(rate_control, "CBR") == 0 ||
|
||||
astrcmpi(rate_control, "VBR") == 0 ||
|
||||
astrcmpi(rate_control, "ABR") == 0;
|
||||
|
||||
if (ffmpegOutput) {
|
||||
fileOutput = obs_output_create("ffmpeg_output",
|
||||
"adv_ffmpeg_output", nullptr, nullptr);
|
||||
|
|
@ -1058,6 +1071,33 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
|
|||
"(advanced output)";
|
||||
obs_output_release(fileOutput);
|
||||
} else {
|
||||
bool useReplayBuffer = config_get_bool(main->Config(),
|
||||
"AdvOut", "RecRB");
|
||||
if (useReplayBuffer) {
|
||||
const char *str = config_get_string(main->Config(),
|
||||
"Hotkeys", "ReplayBuffer");
|
||||
obs_data_t *hotkey = obs_data_create_from_json(str);
|
||||
replayBuffer = obs_output_create("replay_buffer",
|
||||
Str("ReplayBuffer"), nullptr, hotkey);
|
||||
|
||||
obs_data_release(hotkey);
|
||||
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",
|
||||
"adv_file_output", nullptr, nullptr);
|
||||
if (!fileOutput)
|
||||
|
|
@ -1174,6 +1214,9 @@ inline void AdvancedOutput::SetupRecording()
|
|||
|
||||
if (useStreamEncoder) {
|
||||
obs_output_set_video_encoder(fileOutput, h264Streaming);
|
||||
if (replayBuffer)
|
||||
obs_output_set_video_encoder(replayBuffer,
|
||||
h264Streaming);
|
||||
} else {
|
||||
if (rescale && rescaleRes && *rescaleRes) {
|
||||
if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
|
||||
|
|
@ -1185,18 +1228,27 @@ inline void AdvancedOutput::SetupRecording()
|
|||
obs_encoder_set_scaled_size(h264Recording, cx, cy);
|
||||
obs_encoder_set_video(h264Recording, obs_get_video());
|
||||
obs_output_set_video_encoder(fileOutput, h264Recording);
|
||||
if (replayBuffer)
|
||||
obs_output_set_video_encoder(replayBuffer,
|
||||
h264Recording);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_AUDIO_MIXES; i++) {
|
||||
if ((tracks & (1<<i)) != 0) {
|
||||
obs_output_set_audio_encoder(fileOutput, aacTrack[i],
|
||||
idx++);
|
||||
idx);
|
||||
if (replayBuffer)
|
||||
obs_output_set_audio_encoder(replayBuffer,
|
||||
aacTrack[i], idx);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
obs_data_set_string(settings, "path", path);
|
||||
obs_data_set_string(settings, "muxer_settings", mux);
|
||||
obs_output_update(fileOutput, settings);
|
||||
if (replayBuffer)
|
||||
obs_output_update(replayBuffer, settings);
|
||||
obs_data_release(settings);
|
||||
}
|
||||
|
||||
|
|
@ -1549,6 +1601,121 @@ bool AdvancedOutput::StartRecording()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AdvancedOutput::StartReplayBuffer()
|
||||
{
|
||||
const char *path;
|
||||
const char *recFormat;
|
||||
const char *filenameFormat;
|
||||
bool noSpace = false;
|
||||
bool overwriteIfExists = false;
|
||||
const char *rbPrefix;
|
||||
const char *rbSuffix;
|
||||
int rbTime;
|
||||
int rbSize;
|
||||
|
||||
if (!useStreamEncoder) {
|
||||
if (!ffmpegOutput)
|
||||
UpdateRecordingSettings();
|
||||
} else if (!obs_output_active(streamOutput)) {
|
||||
UpdateStreamSettings();
|
||||
}
|
||||
|
||||
UpdateAudioSettings();
|
||||
|
||||
if (!Active())
|
||||
SetupOutputs();
|
||||
|
||||
if (!ffmpegOutput || ffmpegRecording) {
|
||||
path = config_get_string(main->Config(), "AdvOut",
|
||||
ffmpegRecording ? "FFFilePath" : "RecFilePath");
|
||||
recFormat = config_get_string(main->Config(), "AdvOut",
|
||||
ffmpegRecording ? "FFExtension" : "RecFormat");
|
||||
filenameFormat = config_get_string(main->Config(), "Output",
|
||||
"FilenameFormatting");
|
||||
overwriteIfExists = config_get_bool(main->Config(), "Output",
|
||||
"OverwriteIfExists");
|
||||
noSpace = config_get_bool(main->Config(), "AdvOut",
|
||||
ffmpegRecording ?
|
||||
"FFFileNameWithoutSpace" :
|
||||
"RecFileNameWithoutSpace");
|
||||
rbPrefix = config_get_string(main->Config(), "SimpleOutput",
|
||||
"RecRBPrefix");
|
||||
rbSuffix = config_get_string(main->Config(), "SimpleOutput",
|
||||
"RecRBSuffix");
|
||||
rbTime = config_get_int(main->Config(), "AdvOut",
|
||||
"RecRBTime");
|
||||
rbSize = config_get_int(main->Config(), "AdvOut",
|
||||
"RecRBSize");
|
||||
|
||||
os_dir_t *dir = path && path[0] ? os_opendir(path) : nullptr;
|
||||
|
||||
if (!dir) {
|
||||
if (main->isVisible())
|
||||
OBSMessageBox::information(main,
|
||||
QTStr("Output.BadPath.Title"),
|
||||
QTStr("Output.BadPath.Text"));
|
||||
else
|
||||
main->SysTrayNotify(QTStr("Output.BadPath.Text"),
|
||||
QSystemTrayIcon::Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
os_closedir(dir);
|
||||
|
||||
string strPath;
|
||||
strPath += path;
|
||||
|
||||
char lastChar = strPath.back();
|
||||
if (lastChar != '/' && lastChar != '\\')
|
||||
strPath += "/";
|
||||
|
||||
strPath += GenerateSpecifiedFilename(recFormat, noSpace,
|
||||
filenameFormat);
|
||||
ensure_directory_exists(strPath);
|
||||
if (!overwriteIfExists)
|
||||
FindBestFilename(strPath, noSpace);
|
||||
|
||||
obs_data_t *settings = obs_data_create();
|
||||
string f;
|
||||
|
||||
if (rbPrefix && *rbPrefix) {
|
||||
f += rbPrefix;
|
||||
if (f.back() != ' ')
|
||||
f += " ";
|
||||
}
|
||||
|
||||
f += filenameFormat;
|
||||
|
||||
if (rbSuffix && *rbSuffix) {
|
||||
if (*rbSuffix != ' ')
|
||||
f += " ";
|
||||
f += rbSuffix;
|
||||
}
|
||||
|
||||
remove_reserved_file_characters(f);
|
||||
|
||||
obs_data_set_string(settings, "directory", path);
|
||||
obs_data_set_string(settings, "format", f.c_str());
|
||||
obs_data_set_string(settings, "extension", recFormat);
|
||||
obs_data_set_int(settings, "max_time_sec", rbTime);
|
||||
obs_data_set_int(settings, "max_size_mb",
|
||||
usesBitrate ? 0 : rbSize);
|
||||
|
||||
obs_output_update(replayBuffer, settings);
|
||||
|
||||
obs_data_release(settings);
|
||||
}
|
||||
|
||||
if (!obs_output_start(replayBuffer)) {
|
||||
QMessageBox::critical(main,
|
||||
QTStr("Output.StartRecordingFailed"),
|
||||
QTStr("Output.StartFailedGeneric"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AdvancedOutput::StopStreaming(bool force)
|
||||
{
|
||||
if (force)
|
||||
|
|
@ -1565,6 +1732,14 @@ void AdvancedOutput::StopRecording(bool force)
|
|||
obs_output_stop(fileOutput);
|
||||
}
|
||||
|
||||
void AdvancedOutput::StopReplayBuffer(bool force)
|
||||
{
|
||||
if (force)
|
||||
obs_output_force_stop(replayBuffer);
|
||||
else
|
||||
obs_output_stop(replayBuffer);
|
||||
}
|
||||
|
||||
bool AdvancedOutput::StreamingActive() const
|
||||
{
|
||||
return obs_output_active(streamOutput);
|
||||
|
|
@ -1575,6 +1750,11 @@ bool AdvancedOutput::RecordingActive() const
|
|||
return obs_output_active(fileOutput);
|
||||
}
|
||||
|
||||
bool AdvancedOutput::ReplayBufferActive() const
|
||||
{
|
||||
return obs_output_active(replayBuffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue