Ö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 kullanıma sunar. Varsayılan uygulama ExoPlayer, oynatılan medya türü, nasıl ve nerede depolandığı ve nasıl oluşturulduğu hakkında çok az varsayımda bulunmak (dolayısıyla çok az kısıtlama uygulamak) için tasarlanmıştır. Yükleme ve oluşturma işlemlerini doğrudan uygulamak yerine, ExoPlayer uygulamalar bu işi, oynatıcı oluşturulduğunda veya oynatıcıya yeni medya kaynakları aktarıldığında eklenen bileşenlere devreder. Tüm ExoPlayer uygulamalarında ortak olan bileşenler şunlardır:

  • MediaSource oynatılacak medyayı tanımlayan, medyayı yükleyen ve yüklenen medyanın okunabileceği örnekler. Oynatıcıdaki MediaSource.Factory tarafından MediaItem öğesinden MediaSource örneği oluşturulur. Ayrıca, medya kaynağına dayalı oynatma listesi API'si kullanılarak doğrudan oynatıcıya da aktarılabilirler.
  • MediaItem değerini MediaSource değerine dönüştüren MediaSource.Factory örnek. Oynatıcı oluşturulduğunda MediaSource.Factory eklenir.
  • Medyanın ayrı ayrı bileşenlerini oluşturmak için kullanılan Renderer örnek. Bunlar, oynatıcı oluşturulduğunda yerleştirilir.
  • Her bir kullanılabilir Renderer tarafından kullanılacak MediaSource tarafından sağlanan parçaları seçen bir TrackSelector. Oynatıcı oluşturulduğunda TrackSelector yerleştirilir.
  • MediaSource'nin ne zaman daha fazla medya arabelleğe alacağını ve ne kadar medya arabelleğe alınacağını kontrol eden bir LoadControl. Oynatıcı oluşturulduğunda LoadControl eklenir.
  • Oynatıcının yapılandırılmış bir canlı ofsete yakın kalmasını sağlamak için canlı tekrarlar sırasında oynatma hızını kontrol eden bir LivePlaybackSpeedControl. Oyuncu oluşturulduğunda bir LivePlaybackSpeedControl eklenir.

Oyuncu işlevselliğinin parçalarını uygulayan bileşenleri yerleştirme kavramı, kitaplığın her yerinde mevcuttur. Bazı bileşenlerin varsayılan uygulamaları, işi daha fazla yerleştirilmiş bileşene devreder. Bu sayede birçok alt bileşen, özel olarak yapılandırılmış uygulamalarla ayrı ayrı değiştirilebilir.

Oynatıcıyı özelleştirme

Oynatıcıyı bileşen ekleyerek özelleştirmenin bazı yaygın örnekleri aşağıda açıklanmıştır.

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

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

Ağdan yüklenen verileri önbelleğe alma

Geçici anlık önbelleğe alma ve medya indirme ile ilgili kılavuzlara göz atın.

Sunucu etkileşimlerini özelleştirme

Bazı uygulamalar HTTP isteklerini ve yanıtlarını yakalamak 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 bir jetonu başlık olarak ekleyerek kimliğini doğrulayabilir.

Aşağıdaki örnekte, DataSource.Factory öğesine özel bir DefaultMediaSourceFactory 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, yerleştirilen HttpDataSource her HTTP isteğinde "Header: Value" üst bilgisini içerir. Bu davranış, HTTP kaynağıyla her etkileşim için düzeltilmiştir.

Daha ayrıntılı bir yaklaşım için ResolvingDataSource kullanarak tam zamanında davranış ekleyebilirsiniz. Aşağıdaki kod snippet'inde, bir HTTP kaynağıyla etkileşime girmeden hemen önce istek üst bilgilerinin nasıl yerleştirileceği gösterilmektedir:

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'de anlık 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 uygulaması, 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ıca başarısız olmak isteyebilir veya oynatıcının her yeniden deneme arasında ne kadar süre bekleyeceğini kontrol eden geri yükleme 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: 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(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, hata türüne veya başarısız isteğe göre mantığı özelleştirmek için yükleme başarısızlığı hakkında daha fazla bilgi içerir.

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

Ayıklayıcı işaretleri, tek tek biçimlerin aşamalı medyadan nasıl ayıklanacağını özelleştirmek için kullanılabilir. DefaultMediaSourceFactory'e sağlanan DefaultExtractorsFactory üzerinde ayarlanabilir. Aşağıdaki örnekte, MP3 akışlarında dizine dayalı arama özelliğini 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ında, FLAG_ENABLE_CONSTANT_BITRATE_SEEKING işaretleriyle sabit bit hızı varsayımı kullanarak yaklaşık arama özelliğini etkinleştirebilirsiniz. Bu işaretler, yukarıda açıklandığı gibi individual DefaultExtractorsFactory.setXyzExtractorFlags yöntemleri kullanılarak tek tek ayıklayıcılar için ayarlanabilir. Sabit bit hızlı 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, yukarıda açıklanan ayıklayıcı işaretlerini özelleştirme işleminde olduğu gibi DefaultMediaSourceFactory aracılığıyla eklenebilir.

Eşzamansız arabellek kuyruğa almayı etkinleştirme

Zaman uyumsuz arabellek kuyruğa alma, ExoPlayer'ın oluşturma işlem hattında yapılan bir iyileştirmedir. Bu iyileştirme, MediaCodec örneklerini zaman uyumsuz modda çalıştırır ve verilerin kod çözme ve oluşturma işlemlerini planlamak için ek iş parçacıkları kullanır. Bu ayarın etkinleştirilmesi, kare düşmelerini ve ses yetersizliklerini azaltabilir.

Asenkron arabellek kuyruğa alma, Android 12 (API düzeyi 31) ve sonraki sürümlerin yüklü olduğu cihazlarda varsayılan olarak etkindir. Android 6.0 (API düzeyi 23) ve sonraki sürümlerde ise manuel olarak etkinleştirilebilir. Özellikle DRM ile korunan veya yüksek kare hızlı içerikleri oynatırken kare düşmesi ya da ses yetersizliği gözlemlediğiniz belirli cihazlarda bu özelliği etkinleştirebilirsiniz.

En basit durumda, oynatıcıya aşağıdaki şekilde 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 oluşturuyorsanız new DefaultMediaCodecAdapter.Factory(context).forceEnableAsynchronous() ve MediaCodecAudioRenderer oluşturucularına MediaCodecVideoRenderer değerini iletin.

ForwardingSimpleBasePlayer ile işlemleri özelleştirme

Player örneğinin davranışının bir kısmını ForwardingSimpleBasePlayer alt sınıfına sarmalayarak özelleştirebilirsiniz. Bu sınıf, Player yöntemlerini doğrudan uygulamanız gerekmeden belirli "işlemleri" yakalamanıza olanak tanır. Bu, örneğin play(), pause() ve setPlayWhenReady(boolean) gibi öğelerin tutarlı davranışını sağlar. 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 daha hataya açık olan ForwardingPlayer yerine ForwardingSimpleBasePlayer tercih edilmelidir.

Örneğin, oynatma başlatıldığında veya durdurulduğunda özel 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

class PlayerWithCustomPlay extends ForwardingSimpleBasePlayer {

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

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

Ya da SEEK_TO_NEXT komutunun kullanılmasına izin vermemek için (ve Player.seekToNext komutunun hiçbir işlem yapmadığından emin olmak 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

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ştirme

Yukarıdaki örneklerde, oynatıcıya aktarılan tüm MediaItem nesnelerin oynatılması sırasında kullanılmak üzere özelleştirilmiş bileşenler yerleştirilir. Ayrıntılı özelleştirme gerektiğinde, özelleştirilmiş bileşenleri doğrudan oynatıcıya iletilebilen tekil MediaSource örneklerine yerleştirmek de mümkündür. Aşağıdaki örnekte, özel 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, bu sayfanın üst kısmında listelenen bileşenlerin yaygın kullanım alanları için varsayılan uygulamalarını sağlar. Bir 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ığın sağladığı varsayılan uygulamalar tarafından desteklenmeyen bir medya türünü işlemek için özel bir Renderer uygulamak isteyebilirsiniz.
  • TrackSelector: Özel bir TrackSelector'nın uygulanması, bir uygulama geliştiricinin MediaSource tarafından kullanıma sunulan parçaların, mevcut Renderer'lerin her biri tarafından tüketilmek ü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 – Şu anda kitaplık tarafından desteklenmeyen bir kapsayıcı biçimini desteklemeniz gerekiyorsa özel bir Extractor sınıfı uygulayabilirsiniz.
  • MediaSource – Oluşturuculara özel bir şekilde beslemek için medya örnekleri almak veya özel MediaSource birleştirme davranışı uygulamak istiyorsanız özel bir MediaSource sınıfı uygulamak uygun olabilir.
  • MediaSource.Factory – Özel bir MediaSource.Factory uygulama, bir uygulamanın MediaSource oluşturulma şeklini MediaItem kaynağından özelleştirmesine olanak tanır.
  • DataSource – ExoPlayer'ın yukarı akış paketi, farklı kullanım alanları için zaten bir dizi DataSource uygulaması içerir. Verileri özel bir protokol üzerinden, özel bir HTTP yığını kullanarak veya özel bir kalıcı önbellekten yüklemek gibi farklı bir şekilde 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 etkinlikleri uygulamaya geri bildirmesi gerekiyorsa bunu mevcut ExoPlayer bileşenleriyle aynı modeli kullanarak yapmanızı öneririz. Örneğin, EventDispatcher sınıflarını kullanabilir veya bir Handler ile birlikte bir dinleyiciyi bileşenin oluşturucusuna iletebilirsiniz.
  • Uygulamanın oynatma sırasında yeniden yapılandırmasına izin vermek için özel bileşenlerin mevcut ExoPlayer bileşenleriyle aynı modeli kullanmasını öneririz. Bunu yapmak için özel bileşenler PlayerMessage.Target işlevini uygulamalı ve handleMessage yönteminde yapılandırma değişikliklerini 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. Oynatma iş parçacığında teslim edilecek mesajlar göndermek, bu mesajların oynatıcıda gerçekleştirilen diğer işlemlerle birlikte sırayla yürütülmesini sağlar.