MediaSession kullanarak oynatmayı kontrol etme ve reklam yayınlama

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

Komutlar, kulaklıktaki veya TV uzaktan kumandanızdaki oynatma düğmesi gibi fiziksel düğmelerden gelebilir. Bu komutlar, medya denetleyicisi olan istemci uygulamalarından da gelebilir (ör. Google Asistan'a "duraklat" komutu verme). Medya oturumu bu komutları medya uygulamasının oynatıcısına iletir.

Medya oturumu ne zaman seçilir?

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

  • Kulaklıklarıyla. Kullanıcıların, medyayı oynatmak veya duraklatmak ya da sonraki veya önceki parçaya gitmek için kulaklıklarına dokunarak kullanabileceği düğmeler veya dokunma etkileşimleri genellikle vardır.
  • Google Asistan ile konuşarak. Cihazda oynatılan medyayı duraklatmak için "Ok Google, duraklat" demeniz yeterlidir.
  • Wear OS kol saati üzerinden Bu sayede, telefonda oynarken en yaygın oynatma denetimlerine daha kolay erişebilirler.
  • Medya kontrolleri'ni kullanarak. Bu bantta, çalışan her medya oturumunun kontrolleri gösterilir.
  • TV'de. Fiziksel oynatma düğmeleriyle, platform oynatma kontrolüyle ve güç yönetimiyle ilgili işlemlere izin verir (ör. TV, ses çubuğu veya A/V alıcı kapanırsa ya da giriş değiştirilirse uygulamada oynatma durdurulur).
  • Oynatma işlemini etkilemesi gereken diğer harici işlemler.

Bu, birçok kullanım alanı için mükemmeldir. Özellikle aşağıdaki durumlarda MediaSession kullanmayı kesinlikle düşünmeniz önerilir:

  • Film veya canlı TV gibi uzun video içerikleri yayınlıyorsanız
  • Podcast'ler veya müzik oynatma listeleri gibi uzun ses içerikleri yayınlıyorsanız
  • TV uygulaması oluşturuyorsanız

Ancak tüm kullanım alanları MediaSession ile uyumlu değildir. Aşağıdaki durumlarda yalnızca Player kullanabilirsiniz:

  • Harici kontrol veya arka planda oynatma gerektirmeyen kısa biçimli içerikler gösteriyorsunuz.
  • Tek bir etkin video yoktur (ör. kullanıcı bir listede gezinirken ekranda aynı anda birden fazla video gösterilir).
  • Kullanıcınızın harici oynatma kontrollerine ihtiyaç duymadan etkin bir şekilde izleyeceğini düşündüğünüz tek seferlik bir tanıtım veya açıklama videosu oynatıyorsunuz.
  • İçeriğiniz gizliliğe duyarlıysa ve harici işlemlerin medya meta verilerine erişmesini istemiyorsanız (ör. tarayıcıda gizli mod)

Kullanım alanınız yukarıda listelenenlerden hiçbirine uymuyorsa kullanıcı içerikle aktif olarak etkileşimde değilken uygulamanızın oynatmaya devam etmesine izin verip vermeyeceğinizi düşünün. Yanıt evetse muhtemelen MediaSession seçeneğini tercih edersiniz. Cevabınız hayırsa bunun yerine Player kullanmanız daha verimli olabilir.

Medya oturumu oluşturma

Medya oturumu, yönettiği oynatıcıyla birlikte çalışır. Context ve Player nesnesi kullanarak bir medya oturumu oluşturabilirsiniz. Gerektiği zaman bir medya oturumu oluşturup başlatmanız gerekir. Örneğin, Activity veya Fragment öğesinin onStart() ya da onResume() yaşam döngüsü yöntemi ya da medya oturumunun ve ilişkili oynatıcının sahibi olan Service öğesinin onCreate() yöntemi.

Medya oturumu oluşturmak için bir Player başlatın ve MediaSession.Builder'a ş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 yönetmeniz gerekmez.

Bu, örneğin hataları belirtmek için oynatıcıdan bağımsız olarak bir PlaybackState oluşturmanız ve sürdürmeniz gereken platform medya oturumundan farklıdır.

Benzersiz oturum kimliği

Varsayılan olarak MediaSession.Builder, oturum kimliği olarak boş bir dize içeren bir oturum oluşturur. Bir uygulama yalnızca tek bir oturum örneği oluşturmayı amaçlıyorsa (en yaygın durum budur) bu yeterlidir.

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, oturum MediaSession.Builder.setId(String id) ile oluşturulurken ayarlanabilir.

IllegalStateException hatası, uygulamanızı IllegalStateException: Session ID must be unique. ID= hata mesajıyla kilitliyorsa muhtemelen aynı kimliğe sahip daha önce oluşturulmuş bir örnek yayınlanmadan önce beklenmedik bir şekilde bir oturum oluşturulmuştur. Oturumların bir programlama hatası nedeniyle sızdırılmasının önüne geçmek için bu tür durumlar algılanır ve bir istisna atılarak bildirilir.

Diğer istemcilere kontrol verme

Medya oturumu, oynatmayı kontrol etmenin anahtarıdır. Harici kaynaklardan gelen komutları, medyanızı oynatma işini yapan oynatıcıya yönlendirmenizi sağlar. Bu kaynaklar, mikrofonlu kulaklık veya TV uzaktan kumandasındaki oynatma düğmesi gibi fiziksel düğmeler ya da Google Asistan'a "duraklat" talimatı verme gibi dolaylı komutlar olabilir. Benzer şekilde, bildirim ve kilit ekranı denetimlerini kolaylaştırmak için Android sistemine veya oynatmayı kadrandan kontrol edebilmek için Wear OS kol saatine erişim izni verebilirsiniz. Harici istemciler, medya uygulamanıza oynatma komutları göndermek için medya denetleyici 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 şema.
Şekil 1: Medya denetleyici, harici kaynaklardan gelen komutların medya oturumuna iletilmesini kolaylaştırır.

Bir kumanda medya oturumunuza bağlanmak üzereyken onConnect() yöntemi çağrılır. İsteği kabul etmek veya reddetmek için sağlanan ControllerInfo simgesini kullanabilirsiniz. Özel komutları tanımlama bölümünde, bağlantı isteğini kabul etme örneğini görebilirsiniz.

Denetleyici, bağlandıktan sonra oturuma oynatma komutları gönderebilir. Oturum daha sonra bu komutları oynatıcıya iletir. 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ğırma işlevleri de benzer şekilde bir ControllerInfo nesnesi içerir. Böylece, her isteğe verdiğiniz yanıtı kontrolör bazında değiştirebilirsiniz.

Oynatma listesini değiştirme

Bir medya oturumu, oynatma listeleri için ExoPlayer kılavuzunda açıklandığı gibi oynatıcının oynatma listesini doğrudan değiştirebilir. COMMAND_SET_MEDIA_ITEM veya COMMAND_CHANGE_MEDIA_ITEMS özelliğine sahip olan kontrolörler de oynatma listesini değiştirebilir.

Oynatma listesine yeni öğeler eklerken oynatıcı, oynatılabilmeleri için genellikle MediaItem tanımlanmış URI bulunan örnekler gerektirir. Varsayılan olarak, yeni eklenen öğeler URI'leri tanımlanmışsa player.addMediaItem gibi oynatıcı yöntemlerine otomatik olarak yönlendirilir.

Oynatıcıya eklenen MediaItem örneklerini özelleştirmek istiyorsanız onAddMediaItems() öğesini geçersiz kılabilirsiniz. Tanımlanmış bir URI olmadan medya isteyen denetleyicileri desteklemek istediğinizde bu adım gereklidir. Bunun yerine, MediaItem genellikle istenen medyayı tanımlamak için aşağıdaki alanlardan en az birini 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 alınan metin arama sorgusu gibi.
  • MediaItem.MediaMetadata: "Başlık" veya "sanatçı" gibi yapılandırılmış meta veriler.

Tamamen yeni şarkı listeleri için daha fazla özelleştirme seçeneği sunmak istiyorsanız şarkı listesinde başlangıç öğesini ve konumunu tanımlamanıza olanak tanıyan onSetMediaItems() parametresini de geçersiz kılabilirsiniz. Örneğin, istenen tek bir öğeyi oynatma listesinin tamamına genişletebilir ve oynatıcıya başlangıç noktasını asıl istenen öğenin dizini olarak belirtebilirsiniz. Bu özelliğin kullanıldığı onSetMediaItems() örnek uygulamasını oturum demo uygulamasında bulabilirsiniz.

Medya düğmesi tercihlerini yönetme

Sistem kullanıcı arayüzü, Android Auto veya Wear OS gibi her kontrol cihazı, kullanıcıya hangi düğmelerin gösterileceği konusunda kendi kararlarını verebilir. Kullanıcıya göstermek istediğiniz oynatma denetimlerini belirtmek için MediaSession'de medya düğmesi tercihlerini belirleyebilirsiniz. Bu tercihler, her biri kullanıcı arayüzündeki bir düğme için tercih tanımlayan CommandButton örneğinin sıralı bir listesinden oluşur.

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. CommandButton.Builder oluşturulurken simge, önceden tanımlanmış sabitlerden birine ayarlanmalıdır. Bunun gerçek bir Bitmap 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çmelerine yardımcı olur. Önceden tanımlanmış simge sabitlerinden hiçbiri kullanım alanınıza uymuyorsa bunun yerine setCustomIconResId kullanabilirsiniz.
  2. Kullanıcı düğmeyle etkileşimde bulunduğunda tetiklenen işlemi tanımlayan Komut. Player.Command için setPlayerCommand, ö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österilmesini 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 çözüldüğünde aşağıdaki algoritma uygulanır:

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

Kullanıcı arayüzü görüntüleme kısıtlamalarına bağlı olarak medya düğmesi tercihlerinin nasıl çözüleceğinin önizlemesini oluşturmak için CommandButton.DisplayConstraints simgesini kullanabilirsiniz.

Medya düğmesi tercihlerini ayarlama

Medya düğmesi tercihlerini ayarlamanın en kolay yolu, MediaSession öğesini oluştururken listeyi tanımlamaktır. Alternatif olarak, bağlı her bir kumanda için medya düğmesi tercihlerini özelleştirmek üzere MediaSession.Callback.onConnect'ü geçersiz kılabilirsiniz.

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şimde bulunduktan sonra, kumanda kullanıcı arayüzünde gösterilen düğmeleri güncelleyebilirsiniz. Tipik bir örnek, bu düğmeyle ilişkili işlemi tetikledikten 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

Mevcut 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 durdurabilirsiniz.

Özel komutları tanımlama ve işleme

Medya uygulamaları, örneğin medya düğmesi tercihlerinde kullanılabilecek özel komutlar tanımlayabilir. Örneğin, kullanıcının bir medya öğesini favoriler 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 her bağlı kontrol cihazı için kullanılabilen özel komutları ayarlamak üzere MediaSession.Callback.onConnect() değerini geçersiz kılmanı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();
  }
}

Bir MediaController'ten özel komut istekleri almak için Callback'deki 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 mülkünü kullanarak hangi medya denetleyicinin istek gönderdiğini 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 yönetimi, MediaSession üzerinde bulunan Player'e 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 sarı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.

Oynatıcı komutunun istek gönderen denetleyicisini tanımlama

Bir Player yöntemine yapılan çağrı bir MediaController tarafından başlatıldığında, MediaSession.controllerForCurrentRequest ile kaynak kaynağını tanımlayabilir ve mevcut istek için ControllerInfo'ı edinebilirsiniz:

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üğmelerinin işlenmesini özelleştirme

Medya düğmeleri, Android cihazlarda ve diğer çevre birim cihazlarında bulunan donanım düğmeleridir (ör. Bluetooth kulaklıktaki oynatma/duraklatma düğmesi). Media3, oturuma ulaşan medya düğmesi etkinliklerini sizin için yönetir 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 durdurulabilir.

Hata işleme ve raporlama

Bir oturumun yayınladığı ve denetleyicilere bildirdiği iki tür hata vardır. Önemli hatalar, oturum oynatıcısında oynatmayı kesintiye uğratan teknik bir oynatma hatası olduğunu bildirir. Ölümcül hatalar oluştuklarında 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ı

Önemli bir oynatma hatası, oynatıcı tarafından oturuma bildirilir ve ardından Player.Listener.onPlayerError(PlaybackException) ve Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) üzerinden çağrı yapılması için denetleyicilere bildirilir.

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

Bir hata, durumunu STATE_ERROR olarak değiştirerek ve hata kodu ile mesajını PlaybackException'a göre ayarlayarak platform oturumuna kopyalanır. Bu işlem, birlikte çalışabilirlik için gereklidir.

Önemli hataları özelleştirme

Kullanıcıya yerelleştirilmiş ve anlamlı bilgiler sağlamak için, oturum oluşturulurken ForwardingPlayer kullanılarak önemli oynatma hatalarının hata kodu, hata mesajı ve hata ek bilgileri ö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ı, hatayı durdurmak ve hata kodunu, mesajı ya da ekstraları özelleştirmek için ForwardingSimpleBasePlayer'ü kullanabilir. 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 kritik olmayan hatalar, bir uygulama tarafından tüm denetleyicilere veya belirli bir denetleyiciye gönderilebilir:

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

Medya bildirimi denetleyicisine ölümcül olmayan bir hata gönderildiğinde hata kodu ve hata mesajı platform medya oturumuna kopyalanır. Bu durumda PlaybackState.state, STATE_ERROR olarak değiştirilmez.

Önemli olmayan hatalar alma

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