Tworzenie podstawowej aplikacji odtwarzacza multimediów za pomocą Media3 ExoPlayer

Jetpack Media3 ma interfejs Player, który przedstawia podstawowe funkcje odtwarzania plików wideo i audio. ExoPlayer jest domyślną implementacją tego interfejsu w Media3. Zalecamy korzystanie z platformy ExoPlayer, ponieważ zawiera ona kompleksowy zestaw funkcji do stosowania w większości przypadków użycia związanych z odtwarzaniem. Można go też dostosować do innych celów. ExoPlayer dodatkowo wyodrębnia fragmentację urządzenia i systemu operacyjnego, aby Twój kod działał spójnie w całym ekosystemie Androida. ExoPlayer obejmuje:

Na tej stronie opisujemy najważniejsze etapy tworzenia aplikacji do odtwarzania treści. Więcej informacji znajdziesz w pełnych przewodnikach po Media3 ExoPlayer.

Wprowadzenie

Zacznij od dodania zależności do modułów ExoPlayer, UI i Common w Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.3.1"
implementation "androidx.media3:media3-ui:1.3.1"
implementation "androidx.media3:media3-common:1.3.1"

W zależności od przypadku użycia możesz też potrzebować dodatkowych modułów z Media3, np. exoplayer-dash, aby odtwarzać strumienie w formacie DASH.

Zastąp 1.3.1 preferowaną wersją biblioteki. Najnowszą wersję znajdziesz w informacjach o wersji.

Tworzenie odtwarzacza

W Media3 możesz użyć zawartej w nim implementacji interfejsu Player (ExoPlayer) lub utworzyć własną implementację niestandardową.

Tworzenie odtwarzacza ExoPlayer

Najprostszy sposób utworzenia instancji ExoPlayer to:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

Odtwarzacz możesz utworzyć, korzystając z metody cyklu życia onCreate() elementów Activity, Fragment lub Service, w których się on znajduje.

Builder zawiera różne opcje dostosowywania, które mogą Cię zainteresować, na przykład:

Media3 udostępnia komponent interfejsu PlayerView, który możesz uwzględnić w pliku układu swojej aplikacji. Ten komponent zawiera obiekt PlayerControlView do sterowania odtwarzaniem, SubtitleView do wyświetlania napisów i Surface do renderowania wideo.

Przygotowywanie odtwarzacza

Dodaj elementy multimedialne do playlisty, aby umożliwić ich odtwarzanie za pomocą metod takich jak setMediaItem() i addMediaItem(). Następnie wywołaj prepare(), aby rozpocząć ładowanie multimediów i pozyskać niezbędne zasoby.

Nie wykonuj tych czynności, dopóki aplikacja nie będzie działać na pierwszym planie. Jeśli odtwarzacz znajduje się w strefie Activity lub Fragment, oznacza to przygotowanie go do użycia metody cyklu życia onStart() na poziomie API 24 lub wyższym albo metod cyklu życia onResume() na poziomie API 23 i niższych. W przypadku gracza, którego pozycja obejmuje Service, możesz przygotować ją w aplikacji onCreate().

Sterowanie odtwarzaczem

Po przygotowaniu odtwarzacza możesz sterować odtwarzaniem, wywołując w nim metody, takie jak:

Komponenty interfejsu, takie jak PlayerView czy PlayerControlView, będą się aktualizować zgodnie z oczekiwaniami po powiązaniu z odtwarzaczem.

Zwolnij odtwarzacz

Odtwarzanie może wymagać zasobów, które są ograniczone, takich jak dekodery wideo, dlatego ważne jest, aby wywołać release() w odtwarzaczu, co pozwoli zwolnić zasoby, gdy odtwarzacz nie jest już potrzebny.

Jeśli odtwarzacz korzysta z interfejsu Activity lub Fragment, opublikuj go w metodzie cyklu życia onStop() na poziomie API 24 lub wyższym albo w metodzie onPause() na poziomie API 23 i niższych. W przypadku gracza, którego obszar obejmuje Service, możesz zwolnić go w okresie onDestroy().

Zarządzanie odtwarzaniem w sesji multimediów

Na Androidzie sesje multimediów to ustandaryzowany sposób interakcji z odtwarzaczem multimediów niezależnie od granic procesów. Połączenie sesji multimedialnej z odtwarzaczem pozwala reklamować odtwarzanie multimediów na zewnątrz i odbierać polecenia odtwarzania ze źródeł zewnętrznych, na przykład dzięki integracji z systemowymi elementami sterującymi multimediami na urządzeniach mobilnych i urządzeniach z dużymi ekranami.

Aby używać sesji multimediów, dodaj zależność do modułu Media3 Session:

implementation "androidx.media3:media3-session:1.3.1"

Utwórz sesję multimediów

Po zainicjowaniu odtwarzacza możesz utworzyć 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 w każdej implementacji Player, w tym z implementacją ExoPlayer, CastPlayer oraz z implementacją niestandardową.

Przekazywanie kontroli innym klientom

Aplikacje klienckie mogą zaimplementować kontroler multimediów, by sterować odtwarzaniem sesji multimediów. Aby je odbierać, ustaw obiekt wywołania zwrotnego podczas kompilowania MediaSession.

Gdy kontroler ma się połączyć z sesją multimediów, wywoływana jest metoda onConnect(). Możesz użyć udostępnionego ControllerInfo, aby zdecydować, czy chcesz zaakceptować, czy odrzucić żądanie. Odpowiedni przykład znajdziesz w aplikacji demonstracyjnej Media3 Session.

Po nawiązaniu połączenia kontroler może wysyłać do sesji polecenia odtwarzania. W sesji te polecenia są przekazywane graczowi. Zdefiniowane w interfejsie Player polecenia dotyczące odtwarzania i playlisty są obsługiwane automatycznie przez sesję.

Inne metody wywołania zwrotnego pozwalają obsługiwać np. żądania niestandardowych poleceń odtwarzania czy modyfikowanie playlisty. Te wywołania zwrotne podobnie zawierają obiekt ControllerInfo, więc możesz określić kontrolę dostępu dla każdego żądania.

Odtwarzanie multimediów w tle

Aby odtwarzać multimedia, gdy aplikacja nie jest na pierwszym planie, np. aby odtwarzać muzykę, audiobooki lub podcasty, nawet gdy użytkownik jej nie ma, urządzenia Player i MediaSession powinny być zawarte w usłudze na pierwszym planie. Media3 udostępnia do tego celu interfejs MediaSessionService.

Implementowanie: MediaSessionService

Utwórz klasę, która rozszerza zakres 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 klasa 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(), by kontrolować dostęp klienta do sesji multimediów. 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;
}

Łączę z Twoim UI

Teraz gdy sesja multimediów znajduje się w strefie Service innej niż Activity lub Fragment, gdzie działa interfejs odtwarzacza, możesz je połączyć za pomocą MediaController. W metodzie onStart() Activity lub Fragment w swoim interfejsie utwórz SessionToken dla MediaSession, a następnie użyj SessionToken, aby utworzyć MediaController. Tworzenie obiektu 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, dzięki czemu możesz sterować odtwarzaniem za pomocą tych samych metod, takich jak play() i pause(). Podobnie jak w przypadku innych komponentów pamiętaj, aby zwolnić MediaController, gdy nie jest już potrzebny, na przykład w metodzie cyklu życia onStop() obiektu Activity, wywołując metodę MediaController.releaseFuture().

Publikowanie powiadomienia

Usługi na pierwszym planie muszą być aktywne, aby publikować powiadomienia. MediaSessionService automatycznie utworzy dla Ciebie powiadomienie MediaStyle w postaci MediaNotification. Aby udostępnić powiadomienie niestandardowe, utwórz MediaNotification.Provider za pomocą DefaultMediaNotificationProvider.Builder lub niestandardową implementację interfejsu dostawcy. Dodaj dostawcę do urządzenia MediaSession za pomocą setMediaNotificationProvider.

Reklamowanie biblioteki treści

MediaLibraryService bazuje na MediaSessionService, umożliwiając aplikacjom klienckim przeglądanie treści multimedialnych dostarczanych przez Twoją aplikację. Aplikacje klienckie implementują tag MediaBrowser, który umożliwia interakcję z Twoim MediaLibraryService.

Implementacja MediaLibraryService jest podobna do implementowania MediaSessionService. Jedyna różnica jest taka, że w obiekcie onGetSession() trzeba zwracać wartość MediaLibrarySession zamiast MediaSession. W porównaniu z elementem MediaSession.Callback właściwość 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 dla elementów MediaLibraryService oraz (na potrzeby zgodności wstecznej) starszej wersji MediaBrowserService. Dodatkowy filtr intencji umożliwia aplikacjom klienckim korzystanie z interfejsu API MediaBrowserCompat do rozpoznawania Twojego elementu Service.

Element MediaLibrarySession umożliwia udostępnianie biblioteki treści w strukturze drzewa z pojedynczym poziomem głównym MediaItem. Każdy element MediaItem w drzewie może mieć dowolną liczbę podrzędnych węzłów MediaItem. Możesz udostępnić inny poziom główny lub inne drzewo w zależności od żądania aplikacji klienckiej. Na przykład drzewo zwracane klientowi szukającemu listy zalecanych elementów multimedialnych może zawierać tylko główny element MediaItem i 1 poziom podrzędnych węzłów MediaItem. Drzewo wyświetlane w innej aplikacji klienckiej może przedstawiać pełniejszą bibliotekę treści.

Tworzę 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 dostępu do roota MediaItem drzewa treści
  • onGetChildren(), gdy klient wysyła żądanie elementów podrzędnych obiektu MediaItem w drzewie treści
  • onGetSearchResult(), gdy klient wysyła dla danego zapytania do drzewa treści żądanie wyników wyszukiwania

Odpowiednie metody wywołania zwrotnego obejmują obiekt LibraryParams z dodatkowymi sygnałami dotyczącymi typu drzewa treści, którym interesuje się aplikacja kliencka.