Omówienie MediaPlayer

Platforma multimediów Android obsługuje odtwarzanie różnych popularnych typów multimediów, dzięki czemu można łatwo zintegrować dźwięk, filmy i obrazy ze swoimi aplikacjami. Możesz odtwarzać dźwięk i film z plików multimedialnych przechowywanych w zasobach aplikacji (zasoby nieprzetworzone), samodzielnych plików w systemie plików lub ze strumienia danych przesyłanego przez połączenie sieciowe, a wszystko to przy użyciu interfejsów API MediaPlayer.

Z tego dokumentu dowiesz się, jak za pomocą MediaPlayer pisać aplikację do odtwarzania multimediów, która współpracuje z użytkownikiem i systemem w celu uzyskania wysokiej wydajności i wygody. Możesz też użyć ExoPlayer, czyli niestandardowej biblioteki open source, która obsługuje funkcje o wysokiej wydajności niedostępne w wersji MediaPlayer.

Uwaga: dane dźwiękowe możesz odtwarzać tylko na standardowym urządzeniu wyjściowym. Obecnie jest to głośnik urządzenia mobilnego lub zestaw słuchawkowy Bluetooth. Nie można odtwarzać plików dźwiękowych w postaci dźwięku rozmowy podczas rozmowy.

Podstawy

Do odtwarzania dźwięku i filmu w ramach platformy Androida służą te klasy:

MediaPlayer
Ta klasa jest podstawowym interfejsem API do odtwarzania dźwięku i wideo.
AudioManager
Te zajęcia zarządzają źródłami dźwięku i wyjściem audio na urządzeniu.

Deklaracje w pliku manifestu

Zanim zaczniesz tworzyć aplikację przy użyciu MediaPlayer, upewnij się, że plik manifestu zawiera odpowiednie deklaracje pozwalające na korzystanie z powiązanych funkcji.

  • Uprawnienia do internetu – jeśli używasz MediaPlayer do strumieniowego przesyłania treści sieciowych, aplikacja musi prosić o dostęp do sieci.
    <uses-permission android:name="android.permission.INTERNET" />
    
  • Uprawnienia blokady wybudzania – jeśli aplikacja odtwarzacza musi zapobiegać przyciemnianiu ekranu lub uśpieniu procesora albo używa metod MediaPlayer.setScreenOnWhilePlaying() lub MediaPlayer.setWakeMode(), musisz poprosić o to uprawnienie.
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

Korzystanie z MediaPlayer

Jednym z najważniejszych komponentów platformy mediów jest klasa MediaPlayer. Obiekt tej klasy może pobierać, dekodować i odtwarzać zarówno dźwięk, jak i obraz przy minimalnej konfiguracji. Obsługuje kilka różnych źródeł mediów, takie jak:

  • Zasoby lokalne
  • wewnętrzne identyfikatory URI, np. identyfikatory uzyskiwane z mechanizmu rozpoznawania treści;
  • Zewnętrzne adresy URL (strumieniowe przesyłanie danych)

Listę formatów multimediów obsługiwanych przez Androida znajdziesz na stronie Obsługiwane formaty multimediów.

Oto przykład odtwarzania dźwięku, który jest dostępny jako lokalny nieprzetworzony zasób (zapisany w katalogu res/raw/ aplikacji):

Kotlin

var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1)
mediaPlayer.start() // no need to call prepare(); create() does that for you

Java

MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you

W tym przypadku „surowy” zasób to plik, którego system nie próbuje przeanalizować w żaden sposób. Nie powinna to być jednak surowa ścieżka dźwiękowa. Powinien to być odpowiednio zakodowany i sformatowany plik multimedialny w jednym z obsługiwanych formatów.

Można odtworzyć następujące treści z identyfikatora URI dostępnego lokalnie w systemie (na przykład uzyskanego za pomocą programu do rozpoznawania treści):

Kotlin

val myUri: Uri = .... // initialize Uri here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, myUri)
    prepare()
    start()
}

Java

Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

Odtwarzanie ze zdalnego adresu URL przez strumieniowe przesyłanie danych HTTP wygląda tak:

Kotlin

val url = "http://........" // your URL here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(url)
    prepare() // might take long! (for buffering, etc)
    start()
}

Java

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

Uwaga: jeśli przekazujesz adres URL, aby przesyłać strumieniowo plik multimedialny online, plik musi mieć możliwość pobierania progresywnego.

Uwaga: jeśli używasz setDataSource(), musisz przechwycić lub przekazać IllegalArgumentException i IOException, ponieważ plik, do którego się odwołujesz, może nie istnieć.

Przygotowanie asynchroniczne

Użycie właściwości MediaPlayer może być z założenia proste. Pamiętaj jednak, że do prawidłowej integracji z typową aplikacją na Androida potrzeba kilku innych czynników. Na przykład wykonanie wywołania prepare() może zająć dużo czasu, ponieważ może ono wiązać się z pobieraniem i dekodowaniem danych multimedialnych. Tak więc jak w przypadku każdej metody, której uruchomienie może zająć dużo czasu, nigdy nie należy jej wywoływać z wątku interfejsu aplikacji. W rezultacie interfejs użytkownika zawiesza się do czasu powrotu metody, co bardzo niekorzystnie wpływa na wygodę użytkowników i może spowodować błąd ANR (Aplikacja nie odpowiada). Nawet jeśli spodziewasz się, że zasób wczytuje się szybko, pamiętaj, że odpowiedź w interfejsie, której odpowiedź zajmuje ponad 1/10 sekundy, powoduje zauważalne wstrzymanie i daje użytkownikowi wrażenie, że aplikacja działa wolno.

Aby uniknąć zawieszenia wątku w interfejsie, utwórz kolejny wątek, aby przygotować MediaPlayer, i powiadom wątek główny, gdy zostanie zakończony. Mimo że można samodzielnie napisać logikę wątków, wzorzec ten jest tak popularny w przypadku korzystania z elementu MediaPlayer, że platforma zapewnia wygodny sposób na wykonanie tego zadania przy użyciu metody prepareAsync(). Rozpoczyna ona przygotowywanie multimediów w tle i od razu powraca. Gdy multimedia są przygotowywane, wywoływana jest metoda onPrepared() obiektu MediaPlayer.OnPreparedListener skonfigurowana za pomocą setOnPreparedListener().

Stan zarządzania

Kolejnym aspektem właściwości MediaPlayer, o której warto pamiętać, jest to, że jest uzależniona od stanu. Oznacza to, że MediaPlayer ma stan wewnętrzny, o którym musisz pamiętać przy pisaniu kodu, bo niektóre operacje są ważne tylko wtedy, gdy odtwarzacz jest w określonych stanach. Jeśli wykonasz operację w nieprawidłowym stanie, system może zgłosić wyjątek lub inne niepożądane zachowania.

Dokumentacja klasy MediaPlayer zawiera pełny diagram stanu, który wyjaśnia, które metody przenoszą MediaPlayer z jednego stanu do innego. Gdy na przykład utworzysz nowy zasób MediaPlayer, będzie on oznaczony jako Nieaktywny. Należy go zainicjować, wywołując setDataSource() i zmieniając jego stan do stanu Initialized (Inicjowane). Następnie musisz go przygotować za pomocą metody prepare() lub prepareAsync(). Po zakończeniu przygotowywania obiektu MediaPlayer przechodzi w stan Prepared (Przygotowane). Oznacza to, że można wywołać metodę start(), aby umożliwić odtwarzanie multimediów. W tym momencie, jak widać na diagramie, możesz przełączać się między stanami Started, Pause i PlaybackComplete, wywołując m.in. metody takie jak start(), pause() i seekTo(). Zwróć jednak uwagę na to, że gdy wywołasz metodę stop(), nie będzie można ponownie wywołać funkcji start(), dopóki nie przygotujesz ponownie metody MediaPlayer.

Podczas pisania kodu, który wchodzi w interakcję z obiektem MediaPlayer, zawsze pamiętaj o diagramie stanu, ponieważ częstą przyczyną błędów jest wywoływanie jego metod w niewłaściwym stanie.

Zwalnianie odtwarzacza MediaPlayer

MediaPlayer może zużywać cenne zasoby systemowe. Dlatego zawsze stosuj dodatkowe środki ostrożności, aby nie utrzymywać instancji MediaPlayer dłużej, niż jest to konieczne. Gdy już to zrobisz, zawsze wywołaj release(), aby upewnić się, że wszystkie przydzielone do niej zasoby systemowe zostały poprawnie zwolnione. Jeśli na przykład używasz elementu MediaPlayer, a Twoja aktywność otrzymuje wywołanie onStop(), musisz zwolnić MediaPlayer, ponieważ nie ma sensu ich trzymać w czasie, gdy Twoja aktywność nie wchodzi w interakcję z użytkownikiem (chyba że odtwarzasz multimedia w tle, co zostało omówione w następnej sekcji). Oczywiście po wznowieniu lub ponownym uruchomieniu odtwarzania musisz utworzyć nowy plik MediaPlayer i przygotować go ponownie przed wznowieniem odtwarzania.

Aby zwolnić tę czynność, a potem unieważnić MediaPlayer:

Kotlin

mediaPlayer?.release()
mediaPlayer = null

Java

mediaPlayer.release();
mediaPlayer = null;

Rozważmy na przykład problemy, które mogą się pojawić, jeśli zapomnisz zwolnić MediaPlayer, gdy aktywność zostanie zatrzymana, ale utworzysz nowy, gdy aktywność zacznie się ponownie. Jak być może wiesz, gdy użytkownik zmieni orientację ekranu (lub w inny sposób zmieni konfigurację urządzenia), system zajmie się tym, ponownie uruchamiając działanie (domyślnie), dzięki czemu możesz szybko zużyć wszystkie zasoby systemowe, gdy użytkownik obraca urządzenie w pionie i w poziomie, ponieważ przy każdej zmianie orientacji użytkownik tworzy nowy obiekt MediaPlayer, którego nigdy nie udostępnia. Więcej informacji o ponownym uruchamianiu środowiska wykonawczego znajdziesz w artykule Obsługa zmian środowiska wykonawczego.

Możesz się zastanawiać, co się stanie, jeśli chcesz kontynuować odtwarzanie „multimediów w tle”, nawet jeśli użytkownik opuści Twoją aktywność – podobnie jak działa wbudowana aplikacja Muzyka. W tym przypadku potrzebny jest MediaPlayer sterowany przez Usługę, jak opisano w następnej sekcji.

Korzystanie z MediaPlayer w usłudze

Jeśli chcesz, aby multimedia były odtwarzane w tle nawet wtedy, gdy aplikacja nie jest widoczna na ekranie, czyli chcesz, żeby użytkownik korzystał z innych aplikacji, musisz uruchomić usługę i stamtąd sterować instancją MediaPlayer. Odtwarzacz MediaPlayer należy umieścić w usłudze MediaBrowserServiceCompat, a interfejs powinien wchodzić w interakcję z elementem MediaBrowserCompat w innym działaniu.

Zachowaj ostrożność podczas takiej konfiguracji klienta/serwera. Istnieją pewne oczekiwania dotyczące sposobu, w jaki odtwarzacz działający w usłudze w tle komunikuje się z resztą systemu. Jeśli Twoja aplikacja nie spełnia tych oczekiwań, użytkownik może być niezadowolony. Więcej informacji znajdziesz w artykule Tworzenie aplikacji audio.

W tej sekcji opisano specjalne instrukcje zarządzania odtwarzaczem MediaPlayer wdrożonym w ramach usługi.

Asynchronicznie

Przede wszystkim, tak jak w przypadku Activity, wszystkie operacje w elemencie Service są domyślnie wykonywane w jednym wątku. Jeśli uruchamiasz działanie i usługę w tej samej aplikacji, domyślnie korzystają one z tego samego wątku („wątku głównego”). Dlatego usługi muszą szybko przetwarzać intencje przychodzące i nigdy nie wykonywać długich obliczeń podczas odpowiadania na nie. Jeśli spodziewasz się intensywnej pracy lub blokowania połączeń, musisz wykonać te zadania asynchronicznie: z innego zaimplementowanego przez siebie wątku lub za pomocą licznych funkcji platformy do przetwarzania asynchronicznego.

Na przykład, jeśli używasz funkcji MediaPlayer z wątku głównego, użyj wywołania prepareAsync() zamiast prepare() i zaimplementuj MediaPlayer.OnPreparedListener, aby otrzymać powiadomienie o zakończeniu przygotowania i rozpoczęcie odtwarzania. Na przykład:

Kotlin

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mediaPlayer = ... // initialize it here
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Obsługa błędów asynchronicznych

W przypadku operacji synchronicznych błędy są zwykle sygnalizowane wyjątkiem lub kodem błędu, ale za każdym razem, gdy korzystasz z zasobów asynchronicznych, zadbaj o to, aby aplikacja była odpowiednio informowana o błędach. W przypadku obiektu MediaPlayer możesz to zrobić, implementując MediaPlayer.OnErrorListener i ustawiając go w swojej instancji MediaPlayer:

Kotlin

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Pamiętaj, że gdy wystąpi błąd, element MediaPlayer przechodzi do stanu Error (zapoznaj się z dokumentacją klasy MediaPlayer na potrzeby pełnego diagramu stanu). Aby móc ponownie z niej korzystać, musisz ją zresetować.

Korzystanie z blokad uśpienia

Podczas projektowania aplikacji odtwarzających multimedia w tle urządzenie może przejść w tryb uśpienia, gdy usługa jest uruchomiona. System Android stara się oszczędzać baterię, gdy urządzenie jest uśpione, dlatego system próbuje wyłączyć wszystkie niepotrzebne funkcje telefonu, w tym procesor i sprzęt Wi-Fi. Jeśli jednak Twoja usługa odtwarza lub przesyła strumieniowo muzykę, warto, aby system zakłócał odtwarzanie.

Aby mieć pewność, że Twoja usługa będzie nadal działać w tych warunkach, musisz użyć „blokad uśpienia”. Blokada uśpienia pozwala zasygnalizować systemowi, że aplikacja korzysta z funkcji, która powinna być dostępna nawet wtedy, gdy telefon jest nieaktywny.

Uwaga: blokad uśpienia należy zawsze stosować z umiarem.

Aby procesor MediaPlayer nadal działał podczas odtwarzania, wywołaj metodę setWakeMode() podczas inicjowania MediaPlayer. Gdy to zrobisz, MediaPlayer blokuje określoną blokadę podczas odtwarzania i zwalnia ją po wstrzymaniu lub zatrzymaniu:

Kotlin

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

Java

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

Blokada uśpienia w tym przykładzie gwarantuje tylko, że procesor pozostaje aktywny. Jeśli strumieniujesz multimedia przez sieć i korzystasz z Wi-Fi, warto też trzymać urządzenie WifiLock, które trzeba pobrać i udostępnić ręcznie. Dlatego gdy zaczniesz przygotowywać MediaPlayer przy użyciu zdalnego adresu URL, musisz utworzyć i utworzyć blokadę Wi-Fi. Na przykład:

Kotlin

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

Java

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

Gdy wstrzymasz lub zatrzymasz odtwarzanie multimediów albo nie będziesz już potrzebować sieci, zwolnij blokadę:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Czyszczenie

Jak już wspomnieliśmy, obiekt MediaPlayer może zużywać znaczną ilość zasobów systemowych, dlatego należy go przechowywać tak długo, jak to konieczne, i wywoływać metodę release(), gdy nie jest już potrzebny. Ważne jest, aby wywoływać tę metodę czyszczenia w sposób jawny, a nie polegać na funkcji odśmiecania systemu, ponieważ może minąć trochę czasu, zanim odzyskuje on obiekt MediaPlayer, ponieważ jest wrażliwy tylko na potrzeby w zakresie pamięci, a nie na brak innych zasobów związanych z multimediami. W przypadku korzystania z usługi zawsze zastąp metodę onDestroy(), aby mieć pewność, że udostępniasz MediaPlayer:

Kotlin

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

Java

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       super.onDestroy();
       if (mediaPlayer != null) mediaPlayer.release();
   }
}

Poza możliwością zwolnienia MediaPlayer przy wyłączeniu zawsze szukaj innych możliwości. Jeśli na przykład spodziewasz się, że nie będziesz w stanie odtwarzać multimediów przez dłuższy czas (np. po utracie dźwięku), koniecznie zwolnij dotychczasowy plik MediaPlayer i utwórz go ponownie później. Jeśli jednak spodziewasz się zatrzymania odtwarzania tylko na krótki czas, lepiej pozostań przy MediaPlayer, aby uniknąć opłat związanych z jego tworzeniem i przygotowywaniem.

Zarządzanie prawami cyfrowymi (DRM)

Od Androida 8.0 (poziom interfejsu API 26) MediaPlayer obejmuje interfejsy API, które umożliwiają odtwarzanie materiałów chronionych DRM. Są one podobne do niskopoziomowego interfejsu API udostępnianego przez MediaDrm, ale działają na wyższym poziomie i nie ujawniają bazowych obiektów wyodrębniania, drm ani kryptograficznych.

Interfejs MediaPlayer DRM API nie zapewnia pełnej funkcjonalności MediaDrm, ale obsługuje większość typowych przypadków użycia. Obecna implementacja obsługuje te typy treści:

  • Lokalne pliki multimedialne chronione przez Widevine
  • Chronione przez Widevine pliki multimedialne do zdalnego/strumieniowego przesyłania danych

Poniższy fragment kodu pokazuje, jak użyć nowych metod DRM MediaPlayer w prostej implementacji synchronicznej.

Aby zarządzać multimediami kontrolowanymi przez DRM, musisz uwzględnić nowe metody w zwykłym przepływie wywołań MediaPlayer, jak pokazano poniżej:

Kotlin

mediaPlayer?.apply {
    setDataSource()
    setOnDrmConfigHelper() // optional, for custom configuration
    prepare()
    drmInfo?.also {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }

    // MediaPlayer is now ready to use
    start()
    // ...play/pause/resume...
    stop()
    releaseDrm()
}

Java

setDataSource();
setOnDrmConfigHelper(); // optional, for custom configuration
prepare();
if (getDrmInfo() != null) {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// MediaPlayer is now ready to use
start();
// ...play/pause/resume...
stop();
releaseDrm();

Zacznij od zainicjowania obiektu MediaPlayer i ustawienia jego źródła za pomocą setDataSource() (jak zwykle). Następnie, aby użyć DRM, wykonaj te czynności:

  1. Jeśli chcesz, aby aplikacja wykonywała niestandardową konfigurację, zdefiniuj interfejs OnDrmConfigHelper i dołącz go do odtwarzacza za pomocą setOnDrmConfigHelper().
  2. Będziesz dzwonić pod numer prepare().
  3. Będziesz dzwonić pod numer getDrmInfo(). Jeśli źródło ma treści DRM, metoda zwraca niepustą wartość MediaPlayer.DrmInfo.

Jeśli MediaPlayer.DrmInfo istnieje:

  1. Sprawdź mapę dostępnych identyfikatorów UUID i wybierz jeden z nich.
  2. Aby przygotować konfigurację DRM dla bieżącego źródła, wywołaj prepareDrm().
    • Jeśli masz utworzone i zarejestrowane wywołanie zwrotne OnDrmConfigHelper, jest ono wywoływane podczas wykonywania prepareDrm(). W ten sposób możesz przeprowadzić niestandardową konfigurację właściwości DRM przed otwarciem sesji DRM. Wywołanie zwrotne jest wywoływane synchronicznie w wątku o nazwie prepareDrm(). Aby uzyskać dostęp do właściwości DRM, wywołaj getDrmPropertyString() i setDrmPropertyString(). Unikaj wykonywania długich operacji.
    • Jeśli urządzenie nie zostało jeszcze udostępnione, prepareDrm() łączy się też z serwerem obsługi administracyjnej w celu obsługi administracyjnej urządzenia. Może to zająć różny czas w zależności od połączenia sieciowego.
  3. Aby uzyskać nieprzejrzystą tablicę bajtową żądania klucza do wysłania do serwera licencji, wywołaj getKeyRequest().
  4. Aby poinformować mechanizm DRM o odpowiedzi klucza z serwera licencji, wywołaj provideKeyResponse(). Wynik zależy od typu żądania klucza:
    • Jeśli odpowiedź dotyczy żądania klucza offline, wynikiem jest identyfikator zestawu kluczy. Możesz użyć tego identyfikatora zestawu kluczy w restoreKeys(), aby przywrócić klucze do nowej sesji.
    • Jeśli odpowiedź dotyczy żądania przesyłania strumieniowego lub wydania, wynik ma wartość null.

Asynchronicznie uruchamiam aplikację prepareDrm()

Domyślnie interfejs prepareDrm() uruchamia się synchronicznie i blokuje do czasu zakończenia przygotowania. Pierwsze przygotowanie DRM na nowym urządzeniu może też wymagać obsługi administracyjnej, która jest obsługiwana wewnętrznie przez prepareDrm(), a jej zakończenie może trochę potrwać ze względu na operacje sieciowe. Aby uniknąć blokowania domeny prepareDrm(), zdefiniuj i ustaw MediaPlayer.OnDrmPreparedListener.

Gdy skonfigurujesz OnDrmPreparedListener, prepareDrm() przeprowadza obsługę administracyjną (w razie potrzeby) i przygotowanie w tle. Po zakończeniu udostępniania i przygotowywania wywoływany jest detektor. Nie należy wyciągać żadnych założeń dotyczących sekwencji wywołującej lub wątku, w którym działa detektor (chyba że detektor jest zarejestrowany w wątku modułu obsługi). Detektor można wywołać przed lub po zwróceniu funkcji prepareDrm().

Asynchroniczne konfigurowanie DRM

Możesz zainicjować asynchronicznie zabezpieczenie DRM, tworząc i rejestrując MediaPlayer.OnDrmInfoListener na potrzeby przygotowywania zabezpieczeń DRM i MediaPlayer.OnDrmPreparedListener do uruchamiania odtwarzacza. Działają one w połączeniu z funkcją prepareAsync(), jak pokazano poniżej:

Kotlin

setOnPreparedListener()
setOnDrmInfoListener()
setDataSource()
prepareAsync()
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) {
    mediaPlayer.apply {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
override fun onPrepared(mediaPlayer: MediaPlayer) {
    mediaPlayer.start()
}

Java

setOnPreparedListener();
setOnDrmInfoListener();
setDataSource();
prepareAsync();
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
onDrmInfo() {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
onPrepared() {

start();
}

Obsługa zaszyfrowanych multimediów

Od Androida 8.0 (poziom interfejsu API 26) MediaPlayer może też odszyfrowywać pliki CENC (Common Encryption Scheme) oraz zaszyfrowane multimedia na poziomie przykładowym HLS (metoda=SAMPLE-AES) w przypadku podstawowych typów strumieni H.264 i AAC. Wcześniej obsługiwane były multimedia zaszyfrowane na cały segment (Method=AES-128).

Pobieranie multimediów z narzędzia do rozpoznawania treści

Kolejną przydatną w aplikacjach odtwarzacza multimediów funkcją jest pobieranie muzyki dostępnej na urządzeniu. Możesz to zrobić, wysyłając zapytanie do ContentResolver w poszukiwaniu nośników zewnętrznych:

Kotlin

val resolver: ContentResolver = contentResolver
val uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor: Cursor? = resolver.query(uri, null, null, null, null)
when {
    cursor == null -> {
        // query failed, handle error.
    }
    !cursor.moveToFirst() -> {
        // no media on the device
    }
    else -> {
        val titleColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE)
        val idColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID)
        do {
            val thisId = cursor.getLong(idColumn)
            val thisTitle = cursor.getString(titleColumn)
            // ...process entry...
        } while (cursor.moveToNext())
    }
}
cursor?.close()

Java

ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
    // query failed, handle error.
} else if (!cursor.moveToFirst()) {
    // no media on the device
} else {
    int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
    int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
    do {
       long thisId = cursor.getLong(idColumn);
       String thisTitle = cursor.getString(titleColumn);
       // ...process entry...
    } while (cursor.moveToNext());
}

Aby używać tej funkcji z aplikacją MediaPlayer, możesz:

Kotlin

val id: Long = /* retrieve it from somewhere */
val contentUri: Uri =
    ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id )

mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, contentUri)
}

// ...prepare and start...

Java

long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId(
        android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);

mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), contentUri);

// ...prepare and start...

Więcej informacji

Te strony zawierają tematy związane z nagrywaniem, przechowywaniem i odtwarzaniem plików audio i wideo.