クライアントの観測可能性:アンドロイド

Vonage Video SDKは、ほとんどのユースケースで推奨される高レベルの統計APIを通じて、詳細なストリーム品質メトリクスを公開します。このAPIは、音声、ビデオ、ネットワーク、および送信者側の統計を、ピア接続の遷移をまたいで安定したまま、統一されたセッション認識形式で提供します。高度なデバッグのために、SDKは未処理のピア接続データを反映する生のWebRTC統計レポートへのアクセスも提供します。

SDKはまた、パブリッシャーとサブスクライバーの両方に対して、接続の健全性をハイレベルで評価するネットワーク・コンディション・メトリクスを公開します。これらのメトリクスには、ネットワーク・コンディション・スコア、そのスコアの理由、およびサブスクライバについては、観察された問題の原因が接続のどちらにあるかを示すデグラデーション・ソースが含まれます。参照 ネットワークの状態と劣化の原因 詳細はこちら。

オーディオ、ビデオ、メディアリンク統計API

Vonage Video Android SDKは、パブリッシャーとサブスクライバーの両方に対して、オーディオ、ビデオ、メディアリンクの統計情報を定期的に送信します。これらには、パケット数、ビットレート、フレームレートデータ、一時停止/フリーズメトリクス、コーデック情報、および帯域幅推定やネットワーク状態スコアリングなどのトランスポートレベルのネットワークメトリクスが含まれます。

統計は個別のリスナーを通じて配信される:

出版社

  • PublisherKit.VideoStatsListener - ビデオ統計とビデオ品質イベント
  • PublisherKit.AudioStatsListener - オーディオ統計
  • PublisherKit.MediaLinkStatsListener - メディアリンク(トランスポートレベル)統計

加入者

  • SubscriberKit.VideoStatsListener - ビデオ統計とビデオ品質イベント
  • SubscriberKit.AudioStatsListener - オーディオ統計
  • SubscriberKit.MediaLinkStatsListener - メディアリンク(トランスポートレベル)統計

パブリッシャー向け統計の有効化

ビデオ統計

publisher.setVideoStatsListener(new PublisherKit.VideoStatsListener() {
    @Override
    public void onVideoStats(PublisherKit publisher, PublisherKit.PublisherVideoStats[] statsArray) {
        if (statsArray != null && statsArray.length > 0) {
            // For routed sessions, first element is sufficient.
            // For relayed sessions, iterate all statsArray elements to get per-subscriber info.
            PublisherKit.PublisherVideoStats stats = statsArray[0];

            String connectionId = (stats.connectionId != null && !stats.connectionId.isEmpty())
                    ? stats.connectionId
                    : "<none>";
            String subscriberId = (stats.subscriberId != null && !stats.subscriberId.isEmpty())
                    ? stats.subscriberId
                    : "<none>";

            Log.d("VideoStats", "Publisher Video Stats for connectionId: " + connectionId
                    + ", subscriberId: " + subscriberId);

            Log.d("VideoStats", "Video bytes sent: " + stats.videoBytesSent);
            Log.d("VideoStats", "Video packets sent: " + stats.videoPacketsSent);
            Log.d("VideoStats", "Video packets lost: " + stats.videoPacketsLost);
            Log.d("VideoStats", "Stats timestamp: " + stats.timeStamp + " ms");

            if (stats.videoLayers != null) {
                for (PublisherKit.VideoLayerStats layer : stats.videoLayers) {
                    Log.d("VideoStats", "Layer: " + layer.width + "x" + layer.height);
                    Log.d("VideoStats", "  Encoded FPS: " + layer.encodedFrameRate);
                    Log.d("VideoStats", "  Bitrate: " + layer.bitrate + " bps");
                    Log.d("VideoStats", "  Total bitrate (incl. RTP overhead): " + layer.totalBitrate + " bps");
                    Log.d("VideoStats", "  Codec: " + (layer.codec != null ? layer.codec : "unknown"));
                    Log.d("VideoStats", "  Scalability mode: " + (layer.scalabilityMode != null ? layer.scalabilityMode : "none"));
                    Log.d("VideoStats", "  Quality limitation: " + layer.qualityLimitationReason);
                }
            }
        }
    }

    @Override
    public void onVideoQualityChanged(PublisherKit publisher, PublisherKit.PublisherVideoStats stats, String reason) {
        Log.d("Stats", "Publisher video quality event: " + reason);
    }
});

オーディオ統計

publisher.setAudioStatsListener(new PublisherKit.AudioStatsListener() {
    @Override
    public void onAudioStats(PublisherKit publisher, PublisherKit.PublisherAudioStats[] statsArray) {
        if (statsArray != null && statsArray.length > 0) {
            // For routed sessions, first element is sufficient.
            // For relayed sessions, iterate all statsArray elements to get per-subscriber info.
            PublisherKit.PublisherAudioStats stats = statsArray[0];

            String connectionId = (stats.connectionId != null && !stats.connectionId.isEmpty())
                    ? stats.connectionId
                    : "<none>";
            String subscriberId = (stats.subscriberId != null && !stats.subscriberId.isEmpty())
                    ? stats.subscriberId
                    : "<none>";

            Log.d("AudioStats", "Publisher Audio Stats for connectionId: " + connectionId
                    + ", subscriberId: " + subscriberId);

            Log.d("AudioStats", "Audio bytes sent: " + stats.audioBytesSent);
            Log.d("AudioStats", "Audio packets sent: " + stats.audioPacketsSent);
            Log.d("AudioStats", "Audio packets lost: " + stats.audioPacketsLost);
            Log.d("AudioStats", "Stats timestamp: " + stats.timeStamp + " ms");
        }
    }
});

メディアリンク統計

publisher.setMediaLinkStatsListener(new PublisherKit.MediaLinkStatsListener() {
    @Override
    public void onMediaLinkStats(PublisherKit publisher, PublisherKit.PublisherMediaLinkStats[] mediaLinkStats) {
        if (mediaLinkStats != null && mediaLinkStats.length > 0) {
            PublisherKit.PublisherMediaLinkStats stats = mediaLinkStats[0];
            if (stats.transport != null) {
                Log.d("MediaLinkStats", "Uplink bandwidth: " + stats.transport.getConnectionEstimatedBandwidth() + " bps");
                Log.d("MediaLinkStats", "Network condition: " + stats.transport.networkCondition);
                Log.d("MediaLinkStats", "Condition reason: " + stats.transport.networkConditionReason);
            }
        }
    }

    @Override
    public void onNetworkConditionChanged(PublisherKit publisher, PublisherKit.PublisherMediaLinkStats mediaLinkStats, String reason) {
        if (mediaLinkStats.transport != null) {
            Log.d("Stats", "Publisher network condition: " + mediaLinkStats.transport.networkCondition);
            Log.d("Stats", "Reason: " + mediaLinkStats.transport.networkConditionReason);
        }
    }
});

参照 ネットワークの状態と劣化の原因 ネットワーク・コンディション・スコアの解釈とその理由の詳細については、こちらをご覧ください。

ルーティングされたセッション( ビデオ・メディア・ルーター), の統計情報を定義する1つのオブジェクトが含まれます。 の統計情報を定義します。 中継セッションの場合、stats 配列には公開ストリームの加入者ごとのオブジェクトが含まれます。 オブジェクトが含まれます。

加入者統計の有効化

ビデオ統計

subscriber.setVideoStatsListener(new SubscriberKit.VideoStatsListener() {
    @Override
    public void onVideoStats(SubscriberKit subscriber, SubscriberKit.SubscriberVideoStats stats) {
        Log.d("Stats", "Video bytes received: " + stats.videoBytesReceived);
    }

    @Override
    public void onVideoQualityChanged(SubscriberKit subscriber, SubscriberKit.SubscriberVideoStats stats, String reason) {
        Log.d("Stats", "Subscriber video quality event: " + reason);
    }
});

オーディオ統計

subscriber.setAudioStatsListener(new SubscriberKit.AudioStatsListener() {
    @Override
    public void onAudioStats(SubscriberKit subscriber, SubscriberKit.SubscriberAudioStats stats) {
        Log.d("Stats", "Audio packets received: " + stats.audioPacketsReceived);
    }
});

メディアリンク統計

subscriber.setMediaLinkStatsListener(new SubscriberKit.MediaLinkStatsListener() {
    @Override
    public void onMediaLinkStats(SubscriberKit subscriber, SubscriberKit.SubscriberMediaLinkStats mediaLinkStats) {
        if (mediaLinkStats.transport != null) {
            Log.d("MediaLinkStats", "Downlink bandwidth: " + mediaLinkStats.transport.getConnectionEstimatedBandwidth() + " bps");
        }
        if (mediaLinkStats.remotePublisherTransport != null) {
            Log.d("MediaLinkStats", "Remote publisher uplink bandwidth: " + mediaLinkStats.remotePublisherTransport.getConnectionEstimatedBandwidth() + " bps");
        }
        Log.d("MediaLinkStats", "Degradation source: " + mediaLinkStats.networkDegradationSource);
    }

    @Override
    public void onNetworkConditionChanged(SubscriberKit subscriber, SubscriberKit.SubscriberMediaLinkStats mediaLinkStats, String reason) {
        if (mediaLinkStats.transport != null) {
            Log.d("Stats", "Local network condition: " + mediaLinkStats.transport.networkCondition);
        }
        if (mediaLinkStats.remotePublisherTransport != null) {
            Log.d("Stats", "Remote publisher network condition: " + mediaLinkStats.remotePublisherTransport.networkCondition);
        }
        Log.d("Stats", "Degradation source: " + mediaLinkStats.networkDegradationSource);
    }
});

参照 ネットワークの状態と劣化の原因 ネットワーク・コンディション・スコアの解釈とその理由の詳細については、こちらをご覧ください。

統計データ構造

このセクションでは、Android のオーディオおよびビデオ統計 API が提供する構造体とプロパティの概要を説明します。すべての Video SDK プラットフォームが同じ統計情報セットを公開していますが、各プラットフォームで個々のフィールドの構造や名前の付け方に若干の違いがある場合があります。これらの違いは、基本的なメトリクスの違いというよりも、プラットフォーム固有の SDK の設計規約を反映しています。

利用可能な統計とそれが表すものについてのプラットフォームに依存しない説明については、以下を参照のこと。 顧客の観測可能性の概要.

TransportStats

共有トランスポート・レベルのメトリクスを表します。

  • connectionEstimatedBandwidth - 利用可能な接続帯域幅の推定値(bps)。
  • networkCondition - 現在のネットワーク・コンディション・スコアNETWORK_CONDITION_UNKNOWN, NETWORK_CONDITION_CRITICAL, NETWORK_CONDITION_WARNING, NETWORK_CONDITION_FAIR, NETWORK_CONDITION_GOODあるいは NETWORK_CONDITION_EXCELLENT).
  • networkConditionReason - ネットワーク状況に影響を与える主な理由NETWORK_CONDITION_REASON_NONE, NETWORK_CONDITION_REASON_UNKNOWN, NETWORK_CONDITION_REASON_BANDWIDTHあるいは NETWORK_CONDITION_REASON_PACKET_LOSS).

PublisherKit.PublisherVideoStats

パブリッシャーのビデオトラックに関する統計を提供します。これには以下が含まれます:

  • connectionId - リレーセッションの場合、ストリームを購読しているクライアントのコネクションID。ルーティングされたセッションでは未定義。
  • subscriberId - リレーセッションの場合、ストリームを購読するクライアントの購読ID。ルーティングされたセッションでは未定義。
  • videoPacketsLost - 失われたビデオパケットの推定値。
  • videoPacketsSent - ビデオパケットの送信。
  • videoBytesSent - ビデオバイトが送信された。
  • timeStamp - 統計情報が収集されたミリ秒単位のUnixタイムスタンプ。
  • startTime - Unixエポックからのミリ秒単位のタイムスタンプ。
  • videoLayers - ビデオレイヤーの統計情報の配列 ( OTPublisherKitVideoLayerStats).

PublisherKit.PublisherAudioStats

パブリッシャーのオーディオトラックに関する統計を提供します。以下を含みます:

  • connectionId - リレーセッションの場合、ストリームを購読しているクライアントのコネクションID。ルーティングされたセッションでは未定義。
  • subscriberId - リレーセッションの場合、ストリームを購読するクライアントの購読ID。ルーティングされたセッションでは未定義。
  • audioPacketsLost - 失われたパケットの推定値。
  • audioPacketsSent - オーディオパケットを送信。
  • audioBytesSent - 送信されたオーディオ・バイト。
  • timeStamp - ミリ秒単位のUnixタイムスタンプ。
  • startTime - Unixエポックからのミリ秒単位のタイムスタンプ。

PublisherKit.VideoLayerStats

1つのサイマルキャスト・レイヤーまたはSVCレイヤーを表す。

  • width - エンコードされたフレーム幅。
  • height - エンコードされたフレームの高さ。
  • encodedFrameRate - エンコードされた1秒あたりのフレーム数。
  • bitrate - レイヤーのビットレート(bps)。
  • totalBitrate - RTPオーバーヘッドを含むレイヤービットレート(bps)。
  • scalabilityMode - SVC/スケーラビリティ記述子(例えば "L3T3")。
  • qualityLimitationReason - 品質制限の理由(帯域幅、CPU、コーデック、解像度、レイヤーの変更)。
  • codec - このビデオレイヤーが使用するコーデック。

SubscriberKit.SenderStats

送信側の推定メトリクス(オーディオとビデオの両方に反映される)。

  • connectionMaxAllocatedBitrate - 送信側接続で推定される最大ビットレート。
  • connectionEstimatedBandwidth - 現在の推定帯域幅(bps)。

SubscriberKit.SubscriberVideoStats

加入者のビデオトラックに関する統計情報を提供します。これには以下が含まれます:

  • videoPacketsLost - 失われたビデオパケットの推定値。
  • videoPacketsReceived - ビデオパケットを受信。
  • videoBytesReceived - 受信したビデオバイト。
  • timeStamp - 統計情報が収集されたミリ秒単位のUnixタイムスタンプ。
  • senderStats - 送信側のメトリクス(オプション)。
  • width - デコードされたフレーム幅(ピクセル)。
  • height - デコードされたフレームの高さ(ピクセル)。
  • decodedFrameRate - デコードされたフレーム/秒。
  • bitrate - ビデオ・ビットレート(bps)。
  • totalBitrate - RTPオーバーヘッドを含むビットレート(bps)。
  • pauseCount - ポーズ(最後のフレームから5秒以上)の数。意図的な無効化とオーディオフォールバックの場合を含む。
  • totalPausesDuration - 全休止時間(ms)。
  • freezeCount - フリーズカウント(WebRTC定義のフリーズイベント)。
  • totalFreezesDuration - 合計フリーズ時間(ms)。
  • codec - 現在のデコーダーコーデック。

SubscriberKit.SubscriberAudioStats

加入者のオーディオトラックに関する統計情報を提供します。これには以下が含まれます:

  • audioPacketsLost - 失われたパケットの推定値。
  • audioPacketsReceived - パケットを受信した。
  • audioBytesReceived - 受信したバイト数。
  • timeStamp - ミリ秒単位のUnixタイムスタンプ。
  • senderStats - 送信側のメトリクス(オプション)。

PublisherKit.PublisherMediaLinkStats

パブリッシャー接続のトランスポートレベルの統計情報を提供します。

  • transport - この出版社の輸送統計 TransportStats)

SubscriberKit.SubscriberMediaLinkStats

リモートパブリッシャーのネットワークパフォーマンスの可視性を含む、サブスクライバーの接続のトランスポートレベルの統計情報を提供します。これにより、アプリケーションは、接続の問題が加入者のダウンリンクに起因するのか、パブリッシャーのアップリンクに起因するのかを診断できます。

  • transport - このサブスクライバーのローカルトランスポートおよびネットワーク統計。送信側統計および/または音声フォールバックが無効になっている場合、制限されることがある。
  • remotePublisherTransport - リモートパブリッシャーのトランスポートとネットワークの統計。送信者側の統計や音声フォールバックが無効になっている場合は、 制限されるかもしれません。
  • networkDegradationSource - ネットワーク劣化の原因があればそれを示す (NetworkDegradationSource.NONE, NetworkDegradationSource.LOCAL, NetworkDegradationSource.REMOTEあるいは NetworkDegradationSource.BOTH_OR_UNCLEAR).

送信側統計

参照 送信側統計の概要.

送信側統計の有効化

送信者側の統計情報は、購読者が受け取る。送信者側の統計を受け取るには、ストリームのパブリッシャーで senderStatisticsTrack プロパティ true パブリッシャーを構築する際に

Publisher publisher = new Publisher.Builder(context)
        .senderStatsTrack(true) // Enable sender-side stats
        .build();

もし senderStatsTrack が有効でない場合、このパブリッシャーに対して送信者統計チャンネルは発行されません。デフォルト値は false.

送信側統計の受信

パブリッシャーが送信者側の統計情報を有効にしている場合、購読者はビデオとオーディオの統計情報コールバックを介して自動的に統計情報を受け取ります。 前述.その senderStats の両方のプロパティを持つ。 SubscriberKit.SubscriberVideoStats そして SubscriberKit.SubscriberAudioStats は2つの指標を提供する:

  • connectionMaxAllocatedBitrate - 接続時に推定可能な最大ビットレート
  • connectionEstimatedBandwidth - 接続の現在の推定帯域幅

これらのメトリクスは、オーディオとビデオのバンドルごとに計算されるため、ビデオとオーディオの両方の統計に同じ値が表示されます。

subscriber.setVideoStatsListener((subscriber, stats) -> {
    if (stats.senderStats != null) {
        Log.d(TAG, "Connection max allocated bitrate: " + stats.senderStats.connectionMaxAllocatedBitrate);
        Log.d(TAG, "Connection current estimated bandwidth: " + stats.senderStats.connectionEstimatedBandwidth);
    }
});

ネットワークの状態と劣化の原因

SDKは、パブリッシャーとサブスクライバーの両方に対して、コンディションのスコア、そのスコアを駆動する理由、サブスクライバーの劣化ソースを含むリアルタイムのネットワーク・コンディション・メトリクスを提供します。ネットワーク・コンディション・モデル、スコア、理由、およびそれを有効にする方法の詳細については 顧客の観測可能性の概要.

ネットワーク・コンディション・データは2つのチャンネルから入手できる:

  • 定期的な統計: メディアリンク統計イベントには、以下のトランスポートメトリクスが含まれます。 networkCondition そして networkConditionReason.購読者向けには、メディアリンクの統計も含まれる。 remotePublisherTransport そして networkDegradationSource.
  • ネットワーク状態の変更イベント: 両方の専用コールバック 出版社 そして 契約者 は、ネットワーク状態の重大な変化が検出されたときにトリガーされる。

以下の例では、加入者ネットワークの状態データを使用して、劣化の原因を特定する方法を示しています:

@Override
public void onNetworkConditionChanged(SubscriberKit subscriber, SubscriberKit.SubscriberMediaLinkStats mediaLinkStats, String reason) {

    int localCondition = mediaLinkStats.transport != null ? mediaLinkStats.transport.networkCondition : TransportStats.NETWORK_CONDITION_UNKNOWN;
    int remoteCondition = mediaLinkStats.remotePublisherTransport != null ? mediaLinkStats.remotePublisherTransport.networkCondition : TransportStats.NETWORK_CONDITION_UNKNOWN;
    int source = mediaLinkStats.networkDegradationSource;

    if (source == NetworkDegradationSource.LOCAL) {
        Log.d("Stats", "Local network is degraded (condition: " + localCondition + ")");
    } else if (source == NetworkDegradationSource.REMOTE) {
        Log.d("Stats", "Remote publisher network is degraded (condition: " + remoteCondition + ")");
    } else if (source == NetworkDegradationSource.BOTH_OR_UNCLEAR) {
        Log.d("Stats", "Degradation source unclear — local: " + localCondition + ", remote: " + remoteCondition);
    }
}

RTC統計レポート

パブリッシャーの低レベルのピア接続統計を取得するには PublisherKit.getRtcStatsReport() メソッドを使用します。これはメディアストリームのRTC統計レポートを提供する。これは非同期操作である。これは非同期操作である。 PublisherKit.setRtcStatsReportListener(PublisherKit.PublisherRtcStatsReportListener listener) メソッドを実装し PublisherKit.PublisherRtcStatsReportListener.onRtcStatsReport(PublisherKit publisher, PublisherKit.PublisherRtcStats[] stats) メソッドを呼び出す前に PublisherKit.getRtcStatsReport().

統計が利用可能な場合、実装の PublisherKit.PublisherRtcStatsReportListener.onRtcStatsReport(PublisherKit publisher, PublisherKit.PublisherRtcStats[] stats) メソッドが呼び出される。

の配列。 PublisherRtcStats オブジェクトがそのメソッドに渡される。その PublisherRtcStats オブジェクトには jsonArrayOfReports プロパティを使用します。これはRTC統計レポートのJSON配列で、ウェブ・ブラウザで実装されているRtcStatsReportオブジェクトのフォーマットに似ている( Mozillaのドキュメント).

サブスクライバの低レベルのピア接続統計を取得するには SubscriberKit.getRtcStatsReport() メソッドを使用します。これはメディアストリームのRTC統計レポートを提供する。

これは非同期操作である。これは非同期操作である。 SubscriberKit.setRtcStatsReportListener(SubscriberKit.SubscriberRtcStatsReportListener listener) メソッドを実装し SubscriberKit.SubscriberRtcStatsReportListener.onRtcStatsReport(SubscriberKit subscriber, java.lang.String jsonArrayOfReports) メソッドを呼び出す前に SubscriberKit.getRtcStatsReport().

統計が利用可能な場合、実装の SubscriberKit.SubscriberRtcStatsReportListener.onRtcStatsReport(SubscriberKit subscriber, java.lang.String jsonArrayOfReports) メソッドが呼び出される。この jsonArrayOfReports これは、ウェブブラウザで実装されているRtcStatsReportオブジェクトのフォーマットに似ています。 Mozillaのドキュメント).

こちらも参照 このW3Cドキュメント.