Rozszerzenia na Androida

OpenSL ES na Androida rozszerza referencyjną specyfikację OpenSL ES, aby zapewnić zgodność z Androidem oraz wykorzystać możliwości i elastyczność platformy Androida.

Definicja interfejsu API dla rozszerzeń na Androida znajduje się w pliku OpenSLES_Android.h i zawartych w nim plikach nagłówka. Więcej informacji o tych rozszerzeniach znajdziesz w firmie OpenSLES_Android.h. Znajduje się on w katalogu głównym instalacji, w katalogu sysroot/usr/include/SLES. O ile nie zaznaczono inaczej, wszystkie interfejsy są jednoznaczne.

Te rozszerzenia ograniczają możliwość przenoszenia aplikacji do innych implementacji OpenSL ES, ponieważ są one przeznaczone dla Androida. Aby rozwiązać ten problem, unikaj używania rozszerzeń lub wyklucz je za pomocą metody #ifdef podczas kompilacji.

W tabeli poniżej znajdziesz interfejsy i lokalizatory danych na Androida obsługiwane przez Android OpenSL ES w przypadku każdego typu obiektu. Wartości Tak w komórkach wskazują interfejsy i lokalizatory danych dostępne dla poszczególnych typów obiektów.

Cecha Odtwarzacz dźwięku Rejestratory dźwięku Mechanizm Składanka wyjściowa
Kolejka bufora w Androidzie Tak: źródło (dekodowanie) Nie Nie Nie
Konfiguracja Androida Tak Tak Nie Nie
Efekt Androida Tak Nie Nie Tak
Funkcje efektów na Androidzie Nie Nie Tak Nie
Wysyłanie efektów Androida Tak Nie Nie Nie
Prosta kolejka bufora na Androidzie Tak: źródło (odtwarzanie) lub ujście (dekodowanie) Tak Nie Nie
Lokalizator danych kolejki bufora w Androidzie Tak: źródło (dekodowanie) Nie Nie Nie
Lokalizator danych deskryptora plików Androida Tak: źródło Nie Nie Nie
Prosty lokalizator danych kolejki bufora na Androidzie Tak: źródło (odtwarzanie) lub ujście (dekodowanie) Tak: ujście Nie Nie

Interfejs konfiguracji Androida

Interfejs konfiguracji Androida umożliwia ustawianie parametrów obiektów związanych z platformą. Ten interfejs różni się od innych interfejsów OpenSL ES 1.0.1 tym, że aplikacja może z niego korzystać przed utworzeniem wystąpienia odpowiedniego obiektu. Dzięki temu możesz skonfigurować obiekt przed utworzeniem jego wystąpienia. Plik nagłówka OpenSLES_AndroidConfiguration.h, który znajduje się pod adresem /sysroot/usr/include/SLES, zawiera następujące dostępne klucze konfiguracyjne i wartości:

  • Typ strumienia w odtwarzaczach audio (domyślnie SL_ANDROID_STREAM_MEDIA).
  • Nagrywaj profil dla rejestratorów dźwięku (domyślnie SL_ANDROID_RECORDING_PRESET_GENERIC).

Ten fragment kodu pokazuje, jak ustawić typ strumienia audio Androida w odtwarzaczu:

// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
// in the required interface ID array. Do not realize player yet.
// ...
SLAndroidConfigurationItf playerConfig;
result = (*playerObject)->GetInterface(playerObject,
    SL_IID_ANDROIDCONFIGURATION, &playerConfig);
assert(SL_RESULT_SUCCESS == result);
SLint32 streamType = SL_ANDROID_STREAM_ALARM;
result = (*playerConfig)->SetConfiguration(playerConfig,
    SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
assert(SL_RESULT_SUCCESS == result);
// ...
// Now realize the player here.

Możesz użyć podobnego kodu, aby skonfigurować gotowe ustawienia dla dyktafonu:

// ... obtain the configuration interface as the first four lines above, then:
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
result = (*playerConfig)->SetConfiguration(playerConfig,
    RECORDING_PRESET, &presetValue, sizeof(SLuint32));

Interfejsy z efektami na Androidzie

Interfejsy Androida efektów, wysyłania efektów i możliwości efektów zapewniają ogólny mechanizm wysyłania przez aplikację zapytań i używania efektów dźwiękowych specyficznych dla urządzenia. Producenci urządzeń powinni udokumentować wszelkie dostępne efekty dźwiękowe zależne od urządzenia.

Przenośne aplikacje powinny używać do efektów dźwiękowych interfejsów API OpenSL ES 1.0.1, a nie rozszerzeń z efektami na Androida.

Lokalizator danych deskryptora plików Androida

Lokalizator danych Androida pozwala określić źródło odtwarzacza audio w postaci otwartego deskryptora pliku z uprawnieniami do odczytu. Dane muszą być w formacie MIME.

To rozszerzenie jest szczególnie przydatne w połączeniu z natywnym menedżerem zasobów, ponieważ aplikacja odczytuje zasoby z pliku APK za pomocą deskryptora pliku.

Prosty interfejs i lokalizator danych kolejki buforowej na Androidzie

Zgodnie ze specyfikacją referencyjną OpenSL ES 1.0.1 kolejek bufora można używać tylko w odtwarzaczach dźwięku i są one zgodne z PCM oraz innymi formatami danych. Specyfikacja prostego lokalizatora danych i interfejsu kolejki bufora na Androidzie jest taka sama jak specyfikacja referencyjna, z 2 wyjątkami:

  • Możesz korzystać z prostych kolejek bufora na Androidzie z nagrywarkami i odtwarzaczami audio.
  • W tych kolejkach możesz używać tylko formatu danych PCM.

W przypadku nagrywania aplikacja powinna umieszczać w kolejce puste bufory. Gdy zarejestrowane wywołanie zwrotne wysyła powiadomienie, że system zakończył zapisywanie danych w buforze, aplikacja może z niego odczytać dane.

Tak samo działa odtwarzanie. Aby jednak zapewnić zgodność kodu źródłowego w przyszłości, zalecamy używanie w aplikacjach prostych kolejek buforających na Androidzie zamiast kolejek buforowanych w OpenSL ES 1.0.1.

Działanie kolejki bufora

Implementacja na Androida nie obejmuje wymogu specyfikacji referencyjnej, aby kursor odtwarzania wrócił na początek aktualnie odtwarzanego bufora, gdy odtwarzanie rozpoczyna się w stanie SL_PLAYSTATE_STOPPED. Implementacja może się do niego dostosować lub pozostawić bez zmian lokalizację kursora odtwarzania. W efekcie aplikacja nie może zakładać, że zaistnieje jedno z tych działań. Dlatego po przejściu na SL_PLAYSTATE_STOPPED musisz wyraźnie wywoływać metodę BufferQueue::Clear(). Spowoduje to ustawienie kolejki bufora w znanym stanie.

Analogicznie nie ma specyfikacji, która określałaby, czy aktywator wywołania zwrotnego kolejki bufora musi być przejściem do SL_PLAYSTATE_STOPPED czy wykonaniem instrukcji BufferQueue::Clear(). Dlatego nie zalecamy tworzenia zależności od jednej z nich. Aplikacja powinna obsługiwać obie te zależności.

Dynamiczne interfejsy podczas tworzenia obiektu

Dla wygody implementacja OpenSL ES 1.0.1 na Androida umożliwia aplikacji określanie interfejsów dynamicznych podczas tworzenia instancji obiektu. Jest to alternatywa dla użycia DynamicInterfaceManagement::AddInterface() do dodawania tych interfejsów po utworzeniu instancji.

Raportowanie rozszerzeń

Istnieją 3 metody sprawdzania, czy platforma obsługuje rozszerzenia Androida. Są to:

  • Engine::QueryNumSupportedExtensions()
  • Engine::QuerySupportedExtension()
  • Engine::IsExtensionSupported()

Każda z tych metod zwraca wartość ANDROID_SDK_LEVEL_<API-level>, gdzie API-level to poziom interfejsu API platformy, np. ANDROID_SDK_LEVEL_23. Interfejs API platformy na poziomie 9 lub wyższym oznacza, że platforma obsługuje rozszerzenia.

Dekodowanie dźwięku na PCM

W tej sekcji opisujemy wycofane rozszerzenie OpenSL ES 1.0.1, które jest używane na urządzeniach z Androidem do dekodowania zakodowanego strumienia do PCM bez natychmiastowego odtwarzania. W tabeli poniżej znajdziesz zalecenia dotyczące korzystania z tego rozszerzenia i zamienników.

Poziom interfejsu API Alternatywy
15 i mniej kodek typu open source z odpowiednią licencją;
od 16 do 20 Klasa MediaCodec lub kodek open source z odpowiednią licencją
21 i więcej NDK MediaCodec w plikach nagłówka <media/NdkMedia*.h>, klasie MediaCodec lub kodeku open source z odpowiednią licencją

Uwaga: obecnie nie ma dokumentacji dotyczącej wersji NDK interfejsu MediaCodec API. Możesz jednak skorzystać z przykładowego kodu native-codec.

Standardowy odtwarzacz audio odtwarza treści na urządzeniu audio, określając mikser wyjściowy jako ujście danych. Rozszerzenie na Androida różni się tym, że odtwarzacz dźwięku działa jak dekoder, jeśli aplikacja określa źródło danych w postaci identyfikatora URI lub jako lokalizator danych deskryptora pliku Androida opisany w formacie MIME. W takim przypadku ujściem danych jest to prosty lokalizator danych kolejki bufora na Androidzie, który korzysta z formatu danych PCM.

Ta funkcja jest przeznaczona przede wszystkim dla gier, aby wstępnie ładować zasoby audio podczas przechodzenia na nowy poziom gry, co jest podobne do funkcji dostępnej w klasie SoundPool.

Aplikacja powinna początkowo umieszczać zestaw pustych buforów w prostej kolejce buforowej w Androidzie. Następnie aplikacja wypełnia bufory danymi PCM. Wywołanie zwrotne prostej kolejki bufora na Androidzie uruchamia się po zapełnieniu każdego bufora. Moduł obsługi wywołania zwrotnego przetwarza dane PCM, dodaje do kolejki pusty bufor, a potem powraca. Aplikacja odpowiada za śledzenie zdekodowanych buforów. Lista parametrów wywołania zwrotnego nie zawiera wystarczających informacji, aby wskazać bufor zawierający dane lub bufor, który powinien zostać dodany do kolejki.

Źródło danych pośrednio przekazuje informację o końcu strumienia, wyświetlając na jego końcu zdarzenie SL_PLAYEVENT_HEADATEND. Po zdekodowaniu wszystkich otrzymanych danych aplikacja nie wywołuje już na Androidzie wywołania zwrotnego prostej kolejki buforowania.

Format danych PCM ujścia zazwyczaj odpowiada formatowi zakodowanego źródła danych pod względem częstotliwości próbkowania, liczby kanałów i głębi bitowej. Można jednak zdekodować go z inną częstotliwością próbkowania, liczbą kanałów lub głębią bitową. Informacje o przepisie służącym do wykrywania rzeczywistego formatu PCM znajdziesz w artykule o określaniu formatu dekodowanych danych PCM za pomocą metadanych.

Funkcja dekodowania PCM OpenSL ES na Androida obsługuje wstrzymywanie i początkowe przewijanie. Nie obsługuje regulacji głośności, efektów, zapętlenia ani szybkości odtwarzania.

W zależności od implementacji na platformie dekodowanie może wymagać zasobów, które nie mogą być bezczynne. Dlatego sprawdź, czy masz wystarczającą liczbę pustych buforów PCM. W przeciwnym razie dekoder będzie głodny. Może się tak zdarzyć, jeśli na przykład aplikacja wraca z prostego wywołania zwrotnego kolejki bufora na Androidzie bez dodawania kolejnego pustego bufora w kolejce. Skutek głodu dekodera jest nieokreślony, ale może obejmować usunięcie zdekodowanych danych PCM, wstrzymanie procesu dekodowania lub całkowite zakończenie działania dekodera.

Uwaga: aby dekodować zakodowany strumień do PCM, ale nie odtwarzać go od razu, w przypadku aplikacji na Androida w wersji 4.x (poziomy interfejsu API 16–20) zalecamy użycie klasy MediaCodec. W przypadku nowych aplikacji działających na Androidzie 5.0 (poziom interfejsu API 21) lub nowszym zalecamy użycie odpowiednika pakietu NDK: <NdkMedia*.h>. Te pliki nagłówka znajdują się w katalogu media/ w katalogu głównym instalacji.

Dekodowanie strumieniowych danych ADTS AAC do PCM

Odtwarzacz audio działa jako dekoder strumieniowy, jeśli źródłem danych jest lokalizator danych kolejki bufora w Androidzie, który korzysta z formatu danych MIME, a ujście danych to prosty lokalizator danych kolejki bufora na Androidzie, który korzysta z formatu danych PCM. Skonfiguruj format danych MIME w ten sposób:

  • Kontener: SL_CONTAINERTYPE_RAW
  • Ciąg typu MIME: SL_ANDROID_MIME_AACADTS

Ta funkcja jest przeznaczona przede wszystkim do aplikacji streamingowych, które obsługują dźwięk w formacie AAC, ale przed rozpoczęciem odtwarzania muszą przeprowadzić niestandardowe przetwarzanie dźwięku. Większość aplikacji, które muszą dekodować dźwięk do PCM, powinna korzystać z metody opisanej w opisie Dekodowania dźwięku do PCM, ponieważ jest ona prostsza i obsługuje więcej formatów audio. Opisana tu technika jest bardziej specjalistyczna, której należy używać tylko wtedy, gdy spełnione są oba te warunki:

  • Źródło skompresowanego dźwięku to strumień ramek AAC zawartych w nagłówkach ADTS.
  • Aplikacja zarządza tym strumieniem. Dane nie znajdują się w zasobie sieciowym, którego identyfikator jest identyfikatorem URI, ani w pliku lokalnym, którego identyfikator jest deskryptorem pliku.

Aplikacja powinna początkowo umieszczać zestaw wypełnionych buforów w kolejce bufora Androida. Każdy bufor zawiera co najmniej jedną pełną klatkę ADTS AAC. Wywołanie zwrotne kolejki bufora Androida w Androidzie jest uruchamiane po opróżnieniu każdego bufora. Moduł obsługi wywołania zwrotnego powinien ponownie wypełnić i jeszcze raz umieścić bufor, a następnie wrócić do kolejki. Aplikacja nie musi śledzić zakodowanych buforów. Lista parametrów wywołania zwrotnego zawiera wystarczającą ilość informacji, by wskazać bufor, który powinien zostać dodany do kolejki. Koniec transmisji jest wyraźnie oznaczony przez umieszczenie elementu EOS w kolejce. Po zakończeniu okresu próbnego nie można już kolejek.

Zalecamy udostępnienie pełnych buforów ADTS AAC, aby uniknąć utraty dekodera. Może się tak na przykład zdarzyć, gdy aplikacja wraca z wywołania zwrotnego kolejki bufora w Androidzie bez dodawania kolejnego pełnego bufora. Wynik głodu dekodera jest nieokreślony.

Pod każdym względem, oprócz źródła danych, metoda dekodowania strumieniowania jest taka sama jak opisana w opisie dekodowania dźwięku do PCM.

Pomimo podobieństw nazw kolejka bufora w Androidzie nie jest tym samym co prosta kolejka bufora na Androidzie. Dekoder strumieniowania korzysta z obu rodzajów kolejek bufora: z kolejki bufora w Androidzie dla źródła danych ADTS AAC oraz prostej kolejki bufora w Androidzie dla ujścia danych PCM. Więcej informacji o interfejsie API prostej kolejki buforowej na Androidzie znajdziesz w artykule Interfejs i lokalizator danych kolejki bufora na Androidzie. Więcej informacji o interfejsie Android buffer Queue API znajdziesz w pliku index.html w katalogu docs/Additional_library_docs/openmaxal/ w katalogu głównym instalacji.

Określ format dekodowanych danych PCM na podstawie metadanych

Interfejs SLMetadataExtractionItf jest częścią specyfikacji referencyjnej. Jednak klucze metadanych, które wskazują rzeczywisty format zdekodowanych danych PCM, są specyficzne dla Androida. Te klucze metadanych definiuje plik nagłówka OpenSLES_AndroidMetadata.h. Ten plik nagłówka znajduje się w katalogu głównym instalacji w katalogu /sysroot/usr/include/SLES.

Indeksy kluczy metadanych są dostępne natychmiast po zakończeniu wykonywania metody Object::Realize(). Powiązane wartości są jednak dostępne dopiero po zdekodowaniu pierwszych zakodowanych danych przez aplikację. Sprawdzoną metodą jest wysyłanie zapytań o kluczowe indeksy w wątku głównym po wywołaniu metody Object::Realize i odczytywanie wartości metadanych w formacie PCM w prostym module obsługi wywołania zwrotnego kolejki bufora na Androidzie przy pierwszym wywołaniu tej metody. Przykłady pracy z tym interfejsem znajdziesz w przykładowym kodzie w pakiecie NDK.

Nazwy kluczy metadanych są stabilne, ale indeksy kluczowe nie są udokumentowane i mogą ulec zmianie. Aplikacja nie powinna zakładać, że indeksy są stałe w różnych uruchomieniach wykonywania ani że wiele instancji obiektów współużytkuje indeksy w ramach tego samego uruchomienia.

Dane zmiennoprzecinkowe

Aplikacja w Androidzie 5.0 (poziom interfejsu API 21) lub nowszym może dostarczać dane do odtwarzacza AudioPlayer w jednoprecyzyjnym formacie zmiennoprzecinkowym.

W tym przykładowym kodzie metoda Engine::CreateAudioPlayer() tworzy odtwarzacz audio, który używa danych zmiennoprzecinkowych:

#include <SLES/OpenSLES_Android.h>
...
SLAndroidDataFormat_PCM_EX pcm;
pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
pcm.numChannels = 2;
pcm.sampleRate = SL_SAMPLINGRATE_44_1;
pcm.bitsPerSample = 32;
pcm.containerSize = 32;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
...
SLDataSource audiosrc;
audiosrc.pLocator = ...
audiosrc.pFormat = &pcm;
Dowiedz się więcej o dźwięku zmiennoprzecinkowym na stronie Próbkowanie audio.