Oyuncu etkinlikleri

Oynatıcının durumunda yapılan değişiklikler (ör. oynatmanın başlatılması, arabelleğe alma veya hatalar), kayıtlı Player.Listener örneklerine gönderilen etkinlikleri tetikler. Bu etkinlikler, tam sayı sabitleriyle gösterilir ve Player.Event ile Player.Events tarafından tanımlanır.

Player.Listener kaydetme

Oynatıcı etkinlikleri, kayıtlı Player.Listener örneklerine bildirilir. Bu tür etkinlikleri almak için bir işleyici kaydetmek üzere:

Kotlin

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

Java

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

Kotlin kullanıyorsanız media3-common-ktx modülü tarafından sağlanan askıya alma uzantısı işlevlerini de kullanarak eşzamanlı rutinler aracılığıyla etkinlikleri dinleyebilirsiniz. Bu durumda, Player.Listener kaydetmeniz veya kaydını silmeniz gerekmez.

Player.Listener kullanarak oynatma etkinliklerini dinleme

Player.Listener, boş varsayılan yöntemlere sahiptir. Bu nedenle yalnızca ilgilendiğiniz yöntemleri uygulamanız gerekir. Yöntemlerin ve ne zaman çağrıldıklarının tam açıklaması için Javadoc'e bakın. En önemli yöntemlerden bazıları aşağıda daha ayrıntılı olarak açıklanmıştır.

Dinleyiciler, tek tek etkinlik geri çağırmalarını veya bir ya da daha fazla etkinlik birlikte gerçekleştiğinde çağrılan genel bir onEvents geri çağırmayı uygulayabilir. Farklı kullanım alanlarında hangisinin tercih edilmesi gerektiğiyle ilgili açıklama için Individual callbacks vs onEvents bölümüne bakın.

Oynatma durumu değişiklikleri

Oynatıcı durumundaki değişiklikler, kayıtlı bir Player.Listener içinde onPlaybackStateChanged(@State int state) uygulanarak alınabilir. Oynatıcı, dört oynatma durumundan birinde olabilir:

  • Player.STATE_IDLE: Bu, başlangıç durumu, oynatıcı durdurulduğundaki durum ve oynatma başarısız olduğundaki durumdur. Oynatıcı bu durumda yalnızca sınırlı kaynak tutar.
  • Player.STATE_BUFFERING: Oynatıcı, mevcut konumundan hemen oynatmaya başlayamıyor. Bu durumun temel nedeni, daha fazla verinin yüklenmesi gerekmesidir.
  • Player.STATE_READY: Oynatıcı, mevcut konumundan hemen oynatmaya başlayabilir.
  • Player.STATE_ENDED: Oynatıcı, tüm medyaları oynatmayı tamamladı.

Bu durumların yanı sıra, oynatıcıda kullanıcının oynatma isteğini belirtmek için playWhenReady işareti bulunur. Bu işaretle ilgili değişiklikler, onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) uygulanarak alınabilir.

Aşağıdaki üç koşulun tümü karşılandığında bir oynatıcı oynatılıyor (yani konumu ilerliyor ve kullanıcıya medya sunuluyor) demektir:

  • Oyuncu Player.STATE_READY durumundadır.
  • playWhenReady: true
  • Oynatma, Player.getPlaybackSuppressionReason tarafından döndürülen bir nedenle engellenmiyor.

Bu özelliklerin tek tek kontrol edilmesi yerine Player.isPlaying çağrılabilir. Bu durumdaki değişiklikler, onIsPlayingChanged(boolean isPlaying) uygulanarak alınabilir:

Kotlin

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.
      }
    }
  }
)

Java

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.
        }
      }
    });

Oynatma hataları

Oynatmanın başarısız olmasına neden olan hatalar, kayıtlı bir Player.Listener içinde onPlayerError(PlaybackException error) uygulanarak alınabilir. Bir hata oluştuğunda bu yöntem, oynatma durumu Player.STATE_IDLE'ya geçmeden hemen önce çağrılır. Başarısız olan veya durdurulan oynatma işlemleri, ExoPlayer.prepare çağrılarak yeniden denenebilir.

Bazı Player uygulamalarının, başarısızlık hakkında ek bilgi sağlamak için PlaybackException alt sınıflarının örneklerini ilettiğini unutmayın. Örneğin, ExoPlayer, type, rendererIndex ve ExoPlayer'a özel diğer alanları içeren ExoPlaybackException'ı iletir.

Aşağıdaki örnekte, HTTP ağ iletişimi sorunu nedeniyle oynatmanın başarısız olduğu durumların nasıl tespit edileceği gösterilmektedir:

Kotlin

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.
        }
      }
    }
  }
)

Java

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.
          }
        }
      }
    });

Oynatma listesi geçişleri

Oynatma listesindeki yeni bir medya öğesine geçildiğinde, kayıtlı Player.Listener nesnelerinde onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) çağrılır. Nedeni, bunun otomatik bir geçiş mi, arama mı (örneğin, player.next() arandıktan sonra), aynı öğenin tekrarı mı yoksa oynatma listesi değişikliğinden mi (örneğin, şu anda oynatılan öğe kaldırılırsa) kaynaklandığını gösterir.

Meta veri

player.getCurrentMediaMetadata()'dan döndürülen meta veriler birçok nedenden dolayı değişebilir: oynatma listesi geçişleri, akış içi meta veri güncellemeleri veya mevcut MediaItem'nın oynatma sırasında güncellenmesi.

Meta veri değişiklikleriyle ilgileniyorsanız (ör. mevcut başlığı gösteren bir kullanıcı arayüzünü güncellemek için) onMediaMetadataChanged dinleyebilirsiniz.

Oynatma konumu değiştiriliyor

Player.seekTo yöntemlerinin çağrılması, kayıtlı Player.Listener örneklerine yönelik bir dizi geri çağırmayla sonuçlanır:

  1. reason=DISCONTINUITY_REASON_SEEK ile onPositionDiscontinuity. Bu, Player.seekTo işlevinin doğrudan sonucudur. Geri çağırma işlevinde, arama işleminden önceki ve sonraki konum için PositionInfo alanları bulunur.
  2. Arama işlemiyle ilgili anlık durum değişikliğiyle onPlaybackStateChanged. Böyle bir değişiklik olmayabilir.

Bireysel geri aramalar ve onEvents

Dinleyiciler, onIsPlayingChanged(boolean isPlaying) gibi tek tek geri çağırmaları ve genel onEvents(Player player, Events events) geri çağırmayı kullanabilir. Genel geri arama, Player nesnesine erişim sağlar ve birlikte gerçekleşen events kümesini belirtir. Bu geri çağırma, her zaman tek tek etkinliklere karşılık gelen geri çağırmalardan sonra çağrılır.

Kotlin

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

Java

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

Aşağıdaki durumlarda bağımsız etkinlikler tercih edilmelidir:

  • Dinleyici, değişikliklerin nedenleriyle ilgileniyor. Örneğin, onPlayWhenReadyChanged veya onMediaItemTransition için sağlanan nedenler.
  • Dinleyici yalnızca geri çağırma parametreleri aracılığıyla sağlanan yeni değerlere göre hareket eder veya geri çağırma parametrelerine bağlı olmayan başka bir şeyi tetikler.
  • Dinleyici uygulaması, yöntem adında etkinliği neyin tetiklediğine dair net ve okunabilir bir göstergeyi tercih eder.
  • Dinleyici, tüm bireysel etkinlikler ve durum değişiklikleri hakkında bilgi sahibi olması gereken bir analiz sistemine rapor gönderir.

Aşağıdaki durumlarda genel onEvents(Player player, Events events) tercih edilmelidir:

  • Dinleyici, birden fazla etkinlik için aynı mantığı tetiklemek istiyor. Örneğin, hem onPlaybackStateChanged hem de onPlayWhenReadyChanged için kullanıcı arayüzünü güncelleme.
  • Dinleyicinin, başka etkinlikleri tetiklemek için Player nesnesine erişmesi gerekir. Örneğin, medya öğesi geçişinden sonra arama yapma.
  • Dinleyici, ayrı geri çağırma işlevleri aracılığıyla bildirilen birden fazla durum değerini birlikte veya Player getter yöntemleriyle birlikte kullanmayı planlıyor. Örneğin, onTimelineChanged içinde sağlanan Timeline ile Player.getCurrentWindowIndex() kullanmak yalnızca onEvents geri çağırma işlevi içinde güvenlidir.
  • Dinleyici, etkinliklerin mantıksal olarak birlikte gerçekleşip gerçekleşmediğiyle ilgilenir. Örneğin, bir medya öğesi geçişi nedeniyle onPlaybackStateChanged ile STATE_BUFFERING arasında.

Bazı durumlarda dinleyicilerin, tek tek geri aramaları genel onEvents geri aramasıyla birleştirmesi gerekebilir. Örneğin, medya öğesi değişikliği nedenlerini onMediaItemTransition ile kaydetmek için. Ancak yalnızca tüm durum değişiklikleri onEvents içinde birlikte kullanılabildiğinde işlem yapılabilir.

Eş yordamları kullanarak oynatma etkinliklerini dinleme

Alternatif olarak, Player.listenTo kullanarak bir Kotlin eş yordamı başlatabilir ve ilgili Player.Event'ı belirtebilirsiniz:

Player.listen ve Player.listenTo'ın herhangi bir iş parçacığından çağrılabileceğini, geri çağırma lambdasının ise her zaman Player.getApplicationLooper ile ilişkili iş parçacığında çağrıldığını unutmayın. Bu nedenle, eş yordam farklı bir iş parçacığında başlatılmış olsa bile geri çağırma lambda'sının içindeki Player yöntemlerine ve durum özelliklerine erişmek güvenlidir.

Oynatma durumu değişiklikleri

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.
    }
  }
}

Oynatma hataları

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
      }
    }
  }
}

Bireysel geri aramalar ve onEvents

Bir coroutine içinde oynatıcı etkinliklerini dinlerken her zaman tek bir geri çağırma yerine onEvents geri çağırması için uygulama sağlayacaksınız. Hangi etkinliklerin lambda işlevinizin çağrılmasını tetiklemesi gerektiğine bağlı olarak Player.listen ve Player.listenTo arasında seçim yapabilirsiniz. Ancak işlevler başka açılardan eşdeğerdir:

dinle

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

Birden fazla etkinlik türüyle ilgileniyorsanız Player.listenTo işlevine etkinlik listesi iletebilirsiniz. Bu etkinliklerden herhangi biri gerçekleştiğinde lambda'nız çağrılır ve hangi etkinliklerin gerçekten tetiklendiğini kontrol etmek için Events parametresini inceleyebilirsiniz:

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

Bu işlevler onEvents üzerinde çalıştığından, onMediaItemTransition(..., int reason) içindeki neden veya onPositionDiscontinuity(...) içindeki oldPosition gibi tek tek geri çağırmalara iletilen geçici bağımsız değişkenlere erişim sağlamaz. Mantığınız bu belirli bağımsız değişkenlere bağlıysa (ve bunlar Player üzerinde durum özellikleri olarak kullanılamıyorsa) bunun yerine standart Player.Listener arayüzünü kullanmanız gerekir.

AnalyticsListener cihazını kullan

ExoPlayer kullanılırken addAnalyticsListener aranarak oynatıcıya AnalyticsListener kaydedilebilir. AnalyticsListener uygulamaları, analiz ve günlük kaydı amacıyla kullanılabilecek ayrıntılı etkinlikleri dinleyebilir. Daha fazla bilgi için lütfen analizler sayfasını inceleyin.

EventLogger cihazını kullan

EventLogger, günlük kaydı amacıyla doğrudan kitaplık tarafından sağlanan bir AnalyticsListener'dır. Tek bir satırla yararlı ek günlük kaydını etkinleştirmek için EventLogger öğesini ExoPlayer öğesine ekleyin:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Daha fazla ayrıntı için hata ayıklama günlük kaydı sayfasına bakın.

Belirtilen oynatma konumlarında etkinlikleri tetikleme

Bazı kullanım alanlarında, etkinliklerin belirli oynatma konumlarında tetiklenmesi gerekir. Bu özellik, PlayerMessage kullanılarak desteklenir. PlayerMessage, ExoPlayer.createMessage kullanılarak oluşturulabilir. Yürütülmesi gereken oynatma konumu PlayerMessage.setPosition kullanılarak ayarlanabilir. İletiler varsayılan olarak oynatma iş parçacığında yürütülür ancak bu, PlayerMessage.setLooper kullanılarak özelleştirilebilir. PlayerMessage.setDeleteAfterDelivery, belirtilen oynatma konumu her karşılaşıldığında (arama ve tekrar modları nedeniyle bu durum birden çok kez gerçekleşebilir) mesajın yürütülüp yürütülmeyeceğini veya yalnızca ilk kez yürütülüp yürütülmeyeceğini kontrol etmek için kullanılabilir. PlayerMessage yapılandırıldıktan sonra PlayerMessage.send kullanılarak planlanabilir.

Kotlin

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

Java

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