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

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 ExoPlayer, ponieważ zapewnia on kompleksowy zestaw funkcji, które obejmują większość przypadków użycia odtwarzania, i można go dostosować do obsługi dodatkowych przypadków użycia. ExoPlayer abstrahuje też od fragmentacji urządzeń i systemów operacyjnych, dzięki czemu Twój kod działa spójnie w całym ekosystemie Androida. ExoPlayer obejmuje:

  • obsługę playlist;
  • obsługę różnych formatów progresywnego i adaptacyjnego przesyłania strumieniowego formatów
  • obsługę wstawiania reklam po stronie klienta i po stronie serwera; wstawiania reklam
  • obsługę odtwarzania treści chronionych przez DRM.

Na tej stronie znajdziesz opis najważniejszych kroków związanych z tworzeniem 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.10.0"
implementation "androidx.media3:media3-ui:1.10.0"
implementation "androidx.media3:media3-common:1.10.0"

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.

Pamiętaj, aby zastąpić 1.10.0 preferowaną wersją biblioteki. Najnowszą wersję znajdziesz w informacjach o wersji.

Tworzenie odtwarzacza multimediów

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

Tworzenie ExoPlayer

Najprostszy sposób na utworzenie instancji ExoPlayer jest taki:

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órej się znajduje.

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

Media3 udostępnia komponent interfejsu 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 wideo.

Przygotowywanie odtwarzacza

Dodaj elementy multimedialne do playlisty, aby je odtworzyć, za pomocą metod takich jak setMediaItem() i addMediaItem(). Następnie wywołaj prepare(), aby rozpocząć wczytywanie multimediów i uzyskać niezbędne zasoby.

Nie należy wykonywać 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, który znajduje się w Service, możesz go przygotować w onCreate(). Przykład implementacji metod cyklu życia znajdziesz w ćwiczeniach z programowania dotyczących ExoPlayer (zobacz ExoPlayer codelab).

Sterowanie odtwarzaczem

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

Komponenty interfejsu, takie jak PlayerView lub PlayerControlView, zostaną odpowiednio zaktualizowane po powiązaniu z odtwarzaczem.

Zwalnianie odtwarzacza

Odtwarzanie może wymagać zasobów, które są ograniczone, np. dekoderów 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 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. W przypadku odtwarzacza, który znajduje się w Service, możesz go zwolnić w onDestroy(). Przykład implementacji metod cyklu życia znajdziesz w ćwiczeniach z programowania dotyczących ExoPlayer (zobacz ExoPlayer codelab).

Zarządzanie odtwarzaniem za pomocą sesji multimedialnej

W Androidzie sesje multimedialne zapewniają standardowy sposób interakcji z odtwarzaczem multimediów w różnych procesach. 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:

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

Tworzenie sesji multimedialnej

MediaSession możesz utworzyć po zainicjowaniu odtwarzacza 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ą Player implementacją, w tym ExoPlayer, CastPlayer i implementacją niestandardową.

Przyznawanie kontroli innym klientom

Aplikacje klienckie mogą implementować kontroler multimediów aby sterować odtwarzaniem sesji multimedialnej. Aby otrzymywać te żądania, ustaw a wywołania zwrotnego obiekt podczas tworzenia MediaSession.

Gdy kontroler ma się połączyć z sesją multimedialną, wywoływana jest metoda onConnect(). Na podstawie podanego obiektu ControllerInfo możesz zdecydować, czy zaakceptować czy odrzucić prośbę. Przykład znajdziesz w aplikacji w wersji demonstracyjnej sesji Media3.

Po nawiązaniu połączenia 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ślać kontrolę dostępu na podstawie poszczególnych żądań.

Odtwarzanie multimediów w tle

Aby kontynuować odtwarzanie multimediów, gdy aplikacja nie jest na pierwszym planie (np. odtwarzać muzykę, audiobooki lub podcasty, nawet gdy użytkownik nie ma otwartej aplikacji ), Player i MediaSession powinny być zamknięte w usłudze na pierwszym planie. W tym celu Media3 udostępnia interfejs MediaSessionService.

Implementowanie 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 dodaj 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. Aby zaakceptować prośbę o połączenie, zwróć MediaSession, a aby ją odrzucić, zwróć null.

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 Service oddzielnej od Activity lub Fragment, w której znajduje się interfejs odtwarzacza, możesz użyć MediaController, aby je połączyć. W metodzie onStart() w Activity lub Fragment z interfejsem 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, np. w metodzie cyklu życia onStop() w Activity, wywołując MediaController.releaseFuture().

Publikowanie powiadomienia

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

Reklamowanie biblioteki treści

A MediaLibraryService opiera się na 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.

Implementowanie MediaLibraryService jest podobne do implementowania MediaSessionService, z tym wyjątkiem, ż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ę działającą 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 dla starszej wersji MediaBrowserService w celu zapewnienia zgodności wstecznej. Dodatkowy filtr intencji umożliwia aplikacjom klienckim korzystającym z interfejsu API MediaBrowserCompat rozpoznawanie Twojej Service.

MediaLibrarySession umożliwia udostępnianie biblioteki treści w strukturze drzewa z jednym głównym MediaItem. Każdy MediaItem w drzewie może mieć dowolną liczbę podrzędnych węzłów MediaItem. Możesz udostępniać 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 polecanych elementów multimedialnych, może zawierać tylko główny MediaItem i jeden poziom podrzędnych węzłów MediaItem, natomiast drzewo, które zwracasz innej aplikacji klienckiej, może reprezentować pełniejszą bibliotekę treści.

Tworzenie MediaLibrarySession

MediaLibrarySession rozszerza interfejs API MediaSession, dodając interfejsy API do przeglądania treści. W porównaniu z wywołaniem zwrotnym MediaSession, wywołanie zwrotne MediaLibrarySession dodaje metody takie jak:

  • onGetLibraryRoot() w przypadku, gdy klient prosi o główny MediaItem drzewa treści;
  • onGetChildren() w przypadku, gdy klient prosi o elementy podrzędne MediaItem w drzewie treści;
  • onGetSearchResult() w przypadku, gdy klient prosi o wyniki wyszukiwania w drzewie 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óre interesuje aplikację kliencką.