Client Observability
Learn how to use the Vonage Video Client SDK Client Observability API to access a complete set of real-time statistics, including sender, receiver, and network metrics.
Vonage Video SDKs provide calls to access real-time network and media statistics in a video session, including sender-side statistics. These calls report detailed stream quality metrics —such as packet loss, data received, and bandwidth— and can be used on any publisher or subscribed stream.
Sender-side Statistics
During a call, the publisher transmits a media stream to one or more subscribers. The media may be relayed directly or processed through the Media Router. While subscribers can observe the bitrate of the stream they receive, they typically lack visibility into their total downlink capacity. The Sender-Side Statistics API addresses this limitation by providing metrics that help subscribers assess available bandwidth it has to receive media and optimize stream quality.
Sender-side Statistics is a beta feature.
A sender can be a Publisher or the Media Router, depending on whether media is flowing point-to-point or through the media router. The API is called Sender-side Statistics because the sender is the source of the reported metrics, delivered to the receiver which is the subscriber.
The API reports two key metrics per bundle (audio-video pair): the maximum bitrate the sender can estimate, and the current bandwidth estimation. The maximum bitrate is a cap on what can be estimated due to platform limitations. The current bandwidth estimation is the estimated downlink bandwidth of the WebRTC peer connection channel capacity that is available for media, independent of the stream's bitrate.
For example, a publisher may send a VGA stream using less than 1 Mbps, while the Media Router, providing the sender-side statistics, may estimate the current bandwidth at 8 Mbps, indicating additional channel capacity. This information can be used by the application to adjust video layouts, or trigger policy-based actions such as Vonage Quality on Demand (QoD).
Note that its interpretation differs when a single peer connection session is created. In a single peer connection session, multiple audio+video bundles share the same connection, so the total bandwidth estimation should be calculated by summing the individual bundle estimations. In other words, the total bandwidth is shared between all subscribers in the single peer connection.
Supported statistics
The Sender-side statistics, include the following data, that can be used to determine the downlink estimation for a subscriber:
- The maximum bitrate that can be estimated for the connection.
- The current bandwidth estimation for the connection.
Use cases
Optimizing subscriber layouts: Use the Sender API’s estimated bandwidth together with the local end-point RTC statistics to display a high number of subscribers with good video quality.
Adaptive media mode: A subscriber can use the sender statistics determine if the sender’s estimated bandwidth exceeds a defined threshold (for example, 500 kbps) to decide whether to subscribe in video-only or audio-only mode.
Load scaling: Use the sender-side statistics to check if a subscriber can optimally handle an increase in load — such as switching from low-bitrate screen sharing to high-bitrate live video.
Threshold warnings: Use the sender-side statistics to trigger warnings for subscribers if the estimated bandwidth of a sender connection drops below a predefined threshold.
Quality on Demand (QoD) triggers: Use the sender-side statistics to trigger when the estimated network capacity for a subscribed stream falls below a given threshold on mobile network.
Steps to enable sender-side statistics
- Use the corresponding method in the client SDK to publish sender-side statistics.
- In subscribing clients, listen to the corresponding sender statistics events.
Enabling sender-side statistics
To receive sender-side statistics, enable them for the stream’s publisher by passing the publishSenderStats property set to true in the OT.initPublisher call:
const publisher = OT.initPublisher({
publishSenderStats: true
});
If publishSenderStats is not enabled, no sender statistics channel will be published for this publisher. The default value is false.
When initializing the Publisher object, set the audioFallback property of the option you pass into the OT.initPublisher() method:
Subscribing to sender-side statistics
Subscribers automatically receive sender statistics only if the publisher has enabled them and if the user calls Subscriber.getStats() at least once. Note that due to network latency, the first call to getStats may not include sender statistics. Subsequent calls are more likely to return this data.
No additional events or methods are required; the sender statistics are included in the existing stats object returned by getStats().
Receiving statistics events
Sender-side statistics are included as an optional senderStats object inside the stats object passed to the Subscriber.getStats() callback. The senderStats object contains two properties:
connectionMaxAllocatedBitrate— The maximum bitrate that can be estimated for the connection (in bits per second)connectionEstimatedBandwidth— The current estimated bandwidth for the connection (in bits per second)
These two metrics are calculated per audio-video bundle, so the same values appear in both video and audio statistics. Because they reflect the transport rather than individual tracks, the metrics are shared across both audio and video.
subscriber.getStats((stats) => {
console.log(`Packets received: ${stats.videoPacketsReceived}`);
console.log(`Packets lost: ${stats.videoPacketsLost}`);
console.log(`Bytes received: ${stats.videoBytesReceived}`);
console.log(`Timestamp: ${stats.timestamp}`);
if (stats.senderStats) {
console.log(`Connection max allocated bitrate: ${stats.senderStats.connectionMaxAllocatedBitrate} bps`);
console.log(`Connection current estimated bandwidth: ${stats.senderStats.connectionEstimatedBandwidth} bps`);
} else {
console.log("Sender stats not available yet.");
}
});
Known issues
In some cases, when the session is relayed —or in certain routed setups with only two participants— and the Publisher uses Firefox, sender-side statistics may not be available due to browser limitations.
Enabling sender-side statistics
To receive sender-side statistics, enable them for the stream’s publisher by setting the senderStatisticsTrack property to true when building the publisher:
Publisher publisher = new Publisher.Builder(context)
.senderStatsTrack(true) // Enable sender-side stats
.build();
If senderStatsTrack is not enabled, no sender statistics channel will be published for this publisher. The default value is false.
Subscribing to sender-side statistics
Subscribers automatically receive sender statistics only if the publisher has enabled them and if the subscriber registers a listener for network statistics events.
You can subscribe to video and audio stats using the corresponding listener interfaces:
subscriber.setVideoStatsListener((subscriber, stats) -> {
// Received stats
});
subscriber.setAudioStatsListener((subscriber, stats) -> {
// Received stats
});
Receiving statistics events
Sender-side statistics are delivered via the SubscriberKit.VideoStatsListener and SubscriberKit.AudioStatsListener callbacks for video and audio. The SubscriberKit.SubscriberVideoStats and SubscriberKit.SubscriberAudioStats classes each include these properties:
connectionMaxAllocatedBitrate— The maximum bitrate that can be estimated for the connectionconnectionEstimatedBandwidth— The current estimated bitrate for the connection
These two metrics are calculated per audio-video bundle, so the same values appear in both video and audio statistics. Because they reflect the transport rather than individual tracks, the metrics are shared across both audio and video.
The stats object includes an optional senderStats field that provides the sender-side statistics. For example, when using SubscriberKit.setVideoStatsListener(), the stats parameter is a SubscriberKit.SubscriberVideoStats object:
subscriber.setVideoStatsListener((subscriber, stats) -> {
Log.d(TAG, "Packets received: " + stats.videoPacketsReceived);
Log.d(TAG, "Packets lost: " + stats.videoPacketsLost);
Log.d(TAG, "Bytes received: " + stats.videoBytesReceived);
Log.d(TAG, "Timestamp: " + stats.timeStamp);
if (stats.senderStats != null) {
Log.d(TAG, "Connection max allocated bitrate: " + stats.senderStats.connectionMaxAllocatedBitrate);
Log.d(TAG, "Connection current estimated bandwidth: " + stats.senderStats.connectionEstimatedBandwidth);
} else {
Log.d(TAG, "Sender stats not available yet.");
}
});
The same applies to audio stats using SubscriberKit.AudioStatsListener.
Enabling sender-side statistics
To receive sender-side statistics, enable them for the stream’s publisher by setting the senderStatsTrack property to true for the OTPublisherKitSettings object used to create the publisher.
let settings = OTPublisherSettings()
settings.senderStatsTrack = true // Enable sender-side stats
let publisher = OTPublisher(delegate: self, settings: settings)
If senderStatsTrack is not enabled, no sender statistics channel will be published for this publisher. The default value is false.
Subscribing to sender-side statistics
To receive sender statistics, set the networkStatsDelegate for a subscriber:
let subscriber = OTSubscriber(stream: stream, delegate: self)
subscriber?.networkStatsDelegate = self
Then implement the listener for either video or audio stats:
extension MyAppController: OTSubscriberKitNetworkStatsDelegate {
func subscriber(_ subscriber: OTSubscriberKit, videoNetworkStatsUpdated stats: OTSubscriberKitVideoNetworkStats) {
guard let senderStats = stats.senderStats else { return }
// Use stats
}
func subscriber(_ subscriber: OTSubscriberKit, audioNetworkStatsUpdated stats: OTSubscriberKitAudioNetworkStats) {
guard let senderStats = stats.senderStats else { return }
// Use stats
}
}
Receiving statistics events
The OTSubscriberKitNetworkStatsDelegate callbacks for video and audio deliver sender-side statistics, as shown above. The OTSenderStats, included as the senderStats member in both OTSubscriberKitVideoNetworkStats and OTSubscriberKitAudioNetworkStats, provides two properties:
connectionMaxAllocatedBitrate— The maximum bitrate estimated for the connectionconnectionEstimatedBandwidth— The current estimated bandwidth for the connection
These two metrics are calculated per audio-video bundle, so the same values appear in both video and audio statistics. Because they reflect the transport rather than individual tracks, the metrics are shared across both audio and video.
extension MyAppController: OTSubscriberKitNetworkStatsDelegate {
func subscriber(_ subscriber: OTSubscriberKit, videoNetworkStatsUpdated stats: OTSubscriberKitVideoNetworkStats) {
guard let senderStats = stats.senderStats else { return }
print("Sender max allocated bitrate: \(senderStats.connectionMaxAllocatedBitrate)")
print("Sender current estimated bandwidth: \(senderStats.connectionEstimatedBandwidth)")
}
}
Sample app
See the vonage-video-ios-sdk-samples sender-side statistics sample.
Enabling sender-side statistics
To receive sender-side statistics, enable them for the stream’s publisher by setting the senderStatsTrack property to true for the OTPublisherKitSettings object used to create the publisher.
OTPublisherKitSettings *settings = [[OTPublisherKitSettings alloc] init];
settings.senderStatsTrack = YES;
OTPublisher *publisher = [[OTPublisher alloc] initWithDelegate:self
settings:settings];
If senderStatsTrack is not enabled, no sender statistics channel will be published for this publisher. The default value is NO.
Subscribing to sender-side statistics
Subscribers automatically receive sender-side statistics when you register a listener for either video or audio stats.
Example: enabling video stats subscription with sender-side fields:
// In your .h or class extension: adopt the protocol
@interface MyViewController () <OTSubscriberKitNetworkStatsDelegate>
@end
// When creating the OTSubscriber:
OTSubscriber *subscriber = [[OTSubscriber alloc] initWithStream:stream delegate:self];
subscriber.networkStatsDelegate = self;
NSError *err = nil;
[session subscribe:subscriber error:&err];
Implement the delegate method for video stats:
- (void)subscriber:(OTSubscriberKit *)subscriber
videoNetworkStatsUpdated:(OTSubscriberKitVideoNetworkStats *)stats
{
NSLog(@"Video packets received: %llu", (unsigned long long)stats.videoPacketsReceived);
NSLog(@"Video packets lost: %llu", (unsigned long long)stats.videoPacketsLost);
NSLog(@"Video bytes received: %llu", (unsigned long long)stats.videoBytesReceived);
NSLog(@"Timestamp (ms): %llu", (unsigned long long)stats.timestamp);
// The property may be nil if no sender statistics have been received yet.
if (stats.senderStats) {
OTSenderStats *sender = stats.senderStats;
NSLog(@"Connection max allocated bitrate: %lld bps", (long long)sender.connectionMaxAllocatedBitrate);
NSLog(@"Connection current estimated bandwidth: %lld bps", (long long)sender.connectionEstimatedBandwidth);
} else {
NSLog(@"Sender-side stats not available yet.");
}
}
Similarly implement -subscriber:audioNetworkStatsUpdated: for audio stats, which also includes a senderStats property.
Receiving statistics events
Sender-side statistics are delivered via the OTSubscriberKitNetworkStatsDelegate callbacks for video and audio, as shown above. The OTSenderStats, included as the senderStats member in both OTSubscriberKitVideoNetworkStats and OTSubscriberKitAudioNetworkStats, provides two properties:
connectionMaxAllocatedBitrate— The maximum bitrate that can be estimated for the connectionconnectionEstimatedBandwidth— The current estimated bandwidth for the connection
These two metrics are calculated per audio-video bundle, so the same values appear in both video and audio statistics. Because they reflect the transport rather than individual tracks, the metrics are shared across both audio and video.
Enabling sender-side statistics
To receive sender-side statistics, enable them for the stream’s publisher by setting the HasSenderStatsTrack property to true when building the publisher:
var publisherBuilder = new Publisher.Builder()
{
HasSenderStatsTrack = true
};
Publisher publisher = publisherBuilder.Build();
If HasSenderStatsTrack is not enabled, no sender statistics channel will be published for this publisher. The default value is false.
Subscribing to sender-side statistics
Subscribers automatically receive sender statistics only if the publisher has enabled them and if the subscriber registers a listener for network statistics events.
You can subscribe to video and audio stats using the corresponding event handlers:
subscriber.VideoStatsUpdated += (sender, stats) =>
{
// Received video stats
};
subscriber.AudioStatsUpdated += (sender, stats) =>
{
// Received audio stats
};
Receiving statistics events
Sender-side statistics are delivered via the VideoStatsUpdated and AudioStatsUpdated events for video and audio. The SenderStats class, included in both VideoNetworkStatsEventArgs and AudioNetworkStatsEventArgs, provides two properties:
ConnectionMaxAllocatedBitrate— The maximum bitrate that can be estimated for the connectionConnectionEstimatedBandwidth— The current estimated bandwidth for the connection
These two metrics are calculated per audio-video bundle, so the same values appear in both video and audio statistics. Because they reflect the transport rather than individual tracks, the metrics are shared across both audio and video.
Inside the stats event handlers, you can access the sender-side metrics through the optional SenderStats property:
subscriber.VideoStatsUpdated += (sender, stats) =>
{
Console.WriteLine($"Packets received: {stats.PacketsReceived}");
Console.WriteLine($"Packets lost: {stats.PacketsLost}");
Console.WriteLine($"Bytes received: {stats.BytesReceived}");
Console.WriteLine($"Timestamp: {stats.Timestamp}");
if (stats.SenderStats != null)
{
Console.WriteLine($"Connection max allocated bitrate: {stats.SenderStats.ConnectionMaxAllocatedBitrate}");
Console.WriteLine($"Connection current estimated bandwidth: {stats.SenderStats.ConnectionEstimatedBandwidth}");
}
else
{
Console.WriteLine("Sender stats not available yet.");
}
};
The same approach applies to audio stats using the AudioStatsUpdated event.
Enabling sender-side statistics
To receive sender-side statistics, enable them for the stream’s publisher by calling otc_publisher_settings_set_sender_stats_track() function before constructing the publisher:
otc_publisher_settings* settings = otc_publisher_settings_new();
otc_publisher_settings_set_sender_stats_track(settings, OTC_TRUE);
otc_publisher* publisher = otc_publisher_new_with_settings( &publisher_callbacks, settings);
If the sender stats track is not enabled, no sender statistics channel will be published for this publisher. The default value is OTC_FALSE.
Subscribing to sender-side statistics
Subscribers automatically receive sender statistics when you register for the subscriber’s video or audio statistics callbacks and the publisher is sending them.
Example: enabling video statistics subscription with sender-side fields:
subscriber_callbacks.on_video_stats = on_video_stats;
otc_subscriber* subscriber = otc_subscriber_new(stream, &subscriber_callbacks);
otc_session_subscribe(session, subscriber);
Receiving statistics events
Sender-side statistics are delivered as part of the existing subscriber stats callbacks. Specific fields are included in the otc_subscriber_video_stats and otc_subscriber_audio_stats structures:
sender_connection_max_allocated_bitrate— The maximum bitrate that can be estimated for the connectionsender_connection_estimated_bandwidth— The current estimated bandwidth for the connection
These two metrics are calculated per audio-video bundle, so the same values appear in both video and audio statistics. Because they reflect the transport rather than individual tracks, the metrics are shared across both audio and video.
static void on_video_stats(otc_subscriber* subscriber,
void* user_data,
struct otc_subscriber_video_stats stats) {
printf("Packets received: %llu\n", (unsigned long long)stats.packets_received);
printf("Packets lost: %llu\n", (unsigned long long)stats.packets_lost);
printf("Bytes received: %llu\n", (unsigned long long)stats.bytes_received);
printf("Timestamp: %llu ms\n", (unsigned long long)stats.timestamp);
printf("Connection max allocated bitrate: %lld bps\n", (long long)stats.sender_connection_max_allocated_bitrate);
printf("Connection estimated bandwidth: %lld bps\n", (long long)stats.sender_connection_estimated_bandwidth);
}
Notes
- Depending on the SDK, sender-side statistics may not be immediately available the first time stats are requested or on the first stats event after subscription, due to network latency.
- If you create a single peer connection session, the peer connection bandwidth is shared across all subscribers. The maximum bitrate represents the highest bitrate the peer connection can estimate, while the current bitrate reflects each audio-video bundle’s bitrate. All subscribers in the single peer connection share this maximum bitrate. When assessing the sender’s available bandwidth, take this into account. For example, a current bitrate of 2 Mbps may indicate good quality if multiple subscribers share the same single peer connection.