Analytics

ExoPlayer, वीडियो चलाने से जुड़े आंकड़ों की कई तरह की ज़रूरतों को पूरा करता है. आखिर में, आंकड़ों का मतलब है कि वीडियो के प्लेबैक से डेटा इकट्ठा करना, उसे समझना, इकट्ठा करना, और उसकी खास जानकारी देना. इस डेटा का इस्तेमाल डिवाइस पर किया जा सकता है. उदाहरण के लिए, लॉगिंग, डीबग करने या आने वाले समय में प्लेबैक से जुड़े फ़ैसले लेने के लिए. इसके अलावा, सभी डिवाइसों पर प्लेबैक को मॉनिटर करने के लिए, सर्वर को रिपोर्ट किया जा सकता है.

आम तौर पर, किसी Analytics सिस्टम को पहले इवेंट इकट्ठा करने होते हैं. इसके बाद, उन्हें प्रोसेस करना होता है, ताकि वे काम के बन सकें:

  • इवेंट कलेक्शन: इसके लिए, ExoPlayer इंस्टेंस पर AnalyticsListener को रजिस्टर करें. रजिस्टर किए गए Analytics लिसनर को इवेंट मिलते हैं, क्योंकि वे प्लेयर के इस्तेमाल के दौरान होते हैं. हर इवेंट, प्लेलिस्ट में मौजूद उससे जुड़े मीडिया आइटम के साथ-साथ, प्लेबैक की पोज़िशन और टाइमस्टैंप के मेटाडेटा से जुड़ा होता है.
  • इवेंट प्रोसेसिंग: कुछ Analytics सिस्टम, रॉ इवेंट को सर्वर पर अपलोड करते हैं. साथ ही, सभी इवेंट की प्रोसेसिंग सर्वर-साइड पर की जाती है. डिवाइस पर भी इवेंट प्रोसेस किए जा सकते हैं. ऐसा करना आसान हो सकता है. इसके अलावा, इससे अपलोड की जाने वाली जानकारी की मात्रा भी कम हो सकती है. ExoPlayer, PlaybackStatsListener उपलब्ध कराता है. इसकी मदद से, ये काम किए जा सकते हैं:
    1. इवेंट की व्याख्या: आंकड़ों के मकसद से काम का होने के लिए, इवेंट को एक ही प्लेबैक के संदर्भ में समझा जाना चाहिए. उदाहरण के लिए, प्लेयर की स्थिति में बदलाव होने पर STATE_BUFFERING पर सेट होने वाले रॉ इवेंट का मतलब, शुरुआती बफ़रिंग, फिर से बफ़रिंग या वीडियो को आगे-पीछे करने के बाद होने वाली बफ़रिंग हो सकता है.
    2. स्टेट ट्रैकिंग: इस चरण में, इवेंट को काउंटर में बदला जाता है. उदाहरण के लिए, स्थिति में बदलाव वाले इवेंट को काउंटर में बदला जा सकता है. इससे यह ट्रैक किया जा सकता है कि वीडियो चलाने की हर स्थिति में कितना समय बिताया गया. इससे, एक बार वीडियो चलाने पर मिलने वाले आंकड़ों के डेटा की वैल्यू का बुनियादी सेट मिलता है.
    3. एग्रीगेशन: इस चरण में, कई प्लेबैक के आंकड़ों को एक साथ जोड़ा जाता है. आम तौर पर, इसके लिए काउंटर जोड़े जाते हैं.
    4. समरी मेट्रिक का कैलकुलेशन: सबसे काम की मेट्रिक वे होती हैं जो औसत की गिनती करती हैं या अन्य तरीकों से बुनियादी आंकड़ों की वैल्यू को जोड़ती हैं. समरी मेट्रिक को एक या कई प्लेबैक के लिए कैलकुलेट किया जा सकता है.

AnalyticsListener की मदद से इवेंट इकट्ठा करना

प्लेयर से मिले रॉ प्लेबैक इवेंट, AnalyticsListener के लागू होने की जानकारी देते हैं. आपके पास अपना लिसनर जोड़ने और सिर्फ़ उन तरीकों को बदलने का विकल्प है जिनमें आपकी दिलचस्पी है:

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

हर कॉलबैक को पास किया गया EventTime, इवेंट को प्लेलिस्ट में मौजूद किसी मीडिया आइटम के साथ-साथ, प्लेबैक की पोज़िशन और टाइमस्टैंप मेटाडेटा से जोड़ता है:

  • realtimeMs: इवेंट का समय.
  • timeline, windowIndex, और mediaPeriodId: इससे प्लेलिस्ट और प्लेलिस्ट में मौजूद उस आइटम के बारे में पता चलता है जिससे इवेंट जुड़ा है. mediaPeriodId में अतिरिक्त जानकारी शामिल होती है. उदाहरण के लिए, यह बताना कि इवेंट, आइटम में मौजूद किसी विज्ञापन से जुड़ा है या नहीं.
  • eventPlaybackPositionMs: इवेंट होने पर, आइटम में वीडियो चलाने की जगह.
  • currentTimeline, currentWindowIndex, currentMediaPeriodId, और currentPlaybackPositionMs: जैसा कि पहले बताया गया है, लेकिन फ़िलहाल चलाए जा रहे आइटम के लिए. फ़िलहाल चल रहा आइटम, उस आइटम से अलग हो सकता है जिससे इवेंट जुड़ा है. उदाहरण के लिए, अगर इवेंट, चलाए जाने वाले अगले आइटम की प्री-बफ़रिंग से जुड़ा है.

PlaybackStatsListener की मदद से इवेंट प्रोसेस करना

PlaybackStatsListener एक AnalyticsListener है, जो उपयोगकर्ता के डिवाइस पर इवेंट प्रोसेसिंग की सुविधा लागू करता है. यह काउंटर और डेरिव्ड मेट्रिक के साथ PlaybackStats का हिसाब लगाता है. इसमें ये मेट्रिक शामिल हैं:

  • खास जानकारी वाली मेट्रिक, जैसे कि वीडियो चलाने में बीता कुल समय.
  • अडैप्टिव प्लेबैक क्वालिटी मेट्रिक, जैसे कि वीडियो का औसत रिज़ॉल्यूशन.
  • रेंडरिंग क्वालिटी मेट्रिक, जैसे कि ड्रॉप किए गए फ़्रेम की दर.
  • संसाधन के इस्तेमाल से जुड़ी मेट्रिक. उदाहरण के लिए, नेटवर्क पर पढ़े गए बाइट की संख्या.

आपको PlaybackStats Javadoc में, उपलब्ध काउंट और डिराइव्ड मेट्रिक की पूरी सूची मिलेगी.

PlaybackStatsListener, प्लेलिस्ट में मौजूद हर मीडिया आइटम के लिए, अलग-अलग PlaybackStats का हिसाब लगाता है. साथ ही, इन आइटम में डाले गए हर क्लाइंट-साइड विज्ञापन के लिए भी अलग-अलग का हिसाब लगाता है. PlaybackStatsListener को कॉलबैक देकर, प्लेबैक खत्म होने की जानकारी पाई जा सकती है. साथ ही, कॉलबैक को पास किए गए EventTime का इस्तेमाल करके, यह पता लगाया जा सकता है कि कौनसा प्लेबैक खत्म हुआ. एक से ज़्यादा प्लेबैक के लिए, Analytics डेटा को एग्रीगेट किया जा सकता है. PlaybackStatsListener.getPlaybackStats() का इस्तेमाल करके, मौजूदा प्लेबैक सेशन के लिए PlaybackStats से किसी भी समय क्वेरी की जा सकती है.

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

PlaybackStatsListener का कंस्ट्रक्टर, प्रोसेस किए गए इवेंट का पूरा इतिहास सेव रखने का विकल्प देता है. ध्यान दें कि इससे मेमोरी पर अनचाहा असर पड़ सकता है. यह असर, प्लेबैक की अवधि और इवेंट की संख्या पर निर्भर करता है. इसलिए, आपको इसे सिर्फ़ तब चालू करना चाहिए, जब आपको प्रोसेस किए गए इवेंट के पूरे इतिहास का ऐक्सेस चाहिए, न कि सिर्फ़ फ़ाइनल Analytics डेटा का.

ध्यान दें कि PlaybackStats, मीडिया की स्थिति के साथ-साथ, उपयोगकर्ता के मीडिया चलाने के इरादे और ज़्यादा जानकारी देने के लिए, स्थितियों के एक बड़े सेट का इस्तेमाल करता है. जैसे, मीडिया चलाना क्यों रोका गया या खत्म हुआ:

प्लेबैक की स्थिति उपयोगकर्ता का वीडियो चलाने का मकसद गेम खेलने का इरादा नहीं है
वीडियो चलाने से पहले JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
ऐक्टिव प्लेबैक PLAYING
वीडियो चलाने में रुकावट आना BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
एंड स्टेट ENDED, STOPPED, FAILED, ABANDONED

उपयोगकर्ता के वीडियो चलाने के मकसद से यह पता चलता है कि उपयोगकर्ता ने वीडियो के फिर से शुरू होने का इंतज़ार कब किया और कब नहीं. उदाहरण के लिए, PlaybackStats.getTotalWaitTimeMs से, JOINING_FOREGROUND, BUFFERING, और SEEKING स्थितियों में बिताया गया कुल समय दिखता है. हालांकि, इसमें वह समय नहीं दिखता जब वीडियो को रोका गया था. इसी तरह, PlaybackStats.getTotalPlayAndWaitTimeMs से उस कुल समय की जानकारी मिलेगी जिसमें उपयोगकर्ता का मकसद खेलने का था. इसमें, कुल ऐक्टिव इंतज़ार का समय और PLAYING स्टेट में बिताया गया कुल समय शामिल है.

प्रोसेस और इंटरप्रेट किए गए इवेंट

keepHistory=true के साथ PlaybackStatsListener का इस्तेमाल करके, प्रोसेस और इंटरप्रेट किए गए इवेंट रिकॉर्ड किए जा सकते हैं. नतीजे के तौर पर मिलने वाले PlaybackStats में, इवेंट की ये सूचियां शामिल होंगी:

  • playbackStateHistory: एक्सटेंडेड प्लेबैक की स्थितियों की क्रम वाली सूची. इसमें वह EventTime भी शामिल है जिस पर ये स्थितियां लागू होनी शुरू हुईं. किसी खास समय पर स्थिति देखने के लिए, PlaybackStats.getPlaybackStateAtTime का भी इस्तेमाल किया जा सकता है.
  • mediaTimeHistory: वॉल क्लॉक टाइम और मीडिया टाइम पेयर का इतिहास. इससे यह पता लगाया जा सकता है कि मीडिया के कौनसे हिस्से किस समय चलाए गए थे. PlaybackStats.getMediaTimeMsAtRealtimeMs का इस्तेमाल करके, किसी खास समय पर वीडियो की प्लेबैक पोज़िशन भी देखी जा सकती है.
  • videoFormatHistory और audioFormatHistory: वीडियो और ऑडियो फ़ॉर्मैट की क्रम वाली सूचियां, जिनका इस्तेमाल वीडियो चलाने के दौरान किया गया. साथ ही, वह EventTime जब इनका इस्तेमाल शुरू किया गया.
  • fatalErrorHistory और nonFatalErrorHistory: गंभीर और सामान्य गड़बड़ियों की क्रम वाली सूचियां. इनमें वह EventTime भी शामिल है जिस पर वे हुईं. गंभीर गड़बड़ियां वे होती हैं जिनकी वजह से वीडियो चलना बंद हो जाता है. वहीं, सामान्य गड़बड़ियों को ठीक किया जा सकता है.

सिंगल-प्लेबैक के आंकड़ों का डेटा

PlaybackStatsListener का इस्तेमाल करने पर, यह डेटा अपने-आप इकट्ठा हो जाता है. भले ही, आपने keepHistory=false का इस्तेमाल किया हो. फ़ाइनल वैल्यू, सार्वजनिक फ़ील्ड होते हैं. इन्हें PlaybackStats Javadoc और getPlaybackStateDurationMs से मिलने वाली प्लेबैक की स्थिति की अवधि में देखा जा सकता है. आपकी सुविधा के लिए, आपको getTotalPlayTimeMs और getTotalWaitTimeMs जैसे तरीके भी मिलेंगे. इनसे, प्लेबैक की खास स्थितियों के कॉम्बिनेशन की अवधि का पता चलता है.

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

कई प्लेबैक का एग्रीगेट किया गया आंकड़ों का डेटा

PlaybackStats.merge को कॉल करके, कई PlaybackStats को एक साथ जोड़ा जा सकता है. इससे बनने वाले PlaybackStats में, मर्ज किए गए सभी प्लेबैक का कुल डेटा शामिल होगा. ध्यान दें कि इसमें अलग-अलग प्लेबैक इवेंट का इतिहास शामिल नहीं होगा, क्योंकि इन्हें एग्रीगेट नहीं किया जा सकता.

PlaybackStatsListener.getCombinedPlaybackStats का इस्तेमाल करके, किसी PlaybackStatsListener के लाइफ़टाइम में इकट्ठा किए गए सभी आंकड़ों के डेटा को एक साथ देखा जा सकता है.

कैलकुलेट की गई मेट्रिक की खास जानकारी

बुनियादी आंकड़ों के अलावा, PlaybackStats में खास मेट्रिक का हिसाब लगाने के कई तरीके मौजूद हैं.

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

ऐडवांस विषय

Analytics डेटा को प्लेबैक मेटाडेटा से जोड़ना

अलग-अलग प्लेबैक के लिए आंकड़े इकट्ठा करते समय, हो सकता है कि आप प्लेबैक के आंकड़ों को चलाए जा रहे मीडिया के मेटाडेटा से जोड़ना चाहें.

हमारा सुझाव है कि आप MediaItem.Builder.setTag की मदद से, मीडिया के हिसाब से मेटाडेटा सेट करें. मीडिया टैग, रॉ इवेंट के लिए रिपोर्ट किए गए EventTime का हिस्सा होता है. साथ ही, PlaybackStats के खत्म होने पर, इसे संबंधित Analytics डेटा को मैनेज करते समय वापस पाया जा सकता है:

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

कस्टम Analytics इवेंट की रिपोर्टिंग

अगर आपको Analytics डेटा में कस्टम इवेंट जोड़ने हैं, तो आपको इन इवेंट को अपने डेटा स्ट्रक्चर में सेव करना होगा. साथ ही, इन्हें बाद में रिपोर्ट किए गए PlaybackStats के साथ जोड़ना होगा. अगर इससे मदद मिलती है, तो DefaultAnalyticsCollector को एक्सटेंड किया जा सकता है, ताकि आपके कस्टम इवेंट के लिए EventTime इंस्टेंस जनरेट किए जा सकें. साथ ही, उन्हें पहले से रजिस्टर किए गए लिसनर को भेजा जा सके, जैसा कि इस उदाहरण में दिखाया गया है.

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