Audiolatenz

Die Latenz ist die Zeit, die ein Signal durch ein System übertragen muss. Dies sind die häufigsten Latenzarten im Zusammenhang mit Audioanwendungen:

  • Die Latenz der Audioausgabe ist die Zeit zwischen einem von einer App generierten Audiobeispiel und dem Abspielen über den Kopfhöreranschluss oder den integrierten Lautsprecher.
  • Die Audioeingabelatenz ist die Zeit zwischen dem Empfang eines Audiosignals durch die Audioeingabe eines Geräts, z. B. dem Mikrofon, und den gleichen Audiodaten, die für eine App verfügbar sind.
  • Die Umlauflatenz ist die Summe aus der Eingabelatenz, der Anwendungsverarbeitungszeit und der Ausgabelatenz.

  • Die Berührungslatenz ist die Zeit zwischen dem Berühren des Bildschirms durch einen Nutzer und dem Empfang dieses Berührungsereignisses von einer App.
  • Die Aufwärmlatenz ist die Zeit, die für den Start der Audiopipeline benötigt wird, wenn Daten zum ersten Mal in einen Zwischenspeicher gestellt werden.

Auf dieser Seite wird beschrieben, wie Sie eine Audioanwendung mit Ein- und Ausgabe mit niedriger Latenz entwickeln und Aufwärmlatenz vermeiden.

Latenz messen

Es ist schwierig, die Latenz von Audioein- und -ausgabe isoliert zu messen, da dafür genau bekannt sein muss, wann das erste Sample in den Audiopfad gesendet wird. Dies kann jedoch mit einer Lichttestschaltung und einem Oszilloskop durchgeführt werden. Wenn Sie die Audio-Umlauflatenz kennen, können Sie sich der groben Faustregel orientieren: Die Audioeingabe- und -ausgabelatenz beträgt die Hälfte der Umlauf-Audiolatenz im Vergleich zu Pfaden ohne Signalverarbeitung.

Die Umlauf-Audiolatenz variiert je nach Gerätemodell und Android-Build stark. Eine ungefähre Vorstellung von der Umlauflatenz für Nexus-Geräte finden Sie in den veröffentlichten Messungen.

Sie können die Audioumlauflatenz messen, indem Sie eine App erstellen, die ein Audiosignal generiert, auf dieses Signal wartet und die Zeit zwischen dem Senden und dem Empfang misst.

Da die niedrigste Latenz über Audiopfade mit minimaler Signalverarbeitung erreicht wird, können Sie auch einen Audio-Loopback-Dongle verwenden. Damit kann der Test über den Headset-Anschluss durchgeführt werden.

Best Practices zum Minimieren der Latenz

Audioleistung prüfen

Im Android Compatibility Definition Document (CDD) sind die Hardware- und Softwareanforderungen eines kompatiblen Android-Geräts aufgeführt. Weitere Informationen zum gesamten Kompatibilitätsprogramm finden Sie unter Android-Kompatibilität. Das eigentliche CDD-Dokument finden Sie unter CDD.

Auf der CDD wird die Umlauflatenz mit maximal 20 ms angegeben, obwohl Musiker in der Regel 10 ms benötigen. Das liegt daran, dass es wichtige Anwendungsfälle gibt, die um 20 ms aktiviert werden.

Es gibt derzeit keine API zum Ermitteln der Audiolatenz für einen beliebigen Pfad auf einem Android-Gerät zur Laufzeit. Sie können jedoch die folgenden Hardwarefunktions-Flags verwenden, um herauszufinden, ob das Gerät Garantien für die Latenz übernimmt:

Die Kriterien für die Meldung dieser Flags werden in der CDD in den Abschnitten 5.6 Audiolatenz und 5.10 Professional Audio definiert.

So suchen Sie in Java nach diesen Funktionen:

Kotlin

val hasLowLatencyFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)

val hasProFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO)

Java

boolean hasLowLatencyFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);

boolean hasProFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);

In Bezug auf die Beziehung von Audioelementen ist das Feature android.hardware.audio.low_latency eine Voraussetzung für android.hardware.audio.pro. Auf einem Gerät kann android.hardware.audio.low_latency und nicht android.hardware.audio.pro implementiert werden, aber nicht umgekehrt.

Keine Annahmen zur Audioleistung treffen

Beachten Sie die folgenden Annahmen, um Latenzprobleme zu vermeiden:

  • Gehen Sie nicht davon aus, dass die Lautsprecher und Mikrofone in Mobilgeräten im Allgemeinen eine gute Akustik haben. Aufgrund ihrer geringen Größe ist die Akustik in der Regel schwach. Daher wird die Signalverarbeitung ergänzt, um die Klangqualität zu verbessern. Diese Signalverarbeitung führt zu Latenz.
  • Gehen Sie nicht davon aus, dass Ihre Eingabe- und Ausgabe-Callbacks synchronisiert sind. Für die gleichzeitige Eingabe und Ausgabe werden separate Handler zum Abschluss der Zwischenspeicherwarteschlange für jede Seite verwendet. Die relative Reihenfolge dieser Callbacks oder die Synchronisierung der Audiouhren kann nicht garantiert werden, selbst wenn beide Seiten dieselbe Abtastrate verwenden. Ihre Anwendung sollte die Daten mit der richtigen Zwischenspeichersynchronisierung puffern.
  • Gehen Sie nicht davon aus, dass die tatsächliche Abtastrate genau mit der nominalen Abtastrate übereinstimmt. Wenn die nominale Abtastrate beispielsweise 48.000 Hz beträgt, ist es normal, dass der Audiotakt mit einer etwas anderen Frequenz fortschreitet als die CLOCK_MONOTONIC des Betriebssystems. Das liegt daran, dass die Audio- und Systemuhren aus verschiedenen Kristallen ableiten können.
  • Gehen Sie nicht davon aus, dass die tatsächliche Abtastrate für die Wiedergabe genau mit der tatsächlichen Erfassungsabtastrate übereinstimmt, insbesondere wenn sich die Endpunkte auf separaten Pfaden befinden. Wenn Sie beispielsweise über das Mikrofon des Geräts mit einer Nominalabtastrate von 48.000 Hz und über USB-Audio mit einer Nominalabtastrate von 48.000 Hz wiedergeben, unterscheiden sich die tatsächlichen Abtastraten wahrscheinlich leicht voneinander.

Eine Folge potenziell unabhängiger Audiotakte ist die Notwendigkeit einer asynchronen Umwandlung der Abtastrate. Eine einfache, jedoch nicht für die Audioqualität ideale Methode zur Umwandlung asynchroner Abtastraten ist das Duplizieren oder Entfernen von Stichproben bei Bedarf in der Nähe eines Nullkreuzes. Raffiniertere Conversions sind möglich.

Eingabelatenz minimieren

In diesem Abschnitt finden Sie Vorschläge zum Reduzieren der Audioeingabelatenz beim Aufnehmen mit einem integrierten Mikrofon oder einem externen Headset-Mikrofon.

  • Wenn Ihre App die Eingabe überwacht, schlagen Sie Nutzern vor, ein Headset zu verwenden, z. B. indem Sie bei der ersten Ausführung den Bildschirm Am besten mit Kopfhörern anzeigen lassen. Beachten Sie, dass die bloße Verwendung des Headsets nicht die geringstmögliche Latenz garantiert. Möglicherweise müssen Sie weitere Schritte ausführen, um die unerwünschte Signalverarbeitung aus dem Audiopfad zu entfernen, z. B. die Voreinstellung VOICE_RECOGNITION für die Aufnahme verwenden.
  • Sie müssen die nominalen Abtastraten von 44.100 und 48.000 Hz verarbeiten können, wie von getProperty(String) für PROPERTY_OUTPUT_Example_RATE gemeldet. Andere Abtastraten sind möglich, jedoch selten.
  • Sie müssen darauf vorbereitet sein, die von getProperty(String) für PROPERTY_OUTPUT_FRAMES_PER_BUFFER gemeldete Puffergröße zu verarbeiten. Typische Zwischenspeichergrößen sind 96, 128, 160, 192, 240, 256 oder 512 Frames. Es sind jedoch auch andere Werte möglich.

Ausgabelatenz minimieren

Optimale Abtastrate beim Erstellen des Audioplayers verwenden

Um die niedrigste Latenz zu erzielen, müssen Sie Audiodaten bereitstellen, die der optimalen Abtastrate und Puffergröße des Geräts entsprechen. Weitere Informationen finden Sie unter Mit verringerter Latenz konzipieren.

In Java können Sie die optimale Abtastrate über AudioManager abrufen, wie im folgenden Codebeispiel gezeigt:

Kotlin

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val sampleRateStr: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
var sampleRate: Int = sampleRateStr?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 44100 // Use a default value if property not found

Java

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
if (sampleRate == 0) sampleRate = 44100; // Use a default value if property not found

Sobald Sie die optimale Abtastrate kennen, können Sie sie beim Erstellen des Players angeben. In diesem Beispiel wird OpenSL ES verwendet:

// create buffer queue audio player
void Java_com_example_audio_generatetone_MainActivity_createBufferQueueAudioPlayer
        (JNIEnv* env, jclass clazz, jint sampleRate, jint framesPerBuffer)
{
   ...
   // specify the audio source format
   SLDataFormat_PCM format_pcm;
   format_pcm.numChannels = 2;
   format_pcm.samplesPerSec = (SLuint32) sampleRate * 1000;
   ...
}

Hinweis: samplesPerSec bezieht sich auf die Abtastrate pro Kanal in Millihertz (1 Hz = 1.000 mHz).

Optimale Puffergröße verwenden, um Audiodaten in die Warteschlange zu stellen

Die optimale Puffergröße lässt sich ähnlich wie die optimale Abtastrate mithilfe der AudioManager API ermitteln:

Kotlin

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val framesPerBuffer: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
var framesPerBufferInt: Int = framesPerBuffer?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 256 // Use default

Java

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int framesPerBufferInt = Integer.parseInt(framesPerBuffer);
if (framesPerBufferInt == 0) framesPerBufferInt = 256; // Use default

Die Eigenschaft PROPERTY_OUTPUT_FRAMES_PER_BUFFER gibt die Anzahl der Audioframes an, die der HAL-Zwischenspeicher (Hardware Extraction Layer) enthalten kann. Sie sollten die Audiozwischenspeicher so konstruieren, dass sie ein genaues Vielfaches dieser Zahl enthalten. Wenn Sie die richtige Anzahl von Audioframes verwenden, erfolgen die Callbacks in regelmäßigen Intervallen, wodurch Jitter reduziert werden.

Es ist wichtig, die Puffergröße über die API zu bestimmen, anstatt einen hartcodierten Wert zu verwenden, da sich die HAL-Puffergrößen je nach Gerät und Android-Build unterscheiden.

Fügen Sie keine Ausgabeschnittstellen mit Signalverarbeitung hinzu

Der schnelle Mixer unterstützt nur diese Schnittstellen:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUMEN
  • SL_IID_MUTESOLO

Diese Schnittstellen sind nicht zulässig, da sie eine Signalverarbeitung erfordern und dazu führen, dass Ihre Anfrage nach einem Fasttrack abgelehnt wird:

  • SL_IID_BASSBOOST
  • SL_IID_EFFEKTENDE
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • SL_IID_VIRTUALIZER
  • SL_IID_ANDROID-EFFEKT
  • SL_IID_ANDROID-EFFEKTEEND

Füge beim Erstellen des Players nur schnelle Benutzeroberflächen hinzu, wie im folgenden Beispiel gezeigt:

const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };

Prüfen, ob Sie einen Track mit niedriger Latenz verwenden

Führen Sie die folgenden Schritte aus, um zu prüfen, ob Sie einen Track mit niedriger Latenz erhalten haben:

  1. Starten Sie die App und führen Sie dann den folgenden Befehl aus:
  2. adb shell ps | grep your_app_name
    
  3. Notieren Sie sich die Prozess-ID Ihrer App.
  4. Geben Sie jetzt Audioinhalte aus Ihrer App wieder. Sie haben etwa drei Sekunden Zeit, um den folgenden Befehl über das Terminal auszuführen:
  5. adb shell dumpsys media.audio_flinger
    
  6. Suchen Sie nach Ihrer Prozess-ID. Wenn in der Spalte Name ein F angezeigt wird, handelt es sich um einen Track mit niedriger Latenz (das F steht für Fast Track).

Aufwärmlatenz minimieren

Wenn Sie Audiodaten zum ersten Mal in die Warteschlange stellen, dauert es eine geringe, aber dennoch erhebliche Zeit, bis die Audioschaltung des Geräts aufgewärmt ist. Um diese Aufwärmlatenz zu vermeiden, können Sie wie im folgenden Codebeispiel gezeigte Audiodaten mit Stille in die Warteschlange stellen:

#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
    for (i = 0; i<numSamples; i++) {
        silenceBuffer[i] = 0;
    }

Sobald Audiodaten erzeugt werden sollen, können Sie zu Puffern in Warteschleifen wechseln, die echte Audiodaten enthalten.

Hinweis:Eine kontinuierliche Audioausgabe verursacht einen erheblichen Stromverbrauch. Sie müssen die Ausgabe in der Methode onPause() beenden. Sie können die lautlose Ausgabe auch nach einer gewissen Zeit der Nutzerinaktivität pausieren.

Zusätzlicher Beispielcode

Unter NDK-Beispiele kannst du eine Beispiel-App mit Audiolatenz herunterladen.

Weitere Informationen

  1. Audiolatenz für App-Entwickler
  2. Zur Audiolatenz beitragen
  3. Audiolatenz messen
  4. Audio-Aufwärmphase
  5. Latenz (Audio)
  6. Verspätungen bei Umlauf