Bei Android TV-Geräten können mehrere Audioausgänge gleichzeitig angeschlossen sein: Fernsehlautsprechern, Heimkino mit HDMI-Anschluss, Bluetooth-Kopfhörer usw. Diese Audioausgabegeräte unterstützen verschiedene Audiofunktionen, wie Codierungen (Dolby Digital+, DTS und PCM), Abtastrate und Kanäle. Mit HDMI verbundene Fernseher unterstützen beispielsweise eine Vielzahl von Codierungen Verbundene Bluetooth-Kopfhörer unterstützen normalerweise nur PCM.
Die Liste der verfügbaren Audiogeräte und des weitergeleiteten Audiogeräts können sich ebenfalls ändern indem Sie HDMI-Geräte mit heißem Kabel anschließen, Bluetooth-Kopfhörer verbinden oder trennen, oder der Nutzer, der die Audioeinstellungen ändert. Da die Audioausgabefunktionen ändern, selbst wenn Apps Medien wiedergeben, müssen sie sich an diese und die Wiedergabe auf dem neuen weitergeleiteten Audiogerät fortsetzen Funktionen. Die Ausgabe des falschen Audioformats kann zu Fehlern oder wird kein Ton abgespielt.
Apps können den gleichen Inhalt in mehreren Codierungen ausgeben um dem Nutzer je nach Audiogerät ein optimales Audioerlebnis zu bieten Funktionen. Beispielsweise wird ein Dolby Digital-codierter Audiostream abgespielt. ein besserer PCM-Audiostream, wenn Dolby Digital nicht unterstützt wird. Liste der integrierten Android-Apps Decoder zur Umwandlung eines Audiostreams in PCM finden Sie Unterstützte Medienformate:
Während der Wiedergabe sollte die Streaming-App eine
AudioTrack
mit den besten
Von der Ausgabe unterstützte AudioFormat
Audiogerät.
Einen Track im richtigen Format erstellen
Apps sollten ein AudioTrack
erstellen, die Wiedergabe starten und einen Anruf
getRoutedDevice()
um das Standard-Audiogerät festzulegen, über das Ton wiedergegeben wird.
Dabei kann es sich z. B. um einen sicheren, kurzzeitigen PCM-codierten Track handeln, der nur zur
ermittelt das weitergeleitete Gerät und seine Audiofunktionen.
Unterstützte Codierungen abrufen
Verwenden Sie
getAudioProfiles()
(API-Level 31 und höher) oder
getEncodings()
(API-Level 23 und höher), um die Audioformate zu bestimmen, die auf der
Standard-Audiogerät.
Unterstützte Audioprofile und -formate prüfen
AudioProfile
verwenden
(API-Level 31 und höher) oder
isDirectPlaybackSupported()
(API-Level 29 und höher), um unterstützte Kombinationen von Formaten,
Kanalanzahl und Abtastrate.
Einige Android-Geräte unterstützen neben den unterstützten Codierungen auch andere Codierungen.
die vom Audioausgangsgerät unterstützt werden. Diese zusätzlichen Formate sollten
erkannt über isDirectPlaybackSupported()
. In diesen Fällen können die Audiodaten
wird in ein Format umgewandelt, das vom Audioausgabegerät unterstützt wird. Verwenden Sie
isDirectPlaybackSupported()
, um zu prüfen, ob das gewünschte Format unterstützt wird
auch wenn er nicht in der von getEncodings()
zurückgegebenen Liste enthalten ist.
Antizipatorische Audioroute
Mit Android 13 (API-Level 33) wurden vorausschauende Audiorouten eingeführt. Sie können
die Unterstützung von Audioattributen des Geräts voraussehen und Titel für die aktiven
Audiogerät. Sie können
getDirectPlaybackSupport()
um zu prüfen, ob die direkte Wiedergabe des aktuell weitergeleiteten Audios unterstützt wird
Gerät für ein bestimmtes Format und bestimmte Attribute:
Kotlin
val format = AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_E_AC3) .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1) .setSampleRate(48000) .build() val attributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build() if (AudioManager.getDirectPlaybackSupport(format, attributes) != AudioManager.DIRECT_PLAYBACK_NOT_SUPPORTED ) { // The format and attributes are supported for direct playback // on the currently active routed audio path } else { // The format and attributes are NOT supported for direct playback // on the currently active routed audio path }
Java
AudioFormat format = new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_E_AC3) .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1) .setSampleRate(48000) .build(); AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build(); if (AudioManager.getDirectPlaybackSupport(format, attributes) != AudioManager.DIRECT_PLAYBACK_NOT_SUPPORTED) { // The format and attributes are supported for direct playback // on the currently active routed audio path } else { // The format and attributes are NOT supported for direct playback // on the currently active routed audio path }
Alternativ können Sie abfragen, welche Profile für direkte Medien unterstützt werden. Audiogerät wiedergegeben werden. Profile werden dabei ausgeschlossen die nicht unterstützt werden oder beispielsweise von der Framework:
Kotlin
private fun findBestAudioFormat(audioAttributes: AudioAttributes): AudioFormat { val preferredFormats = listOf( AudioFormat.ENCODING_E_AC3, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_DEFAULT ) val audioProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes) val bestAudioProfile = preferredFormats.firstNotNullOf { format -> audioProfiles.firstOrNull { it.format == format } } val sampleRate = findBestSampleRate(bestAudioProfile) val channelMask = findBestChannelMask(bestAudioProfile) return AudioFormat.Builder() .setEncoding(bestAudioProfile.format) .setSampleRate(sampleRate) .setChannelMask(channelMask) .build() }
Java
private AudioFormat findBestAudioFormat(AudioAttributes audioAttributes) { Stream<Integer> preferredFormats = Stream.<Integer>builder() .add(AudioFormat.ENCODING_E_AC3) .add(AudioFormat.ENCODING_AC3) .add(AudioFormat.ENCODING_PCM_16BIT) .add(AudioFormat.ENCODING_DEFAULT) .build(); Stream<AudioProfile> audioProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes).stream(); AudioProfile bestAudioProfile = (AudioProfile) preferredFormats.map(format -> audioProfiles.filter(profile -> profile.getFormat() == format) .findFirst() .orElseThrow(NoSuchElementException::new) ); Integer sampleRate = findBestSampleRate(bestAudioProfile); Integer channelMask = findBestChannelMask(bestAudioProfile); return new AudioFormat.Builder() .setEncoding(bestAudioProfile.getFormat()) .setSampleRate(sampleRate) .setChannelMask(channelMask) .build(); }
In diesem Beispiel ist preferredFormats
eine Liste von
AudioFormat
Instanzen. Sie ist sortiert
wobei das bevorzugteste zuerst in der Liste und das am wenigsten bevorzugte letzte Element ausgewählt wird.
getDirectProfilesForAttributes()
gibt eine Liste der unterstützten
AudioProfile
-Objekte für die
Audiogerät mit der im Lieferumfang enthaltenen
AudioAttributes
Die Liste der
Es wird iteriert, bis eine übereinstimmende unterstützte AudioFormat
-Elemente
AudioProfile
wurde gefunden. AudioProfile
wird als bestAudioProfile
gespeichert.
Die optimalen Stichprobenraten und Kanalmasken werden mithilfe von bestAudioProfile
ermittelt.
Schließlich wird ein angemessenes AudioFormat
-Element
Instanz erstellt wird.
Audiotrack erstellen
Apps sollten diese Informationen verwenden, um ein AudioTrack
für das
AudioFormat
in höchster Qualität, die vom Standard-Audiogerät unterstützt wird
(und für den ausgewählten Inhalt verfügbar).
Änderungen an Audiogeräten abfangen
Um Änderungen an Audiogeräten abzufangen und darauf zu reagieren, müssen Apps:
- Für API-Ebenen ab 24 fügen Sie ein
OnRoutingChangedListener
zum Überwachen von Änderungen an Audiogeräten (HDMI, Bluetooth usw.). - Registrieren Sie für API-Level 23 eine
AudioDeviceCallback
um Änderungen an der Liste der verfügbaren Audiogeräte zu erhalten. - Achten Sie bei API-Levels 21 und 22 auf Ereignisse zu HDMI-Steckdosen und nutzt die zusätzlichen Daten aus den Broadcasts.
- Auch
BroadcastReceiver
zum Überwachen registrieren Statusänderungen vonBluetoothDevice
für Geräte mit einer Version unter API 23, daAudioDeviceCallback
ist nicht unterstützt.
Wenn für „AudioTrack
“ eine Änderung des Audiogeräts erkannt wurde, wird die App
sollte die aktualisierten Audiofunktionen prüfen und bei Bedarf neu erstellen.
den AudioTrack
durch einen anderen AudioFormat
. Wählen Sie diese Option aus, wenn ein
Codierung wird jetzt unterstützt oder die zuvor verwendete Codierung ist
nicht mehr unterstützt.
Beispielcode
Kotlin
// audioPlayer is a wrapper around an AudioTrack // which calls a callback for an AudioTrack write error audioPlayer.addAudioTrackWriteErrorListener { // error code can be checked here, // in case of write error try to recreate the audio track restartAudioTrack(findDefaultAudioDeviceInfo()) } audioPlayer.audioTrack.addOnRoutingChangedListener({ audioRouting -> audioRouting?.routedDevice?.let { audioDeviceInfo -> // use the updated audio routed device to determine // what audio format should be used if (needsAudioFormatChange(audioDeviceInfo)) { restartAudioTrack(audioDeviceInfo) } } }, handler)
Java
// audioPlayer is a wrapper around an AudioTrack // which calls a callback for an AudioTrack write error audioPlayer.addAudioTrackWriteErrorListener(new AudioTrackPlayer.AudioTrackWriteError() { @Override public void audioTrackWriteError(int errorCode) { // error code can be checked here, // in case of write error try to recreate the audio track restartAudioTrack(findDefaultAudioDeviceInfo()); } }); audioPlayer.getAudioTrack().addOnRoutingChangedListener(new AudioRouting.OnRoutingChangedListener() { @Override public void onRoutingChanged(AudioRouting audioRouting) { if (audioRouting != null && audioRouting.getRoutedDevice() != null) { AudioDeviceInfo audioDeviceInfo = audioRouting.getRoutedDevice(); // use the updated audio routed device to determine // what audio format should be used if (needsAudioFormatChange(audioDeviceInfo)) { restartAudioTrack(audioDeviceInfo); } } } }, handler);