The Vonage Video SDK exposes detailed stream-quality metrics through a high-level statistics API—recommended for most use cases—which provides audio, video, network, and sender-side statistics in a unified, session-aware form that remains stable across peer-connection transitions. For advanced debugging, the SDK also offers access to the raw WebRTC stats report, which reflects unprocessed peer-connection data.

Audio and video statistics API

The Vonage Video iOS SDK sends periodic audio and video network statistics for both publishers and subscribers. These include packet counts, bitrates, frame rate data, pause/freeze metrics, codec information, and optional sender-side network estimation.

Statistics are delivered through:

  • OTPublisherKitNetworkStatsDelegate — publisher-side stats

  • OTSubscriberKitNetworkStatsDelegate — subscriber-side stats

To receive them, enable the appropriate delegate on the publisher or subscriber.

Enabling statistics for publishers

Attach a class that adopts OTPublisherKitNetworkStatsDelegate:

@interface MyViewController () <OTPublisherKitDelegate, OTPublisherKitNetworkStatsDelegate>
@end

OTPublisher *publisher = [[OTPublisher alloc] initWithDelegate:self
                                                      settings:settings];
publisher.networkStatsDelegate = self;

Implement the callbacks:

- (void)publisher:(OTPublisherKit *)publisher
videoNetworkStatsUpdated:(NSArray<OTPublisherKitVideoNetworkStats *> *)statsArray {

    OTPublisherKitVideoNetworkStats *stats = statsArray.firstObject;
    if (!stats) return;

    // For routed sessions, the first element is sufficient.
    // For relayed sessions, iterate all elements if you want per-subscriber stats.
    NSString *connectionId = stats.connectionId ?: @"<none>";
    NSString *subscriberId = stats.subscriberId ?: @"<none>";

    NSLog(@"Publisher Video Stats for connectionId: %@, subscriberId: %@", connectionId, subscriberId);
    NSLog(@"Video bytes sent: %lld", stats.videoBytesSent);
    NSLog(@"Video packets sent: %lld", stats.videoPacketsSent);
    NSLog(@"Video packets lost: %lld", stats.videoPacketsLost);
    NSLog(@"Stats timestamp: %f ms", stats.timestamp);

    for (OTPublisherKitVideoLayerStats *layer in stats.videoLayers) {
        NSLog(@"Layer: %dx%d", layer.width, layer.height);
        NSLog(@"  Encoded FPS: %f", layer.encodedFrameRate);
        NSLog(@"  Bitrate: %lld bps", layer.bitrate);
        NSLog(@"  Total bitrate (incl. RTP overhead): %lld bps", layer.totalBitrate);
        NSLog(@"  Codec: %@", layer.codec ?: @"unknown");
        NSLog(@"  Scalability mode: %@", layer.scalabilityMode ?: @"none");
        NSLog(@"  Quality limitation: %@", @(layer.qualityLimitationReason));
    }

    if (stats.transport) {
        NSLog(@"Estimated uplink bandwidth: %lld bps", stats.transport.connectionEstimatedBandwidth);
    }
}

- (void)publisher:(OTPublisherKit *)publisher
audioNetworkStatsUpdated:(NSArray<OTPublisherKitAudioNetworkStats *> *)statsArray {

    OTPublisherKitAudioNetworkStats *stats = statsArray.firstObject;
    if (!stats) return;

    // For routed sessions, the first element is sufficient.
    // For relayed sessions, iterate all elements if you want per-subscriber stats.
    NSString *connectionId = stats.connectionId ?: @"<none>";
    NSString *subscriberId = stats.subscriberId ?: @"<none>";

    NSLog(@"Publisher Audio Stats for connectionId: %@, subscriberId: %@", connectionId, subscriberId);
    NSLog(@"Audio bytes sent: %lld", stats.audioBytesSent);
    NSLog(@"Audio packets sent: %lld", stats.audioPacketsSent);
    NSLog(@"Audio packets lost: %lld", stats.audioPacketsLost);
    NSLog(@"Stats timestamp: %f ms", stats.timestamp);
    
    if (stats.transport) {
        NSLog(@"Estimated uplink bandwidth: %lld bps", stats.transport.connectionEstimatedBandwidth);
    }
}

For a publisher in a routed session (one that uses the Vonage Video Media Router), the stats array includes one object, defining the statistics for the single audio or video media stream that is sent to the Vonage Video Media Router. In a relayed session, the stats array includes an object for each subscriber to the published stream.

Receiving video quality events on the publishers

If you are also interested in video quality events implement this callback:

- (void)publisher:(OTPublisherKit *)publisher
videoQualityChanged:(OTPublisherKitVideoNetworkStats *)stats
           reason:(OTPublisherVideoEventReason)reason {

    NSLog(@"Publisher video quality event: %ld", (long)reason);
}

Enabling statistics for subscribers

Attach a class that adopts OTSubscriberKitNetworkStatsDelegate:

@interface MyViewController () <OTSubscriberKitDelegate, OTSubscriberKitNetworkStatsDelegate>
@end

OTSubscriber *subscriber = [[OTSubscriber alloc] initWithStream:stream
                                                       delegate:self];
subscriber.networkStatsDelegate = self;
[session subscribe:subscriber error:nil];

Implement the callbacks:

- (void)subscriber:(OTSubscriberKit *)subscriber
videoNetworkStatsUpdated:(OTSubscriberKitVideoNetworkStats *)stats {
    NSLog(@"Video bytes received: %llu", stats.videoBytesReceived);
}

- (void)subscriber:(OTSubscriberKit *)subscriber
audioNetworkStatsUpdated:(OTSubscriberKitAudioNetworkStats *)stats {
    NSLog(@"Audio packets received: %llu", stats.audioPacketsReceived);
}

Receiving video quality events on the subscribers

Additionally handle subscriber video quality changed events:

- (void)subscriber:(OTSubscriberKit *)subscriber
videoQualityChanged:(OTSubscriberKitVideoNetworkStats *)stats
            reason:(OTSubscriberVideoEventReason)reason {

    NSLog(@"Subscriber video quality event: %ld", (long)reason);
}

Statistics data structures

This section outlines the structs and properties provided by the iOS audio and video statistics API. While all Video SDK platforms expose the same set statistics, there may be minor differences in how each platform structures or names individual fields. These variations reflect platform-specific SDK design conventions rather than differences in the underlying metrics.

OTTransportStats

Represents shared transport-level estimation.

  • connectionEstimatedBandwidth – Estimated available uplink connection bandwidth (bps).

OTPublisherKitVideoNetworkStats

Provides statistics about a publisher’s video track. It includes:

  • connectionId – In a relayed session, the connection ID of the client subscribing to the stream. Undefined in a routed session.
  • subscriberId – In a relayed session, the subscribed ID of the client subscribing to the stream. Undefined in a routed session.
  • videoPacketsLost – Estimated video packets lost.
  • videoPacketsSent – Video packets sent.
  • videoBytesSent – Video bytes sent.
  • timestamp – Unix timestamp in milliseconds when stats were gathered.
  • startTime – The timestamp, in milliseconds since the Unix epoch, from which the cumulative totals started accumulating.
  • videoLayers – The array of video layer statistics (see OTPublisherKitVideoLayerStats).
  • transport – Tranport level network statistics.

OTPublisherKitAudioNetworkStats

Provides statistics about a publisher’s audio track. It includes:

  • connectionId – In a relayed session, the connection ID of the client subscribing to the stream. Undefined in a routed session.
  • subscriberId – In a relayed session, the subscribed ID of the client subscribing to the stream. Undefined in a routed session.
  • audioPacketsLost – Estimated packets lost.
  • audioPacketsSent – Audio packets sent.
  • audioBytesSent – Audio bytes sent.
  • timestamp – Unix timestamp in milliseconds.
  • startTime – The timestamp, in milliseconds since the Unix epoch, from which the cumulative totals started accumulating.
  • transport – Tranport level network statistics.

OTPublisherKitVideoLayerStats

Represents one simulcast layer or SVC layer.

  • width – Encoded frame width.
  • height – Encoded frame height.
  • encodedFrameRate – Encoded frames per second.
  • bitrate – Layer bitrate (bps).
  • totalBitrate – Layer bitrate including RTP overhead (bps).
  • scalabilityMode – SVC/scalability descriptor (e.g., "L3T3").
  • qualityLimitationReason – Reason for quality limitation (bandwidth, CPU, codec, resolution, or layer change).
  • codec – The codec used by this video layer.

OTSenderStats

Sender-side estimation metrics (mirrored on both audio and video).

  • connectionMaxAllocatedBitrate – Maximum bitrate estimated for the sender connection.
  • connectionEstimatedBandwidth – Current bandwidth estimation (bps).

OTSubscriberKitVideoNetworkStats

Provides statistics about a subscriber’s video track. It includes:

  • videoPacketsLost – Estimated video packets lost.
  • videoPacketsReceived – Video packets received.
  • videoBytesReceived – Video bytes received.
  • timestamp – Unix timestamp in milliseconds when stats were gathered.
  • senderStats – Sender-side metrics (optional).
  • width – Decoded frame width in pixels.
  • height – Decoded frame height in pixels.
  • decodedFrameRate – Decoded frames per second.
  • bitrate – Video bitrate (bps).
  • totalBitrate – Bitrate including RTP overhead (bps).
  • pauseCount – Number of pauses (>5s since last frame). Includes intentional disables and audio-fallback cases.
  • totalPausesDuration – Total pause duration (ms).
  • freezeCount – Freeze count (WebRTC-defined freeze event).
  • totalFreezesDuration – Total freeze duration (ms).
  • codec – Current decoder codec.

OTSubscriberKitAudioNetworkStats

Provides statistics about a subscriber’s audio track. It includes:

  • audioPacketsLost – Estimated packets lost.
  • audioPacketsReceived – Packets received.
  • audioBytesReceived – Bytes received.
  • timestamp – Unix timestamp in milliseconds.
  • senderStats – Sender-side metrics (optional).

Sender-side statistics

See the sender-side statistics overview.

Enabling sender-side statistics

Sender-side statistics are received on the subscribers. 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

If the stream’s publisher has enabled sender-side statistics, subscribers start receiving them automatically once a listener is registered for video or audio stats, as described above.

Implement the delegate method for video stats:

- (void)subscriber:(OTSubscriberKit *)subscriber
videoNetworkStatsUpdated:(OTSubscriberKitVideoNetworkStats *)stats
{
    // 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 connection
  • connectionEstimatedBandwidth — 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.

RTC stats report

To get publisher low-level peer connection statistics, use the [OTPublisherKit getRtcStatsReport:] method. This provides RTC stats reports for the media stream.

This is an asynchronous operation. Set the >[OTPublisherKit rtcStatsReportDelegate]> property and implement the >[OTPublisherKitRtcStatsReportDelegate publisher:rtcStatsReport:]> method prior to calling [OTPublisherKit getRtcStatsReport:].

When the stats are available, the implementation of the >[OTPublisherKitRtcStatsReportDelegate publisher:rtcStatsReport:]> message is sent. The message includes an array of OTPublisherRtcStats objects, which includes a jsonArrayOfReports property.

This is a JSON array of RTC stats reports, which are similar to the format the RtcStatsReport object implemented in web browsers (see these Mozilla docs).

To get subscriber low-level peer connection statistics, use the [OTSubscriberKit getRtcStatsReport:] method. This provides an RTC stats report for the media stream.

This is an asynchronous operation. Set the [OTSubscriberKit rtcStatsReportDelegate]> property and implement the >[OTSubscriberKitRtcStatsReportDelegate subscriber:rtcStatsReport:]> method prior to calling [OTSubscriberKit getRtcStatsReport:].

When the stats are available, the implementation of the >[OTSubscriberKitRtcStatsReportDelegate subscriber:rtcStatsReport:]> message is sent. The message includes an a jsonArrayOfReports parameter.

This is a JSON array of RTC stats reports, which are similar to the format the RtcStatsReport object implemented in web browsers (see these Mozilla docs).

Also see this W3C documentation.

Sample

The Vonage Video iOS SDK Client Observability Sample App demonstrates client observability features in a mobile app built with the iOS client SDK.