MediaSession kullanarak oynatmayı kontrol etme ve reklam yayınlama

Medya oturumları, ses veya video oynatıcıyla etkileşim kurmanın evrensel bir yolunu sunar. Media3'te varsayılan oynatıcı, ExoPlayer arayüzünü uygulayan Player sınıfıdır. Medya oturumunu oynatıcıya bağlamak, bir uygulamanın medya oynatmayı harici olarak tanıtmasına ve harici kaynaklardan oynatma komutları almasına olanak tanır.

Komutlar, kulaklık veya TV uzaktan kumandası üzerindeki oynatma düğmesi gibi fiziksel düğmelerden gelebilir. Ayrıca, Google Asistan'a "duraklat" komutu verme gibi medya denetleyicisi olan istemci uygulamalarından da gelebilir. Medya oturumu, bu komutları medya uygulamasının oynatıcısına devreder.

Medya oturumu ne zaman seçilir?

MediaSession özelliğini uyguladığınızda kullanıcıların oynatmayı kontrol etmesine izin verirsiniz:

  • Kulaklıkları aracılığıyla Kullanıcılar, kulaklıklarında medyayı oynatmak veya duraklatmak ya da sonraki veya önceki parçaya gitmek için genellikle düğmeleri kullanabilir veya dokunma etkileşiminde bulunabilir.
  • Google Asistan ile konuşarak Cihazda oynatılan medyayı duraklatmak için "Ok Google, duraklat" demeniz yaygın bir yöntemdir.
  • Wear OS kol saatleri üzerinden Bu sayede, telefonlarında oyun oynarken en sık kullanılan oynatma kontrollerine daha kolay erişebilirler.
  • Medya kontrolleri aracılığıyla Bu bantta, her çalışan medya oturumunun kontrolleri gösterilir.
  • TV'de Fiziksel oynatma düğmeleri, platform oynatma kontrolü ve güç yönetimiyle (ör. TV, soundbar veya A/V alıcısı kapanırsa ya da giriş değiştirilirse uygulamada oynatma durdurulmalıdır) ilgili işlemlere izin verir.
  • Android Auto medya kontrolleri Bu sayede sürüş sırasında güvenli oynatma kontrolü sağlanır.
  • Oynatmayı etkilemesi gereken diğer tüm harici işlemler.

Bu özellik, birçok kullanım alanı için idealdir. Özellikle şu durumlarda MediaSession kullanmayı kesinlikle düşünmelisiniz:

  • Film veya canlı TV gibi uzun video içerikleri yayınlıyorsanız
  • Podcast'ler veya müzik çalma listeleri gibi uzun ses içerikleri yayınlıyorsanız.
  • TV uygulaması geliştiriyorsunuz.

Ancak tüm kullanım alanları MediaSession ile iyi uyum sağlamaz. Aşağıdaki durumlarda yalnızca Player kullanmak isteyebilirsiniz:

  • Harici kontrol veya arka planda oynatma gerektirmeyen kısa biçimli içerikler gösteriyorsunuz.
  • Kullanıcının bir listede gezinmesi ve ekranda aynı anda birden fazla video gösterilmesi gibi tek bir etkin video yoktur.
  • Kullanıcınızın harici oynatma kontrollerine ihtiyaç duymadan aktif olarak izlemesini beklediğiniz tek seferlik bir tanıtım veya açıklama videosu oynatıyorsunuz.
  • İçeriğiniz gizliliğe duyarlı ve harici işlemlerin medya meta verilerine (ör. tarayıcıda gizli mod) erişmesini istemiyorsunuz.

Kullanım alanınız yukarıda listelenenlerden herhangi birine uymuyorsa kullanıcının içerikle etkin olarak etkileşimde bulunmadığı zamanlarda uygulamanızın oynatmaya devam etmesinin sizin için sorun olup olmayacağını değerlendirin. Cevap evetse muhtemelen MediaSession seçeneğini belirlemek istersiniz. Cevabınız hayırsa bunun yerine Player kullanmanız daha iyi olabilir.

Medya oturumu oluşturma

Medya oturumu, yönettiği oynatıcıyla birlikte bulunur. Context ve Player nesnesiyle bir medya oturumu oluşturabilirsiniz. Medya oturumunu ve ilişkili oynatıcısını içeren Activity veya Fragment'ün onStart() ya da onResume() yaşam döngüsü yöntemi veya Service'ın onCreate() yöntemi gibi gerektiğinde bir medya oturumu oluşturup başlatmanız gerekir.

Medya oturumu oluşturmak için Player öğesini başlatın ve MediaSession.Builder öğesine şu şekilde sağlayın:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Otomatik durum yönetimi

Media3 kitaplığı, oynatıcının durumunu kullanarak medya oturumunu otomatik olarak günceller. Bu nedenle, oynatıcıdan oturuma eşlemeyi manuel olarak yapmanız gerekmez.

Bu, platform medya oturumundan farklıdır. Platform medya oturumunda, oynatıcıdan bağımsız olarak bir PlaybackState oluşturup sürdürmeniz gerekiyordu. Örneğin, hataları belirtmek için.

Benzersiz oturum kimliği

Varsayılan olarak MediaSession.Builder, oturum kimliği olarak boş dize içeren bir oturum oluşturur. Bu, bir uygulamanın yalnızca tek bir oturum örneği oluşturması amaçlandığında yeterlidir. Bu, en yaygın durumdur.

Bir uygulama aynı anda birden fazla oturum örneğini yönetmek istiyorsa her oturumun oturum kimliğinin benzersiz olduğundan emin olmalıdır. Oturum kimliği, MediaSession.Builder.setId(String id) ile oturum oluşturulurken ayarlanabilir.

IllegalStateException hata mesajıyla uygulamanızın kilitlendiğini görüyorsanız IllegalStateException: Session ID must be unique. ID=, aynı kimliğe sahip daha önce oluşturulmuş bir örnek yayınlanmadan önce beklenmedik şekilde bir oturum oluşturulmuş olabilir. Oturumların programlama hatası nedeniyle sızdırılmasını önlemek için bu tür durumlar algılanır ve istisna oluşturularak bildirilir.

Diğer istemcilere kontrol izni verme

Oynatmayı kontrol etmenin anahtarı medya oturumudur. Bu özellik, harici kaynaklardan gelen komutları, medyanızı oynatma işini yapan oynatıcıya yönlendirmenizi sağlar. Bu kaynaklar, fiziksel düğmeler (ör. kulaklık veya TV uzaktan kumandasındaki oynatma düğmesi) ya da dolaylı komutlar (ör. Google Asistan'a "duraklat" komutu verme) olabilir. Aynı şekilde, bildirim ve kilit ekranı denetimlerini kolaylaştırmak için Android sistemine veya oynatmayı saat yüzünden kontrol edebilmek için Wear OS kol saatine erişim izni vermek isteyebilirsiniz. Harici istemciler, medya uygulamanıza oynatma komutları vermek için medya denetleyicisi kullanabilir. Bu komutlar, medya oturumunuz tarafından alınır ve nihayetinde medya oynatıcıya iletilir.

MediaSession ile MediaController arasındaki etkileşimi gösteren bir şema.
Şekil 1: Medya denetleyici, komutların harici kaynaklardan medya oturumuna iletilmesini kolaylaştırır.

Bir kumanda medya oturumunuza bağlanmak üzereyken onConnect() yöntemi çağrılır. İsteği kabul edeceğinize veya reddedeceğinize karar vermek için sağlanan ControllerInfo bilgileri kullanabilirsiniz. Özel komutları bildirme bölümünde bağlantı isteğini kabul etme örneğini inceleyin.

Bağlandıktan sonra bir denetleyici, oturuma oynatma komutları gönderebilir. Oturum daha sonra bu komutları oynatıcıya devreder. Player arayüzünde tanımlanan oynatma ve oynatma listesi komutları, oturum tarafından otomatik olarak işlenir.

Diğer geri çağırma yöntemleri, örneğin özel komut ve oynatma listesini değiştirme isteklerini işlemenize olanak tanır. Bu geri çağırmalar da benzer şekilde bir ControllerInfo nesnesi içerir. Böylece her isteğe nasıl yanıt vereceğinizi denetleyici bazında değiştirebilirsiniz.

Oynatma listesini değiştirme

Medya oturumu, ExoPlayer oynatma listeleri kılavuzunda açıklandığı gibi oynatıcısının oynatma listesini doğrudan değiştirebilir. Ayrıca, denetleyiciler COMMAND_SET_MEDIA_ITEM veya COMMAND_CHANGE_MEDIA_ITEMS seçeneklerinden biri denetleyiciye sunuluyorsa oynatma listesini değiştirebilir.

Oynatma listesine yeni öğeler eklerken oynatıcının, oynatılabilir hale getirmek için genellikle MediaItem örnekleri ve tanımlanmış bir URI gerektirdiği görülür. Yeni eklenen öğeler, URI tanımlanmışsa varsayılan olarak player.addMediaItem gibi oynatıcı yöntemlerine otomatik olarak yönlendirilir.

Oynatıcıya eklenen MediaItem örneklerini özelleştirmek istiyorsanız onAddMediaItems() değerini geçersiz kılabilirsiniz. Bu adım, tanımlanmış bir URI olmadan medya isteyen denetleyicileri desteklemek istediğinizde gereklidir. Bunun yerine, MediaItem genellikle istenen medyayı açıklamak için aşağıdaki alanlardan birini veya daha fazlasını içerir:

  • MediaItem.id: Medyayı tanımlayan genel bir kimlik.
  • MediaItem.RequestMetadata.mediaUri: Özel bir şema kullanabilen ve oynatıcı tarafından doğrudan oynatılması gerekmeyen bir istek URI'si.
  • MediaItem.RequestMetadata.searchQuery: Google Asistan'dan gelenler gibi metin biçiminde bir arama sorgusu.
  • MediaItem.MediaMetadata: "Başlık" veya "sanatçı" gibi yapılandırılmış meta veriler.

Tamamen yeni oynatma listeleri için daha fazla özelleştirme seçeneği istiyorsanız oynatma listesindeki başlangıç öğesini ve konumunu tanımlamanıza olanak tanıyan onSetMediaItems() öğesini de geçersiz kılabilirsiniz. Örneğin, istenen tek bir öğeyi oynatma listesinin tamamına genişletebilir ve oynatıcıya, başlangıçta istenen öğenin dizininden başlamasını söyleyebilirsiniz. Bu özellik ile onSetMediaItems() örnek bir uygulamaonSetMediaItems() oturum demo uygulamasında bulunabilir.

Medya düğmesi tercihlerini yönetme

Örneğin, Sistem Kullanıcı Arayüzü, Android Auto veya Wear OS gibi her denetleyici, kullanıcıya hangi düğmelerin gösterileceği konusunda kendi kararını verebilir. Kullanıcıya hangi oynatma kontrollerini göstermek istediğinizi belirtmek için MediaSession üzerinde medya düğmesi tercihlerini belirleyebilirsiniz. Bu tercihler, CommandButton örneklerinin sıralı bir listesinden oluşur. Bu örneklerin her biri, kullanıcı arayüzündeki bir düğmeyle ilgili tercihi tanımlar.

Komut düğmelerini tanımlama

CommandButton örnekleri, medya düğmesi tercihlerini tanımlamak için kullanılır. Her düğme, istenen kullanıcı arayüzü öğesinin üç yönünü tanımlar:

  1. Görsel görünümü tanımlayan simge. Simge, CommandButton.Builder oluşturulurken önceden tanımlanmış sabitlerden birine ayarlanmalıdır. Bunun gerçek bir bit eşlem veya resim kaynağı olmadığını unutmayın. Genel bir sabit, denetleyicilerin kendi kullanıcı arayüzlerinde tutarlı bir görünüm ve tarz için uygun bir kaynak seçmesine yardımcı olur. Önceden tanımlanmış simge sabitlerinden hiçbiri kullanım alanınıza uymuyorsa bunun yerine setCustomIconResId simgesini kullanabilirsiniz.
  2. Kullanıcı düğmeyle etkileşimde bulunduğunda tetiklenen işlemi tanımlayan Komut. setPlayerCommand için Player.Command, önceden tanımlanmış veya özel SessionCommand için setSessionCommand kullanabilirsiniz.
  3. Düğmenin kontrol cihazı kullanıcı arayüzünde nereye yerleştirilmesi gerektiğini tanımlayan Slot. Bu alan isteğe bağlıdır ve Simge ile Komut'a göre otomatik olarak ayarlanır. Örneğin, bir düğmenin varsayılan "taşma" alanı yerine kullanıcı arayüzünün "ileri" gezinme alanında gösterilmesi gerektiğini belirtmenize olanak tanır.

Kotlin

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

Java

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

Medya düğmesi tercihleri belirlendiğinde aşağıdaki algoritma uygulanır:

  1. Medya düğmesi tercihlerindeki her CommandButton için düğmeyi, mevcut ve izin verilen ilk yuvaya yerleştirin.
  2. Merkezi, ileri ve geri yuvalarından herhangi biri düğmeyle doldurulmamışsa bu yuva için varsayılan düğmeler ekleyin.

CommandButton.DisplayConstraints simgesini kullanarak, medya düğmesi tercihlerinin kullanıcı arayüzü görüntüleme kısıtlamalarına bağlı olarak nasıl çözümleneceğine dair bir önizleme oluşturabilirsiniz.

Medya düğmesi tercihlerini ayarlama

Medya düğmesi tercihlerini ayarlamanın en kolay yolu, MediaSession oluştururken listeyi tanımlamaktır. Alternatif olarak, bağlı her kumanda için medya düğmesi tercihlerini özelleştirmek üzere MediaSession.Callback.onConnect tuşuna basabilirsiniz.

Kotlin

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

Java

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

Kullanıcı etkileşiminden sonra medya düğmesi tercihlerini güncelleme

Oynatıcınızla etkileşimi işledikten sonra, kontrol cihazı kullanıcı arayüzünde gösterilen düğmeleri güncellemek isteyebilirsiniz. Tipik bir örnek, bu düğmeyle ilişkili işlem tetiklendikten sonra simgesini ve işlemini değiştiren bir açma/kapatma düğmesidir. Medya düğmesi tercihlerini güncellemek için MediaSession.setMediaButtonPreferences simgesini kullanarak tüm kumandaların veya belirli bir kumandanın tercihlerini güncelleyebilirsiniz:

Kotlin

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

Java

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

Özel komutlar ekleme ve varsayılan davranışı özelleştirme

Kullanılabilen oynatıcı komutları özel komutlarla genişletilebilir. Ayrıca, varsayılan davranışı değiştirmek için gelen oynatıcı komutlarını ve medya düğmelerini engellemek de mümkündür.

Özel komutları bildirme ve işleme

Medya uygulamaları, örneğin medya düğmesi tercihlerinde kullanılabilen özel komutlar tanımlayabilir. Örneğin, kullanıcının bir medya öğesini favori öğeler listesine kaydetmesine olanak tanıyan düğmeler uygulamak isteyebilirsiniz. MediaController özel komutlar gönderir ve MediaSession.Callback bunları alır.

Özel komutları tanımlamak için MediaSession.Callback.onConnect() işlevini geçersiz kılarak her bağlı kontrol cihazı için kullanılabilir özel komutları ayarlamanız gerekir.

Kotlin

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): MediaSession.ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

Java

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

MediaController cihazından özel komut istekleri almak için Callback içindeki onCustomCommand() yöntemini geçersiz kılın.

Kotlin

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(
        SessionResult(SessionResult.RESULT_SUCCESS)
      )
    }
    ...
  }
}

Java

class CustomMediaSessionCallback implements MediaSession.Callback {
  ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session, 
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args
  ) {
    if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(
        new SessionResult(SessionResult.RESULT_SUCCESS)
      );
    }
    ...
  }
}

Callback yöntemlerine iletilen MediaSession.ControllerInfo nesnesinin packageName özelliğini kullanarak hangi medya denetleyicisinin istekte bulunduğunu izleyebilirsiniz. Bu sayede, sistemden, kendi uygulamanızdan veya diğer istemci uygulamalarından gelen belirli bir komuta yanıt olarak uygulamanızın davranışını özelleştirebilirsiniz.

Varsayılan oynatıcı komutlarını özelleştirme

Tüm varsayılan komutlar ve durum işleme, Player üzerinde bulunan MediaSession öğesine devredilir. Player arayüzünde tanımlanan bir komutun (ör. play() veya seekToNext()) davranışını özelleştirmek için Player öğenizi MediaSession öğesine iletmeden önce ForwardingSimpleBasePlayer içine alın:

Kotlin

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

Java

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

ForwardingSimpleBasePlayer hakkında daha fazla bilgi için Özelleştirme ile ilgili ExoPlayer kılavuzuna bakın.

Bir oynatıcı komutunun istekte bulunan denetleyicisini belirleme

Bir Player yöntemi için MediaController tarafından başlatılan bir çağrı olduğunda, MediaSession.controllerForCurrentRequest ile kaynağı belirleyebilir ve mevcut istek için ControllerInfo değerini alabilirsiniz:

Kotlin

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

Java

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

Medya düğmesi kullanımını özelleştirme

Medya düğmeleri, Android cihazlarda ve diğer çevre birimlerinde (ör. Bluetooth kulaklıklardaki oynat/duraklat düğmesi) bulunan donanım düğmeleridir. Media3, oturuma ulaştıklarında medya düğmesi etkinliklerini sizin için işler ve oturum oynatıcısında uygun Player yöntemini çağırır.

Gelen tüm medya düğmesi etkinliklerinin ilgili Player yönteminde işlenmesi önerilir. Daha gelişmiş kullanım alanları için medya düğmesi etkinlikleri MediaSession.Callback.onMediaButtonEvent(Intent) içinde yakalanabilir.

Hata işleme ve raporlama

Bir oturumun yayınladığı ve kontrol cihazlarına bildirdiği iki tür hata vardır. Önemli hatalar, oynatmayı kesintiye uğratan oturum oynatıcısının teknik oynatma hatasını bildirir. Önemli hatalar, oluştuğu anda denetleyiciye otomatik olarak bildirilir. Önemli olmayan hatalar, oynatmayı kesintiye uğratmayan ve uygulama tarafından manuel olarak kontrol cihazlarına gönderilen teknik olmayan veya politika hatalarıdır.

Önemli oynatma hataları

Oynatıcı tarafından oturuma önemli bir oynatma hatası bildirilir ve ardından Player.Listener.onPlayerError(PlaybackException) ve Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) üzerinden çağrı yapmak için kontrol cihazlarına bildirilir.

Bu durumda, oynatma durumu STATE_IDLE olarak değiştirilir ve MediaController.getPlaybackError(), geçişe neden olan PlaybackException değerini döndürür. Bir denetleyici, hatanın nedeni hakkında bilgi edinmek için PlayerException.errorCode öğesini inceleyebilir.

Birlikte çalışabilirlik için, durumunu STATE_ERROR olarak değiştirerek ve hata kodunu ve mesajını PlaybackException'ye göre ayarlayarak ölümcül bir hata platform oturumuna kopyalanır.

Önemli hataların özelleştirilmesi

Kullanıcıya yerelleştirilmiş ve anlamlı bilgiler sağlamak için oturum oluşturulurken ForwardingPlayer kullanılarak ölümcül bir oynatma hatasının hata kodu, hata mesajı ve hata ekstraları özelleştirilebilir:

Kotlin

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

Java

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

Yönlendiren oynatıcı, ForwardingSimpleBasePlayer kullanarak hatayı yakalayabilir ve hata kodunu, mesajı veya ekstraları özelleştirebilir. Aynı şekilde, orijinal oynatıcıda bulunmayan yeni hatalar da oluşturabilirsiniz:

Kotlin

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

Java

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

Önemli olmayan hatalar

Teknik bir istisnadan kaynaklanmayan ve uygulamalar tarafından tüm denetleyicilere veya belirli bir denetleyiciye gönderilebilen, ölümcül olmayan hatalar:

Kotlin

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

Java

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

Önemli olmayan bir hata medya bildirimi denetleyicisine gönderildiğinde hata kodu ve hata mesajı platform medya oturumuna kopyalanır. Bu sırada PlaybackState.state, STATE_ERROR olarak değiştirilmez.

Önemli olmayan hatalar alma

Bir MediaController, MediaController.Listener.onError uygulayarak önemli olmayan bir hata alır:

Kotlin

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

Java

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });