Özelleştirme

ExoPlayer kitaplığının temelinde Player arayüzü bulunur. Player, medya arabelleğe alma, oynatma, duraklatma ve arama gibi geleneksel üst düzey medya oynatıcı işlevlerini sunar. Varsayılan uygulama ExoPlayer, oynatılan medyanın türü, nasıl ve nerede depolandığı ve nasıl oluşturulduğu hakkında çok az varsayımda bulunacak (ve dolayısıyla çok az kısıtlama getirecek) şekilde tasarlanmıştır. ExoPlayer uygulamaları, medyanın yüklenmesini ve oluşturulmasını doğrudan uygulamak yerine bu işi, bir oynatıcı oluşturulduğunda veya oynatıcıya yeni medya kaynakları iletildiğinde eklenen bileşenlere devreder. Tüm ExoPlayer uygulamalarında ortak olan bileşenler şunlardır:

  • Oynatılacak medyayı tanımlayan, medyayı yükleyen ve yüklenen medyanın okunabileceği MediaSource örnekleri. Oyuncunun içindeki bir MediaSource.Factory, MediaItem kullanarak MediaSource örneği oluşturur. Ayrıca, medya kaynağı tabanlı oynatma listesi API'si kullanılarak doğrudan oynatıcıya da aktarılabilirler.
  • MediaItem değerini MediaSource değerine dönüştüren bir MediaSource.Factory örneği. MediaSource.Factory, oynatıcı oluşturulduğunda eklenir.
  • Medyanın ayrı ayrı bileşenlerini oluşturan Renderer örnek. Bunlar, oynatıcı oluşturulduğunda eklenir.
  • MediaSource tarafından sağlanan parçaları seçerek her bir Renderer tarafından kullanılmasını sağlayan TrackSelector. Oynatıcı oluşturulduğunda bir TrackSelector eklenir.
  • MediaSource öğesinin ne zaman daha fazla medya arabelleğe alacağını ve ne kadar medyanın arabelleğe alınacağını kontrol eden bir LoadControl. Oynatıcı oluşturulduğunda bir LoadControl eklenir.
  • Oynatıcının yapılandırılmış bir canlı ofsete yakın kalmasını sağlamak için canlı oynatmalar sırasında oynatma hızını kontrol eden bir LivePlaybackSpeedControl. Oynatıcı oluşturulduğunda bir LivePlaybackSpeedControl eklenir.

Oynatıcı işlevinin parçalarını uygulayan bileşenleri ekleme kavramı, kitaplığın tamamında mevcuttur. Bazı bileşenlerin varsayılan uygulamaları, işi daha fazla eklenen bileşene devreder. Bu sayede birçok alt bileşen, özel olarak yapılandırılmış uygulamalarla ayrı ayrı değiştirilebilir.

Oynatıcı özelleştirme

Bileşen ekleyerek oynatıcıyı özelleştirmeyle ilgili bazı yaygın örnekler aşağıdaki bölümlerde açıklanmıştır.

Ağ yığınını yapılandırma

ExoPlayer tarafından kullanılan ağ yığınını özelleştirme ile ilgili bir sayfamız var.

Ağdan yüklenen veriler önbelleğe alınıyor

Geçici anlık önbelleğe alma ve medya indirme ile ilgili kılavuzları inceleyin.

Sunucu etkileşimlerini özelleştirme

Bazı uygulamalar HTTP isteklerini ve yanıtlarını kesmek isteyebilir. Özel istek başlıkları eklemek, sunucunun yanıt başlıklarını okumak, isteklerin URI'lerini değiştirmek vb. isteyebilirsiniz. Örneğin, uygulamanız medya segmentlerini isterken başlık olarak bir jeton ekleyerek kimliğini doğrulayabilir.

Aşağıdaki örnekte, DefaultMediaSourceFactory içine özel bir DataSource.Factory ekleyerek bu davranışların nasıl uygulanacağı gösterilmektedir:

Kotlin

val dataSourceFactory = DataSource.Factory {
  val dataSource = httpDataSourceFactory.createDataSource()
  // Set a custom authentication request header.
  dataSource.setRequestProperty("Header", "Value")
  dataSource
}
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

DataSource.Factory dataSourceFactory =
    () -> {
      HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
      // Set a custom authentication request header.
      dataSource.setRequestProperty("Header", "Value");
      return dataSource;
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

Yukarıdaki kod snippet'inde, eklenen HttpDataSource her HTTP isteğine "Header: Value" başlığını ekler. Bu davranış, bir HTTP kaynağıyla her etkileşim için sabitlenmiştir.

Daha ayrıntılı bir yaklaşım için ResolvingDataSource kullanarak tam zamanında davranış ekleyebilirsiniz. Aşağıdaki kod snippet'i, bir HTTP kaynağıyla etkileşime girmeden hemen önce istek başlıklarının nasıl ekleneceğini gösterir:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time request headers.
    dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time request headers.
        dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));

Ayrıca, aşağıdaki snippet'te gösterildiği gibi URI'da tam zamanında değişiklikler yapmak için ResolvingDataSource kullanabilirsiniz:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time URI resolution logic.
    dataSpec.withUri(resolveUri(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time URI resolution logic.
        dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));

Hata işlemeyi özelleştirme

Özel bir LoadErrorHandlingPolicy uygulamak, uygulamaların ExoPlayer'ın yükleme hatalarına tepki verme şeklini özelleştirmesine olanak tanır. Örneğin, bir uygulama birçok kez yeniden denemek yerine hızlı bir şekilde başarısız olmak isteyebilir veya oyuncunun her yeniden deneme arasında ne kadar bekleyeceğini kontrol eden geri alma mantığını özelleştirmek isteyebilir. Aşağıdaki snippet'te, özel geri yükleme aralığı mantığının nasıl uygulanacağı gösterilmektedir:

Kotlin

val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
  object : DefaultLoadErrorHandlingPolicy() {
    override fun getRetryDelayMsFor(
      loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo
    ): Long {
      // Implement custom back-off logic here.
      return 0
    }
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
    )
    .build()

Java

LoadErrorHandlingPolicy loadErrorHandlingPolicy =
    new DefaultLoadErrorHandlingPolicy() {
      @Override
      public long getRetryDelayMsFor(LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo) {
        // Implement custom back-off logic here.
        return 0;
      }
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
        .build();

LoadErrorInfo bağımsız değişkeni, mantığı hata türüne veya başarısız isteğe göre özelleştirmek için başarısız yükleme hakkında daha fazla bilgi içerir.

Ayıklayıcı işaretlerini özelleştirme

Çıkarıcı işaretleri, her bir biçimin progresif medyadan nasıl çıkarılacağını özelleştirmek için kullanılabilir. DefaultMediaSourceFactory için sağlanan DefaultExtractorsFactory üzerinde ayarlanabilir. Aşağıdaki örnekte, MP3 akışları için dizine dayalı aramayı etkinleştiren bir işaret iletilmektedir.

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
    .build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

Sabit bit hızı aramasını etkinleştirme

MP3, ADTS ve AMR akışları için FLAG_ENABLE_CONSTANT_BITRATE_SEEKING işaretleriyle sabit bit hızı varsayımını kullanarak yaklaşık aramayı etkinleştirebilirsiniz. Bu işaretler, daha önce açıklandığı gibi ayrı DefaultExtractorsFactory.setXyzExtractorFlags yöntemleri kullanılarak ayrı ayıklayıcılar için ayarlanabilir. Sabit bit hızı arama özelliğini destekleyen tüm ayıklayıcılar için bu özelliği etkinleştirmek istiyorsanız DefaultExtractorsFactory.setConstantBitrateSeekingEnabled kullanın.

Kotlin

val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);

ExtractorsFactory, daha önce ayıklayıcı işaretlerini özelleştirme bölümünde açıklandığı gibi DefaultMediaSourceFactory kullanılarak yerleştirilebilir.

Eşzamansız arabellek sıraya almayı etkinleştirme

Eşzamansız arabellek sıraya alma, ExoPlayer'ın oluşturma ardışık düzeninde yapılan bir geliştirmedir. MediaCodec örneklerini eşzamansız modda çalıştırır ve verilerin kodunu çözüp oluşturmayı planlamak için ek iş parçacıkları kullanır. Bu ayarı etkinleştirmek, kare düşüşlerini ve ses kesintilerini azaltabilir.

Eşzamansız arabellek sırası, Android 12 (API düzeyi 31) veya sonraki sürümleri çalıştıran cihazlarda varsayılan olarak etkindir ve Android 6.0 (API düzeyi 23) sürümünden itibaren manuel olarak etkinleştirilebilir. Özellikle DRM korumalı veya yüksek kare hızlı içerikler oynatılırken kare düşüşleri ya da ses kesintileri gözlemlediğiniz belirli cihazlar için bu özelliği etkinleştirebilirsiniz.

En basit durumda, oynatıcıya aşağıdaki gibi bir DefaultRenderersFactory eklemeniz gerekir:

Kotlin

val renderersFactory =
  DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing()
val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()

Java

DefaultRenderersFactory renderersFactory =
    new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();

Oluşturucuları doğrudan örneklendiriyorsanız MediaCodecVideoRenderer ve MediaCodecAudioRenderer oluşturucularına new DefaultMediaCodecAdapter.Factory(context).forceEnableAsynchronous() değerini iletin.

ForwardingSimpleBasePlayer ile işlemleri özelleştirme

Bir Player örneğinin bazı davranışlarını, ForwardingSimpleBasePlayer alt sınıfına sarmalayarak özelleştirebilirsiniz. Bu sınıf, Player yöntemlerini doğrudan uygulamak yerine belirli "işlemleri" kesmenize olanak tanır. Bu sayede, örneğin play(), pause() ve setPlayWhenReady(boolean) gibi işlevlerin tutarlı bir şekilde çalışması sağlanır. Ayrıca tüm durum değişikliklerinin kayıtlı Player.Listener örneklerine doğru şekilde yayılmasını sağlar. Çoğu özelleştirme kullanım alanında, bu tutarlılık garantileri nedeniyle hataya daha açık olan ForwardingPlayer yerine ForwardingSimpleBasePlayer tercih edilmelidir.

Örneğin, oynatma başlatıldığında veya durdurulduğunda özel bir mantık eklemek için:

Kotlin

class PlayerWithCustomPlay(player: Player) : ForwardingSimpleBasePlayer(player) {
  override fun handleSetPlayWhenReady(playWhenReady: Boolean): ListenableFuture<*> {
    // Add custom logic
    return super.handleSetPlayWhenReady(playWhenReady)
  }
}

Java

public static final class PlayerWithCustomPlay extends ForwardingSimpleBasePlayer {

  public PlayerWithCustomPlay(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
    // Add custom logic
    return super.handleSetPlayWhenReady(playWhenReady);
  }
}

SEEK_TO_NEXT komutuna izin vermemek (ve Player.seekToNext komutunun işlem yapmamasını sağlamak) için:

Kotlin

class PlayerWithoutSeekToNext(player: Player) : ForwardingSimpleBasePlayer(player) {
  override fun getState(): State {
    val state = super.getState()
    return state
      .buildUpon()
      .setAvailableCommands(
        state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build()
      )
      .build()
  }

  // We don't need to override handleSeek, because it is guaranteed not to be called for
  // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable.
}

Java

public static final class PlayerWithoutSeekToNext extends ForwardingSimpleBasePlayer {

  public PlayerWithoutSeekToNext(Player player) {
    super(player);
  }

  @Override
  protected State getState() {
    State state = super.getState();
    return state
        .buildUpon()
        .setAvailableCommands(
            state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build())
        .build();
  }

  // We don't need to override handleSeek, because it is guaranteed not to be called for
  // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable.
}

MediaSource özelleştirmesi

Yukarıdaki örnekler, oynatıcıya iletilen tüm MediaItem nesnelerinin oynatılması sırasında kullanılacak özelleştirilmiş bileşenleri ekler. Ayrıntılı özelleştirme gerektiğinde, özelleştirilmiş bileşenleri doğrudan oynatıcıya aktarılabilen ayrı MediaSource örneklerine eklemek de mümkündür. Aşağıdaki örnekte, özel bir DataSource.Factory, ExtractorsFactory ve LoadErrorHandlingPolicy kullanmak için ProgressiveMediaSource öğesinin nasıl özelleştirileceği gösterilmektedir:

Kotlin

val mediaSource =
  ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
    .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
    .createMediaSource(MediaItem.fromUri(streamUri))

Java

ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
        .createMediaSource(MediaItem.fromUri(streamUri));

Özel bileşenler oluşturma

Kitaplık, yaygın kullanım alanları için bu sayfanın üst kısmında listelenen bileşenlerin varsayılan uygulamalarını sağlar. ExoPlayer bu bileşenleri kullanabilir ancak standart dışı davranışlar gerekiyorsa özel uygulamaları kullanacak şekilde de oluşturulabilir. Özel uygulamaların bazı kullanım alanları şunlardır:

  • Renderer: Kitaplık tarafından sağlanan varsayılan uygulamaların desteklemediği bir medya türünü işlemek için özel bir Renderer uygulamak isteyebilirsiniz.
  • TrackSelector: Özel bir TrackSelector uygulamak, uygulama geliştiricinin, MediaSource tarafından sunulan parçaların mevcut Renderer'lerin her biri tarafından kullanılmak üzere seçilme şeklini değiştirmesine olanak tanır.
  • LoadControl: Özel bir LoadControl uygulamak, uygulama geliştiricinin oynatıcının arabelleğe alma politikasını değiştirmesine olanak tanır.
  • Extractor: Kitaplık tarafından desteklenmeyen bir kapsayıcı biçimini desteklemeniz gerekiyorsa özel bir Extractor sınıfı uygulamayı düşünebilirsiniz.
  • MediaSource: Oluşturuculara özel bir şekilde beslenecek medya örnekleri elde etmek veya özel MediaSource birleştirme davranışı uygulamak istiyorsanız özel bir MediaSource sınıfı uygulamak uygun olabilir.
  • MediaSource.Factory: Özel bir MediaSource.Factory uygulamak, bir uygulamanın MediaItem öğesinden MediaSource oluşturma şeklini özelleştirmesine olanak tanır.
  • DataSource: ExoPlayer'ın üst paketinde, farklı kullanım alanları için çeşitli DataSource uygulamaları zaten mevcuttur. Verileri başka bir şekilde (ör. özel bir protokol üzerinden, özel bir HTTP yığını kullanarak veya özel bir kalıcı önbellekten) yüklemek için kendi DataSource sınıfınızı uygulamak isteyebilirsiniz.

Özel bileşenler oluştururken aşağıdakileri yapmanızı öneririz:

  • Özel bir bileşenin uygulamaya etkinlikleri geri bildirmesi gerekiyorsa bunu mevcut ExoPlayer bileşenleriyle aynı modeli kullanarak yapmanızı öneririz. Örneğin, EventDispatcher sınıflarını kullanarak veya bileşenin oluşturucusuna bir dinleyiciyle birlikte Handler ileterek.
  • Özel bileşenlerin, oynatma sırasında uygulama tarafından yeniden yapılandırılabilmesi için mevcut ExoPlayer bileşenleriyle aynı modeli kullanmasını öneririz. Bunun için özel bileşenler PlayerMessage.Target arayüzünü uygulamalı ve yapılandırma değişikliklerini handleMessage yönteminde almalıdır. Uygulama kodu, ExoPlayer'ın createMessage yöntemini çağırarak, mesajı yapılandırarak ve PlayerMessage.send kullanarak bileşene göndererek yapılandırma değişikliklerini iletmelidir. Mesajların oynatma iş parçacığında yayınlanmak üzere gönderilmesi, oynatıcıda gerçekleştirilen diğer işlemlerle sırayla yürütülmesini sağlar.