Skip to content

Commit f2a6786

Browse files
iamfatnessclaude
andcommitted
feat(#535): snapshot sources[] node with framesIngested/droppedFrames (slice 0)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 7d50948 commit f2a6786

3 files changed

Lines changed: 62 additions & 0 deletions

File tree

‎native/src/core/MediaCore.cpp‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,28 @@ rpc::Json MediaCore::sessionState() const {
994994
{"showMeters", multiviewShowMeters_},
995995
{"showClock", multiviewShowClock_},
996996
});
997+
// Per-source bus health (#535 slice 0, Task 4): present even when empty —
998+
// the multiviewer-node rule, so the empty (no-source) state stays
999+
// observable rather than the node vanishing.
1000+
{
1001+
rpc::Json::Array sourcesArr;
1002+
if (sourceBus_) {
1003+
const int64_t nowNs = std::chrono::duration_cast<std::chrono::nanoseconds>(
1004+
std::chrono::steady_clock::now().time_since_epoch()).count();
1005+
for (const auto& s : sourceBus_->snapshot(nowNs)) {
1006+
sourcesArr.push_back(rpc::Json::Object{
1007+
{"sourceId", s.descriptor.sourceId},
1008+
{"kind", s.descriptor.kind},
1009+
{"width", static_cast<int>(s.descriptor.width)},
1010+
{"height", static_cast<int>(s.descriptor.height)},
1011+
{"framesIngested", static_cast<double>(s.counters.framesIngested)},
1012+
{"droppedFrames", static_cast<double>(s.counters.droppedFrames)},
1013+
{"health", sourceHealthName(s.health)},
1014+
});
1015+
}
1016+
}
1017+
state.emplace("sources", rpc::Json{sourcesArr});
1018+
}
9971019
const auto recording = recordingState(session);
9981020
if (!recording.isNull()) {
9991021
state.emplace("recording", recording);

‎native/src/core/SourceBus.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ namespace corevideo::core {
1212

1313
enum class SourceHealth { Producing, Warming, Stalled, Failed, Idle };
1414

15+
inline const char* sourceHealthName(SourceHealth health) {
16+
switch (health) {
17+
case SourceHealth::Producing: return "producing";
18+
case SourceHealth::Warming: return "warming";
19+
case SourceHealth::Stalled: return "stalled";
20+
case SourceHealth::Failed: return "failed";
21+
case SourceHealth::Idle: return "idle";
22+
}
23+
return "idle";
24+
}
25+
1526
struct SourceDescriptor {
1627
std::string sourceId; // canonical scheme:id == VideoFrame::participantId
1728
std::string kind; // "zoom" | "capture" | "media" | "composed" | "test"

‎native/tests/SourceBusTest.cpp‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,32 @@ TEST(SourceBusMediaCore, ATestPatternBusSourceCompositesIntoProgram) {
195195
static_cast<uint32_t>(px[c + 0]);
196196
EXPECT_NE(centerPixel, corevideo::compositor::colorFromParticipantId("test:pattern"));
197197
}
198+
199+
// --- Task 4: sessionState() sources[] node ---
200+
201+
TEST(SourceBusSnapshot, SourcesNodeCarriesPerSourceCounters) {
202+
corevideo::core::MediaCore core(corevideo::modules::createStubModules());
203+
core.addSourceForTest(std::make_shared<corevideo::core::TestPatternSource>("test:pattern"));
204+
205+
for (int i = 0; i < 3; ++i) {
206+
core.renderDisplayTick();
207+
}
208+
209+
const auto state = core.sessionState();
210+
const auto* sources = state.get("sources");
211+
ASSERT_NE(sources, nullptr);
212+
const auto& arr = sources->asArray();
213+
ASSERT_EQ(arr.size(), 1u);
214+
EXPECT_EQ(arr.front().getString("sourceId"), "test:pattern");
215+
EXPECT_GE(arr.front().get("framesIngested")->asNumber(), 1.0);
216+
EXPECT_EQ(arr.front().get("droppedFrames")->asNumber(), 0.0);
217+
}
218+
219+
TEST(SourceBusSnapshot, AFreshMediaCoreWithNoSourceStillEmitsAnEmptySourcesArray) {
220+
corevideo::core::MediaCore core(corevideo::modules::createStubModules());
221+
222+
const auto state = core.sessionState();
223+
const auto* sources = state.get("sources");
224+
ASSERT_NE(sources, nullptr);
225+
EXPECT_TRUE(sources->asArray().empty());
226+
}

0 commit comments

Comments
 (0)