OpenSL ES na Androida

OSTRZEŻENIE: standard OpenSL ES został wycofany. Deweloperzy powinni używać biblioteki Oboe typu open source, która jest dostępna na GitHubie. Oboe to kod w języku C++ zapewniający interfejs API podobny do AAudio. Oboe wywołuje AAudio, gdy jest ono dostępne, a gdy nie jest, używa OpenSL ES.

Ta strona zawiera szczegółowe informacje o tym, jak implementacja OpenSL ES™ w NDK różni się od specyfikacji referencyjnej OpenSL ES 1.0.1. Jeśli używasz przykładowego kodu ze specyfikacji, może być konieczne jego zmodyfikowanie, aby działał na Androidzie.

O ile nie zaznaczono inaczej, wszystkie funkcje są dostępne w wersji Androida 2.3 (interfejs API na poziomie 9) i wyższych. Niektóre funkcje są dostępne tylko w Androidzie 4.0 (poziom interfejsu API 14).

Uwaga: dokument z definicją zgodności z Androidem (CDD) zawiera wymagania sprzętowe i oprogramowania zgodnego urządzenia z Androidem. Więcej informacji o ogólnym programie zgodności znajdziesz w dokumentacji na temat zgodności z Androidem, a w  dokumentacji CDD – o samym dokumencie CDD.

OpenSL ES udostępnia interfejs w języku C, który jest też dostępny w języku C++. Zawiera funkcje podobne do tych, które są dostępne w częściach interfejsów API na Androida w języku Java:

Podobnie jak w przypadku wszystkich pakietów NDK (NDK – Native Development Kit) głównym celem OpenSL ES na Androida jest ułatwienie implementacji współdzielonych bibliotek, które mają być wywoływane za pomocą natywnej interfejsu Java (JNI ). Pakiet NDK nie jest przeznaczony do pisania czystych aplikacji C/C++. OpenSL ES to jednak interfejs API o pełnej funkcjonalności, który powinien umożliwić Ci realizację większości potrzeb związanych z dźwiękiem przy użyciu tylko tego interfejsu API, bez wywołań kodu działającego w czasie wykonywania Androida.

Uwaga: interfejs API natywnego dźwięku na Androidzie (wysoko wydajny dźwięk) jest oparty na OpenSL ES, ale nie jest zgodną implementacją żadnego profilu OpenSL ES 1.0.1 (gra, muzyka lub telefon). Dzieje się tak, ponieważ Android nie obsługuje wszystkich funkcji wymaganych przez żaden z tych profili. Wszystkie znane przypadki, w których Android działa inaczej niż określono w specyfikacji, opisano na stronie Rozszerzenia na Androida.

Funkcje dziedziczone ze specyfikacji referencyjnej

Implementacja OpenSL ES na Androidzie w wersji NDK dziedziczy większość funkcji ze specyfikacji referencyjnej, z pewnymi ograniczeniami.

Globalne punkty wejścia

OpenSL ES na Androida obsługuje wszystkie globalne punkty wejścia w specyfikacji Androida. Te punkty wejścia to:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

Obiekty i interfejsy

W tabeli poniżej znajdziesz obiekty i interfejsy obsługiwane przez implementację OpenSL ES w Androidzie NDK. Jeśli w komórce pojawi się opcja Tak, funkcja jest dostępna w tej implementacji.

Obsługa obiektów i interfejsów przez Android NDK.

Funkcja Odtwarzacz audio Rejestratory dźwięku Silnik Zestaw wyjściowy
Wzmocnienie basów Tak Nie Nie Tak
Kolejka buforowania Tak Nie Nie Nie
lokalizator danych kolejki bufora Tak: źródło Nie Nie Nie
Dynamiczne zarządzanie interfejsem Tak Tak Tak Tak
Wysyłanie efektów Tak Nie Nie Nie
Silnik Nie Nie Tak Nie
pogłos środowiska, Nie Nie Nie Tak
Korektor Tak Nie Nie Tak
Lokalizator danych urządzeń wejścia-wyjścia Nie Tak: źródło Nie Nie
Wyodrębnianie metadanych Tak: dekoduj do formatu PCM Nie Nie Nie
Wycisz solo Tak Nie Nie Nie
Obiekt Tak Tak Tak Tak
Lokalizator wyjściowego miksu Tak: zlew Nie Nie Nie
Odtwórz Tak Nie Nie Nie
Szybkość odtwarzania Tak Nie Nie Nie
Stan pobierania z wyprzedzeniem Tak Nie Nie Nie
Gotowy pogłos Nie Nie Nie Tak
Nagraj Nie Tak Nie Nie
Szukaj Tak Nie Nie Nie
lokalizator danych URI Tak: źródło Nie Nie Nie
Wirtualizator Tak Nie Nie Tak
Głośność Tak Nie Nie Nie

W następnej sekcji opisujemy ograniczenia dotyczące niektórych z tych funkcji.

Ograniczenia

W przypadku funkcji wymienionych w tabeli 1 obowiązują pewne ograniczenia. Te ograniczenia stanowią różnice w stosunku do specyfikacji referencyjnej. Pozostała część tej sekcji zawiera informacje o tych różnicach.

Dynamiczne zarządzanie interfejsem

OpenSL ES na Androida nie obsługuje funkcji RemoveInterface ani ResumeInterface.

Kombinacje efektów: pogłos środowiska i gotowy pogłos

Nie można użyć jednocześnie pogłosu środowiskowego i gotowego pogłosu w tym samym miksie wyjściowym.

Platforma może zignorować żądania efektów, jeśli uzna, że obciążenie procesora będzie zbyt duże.

Efekt wysłano

SetSendLevel() obsługuje 1 poziom wysyłania na odtwarzacz audio.

Pogłos środowiska

Pogłos środowiska nie obsługuje pól reflectionsDelay, reflectionsLevel ani reverbDelay struktury SLEnvironmentalReverbSettings.

Format danych MIME

Formatu danych MIME możesz używać tylko w przypadku lokalizatora danych URI i tylko w przypadku odtwarzacza audio. Nie możesz używać tego formatu danych do rejestratora dźwięku.

Implementacja OpenSL ES na Androidzie wymaga zainicjowania mimeType do NULL lub prawidłowego ciągu UTF-8. Musisz też zainicjować containerType prawidłową wartością. Jeśli nie ma innych przesłanek, np. przenośności na inne implementacje lub formaty treści, których aplikacja nie może zidentyfikować na podstawie nagłówka, zalecamy ustawienie wartości mimeType na NULL, a wartości containerType na SL_CONTAINERTYPE_UNSPECIFIED.

OpenSL ES na Androida obsługuje te formaty audio, o ile są one obsługiwane przez platformę Android:

  • WAV PCM.
  • WAV alaw.
  • WAV ulaw.
  • MP3 Ogg Vorbis.
  • AAC LC.
  • HE-AACv1 (AAC+).
  • HE-AACv2 (ulepszona wersja AAC+).
  • AMR.
  • FLAC.

Uwaga: listę formatów audio obsługiwanych przez Androida znajdziesz w artykule Obsługiwane formaty multimediów.

W ramach tej implementacji OpenSL ES obowiązują następujące ograniczenia dotyczące obsługi tych i innych formatów:

  • Formaty AAC muszą znajdować się w kontenerze MP4 lub ADTS.
  • OpenSL ES na Androida nie obsługuje MIDI.
  • WMA nie jest częścią AOSP, a my nie weryfikujemy jego zgodności z OpenSL ES na Androida.
  • Implementacja OpenSL ES w Android NDK nie obsługuje bezpośredniego odtwarzania treści chronionych DRM lub zaszyfrowanych. Aby odtworzyć chronione treści audio, musisz je odszyfrować w aplikacji przed odtworzeniem, a aplikacja musi egzekwować wszelkie ograniczenia DRM.

OpenSL ES na Androida nie obsługuje tych metod manipulowania obiektami:

  • Resume()
  • RegisterCallback()
  • AbortAsyncOperation()
  • SetPriority()
  • GetPriority()
  • SetLossOfControlInterfaces()

Format danych PCM

PCM to jedyny format danych, którego możesz używać z kolejkami buforów. Obsługiwane konfiguracje odtwarzania PCM:

  • 8-bitowa bez znaku lub 16-bitowa ze znakiem.
  • mono lub stereo.
  • bajty w porządku little-endian,
  • Częstotliwości próbkowania:
    • 8000 Hz.
    • 11 025 Hz.
    • 12 000 Hz.
    • 16 000 Hz.
    • 22 050 Hz.
    • 24 000 Hz.
    • 32 000 Hz.
    • 44 100 Hz.
    • 48 000 Hz.

Konfiguracje, które OpenSL ES na Androida obsługują nagrywanie,zależą od urządzenia. Zwykle dźwięk mono/16-bitowy z podpisem 16 000 Hz jest dostępny niezależnie od urządzenia.

Wartość pola samplesPerSec jest podawana w milihercach Hz, mimo że nazwa może być myląca. Aby uniknąć przypadkowego użycia nieprawidłowej wartości, zalecamy zainicjowanie tego pola za pomocą jednej z konstant symbolicznych zdefiniowanych w tym celu, np. SL_SAMPLINGRATE_44_1.

Android 5.0 (poziom interfejsu API 21) i nowsze wersje obsługują dane zmiennoprzecinkowe.

Szybkość odtwarzania

Prędkość odtwarzania OpenSL ES wskazuje szybkość, z jaką obiekt przedstawia dane, wyrażoną w tysięcznych częściach normalnej szybkości lub na mille. Na przykład szybkość odtwarzania 1000 na tysiąc to 1000/1000, czyli normalna szybkość. Zakres szybkości to zamknięty przedział, który wyraża zakres możliwych szybkości odtwarzania.

Obsługa zakresów szybkości odtwarzania i innych funkcji może się różnić w zależności od wersji i implementacji platformy. Aplikacja może określić te możliwości w czasie wykonywania, wysyłając zapytanie do urządzenia za pomocą funkcji PlaybackRate::GetRateRange() lub PlaybackRate::GetCapabilitiesOfRate().

Urządzenie zwykle obsługuje ten sam zakres szybkości dla źródła danych w formacie PCM oraz jednolitą szybkość w zakresie 1000 do 1000 w przypadku innych formatów. Oznacza to, że jednolita szybkość jest w istocie jedną wartością.

Nagraj

OpenSL ES na Androida nie obsługuje zdarzeń SL_RECORDEVENT_HEADATLIMIT ani SL_RECORDEVENT_HEADMOVING.

Szukaj

Metoda SetLoop() umożliwia odtwarzanie całego pliku w pętli. Aby włączyć zapętlenie, ustaw parametr startPos na 0, a parametr endPos na SL_TIME_UNKNOWN.

lokalizator danych kolejki bufora

Odtwarzacz audio lub dyktafon z lokalizatorem danych do obsługi kolejki bufora obsługuje tylko format danych PCM.

lokalizator danych urządzenia wejścia/wyjścia

OpenSL ES na Androida obsługuje lokalizator danych urządzenia wejścia/wyjścia tylko wtedy, gdy użyjesz go jako źródła danych dla Engine::CreateAudioRecorder(). Inicjuj lokalizator danych urządzenia, używając wartości zawartych w tym fragmencie kodu:

SLDataLocator_IODevice loc_dev =
  {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
  SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};

Lokalizator danych URI

OpenSL ES na Androida może używać lokalizatora danych URI tylko w formacie danych MIME i tylko w odtwarzaczu audio. Nie możesz używać identyfikatora URI w przypadku rejestratora dźwięku. Identyfikator URI może używać tylko schematów http: i file:. Inne schematy, takie jak https:, ftp: lub content:, są niedozwolone.

Nie zweryfikowaliśmy obsługi rtsp: z dźwiękiem na platformie Android.

Struktury danych

Android obsługuje te struktury danych OpenSL ES 1.0.1:

  • SLDataFormat_MIME
  • SLDataFormat_PCM
  • SLDataLocator_BufferQueue
  • SLDataLocator_IODevice
  • SLDataLocator_OutputMix
  • SLDataLocator_URI
  • SLDataSink
  • SLDataSource
  • SLEngineOption
  • SLEnvironmentalReverbSettings
  • SLInterfaceID

Konfiguracja platformy

OpenSL ES na Androida jest przeznaczony do aplikacji wielowątkowych i jest bezpieczny pod względem wątków. Obsługuje 1 wyszukiwarkę na aplikację i maksymalnie 32 obiekty na wyszukiwarkę. Dostępna pamięć urządzenia i procesor mogą dodatkowo ograniczać liczbę obiektów, które można wykorzystać.

Te opcje silnika są rozpoznawane, ale są ignorowane przez slCreateEngine:

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

OpenMAX AL i OpenSL ES mogą być używane razem w tej samej aplikacji. W tym przypadku wewnętrznie jest jeden udostępniony obiekt silnika, a limit 32 obiektów jest wspólny dla OpenMAX AL i OpenSL ES. Aplikacja powinna utworzyć oba silniki, użyć obu silników i na koniec je zniszczyć. Implementacja utrzymuje liczbę odwołań do udostępnionego mechanizmu, aby można było go prawidłowo zniszczyć podczas drugiej operacji usuwania.

Informacje o programowaniu

Uwagi dotyczące programowania OpenSL ES zawierają dodatkowe informacje, które zapewniają prawidłowe wdrożenie OpenSL ES.

Uwaga: dla Twojej wygody dołączyliśmy kopię specyfikacji OpenSL ES 1.0.1 wraz z NDK w języku: docs/opensles/OpenSL_ES_Specification_1.0.1.pdf.

Problemy z platformą

Ta sekcja opisuje znane problemy w pierwszej wersji platformy obsługującej te interfejsy API.

Dynamiczne zarządzanie interfejsem

DynamicInterfaceManagement::AddInterface nie działa. Zamiast tego określ interfejs w tablicy przekazywanej do funkcji Create(), jak pokazano w przykładowym kodzie dla pogłosu środowiskowego.

Planowanie przyszłych wersji OpenSL ES

Interfejsy API do obsługi wydajnego dźwięku na Androidzie są oparte na OpenSL ES 1.0.1 grupy Khronos Group. Khronos opublikował poprawioną wersję 1.1 standardu. Zmieniona wersja zawiera nowe funkcje, wyjaśnienia, poprawki błędów typograficznych oraz niektóre niespójności. Większość oczekiwanych niezgodności jest stosunkowo nieznaczna lub występują w obszarach OpenSL ES, które nie są obsługiwane przez Androida.

Aplikacja opracowana w tej wersji powinna działać w przyszłych wersjach platformy Android, o ile przestrzegasz wskazówek podanych w sekcji Plan zgodności binarnej poniżej.

Uwaga: przyszła zgodność źródeł nie jest celem. Oznacza to, że jeśli uaktualnisz NDK do nowszej wersji, może być konieczne zmodyfikowanie kodu źródłowego aplikacji, aby był zgodny z nowym interfejsem API. Spodziewamy się, że większość takich zmian będzie niewielka. Szczegóły znajdziesz poniżej.

Planowanie zgodności binarnej

Aby poprawić zgodność binarną w przyszłości, zalecamy, aby aplikacja przestrzegała tych wytycznych:

  • Używaj tylko udokumentowanego podzbioru funkcji obsługiwanych przez Androida w OpenSL ES 1.0.1.
  • Nie polegaj na konkretnym kodzie wyniku operacji, która się nie udała. Bądź przygotowany na inny kod wyniku.
  • Moduły obsługi wywołań zwrotnych aplikacji zwykle działają w kontekście ograniczonym. Powinny być napisane w taki sposób, aby można było szybko je wykonać, a potem jak najszybciej wrócić. Nie wykonuj operacji złożonych w ramach modułu obsługi wywołania zwrotnego. Na przykład w ramach wywołania zwrotnego ukończenia kolejki bufora możesz umieścić w kolejce kolejny bufor, ale nie utworzysz odtwarzacza dźwięku.
  • Moduły obsługi wywołań zwrotnych powinny być przygotowane do częstszego lub rzadszego wywoływania, aby mogły otrzymywać dodatkowe typy zdarzeń, i powinny ignorować typy zdarzeń, których nie rozpoznają. Wywołania zwrotne skonfigurowane z maską zdarzeń z włączonych typów zdarzeń powinny być przygotowane do wywołania z wieloma ustawionymi jednocześnie wieloma bitami typu zdarzenia. Użyj operatora „&”, aby przetestować każdy bit zdarzenia zamiast instrukcji warunkowej switch.
  • Używaj stanu wstępnego pobierania i wyzwań zwrotnych jako ogólnych wskaźników postępu, ale nie polegaj na konkretnych, zakodowanych na stałe poziomach wypełnienia ani sekwencjach wywołań zwrotnych. Znaczenie poziomu wypełniania stanu wstępnego i zachowanie w przypadku błędów wykrytych podczas wstępnego pobierania mogą ulec zmianie.

Uwaga: więcej informacji znajdziesz w sekcji Oczekujące bufory.

Planowanie pod kątem zgodności źródła

Jak już wspomnieliśmy, w następnej wersji OpenSL ES z grupy Khronos Group spodziewamy się niezgodności kodu źródłowego. Możliwe obszary zmian:

  • Interfejs kolejki bufora prawdopodobnie ulegnie znacznym zmianom, zwłaszcza w obszarach BufferQueue::Enqueue, liście parametrów slBufferQueueCallback i nazwie pola SLBufferQueueState.playIndex. Zalecamy, aby kod aplikacji używał kolejek prostych buforów Androida. W przykładowym kodzie dostarczonym z NDK użyliśmy prostych kolejek buforów Androida do odtwarzania. (do nagrywania i dekodowania do formatu PCM używamy też prostej kolejki buforów Androida, ale jest to spowodowane tym, że standard OpenSL ES 1.0.1 nie obsługuje zapisywania ani dekodowania do odbiornika danych kolejki buforów).
  • Do parametrów wejściowych przekazywanych przez odniesienie i do pól struktury SLchar * używanych jako wartości wejściowe zostanie dodany parametr const. Nie powinno to wymagać żadnych zmian w kodzie.
  • Niektóre obecnie podpisane parametry zostaną zastąpione typami niepodpisanymi. Może być konieczne zmodyfikowanie typu parametru z SLint32 na SLuint32 lub podobny albo dodanie operatora przypisania.
  • Equalizer::GetPresetName kopiuje ciąg znaków do pamięci aplikacji zamiast zwracać wskaźnika do pamięci implementacji. To znacząca zmiana, dlatego zalecamy unikanie wywoływania tej metody lub odizolowanie jej używania.
  • W typach strukturalnych pojawią się dodatkowe pola. W przypadku parametrów wyjściowych te nowe pola można zignorować, ale w przypadku parametrów wejściowych nowe pola trzeba zainicjować. Na szczęście wszystkie te pola znajdują się w obszarach, które nie są obsługiwane przez Androida.
  • Zmienią się identyfikatory GUID interfejsu. Aby uniknąć zależności, odwołuj się do interfejsów za pomocą nazwy symbolicznej zamiast identyfikatora GUID.
  • SLchar zmieni się z unsigned char na char. Ma to wpływ głównie na lokalizator danych URI i format danych MIME.
  • Nazwa SLDataFormat_MIME.mimeType zostanie zmieniona na pMimeType, a nazwa SLDataLocator_URI.URI zostanie zmieniona na pURI. Zalecamy inicjowanie struktur danych SLDataFormat_MIMESLDataLocator_URI za pomocą listy wartości oddzielonych przecinkami w nawiasach klamrowych zamiast nazwy pola, aby odizolować kod od tej zmiany. Ta technika jest używana w przykładowym kodzie.
  • SL_DATAFORMAT_PCM nie zezwala aplikacji na określenie reprezentacji danych jako liczby całkowitej ze znakiem, liczby całkowitej bez znaku lub liczby zmiennoprzecinkowej. Implementacja na Androidzie zakłada, że 8-bitowe dane są liczbą całkowitą bez znaku, a 16-bitowe – liczbą całkowitą ze znakiem. Dodatkowo pole samplesPerSec jest nieprawidłowe, ponieważ rzeczywiste jednostki to milisekundy. Te problemy powinny zostać rozwiązane w kolejnych wersjach OpenSL ES, które wprowadzą nowy format rozszerzonych danych PCM, który pozwoli aplikacji wyraźnie określić reprezentację i poprawi nazwę pola. Będzie to nowy format danych, ale obecny format danych PCM nadal będzie dostępny (choć przestarzały), więc nie powinno to wymagać wprowadzania żadnych zmian w kodzie.