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

Jetpack Media3 definiuje interfejs Player, który zawiera 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, a także można go dostosować do innych przypadków użycia. ExoPlayer eliminuje też problemy związane z fragmentacją urządzeń i systemów operacyjnych, dzięki czemu Twój kod działa spójnie w całej ekosystemie Androida. ExoPlayer obejmuje:

Na tej stronie znajdziesz omówienie niektórych kluczowych etapów tworzenia aplikacji do odtwarzania. Więcej informacji znajdziesz w pełnych przewodnikach dotyczących Media3 ExoPlayer.

Pierwsze kroki

Aby rozpocząć, dodaj zależność od modułów ExoPlayer, UI i Common w Jetpack Media3:

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

W zależności od zastosowania możesz też potrzebować dodatkowych modułów Media3, takich jak exoplayer-dash, aby odtwarzać strumienie w formacie DASH.

Pamiętaj, aby zastąpić 1.5.0 preferowaną wersją biblioteki. Aby dowiedzieć się więcej o najnowszej wersji, zapoznaj się z informacjami o wersji.

Tworzenie odtwarzacza

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

Tworzenie ExoPlayera

Najprostszym sposobem utworzenia instancji ExoPlayer jest:

Kotlin

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

Java

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

Odtwarzacz multimediów możesz utworzyć w metodzie cyklu życia onCreate() w Activity, Fragment lub Service, w którym się znajduje.

Builder zawiera szereg opcji dostosowywania, które mogą Cię zainteresować, takie jak:

Media3 udostępnia komponent UI PlayerView, który możesz umieścić w pliku układu aplikacji. Ten komponent zawiera PlayerControlView do sterowania odtwarzaniem, SubtitleView do wyświetlania napisów i Surface do renderowania filmu.

Przygotowywanie odtwarzacza

Dodaj elementy multimedialne do playlisty, aby odtwarzać je za pomocą metod takich jak setMediaItem() i addMediaItem(). Następnie zadzwoń pod numer prepare(), aby rozpocząć wczytywanie multimediów i pobrać niezbędne zasoby.

Zanim aplikacja będzie na pierwszym planie, nie wykonuj tych czynności. Jeśli odtwarzacz znajduje się w stanach Activity lub Fragment, oznacza to, że należy go przygotować w metodzie cyklu życia onStart() na poziomie interfejsu API 24 lub nowszym albo w metodzie cyklu życia onResume() na poziomie interfejsu API 23 lub niższym. Jeśli gracz jest w Service, możesz go przygotować w onCreate().

Sterowanie odtwarzaczem

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

Komponenty interfejsu, takie jak PlayerView lub PlayerControlView, będą się odpowiednio aktualizować po połączeniu z odtwarzaczem.

Zwolnij odtwarzacz

Odtwarzanie może wymagać zasobów, których podaż jest ograniczona, takich jak dekodery wideo, dlatego ważne jest, aby wywołać release() w odtwarzaczu, aby zwolnić zasoby, gdy odtwarzacz nie jest już potrzebny.

Jeśli odtwarzacz znajduje się w stanach Activity lub Fragment, zwalniaj go za pomocą metody cyklu życia onStop() na poziomie interfejsu API 24 lub nowszym albo metody onPause() na poziomie interfejsu API 23 lub niższym. Jeśli gracz znajduje się w Service, możesz go przenieść do onDestroy().

Zarządzanie odtwarzaniem za pomocą sesji multimediów

Na Androidzie sesje multimediów zapewniają ustandaryzowany sposób interakcji z odtwarzaczem multimediów niezależnie od granic procesów. Połączenie sesji multimediów z odtwarzaczem pozwala reklamować odtwarzanie multimediów na zewnątrz i odbierać polecenia odtwarzania z zewnętrznych źródeł, na przykład w celu integracji z elementami sterującymi odtwarzaniem multimediów w systemie na urządzeniach mobilnych i urządzeniach z dużym ekranem.

Aby korzystać z sesji multimediów, dodaj zależność od modułu Media3 Session:

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

Tworzenie sesji multimedialnej

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. Ta funkcja działa z dowolną implementacją Player, w tym ExoPlayer, CastPlayer lub implementacją niestandardową.

Przyznawanie kontroli innym klientom

Aplikacje klienckie mogą stosować sterownik multimediów do sterowania odtwarzaniem multimediów. Aby otrzymywać te żądania, podczas tworzenia MediaSession ustaw obiekt callback.

Gdy kontroler ma się połączyć z sesją multimediów, wywoływana jest metoda onConnect(). Możesz użyć otrzymanego ControllerInfo, aby zdecydować, czy zaakceptować, czy odrzucić prośbę. Przykład takiej sytuacji znajdziesz w aplikacji demonstracyjnej Media3 Session.

Po nawiązaniu połączenia kontroler może wysyłać do sesji polecenia odtwarzania. Sesja przekazuje te polecenia odtwarzaczowi. Polecenia odtwarzania i playlist zdefiniowane w interfejsie Player są obsługiwane automatycznie przez sesję.

Inne metody wywołania umożliwiają obsługę na przykład żądań niestandardowych poleceń odtwarzania i modyfikowania playlisty. Te funkcje zwracane zawierają również obiekt ControllerInfo, dzięki czemu możesz określać kontrolę dostępu w przypadku poszczególnych żądań.

odtwarzanie multimediów w tle,

Aby odtwarzanie multimediów było kontynuowane, gdy aplikacja nie jest na pierwszym planie (np. aby odtwarzać muzykę, audiobooki lub podcasty, gdy użytkownik nie ma otwartej aplikacji), funkcje PlayerMediaSession powinny być zapakowane w usługę na pierwszym planie. W tym celu Media3 udostępnia interfejs MediaSessionService.

Wdrażanie MediaSessionService

Utwórz klasę rozszerzającą klasę MediaSessionService i utwórz instancję klasy 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 uprawnieniem FOREGROUND_SERVICE do uruchamiania usługi 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 utworzonym przez siebie module zastąpij metodę onGetSession(), aby kontrolować dostęp klienta do sesji multimediów. Zwracaj wartość 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 użytkownika

Teraz, gdy sesja multimediów jest w Service oddzielna od Activity lub Fragment, gdzie znajduje się interfejs użytkownika odtwarzacza, możesz użyć MediaController, aby połączyć je ze sobą. W metodzie onStart() klasy Activity lub Fragment w interfejsie użytkownika utwórz obiekt SessionToken dla obiektu MediaSession, a następnie użyj obiektu SessionToken do utworzenia obiektu 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 korzysta z interfejsu Player, więc do sterowania odtwarzaniem możesz używać 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, np. w metodach cyklu życia onStop() w klasie Activity, wywołując funkcję MediaController.releaseFuture().

Publikowanie powiadomienia

Usługi na pierwszym planie muszą publikować powiadomienia podczas działania. MediaSessionService automatycznie utworzy dla Ciebie powiadomienie MediaStyle w postaci MediaNotification. Aby wyświetlić powiadomienie niestandardowe, utwórz MediaNotification.Provider za pomocą DefaultMediaNotificationProvider.Builder lub stwórz niestandardową implementację interfejsu dostawcy. Dodaj dostawcę do MediaSession za pomocą setMediaNotificationProvider.

Promowanie biblioteki treści

MediaLibraryService opiera się na MediaSessionService, umożliwiając aplikacjom klienta przeglądanie treści multimedialnych udostępnianych przez Twoją aplikację. Aplikacje klienta implementują MediaBrowser, aby wchodzić w interakcje z Twoją aplikacją MediaLibraryService.

Wdrażanie funkcji MediaLibraryService jest podobne do wdrażania funkcji MediaSessionService, z tą różnicą, że w funkcji onGetSession() zamiast MediaSession należy zwracać MediaLibrarySession. W porównaniu z MediaSession.Callback usługa MediaLibrarySession.Callback zawiera dodatkowe metody, które umożliwiają klientowi przeglądarki poruszanie się po treściach oferowanych przez Twoją 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" />

Przykład powyżej zawiera filtr intencji zarówno dla MediaLibraryService, jak i dla starszego MediaBrowserService (ze względu na zgodność wsteczną). 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 element rdzeniowy lub inny element drzewa na podstawie żądania aplikacji klienckiej. Na przykład drzewo zwracane klientowi, który szuka listy polecanych elementów multimedialnych, może zawierać tylko węzeł główny MediaItem i jeden poziom węzłów podrzędnych MediaItem, podczas gdy drzewo zwracane innej aplikacji klienta może reprezentować bardziej kompletną bibliotekę treści.

Tworzenie MediaLibrarySession

MediaLibrarySession rozszerza interfejs API MediaSession, aby dodać interfejsy API przeglądania treści. W porównaniu z MediaSession wywołaniem zwrotnym wywołanie zwrotne MediaLibrarySession zawiera takie metody jak:

  • onGetLibraryRoot() (gdy klient poprosi o korzeń MediaItem drzewa treści)
  • onGetChildren() gdy klient prosi o podanie elementów podrzędnych elementu MediaItem w drzewie treści
  • onGetSearchResult()gdy klient żąda wyników wyszukiwania z drzewa treści dla danego zapytania

Odpowiednie metody wywołania będą zawierać obiekt LibraryParams z dodatkowymi sygnałami o typie drzewa treści, którym zainteresowana jest aplikacja klienta.