Audiofunktionen

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

Die App sollte 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 recodiert, 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 von BluetoothDevice für Geräte mit einer Version unter API 23, da AudioDeviceCallback 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);