Analytics

يتوافق ExoPlayer مع مجموعة كبيرة من احتياجات إحصاءات التشغيل. في نهاية المطاف، تدور تحليلات الأداء حول جمع البيانات وتفسيرها وتجميعها وتلخيصها من عمليات التشغيل. يمكن استخدام هذه البيانات إما على الجهاز، مثلاً لتسجيل ملف السجلّ أو تصحيح الأخطاء أو لاتخاذ قرارات بشأن التشغيل في المستقبل، أو إرسالها إلى أحد الخوادم لمراقبة عمليات التشغيل على جميع الأجهزة.

يحتاج نظام الإحصاءات عادةً إلى جمع الأحداث أولاً، ثم معالجتها بشكل أكبر لجعلها ذات مغزى:

  • جمع الأحداث: يمكن إجراء ذلك من خلال تسجيل AnalyticsListener على مثيل ExoPlayer. تتلقّى أدوات معالجة الإحصاءات المسجّلة الأحداث عند وقوعها أثناء استخدام المشغّل. يرتبط كل حدث بعنصر الوسائط المقابل في قائمة التشغيل، بالإضافة إلى موضع التشغيل والبيانات الوصفية للطابع الزمني.
  • معالجة الأحداث: تُحمِّل بعض أنظمة الإحصاءات الأحداث الأوّلية إلى خادم، ويتم تنفيذ جميع عمليات معالجتها من جهة الخادم. من الممكن أيضًا معالجة الأحداث على الجهاز، وقد يكون ذلك أبسط أو يقلل من كمية المعلومات التي يجب تحميلها. يوفّر ExoPlayer PlaybackStatsListener، الذي يتيح لك تنفيذ خطوات المعالجة التالية:
    1. تفسير الأحداث: لكي تكون الأحداث مفيدة لأغراض الإحصاءات، يجبinterpreted تفسيرها في سياق تشغيل واحد. على سبيل المثال، قد يتوافق الحدث الأولي لتغيير حالة اللاعب إلى STATE_BUFFERING مع عملية التخزين المؤقت الأولية أو التخزين المؤقت أو التخزين المؤقت الذي يحدث بعد البحث.
    2. تتبُّع الحالة: تحوّل هذه الخطوة الأحداث إلى عدادات. على سبيل المثال، يمكن تحويل أحداث تغيير الحالة إلى عدّادات تتتبّع مقدار الوقت المستغرق في كل حالة تشغيل. والنتيجة هي مجموعة أساسية من قيم data التحليلات لتشغيل واحد.
    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: لتحديد قائمة التشغيل والitem ضمن قائمة التشغيل التي ينتمي إليها الحدث تحتوي السمة mediaPeriodId على معلومات إضافية اختيارية، على سبيل المثال تشير إلى ما إذا كان الحدث ينتمي إلى إعلان ضمن العنصر.
  • eventPlaybackPositionMs: موضع التشغيل في العنصر عند وقوع الحدث
  • currentTimeline وcurrentWindowIndex وcurrentMediaPeriodId و currentPlaybackPositionMs: كما هو موضّح أعلاه، ولكن للمحتوى الذي يتم تشغيله حاليًا قد يكون المحتوى الذي يتم تشغيله حاليًا مختلفًا عن المحتوى الذي ينتمي إليه الحدث، على سبيل المثال إذا كان الحدث يتوافق مع التخزين المؤقت المُسبَق للمحتوى التالي الذي سيتم تشغيله.

معالجة الأحداث باستخدام PlaybackStatsListener

PlaybackStatsListener هو AnalyticsListener ينفذ معالجة الأحداث على الجهاز. ويتم احتساب PlaybackStats باستخدام العدّادات والمقاييس المشتقة، بما في ذلك:

  • المقاييس التلخيصية، مثل إجمالي وقت التشغيل
  • مقاييس جودة التشغيل التكيُّفية، مثل متوسط درجة دقة الفيديو
  • مقاييس جودة العرض، مثل معدّل اللقطات المفقودة
  • مقاييس استخدام الموارد، مثل عدد وحدات البايت التي تمت قراءتها عبر الشبكة

يمكنك العثور على قائمة كاملة بالأعداد والمقاييس المستمدة المتاحة فيملف ملف PlaybackStats Javadoc.

تحسب PlaybackStatsListener قيمة PlaybackStats منفصلة لكل عنصر وسائط في قائمة التشغيل، وأيضًا لكل إعلان من جهة العميل يتم إدراجه في هذه العناصر. يمكنك توفير ردّ اتصال إلى PlaybackStatsListener للاطّلاع على عمليات التشغيل التي اكتملت، واستخدام EventTime الذي تم تمريره إلى ردّ الاتصال لتحديد عملية التشغيل التي اكتملت. من الممكن تجميع بيانات الإحصاءات لعمليات تشغيل متعدّدة. من الممكن أيضًا الاستعلام عن PlaybackStats لجلسة التشغيل الحالية في أي وقت باستخدام 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.
        }));

يقدّم مُنشئ PlaybackStatsListener خيار الاحتفاظ بسجلّ كامل للأحداث التي تمت معالجتها. يُرجى العِلم أنّ هذا قد يؤدي إلى زيادة غير معروفة في استخدام الذاكرة استنادًا إلى مدة التشغيل وعدد الأحداث. لذلك، يجب تفعيله فقط إذا كنت بحاجة إلى الوصول إلى السجلّ الكامل للأحداث التي تمت معالجتها، بدلاً من بيانات الإحصاءات النهائية فقط.

يُرجى العِلم أنّ 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.

الأحداث التي تمت معالجتها وتفسيرها

يمكنك تسجيل الأحداث التي تمت معالجتها وتفسيرها باستخدام PlaybackStatsListener مع keepHistory=true. سيتضمّن PlaybackStats الناتج قوائم الأحداث التالية:

  • playbackStateHistory: قائمة بحالات التشغيل الموسّعة مرتبة زمنيًا مع تحديد EventTime الذي بدأ تطبيقها فيه يمكنك أيضًا استخدام PlaybackStats.getPlaybackStateAtTime للاطّلاع على الحالة في وقت معيّن بالساعات العادية.
  • mediaTimeHistory: سجلّ يتضمن أزواجًا من وقت الساعة العادي ووقت الوسائط، ما يتيح لك إعادة إنشاء أجزاء الوسائط التي تم تشغيلها في وقت معيّن. يمكنك أيضًا استخدام PlaybackStats.getMediaTimeMsAtRealtimeMs للاطّلاع على نقطة بدء التقديم/الترجيع في وقت محدّد على مدار الساعة.
  • videoFormatHistory وaudioFormatHistory: قائمتان بتنسيقات الفيديو والملفّات الصوتية التي تم استخدامها أثناء التشغيل مع EventTime الذي بدأ استخدامه
  • fatalErrorHistory وnonFatalErrorHistory: قائمتان مرتبتان بالأخطاء الفادحة والأخطاء غير الفادحة مع EventTime التي حدثت فيها الأخطاء الفادحة هي تلك التي أدّت إلى إنهاء التشغيل، في حين أنّ الأخطاء غير الفادحة قد يكون من الممكن استردادها.

بيانات إحصاءات التشغيل الفردي

يتم جمع هذه البيانات تلقائيًا في حال استخدام PlaybackStatsListener، حتى مع keepHistory=false. القيم النهائية هي الحقول العامة التي يمكنك العثور عليها في PlaybackStats Javadoc ومدد حالات التشغيل التي يعرضها getPlaybackStateDurationMs. لتسهيل الأمر، ستجد أيضًا methods مثل 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 معًا من خلال الاتصال بالرقم PlaybackStats.merge. وستتضمّن علامة 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());

مواضيع متقدّمة

ربط بيانات الإحصاءات ببيانات تعريف التشغيل

عند جمع بيانات الإحصاءات لعمليات تشغيل فردية، قد تحتاج إلى ربط بيانات إحصاءات التشغيل بالبيانات الوصفية عن الوسائط التي يتم تشغيلها.

ننصحك بضبط البيانات الوصفية الخاصة بالوسائط باستخدام MediaItem.Builder.setTag. علامة الوسائط هي جزء من EventTime التي يتم الإبلاغ عنها للأحداث الأوّلية وعند اكتمال PlaybackStats، حتى يمكن استرجاعها بسهولة عند التعامل مع بيانات الإحصاءات المقابلة:

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

إعداد تقارير عن أحداث الإحصاءات المخصّصة

إذا كنت بحاجة إلى إضافة أحداث مخصّصة إلى بيانات الإحصاءات، عليك حفظ هذه الأحداث في بنية البيانات الخاصة بك ودمجها مع PlaybackStats التي يتم الإبلاغ عنها لاحقًا. إذا كان ذلك مفيدًا، يمكنك توسيع نطاق DefaultAnalyticsCollector للتمكّن من إنشاء مثيلات EventTime لأحداثك المخصّصة وإرسالها إلى المستمعين المسجّلين مسبقًا كما هو موضّح في المثال التالي.

Kotlin

private interface ExtendedListener : AnalyticsListener {
  fun onCustomEvent(eventTime: EventTime)
}

private class ExtendedCollector : DefaultAnalyticsCollector(Clock.DEFAULT) {
  fun customEvent() {
    val eventTime = generateCurrentPlayerMediaPeriodEventTime()
    sendEvent(eventTime, CUSTOM_EVENT_ID) { listener: AnalyticsListener ->
      if (listener is ExtendedListener) {
        listener.onCustomEvent(eventTime)
      }
    }
  }
}

// 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

private interface ExtendedListener extends AnalyticsListener {
  void onCustomEvent(EventTime eventTime);
}

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

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