Analytics

ExoPlayer طیف گسترده‌ای از نیازهای تجزیه و تحلیل پخش را پشتیبانی می‌کند. در نهایت، تجزیه و تحلیل در مورد جمع‌آوری، تفسیر، تجمیع و خلاصه‌سازی داده‌ها از پخش‌ها است. این داده‌ها می‌توانند یا در دستگاه - به عنوان مثال برای ثبت وقایع، اشکال‌زدایی یا برای اطلاع‌رسانی در مورد تصمیمات پخش آینده - یا به یک سرور گزارش شوند تا پخش‌ها را در تمام دستگاه‌ها نظارت کنند.

یک سیستم تحلیلی معمولاً ابتدا باید رویدادها را جمع‌آوری کند و سپس آنها را پردازش کند تا معنادار شوند:

  • جمع‌آوری رویدادها : این کار را می‌توان با ثبت یک AnalyticsListener روی یک نمونه ExoPlayer انجام داد. شنونده‌های تحلیلی ثبت‌شده، رویدادها را هنگام استفاده از پخش‌کننده دریافت می‌کنند. هر رویداد با آیتم رسانه‌ای مربوطه در لیست پخش و همچنین موقعیت پخش و ابرداده‌های زمان‌بندی مرتبط است.
  • پردازش رویداد : برخی از سیستم‌های تحلیلی، رویدادهای خام را به یک سرور آپلود می‌کنند و تمام پردازش رویداد در سمت سرور انجام می‌شود. همچنین می‌توان رویدادها را روی دستگاه پردازش کرد و انجام این کار ممکن است ساده‌تر باشد یا میزان اطلاعات مورد نیاز برای آپلود را کاهش دهد. ExoPlayer PlaybackStatsListener ارائه می‌دهد که به شما امکان می‌دهد مراحل پردازش زیر را انجام دهید:
    1. تفسیر رویداد : برای اینکه رویدادها برای اهداف تحلیلی مفید باشند، باید در چارچوب یک پخش واحد تفسیر شوند. برای مثال، رویداد خام تغییر وضعیت بازیکن به STATE_BUFFERING ممکن است مربوط به بافرینگ اولیه، بافرینگ مجدد یا بافرینگی باشد که پس از یک جستجو اتفاق می‌افتد.
    2. ردیابی وضعیت : این مرحله رویدادها را به شمارنده تبدیل می‌کند. برای مثال، رویدادهای تغییر وضعیت را می‌توان به شمارنده‌هایی تبدیل کرد که میزان زمان صرف شده در هر وضعیت پخش را ردیابی می‌کنند. نتیجه، مجموعه‌ای اولیه از مقادیر داده‌های تحلیلی برای یک پخش واحد است.
    3. تجمیع : این مرحله، داده‌های تحلیلی را در چندین پخش، معمولاً با جمع کردن شمارنده‌ها، ترکیب می‌کند.
    4. محاسبه معیارهای خلاصه : بسیاری از مفیدترین معیارها، آنهایی هستند که میانگین‌ها را محاسبه می‌کنند یا مقادیر داده‌های تحلیلی اولیه را به روش‌های دیگر ترکیب می‌کنند. معیارهای خلاصه را می‌توان برای پخش‌های تکی یا چندگانه محاسبه کرد.

جمع‌آوری رویداد با AnalyticsListener

رویدادهای پخش خام از پخش‌کننده به پیاده‌سازی‌های AnalyticsListener گزارش می‌شوند. شما می‌توانید به راحتی شنونده‌ی خود را اضافه کنید و فقط متدهایی را که به آنها علاقه‌مند هستید، بازنویسی کنید:

کاتلین

exoPlayer.addAnalyticsListener(
  object : AnalyticsListener {
    override fun onPlaybackStateChanged(eventTime: EventTime, @Player.State state: Int) {}

    override fun onDroppedVideoFrames(
      eventTime: EventTime,
      droppedFrames: Int,
      elapsedMs: Long,
    ) {}
  }
)

جاوا

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 ارسالی به فراخوانی برای شناسایی پخش پایان‌یافته استفاده کنید. می‌توان داده‌های تحلیلی را برای چندین پخش جمع‌آوری کرد . همچنین می‌توان در هر زمان با استفاده از PlaybackStatsListener.getPlaybackStats() از PlaybackStats برای جلسه پخش فعلی پرس‌وجو کرد.

کاتلین

exoPlayer.addAnalyticsListener(
  PlaybackStatsListener(/* keepHistory= */ true) {
    eventTime: EventTime?,
    playbackStats: PlaybackStats?
    -> // Analytics data for the session started at `eventTime` is ready.
  }
)

جاوا

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 را نیز خواهید یافت که مدت زمان ترکیبات حالت پخش خاص را برمی‌گردانند.

کاتلین

Log.d(
  "DEBUG",
  "Playback summary: " +
    "play time = " +
    playbackStats.totalPlayTimeMs +
    ", rebuffers = " +
    playbackStats.totalRebufferCount,
)

جاوا

Log.d(
    "DEBUG",
    "Playback summary: "
        + "play time = "
        + playbackStats.getTotalPlayTimeMs()
        + ", rebuffers = "
        + playbackStats.totalRebufferCount);

داده‌های تحلیلی تجمیع‌شده از چندین پخش

شما می‌توانید چندین PlaybackStats با فراخوانی PlaybackStats.merge با هم ترکیب کنید. PlaybackStats حاصل شامل داده‌های تجمیع‌شده‌ی تمام پخش‌های ادغام‌شده خواهد بود. توجه داشته باشید که شامل تاریخچه‌ی رویدادهای پخش جداگانه نخواهد بود، زیرا این رویدادها قابل تجمیع نیستند.

می‌توان از PlaybackStatsListener.getCombinedPlaybackStats برای دریافت یک نمای کلی از تمام داده‌های تحلیلی جمع‌آوری‌شده در طول عمر یک PlaybackStatsListener استفاده کرد.

معیارهای خلاصه محاسبه شده

علاوه بر داده‌های تحلیلی اولیه، PlaybackStats روش‌های زیادی برای محاسبه معیارهای خلاصه ارائه می‌دهد.

کاتلین

Log.d(
  "DEBUG",
  "Additional calculated summary metrics: " +
    "average video bitrate = " +
    playbackStats.meanVideoFormatBitrate +
    ", mean time between rebuffers = " +
    playbackStats.meanTimeBetweenRebuffers,
)

جاوا

Log.d(
    "DEBUG",
    "Additional calculated summary metrics: "
        + "average video bitrate = "
        + playbackStats.getMeanVideoFormatBitrate()
        + ", mean time between rebuffers = "
        + playbackStats.getMeanTimeBetweenRebuffers());

مباحث پیشرفته

مرتبط کردن داده‌های تحلیلی با فراداده‌های پخش

هنگام جمع‌آوری داده‌های تحلیلی برای پخش‌های جداگانه، ممکن است بخواهید داده‌های تحلیلی پخش را با فراداده‌های مربوط به رسانه در حال پخش مرتبط کنید.

توصیه می‌شود فراداده‌های مختص رسانه را با MediaItem.Builder.setTag تنظیم کنید. تگ media بخشی از EventTime گزارش‌شده برای رویدادهای خام و پس از اتمام PlaybackStats است، بنابراین می‌توان آن را هنگام مدیریت داده‌های تحلیلی مربوطه به راحتی بازیابی کرد:

کاتلین

PlaybackStatsListener(/* keepHistory= */ false) {
  eventTime: EventTime,
  playbackStats: PlaybackStats ->
  val mediaTag =
    eventTime.timeline
      .getWindow(eventTime.windowIndex, Timeline.Window())
      .mediaItem
      .localConfiguration
      ?.tag
  // Report playbackStats with mediaTag metadata.
}

جاوا

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 را برای رویدادهای سفارشی خود ایجاد کنید و آنها را به شنوندگان از قبل ثبت شده ارسال کنید، همانطور که در مثال زیر نشان داده شده است.

کاتلین

@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()
}

جاوا

@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();
}