Skip to content

Commit 0d429bc

Browse files
Add enhanced broadcasting per-display stats (#1738)
* Add enhanced broadcasting per-display stats * Clang format * Wait for source manager cleanup after release in tests * Scope display stats to Enhanced Broadcasting
1 parent 9a5c754 commit 0d429bc

17 files changed

Lines changed: 300 additions & 7 deletions

js/module.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,14 @@ export interface IStreaming {
808808
kbitsPerSec: number;
809809
dataOutput: number;
810810
}
811+
export interface IEnhancedBroadcastingDisplayStats {
812+
kbitsPerSec: number;
813+
dataOutput: number;
814+
}
815+
export interface IEnhancedBroadcastingPerDisplayStats {
816+
horizontal: IEnhancedBroadcastingDisplayStats;
817+
vertical: IEnhancedBroadcastingDisplayStats;
818+
}
811819
export interface EOutputSignal {
812820
type: string;
813821
signal: string;
@@ -849,6 +857,7 @@ export interface IAdvancedStreamingFactory {
849857
}
850858
export interface IEnhancedBroadcastingAdvancedStreaming extends IAdvancedStreaming {
851859
additionalVideo?: IVideo;
860+
displayStats: IEnhancedBroadcastingPerDisplayStats;
852861
}
853862
export interface IEnhancedBroadcastingAdvancedStreamingFactory {
854863
create(): IEnhancedBroadcastingAdvancedStreaming;
@@ -857,6 +866,7 @@ export interface IEnhancedBroadcastingAdvancedStreamingFactory {
857866
}
858867
export interface IEnhancedBroadcastingSimpleStreaming extends ISimpleStreaming {
859868
additionalVideo?: IVideo;
869+
displayStats: IEnhancedBroadcastingPerDisplayStats;
860870
}
861871
export interface IEnhancedBroadcastingSimpleStreamingFactory {
862872
create(): IEnhancedBroadcastingSimpleStreaming;

js/module.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,16 @@ export interface IStreaming {
17311731
dataOutput: number;
17321732
}
17331733

1734+
export interface IEnhancedBroadcastingDisplayStats {
1735+
kbitsPerSec: number;
1736+
dataOutput: number;
1737+
}
1738+
1739+
export interface IEnhancedBroadcastingPerDisplayStats {
1740+
horizontal: IEnhancedBroadcastingDisplayStats;
1741+
vertical: IEnhancedBroadcastingDisplayStats;
1742+
}
1743+
17341744
export interface EOutputSignal {
17351745
type: string,
17361746
signal: string,
@@ -1788,6 +1798,7 @@ export interface IEnhancedBroadcastingAdvancedStreaming extends IAdvancedStreami
17881798
// If set, the Enhanced Broadcasting stream will be in the Dual Output mode.
17891799
// This value should be initialized before the stream start.
17901800
additionalVideo?: IVideo,
1801+
displayStats: IEnhancedBroadcastingPerDisplayStats,
17911802
}
17921803

17931804
export interface IEnhancedBroadcastingAdvancedStreamingFactory {
@@ -1800,6 +1811,7 @@ export interface IEnhancedBroadcastingSimpleStreaming extends ISimpleStreaming {
18001811
// If set, the Enhanced Broadcasting stream will be in the Dual Output mode.
18011812
// This value should be initialized before the stream start.
18021813
additionalVideo?: IVideo,
1814+
displayStats: IEnhancedBroadcastingPerDisplayStats,
18031815
}
18041816

18051817
export interface IEnhancedBroadcastingSimpleStreamingFactory {

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Napi::Object osn::EnhancedBroadcastingAdvancedStreaming::Init(Napi::Env env, Nap
5454
InstanceAccessor("totalFrames", &osn::EnhancedBroadcastingAdvancedStreaming::GetTotalFrames, nullptr),
5555
InstanceAccessor("kbitsPerSec", &osn::EnhancedBroadcastingAdvancedStreaming::GetKBitsPerSec, nullptr),
5656
InstanceAccessor("dataOutput", &osn::EnhancedBroadcastingAdvancedStreaming::GetDataOutput, nullptr),
57+
InstanceAccessor("displayStats", &osn::EnhancedBroadcastingAdvancedStreaming::GetDisplayStats, nullptr),
5758

5859
InstanceAccessor("audioTrack", &osn::EnhancedBroadcastingAdvancedStreaming::GetAudioTrack,
5960
&osn::EnhancedBroadcastingAdvancedStreaming::SetAudioTrack),
@@ -168,6 +169,31 @@ void osn::EnhancedBroadcastingAdvancedStreaming::SetAdditionalCanvas(const Napi:
168169
ValidateResponse(info, response);
169170
}
170171

172+
Napi::Value osn::EnhancedBroadcastingAdvancedStreaming::GetDisplayStats(const Napi::CallbackInfo &info)
173+
{
174+
auto conn = GetConnection(info);
175+
if (!conn)
176+
return info.Env().Undefined();
177+
178+
auto response = conn->call_synchronous_helper(className, "GetDisplayStats", {ipc::value(this->uid)});
179+
180+
if (!ValidateResponse(info, response) || response.size() < 5)
181+
return info.Env().Undefined();
182+
183+
Napi::Object horizontal = Napi::Object::New(info.Env());
184+
horizontal.Set("kbitsPerSec", Napi::Number::New(info.Env(), response[1].value_union.fp64));
185+
horizontal.Set("dataOutput", Napi::Number::New(info.Env(), response[2].value_union.fp64));
186+
187+
Napi::Object vertical = Napi::Object::New(info.Env());
188+
vertical.Set("kbitsPerSec", Napi::Number::New(info.Env(), response[3].value_union.fp64));
189+
vertical.Set("dataOutput", Napi::Number::New(info.Env(), response[4].value_union.fp64));
190+
191+
Napi::Object stats = Napi::Object::New(info.Env());
192+
stats.Set("horizontal", horizontal);
193+
stats.Set("vertical", vertical);
194+
return stats;
195+
}
196+
171197
Napi::Value osn::EnhancedBroadcastingAdvancedStreaming::GetLegacySettings(const Napi::CallbackInfo &info)
172198
{
173199
auto conn = GetConnection(info);

obs-studio-client/source/enhanced-broadcasting-advanced-streaming.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ class EnhancedBroadcastingAdvancedStreaming : public Napi::ObjectWrap<osn::Enhan
3232

3333
Napi::Value GetAdditionalCanvas(const Napi::CallbackInfo &info);
3434
void SetAdditionalCanvas(const Napi::CallbackInfo &info, const Napi::Value &value);
35+
Napi::Value GetDisplayStats(const Napi::CallbackInfo &info);
3536

3637
static Napi::Value GetLegacySettings(const Napi::CallbackInfo &info);
3738
static void SetLegacySettings(const Napi::CallbackInfo &info, const Napi::Value &value);
3839
};
39-
}
40+
}

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Napi::Object osn::EnhancedBroadcastingSimpleStreaming::Init(Napi::Env env, Napi:
6060
InstanceAccessor("totalFrames", &osn::EnhancedBroadcastingSimpleStreaming::GetTotalFrames, nullptr),
6161
InstanceAccessor("kbitsPerSec", &osn::EnhancedBroadcastingSimpleStreaming::GetKBitsPerSec, nullptr),
6262
InstanceAccessor("dataOutput", &osn::EnhancedBroadcastingSimpleStreaming::GetDataOutput, nullptr),
63+
InstanceAccessor("displayStats", &osn::EnhancedBroadcastingSimpleStreaming::GetDisplayStats, nullptr),
6364

6465
InstanceMethod("start", &osn::EnhancedBroadcastingSimpleStreaming::Start),
6566
InstanceMethod("stop", &osn::EnhancedBroadcastingSimpleStreaming::Stop),
@@ -161,6 +162,31 @@ void osn::EnhancedBroadcastingSimpleStreaming::SetAdditionalCanvas(const Napi::C
161162
ValidateResponse(info, response);
162163
}
163164

165+
Napi::Value osn::EnhancedBroadcastingSimpleStreaming::GetDisplayStats(const Napi::CallbackInfo &info)
166+
{
167+
auto conn = GetConnection(info);
168+
if (!conn)
169+
return info.Env().Undefined();
170+
171+
auto response = conn->call_synchronous_helper(className, "GetDisplayStats", {ipc::value(this->uid)});
172+
173+
if (!ValidateResponse(info, response) || response.size() < 5)
174+
return info.Env().Undefined();
175+
176+
Napi::Object horizontal = Napi::Object::New(info.Env());
177+
horizontal.Set("kbitsPerSec", Napi::Number::New(info.Env(), response[1].value_union.fp64));
178+
horizontal.Set("dataOutput", Napi::Number::New(info.Env(), response[2].value_union.fp64));
179+
180+
Napi::Object vertical = Napi::Object::New(info.Env());
181+
vertical.Set("kbitsPerSec", Napi::Number::New(info.Env(), response[3].value_union.fp64));
182+
vertical.Set("dataOutput", Napi::Number::New(info.Env(), response[4].value_union.fp64));
183+
184+
Napi::Object stats = Napi::Object::New(info.Env());
185+
stats.Set("horizontal", horizontal);
186+
stats.Set("vertical", vertical);
187+
return stats;
188+
}
189+
164190
Napi::Value osn::EnhancedBroadcastingSimpleStreaming::GetLegacySettings(const Napi::CallbackInfo &info)
165191
{
166192
auto conn = GetConnection(info);
@@ -195,4 +221,4 @@ void osn::EnhancedBroadcastingSimpleStreaming::SetLegacySettings(const Napi::Cal
195221

196222
if (!ValidateResponse(info, response))
197223
return;
198-
}
224+
}

obs-studio-client/source/enhanced-broadcasting-simple-streaming.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ class EnhancedBroadcastingSimpleStreaming : public Napi::ObjectWrap<osn::Enhance
3232

3333
Napi::Value GetAdditionalCanvas(const Napi::CallbackInfo &info);
3434
void SetAdditionalCanvas(const Napi::CallbackInfo &info, const Napi::Value &value);
35+
Napi::Value GetDisplayStats(const Napi::CallbackInfo &info);
3536

3637
static Napi::Value GetLegacySettings(const Napi::CallbackInfo &info);
3738
static void SetLegacySettings(const Napi::CallbackInfo &info, const Napi::Value &value);
3839
};
39-
}
40+
}

obs-studio-client/source/streaming.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,4 @@ class Streaming : public WorkerSignals {
6565
void Start(const Napi::CallbackInfo &info);
6666
void Stop(const Napi::CallbackInfo &info);
6767
};
68-
}
68+
}

obs-studio-server/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ SET(osn-server_SOURCES
323323
"${PROJECT_SOURCE_DIR}/source/osn-simple-streaming.hpp"
324324
"${PROJECT_SOURCE_DIR}/source/osn-advanced-streaming.cpp"
325325
"${PROJECT_SOURCE_DIR}/source/osn-advanced-streaming.hpp"
326+
"${PROJECT_SOURCE_DIR}/source/osn-enhanced-broadcasting-display-stats-tracker.cpp"
327+
"${PROJECT_SOURCE_DIR}/source/osn-enhanced-broadcasting-display-stats-tracker.hpp"
326328
"${PROJECT_SOURCE_DIR}/source/osn-enhanced-broadcasting-advanced-streaming.cpp"
327329
"${PROJECT_SOURCE_DIR}/source/osn-enhanced-broadcasting-advanced-streaming.hpp"
328330
"${PROJECT_SOURCE_DIR}/source/osn-enhanced-broadcasting-simple-streaming.cpp"
@@ -660,4 +662,4 @@ if(BUILD_TESTING)
660662
DL_PATHS
661663
${_osn_server_test_dl_paths}
662664
)
663-
endif()
665+
endif()

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ void osn::IEnhancedBroadcastingAdvancedStreaming::Register(ipc::server &srv)
7070
cls->register_function(std::make_shared<ipc::function>("GetTotalFrames", std::vector<ipc::type>{ipc::type::UInt64}, GetTotalFrames));
7171
cls->register_function(std::make_shared<ipc::function>("GetKBitsPerSec", std::vector<ipc::type>{ipc::type::UInt64}, GetKBitsPerSec));
7272
cls->register_function(std::make_shared<ipc::function>("GetDataOutput", std::vector<ipc::type>{ipc::type::UInt64}, GetDataOutput));
73+
cls->register_function(std::make_shared<ipc::function>(
74+
"GetDisplayStats", std::vector<ipc::type>{ipc::type::UInt64},
75+
EnhancedBroadcasting<AdvancedStreaming>::GetDisplayStats<EnhancedBroadcastingAdvancedStreaming, IEnhancedBroadcastingAdvancedStreaming>));
7376

7477
cls->register_function(
7578
std::make_shared<ipc::function>("GetAdditionalVideoCanvas", std::vector<ipc::type>{ipc::type::UInt64}, GetAdditionalVideoCanvas));
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include "osn-enhanced-broadcasting-display-stats-tracker.hpp"
2+
3+
#include <limits>
4+
5+
#include <util/platform.h>
6+
7+
namespace osn {
8+
9+
void enhanced_broadcasting_display_stats_callback(obs_output_t *, encoder_packet *packet, encoder_packet_time *, void *param)
10+
{
11+
if (!packet || packet->type != OBS_ENCODER_VIDEO)
12+
return;
13+
14+
auto *tracker = static_cast<EnhancedBroadcastingDisplayStatsTracker *>(param);
15+
if (!tracker)
16+
return;
17+
18+
tracker->AddVideoPacketBytes(packet->track_idx, packet->size);
19+
}
20+
21+
EnhancedBroadcastingDisplayStatsTracker::EnhancedBroadcastingDisplayStatsTracker(const Config &config)
22+
{
23+
trackCanvasIndices.reserve(config.encoder_configurations.size());
24+
for (const auto &encoderConfig : config.encoder_configurations) {
25+
trackCanvasIndices.push_back(encoderConfig.canvas_index);
26+
}
27+
28+
const uint64_t now = os_gettime_ns();
29+
for (auto &counter : counters) {
30+
counter.lastBytesSentTime.store(now);
31+
}
32+
}
33+
34+
void EnhancedBroadcastingDisplayStatsTracker::AddVideoPacketBytes(size_t trackIndex, size_t bytes)
35+
{
36+
if (trackIndex >= trackCanvasIndices.size())
37+
return;
38+
39+
const uint32_t canvasIndex = trackCanvasIndices[trackIndex];
40+
if (canvasIndex >= counters.size())
41+
return;
42+
43+
counters[canvasIndex].totalBytes.fetch_add(bytes, std::memory_order_relaxed);
44+
}
45+
46+
EnhancedBroadcastingPerDisplayStats EnhancedBroadcastingDisplayStatsTracker::CalculateStats()
47+
{
48+
return {CalculateDisplayStats(0), CalculateDisplayStats(1)};
49+
}
50+
51+
EnhancedBroadcastingDisplayStats EnhancedBroadcastingDisplayStatsTracker::CalculateDisplayStats(size_t canvasIndex)
52+
{
53+
EnhancedBroadcastingDisplayStats result;
54+
if (canvasIndex >= counters.size())
55+
return result;
56+
57+
auto &counter = counters[canvasIndex];
58+
const uint64_t bytesSent = counter.totalBytes.load(std::memory_order_relaxed);
59+
const uint64_t bytesSentTime = os_gettime_ns();
60+
const uint64_t lastBytesSent = counter.lastBytesSent.exchange(bytesSent);
61+
const uint64_t lastBytesSentTime = counter.lastBytesSentTime.exchange(bytesSentTime);
62+
63+
if (bytesSent >= lastBytesSent && lastBytesSentTime != 0) {
64+
const uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
65+
const double timePassed = double(bytesSentTime - lastBytesSentTime) / 1000000000.0;
66+
if (timePassed > std::numeric_limits<double>::epsilon())
67+
result.kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
68+
}
69+
70+
result.dataOutput = bytesSent / (1024.0 * 1024.0);
71+
return result;
72+
}
73+
74+
}

0 commit comments

Comments
 (0)