提交 07007bcc 编写于 作者: J jp9000

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.
上级 efe31c9f
...@@ -93,6 +93,7 @@ set(obs_SOURCES ...@@ -93,6 +93,7 @@ set(obs_SOURCES
window-basic-settings.cpp window-basic-settings.cpp
window-basic-interaction.cpp window-basic-interaction.cpp
window-basic-properties.cpp window-basic-properties.cpp
window-basic-main-outputs.cpp
window-basic-source-select.cpp window-basic-source-select.cpp
window-license-agreement.cpp window-license-agreement.cpp
window-basic-status-bar.cpp window-basic-status-bar.cpp
...@@ -118,6 +119,7 @@ set(obs_HEADERS ...@@ -118,6 +119,7 @@ set(obs_HEADERS
window-basic-settings.hpp window-basic-settings.hpp
window-basic-interaction.hpp window-basic-interaction.hpp
window-basic-properties.hpp window-basic-properties.hpp
window-basic-main-outputs.hpp
window-basic-source-select.hpp window-basic-source-select.hpp
window-license-agreement.hpp window-license-agreement.hpp
window-basic-status-bar.hpp window-basic-status-bar.hpp
......
#include <string>
#include <QMessageBox>
#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<BasicOutputHandler*>(data);
QMetaObject::invokeMethod(output->main, "StreamingStart");
UNUSED_PARAMETER(params);
}
static void OBSStopStreaming(void *data, calldata_t *params)
{
BasicOutputHandler *output = static_cast<BasicOutputHandler*>(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<BasicOutputHandler*>(data);
QMetaObject::invokeMethod(output->main, "RecordingStart");
UNUSED_PARAMETER(params);
}
static void OBSStopRecording(void *data, calldata_t *params)
{
BasicOutputHandler *output = static_cast<BasicOutputHandler*>(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);
}
#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);
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "window-namedialog.hpp" #include "window-namedialog.hpp"
#include "window-basic-source-select.hpp" #include "window-basic-source-select.hpp"
#include "window-basic-main.hpp" #include "window-basic-main.hpp"
#include "window-basic-main-outputs.hpp"
#include "window-basic-properties.hpp" #include "window-basic-properties.hpp"
#include "window-log-reply.hpp" #include "window-log-reply.hpp"
#include "window-remux.hpp" #include "window-remux.hpp"
...@@ -294,36 +295,6 @@ static inline bool HasAudioDevices(const char *source_id) ...@@ -294,36 +295,6 @@ static inline bool HasAudioDevices(const char *source_id)
return count != 0; return count != 0;
} }
static void OBSStartStreaming(void *data, calldata_t *params)
{
UNUSED_PARAMETER(params);
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"StreamingStart");
}
static void OBSStopStreaming(void *data, calldata_t *params)
{
int code = (int)calldata_int(params, "code");
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"StreamingStop", Q_ARG(int, code));
}
static void OBSStartRecording(void *data, calldata_t *params)
{
UNUSED_PARAMETER(params);
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"RecordingStart");
}
static void OBSStopRecording(void *data, calldata_t *params)
{
UNUSED_PARAMETER(params);
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"RecordingStop");
}
#define SERVICE_PATH "obs-studio/basic/service.json" #define SERVICE_PATH "obs-studio/basic/service.json"
void OBSBasic::SaveService() void OBSBasic::SaveService()
...@@ -380,49 +351,6 @@ bool OBSBasic::LoadService() ...@@ -380,49 +351,6 @@ bool OBSBasic::LoadService()
return !!service; 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() bool OBSBasic::InitService()
{ {
if (LoadService()) if (LoadService())
...@@ -458,6 +386,8 @@ bool OBSBasic::InitBasicConfigDefaults() ...@@ -458,6 +386,8 @@ bool OBSBasic::InitBasicConfigDefaults()
uint32_t cy = monitors[0].cy; uint32_t cy = monitors[0].cy;
/* TODO: temporary */ /* TODO: temporary */
config_set_default_string(basicConfig, "Output", "Type", "Simple");
config_set_default_string(basicConfig, "SimpleOutput", "FilePath", config_set_default_string(basicConfig, "SimpleOutput", "FilePath",
GetDefaultVideoSavePath().c_str()); GetDefaultVideoSavePath().c_str());
config_set_default_uint (basicConfig, "SimpleOutput", "VBitrate", config_set_default_uint (basicConfig, "SimpleOutput", "VBitrate",
...@@ -569,6 +499,16 @@ void OBSBasic::InitPrimitives() ...@@ -569,6 +499,16 @@ void OBSBasic::InitPrimitives()
obs_leave_graphics(); obs_leave_graphics();
} }
void OBSBasic::ResetOutputs()
{
if (!outputHandler || !outputHandler->Active()) {
outputHandler.reset();
outputHandler.reset(CreateSimpleOutputHandler(this));
} else {
outputHandler->Update();
}
}
void OBSBasic::OBSInit() void OBSBasic::OBSInit()
{ {
char savePath[512]; char savePath[512];
...@@ -609,10 +549,8 @@ void OBSBasic::OBSInit() ...@@ -609,10 +549,8 @@ void OBSBasic::OBSInit()
AddExtraModulePaths(); AddExtraModulePaths();
obs_load_all_modules(); obs_load_all_modules();
if (!InitOutputs()) ResetOutputs();
throw "Failed to initialize outputs";
if (!InitEncoders())
throw "Failed to initialize encoders";
if (!InitService()) if (!InitService())
throw "Failed to initialize service"; throw "Failed to initialize service";
...@@ -642,6 +580,8 @@ OBSBasic::~OBSBasic() ...@@ -642,6 +580,8 @@ OBSBasic::~OBSBasic()
delete cpuUsageTimer; delete cpuUsageTimer;
os_cpu_usage_info_destroy(cpuUsageInfo); os_cpu_usage_info_destroy(cpuUsageInfo);
outputHandler.reset();
if (interaction) if (interaction)
delete interaction; delete interaction;
...@@ -2181,7 +2121,7 @@ void OBSBasic::StreamingStart() ...@@ -2181,7 +2121,7 @@ void OBSBasic::StreamingStart()
{ {
ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setText(QTStr("Basic.Main.StopStreaming"));
ui->streamButton->setEnabled(true); ui->streamButton->setEnabled(true);
ui->statusbar->StreamStarted(streamOutput); ui->statusbar->StreamStarted(outputHandler->streamOutput);
} }
void OBSBasic::StreamingStop(int code) void OBSBasic::StreamingStop(int code)
...@@ -2212,7 +2152,6 @@ void OBSBasic::StreamingStop(int code) ...@@ -2212,7 +2152,6 @@ void OBSBasic::StreamingStop(int code)
errorMessage = Str("Output.ConnectFail.Disconnected"); errorMessage = Str("Output.ConnectFail.Disconnected");
} }
activeRefs--;
ui->statusbar->StreamStopped(); ui->statusbar->StreamStopped();
ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
...@@ -2226,89 +2165,25 @@ void OBSBasic::StreamingStop(int code) ...@@ -2226,89 +2165,25 @@ void OBSBasic::StreamingStop(int code)
void OBSBasic::RecordingStart() void OBSBasic::RecordingStart()
{ {
ui->statusbar->RecordingStarted(fileOutput); ui->statusbar->RecordingStarted(outputHandler->fileOutput);
} }
void OBSBasic::RecordingStop() void OBSBasic::RecordingStop()
{ {
ui->statusbar->RecordingStopped(); ui->statusbar->RecordingStopped();
activeRefs--;
ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); 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() void OBSBasic::on_streamButton_clicked()
{ {
SaveProject(); SaveProject();
if (obs_output_active(streamOutput)) { if (outputHandler->StreamingActive()) {
obs_output_stop(streamOutput); outputHandler->StopStreaming();
} else { } else {
SaveService(); 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->setEnabled(false);
ui->streamButton->setText( ui->streamButton->setText(
QTStr("Basic.Main.Connecting")); QTStr("Basic.Main.Connecting"));
...@@ -2320,48 +2195,11 @@ void OBSBasic::on_recordButton_clicked() ...@@ -2320,48 +2195,11 @@ void OBSBasic::on_recordButton_clicked()
{ {
SaveProject(); SaveProject();
if (obs_output_active(fileOutput)) { if (outputHandler->RecordingActive()) {
obs_output_stop(fileOutput); outputHandler->StopRecording();
} else { } else {
const char *path = config_get_string(basicConfig, if (outputHandler->StartRecording()) {
"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++;
ui->recordButton->setText( ui->recordButton->setText(
QTStr("Basic.Main.StopRecording")); QTStr("Basic.Main.StopRecording"));
} }
......
...@@ -47,6 +47,8 @@ class QNetworkReply; ...@@ -47,6 +47,8 @@ class QNetworkReply;
#define AUX_AUDIO_2 Str("AuxAudioDevice2") #define AUX_AUDIO_2 Str("AuxAudioDevice2")
#define AUX_AUDIO_3 Str("AuxAudioDevice3") #define AUX_AUDIO_3 Str("AuxAudioDevice3")
struct BasicOutputHandler;
class OBSBasic : public OBSMainWindow { class OBSBasic : public OBSMainWindow {
Q_OBJECT Q_OBJECT
...@@ -69,11 +71,8 @@ private: ...@@ -69,11 +71,8 @@ private:
QPointer<QTimer> cpuUsageTimer; QPointer<QTimer> cpuUsageTimer;
os_cpu_usage_info_t *cpuUsageInfo = nullptr; os_cpu_usage_info_t *cpuUsageInfo = nullptr;
obs_output_t *fileOutput = nullptr;
obs_output_t *streamOutput = nullptr;
obs_service_t *service = nullptr; obs_service_t *service = nullptr;
obs_encoder_t *aac = nullptr; std::unique_ptr<BasicOutputHandler> outputHandler;
obs_encoder_t *x264 = nullptr;
gs_vertbuffer_t *box = nullptr; gs_vertbuffer_t *box = nullptr;
gs_vertbuffer_t *circle = nullptr; gs_vertbuffer_t *circle = nullptr;
...@@ -87,8 +86,6 @@ private: ...@@ -87,8 +86,6 @@ private:
ConfigFile basicConfig; ConfigFile basicConfig;
int activeRefs = 0;
void DrawBackdrop(float cx, float cy); void DrawBackdrop(float cx, float cy);
void SetupEncoders(); void SetupEncoders();
...@@ -105,8 +102,6 @@ private: ...@@ -105,8 +102,6 @@ private:
void SaveService(); void SaveService();
bool LoadService(); bool LoadService();
bool InitOutputs();
bool InitEncoders();
bool InitService(); bool InitService();
bool InitBasicConfigDefaults(); bool InitBasicConfigDefaults();
...@@ -200,6 +195,8 @@ public: ...@@ -200,6 +195,8 @@ public:
int ResetVideo(); int ResetVideo();
bool ResetAudio(); bool ResetAudio();
void ResetOutputs();
void ResetAudioDevice(const char *sourceId, const char *deviceName, void ResetAudioDevice(const char *sourceId, const char *deviceName,
const char *deviceDesc, int channel); const char *deviceDesc, int channel);
void ResetAudioDevices(); void ResetAudioDevices();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册