Jetpack Media3 definiuje interfejs Player, który określa podstawowe funkcje odtwarzania plików wideo i audio. ExoPlayer to domyślna implementacja tego interfejsu w Media3. Zalecamy korzystanie z ExoPlayera, ponieważ zapewnia on kompleksowy zestaw funkcji, które obejmują większość przypadków użycia odtwarzania, i można go dostosować do obsługi wszelkich dodatkowych przypadków użycia. ExoPlayer eliminuje też fragmentację urządzeń i systemów operacyjnych, dzięki czemu kod działa spójnie w całym ekosystemie Androida. ExoPlayer obejmuje:
- obsługę playlist,
- Obsługa różnych formatów strumieniowania progresywnego i adaptacyjnego.
- obsługę wstawiania reklam zarówno po stronie klienta, jak i po stronie serwera;
- obsługę odtwarzania treści chronionych DRM,
Na tej stronie znajdziesz opis niektórych kluczowych kroków tworzenia aplikacji do odtwarzania. Więcej informacji znajdziesz w naszych pełnych przewodnikach po Media3 ExoPlayer.
Pierwsze kroki
Na początek dodaj zależność od modułów ExoPlayer, UI i Common biblioteki Jetpack Media3:
Kotlin
implementation("androidx.media3:media3-exoplayer:1.11.1")
implementation("androidx.media3:media3-ui:1.11.1")
implementation("androidx.media3:media3-common:1.11.1")
Dynamiczny
implementation "androidx.media3:media3-exoplayer:1.11.1"
implementation "androidx.media3:media3-ui:1.11.1"
implementation "androidx.media3:media3-common:1.11.1"
W zależności od przypadku użycia możesz też potrzebować dodatkowych modułów z Media3, takich jak exoplayer-dash, aby odtwarzać strumienie w formacie DASH.
Zastąp 1.11.1 preferowaną wersją biblioteki. Najnowszą wersję znajdziesz w informacjach o wersji.
Tworzenie odtwarzacza multimediów
W Media3 możesz użyć dołączonej implementacji interfejsu Player, ExoPlayer, lub utworzyć własną implementację niestandardową.
Tworzenie ExoPlayera
Najprostszy sposób na utworzenie instancji ExoPlayer:
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
Odtwarzacz multimedialny możesz utworzyć w metodzie cyklu życia onCreate() elementu Activity, Fragment lub Service, w którym się znajduje.
Builder zawiera szereg opcji dostosowywania, które mogą Cię zainteresować, takich jak:
setAudioAttributes(), aby skonfigurować obsługę fokusu audio.setHandleAudioBecomingNoisy(), aby skonfigurować zachowanie odtwarzania po odłączeniu urządzenia wyjściowego audio.setTrackSelector(), aby skonfigurować wybór ścieżki.
Media3 udostępnia komponent interfejsu PlayerView, który możesz umieścić w pliku układu aplikacji. Ten komponent zawiera element PlayerControlView do sterowania odtwarzaniem, element SubtitleView do wyświetlania napisów i element Surface do renderowania filmu.
Przygotowuję odtwarzacz
Dodaj elementy multimedialne do playlisty, aby je odtwarzać, używając metod takich jak setMediaItem() i addMediaItem(). Następnie wywołaj prepare(), aby rozpocząć wczytywanie multimediów i uzyskać niezbędne zasoby.
Nie wykonuj tych czynności, zanim aplikacja nie znajdzie się na pierwszym planie. Jeśli odtwarzacz znajduje się w Activity lub Fragment, oznacza to, że należy go przygotować w metodzie cyklu życia onStart() na poziomie interfejsu API 24 lub wyższym albo w metodzie cyklu życia onResume() na poziomie interfejsu API 23 lub niższym. W przypadku odtwarzacza w Service możesz przygotować go w onCreate(). Przykład implementacji metod cyklu życia znajdziesz w ćwiczeniach z programowania dotyczących ExoPlayera.
Sterowanie odtwarzaczem
Po przygotowaniu odtwarzacza możesz sterować odtwarzaniem, wywołując w nim metody takie jak:
play()ipause(), aby rozpocząć i wstrzymać odtwarzanie;seekTo(), aby przejść do określonego miejsca w bieżącym elemencie multimedialnym.seekToNextMediaItem()iseekToPreviousMediaItem(), aby poruszać się po playliście
Elementy interfejsu, takie jak PlayerView lub PlayerControlView, będą odpowiednio aktualizowane po powiązaniu z odtwarzaczem.
Zwolnij zawodnika
Odtwarzanie może wymagać zasobów, które są dostępne w ograniczonej ilości, takich jak dekodery wideo, dlatego ważne jest, aby wywołać metodę release() na odtwarzaczu, aby zwolnić zasoby, gdy odtwarzacz nie jest już potrzebny.
Jeśli odtwarzacz znajduje się w Activity lub Fragment, zwolnij go w metodzie cyklu życia onStop() na poziomie interfejsu API 24 lub wyższym albo w metodzie onPause() na poziomie interfejsu API 23 lub niższym. Jeśli gracz jest w Service, możesz go zwolnić w onDestroy(). Przykład implementacji metod cyklu życia znajdziesz w ćwiczeniach z programowania dotyczących ExoPlayera.
Zarządzanie odtwarzaniem za pomocą sesji multimedialnej
Na Androidzie sesje multimedialne zapewniają standardowy sposób interakcji z odtwarzaczem multimediów niezależnie od granic procesów. Połączenie sesji multimedialnej z odtwarzaczem umożliwia przekazywanie informacji o odtwarzaniu multimediów źródłom zewnętrznym i otrzymywanie poleceń odtwarzania ze źródeł zewnętrznych, np. w celu integracji z systemowymi opcjami sterowania multimediami na urządzeniach mobilnych i urządzeniach z dużym ekranem.
Aby korzystać z sesji multimedialnych, dodaj zależność od modułu Media3 Session:
Kotlin
implementation("androidx.media3:media3-session:1.11.1")
Dynamiczny
implementation "androidx.media3:media3-session:1.11.1"
Tworzenie sesji multimedialnej
Po zainicjowaniu odtwarzacza możesz utworzyć obiekt MediaSession w ten sposób:
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();
Media3 automatycznie synchronizuje stan Player ze stanem MediaSession. Działa to z każdą implementacją Player, w tym ExoPlayer, CastPlayer lub implementacją niestandardową.
Przyznawanie kontroli innym klientom
Aplikacje klienckie mogą implementować kontroler multimediów, aby sterować odtwarzaniem sesji multimedialnej. Aby otrzymywać te żądania, podczas tworzenia obiektu MediaSession ustaw obiekt callback.
Gdy kontroler ma się połączyć z sesją multimedialną, wywoływana jest metoda onConnect(). Na podstawie podanego obiektu ControllerInfo możesz zaakceptować lub odrzucić prośbę. Przykład znajdziesz w aplikacji demonstracyjnej sesji Media3.
Po połączeniu kontroler może wysyłać do sesji polecenia odtwarzania. Sesja przekazuje te polecenia do odtwarzacza. Polecenia odtwarzania i playlisty zdefiniowane w interfejsie Player są automatycznie obsługiwane przez sesję.
Inne metody wywołania zwrotnego umożliwiają obsługę np. żądań niestandardowych poleceń odtwarzania i modyfikowania playlisty. Te wywołania zwrotne również zawierają obiekt ControllerInfo, dzięki czemu możesz określić kontrolę dostępu na podstawie poszczególnych żądań.
Odtwarzanie multimediów w tle
Aby kontynuować odtwarzanie multimediów, gdy aplikacja nie działa na pierwszym planie, np. odtwarzać muzykę, audiobooki lub podcasty, nawet gdy użytkownik nie ma otwartej aplikacji, komponenty Player i MediaSession powinny być umieszczone w usłudze na pierwszym planie. W tym celu Media3 udostępnia interfejs MediaSessionService.
Wdrażanie MediaSessionService
Utwórz klasę, która rozszerza MediaSessionService, i utwórz instancję MediaSession w metodzie cyklu życia onCreate().
Kotlin
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
// Create your Player and MediaSession in the onCreate lifecycle event
override fun onCreate() {
super.onCreate()
val player = ExoPlayer.Builder(this).build()
mediaSession = MediaSession.Builder(this, player).build()
}
// Remember to release the player and media session in onDestroy
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
}
Java
public class PlaybackService extends MediaSessionService {
private MediaSession mediaSession = null;
@Override
public void onCreate() {
super.onCreate();
ExoPlayer player = new ExoPlayer.Builder(this).build();
mediaSession = new MediaSession.Builder(this, player).build();
}
@Override
public void onDestroy() {
mediaSession.getPlayer().release();
mediaSession.release();
mediaSession = null;
super.onDestroy();
}
}
W pliku manifestu zadeklaruj klasę Service z filtrem intencji MediaSessionService i poproś o uprawnienie FOREGROUND_SERVICE, aby uruchomić usługę na pierwszym planie:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Na koniec w utworzonej klasie zastąp metodę onGetSession(), aby kontrolować dostęp klienta do sesji multimedialnej. Zwróć MediaSession, aby zaakceptować prośbę o połączenie, lub null, aby ją odrzucić.
Kotlin
// This example always accepts the connection request
override fun onGetSession(
controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession
Java
@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
// This example always accepts the connection request
return mediaSession;
}
Łączenie z interfejsem
Teraz, gdy sesja multimedialna znajduje się w komponencie Service, który jest oddzielony od komponentu Activity lub Fragment, w którym znajduje się interfejs odtwarzacza, możesz je połączyć za pomocą komponentu MediaController. W metodzie onStart() klasy Activity lub Fragment z interfejsem użytkownika utwórz SessionToken dla MediaSession, a następnie użyj SessionToken, aby utworzyć MediaController. Tworzenie MediaController odbywa się asynchronicznie.
Kotlin
override fun onStart() {
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener(
{
// Call controllerFuture.get() to retrieve the MediaController.
// MediaController implements the Player interface, so it can be
// attached to the PlayerView UI component.
playerView.setPlayer(controllerFuture.get())
},
MoreExecutors.directExecutor()
)
}
Java
@Override
public void onStart() {
SessionToken sessionToken =
new SessionToken(this, new ComponentName(this, PlaybackService.class));
ListenableFuture<MediaController> controllerFuture =
new MediaController.Builder(this, sessionToken).buildAsync();
controllerFuture.addListener(() -> {
// Call controllerFuture.get() to retrieve the MediaController.
// MediaController implements the Player interface, so it can be
// attached to the PlayerView UI component.
playerView.setPlayer(controllerFuture.get());
}, MoreExecutors.directExecutor())
}
MediaController implementuje interfejs Player, więc możesz używać tych samych metod, takich jak play() i pause(), do sterowania odtwarzaniem. Podobnie jak w przypadku innych komponentów pamiętaj, aby zwolnić MediaController, gdy nie jest już potrzebny, w metodzie cyklu życia onStop() obiektu Activity, wywołując MediaController.releaseFuture().
Publikowanie powiadomienia
Usługi na pierwszym planie muszą publikować powiadomienia, gdy są aktywne. MediaSessionService automatycznie utworzy dla Ciebie powiadomienie MediaStyle w formie MediaNotification. Aby wyświetlić niestandardowe powiadomienie, utwórz obiekt MediaNotification.Provider za pomocą DefaultMediaNotificationProvider.Builder lub utwórz niestandardową implementację interfejsu dostawcy. Dodaj dostawcę do MediaSession za pomocą setMediaNotificationProvider.
Reklamowanie biblioteki treści
MediaLibraryService rozszerza MediaSessionService, umożliwiając aplikacjom klienckim przeglądanie treści multimedialnych udostępnianych przez Twoją aplikację. Aplikacje klienckie implementują MediaBrowser, aby wchodzić w interakcje z MediaLibraryService.
Implementacja MediaLibraryService jest podobna do implementacji MediaSessionService, z tą różnicą, że w onGetSession() należy zwrócić MediaLibrarySession zamiast MediaSession. W porównaniu z MediaSession.Callback MediaLibrarySession.Callback zawiera dodatkowe metody, które umożliwiają klientowi przeglądarki poruszanie się po treściach oferowanych przez usługę biblioteki.
Podobnie jak w przypadku MediaSessionService, zadeklaruj MediaLibraryService w pliku manifestu i poproś o uprawnienie FOREGROUND_SERVICE, aby uruchomić usługę na pierwszym planie:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Powyższy przykład zawiera filtr intencji zarówno dla MediaLibraryService, jak i starszej wersji MediaBrowserService (w celu zapewnienia zgodności wstecznej). Dodatkowy filtr intencji umożliwia aplikacjom klienckim korzystającym z interfejsu API MediaBrowserCompat rozpoznawanie Twojego Service.
MediaLibrarySession umożliwia wyświetlanie biblioteki treści w strukturze drzewa z jednym elementem głównym MediaItem. Każdy węzeł MediaItem w drzewie może mieć dowolną liczbę węzłów podrzędnych MediaItem. Możesz wyświetlać inny katalog główny lub inne drzewo w zależności od żądania aplikacji klienckiej. Na przykład drzewo, które zwracasz klientowi szukającemu listy zalecanych elementów multimedialnych, może zawierać tylko węzeł główny MediaItem i jeden poziom węzłów podrzędnych MediaItem, podczas gdy drzewo, które zwracasz innej aplikacji klienckiej, może reprezentować bardziej kompletną bibliotekę treści.
Tworzenie: MediaLibrarySession
MediaLibrarySession rozszerza interfejs API MediaSession, aby dodać interfejsy API do przeglądania treści. W porównaniu z wywołaniem zwrotnym MediaSession wywołanie zwrotne MediaLibrarySession dodaje metody takie jak:
onGetLibraryRoot(), gdy klient żąda katalogu głównegoMediaItemdrzewa treści;onGetChildren(), gdy klient żąda elementów podrzędnychMediaItemw drzewie treści.onGetSearchResult(), gdy klient prosi o wyniki wyszukiwania z drzewa treści dla danego zapytania.
Odpowiednie metody wywołania zwrotnego będą zawierać obiekt LibraryParams z dodatkowymi sygnałami dotyczącymi typu drzewa treści, którym jest zainteresowana aplikacja kliencka.