WARNUNG: OpenSL ES wurde eingestellt. Entwickler sollten die Open-Source- Oboenbibliothek, die auf GitHub verfügbar ist Oboe ist ein C++-Wrapper, der eine API bietet, die der AAudio: Oboe ruft AAudio auf, wenn AAudio verfügbar. Falls AAudio nicht verfügbar ist, wird OpenSL ES verwendet.
Die Hinweise in diesem Abschnitt ergänzen die OpenSL ES 1.0.1 Spezifikation.
Objekt- und Schnittstelleninitialisierung
Zwei Aspekte des OpenSL ES-Programmiermodells, die neuen Entwicklern vielleicht noch nicht vertraut sind, sind die die Unterscheidung zwischen Objekten und Schnittstellen und die Initialisierungssequenz.
Kurz gesagt, ähnelt ein OpenSL ES-Objekt dem Objektkonzept in
Programmiersprachen wie Java
und C++, außer ein OpenSL ES-Objekt ist nur über die zugehörigen Schnittstellen sichtbar.
Dazu gehören
die erste Schnittstelle für alle Objekte, die SLObjectItf
heißt.
Es gibt kein Handle für ein Objekt.
nur ein Handle zur SLObjectItf
-Schnittstelle des Objekts.
Zuerst wird ein OpenSL ES-Objekt erstellt, das ein SLObjectItf
zurückgibt.
realisiert. Das ähnelt dem gängigen Programmiermuster, bei dem zuerst ein Objekt erstellt wird (was nur bei fehlendem Arbeitsspeicher oder ungültigen Parametern fehlschlagen sollte) und dann die Initialisierung abgeschlossen wird (was aufgrund von fehlenden Ressourcen fehlschlagen kann). Der Schritt „Realisieren“ gibt
eine logische Stelle zu implementieren,
um bei Bedarf zusätzliche Ressourcen zuzuweisen.
Als Teil der API zum Erstellen eines Objekts gibt eine Anwendung ein Array der gewünschten Schnittstellen an die es später erwerben möchte. Hinweis: Mit diesem Array werden die Schnittstellen nicht automatisch erworben. Es gibt lediglich an, dass sie in Zukunft erworben werden sollen. Benutzeroberflächen unterscheiden sich implizit oder explizit. Eine explizite Schnittstelle muss im Array aufgeführt sein, wenn später übernommen werden. Eine implizite Schnittstelle muss nicht im Array erstellen, aber es schadet nicht, sie dort zu veröffentlichen. OpenSL ES hat eine weitere Art von Schnittstellen, Dynamic, die nicht im Objekt angegeben werden muss Array erstellen und kann hinzugefügt werden, nachdem das Objekt erstellt wurde. Die Android-Implementierung bietet eine praktische Funktion, vermeiden, die in den Dynamische Schnittstellen bei der Objekterstellung
Nachdem das Objekt erstellt und realisiert wurde, sollte die Anwendung Schnittstellen für alle
Funktion, die sie benötigt, unter Verwendung von GetInterface
für die anfängliche SLObjectItf
.
Anschließend kann das Objekt über seine Schnittstellen verwendet werden. Einige Objekte erfordern jedoch eine weitere Einrichtung. Ein Audioplayer mit URI-Datenquelle benötigt eine intensivere Vorbereitung um Verbindungsfehler zu erkennen. Weitere Informationen finden Sie in der Im Abschnitt Vorabruf des Audioplayers finden Sie weitere Informationen.
Nachdem Ihre Anwendung mit dem Objekt fertig ist, sollten Sie es explizit löschen. sieh dir die Vernichten weiter unten.
Vorabruf des Audioplayers
Bei einem Audioplayer mit URI-Datenquelle weist Object::Realize
Ressourcen, aber nicht
eine Verbindung zur Datenquelle herstellen (vorbereiten) oder mit dem Vorabruf von Daten beginnen. Diese treten auf, sobald der
Der Player-Status ist entweder auf SL_PLAYSTATE_PAUSED
oder SL_PLAYSTATE_PLAYING
festgelegt.
Einige Informationen sind möglicherweise noch relativ spät in der Sequenz unbekannt. Insbesondere wird bei Player::GetDuration
SL_TIME_UNKNOWN
zurückgegeben und MuteSolo::GetChannelCount
gibt entweder erfolgreich „0“ zurück oder das Fehlerergebnis SL_RESULT_PRECONDITIONS_VIOLATED
. Diese APIs geben die richtigen Werte
sobald sie bekannt sind.
Zu den weiteren Eigenschaften, die anfänglich unbekannt sind, gehören die Stichprobenrate und tatsächlicher Medieninhaltstyp auf der Überschrift des Inhalts (im Gegensatz zu den anwendungsspezifischen MIME-Typ und Containertyp). Diese werden auch später im Verlauf Vorabrufen vorbereiten, aber es gibt keine APIs, um sie abzurufen.
Über die Oberfläche für den Prefetch-Status lässt sich erkennen, verfügbar ist, oder Ihr Anwendung regelmäßig abfragen kann. Beachten Sie, dass einige Informationen wie der Dauer eines Streamings MP3-Format, sind möglicherweise nie bekannt.
Die Benutzeroberfläche für den Prefetch-Status ist auch nützlich, um Fehler zu erkennen. Callback registrieren
und aktivieren Sie
mindestens SL_PREFETCHEVENT_FILLLEVELCHANGE
und SL_PREFETCHEVENT_STATUSCHANGE
Ereignisse. Wenn beide Ereignisse gleichzeitig ausgeliefert werden und
PrefetchStatus::GetFillLevel
gibt eine Ebene von null an und
PrefetchStatus::GetPrefetchStatus
meldet SL_PREFETCHSTATUS_UNDERFLOW
,
dann das
weist auf einen nicht behebbaren Fehler in der Datenquelle hin. Dazu gehört auch die Unfähigkeit,
verbinden Sie sich mit dem
Datenquelle, da der lokale Dateiname nicht vorhanden ist oder der Netzwerk-URI ungültig ist.
Die nächste Version von OpenSL ES soll voraussichtlich mehr expliziten Support für der Fehlerbehandlung Datenquelle verwendet werden. Für die zukünftige Kompatibilität mit Binärprogrammen beabsichtigen wir jedoch, um die aktuelle Situation zu unterstützen, Methode zum Melden eines nicht behebbaren Fehlers.
Eine empfohlene Codesequenz sieht so aus:
Engine::CreateAudioPlayer
Object:Realize
Object::GetInterface
fürSL_IID_PREFETCHSTATUS
PrefetchStatus::SetCallbackEventsMask
PrefetchStatus::SetFillUpdatePeriod
PrefetchStatus::RegisterCallback
Object::GetInterface
fürSL_IID_PLAY
Play::SetPlayState
bisSL_PLAYSTATE_PAUSED
oderSL_PLAYSTATE_PLAYING
Hinweis: Vorbereitung und Vorabruf erfolgen hier. während dieser Zeit wird Ihr Rückruf mit regelmäßige Statusupdates zu erhalten.
Vernichten
Achten Sie darauf, alle Objekte zu löschen, wenn Sie Ihre Anwendung verlassen. Objekte sollten in umgekehrte Reihenfolge ihrer Erstellung, da es nicht sicher ist, ein Objekt zu zerstören, Objekte. Zerstören wir z. B. diese Reihenfolge: Audioplayer und -rekorder, Ausgabemix und dann und schließlich den Motor.
OpenSL ES unterstützt nicht die automatische
automatische Speicherbereinigung oder
Referenz
Zählung der Schnittstellen. Nach dem Anruf von Object::Destroy
, alle vorhandenen
Schnittstellen, die
die vom verknüpften Objekt abgeleitet wurden, nicht definiert werden.
Die Android OpenSL ES-Implementierung erkennt die falsche Verwendung solcher Schnittstellen nicht. Wenn Sie solche Schnittstellen weiterhin verwenden, nachdem das Objekt gelöscht wurde, kann Ihre Anwendung zu Abstürzen oder unvorhersehbarem Verhalten.
Es empfiehlt sich, sowohl die primäre Objektschnittstelle als auch alle zugehörigen
im Rahmen der Sequenz zum Löschen von Objekten zu NULL
, wodurch eine versehentliche
Missbrauch eines veralteten Schnittstellen-Handles.
Stereo-Schwenken
Wenn Volume::EnableStereoPosition
verwendet wird, um das Stereo-Schwenken einer Monoquelle zu aktivieren,
Die Gesamtmenge ist um 3 dB reduziert.
Schallleistung
Level auf. Dies ist erforderlich, damit der gesamte Schallleistungspegel konstant bleibt,
ist die Quelle
die von einem Kanal zum anderen
geschwenkt wurden. Aktivieren Sie daher die Stereo-Positionierung nur,
. Weitere Informationen findest du im Wikipedia-Artikel
Audio-Schwenken.
Callbacks und Threads
Callback-Handler werden im Allgemeinen synchron aufgerufen, wenn die Implementierung ein . Dieser Punkt ist asynchron zur Anwendung. Sie sollten daher einen nicht blockierenden Synchronisierungsmechanismus verwenden, um den Zugriff auf alle Variablen zu steuern, die zwischen der Anwendung und dem Callback-Handler freigegeben werden. Im Beispielcode, z. B. für Pufferwarteschlangen, haben wir entweder dies weggelassen. oder aus Gründen der Einfachheit eine blockierende Synchronisierung verwendet. Echte, nicht aus Das Blockieren der Synchronisierung ist für jeglichen Produktionscode von entscheidender Bedeutung.
Callback-Handler werden von internen Nicht-Anwendungs-Threads aufgerufen, die nicht an die Android-Laufzeit, daher können sie JNI nicht verwenden. Da diese internen Threads entscheidend für der Integrität der OpenSL ES-Implementierung gerechtfertigt ist, sollte ein Callback-Handler ebenfalls keine oder übermäßige Arbeit.
Wenn Ihr Callback-Handler JNI verwenden oder Arbeiten ausführen muss, die nicht proportional zur -Rückruf zurück, sollte der Handler stattdessen ein Ereignis für einen anderen zu verarbeitenden Thread posten. Beispiele für zulässige Callback-Arbeitslasten sind das Rendern und Einfügen des nächsten Ausgabepuffers (für einen Audioplayer), die Verarbeitung des gerade gefüllten Eingabepuffers und das Einfügen des nächsten leeren Puffers (für einen Audiorekorder) oder einfache APIs wie die meisten Get-APIs. Weitere Informationen zur Arbeitslast finden Sie unten im Abschnitt Leistung.
Beachten Sie, dass das Gegenteil sicher ist: ein Android-Anwendungs-Thread, der in die JNI eingedrungen ist. darf OpenSL ES APIs direkt aufrufen, einschließlich derjenigen, die blockieren. Das Blockieren von Anrufen ist jedoch nicht aus dem Hauptthread empfohlen, da dies zu App antwortet nicht (ANR).
Die Feststellung in Bezug auf den Thread, der einen Rückruf-Handler aufruft, bleibt größtenteils dem Implementierung. Der Grund für diese Flexibilität besteht darin, zukünftige Optimierungen zu ermöglichen, vor allem auf Multi-Core-Geräte.
Der Thread, in dem der Rückruf-Handler ausgeführt wird, hat nicht garantiert dieselbe Identität bei verschiedenen Aufrufen. Verlassen Sie sich daher nicht auf den pthread_t
, der von
pthread_self()
oder die pid_t
, die von gettid()
zurückgegeben wurden,
bei allen Anrufen einheitlich sind. Aus dem gleichen Grund sollten Sie keine TLS APIs (Local Storage) für Threads wie
pthread_setspecific()
und pthread_getspecific()
von einem Rückruf.
Die Implementierung garantiert, dass gleichzeitige Rückrufe derselben Art für den zum gleichen Objekt nicht auftreten. Gleichzeitige Rückrufe unterschiedlicher Art für dasselbe Objekt sind bei unterschiedliche Threads.
Leistung
Da OpenSL ES eine native C API ist, haben Nicht-Laufzeit-Anwendungsthreads, die OpenSL ES aufrufen, laufzeitbezogenen Aufwand wie Pausen der automatischen Speicherbereinigung. Mit einer Ausnahme, die nachfolgend beschrieben ist, bietet OpenSL ES keine weiteren Leistungsvorteile. Insbesondere OpenSL ES garantiert keine Verbesserungen wie eine niedrigere Audiolatenz und höhere Priorität gegenüber den Plattformen, die die Plattform im Allgemeinen bietet. Da das Unternehmen Die Android-Plattform und spezifische Geräteimplementierungen entwickeln sich ständig weiter, eine OpenSL ES-Anwendung. wird von künftigen Verbesserungen der Systemleistung profitieren.
Eine dieser Entwicklungen ist die Unterstützung
Latenz der Audioausgabe.
Die Grundlage für reduzierte
dass die Ausgabelatenz zuerst in Android 4.1 (API-Level 16) enthalten war,
wurden in Android 4.2 (API-Level 17) fortgesetzt. Diese Verbesserungen sind verfügbar über
OpenSL ES für Geräteimplementierungen, die
android.hardware.audio.low_latency
freischalten.
Wenn das Gerät diese Funktion nicht beansprucht, aber Android 2.3 (API-Level 9) unterstützt
können Sie zwar die OpenSL ES APIs verwenden, die Ausgabelatenz kann jedoch höher sein.
Je niedriger
Der Ausgabelatenzpfad wird nur verwendet, wenn die Anwendung eine Puffergröße und eine Abtastrate anfordert
die
ist mit der nativen Ausgabekonfiguration des Geräts kompatibel. Diese Parameter sind
gerätespezifische und
erhalten Sie wie unten beschrieben.
Ab Android 4.2 (API-Ebene 17) kann eine Anwendung die native oder optimale Ausgabeabtastrate und ‑puffergröße für den primären Ausgabestream des Geräts abfragen. In Kombination mit dem oben genannten Funktionstest kann sich eine App jetzt für eine Ausgabe mit niedrigerer Latenz auf Geräten konfigurieren, die die Unterstützung angeben.
Für Android 4.2 (API-Ebene 17) und niedriger ist für eine geringere Latenz eine Pufferanzahl von mindestens zwei erforderlich. Ab Android 4.3 (API-Level 18) wird ein Puffer ist die Anzahl von Eins ausreichend, um die Latenz zu verringern.
Alle OpenSL-ES-Schnittstellen für Ausgabeeffekte schließen den Pfad mit niedrigerer Latenz aus.
Die empfohlene Reihenfolge lautet:
- Prüfen Sie, ob API-Level 9 oder höher vorhanden ist, um die Verwendung von OpenSL ES zu bestätigen.
- Prüfe mit folgendem Code, ob die Funktion
android.hardware.audio.low_latency
verfügbar ist: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);
- Überprüfen Sie das API-Level 17 oder höher, um die Verwendung von zu bestätigen.
android.media.AudioManager.getProperty()
- Rufen Sie die native oder optimale Ausgabeabtastrate und Puffergröße für die
Primärausgabe
mit folgendem Code streamen:
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
undframesPerBuffer
sind Strings. Erste Überprüfung auf null und konvertieren Sie sie dann mithilfe vonInteger.parseInt()
in eine Ganzzahl. - Erstellen Sie jetzt mit OpenSL ES einen AudioPlayer mit PCM-Pufferwarteschlangen-Data Locator.
Hinweis: Sie können die Größe der Audiozwischenspeicher Test-App zur Bestimmung der nativen Puffergröße und Abtastrate für OpenSL ES-Audio Apps auf Ihrem Audiogerät. Sie können auch GitHub aufrufen, um Audiopuffergröße.
Die Anzahl der Audioplayer mit niedrigerer Latenz ist begrenzt. Wenn für Ihre Anwendung mehr als ein paar Audioquellen können Sie Ihre Audioinhalte auf Anwendungsebene mischen. Deaktivieren Sie Ihre Audioplayer, wenn Ihre Aktivität pausiert ist, da sie eine globale Ressource sind, die mit anderen Apps geteilt wird.
Um hörbare Störungen zu vermeiden, muss der Callback-Handler für die Pufferwarteschlange innerhalb eines vorhersehbaren Zeitfensters. Dies impliziert normalerweise keine unbegrenzte Blockierung von Mutexen, Bedingungen, oder E/A-Operationen. Verwenden Sie stattdessen TryLocks, Sperren und Wartezeiten mit Zeitüberschreitungen sowie nicht blockierende Algorithmen.
Die Berechnung, die zum Rendern des nächsten Buffers (für AudioPlayer) oder zum Verbrauchen des vorherigen Buffers (für AudioRecord) erforderlich ist, sollte für jeden Rückruf ungefähr gleich viel Zeit in Anspruch nehmen. Vermeiden Sie Algorithmen, die in einem nicht deterministischen Zeitraum ausgeführt werden oder in einem Burst- ihre Berechnungen durchführen. Eine Callback-Berechnung erfolgt stoßweise, wenn die für einen bestimmten Callback aufgewendete CPU-Zeit deutlich größer als der Durchschnitt ist. Zusammenfassend lässt sich sagen, dass die CPU-Ausführungszeit den Handler auf eine Varianz nahe null und legt ihn fest, dass er nicht für unbegrenzte Zeiträume blockiert wird.
Audio mit niedrigerer Latenz ist nur für diese Ausgaben möglich:
- On-Device-Lautsprecher
- Kabelgebundene Kopfhörer
- Kabelgebundene Headsets
- Ausrichten
- USB Digital Audio
Auf einigen Geräten ist die Sprecherlatenz höher als bei anderen Pfaden aufgrund der digitalen Signalverarbeitung für Korrektur und Schutz des Lautsprechers.
Ab Android 5.0 (API-Level 21): geringere Latenz
Audioeingabe wird auf ausgewählten Geräten unterstützt. Um diese Funktion zu nutzen, müssen Sie zuerst bestätigen,
dass eine Ausgabe mit geringerer Latenz
wie oben beschrieben zur Verfügung steht. Die Möglichkeit einer Ausgabe mit geringerer Latenz
ist eine Voraussetzung für
die Eingabefunktion mit niedrigerer Latenz. Erstellen Sie dann einen Audiorekorder mit demselben
Abtastrate und Puffergröße, wie sie für die Ausgabe verwendet werden. OpenSL-ES-Schnittstellen für Eingabeeffekte
den Pfad mit niedrigerer Latenz ausschließen. Für eine geringere Latenz muss das Aufnahme-Preset SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
verwendet werden. Dieses Preset deaktiviert die gerätespezifische digitale Signalverarbeitung, die dem Eingabepfad eine Latenz hinzufügen kann.
Weitere Informationen zu Voreinstellungen für Datensätze finden Sie in der
Android-Konfiguration
oben im Abschnitt "Benutzeroberfläche".
Zur gleichzeitigen Eingabe und Ausgabe werden jeweils separate Handler für die Zwischenspeicherwarteschlange verwendet. zu verstehen. Es gibt keine Garantie für die relative Reihenfolge dieser Callbacks oder die Synchronisierung von auch wenn beide Seiten die gleiche Abtastrate verwenden. Ihre Anwendung sollte die Daten mit einer ordnungsgemäßen Puffersynchronisierung puffern.
Eine Folge potenziell unabhängiger Audiouhren ist die Notwendigkeit einer asynchronen Abtastrate. Conversion. Ein einfaches (aber nicht ideal für die Audioqualität) Verfahren für die asynchrone Abtastrate Conversion besteht darin, Stichproben nach Bedarf nahe einem Punkt zu duplizieren oder zu verwerfen. Komplexer Conversions möglich sind.
Leistungsmodi
Ab Android 7.1 (API-Ebene 25) bietet OpenSL ES die Möglichkeit, einen Leistungsmodus für den Audiopfad anzugeben. Folgende Optionen sind verfügbar:
SL_ANDROID_PERFORMANCE_NONE
: Keine spezifischen Leistungsanforderungen. Ermöglicht Hardware- und Softwareeffekte.SL_ANDROID_PERFORMANCE_LATENCY
: Priorität hat die Latenz. Keine Hardware- oder Softwareeffekte. Dies ist der Standardmodus.SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS
: Priorität wird Latenz gegeben und dennoch Hardware- und Softwareeffekte nutzen.SL_ANDROID_PERFORMANCE_POWER_SAVING
: Priorität der Energieeinsparung. Ermöglicht Hardware- und Softwareeffekte.
Hinweis: Wenn Sie keinen Pfad mit niedriger Latenz benötigen und
der im Gerät integrierten Audioeffekte zu nutzen (z. B. zur Verbesserung der akustischen
Qualität für Videowiedergabe) festgelegt ist, musst du den Leistungsmodus explizit auf
SL_ANDROID_PERFORMANCE_NONE
Zum Festlegen des Leistungsmodus musst du SetConfiguration
über die Android-App aufrufen
Konfigurationsoberfläche wie unten dargestellt:
// 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));
Sicherheit und Berechtigungen
Die Sicherheit bei Android wird auf Prozessebene festgelegt. Java-Programmierung Sprachcode kann nicht mehr als nativer Code und nativer Code auch nicht mehr als nativer Code. Code der Programmiersprache Java. Die einzigen Unterschiede zwischen ihnen sind die verfügbaren APIs.
Für Anwendungen, die OpenSL ES verwenden, müssen die Berechtigungen angefordert werden, die für ähnliche
nicht native APIs. Wenn Ihre Anwendung beispielsweise Audio aufzeichnet, benötigt sie
Berechtigung „android.permission.RECORD_AUDIO
“. Für Anwendungen mit Audioeffekten müssen
android.permission.MODIFY_AUDIO_SETTINGS
Anwendungen, die Netzwerk-URI-Ressourcen wiedergeben
android.permission.NETWORK
erforderlich. Weitere Informationen finden Sie unter
Arbeiten mit Systemen
Berechtigungen:
Je nach Plattformversion und Implementierung können Parser für Medieninhalte und Software-Codecs im Kontext der Android-Anwendung ausgeführt werden, die OpenSL ES aufruft. Hardware-Codecs sind abstrakt, aber geräteabhängig. Fehlerhafte Inhalte, die darauf abzielen, den Parser und Codec auszunutzen Schwachstellen sind ein bekannter Angriffsvektor. Wir empfehlen, Medien nur von vertrauenswürdigen Quellen abzuspielen. oder Ihre Anwendung so partitionieren, dass Code Medien aus nicht vertrauenswürdige Quellen werden in einer relativ Sandbox-Umgebung ausgeführt. So können Sie beispielsweise Medien aus nicht vertrauenswürdigen Quellen in einem separaten Prozess verarbeiten. Obwohl beide Prozesse weiterhin unter derselben UID ausgeführt werden, erschwert diese Trennung einen Angriff.