يتيح 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
@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(); }
تتبُّع علامات الميزات ووضع Canary
عند إجراء اختبارات A/B أو تقييم تأثير تحسينات Media3 الجديدة، يمكنك ربط بيانات إحصاءات التشغيل بإشارات الميزات النشطة ووضع Canary.
يمكنك الاستعلام عن Flags.getLogString أو فحص عمليات إلغاء علامات معيّنة باستخدام
Flags.getEnabledOverrides وFlags.getDisabledOverrides لتضمين حالات العلامات
في حمولات التحليلات المخصّصة أو البيانات الوصفية للجلسة.