Analytics

ExoPlayer תומך במגוון רחב של צורכי ניתוח נתוני הפעלה. בסופו של דבר, ניתוח נתונים מבוסס על איסוף, פירוש, צבירה וסיכום של נתונים מההפעלות. ניתן להשתמש בנתונים האלה במכשיר – לדוגמה, רישום ביומן, ניפוי באגים או כדי לקבל החלטות עתידיות לגבי ההפעלה - או דיווח שרת למעקב אחר הפעלות בכל המכשירים.

בדרך כלל, מערכת ניתוח הנתונים צריכה לאסוף קודם אירועים ואז לעבד אותם כדי שיהיו משמעותיים:

  • אוסף אירועים: כדי לעשות זאת, אפשר לרשום AnalyticsListener ב-ExoPlayer מכונה. מאזינים רשומים של 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 שהועבר לקריאה החוזרת כדי לזהות ההפעלה הסתיימה. אפשר לצבור נתונים של ניתוח נתונים עבור הפעלות מרובות. אפשר גם להריץ שאילתה על 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.
        }));

ה-constructor של PlaybackStatsListener מאפשר לשמור את כל היסטוריה של אירועים שעובדו. הערה: הפעולות האלה עלולות לגרום לתקורה לא ידועה של הזיכרון בהתאם למשך ההפעלה ולמספר האירועים. לכן אתם צריך להפעיל אותה רק אם נדרשת גישה לכל ההיסטוריה של העיבוד אירועים, במקום רק לנתוני הניתוח הסופיים.

חשוב לשים לב ש-PlaybackStats משתמש בקבוצה מורחבת של מצבים כדי לציין לא רק את מצב המדיה, אלא גם את כוונת המשתמש להפעיל ולהציג באופן מפורט יותר כמו הסיבה להפסקה או לסיום של ההפעלה:

מצב ההפעלה כוונת המשתמש לשחק אין לי כוונה לשחק
לפני ההפעלה JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
הפעלה פעילה PLAYING
ההפעלה נקטעת BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
מדינות (States) סיום 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. לנוחיותכם, תוכלו למצוא גם כמו 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();