ExoPlayer תומך במגוון רחב של צורכי ניתוח נתוני הפעלה. בסופו של דבר, ניתוח נתונים הוא איסוף, פירוש, צבירה וסיכום של נתונים מהפעלות. אפשר להשתמש בנתונים האלה במכשיר — לדוגמה, לרישום ביומן, ניפוי באגים או כדי לקבל החלטות מושכלות לגבי הפעלה בעתיד — או לדווח לשרת כדי לעקוב אחרי ההפעלות בכל המכשירים.
בדרך כלל, מערכת ניתוח נתונים צריכה קודם לאסוף אירועים, ואז לעבד אותם כדי שיהיה להם משמעות:
- איסוף אירועים: אפשר לעשות זאת על ידי רישום
AnalyticsListener
במכונה שלExoPlayer
. מאזינים רשומים של Analytics מקבלים אירועים בזמן שהם מתרחשים במהלך השימוש בנגן. כל אירוע משויך לפריט המדיה התואם בפלייליסט, וגם למיקום ההפעלה ולמטא-נתונים של חותמת הזמן. - עיבוד אירועים: יש מערכות ניתוח נתונים שמעלות אירועים גולמיים לשרת, וכל עיבוד האירועים מתבצע בצד השרת. אפשר גם לעבד אירועים במכשיר, וזה יכול להיות פשוט יותר או לצמצם את כמות המידע שצריך להעלות. 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
שמוענק לכל קריאה חוזרת משייך את האירוע לפריט מדיה בפלייליסט, וגם למטא-נתונים של מיקום ההפעלה ושל חותמת הזמן:
realtimeMs
: השעה לפי שעון הקיר של האירוע.timeline
, windowIndex
ו-mediaPeriodId
: מגדירים את הפלייליסט ואת הפריט בפלייליסט שאליו שייך האירוע. השדהmediaPeriodId
מכיל מידע נוסף אופציונלי, למשל, אם האירוע שייך למודעה בתוך הפריט.eventPlaybackPositionMs
: מיקום ההפעלה בפריט כשהאירוע התרחש.currentTimeline
,currentWindowIndex
,currentMediaPeriodId
ו-currentPlaybackPositionMs
: כמו למעלה, אבל לגבי הפריט שמוצג כרגע. יכול להיות שהפריט שמוצג כרגע שונה מהפריט שאליו שייך האירוע. לדוגמה, אם האירוע תואם לאחסון מראש של הפריט הבא שיוצג.
עיבוד אירועים באמצעות PlaybackStatsListener
PlaybackStatsListener
הוא AnalyticsListener
שמטמיע עיבוד אירועים במכשיר. היא מחשבת את PlaybackStats
, עם מונים ומדדים נגזרים, כולל:
- מדדי סיכום, למשל משך ההפעלה הכולל.
- מדדים מותאמים של איכות ההפעלה, למשל הרזולוציה הממוצעת של הסרטון.
- מדדי איכות הרינדור, למשל שיעור הפריימים שהוחמצו.
- מדדי שימוש במשאבים, למשל מספר הבייטים שנקראים ברשת.
רשימה מלאה של הספירות והמדדים הנגזרים הזמינים מופיעה ב-Javadoc של PlaybackStats
.
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();