Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ on `Call` (`get_call_participant_session_metrics`,
- A VP8 or VP9 track continues its picture IDs when its encoder is set up again,
for example after a size or bitrate change. Before, the picture ID jumped to a
new value on the same SSRC, which RFC 7741 does not permit.
- The vendored SFU protocol removes the deprecated `SendStatsRequest` fields
`subscriber_stats`, `publisher_stats`, `audio_devices`, `video_devices`,
`subscriber_rtc_stats` and `publisher_rtc_stats`. A stats report sends the
`get_stats()` samples only in the `getstats` records of `rtc_stats`. Before,
it also sent them as JSON strings in `subscriber_stats` and
`publisher_stats`.

# v0.1.0-preview.2

Expand Down
7 changes: 1 addition & 6 deletions proto/video/sfu/signal_rpc/signal.proto
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,18 @@ message SendMetricsResponse {

message SendStatsRequest {
string session_id = 1;
string subscriber_stats = 2;
string publisher_stats = 3;
reserved 2, 3, 7, 8, 13, 14;
string webrtc_version = 4;
string sdk = 5;
string sdk_version = 6;

models.InputDevices audio_devices = 7;
models.InputDevices video_devices = 8;
oneof device_state {
models.AndroidState android = 9;
models.AppleState apple = 10;
}

Telemetry telemetry = 11;
models.RTMPIngress rtmp = 12;
string subscriber_rtc_stats = 13 [deprecated = true];
string publisher_rtc_stats = 14 [deprecated = true];
string rtc_stats = 15;
// Encode stats for the publisher
repeated models.PerformanceStats encode_stats = 16;
Expand Down
33 changes: 13 additions & 20 deletions src/rtc/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
//! Ported from stream-video-js `stats/SfuStatsReporter.ts`. The reporter drains
//! the signal / publisher / subscriber [`Tracer`]s into the `rtc_stats` JSON
//! array (ICE / track / PC-state / RPC events), samples each PeerConnection's
//! `get_stats()` into `{subscriber,publisher}_stats` plus a `getstats` trace
//! record, and posts a fully-populated [`SendStatsRequest`] over the Twirp
//! signal client. The loop is interval-driven (no busy-wait) and is drained one
//! final time on leave / reconnect so end-of-call events are recorded.
//! `get_stats()` into a `getstats` trace record, and posts a fully-populated
//! [`SendStatsRequest`] over the Twirp signal client. The loop is
//! interval-driven (no busy-wait) and is drained one final time on leave /
//! reconnect so end-of-call events are recorded.
//!
//! Cadence is driven by the coordinator's cached `stats_options`
//! (`reporting_interval_ms`); when the coordinator leaves it unset we default it
Expand Down Expand Up @@ -154,8 +154,8 @@ impl StatsReporter {
let _guard = self.send_lock.lock().await;

tracing::debug!(session_id = %self.session_id, "stream.rtc.stats.report_start");
let (subscriber_stats, subscriber_flat) = collect_stats(&self.subscriber).await;
let (publisher_stats, publisher_flat) = collect_stats(&self.publisher).await;
let subscriber_flat = collect_stats(&self.subscriber).await;
let publisher_flat = collect_stats(&self.publisher).await;
let ts = now_ms();

let signal_traces = self.signal_tracer.take();
Expand Down Expand Up @@ -184,8 +184,6 @@ impl StatsReporter {
let request = SendStatsRequest {
session_id: self.session_id.clone(),
unified_session_id: self.unified_session_id.clone(),
subscriber_stats,
publisher_stats,
webrtc_version: identity::WEBRTC_VERSION.to_owned(),
sdk: identity::sdk_name().to_owned(),
sdk_version: identity::sdk_version(),
Expand Down Expand Up @@ -261,26 +259,21 @@ fn getstats_record(id: Option<&str>, flattened: Value, ts: i64) -> TraceRecord {
TraceRecord("getstats".to_owned(), id.map(str::to_owned), flattened, ts)
}

/// Collect `pc.get_stats()` (time-boxed) as `(json_string, flattened_value)`.
///
/// The string form fills `SendStats.{subscriber,publisher}_stats`; the
/// flattened value rides in the `getstats` `rtc_stats` record. `flatten`
/// mirrors JS: the stats-report map becomes an array of stat objects.
async fn collect_stats(pc: &Arc<RTCPeerConnection>) -> (String, Value) {
/// Collect `pc.get_stats()` (time-boxed), flattened for the `getstats`
/// `rtc_stats` record. `flatten` mirrors JS: the stats-report map becomes an
/// array of stat objects.
async fn collect_stats(pc: &Arc<RTCPeerConnection>) -> Value {
let report = match tokio::time::timeout(GET_STATS_TIMEOUT, pc.get_stats()).await {
Ok(report) => report,
Err(_) => {
tracing::debug!("stream.rtc.stats.get_stats_timed_out");
return ("[]".to_owned(), Value::Array(Vec::new()));
return Value::Array(Vec::new());
}
};
let value = serde_json::to_value(&report).unwrap_or(Value::Null);
let flattened = match value {
match serde_json::to_value(&report).unwrap_or(Value::Null) {
Value::Object(map) => Value::Array(map.into_values().collect()),
other => other,
};
let as_string = serde_json::to_string(&flattened).unwrap_or_else(|_| "[]".to_owned());
(as_string, flattened)
}
}

#[cfg(test)]
Expand Down
Loading