Observabilité côté client — Android

Le SDK Android d'OpenTok propose des appels permettant d'accéder aux statistiques réseau et multimédia en temps réel au cours d'une session vidéo. Ces appels fournissent des indicateurs détaillés sur la qualité du flux — tels que la perte de paquets, les données reçues et la bande passante — et peuvent être utilisés sur n'importe quel flux émetteur ou abonné.

Le SDK vidéo OpenTok fournit des indicateurs détaillés sur la qualité du flux via une API de statistiques de haut niveau — recommandée pour la plupart des cas d’utilisation — qui propose des statistiques audio, vidéo, réseau et côté émetteur sous une forme unifiée et adaptée à la session, qui reste stable lors des transitions entre connexions entre pairs. Pour un débogage avancé, le SDK offre également l'accès au rapport brut des statistiques WebRTC, qui reflète les données brutes relatives aux connexions entre pairs.

Le SDK expose également des mesures de l'état du réseau qui fournissent une évaluation de haut niveau de la santé de la connexion pour les éditeurs et les abonnés. Ces mesures comprennent un score d'état du réseau, la raison de ce score et, pour les abonnés, une source de dégradation indiquant quel côté de la connexion est responsable des problèmes observés. Voir État du réseau et source de dégradation pour plus de détails.

Ce guide comprend les sections suivantes :

API de statistiques sur les liens audio, vidéo et multimédia

Le SDK Android d'OpenTok transmet régulièrement des statistiques relatives à l'audio, à la vidéo et aux liens multimédias, tant pour les émetteurs que pour les abonnés. Ces statistiques comprennent le nombre de paquets, les débits binaires, les données relatives à la fréquence d'images, les indicateurs de pause/gel, les informations sur les codecs, ainsi que des indicateurs réseau au niveau du transport, tels que l'estimation de la bande passante et l'évaluation de l'état du réseau.

Les statistiques sont fournies par des auditeurs distincts :

Éditeur :

  • PublisherKit.VideoStatsListener - statistiques vidéo et événements relatifs à la qualité vidéo
  • PublisherKit.AudioStatsListener - statistiques audio
  • PublisherKit.MediaLinkStatsListener - statistiques sur la liaison média (niveau transport)

Abonné :

  • SubscriberKit.VideoStatsListener - statistiques vidéo et événements relatifs à la qualité vidéo
  • SubscriberKit.AudioStatsListener - statistiques audio
  • SubscriberKit.MediaLinkStatsListener - statistiques sur la liaison média (niveau transport)

Activation des statistiques pour les éditeurs

Statistiques vidéo

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);
    }
});

Statistiques audio

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");
        }
    }
});

Statistiques sur les liens avec les médias

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);
        }
    }
});

Voir État du réseau et source de dégradation pour plus de détails sur l'interprétation des notes et des raisons de l'état du réseau.

Pour un éditeur dans une session acheminée (qui utilise l'option Routeur vidéo multimédia de Vonage, le tableau « stats » contient un objet définissant les statistiques relatives au flux multimédia audio ou vidéo unique envoyé au routeur multimédia Vonage Video. Dans une session relayée, le tableau « stats » contient un objet pour chaque abonné au flux publié.

Activation des statistiques pour les abonnés

Statistiques vidéo

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

Statistiques audio

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

Statistiques sur les liens avec les médias

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);
    }
});

Voir État du réseau et source de dégradation pour plus de détails sur l'interprétation des notes et des raisons de l'état du réseau.

Structures de données statistiques

Cette section présente les structures et les propriétés fournies par l'API de statistiques audio et vidéo d'Android. Bien que toutes les plateformes SDK vidéo exposent le même ensemble de statistiques, il peut y avoir des différences mineures dans la façon dont chaque plateforme structure ou nomme les champs individuels. Ces variations reflètent les conventions de conception des SDK spécifiques à chaque plateforme plutôt que des différences dans les mesures sous-jacentes.

Pour une explication indépendante de la plate-forme des statistiques disponibles et de ce qu'elles représentent, voir aperçu de l'observabilité du client.

TransportStats

Représente les mesures partagées au niveau du transport.

  • connectionEstimatedBandwidth - Estimation de la largeur de bande de connexion disponible (bps).
  • networkCondition - Score de l'état actuel du réseau (NETWORK_CONDITION_UNKNOWN, NETWORK_CONDITION_CRITICAL, NETWORK_CONDITION_WARNING, NETWORK_CONDITION_FAIR, NETWORK_CONDITION_GOODou NETWORK_CONDITION_EXCELLENT).
  • networkConditionReason - Raison principale de l'impact sur l'état du réseau (NETWORK_CONDITION_REASON_NONE, NETWORK_CONDITION_REASON_UNKNOWN, NETWORK_CONDITION_REASON_BANDWIDTHou NETWORK_CONDITION_REASON_PACKET_LOSS).

PublisherKit.PublisherVideoStats

Fournit des statistiques sur la piste vidéo d'un éditeur. Elles comprennent :

  • connectionId - Dans une session relayée, l'identifiant de connexion du client souscrivant au flux. Indéfini dans une session routée.
  • subscriberId - Dans une session relayée, l'identifiant du client abonné au flux. Indéfini dans une session routée.
  • videoPacketsLost - Estimation des paquets vidéo perdus.
  • videoPacketsSent - Paquets vidéo envoyés.
  • videoBytesSent - Octets vidéo envoyés.
  • timeStamp - Date Unix en millisecondes à laquelle les statistiques ont été recueillies.
  • startTime - Le timestamp, en millisecondes depuis l'époque Unix, à partir duquel les totaux cumulés ont commencé à s'accumuler.
  • videoLayers - Tableau de statistiques sur la couche vidéo (voir OTPublisherKitVideoLayerStats).

PublisherKit.PublisherAudioStats

Fournit des statistiques sur la piste audio d'un éditeur. Elle comprend :

  • connectionId - Dans une session relayée, l'identifiant de connexion du client souscrivant au flux. Indéfini dans une session routée.
  • subscriberId - Dans une session relayée, l'identifiant du client abonné au flux. Indéfini dans une session routée.
  • audioPacketsLost - Estimation des paquets perdus.
  • audioPacketsSent - Paquets audio envoyés.
  • audioBytesSent - Octets audio envoyés.
  • timeStamp - Horodatage Unix en millisecondes.
  • startTime - Le timestamp, en millisecondes depuis l'époque Unix, à partir duquel les totaux cumulés ont commencé à s'accumuler.

PublisherKit.VideoLayerStats

Représente une couche simulcast ou une couche SVC.

  • width - Largeur de la trame encodée.
  • height - Hauteur du cadre codé.
  • encodedFrameRate - Images codées par seconde.
  • bitrate - Débit binaire de la couche (bps).
  • totalBitrate - Débit de la couche incluant le surdébit RTP (bps).
  • scalabilityMode - Descripteur SVC/scalability (par exemple, "L3T3").
  • qualityLimitationReason - Raison de la limitation de la qualité (bande passante, CPU, codec, résolution ou changement de couche).
  • codec - Le codec utilisé par cette couche vidéo.

PublisherKit.PublisherMediaLinkStats

Fournit des statistiques au niveau du transport pour la connexion d'un éditeur.

  • transport - Statistiques de transport pour cet éditeur (voir TransportStats)

SubscriberKit.SenderStats

Mesures d'estimation du côté de l'expéditeur (reflétées à la fois sur l'audio et la vidéo).

  • connectionMaxAllocatedBitrate - Débit maximum estimé pour la connexion de l'expéditeur.
  • connectionEstimatedBandwidth - Estimation de la largeur de bande actuelle (bps).

SubscriberKit.SubscriberVideoStats

Fournit des statistiques sur la piste vidéo d'un abonné. Elles comprennent :

  • videoPacketsLost - Estimation des paquets vidéo perdus.
  • videoPacketsReceived - Paquets vidéo reçus.
  • videoBytesReceived - Octets vidéo reçus.
  • timeStamp - Date Unix en millisecondes à laquelle les statistiques ont été recueillies.
  • senderStats - Mesures du côté de l'expéditeur (facultatif).
  • width - Largeur de la trame décodée en pixels.
  • height - Hauteur de l'image décodée en pixels.
  • decodedFrameRate - Images décodées par seconde.
  • bitrate - Débit binaire vidéo (bps).
  • totalBitrate - Débit binaire incluant l'overhead RTP (bps).
  • pauseCount - Nombre de pauses (>5s depuis la dernière image). Inclut les désactivations intentionnelles et les cas de retour audio.
  • totalPausesDuration - Durée totale de la pause (ms).
  • freezeCount - Compte de gel (événement de gel défini par WebRTC).
  • totalFreezesDuration - Durée totale de l'arrêt sur image (ms).
  • codec - Codec du décodeur actuel.

SubscriberKit.SubscriberMediaLinkStats

Fournit des statistiques au niveau du transport pour les connexions d'un abonné, y compris une visibilité sur les performances du réseau de l'éditeur distant. Cela permet aux Applications de diagnostiquer si les problèmes de connexion proviennent de la liaison descendante de l'abonné ou de la liaison montante de l'éditeur.

  • transport - Statistiques locales de transport et de réseau pour cet abonné. Peut être limité si les statistiques côté expéditeur et/ou le repli audio sont désactivés.
  • remotePublisherTransport - Statistiques de transport et de réseau de l'éditeur distant. Peut être limité si les statistiques côté expéditeur et/ou le repli audio sont désactivés.
  • networkDegradationSource - Indique la source de la dégradation du réseau, le cas échéant (NetworkDegradationSource.NONE, NetworkDegradationSource.LOCAL, NetworkDegradationSource.REMOTEou NetworkDegradationSource.BOTH_OR_UNCLEAR).

SubscriberKit.SubscriberAudioStats

Fournit des statistiques sur la piste audio d'un abonné. Elles comprennent :

  • audioPacketsLost - Estimation des paquets perdus.
  • audioPacketsReceived - Paquets reçus.
  • audioBytesReceived - Octets reçus.
  • timeStamp - Horodatage Unix en millisecondes.
  • senderStats - Mesures du côté de l'expéditeur (facultatif).

Statistiques côté expéditeur

Voir le aperçu des statistiques côté expéditeur.

Activation des statistiques côté expéditeur

Les statistiques côté émetteur sont reçues par les abonnés. Pour recevoir les statistiques côté émetteur, il faut les activer pour l'éditeur du flux en définissant l'attribut senderStatisticsTrack à la propriété true lors de la construction de l'éditeur :

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

Si senderStatsTrack n'est pas activé, aucun canal de statistiques sur les expéditeurs ne sera publié pour cet éditeur. La valeur par défaut est false.

Réception des statistiques côté expéditeur

Si l'éditeur a activé les statistiques côté expéditeur, les abonnés les reçoivent automatiquement via les rappels de statistiques vidéo et audio. décrite ci-dessus. Les senderStats sur les deux SubscriberKit.SubscriberVideoStats et SubscriberKit.SubscriberAudioStats fournit deux mesures :

  • connectionMaxAllocatedBitrate - Le débit maximum qui peut être estimé pour la connexion
  • connectionEstimatedBandwidth - La largeur de bande actuelle estimée pour la connexion

Ces mesures sont calculées par groupe audio-vidéo, de sorte que les mêmes valeurs apparaissent dans les statistiques vidéo et audio.

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);
    }
});

État du réseau et source de dégradation

Le SDK fournit des mesures de l'état du réseau en temps réel pour les éditeurs et les abonnés, y compris une note d'état, la raison de cette note et une source de dégradation pour les abonnés. Pour une explication complète du modèle d'état du réseau, des scores, des raisons et de la manière de l'activer, voir le document aperçu de l'observabilité du client.

Les données sur l'état du réseau sont disponibles par le biais de deux canaux :

  • Statistiques périodiques : Les événements liés aux statistiques des médias comprennent des mesures de transport avec networkCondition et networkConditionReason. Pour les abonnés, les statistiques sur les liens avec les médias comprennent également remotePublisherTransport et networkDegradationSource.
  • Événements liés à la modification de l'état du réseau : Des rappels dédiés sur les deux éditeur et abonné sont déclenchés lorsqu'un changement significatif de l'état du réseau est détecté.

L'exemple suivant montre comment utiliser les données sur l'état du réseau de l'abonné pour identifier la source de la dégradation :

@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);
    }
}

Rapport statistique RTC

Pour obtenir des statistiques de bas niveau sur les connexions entre pairs pour un éditeur, utilisez la commande PublisherKit.getRtcStatsReport() méthode. Celle-ci fournit des rapports statistiques RTC pour le flux multimédia. Il s'agit d'une opération asynchrone. Appelez la PublisherKit.setRtcStatsReportListener(PublisherKit.PublisherRtcStatsReportListener listener) méthode, puis implémentez la méthode [PublisherKit.PublisherRtcStatsReportListener.onRtcStatsReport(PublisherKit publisher, PublisherKit.PublisherRtcStats[] stats)](/opentok/sdks/android/reference/com/opentok/android/PublisherKit.PublisherRtcStatsReportListener#onRtcStatsReport-com.opentok.android.PublisherKit-com.opentok.android.PublisherKit.PublisherRtcStats:A-) avant d’appeler PublisherKit.getRtcStatsReport(). Lorsque les statistiques sont disponibles, l’implémentation de la [PublisherKit.PublisherRtcStatsReportListener.onRtcStatsReport(PublisherKit publisher, PublisherKit.PublisherRtcStats[] stats)](/opentok/sdks/android/reference/com/opentok/android/PublisherKit.PublisherRtcStatsReportListener#onRtcStatsReport-com.opentok.android.PublisherKit-com.opentok.android.PublisherKit.PublisherRtcStats:A-) est appelée. Un tableau de Éditeur RtcStats objets est transmis à cette méthode. L'objet PublisherRtcStats comprend une propriété jsonArrayOfReports. Il s'agit d'un tableau JSON contenant des rapports de statistiques RTC, dont le format est similaire à celui de l'objet RtcStatsReport implémenté dans les navigateurs Web (voir ces documents de Mozilla. Voir aussi cette documentation du W3C.

Pour obtenir des statistiques de bas niveau sur les connexions entre pairs pour un abonné, utilisez la commande SubscriberKit.getRtcStatsReport() méthode. Celle-ci fournit un rapport statistique RTC pour le flux multimédia. Il s'agit d'une opération asynchrone. Appelez la SubscriberKit.setRtcStatsReportListener(SubscriberKit.SubscriberRtcStatsReportListener listener) méthode, puis implémenter la SubscriberKit.SubscriberRtcStatsReportListener.onRtcStatsReport(SubscriberKit subscriber, java.lang.String jsonArrayOfReports) méthode avant d'appeler SubscriberKit.getRtcStatsReport(). Lorsque les statistiques sont disponibles, l'implémentation de la SubscriberKit.SubscriberRtcStatsReportListener.onRtcStatsReport(SubscriberKit subscriber, java.lang.String jsonArrayOfReports) La méthode est appelée. Le paramètre `jsonArrayOfReports` est un tableau JSON contenant des rapports de statistiques RTC, dont le format est similaire à celui de l'objet `RtcStatsReport` implémenté dans les navigateurs Web (voir ces documents de Mozilla. Voir aussi cette documentation du W3C.