رویدادهای بازیکن

تغییرات در وضعیت «پخش‌کننده» (مثل شروع بازپخش، بافرینگ، یا خطاها) باعث راه‌اندازی رویدادهایی می‌شود که به نمونه‌های ثبت‌شده Player.Listener ارسال می‌شوند. این رویدادها با ثابت‌های عدد صحیح نشان داده می‌شوند و Player.Event و Player.Events آن‌ها را تعریف می‌کنند.

ثبت Player.Listener

رویدادهای پخش‌کننده به نمونه‌های ثبت‌شده Player.Listener گزارش می‌شود. برای ثبت شنونده‌ای برای دریافت چنین رویدادهایی:

کاتلین

// Add a listener to receive events from the player.
player.addListener(listener)

جاوا

// Add a listener to receive events from the player.
player.addListener(listener);

اگر از Kotlin استفاده می‌کنید، می‌توانید از توابع افزونه تعلیق‌کننده ارائه‌شده توسط واحد media3-common-ktx نیز برای گوش دادن به رویدادها بااستفاده از روتین‌های هم‌زمان استفاده کنید. در این صورت، نیازی به ثبت یا لغو ثبت Player.Listener به‌صورت صریح نخواهید داشت.

بااستفاده از Player.Listener به رویدادهای بازپخش گوش دهید

‫Player.Listener روش‌های پیش‌فرض خالی دارد، بنابراین فقط باید روش‌هایی را که به آن‌ها علاقه دارید پیاده‌سازی کنید. برای شرح کامل روش‌ها و زمان فراخوانی آن‌ها، به Javadoc مراجعه کنید. برخی‌از مهم‌ترین روش‌ها در زیر با جزئیات بیشتری شرح داده شده است.

شنوندگان می‌توانند از بین پیاده‌سازی کردن برگشت‌های تماس رویداد تکی یا برگشت تماس onEvents عمومی که پس‌از وقوع یک یا چند رویداد باهم فراخوانی می‌شود انتخاب کنند. برای توضیح اینکه کدام یک برای موارد استفاده مختلف باید ترجیح داده شود، به Individual callbacks vs onEvents مراجعه کنید.

تغییرات وضعیت بازپخش

با پیاده‌سازی onPlaybackStateChanged(@State int state) در Player.Listener ثبت‌شده، می‌توان تغییرات وضعیت پخش‌کننده را دریافت کرد. پخش‌کننده می‌تواند در یکی از چهار وضعیت بازپخش باشد:

  • Player.STATE_IDLE: این وضعیت اولیه است، وضعیتی که پخش‌کننده متوقف شده است و زمانی که بازپخش ناموفق است. بازیکن در این حالت فقط منابع محدودی خواهد داشت.
  • ‫Player.STATE_BUFFERING: پخش‌کننده نمی‌تواند بلافاصله از موقعیت فعلی خود پخش کند. این اتفاق بیشتر به این دلیل رخ می‌دهد که داده‌های بیشتری باید بار شود.
  • ‫Player.STATE_READY: پخش‌کننده می‌تواند بلافاصله از موقعیت فعلی خود پخش را شروع کند.
  • ‫Player.STATE_ENDED: پخش‌کننده پخش همه رسانه‌ها را تمام کرد.

علاوه‌بر این وضعیت‌ها، پخش‌کننده یک پرچم playWhenReady برای نشان دادن قصد کاربر برای پخش دارد. با پیاده‌سازی onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) می‌توانید تغییرات این پرچم را دریافت کنید.

وقتی هر سه شرط زیر برقرار باشد، یک پخش‌کننده درحال پخش است (یعنی موقعیت آن درحال پیشرفت است و رسانه به کاربر ارائه می‌شود):

  • بازیکن در وضعیت Player.STATE_READY است
  • playWhenReady true است
  • بازپخش به‌دلیل برگشت داده‌شده توسط Player.getPlaybackSuppressionReason متوقف نشده است

به‌جای اینکه مجبور باشید این دارایی‌ها را به‌صورت جداگانه بررسی کنید، می‌توانید Player.isPlaying را فراخوانی کنید. با پیاده‌سازی onIsPlayingChanged(boolean isPlaying) می‌توانید تغییرات این وضعیت را دریافت کنید:

کاتلین

player.addListener(
  object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      if (isPlaying) {
        // Active playback.
      } else {
        // Not playing because playback is paused, ended, suppressed, or the player
        // is buffering, stopped or failed. Check player.playWhenReady,
        // player.playbackState, player.playbackSuppressionReason and
        // player.playerError for details.
      }
    }
  }
)

جاوا

player.addListener(
    new Player.Listener() {
      @Override
      public void onIsPlayingChanged(boolean isPlaying) {
        if (isPlaying) {
          // Active playback.
        } else {
          // Not playing because playback is paused, ended, suppressed, or the player
          // is buffering, stopped or failed. Check player.getPlayWhenReady,
          // player.getPlaybackState, player.getPlaybackSuppressionReason and
          // player.getPlaybackError for details.
        }
      }
    });

خطاهای بازپخش

خطاهایی که باعث ناموفق شدن بازپخش می‌شوند را می‌توان با پیاده‌سازی onPlayerError(PlaybackException error) در Player.Listener ثبت‌شده دریافت کرد. وقتی خطایی رخ می‌دهد، این روش بلافاصله قبل‌از انتقال وضعیت بازپخش به Player.STATE_IDLE فراخوانی می‌شود. بازپخش‌های ناموفق یا متوقف‌شده را می‌توان با تماس گرفتن با ExoPlayer.prepare دوباره امتحان کرد.

توجه داشته باشید که برخی‌از پیاده‌سازی‌های Player نمونه‌هایی از زیرکلاس‌های PlaybackException را برای ارائه اطلاعات بیشتر درباره خرابی ارسال می‌کنند. برای مثال، ExoPlayer ExoPlaybackException را ارسال می‌کند که type، rendererIndex، و دیگر فیلدهای مختص ExoPlayer را دارد.

مثال زیر نشان می‌دهد که چگونه تشخیص دهید که بازپخش به‌دلیل یک مشکل شبکه HTTP ناموفق بوده است:

کاتلین

player.addListener(
  object : Player.Listener {
    override fun onPlayerError(error: PlaybackException) {
      val cause = error.cause
      if (cause is HttpDataSourceException) {
        // An HTTP error occurred.
        val httpError = cause
        // It's possible to find out more about the error both by casting and by querying
        // the cause.
        if (httpError is InvalidResponseCodeException) {
          // Cast to InvalidResponseCodeException and retrieve the response code, message
          // and headers.
        } else {
          // Try calling httpError.getCause() to retrieve the underlying cause, although
          // note that it may be null.
        }
      }
    }
  }
)

جاوا

player.addListener(
    new Player.Listener() {
      @Override
      public void onPlayerError(PlaybackException error) {
        @Nullable Throwable cause = error.getCause();
        if (cause instanceof HttpDataSourceException) {
          // An HTTP error occurred.
          HttpDataSourceException httpError = (HttpDataSourceException) cause;
          // It's possible to find out more about the error both by casting and by querying
          // the cause.
          if (httpError instanceof HttpDataSource.InvalidResponseCodeException) {
            // Cast to InvalidResponseCodeException and retrieve the response code, message
            // and headers.
          } else {
            // Try calling httpError.getCause() to retrieve the underlying cause, although
            // note that it may be null.
          }
        }
      }
    });

گذار فهرست پخش

هرگاه پخش‌کننده به یک عنصر رسانه‌ای جدید در فهرست پخش تغییر کند، onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) روی اشیای ثبت‌شده Player.Listener فراخوانده می‌شود. دلیل نشان می‌دهد که آیا این یک انتقال خودکار، یک جستجو (برای مثال پس‌از تماس با player.next())، تکرار همان مورد، یا ناشی از تغییر فهرست پخش (برای مثال، اگر مورد درحال پخش حذف شود) بوده است.

فراداده

فراداده برگشتی از player.getCurrentMediaMetadata() می‌تواند به دلایل زیادی تغییر کند: انتقال فهرست پخش، به‌روزرسانی‌های فراداده در جاری‌سازی، یا به‌روزرسانی MediaItem فعلی در حین پخش.

اگر به تغییرات فراداده علاقه‌مند هستید، برای مثال برای به‌روزرسانی واسط کاربری که عنوان فعلی را نشان می‌دهد، می‌توانید به onMediaMetadataChanged گوش دهید.

درحال جستجو

فراخوانی روش‌های Player.seekTo منجر به مجموعه‌ای از برگشت تماس به نمونه‌های ثبت‌شده Player.Listener می‌شود:

  1. ‫onPositionDiscontinuity با reason=DISCONTINUITY_REASON_SEEK. این نتیجه مستقیم تماس با Player.seekTo است. این برگشتی دارای PositionInfo فیلد برای موقعیت قبل و بعداز جستجو است.
  2. onPlaybackStateChanged با هرگونه تغییر وضعیت فوری مربوط به جستجو. توجه داشته باشید که ممکن است چنین تغییری وجود نداشته باشد.

تماس‌های برگشتی فردی دربرابر onEvents

شنوندگان می‌توانند بین پیاده‌سازی برگشت تماس‌های فردی مثل onIsPlayingChanged(boolean isPlaying) و برگشت تماس عمومی onEvents(Player player, Events events) انتخاب کنند. ‫callback عمومی دسترسی به شیء Player را فراهم می‌کند و مجموعه events را که با هم رخ داده‌اند مشخص می‌کند. این بازخوان همیشه پس‌از بازخوان‌هایی که مربوط به رویدادهای تکی هستند فراخوانی می‌شود.

کاتلین

override fun onEvents(player: Player, events: Player.Events) {
  if (
    events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) ||
      events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)
  ) {
    uiModule.updateUi(player)
  }
}

جاوا

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
      || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
    uiModule.updateUi(player);
  }
}

در موارد زیر، رویدادهای فردی باید ترجیح داده شوند:

  • شنونده به دلایل تغییرات علاقه‌مند است. برای مثال، دلایل ارائه‌شده برای onPlayWhenReadyChanged یا onMediaItemTransition.
  • شنونده فقط روی مقادیر جدیدی که ازطریق پارامترهای تماس برگشتی ارائه شده است عمل می‌کند یا چیز دیگری را راه‌اندازی می‌کند که به پارامترهای تماس برگشتی بستگی ندارد.
  • پیاده‌سازی شنونده ترجیح می‌دهد نشانگر خوانا و واضحی از آنچه رویداد را در نام روش راه‌اندازی کرده است داشته باشد.
  • شنونده به سیستم تجزیه‌وتحلیلی گزارش می‌دهد که باید از همه رویدادهای تکی و تغییرات وضعیت مطلع باشد.

در موارد زیر، onEvents(Player player, Events events) عمومی باید ترجیح داده شود:

  • شنونده می‌خواهد منطق یکسانی را برای چندین رویداد راه‌اندازی کند. برای مثال، به‌روزرسانی واسط کاربر برای onPlaybackStateChanged و onPlayWhenReadyChanged.
  • شنونده برای راه‌اندازی رویدادهای بیشتر باید به شیء Player دسترسی داشته باشد، برای مثال، جستجو پس‌از انتقال عنصر رسانه‌ای.
  • شنونده قصد دارد از چندین مقدار وضعیت که ازطریق چندین برگشت‌پذیر جداگانه گزارش می‌شوند به‌صورت ترکیبی یا همراه با روش‌های دریافت‌کننده Player استفاده کند. برای مثال، استفاده از Player.getCurrentWindowIndex() با Timeline ارائه‌شده در onTimelineChanged فقط از درون onEvents بازخوانی ایمن است.
  • شنونده علاقه‌مند است بداند آیا رویدادها به‌طور منطقی با هم رخ داده‌اند یا نه. برای مثال، onPlaybackStateChanged به STATE_BUFFERING به‌دلیل گذار عنصر رسانه‌ای.

در برخی موارد، شنوندگان ممکن است نیاز داشته باشند تا برگشتی‌های تماس فردی را با برگشتی تماس عمومی onEvents ترکیب کنند، برای مثال برای ثبت دلایل تغییر عنصر رسانه‌ای با onMediaItemTransition، اما فقط زمانی اقدام کنند که همه تغییرات وضعیت بتوانند به‌طور هم‌زمان در onEvents استفاده شوند.

گوش دادن به رویدادهای بازپخش بااستفاده از روتین‌های همکار

یا می‌توانید بااستفاده از Player.listenTo یک روال مشترک Kotlin راه‌اندازی کنید و Player.Event مربوطه را مشخص کنید:

توجه داشته باشید که Player.listen و Player.listenTo را می‌توان از هر رشته‌ای فراخوانی کرد، درحالی‌که لامبدای تماس برگشتی همیشه در رشته مرتبط با Player.getApplicationLooper فراخوانی می‌شود. بنابراین، دسترسی به روش‌های Player و ویژگی‌های وضعیت در داخل لامبدای برگشت تماس ایمن است، حتی اگر روتین همکار در رشته‌ای متفاوت راه‌اندازی شده باشد.

تغییرات وضعیت بازپخش

coroutineScope.launch {
  player.listenTo(Player.EVENT_IS_PLAYING_CHANGED) {
    // `Player` is a receiver scope for this trailing lambda
    if (isPlaying) {
      // Active playback.
    } else {
      // Not playing.
    }
  }
}

خطاهای بازپخش

coroutineScope.launch {
  player.listenTo(Player.EVENT_PLAYER_ERROR) {
    val error = playerError ?: return@listenTo
    val cause = error.cause
    if (cause is HttpDataSourceException) {
      // An HTTP error occurred.
      if (cause is InvalidResponseCodeException) {
        // Retrieve the response code, message and headers
      } else {
        // Try calling cause.cause to retrieve the underlying cause
      }
    }
  }
}

تماس‌های برگشتی فردی دربرابر onEvents

هنگام گوش دادن به رویدادهای پخش‌کننده در یک روتین همکار، همیشه پیاده‌سازی برای onEvents برگشت تماس را ارائه خواهید داد، برخلاف برگشت تماس تکی. بسته به اینکه کدام رویدادها باید باعث فراخوانی لامبدای شما شوند، می‌توانید بین Player.listen و Player.listenTo انتخاب کنید. اما توابع درغیراین‌صورت معادل هستند:

گوش دادن

coroutineScope.launch {
  player.listen { events ->
    // `Player` is a receiver scope for this trailing lambda
    if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
      // Access the player state directly from the receiver
      updateUi(playbackState)
    }

    if (events.contains(Player.EVENT_PLAYER_ERROR)) {
      // Access the error directly from the player
      handleError(playerError)
    }
  }
}

listenTo

coroutineScope.launch {
  player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events ->
    // `Player` is a receiver scope for this trailing lambda
    if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
      // Access the player state directly from the receiver
      updateUi(playbackState)
    }

    if (events.contains(Player.EVENT_PLAYER_ERROR)) {
      // Access the error directly from the player
      handleError(playerError)
    }
  }
}

اگر به چند نوع رویداد علاقه‌مندید، می‌توانید فهرستی از رویدادها را به Player.listenTo ارسال کنید. هرگاه هریک از آن رویدادها رخ دهد، لامبدای شما فراخوانده می‌شود و می‌توانید پارامتر Events را بازرسی کنید تا ببینید کدام رویدادها واقعاً راه‌اندازی شده‌اند:

coroutineScope.launch {
  player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events ->
    // Unclear which event got triggered without querying `events` parameter
    // The following function will fire whenever either one is caught
    updateUiAndHandleError(playbackState, playerError)
  }
}

ازآنجایی‌که این عملکردها در onEvents کار می‌کنند، به استدلال‌های گذرا که به برگشتی‌های جداگانه ارسال می‌شوند، مثل دلیل در onMediaItemTransition(..., int reason) یا oldPosition در onPositionDiscontinuity(...) دسترسی ندارند. اگر منطق شما به این آرگومان‌های خاص متکی است (و این آرگومان‌ها به‌عنوان دارایی‌های حالت در Player دردسترس نیستند)، باید به‌جای آن از میانای استاندارد Player.Listener استفاده کنید.

استفاده از AnalyticsListener

هنگام استفاده از ExoPlayer، با تماس با addAnalyticsListener می‌توان AnalyticsListener را در بازیکن ثبت کرد. پیاده‌سازی‌های AnalyticsListener می‌توانند به رویدادهای دقیقی که ممکن است برای اهداف تحلیل و ثبت گزارش مفید باشند گوش دهند. لطفاً برای جزئیات بیشتر به صفحه Analytics مراجعه کنید.

استفاده از EventLogger

‫EventLogger یک AnalyticsListener است که مستقیماً توسط کتابخانه برای اهداف گزارش‌دهی ارائه می‌شود. برای فعال کردن گزارش‌گیری اضافی مفید با یک خط، EventLogger را به ExoPlayer اضافه کنید:

کاتلین

player.addAnalyticsListener(EventLogger())

جاوا

player.addAnalyticsListener(new EventLogger());

برای جزئیات بیشتر، صفحه ثبت گزارش اشکال‌زدایی را ببینید.

رویدادها را در موقعیت‌های بازپخش مشخص‌شده فعال کنید

برخی‌از موارد استفاده نیاز به فعال کردن رویدادها در موقعیت‌های مشخص پخش دارند. این ویژگی بااستفاده از PlayerMessage پشتیبانی می‌شود. ‫PlayerMessage را می‌توان بااستفاده از ExoPlayer.createMessage ایجاد کرد. موقعیت بازپخش که در آن باید اجرا شود را می‌توان بااستفاده از PlayerMessage.setPosition تنظیم کرد. پیام‌ها به‌طور پیش‌فرض در رشته بازپخش اجرا می‌شوند، اما می‌توان این را بااستفاده از PlayerMessage.setLooper سفارشی‌سازی کرد. از PlayerMessage.setDeleteAfterDelivery می‌توان برای کنترل اینکه آیا پیام هر بار که موقعیت بازپخش مشخص‌شده با آن مواجه می‌شود اجرا شود (این ممکن است به‌دلیل حالت‌های جستجو و تکرار چندین بار اتفاق بیفتد) یا فقط اولین بار استفاده کرد. پس‌از پیکربندی PlayerMessage، می‌توان آن را بااستفاده از PlayerMessage.send زمان‌بندی کرد.

کاتلین

player
  .createMessage { messageType: Int, payload: Any? -> }
  .setLooper(Looper.getMainLooper())
  .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000)
  .setPayload(customPayloadData)
  .setDeleteAfterDelivery(false)
  .send()

جاوا

player
    .createMessage(
        (messageType, payload) -> {
          // Do something at the specified playback position.
        })
    .setLooper(Looper.getMainLooper())
    .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000)
    .setPayload(customPayloadData)
    .setDeleteAfterDelivery(false)
    .send();