يدعم ExoPlayer مجموعة كبيرة من احتياجات إحصاءات التشغيل. في النهاية، تتمحور الإحصاءات حول جمع البيانات من عمليات التشغيل وتفسيرها وتجميعها وتلخيصها. ويمكن استخدام هذه البيانات إما على الجهاز، مثل تسجيل المحتوى أو تصحيح الأخطاء أو للإبلاغ عن قرارات التشغيل المستقبلية، أو إرسال تقارير إلى أحد الخوادم لمراقبة عمليات التشغيل على جميع الأجهزة.
يحتاج نظام التحليلات عادةً إلى جمع الأحداث أولاً، ثم معالجتها بشكل أكبر لجعلها ذات مغزى:
- مجموعة الفعاليات:
يمكن إجراء ذلك من خلال تسجيل
AnalyticsListener
في مثيلExoPlayer
. يتلقّى مستمعو التحليلات المسجّلون الأحداث عند حدوثها أثناء استخدام المشغّل. ويتم ربط كل حدث بعنصر الوسائط المقابل في قائمة التشغيل، بالإضافة إلى موضع التشغيل والبيانات الوصفية للطابع الزمني. - معالجة الأحداث:
تُحمِّل بعض أنظمة الإحصاءات أحداثًا أولية إلى خادم، ويتم تنفيذ جميع عمليات معالجة الأحداث من جهة الخادم. من الممكن أيضًا معالجة الأحداث على الجهاز،
وقد يكون ذلك أكثر بساطة أو يقلل من مقدار المعلومات
التي يجب تحميلها. يوفّر ExoPlayer إمكانية استخدام
PlaybackStatsListener
التي تتيح لك تنفيذ خطوات المعالجة التالية:- تفسير الأحداث: لتحقيق الاستفادة من أغراض الإحصاءات، يجب
تفسير الأحداث في سياق عملية تشغيل واحدة. على سبيل المثال، إنّ الحدث الأولي لتغيير حالة المشغّل إلى
STATE_BUFFERING
قد يتطابق مع عملية تخزين مؤقت مبدئية أو عملية إعادة تخزين مؤقت أو عملية تخزين مؤقت تحدث بعد عملية التقديم والترجيع. - تتبُّع الحالة: تحوِّل هذه الخطوة الأحداث إلى عدّادات. على سبيل المثال، يمكن تحويل أحداث تغيير الحالة إلى عدّادات تتّبع الوقت المستغرَق في كل حالة تشغيل. والنتيجة هي مجموعة أساسية من قيم بيانات الإحصاءات لعملية تشغيل واحدة.
- التجميع: تجمع هذه الخطوة بين بيانات الإحصاءات على مستوى عمليات التشغيل المتعددة، وذلك عادةً من خلال إضافة عدّادات.
- احتساب مقاييس الملخّص: إنّ العديد من المقاييس الأكثر فائدة هي تلك التي تحتسب المتوسطات أو تجمع قيم بيانات الإحصاءات الأساسية بطرق أخرى. يمكن احتساب مقاييس الملخص لعمليات تشغيل فردية أو متعددة.
- تفسير الأحداث: لتحقيق الاستفادة من أغراض الإحصاءات، يجب
تفسير الأحداث في سياق عملية تشغيل واحدة. على سبيل المثال، إنّ الحدث الأولي لتغيير حالة المشغّل إلى
مجموعة الأحداث باستخدام 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. }));
توفّر طريقة إنشاء 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. });
إعداد تقارير عن أحداث الإحصاءات المخصّصة
إذا كنت بحاجة إلى إضافة أحداث مخصّصة إلى بيانات الإحصاءات، عليك حفظ
هذه الأحداث في بنية بياناتك ودمجها مع
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();