From 07007bcc6edd9604ef1924ff81953794340cea42 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 6 Feb 2015 03:17:33 -0800 Subject: [PATCH] UI: Refactor output handling To accommodate multiple types of outputs, there has to be some level of abstraction. The BasicOutputHandler structure will give us a way that we can switch between different output configurations. --- obs/CMakeLists.txt | 2 + obs/window-basic-main-outputs.cpp | 251 ++++++++++++++++++++++++++++++ obs/window-basic-main-outputs.hpp | 27 ++++ obs/window-basic-main.cpp | 212 +++---------------------- obs/window-basic-main.hpp | 13 +- 5 files changed, 310 insertions(+), 195 deletions(-) create mode 100644 obs/window-basic-main-outputs.cpp create mode 100644 obs/window-basic-main-outputs.hpp diff --git a/obs/CMakeLists.txt b/obs/CMakeLists.txt index 74e8e997b..f1a7e79d4 100644 --- a/obs/CMakeLists.txt +++ b/obs/CMakeLists.txt @@ -93,6 +93,7 @@ set(obs_SOURCES window-basic-settings.cpp window-basic-interaction.cpp window-basic-properties.cpp + window-basic-main-outputs.cpp window-basic-source-select.cpp window-license-agreement.cpp window-basic-status-bar.cpp @@ -118,6 +119,7 @@ set(obs_HEADERS window-basic-settings.hpp window-basic-interaction.hpp window-basic-properties.hpp + window-basic-main-outputs.hpp window-basic-source-select.hpp window-license-agreement.hpp window-basic-status-bar.hpp diff --git a/obs/window-basic-main-outputs.cpp b/obs/window-basic-main-outputs.cpp new file mode 100644 index 000000000..b332c2192 --- /dev/null +++ b/obs/window-basic-main-outputs.cpp @@ -0,0 +1,251 @@ +#include +#include +#include "window-basic-main.hpp" +#include "window-basic-main-outputs.hpp" + +using namespace std; + +static void OBSStartStreaming(void *data, calldata_t *params) +{ + BasicOutputHandler *output = static_cast(data); + QMetaObject::invokeMethod(output->main, "StreamingStart"); + + UNUSED_PARAMETER(params); +} + +static void OBSStopStreaming(void *data, calldata_t *params) +{ + BasicOutputHandler *output = static_cast(data); + int code = (int)calldata_int(params, "code"); + + QMetaObject::invokeMethod(output->main, + "StreamingStop", Q_ARG(int, code)); + output->activeRefs--; +} + +static void OBSStartRecording(void *data, calldata_t *params) +{ + BasicOutputHandler *output = static_cast(data); + + QMetaObject::invokeMethod(output->main, "RecordingStart"); + + UNUSED_PARAMETER(params); +} + +static void OBSStopRecording(void *data, calldata_t *params) +{ + BasicOutputHandler *output = static_cast(data); + + QMetaObject::invokeMethod(output->main, "RecordingStop"); + output->activeRefs--; + + UNUSED_PARAMETER(params); +} + +/* ------------------------------------------------------------------------ */ + +struct SimpleOutput : BasicOutputHandler { + OBSEncoder aac; + OBSEncoder h264; + + SimpleOutput(OBSBasic *main_); + + virtual void Update() override; + + void SetupOutputs(); + + virtual bool StartStreaming(obs_service_t *service) override; + virtual bool StartRecording() override; + virtual void StopStreaming() override; + virtual void StopRecording() override; + virtual bool StreamingActive() const override; + virtual bool RecordingActive() const override; +}; + +SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) +{ + streamOutput = obs_output_create("rtmp_output", "simple_stream", + nullptr); + if (!streamOutput) + throw "Failed to create stream output (simple output)"; + + fileOutput = obs_output_create("flv_output", "simple_file_output", + nullptr); + if (!fileOutput) + throw "Failed to create recording output (simple output)"; + + h264 = obs_video_encoder_create("obs_x264", "simple_h264", nullptr); + if (!h264) + throw "Failed to create h264 encoder (simple output)"; + + aac = obs_audio_encoder_create("libfdk_aac", "simple_aac", nullptr, 0); + if (!aac) + aac = obs_audio_encoder_create("ffmpeg_aac", "simple_aac", + nullptr, 0); + if (!aac) + throw "Failed to create audio encoder (simple output)"; + + signal_handler_connect(obs_output_get_signal_handler(streamOutput), + "start", OBSStartStreaming, this); + signal_handler_connect(obs_output_get_signal_handler(streamOutput), + "stop", OBSStopStreaming, this); + + signal_handler_connect(obs_output_get_signal_handler(fileOutput), + "start", OBSStartRecording, this); + signal_handler_connect(obs_output_get_signal_handler(fileOutput), + "stop", OBSStopRecording, this); +} + +void SimpleOutput::Update() +{ + obs_data_t *h264Settings = obs_data_create(); + obs_data_t *aacSettings = obs_data_create(); + + int videoBitrate = config_get_uint(main->Config(), "SimpleOutput", + "VBitrate"); + int videoBufsize = config_get_uint(main->Config(), "SimpleOutput", + "Bufsize"); + int audioBitrate = config_get_uint(main->Config(), "SimpleOutput", + "ABitrate"); + bool advanced = config_get_bool(main->Config(), "SimpleOutput", + "UseAdvanced"); + bool useCBR = config_get_bool(main->Config(), "SimpleOutput", + "UseCBR"); + bool useBufsize = config_get_bool(main->Config(), "SimpleOutput", + "UseBufsize"); + const char *preset = config_get_string(main->Config(), + "SimpleOutput", "Preset"); + const char *custom = config_get_string(main->Config(), + "SimpleOutput", "x264Settings"); + + obs_data_set_int(h264Settings, "bitrate", videoBitrate); + obs_data_set_bool(h264Settings, "use_bufsize", useBufsize); + obs_data_set_int(h264Settings, "buffer_size", videoBufsize); + + if (advanced) { + obs_data_set_string(h264Settings, "preset", preset); + obs_data_set_string(h264Settings, "x264opts", custom); + obs_data_set_bool(h264Settings, "cbr", useCBR); + } else { + obs_data_set_bool(h264Settings, "cbr", true); + } + + obs_data_set_int(aacSettings, "bitrate", audioBitrate); + + obs_encoder_update(h264, h264Settings); + obs_encoder_update(aac, aacSettings); + + obs_data_release(h264Settings); + obs_data_release(aacSettings); +} + +inline void SimpleOutput::SetupOutputs() +{ + SimpleOutput::Update(); + obs_encoder_set_video(h264, obs_get_video()); + obs_encoder_set_audio(aac, obs_get_audio()); +} + +bool SimpleOutput::StartStreaming(obs_service_t *service) +{ + if (!Active()) + SetupOutputs(); + + obs_output_set_video_encoder(streamOutput, h264); + obs_output_set_audio_encoder(streamOutput, aac, 0); + obs_output_set_service(streamOutput, service); + + bool reconnect = config_get_bool(main->Config(), "SimpleOutput", + "Reconnect"); + int retryDelay = config_get_uint(main->Config(), "SimpleOutput", + "RetryDelay"); + int maxRetries = config_get_uint(main->Config(), "SimpleOutput", + "MaxRetries"); + if (!reconnect) + maxRetries = 0; + + obs_output_set_reconnect_settings(streamOutput, maxRetries, + retryDelay); + + if (obs_output_start(streamOutput)) { + activeRefs++; + return true; + } + + return false; +} + +bool SimpleOutput::StartRecording() +{ + if (!Active()) + SetupOutputs(); + + const char *path = config_get_string(main->Config(), + "SimpleOutput", "FilePath"); + + os_dir_t *dir = path ? os_opendir(path) : nullptr; + + if (!dir) { + QMessageBox::information(main, + QTStr("Output.BadPath.Title"), + QTStr("Output.BadPath.Text")); + return false; + } + + os_closedir(dir); + + string strPath; + strPath += path; + + char lastChar = strPath.back(); + if (lastChar != '/' && lastChar != '\\') + strPath += "/"; + + strPath += GenerateTimeDateFilename("flv"); + + SetupOutputs(); + + obs_output_set_video_encoder(fileOutput, h264); + obs_output_set_audio_encoder(fileOutput, aac, 0); + + obs_data_t *settings = obs_data_create(); + obs_data_set_string(settings, "path", strPath.c_str()); + + obs_output_update(fileOutput, settings); + + obs_data_release(settings); + + if (obs_output_start(fileOutput)) { + activeRefs++; + return true; + } + + return false; +} + +void SimpleOutput::StopStreaming() +{ + obs_output_stop(streamOutput); +} + +void SimpleOutput::StopRecording() +{ + obs_output_stop(fileOutput); +} + +bool SimpleOutput::StreamingActive() const +{ + return obs_output_active(streamOutput); +} + +bool SimpleOutput::RecordingActive() const +{ + return obs_output_active(fileOutput); +} + +/* ------------------------------------------------------------------------ */ + +BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main) +{ + return new SimpleOutput(main); +} diff --git a/obs/window-basic-main-outputs.hpp b/obs/window-basic-main-outputs.hpp new file mode 100644 index 000000000..1134c8b47 --- /dev/null +++ b/obs/window-basic-main-outputs.hpp @@ -0,0 +1,27 @@ +#pragma once + +class OBSBasic; + +struct BasicOutputHandler { + OBSOutput fileOutput; + OBSOutput streamOutput; + int activeRefs = 0; + OBSBasic *main; + + inline BasicOutputHandler(OBSBasic *main_) : main(main_) {} + + virtual ~BasicOutputHandler() {}; + + virtual bool StartStreaming(obs_service_t *service) = 0; + virtual bool StartRecording() = 0; + virtual void StopStreaming() = 0; + virtual void StopRecording() = 0; + virtual bool StreamingActive() const = 0; + virtual bool RecordingActive() const = 0; + + virtual void Update() = 0; + + inline bool Active() const {return !!activeRefs;} +}; + +BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main); diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 6f7ccadea..657dd43f4 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -36,6 +36,7 @@ #include "window-namedialog.hpp" #include "window-basic-source-select.hpp" #include "window-basic-main.hpp" +#include "window-basic-main-outputs.hpp" #include "window-basic-properties.hpp" #include "window-log-reply.hpp" #include "window-remux.hpp" @@ -294,36 +295,6 @@ static inline bool HasAudioDevices(const char *source_id) return count != 0; } -static void OBSStartStreaming(void *data, calldata_t *params) -{ - UNUSED_PARAMETER(params); - QMetaObject::invokeMethod(static_cast(data), - "StreamingStart"); -} - -static void OBSStopStreaming(void *data, calldata_t *params) -{ - int code = (int)calldata_int(params, "code"); - QMetaObject::invokeMethod(static_cast(data), - "StreamingStop", Q_ARG(int, code)); -} - -static void OBSStartRecording(void *data, calldata_t *params) -{ - UNUSED_PARAMETER(params); - - QMetaObject::invokeMethod(static_cast(data), - "RecordingStart"); -} - -static void OBSStopRecording(void *data, calldata_t *params) -{ - UNUSED_PARAMETER(params); - - QMetaObject::invokeMethod(static_cast(data), - "RecordingStop"); -} - #define SERVICE_PATH "obs-studio/basic/service.json" void OBSBasic::SaveService() @@ -380,49 +351,6 @@ bool OBSBasic::LoadService() return !!service; } -bool OBSBasic::InitOutputs() -{ - fileOutput = obs_output_create("flv_output", "default_file_output", - nullptr); - if (!fileOutput) - return false; - - streamOutput = obs_output_create("rtmp_output", "default_stream", - nullptr); - if (!streamOutput) - return false; - - signal_handler_connect(obs_output_get_signal_handler(streamOutput), - "start", OBSStartStreaming, this); - signal_handler_connect(obs_output_get_signal_handler(streamOutput), - "stop", OBSStopStreaming, this); - - signal_handler_connect(obs_output_get_signal_handler(fileOutput), - "start", OBSStartRecording, this); - signal_handler_connect(obs_output_get_signal_handler(fileOutput), - "stop", OBSStopRecording, this); - - return true; -} - -bool OBSBasic::InitEncoders() -{ - x264 = obs_video_encoder_create("obs_x264", "default_h264", nullptr); - if (!x264) - return false; - - aac = obs_audio_encoder_create("libfdk_aac", "default_aac", nullptr, 0); - - if (!aac) - aac = obs_audio_encoder_create("ffmpeg_aac", "default_aac", - nullptr, 0); - - if (!aac) - return false; - - return true; -} - bool OBSBasic::InitService() { if (LoadService()) @@ -458,6 +386,8 @@ bool OBSBasic::InitBasicConfigDefaults() uint32_t cy = monitors[0].cy; /* TODO: temporary */ + config_set_default_string(basicConfig, "Output", "Type", "Simple"); + config_set_default_string(basicConfig, "SimpleOutput", "FilePath", GetDefaultVideoSavePath().c_str()); config_set_default_uint (basicConfig, "SimpleOutput", "VBitrate", @@ -569,6 +499,16 @@ void OBSBasic::InitPrimitives() obs_leave_graphics(); } +void OBSBasic::ResetOutputs() +{ + if (!outputHandler || !outputHandler->Active()) { + outputHandler.reset(); + outputHandler.reset(CreateSimpleOutputHandler(this)); + } else { + outputHandler->Update(); + } +} + void OBSBasic::OBSInit() { char savePath[512]; @@ -609,10 +549,8 @@ void OBSBasic::OBSInit() AddExtraModulePaths(); obs_load_all_modules(); - if (!InitOutputs()) - throw "Failed to initialize outputs"; - if (!InitEncoders()) - throw "Failed to initialize encoders"; + ResetOutputs(); + if (!InitService()) throw "Failed to initialize service"; @@ -642,6 +580,8 @@ OBSBasic::~OBSBasic() delete cpuUsageTimer; os_cpu_usage_info_destroy(cpuUsageInfo); + outputHandler.reset(); + if (interaction) delete interaction; @@ -2181,7 +2121,7 @@ void OBSBasic::StreamingStart() { ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); - ui->statusbar->StreamStarted(streamOutput); + ui->statusbar->StreamStarted(outputHandler->streamOutput); } void OBSBasic::StreamingStop(int code) @@ -2212,7 +2152,6 @@ void OBSBasic::StreamingStop(int code) errorMessage = Str("Output.ConnectFail.Disconnected"); } - activeRefs--; ui->statusbar->StreamStopped(); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); @@ -2226,89 +2165,25 @@ void OBSBasic::StreamingStop(int code) void OBSBasic::RecordingStart() { - ui->statusbar->RecordingStarted(fileOutput); + ui->statusbar->RecordingStarted(outputHandler->fileOutput); } void OBSBasic::RecordingStop() { ui->statusbar->RecordingStopped(); - activeRefs--; ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); } -void OBSBasic::SetupEncoders() -{ - if (activeRefs == 0) { - obs_data_t *x264Settings = obs_data_create(); - obs_data_t *aacSettings = obs_data_create(); - - int videoBitrate = config_get_uint(basicConfig, "SimpleOutput", - "VBitrate"); - int audioBitrate = config_get_uint(basicConfig, "SimpleOutput", - "ABitrate"); - bool advanced = config_get_bool(basicConfig, "SimpleOutput", - "UseAdvanced"); - bool useCBR = config_get_bool(basicConfig, "SimpleOutput", - "UseCBR"); - const char *preset = config_get_string(basicConfig, - "SimpleOutput", "Preset"); - const char *custom = config_get_string(basicConfig, - "SimpleOutput", "x264Settings"); - - obs_data_set_int(x264Settings, "bitrate", videoBitrate); - obs_data_set_int(x264Settings, "buffer_size", videoBitrate); - - if (advanced) { - obs_data_set_string(x264Settings, "preset", preset); - obs_data_set_string(x264Settings, "x264opts", custom); - obs_data_set_bool(x264Settings, "cbr", useCBR); - } else { - obs_data_set_bool(x264Settings, "cbr", true); - } - - obs_data_set_int(aacSettings, "bitrate", audioBitrate); - - obs_encoder_update(x264, x264Settings); - obs_encoder_update(aac, aacSettings); - - obs_data_release(x264Settings); - obs_data_release(aacSettings); - - obs_encoder_set_video(x264, obs_get_video()); - obs_encoder_set_audio(aac, obs_get_audio()); - } -} - void OBSBasic::on_streamButton_clicked() { SaveProject(); - if (obs_output_active(streamOutput)) { - obs_output_stop(streamOutput); + if (outputHandler->StreamingActive()) { + outputHandler->StopStreaming(); } else { - SaveService(); - SetupEncoders(); - - obs_output_set_video_encoder(streamOutput, x264); - obs_output_set_audio_encoder(streamOutput, aac, 0); - obs_output_set_service(streamOutput, service); - - bool reconnect = config_get_bool(basicConfig, "SimpleOutput", - "Reconnect"); - int retryDelay = config_get_uint(basicConfig, "SimpleOutput", - "RetryDelay"); - int maxRetries = config_get_uint(basicConfig, "SimpleOutput", - "MaxRetries"); - if (!reconnect) - maxRetries = 0; - - obs_output_set_reconnect_settings(streamOutput, maxRetries, - retryDelay); - - if (obs_output_start(streamOutput)) { - activeRefs++; + if (outputHandler->StartStreaming(service)) { ui->streamButton->setEnabled(false); ui->streamButton->setText( QTStr("Basic.Main.Connecting")); @@ -2320,48 +2195,11 @@ void OBSBasic::on_recordButton_clicked() { SaveProject(); - if (obs_output_active(fileOutput)) { - obs_output_stop(fileOutput); + if (outputHandler->RecordingActive()) { + outputHandler->StopRecording(); } else { - const char *path = config_get_string(basicConfig, - "SimpleOutput", "FilePath"); - - os_dir_t *dir = path ? os_opendir(path) : nullptr; - - if (!dir) { - QMessageBox::information(this, - QTStr("Output.BadPath.Title"), - QTStr("Output.BadPath.Text")); - return; - } - - os_closedir(dir); - - string strPath; - strPath += path; - - char lastChar = strPath.back(); - if (lastChar != '/' && lastChar != '\\') - strPath += "/"; - - strPath += GenerateTimeDateFilename("flv"); - - SetupEncoders(); - - obs_output_set_video_encoder(fileOutput, x264); - obs_output_set_audio_encoder(fileOutput, aac, 0); - - obs_data_t *settings = obs_data_create(); - obs_data_set_string(settings, "path", strPath.c_str()); - - obs_output_update(fileOutput, settings); - - obs_data_release(settings); - - if (obs_output_start(fileOutput)) { - activeRefs++; - + if (outputHandler->StartRecording()) { ui->recordButton->setText( QTStr("Basic.Main.StopRecording")); } diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 57bcf05c5..63b713ee8 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -47,6 +47,8 @@ class QNetworkReply; #define AUX_AUDIO_2 Str("AuxAudioDevice2") #define AUX_AUDIO_3 Str("AuxAudioDevice3") +struct BasicOutputHandler; + class OBSBasic : public OBSMainWindow { Q_OBJECT @@ -69,11 +71,8 @@ private: QPointer cpuUsageTimer; os_cpu_usage_info_t *cpuUsageInfo = nullptr; - obs_output_t *fileOutput = nullptr; - obs_output_t *streamOutput = nullptr; obs_service_t *service = nullptr; - obs_encoder_t *aac = nullptr; - obs_encoder_t *x264 = nullptr; + std::unique_ptr outputHandler; gs_vertbuffer_t *box = nullptr; gs_vertbuffer_t *circle = nullptr; @@ -87,8 +86,6 @@ private: ConfigFile basicConfig; - int activeRefs = 0; - void DrawBackdrop(float cx, float cy); void SetupEncoders(); @@ -105,8 +102,6 @@ private: void SaveService(); bool LoadService(); - bool InitOutputs(); - bool InitEncoders(); bool InitService(); bool InitBasicConfigDefaults(); @@ -200,6 +195,8 @@ public: int ResetVideo(); bool ResetAudio(); + void ResetOutputs(); + void ResetAudioDevice(const char *sourceId, const char *deviceName, const char *deviceDesc, int channel); void ResetAudioDevices(); -- GitLab