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()
lubMediaPlayer.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:
- Jeśli chcesz, aby aplikacja wykonywała niestandardową konfigurację, zdefiniuj interfejs
OnDrmConfigHelper
i dołącz go do odtwarzacza za pomocąsetOnDrmConfigHelper()
. - Będziesz dzwonić pod numer
prepare()
. - 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:
- Sprawdź mapę dostępnych identyfikatorów UUID i wybierz jeden z nich.
- 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 wykonywaniaprepareDrm()
. 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 nazwieprepareDrm()
. Aby uzyskać dostęp do właściwości DRM, wywołajgetDrmPropertyString()
isetDrmPropertyString()
. 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. - Aby uzyskać nieprzejrzystą tablicę bajtową żądania klucza do wysłania do serwera licencji, wywołaj
getKeyRequest()
. - 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.
- Jeśli odpowiedź dotyczy żądania klucza offline, wynikiem jest identyfikator zestawu kluczy. Możesz użyć tego identyfikatora zestawu kluczy w
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.