ExoPlayer תומך במגוון רחב של צורכי ניתוח נתונים של הפעלה. בסופו של דבר, ניתוח נתונים עוסק באיסוף, בפירוש, בצבירה ובסיכום של נתונים מהפעלות. אפשר להשתמש בנתונים האלה במכשיר – לדוגמה, לרישום ביומן, לניפוי באגים או כדי לקבל החלטות לגבי הפעלה עתידית – או לדווח עליהם לשרת כדי לעקוב אחרי ההפעלות בכל המכשירים.
מערכת ניתוח נתונים בדרך כלל צריכה קודם לאסוף אירועים, ואז לעבד אותם כדי להפיק מהם תובנות:
- איסוף אירועים:
כדי לעשות את זה, צריך לרשום
AnalyticsListenerבמופע שלExoPlayer. מאזיני ניתוח נתונים רשומים מקבלים אירועים כשהם מתרחשים במהלך השימוש בנגן. כל אירוע משויך לקובץ המדיה המתאים בפלייליסט, וגם למטא-נתונים של מיקום ההפעלה וחותמת הזמן. - עיבוד אירועים:
בחלק ממערכות הניתוח, אירועים גולמיים מועלים לשרת, וכל עיבוד האירועים מתבצע בצד השרת. אפשר גם לעבד אירועים במכשיר, וכך לפשט את התהליך או לצמצם את כמות המידע שצריך להעלות. ExoPlayer מספק את
PlaybackStatsListener, שמאפשר לבצע את שלבי העיבוד הבאים:- פרשנות של אירועים: כדי שאירועים יהיו שימושיים למטרות ניתוח, צריך לפרש אותם בהקשר של הפעלה אחת. לדוגמה, האירוע הגולמי של שינוי מצב הנגן ל-
STATE_BUFFERINGיכול להתאים לטעינה ראשונית, לטעינה חוזרת או לטעינה שמתרחשת אחרי חיפוש. - מעקב אחרי מצב: בשלב הזה האירועים מומרים למונים. לדוגמה, אפשר להמיר אירועים של שינוי מצב למונים שעוקבים אחרי משך הזמן בכל מצב הפעלה. התוצאה היא קבוצה בסיסית של ערכי נתונים לניתוח של הפעלה אחת.
- צבירה: בשלב הזה משלבים את נתוני הניתוח של כמה הפעלות, בדרך כלל על ידי הוספת מוני צפיות.
- חישוב של מדדי סיכום: רבים מהמדדים השימושיים ביותר הם אלה שמחשבים ממוצעים או משלבים את ערכי הנתונים הבסיסיים של Analytics בדרכים אחרות. אפשר לחשב את מדדי הסיכום להפעלה אחת או לכמה הפעלות.
- פרשנות של אירועים: כדי שאירועים יהיו שימושיים למטרות ניתוח, צריך לפרש אותם בהקשר של הפעלה אחת. לדוגמה, האירוע הגולמי של שינוי מצב הנגן ל-
איסוף אירועים באמצעות 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 שמועבר לכל קריאה חוזרת (callback) משייך את האירוע לקובץ מדיה בפלייליסט, וגם למטא-נתונים של מיקום ההפעלה וחותמת הזמן:
-
realtimeMs: השעה שבה התרחש האירוע. -
timeline,windowIndexו-mediaPeriodId: מגדירים את הפלייליסט ואת הפריט בפלייליסט שאליו האירוע שייך. השדהmediaPeriodIdמכיל מידע אופציונלי נוסף, למשל אם האירוע שייך למודעה בתוך הפריט. -
eventPlaybackPositionMs: מיקום ההפעלה בפריט כשהאירוע התרחש. -
currentTimeline,currentWindowIndex,currentMediaPeriodIdו-currentPlaybackPositionMs: כמו שתואר קודם, אבל לפריט שמופעל כרגע. הפריט שמופעל כרגע עשוי להיות שונה מהפריט שאליו האירוע שייך, למשל אם האירוע תואם לטעינה מראש של הפריט הבא שיופעל.
עיבוד אירועים באמצעות PlaybackStatsListener
PlaybackStatsListener הוא AnalyticsListener שמטמיע עיבוד אירועים במכשיר. הוא מחשב את PlaybackStats, עם מונים ומדדים נגזרים, כולל:
- מדדי סיכום, למשל זמן ההפעלה הכולל.
- מדדים של איכות וידאו דינמית, למשל רזולוציית הווידאו הממוצעת.
- מדדי איכות העיבוד, למשל שיעור הפריימים שהושמטו.
- מדדי שימוש במשאבים, למשל מספר הבייטים שנקראו ברשת.
רשימה מלאה של הספירות הזמינות והמדדים הנגזרים מופיעה ב-PlaybackStats Javadoc.
PlaybackStatsListener מחשב PlaybackStats נפרד לכל קובץ מדיה בפלייליסט, וגם לכל מודעה בצד הלקוח שמוכנסת לפריטים האלה. אפשר לספק קריאה חוזרת (callback) ל-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 |
| מצבי סיום | 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. });
דיווח על אירועי 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(); }
מעקב אחרי מתגי feature flag ומצב Canary
כשמבצעים בדיקות A/B או בודקים את ההשפעה של אופטימיזציות חדשות ב-Media3, אפשר לקשר בין נתוני הניתוח של ההפעלה לבין feature flags פעילים ומצב Canary.
אתם יכולים לשלוח שאילתות ל-Flags.getLogString או לבדוק שינויים ספציפיים של דגלים באמצעות Flags.getEnabledOverrides ו-Flags.getDisabledOverrides כדי לכלול מצבי דגלים במטא-נתונים של הסשן או במטען הייעודי (payload) של ניתוח הנתונים בהתאמה אישית.