OpenSL ES na Androida

Ta strona zawiera szczegółowe informacje o tym, czym implementacja OpenSL EST za pomocą NDK różni się od specyfikacji referencyjnej OpenSL ES 1.0.1. Jeśli używasz przykładowego kodu ze specyfikacji, być może trzeba będzie go zmodyfikować pod kątem działania na Androidzie.

O ile nie wskazano inaczej, wszystkie funkcje są dostępne na urządzeniach z Androidem 2.3 (poziom interfejsu API 9) i nowszym. Niektóre funkcje są dostępne tylko na Androidzie 4.0 (poziom interfejsu API 14). Są to wymienione poniżej funkcje.

Uwaga: dokument CDD (Android Compatibility Definition Document) zawiera listę wymagań dotyczących sprzętu i oprogramowania zgodnego urządzenia z Androidem. Więcej informacji na temat ogólnego programu zgodności znajdziesz w artykule na temat zgodności z Androidem, a dokumentu dotyczącego dokumentu CDD znajdziesz na stronie CDD.

OpenSL ES udostępnia interfejs w języku C, z którego można też korzystać w C++. Udostępnia funkcje podobne do części audio tych interfejsów API w języku Java na Androida:

Tak jak w przypadku całego pakietu Android Native Development Kit (NDK), głównym celem OpenSL ES na Androida jest ułatwienie implementacji bibliotek udostępnionych, które można wywoływać za pomocą natywnego interfejsu Java (JNI ). Pakiet NDK nie służy do tworzenia aplikacji w języku C/C++. OpenSL ES to jednak interfejs API o pełnym zakresie funkcji i spodziewamy się, że będziesz w stanie zaspokoić większość swoich potrzeb związanych z dźwiękiem, używając tylko tego interfejsu API, bez konieczności ponownego wywoływania kodu w środowisku wykonawczym Androida.

Uwaga: chociaż oparty na OpenSL ES, natywny interfejs API Androida do odtwarzania dźwięku (wydajna ścieżka dźwiękowa) nie jest zgodną implementacją żadnego profilu OpenSL ES 1.0.1 (gry, muzyki lub telefonu). Dzieje się tak, ponieważ Android nie oferuje wszystkich funkcji wymaganych przez którykolwiek z profili. Znane przypadki, w których Android działa inaczej niż specyfikacja, opisujemy na stronie Rozszerzenia na Androida.

Funkcje dziedziczone ze specyfikacji referencyjnej

Implementacja OpenSL ES na Androida z pakietu Android NDK dziedziczy znaczną część zestawu funkcji ze specyfikacji referencyjnej, z pewnymi ograniczeniami.

Globalne punkty wejścia

OpenSL ES na Androida obsługuje wszystkie globalne punkty wejścia wymienione w specyfikacji Androida. Są to między innymi:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

Obiekty i interfejsy

W tabeli poniżej znajdziesz obiekty i interfejsy obsługiwane przez implementację pakietu Android NDK na OpenSL ES. Jeśli w komórce znajduje się wartość Tak, oznacza to, że funkcja jest dostępna w tej implementacji.

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

Cecha Odtwarzacz dźwięku Rejestratory dźwięku Mechanizm Składanka wyjściowa
Wzmocnienie basów Tak Nie Nie Tak
Kolejka bufora Tak Nie Nie Nie
Lokalizator danych kolejki bufora Tak: źródło Nie Nie Nie
Dynamiczne zarządzanie interfejsem Tak Tak Tak Tak
Wysłano efekt Tak Nie Nie Nie
Mechanizm Nie Nie Tak Nie
Pogłos środowiskowy Nie Nie Nie Tak
Korektor Tak Nie Nie Tak
Lokalizator danych urządzenia I/O Nie Tak: źródło Nie Nie
Wyodrębnianie metadanych Tak: dekoduj do PCM Nie Nie Nie
Wycisz solo Tak Nie Nie Nie
Obiekt Tak Tak Tak Tak
Lokalizator miksu wyjściowego Tak: ujście Nie Nie Nie
Odtwórz Tak Nie Nie Nie
Prędkość odtwarzania Tak Nie Nie Nie
Stan wstępnego pobierania 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 opisano ograniczenia niektórych z tych funkcji.

Ograniczenia

Funkcje opisane w tabeli 1 podlegają pewnym ograniczeniom. Te ograniczenia oznaczają różnice w stosunku do specyfikacji referencyjnej. W pozostałej części tej sekcji dowiesz się więcej na temat tych różnic.

Dynamiczne zarządzanie interfejsem

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

Kombinacje efektów: pogłos otoczenia i gotowy pogłos

Nie możesz używać pogłosu środowiska i gotowego pogłosu na tym samym wyjściu.

Platforma może zignorować żądania efektu, jeśli oszacuje, że obciążenie procesora byłoby zbyt duże.

Wysłano efekt

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

Pogłos środowiskowy

Pogłos środowiskowy nie obsługuje pól reflectionsDelay, reflectionsLevel ani reverbDelay elementu SLEnvironmentalReverbSettings struct.

Format danych MIME

Formatu danych MIME można używać tylko z lokalizatorem danych URI i tylko w przypadku odtwarzacza audio. Nie można użyć tego formatu danych w przypadku dyktafonu.

Implementacja OpenSL ES na Androida wymaga zainicjowania mimeType jako NULL lub prawidłowego ciągu znaków UTF-8. Musisz też zainicjować containerType prawidłową wartość. W przypadku braku innych kwestii, takich jak możliwość przenoszenia do innych implementacji lub formatów treści, których aplikacja nie może zidentyfikować za pomocą nagłówka, zalecamy ustawienie mimeType na NULL i containerType na SL_CONTAINERTYPE_UNSPECIFIED.

OpenSL ES na Androida obsługuje te formaty audio, o ile platforma Android również je obsługuje:

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

Uwaga: listę formatów dźwięku obsługiwanych przez Androida znajdziesz w artykule Obsługiwane formaty multimediów.

W ramach tej implementacji OpenSL ES obowiązują następujące ograniczenia:

  • Formaty AAC muszą znajdować się w kontenerze MP4 lub ADTS.
  • OpenSL ES na Androida nie obsługuje MIDI.
  • WMA nie jest częścią systemu AOSP i nie zweryfikowaliśmy jego zgodności z OpenSL ES na Androida.
  • Implementacja OpenSL ES w Androidzie NDK nie pozwala na bezpośrednie odtwarzanie DRM ani treści zaszyfrowanych. Aby odtworzyć chronione treści audio, musisz odszyfrować je w aplikacji, zanim zacznie się odtwarzanie. Aplikacja musi egzekwować 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 bufora. Obsługiwane konfiguracje odtwarzania PCM mają te cechy:

  • 8-bitowy bez podpisu lub 16-bitowy.
  • Mono lub stereo.
  • Odpowiednia kolejność bajtów w formacie Little-endian.
  • Przykładowe częstotliwości:
    • 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 obsługiwane przez OpenSL ES na potrzeby nagrywania zależą od urządzenia. Zazwyczaj dostępne jest podpisywanie 16 000 Hz mono/16-bitowe niezależnie od urządzenia.

Wartość w polu samplesPerSec jest wyrażona w miliHz, mimo że nazwa wprowadza w błąd. Aby uniknąć przypadkowego użycia niewłaściwej wartości, zalecamy zainicjowanie tego pola przy użyciu jednej ze stałych symboli zdefiniowanych do tego celu, np. SL_SAMPLINGRATE_44_1.

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

Prędkość odtwarzania

Współczynnik odtwarzania OpenSL ES wskazuje szybkość, z jaką obiekt przedstawia dane, wyrażoną w tysięcznych normalnej szybkości, czyli na tysiąc kilometrów. Na przykład szybkość odtwarzania 1000 na kilometr to 1000/1000, czyli normalna prędkość. Zakres szybkości to zamknięty przedział czasu, w którym określono 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 platformy i implementacji. Aplikacja może określać te możliwości w czasie działania, 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 przesyłania dla źródła danych w formacie PCM i zakres szybkości jednostkowej od 1000 do 1000 na tysiąc dla innych formatów. Oznacza to, że zakres szybkości jednostkowej jest efektywnie jedną wartością.

Nagraj

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

Szukaj

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

Lokalizator danych kolejki bufora

Odtwarzacz audio lub rejestrator z lokalizatorem danych dla kolejki bufora obsługuje tylko format danych PCM.

Lokalizator danych urządzenia I/O

OpenSL ES na Androida obsługuje używanie lokalizatora danych urządzenia wejścia-wyjścia tylko wtedy, gdy jako źródło danych dla aplikacji Engine::CreateAudioRecorder() określisz lokalizator. Zainicjuj lokalizator danych urządzenia, używając wartości podanych 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 przypadku odtwarzacza audio. W rejestratorze dźwięku nie można użyć lokalizatora danych URI. W identyfikatorze URI można używać tylko schematów http: i file:. Inne schematy, takie jak https:, ftp: lub content:, są niedozwolone.

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

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

Oprogramowanie OpenSL ES na Androida obsługuje aplikacje wielowątkowe i jest bezpieczne w wątkach. Obsługuje jeden silnik na aplikację i do 32 obiektów na silnik. Dostępna pamięć urządzenia i procesor mogą jeszcze bardziej ograniczać liczbę używanych obiektów.

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

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

Standardów OpenMAX AL i OpenSL ES można używać jednocześnie w tej samej aplikacji. W tym przypadku istnieje wewnętrznie jeden udostępniony obiekt wyszukiwarki, a limit 32 obiektów jest dzielony między OpenMAX AL i OpenSL ES. Aplikacja powinna utworzyć oba silniki, używać obu, a na koniec zniszczyć oba. Implementacja przechowuje liczbę odwołań we współdzielonym mechanizmie, aby została poprawnie zniszczona podczas drugiej operacji zniszczenia.

Uwagi dotyczące programowania

Uwagi dotyczące programowania OpenSL ES zawierają dodatkowe informacje, które pomogą Ci prawidłowo wdrożyć OpenSL ES.

Uwaga: dla ułatwienia załączamy kopię specyfikacji OpenSL ES 1.0.1 z pakietem NDK w usłudze docs/opensles/OpenSL_ES_Specification_1.0.1.pdf.

Problemy z platformą

W tej sekcji opisano znane problemy we wstępnej wersji platformy, która obsługuje te interfejsy API.

Dynamiczne zarządzanie interfejsem

DynamicInterfaceManagement::AddInterface nie działa. Zamiast tego określ interfejs w tablicy, która jest przekazywana do Create(), jak w przykładowym kodzie pogłosu środowiskowego.

Zaplanuj przyszłe wersje OpenSL ES

Interfejsy API audio o wysokiej wydajności na Androida opierają się na Khronos Group OpenSL ES 1.0.1. Firma Khronos opublikowała poprawioną wersję 1.1 tego standardu. Zawiera ona nowe funkcje, objaśnienia, poprawki literówek i pewne niezgodności. Większość spodziewanych niezgodności jest stosunkowo niewielka lub dotyczy obszarów OpenSL ES, które nie są obsługiwane przez Androida.

Aplikacja opracowana w tej wersji powinna działać w przyszłych wersjach platformy Androida, o ile będziesz przestrzegać wytycznych podanych w sekcji Planowanie zgodności plików binarnych poniżej.

Uwaga: zgodność źródeł w przyszłości nie jest celem. Oznacza to, że po uaktualnieniu do nowszej wersji NDK może być konieczne zmodyfikowanie kodu źródłowego aplikacji w celu dostosowania do nowego interfejsu API. Spodziewamy się, że większość z tych zmian będzie nieznaczna. Szczegóły znajdziesz poniżej.

Zapewnij zgodność z plikami binarnymi

Aby w przyszłości zapewnić zgodność aplikacji z plikami binarnymi, zalecamy stosowanie się do tych wytycznych:

  • Korzystaj tylko z udokumentowanego zestawu funkcji obsługiwanych przez Androida z OpenSL ES 1.0.1.
  • Nie zależą od konkretnego kodu wyniku w przypadku nieudanej operacji. Przygotuj się na użycie innego kodu wyniku.
  • Moduły obsługi wywołań zwrotnych aplikacji zwykle działają w kontekście ograniczonym. Trzeba ich napisać tak, aby szybko wykonywały swoją pracę i jak najszybciej powracały do niej. Nie uruchamiaj złożonych operacji w obrębie modułu obsługi wywołania zwrotnego. Na przykład w ramach wywołania zwrotnego ukończenia kolejki bufora możesz dodać do kolejki kolejny bufor, ale nie możesz utworzyć odtwarzacza dźwięku.
  • Moduły obsługi wywołań zwrotnych powinny być przygotowane do częstszego lub rzadszego wywoływania, aby otrzymywać dodatkowe typy zdarzeń i ignorować typy zdarzeń, których nie rozpoznają. Wywołania zwrotne skonfigurowane z maską zdarzenia utworzoną z włączonych typów zdarzeń powinny być przygotowane do wywoływania z wieloma ustawionymi bitami typów zdarzeń jednocześnie. Użyj znaku „&”, aby przetestować każdy bit zdarzenia, a nie przypadek przełącznika.
  • Używaj stanu pobierania z wyprzedzeniem i wywołań zwrotnych jako ogólnych wskaźników postępu, ale nie zależą od konkretnych, zakodowanych na stałe poziomów wypełnienia ani sekwencji wywołań zwrotnych. Znaczenie poziomu wypełnienia stanu pobierania z wyprzedzeniem i działanie błędów wykrytych podczas pobierania z wyprzedzeniem mogą ulec zmianie.

Uwaga: więcej informacji znajdziesz w sekcji Działanie kolejki bufora poniżej.

Zaplanuj zgodność źródła

Jak już wspomnieliśmy, w następnej wersji OpenSL ES firmy Khronos Group należy spodziewać się niezgodności kodu źródłowego. Prawdopodobne obszary zmian:

  • Prawdopodobnie nastąpią istotne zmiany w interfejsie kolejki bufora, zwłaszcza w obszarach BufferQueue::Enqueue, listy parametrów slBufferQueueCallback i nazwie pola SLBufferQueueState.playIndex. Zalecamy, aby w kodzie aplikacji używać prostych kolejek bufora na Androidzie. Z tego powodu w przykładowym kodzie dołączonym do pakietu NDK użyliśmy prostych kolejek bufora na Androidzie do odtwarzania. (Używamy też prostej kolejki bufora na Androidzie do nagrywania i dekodowania plików w PCM, ale jest to spowodowane tym, że standardowy OpenSL ES 1.0.1 nie obsługuje nagrywania ani dekodowania do ujścia danych kolejki bufora).
  • Do parametrów wejściowych przekazywanych przez odwołanie zostanie dodane pole const oraz do pól struktury SLchar * używanych jako wartości wejściowe. Nie powinno to wymagać żadnych zmian w kodzie.
  • Niektóre parametry, które są obecnie podpisane, zostaną zastąpione przez typy niepodpisane. Może być konieczna zmiana typu parametru z SLint32 na SLuint32 lub podobna. Możesz też dodać rzutowanie.
  • Equalizer::GetPresetName kopiuje ciąg znaków do pamięci aplikacji, zamiast zwracać wskaźnik do pamięci implementacji. Będzie to znacząca zmiana, dlatego zalecamy unikanie wywoływania tej metody lub odizolowanie jej stosowania.
  • W typach struct będą dodatkowe pola. W przypadku parametrów wyjściowych możesz je ignorować, ale w przypadku parametrów wejściowych należy zainicjować nowe pola. Na szczęście wszystkie te pola znajdują się na obszarach, które nie są obsługiwane przez Androida.
  • Identyfikatory GUID interfejsu ulegną zmianie. Aby uniknąć zależności, używaj nazwy symbolicznej, a nie identyfikatora GUID, by używać interfejsów.
  • SLchar zmieni się z unsigned char na char. Dotyczy to głównie lokalizatora danych URI i formatu danych MIME.
  • Nazwa usługi SLDataFormat_MIME.mimeType zostanie zmieniona na pMimeType, a nazwa SLDataLocator_URI.URI – na pURI. Aby odizolować kod od tej zmiany, zalecamy zainicjowanie struktur danych SLDataFormat_MIME i SLDataLocator_URI za pomocą listy wartości rozdzielonych przecinkami, a nie nazwy pola. Ta technika jest używana w przykładowym kodzie.
  • Funkcja SL_DATAFORMAT_PCM nie zezwala aplikacji na określanie reprezentacji danych w postaci liczby całkowitej, nieoznaczonej liczby całkowitej lub liczby zmiennoprzecinkowej. Implementacja na Androida zakłada, że 8-bitowe dane to nieoznaczona liczba całkowita, a 16-bitowa liczba całkowita ze znakiem. Ponadto pole samplesPerSec jest błędne, ponieważ rzeczywiste jednostki podane są w miliHz. Spodziewamy się, że te problemy zostaną rozwiązane w kolejnej wersji OpenSL ES, która będzie zawierać nowy rozszerzony format danych PCM, który umożliwi aplikacji wyraźne określenie reprezentacji i poprawienie nazwy pola. Będzie to nowy format danych, a obecny format danych PCM będzie nadal dostępny (chociaż jest wycofany), więc nie musisz wprowadzać żadnych natychmiastowych zmian w kodzie.