ExoPlayer est compatible avec un large éventail de besoins d'analyse de la lecture. En fin de compte, l'analyse consiste à collecter, interpréter, agréger et résumer les données des lectures. Ces données peuvent être utilisées sur l'appareil (par exemple, pour la journalisation, le débogage ou pour éclairer les décisions de lecture futures) ou être transmises à un serveur pour surveiller les lectures sur tous les appareils.
Un système d'analyse doit généralement commencer par collecter les événements, puis les traiter pour les rendre significatifs :
- Collecte d'événements : pour ce faire, enregistrez un
AnalyticsListenersur une instanceExoPlayer. Les écouteurs d'analyse enregistrés reçoivent les événements au fur et à mesure qu'ils se produisent pendant l'utilisation du lecteur. Chaque événement est associé à l'élément multimédia correspondant dans la playlist, ainsi qu'aux métadonnées de position de lecture et de code temporel. - Traitement des événements : certains systèmes d'analyse importent des événements bruts sur un serveur, et l'ensemble du traitement des événements est effectué côté serveur. Il est également possible de traiter les événements sur l'appareil, ce qui peut être plus simple ou réduire la quantité d'informations à importer. ExoPlayer fournit
PlaybackStatsListener, qui vous permet d'effectuer les étapes de traitement suivantes :- Interprétation des événements : pour être utiles à des fins d'analyse, les événements doivent être interprétés dans le contexte d'une seule lecture. Par exemple, l'événement brut d'un changement d'état du lecteur vers
STATE_BUFFERINGpeut correspondre à la mise en mémoire tampon initiale, à une nouvelle mise en mémoire tampon ou à une mise en mémoire tampon qui se produit après une recherche. - Suivi de l'état : cette étape convertit les événements en compteurs. Par exemple, les événements de changement d'état peuvent être convertis en compteurs qui suivent le temps passé dans chaque état de lecture. Le résultat est un ensemble de base de valeurs de données analytiques pour une seule lecture.
- Agrégation : cette étape combine les données d'analyse de plusieurs lectures, généralement en additionnant les compteurs.
- Calcul des métriques récapitulatives : la plupart des métriques les plus utiles sont celles qui calculent des moyennes ou combinent les valeurs de données d'analyse de base d'autres manières. Les métriques récapitulatives peuvent être calculées pour une ou plusieurs lectures.
- Interprétation des événements : pour être utiles à des fins d'analyse, les événements doivent être interprétés dans le contexte d'une seule lecture. Par exemple, l'événement brut d'un changement d'état du lecteur vers
Collecte d'événements avec AnalyticsListener
Les événements de lecture bruts du lecteur sont signalés aux implémentations AnalyticsListener. Vous pouvez ajouter votre propre écouteur et ne remplacer que les méthodes qui vous intéressent :
Kotlin
exoPlayer.addAnalyticsListener( object : AnalyticsListener { override fun onPlaybackStateChanged(eventTime: EventTime, @Player.State state: Int) {} override fun onDroppedVideoFrames( eventTime: EventTime, droppedFrames: Int, elapsedMs: Long, ) {} } )
Java
exoPlayer.addAnalyticsListener( new AnalyticsListener() { @Override public void onPlaybackStateChanged(EventTime eventTime, @Player.State int state) {} @Override public void onDroppedVideoFrames( EventTime eventTime, int droppedFrames, long elapsedMs) {} });
Le EventTime transmis à chaque rappel associe l'événement à un élément multimédia de la playlist, ainsi qu'à la position de lecture et aux métadonnées d'horodatage :
realtimeMs: heure de l'événement.timeline,windowIndexetmediaPeriodId: définissent la playlist et l'élément de la playlist auquel l'événement appartient.mediaPeriodIdcontient des informations supplémentaires facultatives, par exemple pour indiquer si l'événement appartient à une annonce dans l'élément.eventPlaybackPositionMs: position de lecture dans l'élément au moment où l'événement s'est produit.currentTimeline,currentWindowIndex,currentMediaPeriodIdetcurrentPlaybackPositionMs: comme décrit précédemment, mais pour l'élément en cours de lecture. L'élément en cours de lecture peut être différent de l'élément auquel l'événement appartient, par exemple si l'événement correspond à la mise en mémoire tampon préalable de l'élément suivant à lire.
Traitement des événements avec PlaybackStatsListener
PlaybackStatsListener est un AnalyticsListener qui implémente le traitement des événements sur l'appareil. Il calcule PlaybackStats, avec des compteurs et des métriques dérivées, y compris :
- Métriques récapitulatives, par exemple la durée totale de lecture.
- Métriques de qualité de la lecture adaptative, par exemple la résolution vidéo moyenne.
- Métriques de qualité de rendu, par exemple le taux d'images supprimées.
- Métriques d'utilisation des ressources, par exemple le nombre d'octets lus sur le réseau.
Vous trouverez la liste complète des nombres et des métriques dérivées disponibles dans le Javadoc PlaybackStats.
PlaybackStatsListener calcule des PlaybackStats distincts pour chaque élément multimédia de la playlist, ainsi que pour chaque annonce côté client insérée dans ces éléments. Vous pouvez fournir un rappel à PlaybackStatsListener pour être informé des lectures terminées et utiliser le EventTime transmis au rappel pour identifier la lecture terminée. Il est possible d'agréger les données d'analyse pour plusieurs lectures. Il est également possible d'interroger le PlaybackStats pour la session de lecture en cours à tout moment à l'aide de PlaybackStatsListener.getPlaybackStats().
Kotlin
exoPlayer.addAnalyticsListener( PlaybackStatsListener(/* keepHistory= */ true) { eventTime: EventTime?, playbackStats: PlaybackStats? -> // Analytics data for the session started at `eventTime` is ready. } )
Java
exoPlayer.addAnalyticsListener( new PlaybackStatsListener( /* keepHistory= */ true, (eventTime, playbackStats) -> { // Analytics data for the session started at `eventTime` is ready. }));
Le constructeur de PlaybackStatsListener permet de conserver l'historique complet des événements traités. Notez que cela peut entraîner une surcharge de mémoire inconnue en fonction de la durée de la lecture et du nombre d'événements. Par conséquent, vous ne devez l'activer que si vous avez besoin d'accéder à l'historique complet des événements traités, et pas seulement aux données d'analyse finales.
Notez que PlaybackStats utilise un ensemble étendu d'états pour indiquer non seulement l'état du contenu multimédia, mais aussi l'intention utilisateur de le lire, ainsi que des informations plus détaillées, telles que la raison pour laquelle la lecture a été interrompue ou s'est terminée :
| État de lecture | Intention utilisateur de jouer | Aucune intention de jouer |
|---|---|---|
| Avant la lecture | JOINING_FOREGROUND |
NOT_STARTED, JOINING_BACKGROUND |
| Lecture active | PLAYING |
|
| Lecture interrompue | BUFFERING, SEEKING |
PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING et INTERRUPTED_BY_AD |
| États finaux | ENDED, STOPPED, FAILED, ABANDONED |
L'intention utilisateur de lecture est importante pour distinguer les moments où l'utilisateur attendait activement que la lecture continue des temps d'attente passifs. Par exemple, PlaybackStats.getTotalWaitTimeMs renvoie la durée totale passée dans les états JOINING_FOREGROUND, BUFFERING et SEEKING, mais pas la durée pendant laquelle la lecture a été mise en pause. De même, PlaybackStats.getTotalPlayAndWaitTimeMs renvoie la durée totale pendant laquelle l'utilisateur a l'intention de jouer, c'est-à-dire le temps d'attente actif total et le temps total passé à l'état PLAYING.
Événements traités et interprétés
Vous pouvez enregistrer les événements traités et interprétés en utilisant PlaybackStatsListener avec keepHistory=true. Le PlaybackStats obtenu contiendra les listes d'événements suivantes :
playbackStateHistory: liste numérotée des états de lecture étendus avec la valeurEventTimeà laquelle ils ont commencé à s'appliquer. Vous pouvez également utiliserPlaybackStats.getPlaybackStateAtTimepour rechercher l'état à une heure donnée.mediaTimeHistory: historique des paires d'horloge et de temps multimédia vous permettant de reconstituer les parties du contenu multimédia qui ont été lues à quel moment. Vous pouvez également utiliserPlaybackStats.getMediaTimeMsAtRealtimeMspour rechercher la position de lecture à une heure donnée.videoFormatHistoryetaudioFormatHistory: listes numérotées des formats vidéo et audio utilisés pendant la lecture avec leEventTimeauquel ils ont commencé à être utilisés.fatalErrorHistoryetnonFatalErrorHistory: listes numérotées d'erreurs fatales et non fatales avec l'EventTimeauquel elles se sont produites. Les erreurs fatales sont celles qui ont mis fin à la lecture, tandis que les erreurs non fatales ont pu être corrigées.
Données analytiques d'une seule lecture
Ces données sont automatiquement collectées si vous utilisez PlaybackStatsListener, même avec keepHistory=false. Les valeurs finales sont les champs publics que vous trouverez dans le Javadoc PlaybackStats et les durées d'état de lecture renvoyées par getPlaybackStateDurationMs. Pour plus de commodité, vous trouverez également des méthodes telles que getTotalPlayTimeMs et getTotalWaitTimeMs qui renvoient la durée de combinaisons d'états de lecture spécifiques.
Kotlin
Log.d( "DEBUG", "Playback summary: " + "play time = " + playbackStats.totalPlayTimeMs + ", rebuffers = " + playbackStats.totalRebufferCount, )
Java
Log.d( "DEBUG", "Playback summary: " + "play time = " + playbackStats.getTotalPlayTimeMs() + ", rebuffers = " + playbackStats.totalRebufferCount);
Données analytiques agrégées de plusieurs lectures
Vous pouvez combiner plusieurs PlaybackStats en appelant PlaybackStats.merge. Le PlaybackStats obtenu contiendra les données agrégées de toutes les lectures fusionnées. Notez qu'il ne contiendra pas l'historique des événements de lecture individuels, car ils ne peuvent pas être agrégés.
PlaybackStatsListener.getCombinedPlaybackStats permet d'obtenir une vue agrégée de toutes les données d'analyse collectées pendant la durée de vie d'un PlaybackStatsListener.
Métriques récapitulatives calculées
En plus des données d'analyse de base, PlaybackStats fournit de nombreuses méthodes pour calculer des métriques récapitulatives.
Kotlin
Log.d( "DEBUG", "Additional calculated summary metrics: " + "average video bitrate = " + playbackStats.meanVideoFormatBitrate + ", mean time between rebuffers = " + playbackStats.meanTimeBetweenRebuffers, )
Java
Log.d( "DEBUG", "Additional calculated summary metrics: " + "average video bitrate = " + playbackStats.getMeanVideoFormatBitrate() + ", mean time between rebuffers = " + playbackStats.getMeanTimeBetweenRebuffers());
Rubriques avancées
Associer des données analytiques à des métadonnées de lecture
Lorsque vous collectez des données analytiques pour des lectures individuelles, vous pouvez associer les données analytiques de lecture aux métadonnées du contenu multimédia en cours de lecture.
Il est conseillé de définir des métadonnées spécifiques au contenu multimédia avec MediaItem.Builder.setTag.
La balise multimédia fait partie de l'élément EventTime signalé pour les événements bruts et lorsque les PlaybackStats sont terminés. Elle peut donc être récupérée lors du traitement des données d'analyse correspondantes :
Kotlin
PlaybackStatsListener(/* keepHistory= */ false) { eventTime: EventTime, playbackStats: PlaybackStats -> val mediaTag = eventTime.timeline .getWindow(eventTime.windowIndex, Timeline.Window()) .mediaItem .localConfiguration ?.tag // Report playbackStats with mediaTag metadata. }
Java
new PlaybackStatsListener( /* keepHistory= */ false, (eventTime, playbackStats) -> { Object mediaTag = eventTime.timeline.getWindow(eventTime.windowIndex, new Timeline.Window()) .mediaItem .localConfiguration .tag; // Report playbackStats with mediaTag metadata. });
Créer des rapports sur les événements d'analyse personnalisés
Si vous devez ajouter des événements personnalisés aux données d'analyse, vous devez les enregistrer dans votre propre structure de données et les combiner ultérieurement avec les PlaybackStats signalés. Si cela vous aide, vous pouvez étendre DefaultAnalyticsCollector pour pouvoir générer des instances EventTime pour vos événements personnalisés et les envoyer aux écouteurs déjà enregistrés, comme illustré dans l'exemple suivant.
Kotlin
@OptIn(UnstableApi::class) private interface ExtendedListener : AnalyticsListener { fun onCustomEvent(eventTime: EventTime) } @OptIn(UnstableApi::class) private class ExtendedCollector : DefaultAnalyticsCollector(Clock.DEFAULT) { fun customEvent() { val eventTime = super.generateCurrentPlayerMediaPeriodEventTime() super.sendEvent(eventTime, CUSTOM_EVENT_ID) { listener: AnalyticsListener -> if (listener is ExtendedListener) { listener.onCustomEvent(eventTime) } } } } @OptIn(UnstableApi::class) fun useExtendedAnalyticsCollector(context: Context) { // Usage - Setup and listener registration. val player = ExoPlayer.Builder(context).setAnalyticsCollector(ExtendedCollector()).build() player.addAnalyticsListener( object : ExtendedListener { override fun onCustomEvent(eventTime: EventTime) { // Save custom event for analytics data. } } ) // Usage - Triggering the custom event. (player.analyticsCollector as ExtendedCollector).customEvent() }
Java
@OptIn(markerClass = UnstableApi.class) private interface ExtendedListener extends AnalyticsListener { void onCustomEvent(EventTime eventTime); } @OptIn(markerClass = UnstableApi.class) private static class ExtendedCollector extends DefaultAnalyticsCollector { public ExtendedCollector() { super(Clock.DEFAULT); } public void customEvent() { AnalyticsListener.EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); sendEvent( eventTime, CUSTOM_EVENT_ID, listener -> { if (listener instanceof ExtendedListener) { ((ExtendedListener) listener).onCustomEvent(eventTime); } }); } } @OptIn(markerClass = UnstableApi.class) public static void useExtendedAnalyticsCollector(Context context) { // Usage - Setup and listener registration. ExoPlayer player = new ExoPlayer.Builder(context).setAnalyticsCollector(new ExtendedCollector()).build(); player.addAnalyticsListener( (ExtendedListener) eventTime -> { // Save custom event for analytics data. }); // Usage - Triggering the custom event. ((ExtendedCollector) player.getAnalyticsCollector()).customEvent(); }
Flags de fonctionnalité de suivi et mode Canary
Lorsque vous effectuez des tests A/B ou évaluez l'impact de nouvelles optimisations Media3, vous pouvez mettre en corrélation les données d'analyse de lecture avec les flags de fonctionnalité actifs et le mode Canary.
Vous pouvez interroger Flags.getLogString ou inspecter des remplacements de flag spécifiques à l'aide de Flags.getEnabledOverrides et Flags.getDisabledOverrides pour inclure les états des flags dans vos charges utiles d'analyse personnalisées ou vos métadonnées de session.