Skip to content

Commit 4d9b385

Browse files
Enhanced broadcasting with the new API proof of concept
1 parent 221dac6 commit 4d9b385

16 files changed

+229
-45
lines changed

js/module.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ export interface IStreaming {
798798
service: IService;
799799
enforceServiceBitrate: boolean;
800800
enableTwitchVOD: boolean;
801+
enhancedBroadcasting: boolean;
801802
delay: IDelay;
802803
reconnect: IReconnect;
803804
network: INetwork;

js/module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,7 @@ export interface IStreaming {
17091709
service: IService,
17101710
enforceServiceBitrate: boolean,
17111711
enableTwitchVOD: boolean,
1712+
enhancedBroadcasting: boolean,
17121713
delay: IDelay,
17131714
reconnect: IReconnect,
17141715
network: INetwork,

obs-studio-client/source/advanced-streaming.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Napi::Object osn::AdvancedStreaming::Init(Napi::Env env, Napi::Object exports)
3737
InstanceAccessor("service", &osn::AdvancedStreaming::GetService, &osn::AdvancedStreaming::SetService),
3838
InstanceAccessor("enforceServiceBitrate", &osn::AdvancedStreaming::GetEnforceServiceBirate, &osn::AdvancedStreaming::SetEnforceServiceBirate),
3939
InstanceAccessor("enableTwitchVOD", &osn::AdvancedStreaming::GetEnableTwitchVOD, &osn::AdvancedStreaming::SetEnableTwitchVOD),
40+
InstanceAccessor("enhancedBroadcasting", &osn::AdvancedStreaming::GetEnhancedBroadcasting, &osn::AdvancedStreaming::SetEnhancedBroadcasting),
4041
InstanceAccessor("signalHandler", &osn::AdvancedStreaming::GetSignalHandler, &osn::AdvancedStreaming::SetSignalHandler),
4142
InstanceAccessor("delay", &osn::AdvancedStreaming::GetDelay, &osn::AdvancedStreaming::SetDelay),
4243
InstanceAccessor("reconnect", &osn::AdvancedStreaming::GetReconnect, &osn::AdvancedStreaming::SetReconnect),

obs-studio-client/source/simple-streaming.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Napi::Object osn::SimpleStreaming::Init(Napi::Env env, Napi::Object exports)
3939
InstanceAccessor("service", &osn::SimpleStreaming::GetService, &osn::SimpleStreaming::SetService),
4040
InstanceAccessor("enforceServiceBitrate", &osn::SimpleStreaming::GetEnforceServiceBirate, &osn::SimpleStreaming::SetEnforceServiceBirate),
4141
InstanceAccessor("enableTwitchVOD", &osn::SimpleStreaming::GetEnableTwitchVOD, &osn::SimpleStreaming::SetEnableTwitchVOD),
42+
InstanceAccessor("enhancedBroadcasting", &osn::SimpleStreaming::GetEnhancedBroadcasting, &osn::SimpleStreaming::SetEnhancedBroadcasting),
4243
InstanceAccessor("audioEncoder", &osn::SimpleStreaming::GetAudioEncoder, &osn::SimpleStreaming::SetAudioEncoder),
4344
InstanceAccessor("useAdvanced", &osn::SimpleStreaming::GetUseAdvanced, &osn::SimpleStreaming::SetUseAdvanced),
4445
InstanceAccessor("customEncSettings", &osn::SimpleStreaming::GetCustomEncSettings, &osn::SimpleStreaming::SetCustomEncSettings),

obs-studio-client/source/streaming.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,29 @@ void osn::Streaming::SetEnforceServiceBirate(const Napi::CallbackInfo &info, con
143143
conn->call_synchronous_helper(className, "SetEnforceServiceBirate", {ipc::value(this->uid), ipc::value(value.ToBoolean().Value())});
144144
}
145145

146+
Napi::Value osn::Streaming::GetEnhancedBroadcasting(const Napi::CallbackInfo &info)
147+
{
148+
auto conn = GetConnection(info);
149+
if (!conn)
150+
return info.Env().Undefined();
151+
152+
std::vector<ipc::value> response = conn->call_synchronous_helper(className, "GetEnhancedBroadcasting", {ipc::value(this->uid)});
153+
154+
if (!ValidateResponse(info, response))
155+
return info.Env().Undefined();
156+
157+
return Napi::Boolean::New(info.Env(), response[1].value_union.ui32);
158+
}
159+
160+
void osn::Streaming::SetEnhancedBroadcasting(const Napi::CallbackInfo &info, const Napi::Value &value)
161+
{
162+
auto conn = GetConnection(info);
163+
if (!conn)
164+
return;
165+
166+
conn->call_synchronous_helper(className, "SetEnhancedBroadcasting", {ipc::value(this->uid), ipc::value(value.ToBoolean().Value())});
167+
}
168+
146169
Napi::Value osn::Streaming::GetEnableTwitchVOD(const Napi::CallbackInfo &info)
147170
{
148171
auto conn = GetConnection(info);

obs-studio-client/source/streaming.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class Streaming : public WorkerSignals {
4040
void SetEnforceServiceBirate(const Napi::CallbackInfo &info, const Napi::Value &value);
4141
Napi::Value GetEnableTwitchVOD(const Napi::CallbackInfo &info);
4242
void SetEnableTwitchVOD(const Napi::CallbackInfo &info, const Napi::Value &value);
43+
Napi::Value GetEnhancedBroadcasting(const Napi::CallbackInfo &info);
44+
void SetEnhancedBroadcasting(const Napi::CallbackInfo &info, const Napi::Value &value);
4345
Napi::Value GetDelay(const Napi::CallbackInfo &info);
4446
void SetDelay(const Napi::CallbackInfo &info, const Napi::Value &value);
4547
Napi::Value GetReconnect(const Napi::CallbackInfo &info);

obs-studio-server/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,11 @@ SET(osn-server_SOURCES
292292
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video-data-model.hpp"
293293
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video-output.cpp"
294294
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video-output.hpp"
295+
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video-system-info.hpp"
296+
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video.cpp"
297+
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video.hpp"
295298
"${PROJECT_SOURCE_DIR}/source/osn-audio-bitrate.cpp"
296299
"${PROJECT_SOURCE_DIR}/source/osn-audio-bitrate.hpp"
297-
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video-system-info.hpp"
298300
"${PROJECT_SOURCE_DIR}/source/osn-output.cpp"
299301
"${PROJECT_SOURCE_DIR}/source/osn-output.hpp"
300302
"${PROJECT_SOURCE_DIR}/source/osn-properties.cpp"

obs-studio-server/source/nodeobs_service.cpp

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@
2828
#include <osn-video.hpp>
2929
#include "osn-vcam.hpp"
3030

31-
#include "osn-multitrack-video-configuration.hpp"
32-
#include "osn-audio-bitrate.hpp"
33-
#include "osn-multitrack-video-output.hpp"
31+
#include "osn-multitrack-video.hpp"
3432

3533
#ifdef __APPLE__
3634
#include <sys/types.h>
@@ -92,13 +90,7 @@ std::mutex signalMutex;
9290
std::queue<SignalInfo> outputSignal;
9391
std::thread releaseWorker;
9492

95-
struct EnhancedBroadcastOutputObjects {
96-
OBSOutputAutoRelease obsOutput;
97-
std::shared_ptr<obs_encoder_group_t> videoEncoderGroup;
98-
std::vector<OBSEncoderAutoRelease> audioEncoders;
99-
OBSServiceAutoRelease multitrackVideoService;
100-
};
101-
static std::optional<EnhancedBroadcastOutputObjects> enhancedBroadcastContext;
93+
static std::optional<osn::EnhancedBroadcastOutputObjects> enhancedBroadcastContext;
10294

10395
OBS_service::OBS_service() {}
10496
OBS_service::~OBS_service() {}
@@ -1266,18 +1258,6 @@ const char *OBS_service::getStreamOutputType(const obs_service_t *service)
12661258
return nullptr;
12671259
}
12681260

1269-
static int GetAudioBitrate()
1270-
{
1271-
const char *audio_encoder_id = config_get_string(ConfigManager::getInstance().getBasic(), "SimpleOutput", "StreamAudioEncoder");
1272-
1273-
const int bitrate = (int)config_get_uint(ConfigManager::getInstance().getBasic(), "SimpleOutput", "ABitrate");
1274-
1275-
if (audio_encoder_id && strcmp(audio_encoder_id, "opus") == 0)
1276-
return osn::FindClosestAvailableSimpleOpusBitrate(bitrate);
1277-
1278-
return osn::FindClosestAvailableSimpleAACBitrate(bitrate);
1279-
}
1280-
12811261
static inline bool ServiceSupportsVodTrack(const char *service)
12821262
{
12831263
static const char *vodTrackServices[] = {"Twitch"};
@@ -1307,11 +1287,6 @@ static bool IsVodTrackEnabled(obs_service_t *service)
13071287
return vodTrackEnabledAdv && ServiceSupportsVodTrack(name);
13081288
}
13091289

1310-
static bool IsMultitrackVideoEnabled()
1311-
{
1312-
return config_get_bool(ConfigManager::getInstance().getBasic(), "EnhancedBroadcasting", "EnableMultitrackVideo");
1313-
}
1314-
13151290
bool OBS_service::startSingleTrackStreaming(StreamServiceId serviceId)
13161291
{
13171292
const char *type = getStreamOutputType(services[serviceId]);
@@ -1404,7 +1379,7 @@ bool OBS_service::startMultiTrackStreaming(StreamServiceId serviceId, bool dualS
14041379
throw std::runtime_error("startStreaming - go live config is empty");
14051380
}
14061381

1407-
const auto audio_bitrate = GetAudioBitrate();
1382+
const auto audio_bitrate = osn::GetMultitrackAudioBitrate();
14081383
const auto audio_encoder_id = osn::GetSimpleAACEncoderForBitrate(audio_bitrate);
14091384

14101385
std::vector<OBSEncoderAutoRelease> audio_encoders;
@@ -1429,7 +1404,7 @@ bool OBS_service::startMultiTrackStreaming(StreamServiceId serviceId, bool dualS
14291404

14301405
isStreaming[serviceId] = obs_output_start(output);
14311406

1432-
enhancedBroadcastContext.emplace(EnhancedBroadcastOutputObjects{
1407+
enhancedBroadcastContext.emplace(osn::EnhancedBroadcastOutputObjects{
14331408
std::move(output),
14341409
video_encoder_group,
14351410
std::move(audio_encoders),
@@ -1447,7 +1422,7 @@ bool OBS_service::startStreaming(StreamServiceId serviceId, bool dualStreamingMo
14471422
}
14481423

14491424
try {
1450-
if (isTwitchStream(serviceId) && (IsMultitrackVideoEnabled() || dualStreamingMode)) {
1425+
if (isTwitchStream(serviceId) && (osn::IsMultitrackVideoEnabled() || dualStreamingMode)) {
14511426
return startMultiTrackStreaming(serviceId, dualStreamingMode);
14521427
}
14531428

@@ -1634,7 +1609,7 @@ bool OBS_service::updateRecordingEncoders(bool isSimpleMode, StreamServiceId ser
16341609

16351610
bool simpleUsesStream = false;
16361611
bool advancedUsesStream = false;
1637-
if (!IsMultitrackVideoEnabled()) {
1612+
if (!osn::IsMultitrackVideoEnabled()) {
16381613
simpleUsesStream = isSimpleMode && simpleQuality.compare("Stream") == 0;
16391614
advancedUsesStream = !isSimpleMode && (advancedQuality.compare("") == 0 || advancedQuality.compare("none") == 0);
16401615
}
@@ -1766,7 +1741,7 @@ void OBS_service::stopStreaming(bool forceStop, StreamServiceId serviceId)
17661741
obs_output_stop(streamingOutput[serviceId]);
17671742

17681743
/* Unregister the BPM (Broadcast Performance Metrics) callback and destroy the allocated metrics data. */
1769-
if (isTwitchStream(serviceId) && IsMultitrackVideoEnabled()) {
1744+
if (isTwitchStream(serviceId) && osn::IsMultitrackVideoEnabled()) {
17701745
obs_output_remove_packet_callback(streamingOutput[serviceId], bpm_inject, NULL);
17711746
bpm_destroy(streamingOutput[serviceId]);
17721747
}

obs-studio-server/source/osn-advanced-streaming.cpp

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "nodeobs_audio_encoders.h"
2525
#include "osn-audio-track.hpp"
2626

27+
#include "osn-multitrack-video.hpp"
28+
2729
void osn::IAdvancedStreaming::Register(ipc::server &srv)
2830
{
2931
std::shared_ptr<ipc::collection> cls = std::make_shared<ipc::collection>("AdvancedStreaming");
@@ -42,6 +44,9 @@ void osn::IAdvancedStreaming::Register(ipc::server &srv)
4244
cls->register_function(std::make_shared<ipc::function>("GetEnableTwitchVOD", std::vector<ipc::type>{ipc::type::UInt64}, GetEnableTwitchVOD));
4345
cls->register_function(
4446
std::make_shared<ipc::function>("SetEnableTwitchVOD", std::vector<ipc::type>{ipc::type::UInt64, ipc::type::UInt32}, SetEnableTwitchVOD));
47+
cls->register_function(std::make_shared<ipc::function>("GetEnhancedBroadcasting", std::vector<ipc::type>{ipc::type::UInt64}, GetEnhancedBroadcasting));
48+
cls->register_function(
49+
std::make_shared<ipc::function>("SetEnhancedBroadcasting", std::vector<ipc::type>{ipc::type::UInt64, ipc::type::UInt32}, SetEnhancedBroadcasting));
4550
cls->register_function(std::make_shared<ipc::function>("GetAudioTrack", std::vector<ipc::type>{ipc::type::UInt64}, GetAudioTrack));
4651
cls->register_function(std::make_shared<ipc::function>("SetAudioTrack", std::vector<ipc::type>{ipc::type::UInt64, ipc::type::UInt32}, SetAudioTrack));
4752
cls->register_function(std::make_shared<ipc::function>("GetTwitchTrack", std::vector<ipc::type>{ipc::type::UInt64}, GetTwitchTrack));
@@ -369,17 +374,94 @@ void osn::IAdvancedStreaming::Start(void *data, const int64_t id, const std::vec
369374
PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Advanced streaming reference is not valid.");
370375
}
371376

377+
if (!streaming->service) {
378+
PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Invalid service.");
379+
}
380+
381+
if (streaming->enhancedBroadcasting) {
382+
const bool dualStreamingMode = true;
383+
384+
blog(LOG_INFO, "osn::IAdvancedStreaming::Start - service id: %s", obs_service_get_id(streaming->service));
385+
386+
const bool is_custom = strncmp("rtmp_custom", obs_service_get_type(streaming->service), 11) == 0;
387+
388+
OBSDataAutoRelease settings = obs_service_get_settings(streaming->service);
389+
std::string key = obs_data_get_string(settings, "key");
390+
391+
const char *service_name = "<unknown>";
392+
if (is_custom && obs_data_has_user_value(settings, "service_name")) {
393+
service_name = obs_data_get_string(settings, "service_name");
394+
} else if (!is_custom) {
395+
service_name = obs_data_get_string(settings, "service");
396+
}
397+
398+
std::optional<std::string> custom_rtmp_url;
399+
auto server = obs_data_get_string(settings, "server");
400+
if (strcmp(server, "auto") != 0) {
401+
custom_rtmp_url = server;
402+
}
403+
404+
auto service_custom_server = obs_data_get_bool(settings, "using_custom_server");
405+
if (custom_rtmp_url.has_value()) {
406+
blog(LOG_INFO, "Using %sserver '%s'", service_custom_server ? "custom " : "", custom_rtmp_url->c_str());
407+
}
408+
409+
auto auto_config_url = osn::MultitrackVideoAutoConfigURL(streaming->service);
410+
blog(LOG_INFO, "Auto config URL: %s", auto_config_url.c_str());
411+
412+
// TODO: !!!!!!!!!!!!!!!! VOD track index from options instead of global config??????????????????????????????????????????????????????
413+
const auto vodTrackIndex = int(config_get_int(ConfigManager::getInstance().getBasic(), "AdvOut", "VodTrackIndex")) - 1;
414+
blog(LOG_INFO, "vodTrackIndex: %d", vodTrackIndex);
415+
416+
auto vod_track_mixer = streaming->enableTwitchVOD ? std::optional{vodTrackIndex} : std::nullopt;
417+
418+
auto go_live_post = osn::constructGoLivePost(StreamServiceId::Main, dualStreamingMode, key, std::nullopt, std::nullopt, vod_track_mixer.has_value());
419+
std::optional<osn::Config> go_live_config = osn::DownloadGoLiveConfig(auto_config_url, go_live_post);
420+
if (!go_live_config.has_value()) {
421+
throw std::runtime_error("startStreaming - go live config is empty");
422+
}
423+
424+
const auto audio_bitrate = osn::GetMultitrackAudioBitrate();
425+
const auto audio_encoder_id = osn::GetSimpleAACEncoderForBitrate(audio_bitrate);
426+
427+
std::vector<OBSEncoderAutoRelease> audio_encoders;
428+
std::shared_ptr<obs_encoder_group_t> video_encoder_group;
429+
auto output =
430+
osn::SetupOBSOutput("Enhanced Broadcasting", go_live_config.value(), audio_encoders, video_encoder_group, audio_encoder_id, 0, vod_track_mixer);
431+
if (!output) {
432+
throw std::runtime_error("startStreaming - failed to create multitrack output");
433+
}
434+
435+
auto multitrack_video_service = osn::create_service(*go_live_config, std::nullopt, ""); // Stream key is defined by config from Twitch
436+
if (!multitrack_video_service) {
437+
throw std::runtime_error("startStreaming - failed to create multitrack video service");
438+
}
439+
440+
streaming->SetOutput(output.Get());
441+
obs_output_set_service(output, multitrack_video_service);
442+
443+
// Register the BPM (Broadcast Performance Metrics) callback
444+
obs_output_add_packet_callback(output, bpm_inject, NULL);
445+
446+
streaming->StartOutput();
447+
448+
streaming->enhancedBroadcastContext.emplace(EnhancedBroadcastOutputObjects{
449+
std::move(output),
450+
video_encoder_group,
451+
std::move(audio_encoders),
452+
std::move(multitrack_video_service),
453+
});
454+
}
455+
372456
// TODO: enhanced broadcasting case
457+
return;
458+
373459
if (!streaming->videoEncoder) {
374460
PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Invalid video encoder.");
375461
}
376462

377463
streaming->UpdateEncoders();
378464

379-
if (!streaming->service) {
380-
PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Invalid service.");
381-
}
382-
383465
const char *type = OBS_service::getStreamOutputType(streaming->service);
384466
if (!type)
385467
type = "rtmp_output";
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include "osn-multitrack-video.hpp"
2+
3+
#include "osn-audio-bitrate.hpp"
4+
5+
#include "nodeobs_configManager.hpp"
6+
#include <util/config-file.h>
7+
8+
9+
namespace osn {
10+
11+
bool IsMultitrackVideoEnabled()
12+
{
13+
return config_get_bool(ConfigManager::getInstance().getBasic(), "EnhancedBroadcasting", "EnableMultitrackVideo");
14+
}
15+
16+
int GetMultitrackAudioBitrate()
17+
{
18+
const char *audio_encoder_id = config_get_string(ConfigManager::getInstance().getBasic(), "SimpleOutput", "StreamAudioEncoder");
19+
const int bitrate = (int)config_get_uint(ConfigManager::getInstance().getBasic(), "SimpleOutput", "ABitrate");
20+
21+
if (audio_encoder_id && strcmp(audio_encoder_id, "opus") == 0)
22+
return osn::FindClosestAvailableSimpleOpusBitrate(bitrate);
23+
24+
return osn::FindClosestAvailableSimpleAACBitrate(bitrate);
25+
}
26+
27+
}

0 commit comments

Comments
 (0)