Medya oturumları, ses veya video oynatıcıyla etkileşim kurmanın 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 oynatmayı harici olarak tanıtmasına ve harici kaynaklardan oynatma komutları almasına olanak tanır.
Komutlar, kulaklıktaki oynatma düğmesi veya TV uzaktan kumandası gibi fiziksel düğmelerden gelebilir. Ayrıca, medya kumandası olan istemci uygulamalarından da gelebilir. Örneğin, Gemini veya Google Asistan gibi yapay zeka asistanlarına "duraklat" talimatı verilebilir. Medya oturumu, bu komutları medya uygulamasının oynatıcısına devreder.
Medya oturumu ne zaman seçilir?
MediaSession uyguladığınızda kullanıcıların oynatmayı kontrol etmesine izin verirsiniz:
- Kulaklık aracılığıyla. Kullanıcıların medyayı oynatmak veya duraklatmak ya da sonraki veya önceki parçaya gitmek için kulaklıklarında kullanabileceği düğmeler veya dokunma etkileşimleri vardır.
- Gemini veya Google Asistan gibi asistan yapay zekalarla konuşarak. Cihazda o anda oynatılan medyayı duraklatmak için "Ok Google, duraklat" demek yaygın bir kalıptır.
- Wear OS kol saatleri üzerinden Bu sayede, telefonlarında oyun oynarken en yaygın oynatma kontrollerine daha kolay erişebilirler.
- Medya kontrolleri aracılığıyla. Bu bantta, çalışan her medya oturumu için kontroller gösterilir.
- TV'de. Fiziksel oynatma düğmeleri, platform oynatma kontrolü ve güç yönetimi ile işlemlere izin verir (örneğin, TV, ses çubuğu veya A/V alıcısı kapanırsa ya da giriş değiştirilirse uygulamada oynatma durdurulmalıdır).
- Android Auto medya kontrolleri aracılığıyla Bu sayede, sürüş sırasında oynatma kontrollerini güvenli bir şekilde kullanabilirsiniz.
- Oynatmayı etkilemesi gereken diğer tüm harici işlemler.
Bu, birçok kullanım alanı için idealdir. Özellikle aşağıdaki durumlarda MediaSession kullanmayı kesinlikle düşünmelisiniz:
- 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ı geliştiriyorsanız.
Ancak tüm kullanım alanları MediaSession için uygun değildir. Aşağıdaki durumlarda yalnızca Player kullanmak isteyebilirsiniz:
- Harici kontrol veya arka planda oynatma gerektirmeyen kısa içerikler gösteriyorsanız.
- Tek bir etkin video yoktur. Örneğin, kullanıcı bir listede gezinirken ekranda aynı anda birden fazla video gösterilir.
- 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 gizlilik açısından hassas ve harici işlemlerin medya meta verilerine erişmesini istemiyorsunuz (ö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 bulunmadığında uygulamanızın oynatmaya devam etmesini isteyip istemediğinizi değerlendirin. Cevabınız evetse MediaSession seçeneğini tercih edebilirsiniz. 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 çalışır. Context ve Player nesnesiyle bir medya oturumu oluşturabilirsiniz. Medya oturumunu ve ilişkili oynatıcısını barındıran Activity veya Fragment öğesinin onStart() ya da onResume() yaşam döngüsü yöntemi veya Service öğesinin onCreate() yöntemi gibi gerektiğinde bir medya oturumu oluşturup başlatmanız gerekir.
Medya oturumu oluşturmak için bir Player başlatın ve bunu şu şekilde MediaSession.Builder öğesine 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 işleme
Media3 kitaplığı, oynatıcının durumunu kullanarak medya oturumunu otomatik olarak günceller. Bu nedenle, oyuncudan oturuma eşlemeyi manuel olarak yapmanız gerekmez.
Bu, oynatıcıdan bağımsız olarak bir PlaybackState oluşturup sürdürmeniz gereken platform medya oturumundan farklıdır (ör. hataları belirtmek için).
Benzersiz oturum kimliği
Varsayılan olarak MediaSession.Builder, oturum kimliği olarak boş bir dize içeren bir oturum oluşturur. Uygulama yalnızca tek bir oturum örneği oluşturmak istiyorsa bu yeterlidir. En yaygın durum budur.
Bir uygulama aynı anda birden fazla oturum örneğini yönetmek istiyorsa her oturumun oturum kimliğinin benzersiz olmasını sağlamalıdır. Oturum kimliği, MediaSession.Builder.setId(String id) ile oturum oluşturulurken ayarlanabilir.
Uygulamanızın IllegalStateException: Session ID must be unique. ID= hata mesajıyla kilitlenmesine neden olan bir IllegalStateException görüyorsanız büyük olasılıkla daha önce aynı kimlikle oluşturulmuş bir örnek yayınlanmadan önce beklenmedik bir şekilde oturum oluşturulmuştur. Oturumların bir programlama hatası nedeniyle sızdırılmasını önlemek için bu tür durumlar algılanır ve bir istisna oluşturularak bildirilir.
Diğer istemcilere kontrol izni verme
Medya oturumu, oynatmayı kontrol etmenin anahtarıdır. Bu sayede, harici kaynaklardan gelen komutları medya içeriğinizi oynatan oynatıcıya yönlendirebilirsiniz. Bu kaynaklar, kulaklık veya TV uzaktan kumandasındaki oynat düğmesi gibi fiziksel düğmeler ya da Gemini veya Google Asistan'a "duraklat" talimatı vermek gibi dolaylı komutlar olabilir. Benzer şekilde, bildirim ve kilit ekranı kontrolleri için Android sistemine veya oynatmayı saat kadranından kontrol edebilmek için Wear OS kol saatine erişim izni vermek isteyebilirsiniz. Harici istemciler, medya uygulamanıza oynatma komutları vermek için medya kumandası kullanabilir. Medya oturumunuz bu komutları alır ve medya oynatıcıya iletir.
Bir kumanda medya oturumunuza bağlanmak üzereyken oturum onConnectAsync() yöntemini çağırır. İsteği kabul etmek veya reddetmek için sağlanan ControllerInfo kullanabilirsiniz. Bağlantı isteğini kabul etme örneği için Özel komutları bildirme ve işleme başlıklı makaleyi inceleyin.
Bağlantı kurulduktan sonra kumanda, oturuma oynatma komutları gönderebilir. Oturum, 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 komutlar 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, Oynatma listeleri için ExoPlayer kılavuzunda açıklandığı gibi oynatıcısının oynatma listesini doğrudan değiştirebilir.
Denetleyiciler, COMMAND_SET_MEDIA_ITEM veya COMMAND_CHANGE_MEDIA_ITEMS erişimine sahipse oynatma listesini de değiştirebilir.
Oynatma listesine yeni öğeler eklerken oynatıcı, öğelerin oynatılabilmesi için genellikle tanımlanmış URI'ye sahip MediaItem örnekleri gerektirir. Varsayılan olarak, yeni eklenen öğeler, tanımlanmış bir URI'leri varsa 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. Bu adım, tanımlı 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.mediaId: 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: Metin tabanlı bir arama sorgusu (ör. Gemini veya Google Asistan'dan).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 onSetMediaItems() yöntemini de geçersiz kılabilirsiniz. Bu yöntem, oynatma listesindeki başlangıç öğesini ve konumunu tanımlamanıza olanak tanır. Örneğin, istenen tek bir öğeyi tüm oynatma listesine genişletebilir ve oynatıcıya, başlangıçta istenen öğenin dizininde başlamasını söyleyebilirsiniz. Bu özelliğin kullanıldığı onSetMediaItems() için örnek bir uygulama görmek istiyorsanız oturum demo uygulamasına bakın.
Oynatma listesi meta verilerini yayınlama
Media3, etkin parça oynatmayla birlikte yapılandırılmış oynatma listesi meta verilerini yayınlamak için API'ler sağlar. Uygulamanızın oynatma listesi üyeliğini ve açıklayıcı meta verileri medya oturumlarında doldurma şeklini standartlaştırmak, Android'in uygulamanızda oynatma için sistem düzeyinde giriş noktaları sunmasına olanak tanır.
Bu bağlamda, oynatma listesi terimi, sırayla çalınacak parçaların sıralı gruplandırmasını ifade eder. Bu, yalnızca kullanıcı veya uygulama tarafından tanımlanan oynatılabilir öğe listeleri için değil, aynı zamanda müzik albümleri, podcast bölümleri ve sesli kitap bölümleri gibi gruplandırmalar için de geçerlidir.
Oynatma listesi kimliğini ve oynatma listesi meta verilerini bildirme
Media3'te, oynatma listesi tanımlama ve oynatma listesi açıklayıcı meta verileri iki MediaMetadata kapsamına bölünmüştür:
- Öğe düzeyinde oynatma listesi kimliği (
MediaItem.mediaMetadata.playlistId):- Öğenin
MediaMetadatadeğerindeMediaMetadata.Builder.setPlaylistIdyöntemini çağırarak oynatılanMediaItemöğesini bir oynatma listesinin parçası olarak işaretleyin.MediaItem.mediaIdise her bir parçayı benzersiz şekilde tanımlar. playlistId, oynatma listesi devam ettirilebildiği sürece (ör.37i9dQZF1DX76Wlfdnj7AP) oturumlar genelinde oynatma listesini benzersiz şekilde tanımlayan, uygulama tarafından tanımlanmış opak bir dizedir ve farklı bir oynatma listesi için yeniden kullanılmamalıdır. Bağlı kumandalar bu kimliği yorumlamaz, depolar ve oynatma listesini devam ettirmek için uygulamanıza geri gönderir.- Oynatma listesine ait her parça aynı
playlistIddeğerini taşır. Oynatma listesi bağlamında çalınmayan parçalar (ör. tek bir bağımsız parça) içinplaylistIddeğerini ayarlamayın (null).
- Öğenin
- Oynatıcı düzeyinde oynatma listesi meta verileri (
Player.getPlaylistMetadata):- Özel bir
MediaMetadataörneğini oynatma listesinin açıklayıcı özellikleriyle (setTitle,setArtworkUrivesetMediaType) doldurun vePlayer.setPlaylistMetadatakullanarak oynatıcıda ayarlayın. - Bağlı bir denetleyici, boş olmayan bir
currentMediaItem.mediaMetadata.playlistIdalgıladığında oynatma listesinin başlığını ve poster URI'sini almak içinMediaController.getPlaylistMetadataöğesini inceler. - Etkin oynatma listesi her değiştiğinde (örneğin, kullanıcı farklı bir oynatma listesi başlattığında) oynatma listesi meta verilerini güncelleyin. Böylece
getPlaylistMetadata, her zaman mevcut öğeninplaylistIdtarafından referans verilen oynatma listesini tanımlar.
- Özel bir
Aşağıdaki örnekte, oynatma listesi meta verilerinin Player üzerinde nasıl yayınlanacağı ve bir parçanın (MediaItem) bu oynatma listesiyle nasıl ilişkilendirileceği gösterilmektedir:
Kotlin
// 1. Declare the playlist descriptive metadata on the Player val playlistMetadata = MediaMetadata.Builder() .setTitle("Morning Vibes") .setArtworkUri(Uri.parse("https://example.com/image/morning-vibes")) .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST) .build() player.setPlaylistMetadata(playlistMetadata) // 2. Mark the playing track(s) with the playlistId it belongs to val trackItem = MediaItem.Builder() .setMediaId("foobar:track:6rqhFgbbKwnb9MLmUQDhG6") .setMediaMetadata( MediaMetadata.Builder() .setTitle("Example Track") .setArtist("Example Artist") .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) .setPlaylistId("37i9dQZF1DX76Wlfdnj7AP") .build() ) .build() player.setMediaItem(trackItem)
Java
// 1. Declare the playlist descriptive metadata on the Player MediaMetadata playlistMetadata = new MediaMetadata.Builder() .setTitle("Morning Vibes") .setArtworkUri(Uri.parse("https://example.com/image/morning-vibes")) .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST) .build(); player.setPlaylistMetadata(playlistMetadata); // 2. Mark the playing track(s) with the playlistId it belongs to MediaItem trackItem = new MediaItem.Builder() .setMediaId("foobar:track:6rqhFgbbKwnb9MLmUQDhG6") .setMediaMetadata( new MediaMetadata.Builder() .setTitle("Example Track") .setArtist("Example Artist") .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) .setPlaylistId("37i9dQZF1DX76Wlfdnj7AP") .build()) .build(); player.setMediaItem(trackItem);
Oynatma listesini devam ettirme
Kullanıcı devam ettirmek için bir oynatma listesi seçtiğinde kumanda, uygulamanızın medya oturumuna bağlanır ve mediaId değeri depolanan playlistId değerine ayarlanmış bir oynatma listesi MediaItem gönderir:
Kotlin
val playlistItem = MediaItem.Builder() .setMediaId("37i9dQZF1DX76Wlfdnj7AP") .setMediaMetadata( MediaMetadata.Builder() .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST) .setIsPlayable(true) .build() ) .build() controller.setMediaItem(playlistItem) controller.prepare() controller.play()
Java
MediaItem playlistItem = new MediaItem.Builder() .setMediaId("37i9dQZF1DX76Wlfdnj7AP") .setMediaMetadata( new MediaMetadata.Builder() .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST) .setIsPlayable(true) .build()) .build(); controller.setMediaItem(playlistItem); controller.prepare(); controller.play();
Uygulamanız, MediaItem oynatma listesini MediaSession.Callback.onSetMediaItems (veya onAddMediaItems) içindeki oynatılabilir parçalarına genişletir ve önceki bölümde gösterildiği gibi playlistId ve playlistMetadata öğelerini tekrar yayınlar.
Medya düğmesi tercihlerini yönetme
Sistem kullanıcı arayüzü, Android Auto veya Wear OS gibi her denetleyici, kullanıcıya hangi düğmelerin gösterileceğine dair kendi kararlarını verebilir. Kullanıcıya göstermek istediğiniz oynatma kontrollerini belirtmek için MediaSession üzerinde medya düğmesi tercihlerini belirtebilirsiniz. Bu tercihler, her biri kullanıcı arayüzündeki bir düğme için tercih tanımlayan sıralı bir CommandButton örneği 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:
- Görsel görünümü tanımlayan simge.
CommandButton.Builderoluş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çmesine yardımcı olur. Önceden tanımlanmış simge sabitlerinden hiçbiri kullanım alanınıza uymuyorsa bunun yerinesetCustomIconResIdkullanabilirsiniz. - Kullanıcı düğmeyle etkileşimde bulunduğunda tetiklenen işlemi tanımlayan Command.
Player.CommandiçinsetPlayerCommand, önceden tanımlanmış veya özelSessionCommandiçinsetSessionCommandkullanabilirsiniz. - Düğmenin kumanda kullanıcı arayüzünde nereye yerleştirileceğ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) .setPlayerCommand(Player.COMMAND_SEEK_FORWARD) .setSlots(CommandButton.SLOT_FORWARD) .build()
Java
CommandButton button = new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15) .setPlayerCommand(Player.COMMAND_SEEK_FORWARD) .setSlots(CommandButton.SLOT_FORWARD) .build();
Medya düğmesi tercihleri çözümlenirken aşağıdaki algoritma uygulanır:
- Medya düğmesi tercihlerindeki her
CommandButtoniçin düğmeyi izin verilen ilk kullanılabilir yuvaya yerleştirin. - Merkezi, ileri ve geri yuvalardan herhangi biri düğmeyle doldurulmamışsa bu yuva için varsayılan düğmeler ekleyin.
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şturmak için CommandButton.DisplayConstraints yöntemini kullanabilirsiniz.
Medya düğmesi tercihlerini ayarlama
Medya düğmesi tercihlerini ayarlamanın en kolay yolu, MediaSession oluşturulurken listeyi tanımlamaktır. Alternatif olarak, bağlı her kumanda için medya düğmesi tercihlerini özelleştirmek üzere MediaSession.Callback.onConnectAsync() değerini 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 bir etkileşimi işledikten sonra kumanda kullanıcı arayüzünde gösterilen düğmeleri güncellemek isteyebilirsiniz. Bunun tipik bir örneği, kendisiyle ilişkili işlemi tetikledikten sonra simgesini ve işlemini değiştiren açma/kapatma düğmesidir. Medya düğmesi tercihlerini güncellemek için MediaSession.setMediaButtonPreferences 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 de kesebilirsiniz.
Özel komutları bildirme 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 favori öğeler listesine kaydetmesine olanak tanıyan düğmeler ekleyebilirsiniz. MediaController özel komutlar gönderir ve MediaSession.Callback bunları alır.
Özel komutları tanımlamak için bağlı her kumandada kullanılabilir özel komutları ayarlamak üzere MediaSession.Callback.onConnectAsync() değerini geçersiz kılmanız gerekir.
Kotlin
private class CustomMediaSessionCallback : MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnectAsync( session: MediaSession, controller: ControllerInfo, ): ListenableFuture<ConnectionResult> { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return Futures.immediateFuture( AcceptedResultBuilder(session, controller) .setAvailableSessionCommands(sessionCommands) .build() ) } }
Java
private static class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ListenableFuture<ConnectionResult> onConnectAsync( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return Futures.immediateFuture( new AcceptedResultBuilder(session, controller) .setAvailableSessionCommands(sessionCommands) .build()); } }
MediaController cihazdan özel komut istekleri almak için Callback içindeki onCustomCommand() yöntemini geçersiz kılın.
Kotlin
private class CustomCallback : MediaSession.Callback { // ... override fun onCustomCommand( session: MediaSession, controller: 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)) } // ... return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } }
Java
private static class CustomCallback 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)); } // ... return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } }
Callback yöntemlerine iletilen MediaSession.ControllerInfo nesnesinin packageName özelliğini kullanarak hangi medya kumandasının istekte bulunduğunu takip edebilirsiniz. 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, MediaSession üzerindeki Player öğ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 sarmalayın:
Kotlin
val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) { // Customizations } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
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ı komutunu isteyen kumandayı tanımlama
Bir Player yöntemine yapılan çağrı MediaController tarafından başlatıldığında, kaynağı MediaSession.controllerForCurrentRequest ile tanımlayabilir ve mevcut istek için ControllerInfo değerini alabilirsiniz:
Kotlin
private class CallerAwarePlayer(player: Player) : ForwardingSimpleBasePlayer(player) { private lateinit var session: MediaSession 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
private static final class CallerAwarePlayer extends ForwardingSimpleBasePlayer { private MediaSession session; 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 bulunan donanım düğmeleridir (ör. Bluetooth mikrofonlu kulaklıktaki oynat/duraklat düğmesi). Media3, medya düğmesi etkinliklerini oturuma ulaştıklarında sizin için işler ve oturum oynatıcıda uygun Player yöntemini çağırır.
Gelen tüm medya düğmesi etkinliklerini ilgili Player yönteminde işlemeniz önerilir. Daha gelişmiş kullanım alanları için medya düğmesi etkinlikleri MediaSession.Callback.onMediaButtonEvent() içinde kesilebilir.
Hata işleme ve raporlama
Bir oturumun oluşturduğu ve denetleyicilere bildirdiği iki tür hata vardır. Önemli hatalar, oturum oynatıcısının oynatmayı kesintiye uğratan teknik bir oynatma hatasını bildirir. Önemli hatalar, meydana geldiklerinde denetleyiciye otomatik olarak bildirilir. Önemli olmayan hatalar, oynatmayı kesintiye uğratmayan ve uygulama tarafından denetleyicilere manuel olarak gönderilen teknik olmayan veya politikayla ilgili hatalardır.
Önemli oynatma hataları
Oynatıcı, oturuma önemli bir oynatma hatası bildirir ve ardından bu hata, Player.Listener.onPlayerError(PlaybackException) ve Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) üzerinden çağrılmak üzere denetleyicilere bildirilir.
Böyle bir durumda, oynatma durumu STATE_IDLE olarak değiştirilir ve MediaController.getPlayerError(), geçişe neden olan PlaybackException değerini döndürür. Denetleyici, hatanın nedeni hakkında bilgi edinmek için PlaybackException.errorCode öğesini inceleyebilir.
Özel oynatıcı hatası ayarlama
Oynatıcı tarafından bildirilen önemli hatalara ek olarak, bir uygulama MediaSession.setPlaybackException(PlaybackException) kullanarak MediaSession düzeyinde özel bir PlaybackException ayarlayabilir. Bu sayede uygulama, bağlı kumandalara hata durumu sinyali gönderebilir. İstisna, tüm bağlı denetleyiciler veya belirli bir ControllerInfo için ayarlanabilir.
Bir uygulama bu API'yi kullanarak PlaybackException ayarladığında:
Bağlı
MediaControllerörnekleri bilgilendirilir. DenetleyicidekiListener.onPlayerError(PlaybackException)veListener.onPlayerErrorChanged(@Nullable PlaybackException)geri çağırmaları, sağlanan istisnayla çağrılır.MediaController.getPlayerError()yöntemi, uygulama tarafından ayarlananPlaybackExceptiondeğerini döndürür.Etkilenen kumandaların oynatma durumu
Player.STATE_IDLEolarak değişir.Kullanılabilir komutlar kaldırılır ve yalnızca
COMMAND_GET_TIMELINEgibi okuma komutları (önceden verilmişse) kalır. Örneğin,Timelinedurumu, istisna denetleyiciye uygulandığında dondurulur.COMMAND_PLAYgibi oynatıcının durumunu değiştirmeye çalışan komutlar, ilgili denetleyici için oynatma istisnası uygulama tarafından kaldırılana kadar kaldırılır.
Daha önce ayarlanmış özel bir PlaybackException değerini temizlemek ve normal oynatıcı durumu raporlamasını geri yüklemek için bir uygulama MediaSession.setPlaybackException(/* playbackException= */ null) veya MediaSession.setPlaybackException(ControllerInfo,
/* playbackException= */ null) yöntemini çağırabilir.
Önemli hataları özelleştirme
Kullanıcıya yerelleştirilmiş ve anlamlı bilgiler sağlamak için gerçek oynatıcıdan gelen önemli bir oynatma hatasının hata kodunu, hata mesajını ve hata eklerini özelleştirebilirsiniz. Bunun için oturumu oluştururken ForwardingSimpleBasePlayer kullanın:
Kotlin
val session = MediaSession.Builder(context, ErrorForwardingPlayer(context, player)).build()
Java
MediaSession session = new MediaSession.Builder(context, new ErrorForwardingPlayer(context, player)).build();
ForwardingSimpleBasePlayer alt sınıfınız, hatayı yakalamak ve hata kodunu, mesajı veya ekstraları özelleştirmek için getState() yöntemini geçersiz kılabilir. Aynı şekilde, orijinal oynatıcıda bulunmayan yeni hatalar da oluşturabilirsiniz:
Kotlin
private 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 } private 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
private static 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 önemli 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 bildirim denetleyicisine önemli olmayan bir hata gönderildiğinde hata kodu ve hata mesajı, platform medya oturumuna kopyalanır ancak 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. } });