OSTRZEŻENIE: OpenSL ES został wycofany. Deweloperzy powinni używać oprogramowania open source Biblioteka obojów dostępna na GitHubie. Oboe to kod w języku C++ zapewniający interfejs API podobny do tego AAudio Obój wywołuje AAudio, gdy AAudio jest i gdy jest niedostępna, przełącza się na OpenSL ES.
Uwagi w tej sekcji uzupełniają specyfikację OpenSL ES 1.0.1.
Inicjowanie obiektów i interfejsu
Dwa aspekty modelu programowania OpenSL ES, których nowi deweloperzy mogą nie znać, to: różnice między obiektami i interfejsami oraz sekwencja inicjowania.
Obiekt OpenSL ES jest podobny do koncepcji obiektu w
języki programowania, np. Java
i C++, ale obiekt OpenSL ES jest widoczny tylko przez powiązane z nim interfejsy.
Obejmuje to m.in.
początkowym interfejsem dla wszystkich obiektów o nazwie SLObjectItf
.
Nie ma nicka dla obiektu
samego, tylko uchwyt do interfejsu SLObjectItf
obiektu.
Najpierw tworzony jest obiekt OpenSL ES, który zwraca SLObjectItf
, a potem
zrealizowane. Przypomina to typowy wzorzec programistyczny dotyczący pierwszego tworzenia
(który nigdy nie powinien ulegać awarii poza brakiem pamięci lub nieprawidłowymi parametrami), a następnie
rozpoczynania inicjowania (co może się nie powieść z powodu braku zasobów). Etap realizacji zapewnia implementacji logiczne miejsce na przydzielenie dodatkowych zasobów w razie potrzeby.
W ramach interfejsu API do tworzenia obiektu aplikacja określa tablicę żądanych interfejsów który planuje później pozyskać. Zwróć uwagę, że ta tablica nie automatycznie pozyskać interfejsy; wskazuje jedynie na przyszły zamiar ich pozyskania. Interfejsy są oznaczane jako implicit lub jawne. Jeśli interfejs będzie używany później, musi być wymieniony w tablicy. Implikatywny interfejs nie musi być wymieniony w tablicy tworzenia obiektu , ale nic nie stoi na przeszkodzie, aby go tam umieścić. OpenSL ES ma jeszcze jeden rodzaj interfejsu o nazwie dynamic, którego nie trzeba określać w obiekcie. utwórz tablicę i można ją dodać później po utworzeniu obiektu. Wdrożenie Androida zapewnia wygodniejszą funkcję, uniknąć takiej złożoności, co opisano w Interfejsy dynamiczne podczas tworzenia obiektów.
Po utworzeniu i uruchomieniu obiektu aplikacja powinna pobrać interfejsy dla każdego
potrzebne funkcje, używając GetInterface
na pierwszym planie SLObjectItf
.
Obiekt jest też dostępny do użycia przez interfejsy, ale pamiętaj, Niektóre obiekty wymagają przeprowadzić dodatkową konfigurację. Odtwarzacz audio ze źródłem danych URI wymaga więcej przygotowania aby wykrywać błędy połączenia. Więcej informacji znajdziesz w sekcji Wstępne pobieranie przez odtwarzacz audio.
Gdy aplikacja zakończy działanie obiektu, należy go jawnie zniszczyć. zobacz Zniszcz sekcję poniżej.
Pobieranie z wyprzedzeniem odtwarzacza audio
W przypadku odtwarzacza audio ze źródłem danych URI Object::Realize
przydziela
zasobów, ale nie
połączyć się ze źródłem danych (przygotować) lub rozpocząć pobieranie danych z wyprzedzeniem. Pojawiają się one, gdy
stan odtwarzacza jest ustawiony na SL_PLAYSTATE_PAUSED
lub SL_PLAYSTATE_PLAYING
.
Niektóre informacje mogą być nieznane jeszcze stosunkowo późno w tej sekwencji. W
najpierw Player::GetDuration
zwraca SL_TIME_UNKNOWN
i
MuteSolo::GetChannelCount
zwraca błąd z liczbą kanałów na 0 lub parametrem
wynik błędu SL_RESULT_PRECONDITIONS_VIOLATED
. Te interfejsy API zwracają odpowiednie wartości
gdy są już znane.
Inne właściwości, które są początkowo nieznane, to m.in. częstotliwość próbkowania i rzeczywisty typ treści multimedialnych na podstawie nagłówka treści (w przeciwieństwie do określony przez aplikację typ MIME oraz typ kontenera). Są one również określane później przygotowuj/pobierania z wyprzedzeniem, ale nie ma interfejsów API, je pobrać.
Interfejs stanu pobierania z wyprzedzeniem przydaje się do wykrywania, czy wszystkie informacje lub aplikacja może okresowo przeprowadzać sondowanie. Pamiętaj, że niektóre informacje, takie jak czas trwania strumieniowego przesyłania danych, MP3, mogą nigdy nie być znane.
Interfejs stanu pobierania z wyprzedzeniem przydaje się też do wykrywania błędów. Rejestrowanie wywołania zwrotnego
i włącz
co najmniej SL_PREFETCHEVENT_FILLLEVELCHANGE
i SL_PREFETCHEVENT_STATUSCHANGE
zdarzeń. Jeśli oba te zdarzenia są realizowane jednocześnie,
PrefetchStatus::GetFillLevel
zgłasza poziom zerowy oraz
PrefetchStatus::GetPrefetchStatus
zgłasza SL_PREFETCHSTATUS_UNDERFLOW
,
to
wskazuje nieodwracalny błąd w źródle danych. Obejmuje to brak możliwości połączenia ze źródłem danych, ponieważ lokalna nazwa pliku nie istnieje lub identyfikator URI sieci jest nieprawidłowy.
W następnej wersji OpenSL ES planujemy dodać wyraźniejsze obsługi błędów źródła danych. Jednak dla zapewnienia zgodności z danymi binarnymi w przyszłości zamierzamy kontynuować aby obsługiwać bieżącą to metoda zgłaszania nieodwracalnego błędu.
Podsumowując, zalecana sekwencja kodu to:
Engine::CreateAudioPlayer
Object:Realize
Object::GetInterface
przezSL_IID_PREFETCHSTATUS
PrefetchStatus::SetCallbackEventsMask
PrefetchStatus::SetFillUpdatePeriod
PrefetchStatus::RegisterCallback
Object::GetInterface
przezSL_IID_PLAY
Play::SetPlayState
doSL_PLAYSTATE_PAUSED
lubSL_PLAYSTATE_PLAYING
Uwaga: Tutaj odbywa się przygotowanie i pobieranie z wyprzedzeniem. w tym czasie jest nawiązywane połączenie zwrotne z numerem okresowe aktualizacje stanu.
Zniszcz
Pamiętaj, aby zniszczyć wszystkie obiekty przy zamykaniu aplikacji. Obiekty powinny zostać zniszczone w odwrotnej kolejności, ponieważ nie jest bezpieczne zniszczenie obiektu, który obiektów. Na przykład zniszcz w tej kolejności: odtwarzacze i dyktafony, miks wyjściowy, a następnie a na koniec silnik.
OpenSL ES nie obsługuje automatycznego czyszczenia pamięci lub
odwołanie
i zliczania interfejsów. Po wywołaniu funkcji Object::Destroy
wszystkie istniejące interfejsy wyprowadzone z powiązanego obiektu stają się niezdefiniowane.
Implementacja Android OpenSL ES nie wykrywa nieprawidłowego użycia takich interfejsów. Dalsze korzystanie z takich interfejsów po zniszczeniu obiektu może spowodować, że aplikacja ulegają awarii lub zachowują się w nieprzewidywalny sposób.
Zalecamy jawne ustawienie zarówno głównego interfejsu obiektów, jak i wszystkich powiązanych
korzysta z interfejsu NULL
w ramach sekwencji niszczenia obiektów, co zapobiega przypadkowemu
niewłaściwe użycie nieaktualnego uchwytu interfejsu.
Panoramowanie stereo
Gdy Volume::EnableStereoPosition
jest używany do włączania panoram stereo ze źródła mono,
zmniejszyła się o 3 dB,
moc dźwięku
poziomu usługi. Jest to konieczne, aby całkowity poziom mocy dźwięku mógł pozostać na stałym poziomie
źródło to
przejście z jednego kanału na drugi. W związku z tym włączaj pozycjonowanie stereo tylko wtedy, gdy chcesz,
. Więcej informacji znajdziesz w artykule w Wikipedii na temat:
przesuwania dźwięku.
Wywołania zwrotne i wątki
Moduły obsługi wywołań zwrotnych są zwykle wywoływane synchronicznie, gdy implementacja wykrywa . Ten punkt jest asynchroniczny w odniesieniu do aplikacji, należy więc użyć funkcji nieblokującej synchronizacji do kontrolowania dostępu do dowolnych zmiennych udostępnianych między aplikacją a do obsługi wywołania zwrotnego. W przykładowym kodzie, np. w kolejkach buforów, pominęliśmy to lub używasz synchronizacji blokowania w celu uproszczenia. Pamiętaj jednak, że odpowiednie jest kluczowe dla każdego kodu produkcyjnego.
Moduły obsługi wywołań zwrotnych są wywoływane z wątków wewnętrznych niezwiązanych z aplikacją, które nie są dołączone do wywołania środowiska wykonawczego Androida, więc nie można w nich korzystać z JNI. Ponieważ wątki wewnętrzne o znaczeniu krytycznym dla integralności implementacji OpenSL ES, moduł obsługi wywołań zwrotnych również nie powinien blokować lub wykonaj nadmiernej pracy.
Jeśli moduł obsługi wywołań zwrotnych musi używać JNI lub wykonywać pracę nieproporcjonalną do wywołanie zwrotne, moduł obsługi powinien zamiast tego opublikować zdarzenie w innym wątku do przetworzenia. Przykłady dopuszczalny zbiór zadań wywołania zwrotnego obejmuje renderowanie i umieszczenie następnego bufora wyjściowego w kolejce (w przypadku odtwarzacza audio) przetwarzanie wypełnionego bufora wejściowego i dodawanie kolejnych do kolejki pusty bufor (w przypadku AudioRecorder) lub prostych interfejsów API, takich jak większość rodziny Get. Zobacz W sekcji Wydajność poniżej dotyczącą zadania.
Zwróć uwagę, że odwrotność jest bezpieczna: wątek aplikacji na Androida dodany do JNI ma prawo do bezpośrednio wywoływać interfejsy API OpenSL ES, łącznie z tymi, które blokują. Blokowanie połączeń nie polecane w wątku głównym, ponieważ mogą spowodować Aplikacja nie odpowiada (ANR).
Decyzja dotycząca wątku, który wywołuje moduł obsługi wywołania zwrotnego, należy głównie do implementacji. Dzieje się tak dlatego, że umożliwiamy przyszłe optymalizacje, zwłaszcza na urządzeniach wielordzeniowych.
Wątek, w którym działa moduł obsługi wywołania zwrotnego, nie ma takiej samej tożsamości w całym
różnych połączeń. Dlatego nie polegaj na danych typu pthread_t
zwróconych przez
pthread_self()
lub pid_t
zwrócone przez gettid()
na
spójna w przypadku wszystkich wywołań. Z tego samego powodu nie używaj interfejsów API pamięci lokalnej TLS, takich jak
pthread_setspecific()
i pthread_getspecific()
z oddzwonienia.
Implementacja gwarantuje, że równoczesne wywołania zwrotne tego samego rodzaju ten sam obiekt, czy nie występują. W przypadku tego samego obiektu możliwe są jednak równoczesne wywołania zwrotne różnego rodzaju w różnych wątkach.
Wydajność
OpenSL ES to natywny interfejs API C, dlatego wątki aplikacji inne niż środowisko wykonawcze, które wywołują OpenSL ES, nie mają związane ze środowiskiem wykonawczym, takie jak wstrzymania czyszczenia pamięci. Z jednym wyjątkiem opisanym poniżej Poza tym korzystanie z OpenSL ES nie wiąże się z żadną dodatkową poprawką wydajności. W szczególności Użycie OpenSL ES nie gwarantuje ulepszeń, takich jak mniejsze opóźnienie dźwięku czy większe planowanie harmonogramu w stosunku do tego, co zapewnia platforma. Z drugiej strony, Platforma Androida i różne implementacje urządzeń stale się rozwijają – aplikacja OpenSL ES mogą skorzystać na wszelkich przyszłych ulepszeniach wydajności systemu.
Jedną z takich ewolucji jest obsługa
opóźnienia na wyjściu audio.
Podstawy zmniejszenia opóźnienia wyjścia zostały po raz pierwszy uwzględnione w Androidzie 4.1 (interfejs API na poziomie 16), a potem w Androidzie 4.2 (interfejs API na poziomie 17) nastąpiła dalsza poprawa. Te ulepszenia są dostępne w OpenSL ES na urządzeniach, które obsługują funkcję android.hardware.audio.low_latency
.
Jeśli urządzenie nie zgłasza praw do tej funkcji, ale obsługuje Androida 2.3 (poziom API 9)
lub nowszy, nadal możesz używać interfejsów OpenSL ES API, ale opóźnienia mogą być większe.
Niższa
ścieżka czasu oczekiwania wyjściowego jest używana tylko wtedy, gdy aplikacja żąda rozmiaru bufora i częstotliwości próbkowania
które są
zgodne z natywną konfiguracją urządzenia wyjściowego. Te parametry to
na konkretnych urządzeniach,
należy uzyskać w sposób opisany poniżej.
Począwszy od Androida 4.2 (poziom interfejsu API 17) aplikacja może wysyłać zapytania dotyczące natywna lub optymalna częstotliwość próbkowania wyjściowego i rozmiar bufora dla głównych danych wyjściowych urządzenia . W połączeniu z wspomnianym wcześniej testem funkcji aplikacja może się teraz konfigurować pod kątem krótszych czasów oczekiwania na urządzeniach, które go obsługują.
W przypadku Androida 4.2 (poziom interfejsu API 17) i starszych liczba buforów wynosząca co najmniej 2 jest jest wymagane w celu skrócenia czasu oczekiwania. Począwszy od Androida 4.3 (poziom interfejsu API 18), bufor jest wystarczająca do zmniejszenia opóźnienia.
Wszystkie interfejsy OpenSL ES do obsługi efektów wyjściowych nie zawierają ścieżki o mniejszym czasie oczekiwania.
Zalecana sekwencja wygląda tak:
- Sprawdź, czy poziom interfejsu API wynosi co najmniej 9, aby potwierdzić użycie OpenSL ES.
- Sprawdź funkcję
android.hardware.audio.low_latency
, używając takiego kodu:Kotlin
import android.content.pm.PackageManager ... val pm: PackageManager = context.packageManager val claimsFeature: Boolean = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)
Java
import android.content.pm.PackageManager; ... PackageManager pm = getContext().getPackageManager(); boolean claimsFeature = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
- Sprawdź, czy interfejs API jest na poziomie 17 lub wyższym, aby potwierdzić użycie interfejsu
android.media.AudioManager.getProperty()
. - Pobierz natywną lub optymalną częstotliwość próbkowania wyjściowego i rozmiar bufora dla urządzenia
podstawowe dane wyjściowe
użyj takiego kodu:
Kotlin
import android.media.AudioManager ... val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager val sampleRate: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE) val framesPerBuffer: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
Java
import android.media.AudioManager; ... AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
sampleRate
iframesPerBuffer
to ciągi tekstowe. Najpierw sprawdź null, a następnie przekonwertuj na liczbę całkowitą za pomocą funkcjiInteger.parseInt()
. - Teraz w OpenSL ES utwórz odtwarzacz audio z lokalizatorem danych kolejki bufora PCM.
Uwaga: możesz użyć aplikacji testowej Rozmiar bufora audio, aby określić natywny rozmiar bufora i częstotliwość próbkowania dla aplikacji audio OpenSL ES na urządzeniu audio. Możesz też odwiedzić GitHuba, aby zobaczyć rozmiar bufora audio.
Liczba odtwarzaczy dźwięku o krótszym opóźnieniu jest ograniczona. Jeśli aplikacja wymaga więcej niż kilka źródeł dźwięku, rozważ miksowanie dźwięku na poziomie aplikacji. Pamiętaj, aby niszczyć odtwarzacze audio, gdy Twoja aktywność jest wstrzymana, ponieważ są one zasobem globalnym udostępnianym innym aplikacjom.
Aby uniknąć słyszalnych zakłóceń, wywołanie obsługi kolejki buforowej musi być wykonywane w małym i przewidywalnym oknie czasowym. Zwykle oznacza to brak nieograniczonego blokowania na semaforach, warunkach lub operacjach wejścia-wyjścia. Zamiast tego rozważ wypróbowanie blokad, blokad i oczekiwania z limitami czasu. za pomocą algorytmów nieblokujących.
Obliczenia wymagane do renderowania następnego bufora (w przypadku AudioPlayer) lub wykorzystania poprzedniego bufora (w przypadku AudioRecord) powinny zajmować mniej więcej tyle samo czasu w przypadku każdego wywołania zwrotnego. Unikaj algorytmów, które działają w niedeterministycznym czasie lub działają w i wykonywać obliczenia. Obliczenia wywołania zwrotnego są niestabilne, jeśli czas procesora poświęcony na dowolne wywołanie zwrotne jest znacznie większy niż średnia. Podsumowując, optymalny czas pracy procesora to dla modułu obsługi wariancję bliską zera, a moduł obsługi nie będzie blokował w przypadku nieograniczonych czasów.
Dźwięk z krótszym opóźnieniem jest możliwy tylko w przypadku tych wyjść:
- Głośniki urządzenia.
- Słuchawki przewodowe.
- Przewodowe zestawy słuchawkowe.
- Wytnij.
- Cyfrowe USB audio.
Na niektórych urządzeniach opóźnienie na głośniku jest dłuższe niż w innych ścieżkach ze względu na przetwarzanie sygnałów cyfrowych korekcji i ochronie głośnika.
Od Androida 5.0 (poziom interfejsu API 21) mniejsze opóźnienia
wejście audio jest obsługiwane przez wybrane urządzenia. Aby korzystać z tej funkcji, potwierdź
że dostępne jest wyjście z mniejszym czasem oczekiwania, jak opisano powyżej. Możliwość zmniejszenia opóźnienia
jest warunkiem wstępnym funkcji wprowadzania
mniejszego opóźnienia. Następnie utwórz Dyktafon z tym samym
częstotliwości próbkowania i rozmiaru bufora, jakie będą używane jako dane wyjściowe. Interfejsy OpenSL ES do efektów wejściowych wykluczają ścieżkę o mniejszym opóźnieniu. Gotowe ustawienia nagrywania
Aby zmniejszyć opóźnienie, należy użyć interfejsu SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
; w tym
wyłącza przetwarzanie sygnałów cyfrowych konkretnego urządzenia, co może zwiększyć opóźnienie ścieżki wejściowej.
Więcej informacji o wstępnie ustawionych wartościach znajdziesz w sekcji Interfejs konfiguracji Androida powyżej.
W przypadku jednoczesnego wprowadzania i wyświetlania danych stosowane są osobne moduły obsługi uzupełniania kolejki bufora z boku strony. Nie ma gwarancji względnej kolejności wywołań zwrotnych ani synchronizacji zegary audio, nawet jeśli obie strony używają tej samej częstotliwości próbkowania. Twoja aplikacja powinien buforować z prawidłową synchronizacją bufora.
Jednym z efektów potencjalnie niezależnych zegarów audio jest konieczność asynchronicznej konwersji częstotliwości próbkowania. Prostą (choć nieidealną pod względem jakości dźwięku) techniką asynchronicznej konwersji częstotliwości próbkowania jest powielanie lub pomijanie próbek w pobliżu punktu przecięcia z osią zero. Bardziej wyszukany konwersje.
Tryby wydajności
Począwszy od Androida 7.1 (API Level 25) w OpenSL ES wprowadziliśmy sposób określania trybu wydajności dla ścieżki audio. Dostępne są następujące opcje:
SL_ANDROID_PERFORMANCE_NONE
: brak konkretnych wymagań dotyczących wyników. Umożliwia stosowanie efektów sprzętowych i programowych.SL_ANDROID_PERFORMANCE_LATENCY
: priorytet ma opóźnienie. Bez sprzętu lub efekty programowe. Jest to tryb domyślny.SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS
: priorytet ma opóźnienie, ale nadal są dozwolone efekty sprzętowe i programowe.SL_ANDROID_PERFORMANCE_POWER_SAVING
: priorytet nadawany oszczędzaniu energii. Umożliwia stosowanie efektów sprzętowych i programowych.
Uwaga: jeśli nie potrzebujesz ścieżki o krótkim czasie oczekiwania i chcesz podjąć
wykorzystać wbudowane efekty dźwiękowe (np. poprawić akustykę
jakości odtwarzania filmu), musisz ustawić tryb wydajności na
SL_ANDROID_PERFORMANCE_NONE
Aby ustawić tryb wydajności, musisz wywołać funkcję SetConfiguration
za pomocą Androida
jak pokazano poniżej:
// Obtain the Android configuration interface using a previously configured SLObjectItf. SLAndroidConfigurationItf configItf = nullptr; (*objItf)->GetInterface(objItf, SL_IID_ANDROIDCONFIGURATION, &configItf); // Set the performance mode. SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE; result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(performanceMode));
Bezpieczeństwo i uprawnienia
Kto może co robić, zabezpieczenia w Androidzie są wykonywane na poziomie procesu. Programowanie w Javie nie potrafi on zrobić nic więcej niż kod natywny. Nie może on też robić nic więcej Kod w języku programowania Java. Różnią się one tylko dostępnymi interfejsami API.
Aplikacje korzystające z OpenSL ES muszą prosić o uprawnienia, których potrzebują w przypadku podobnych
nienatywnych interfejsów API. Jeśli na przykład aplikacja nagrywa dźwięk,
Uprawnienie android.permission.RECORD_AUDIO
. Aplikacje korzystające z efektów dźwiękowych muszą mieć
android.permission.MODIFY_AUDIO_SETTINGS
Aplikacje odtwarzające zasoby identyfikatora URI sieci
potrzebują android.permission.NETWORK
. Więcej informacji:
Praca z systemem
Uprawnienia.
W zależności od wersji platformy i implementacji parsery treści multimedialnych kodeki mogą działać w kontekście aplikacji na Androida, która wywołuje OpenSL ES (kodeki sprzętowe są są wyodrębnione, ale zależą od urządzenia). Nieprawidłowo sformułowane treści, które mają na celu wykorzystanie luk w zabezpieczeniach parsowania i kodowania, to znany wektor ataku. Zalecamy odtwarzanie multimediów tylko z wiarygodnych źródeł partycjonować aplikację w taki sposób, by kod obsługujący multimedia niewiarygodne źródła działają we stosunkowo piaskownicy. Możesz na przykład: przetwarzać multimedia z niezaufanych źródeł w osobnym procesie. Chociaż oba te procesy korzystające z tego samego identyfikatora UID, takie rozdzielenie utrudnia przeprowadzanie ataku.