Oyuncu etkinlikleri

Oynatıcının durumundaki değişiklikler (ör. oynatmanın başlaması, arabelleğe alma veya hatalar), kayıtlı Player.Listener örneklerine gönderilen etkinlikleri tetikler. Bu etkinlikler tam sayı sabitleriyle temsil edilir 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 istiyorsanız:

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 eş yordamları kullanarak etkinlikleri dinlemek için media3-common-ktx modülü tarafından sağlanan askıya alma uzantısı işlevlerini de kullanabilirsiniz. Bu durumda, Player.Listener kaydetmeniz veya kaydını iptal etmeniz gerekmez.

Player.Listener kullanarak oynatma etkinliklerini dinleme

Player.Listener boş varsayılan yöntemlere sahip olduğundan yalnızca ilgilendiğiniz yöntemleri uygulamanız gerekir. Yöntemlerin tam açıklaması ve ne zaman çağrıldıkları için Javadoc'u inceleyin. En önemli yöntemlerden bazıları daha sonra ayrıntılı olarak açıklanacaktır.

Dinleyiciler, bağımsız etkinlik geri çağırmaları veya bir ya da daha fazla etkinlik birlikte gerçekleştikten sonra çağrılan genel bir onEvents geri çağırma işlevi uygulama arasında seçim yapabilir. Farklı kullanım alanları için 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

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

  • Player.STATE_IDLE: Bu, ilk durumdur. Oynatıcı durdurulduğunda ve oynatma başarısız olduğunda bu durum geçerlidir. Oyuncu bu durumda yalnızca sınırlı kaynaklara sahip olur.
  • Player.STATE_BUFFERING: Oynatıcı, mevcut konumundan hemen oynatamıyor. Bu durum genellikle daha fazla verinin yüklenmesi gerektiğinde ortaya çıkar.
  • Player.STATE_READY: Oynatıcı, mevcut konumundan hemen oynatabilir.
  • Player.STATE_ENDED: Oynatıcı tüm medyayı oynatmayı tamamladı.

Bu durumlara ek olarak, oynatıcıda kullanıcının oynatma niyetini gösteren bir playWhenReady işareti bulunur. Bu işaretteki değişiklikler, onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) uygulanarak alınabilir.

Aşağıdaki üç koşulun tümü karşılandığında oynatıcı oynatılıyordur (yani konumu ilerliyordur ve medya kullanıcıya sunuluyordur):

  • Oynatıcı Player.STATE_READY durumunda
  • playWhenReady: true
  • Oynatma, Player.getPlaybackSuppressionReason tarafından döndürülen bir nedenle engellenmedi

Bu özellikleri tek tek kontrol etmek 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, oynatma durumu Player.STATE_IDLE olarak değişmeden hemen önce bu yöntem ç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, hatayla ilgili 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 özgü diğer alanları içeren ExoPlaybackException değerini iletir.

Aşağıdaki örnekte, bir HTTP ağ sorunu nedeniyle oynatmanın başarısız olduğu durumun 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.
        }
      }
    }
 s.kt

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

Oynatma listesi geçişleri

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

Meta veri

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

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

Oynatma konumu değiştiriliyor

Player.seekTo yöntemlerini çağırmak, kayıtlı Player.Listener örneklerine bir dizi geri çağırma işlemiyle sonuçlanır:

  1. onPositionDiscontinuity, reason=DISCONTINUITY_REASON_SEEK ile. Bu, Player.seekTo yöntemini çağırmanın doğrudan sonucudur. Geri çağırma, arama öncesi ve sonrası konum için PositionInfo alanlarına sahiptir.
  2. onPlaybackStateChanged, aramayla ilgili anlık durum değişikliğiyle birlikte. Böyle bir değişiklik olmayabileceğini unutmayın.

Etkileşimli arama çubuğu sürükleme için ExoPlayer.setScrubbingModeEnabled ile scrubbing modunu etkinleştirirseniz ExoPlayer oynatmayı bastırır ve Player.PLAYBACK_SUPPRESSION_REASON_SCRUBBING ile onPlaybackSuppressionReasonChanged yöntemini çağırır. ExoPlayer, yerini alan ara aramaları bırakıp AnalyticsListener.onDroppedSeeksWhileScrubbing öğesine bildirse bile her Player.seekTo çağrısı onPositionDiscontinuity öğesini tetikler.

Bireysel geri aramalar ve onEvents

Dinleyiciler, onIsPlayingChanged(boolean isPlaying) gibi ayrı geri çağırmaları ve genel onEvents(Player player, Events events) geri çağırmayı uygulama arasında seçim yapabilir. Genel geri çağırma, Player nesnesine erişim sağlar ve birlikte gerçekleşen events grubunu belirtir. Bu geri çağırma, her zaman bağımsız 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 bireysel etkinlikler tercih edilmelidir:

  • Dinleyici, değişikliklerin nedenleriyle ilgilenir. Örneğin, onPlayWhenReadyChanged veya onMediaItemTransition için belirtilen nedenler.
  • İşleyici yalnızca geri çağırma parametreleri aracılığıyla sağlanan yeni değerlere göre işlem yapar veya geri çağırma parametrelerine bağlı olmayan başka bir şeyi tetikler.
  • İşleyici uygulaması, yöntemin adında etkinliği neyin tetiklediğinin açık ve okunabilir bir şekilde belirtilmesini tercih eder.
  • Dinleyici, tüm bağımsız etkinlikleri ve durum değişikliklerini bilmesi gereken bir analiz sistemine rapor gönderir.

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

  • Dinleyici, birden fazla etkinlik için aynı mantığı tetiklemek istiyor. Örneğin, hem onPlaybackStateChanged hem de onPlayWhenReadyChanged için bir kullanıcı arayüzünü güncelleme.
  • Dinleyicinin, başka etkinlikleri tetiklemek için Player nesnesine erişmesi gerekir. Örneğin, bir medya öğesi geçişinden sonra arama yapma.
  • Dinleyici, ayrı geri çağırmalar aracılığıyla bildirilen birden fazla durum değerini birlikte veya Player alıcı yöntemleriyle birlikte kullanmayı amaçlar. Örneğin, onTimelineChanged içinde sağlanan Timeline ile Player.getCurrentWindowIndex() kullanmak yalnızca onEvents geri çağırma işlevinde 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, STATE_BUFFERING olarak değişir.

Bazı durumlarda dinleyicilerin, tek tek geri çağırmaları genel onEvents geri çağırmasıyla birleştirmesi gerekebilir. Örneğin, medya öğesi değişiklik nedenlerini onMediaItemTransition ile kaydetmek ancak yalnızca tüm durum değişiklikleri onEvents içinde birlikte kullanılabildiğinde işlem yapmak için.

Eş yordamları kullanarak oynatma etkinliklerini dinleme

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

Player.listen ve Player.listenTo herhangi bir iş parçacığından çağrılabilir. Geri çağırma lambda ise her zaman Player.getApplicationLooper ile ilişkili iş parçacığında çağrılır. Bu nedenle, eş yordam farklı bir iş parçacığında başlatılmış olsa bile geri çağırma lambda'sındaki 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 eş yordamda oyuncu etkinliklerini dinlerken tek bir geri çağırma yerine her zaman onEvents geri çağırma için uygulama sağlarsınız. Lambda'nızın çağrılmasını tetiklemesi gereken etkinliklere bağlı olarak Player.listen ve Player.listenTo arasında seçim yapabilirsiniz. Ancak işlevler diğer 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 yöntemine bir etkinlik listesi iletebilirsiniz. Bu etkinliklerden herhangi biri gerçekleştiğinde lambda'nız çağrılır ve hangi etkinliklerin 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, ayrı geri çağırmalara iletilen geçici bağımsız değişkenlere erişim sağlamaz. Mantığınız bu bağımsız değişkenlere dayanıyorsa (ve bunlar Player üzerinde durum özellikleri olarak kullanılamıyorsa) standart Player.Listener arayüzünü kullanmanız gerekir.

AnalyticsListener cihazını kullan

ExoPlayer kullanılırken addAnalyticsListener çağrılarak oynatıcıya bir AnalyticsListener kaydedilebilir. AnalyticsListener uygulamaları, analiz ve günlük kaydı için yararlı olabilecek ayrıntılı etkinlikleri dinleyebilir. Daha fazla bilgi için analiz sayfasına bakın.

EventLogger cihazını kullan

EventLogger, günlük kaydı amacıyla doğrudan kitaplık tarafından sağlanan bir AnalyticsListener değeridir. Tek bir satırla faydalı 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 bilgi için hata ayıklama günlüğü sayfasına bakın.

Belirtilen oynatma konumlarında etkinlikleri tetikleme

Bazı kullanım alanları, belirli oynatma konumlarında etkinliklerin tetiklenmesini gerektirir. Bu, PlayerMessage kullanılarak desteklenir. ExoPlayer.createMessage kullanılarak PlayerMessage oluşturulabilir. Komutun yürütülmesi gereken oynatma konumu PlayerMessage.setPosition kullanılarak ayarlanabilir. Mesajlar varsayılan olarak oynatma iş parçacığında yürütülür ancak bu, PlayerMessage.setLooper kullanılarak özelleştirilebilir. PlayerMessage.setDeleteAfterDelivery, mesajın belirtilen oynatma konumuna her gelindiğinde mi (arama ve tekrar modları nedeniyle bu durum birden fazla kez gerçekleşebilir) yoksa yalnızca ilk seferde mi yürütüleceğ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();