Funzionalità audio

I dispositivi Android TV possono avere più uscite audio collegate contemporaneamente: Altoparlanti TV, home cinema connesso tramite HDMI, cuffie Bluetooth e così via. Questi dispositivi di output audio possono supportare diverse funzionalità audio, come codifiche (Dolby Digital+, DTS e PCM), frequenza di campionamento e canali. Ad esempio, le TV connesse tramite HDMI supportano una moltitudine di codifiche. mentre le cuffie Bluetooth connesse di solito supportano solo PCM.

Anche l'elenco di dispositivi audio disponibili e il dispositivo audio con routing possono cambiare collegando a caldo i dispositivi HDMI, collegando o scollegando cuffie Bluetooth, o dall'utente che modifica le impostazioni audio. Poiché le funzionalità di output audio anche durante la riproduzione di contenuti multimediali, le app devono adattarsi le modifiche e continua la riproduzione sul nuovo dispositivo audio instradato e sul relativo le funzionalità di machine learning. La generazione di un formato audio sbagliato può causare errori o senza alcun suono.

Le app hanno la capacità di produrre gli stessi contenuti in più codifiche per offrire all'utente la migliore esperienza audio in base al dispositivo audio. le funzionalità di machine learning. Ad esempio, uno stream audio codificato in Dolby Digital viene riprodotto se la TV lo supporta, mentre è disponibile uno stream audio PCM più ampiamente supportato scelto se non è previsto alcun supporto per Dolby Digital. L'elenco delle app Android integrate decodificatori usati per trasformare un flusso audio in PCM Formati multimediali supportati.

Durante la riproduzione, l'app di streaming deve creare AudioTrack con i migliori AudioFormat supportato dall'output dispositivo audio.

Crea una traccia con il formato giusto

Le app dovrebbero creare un AudioTrack, iniziare a riprodurlo e chiamare getRoutedDevice() per determinare il dispositivo audio predefinito da cui riprodurre l'audio. Può essere, ad esempio, una traccia PCM codificata in silenzio breve e sicura, usata solo per determinano il dispositivo su cui viene eseguito il routing e le relative funzionalità audio.

Ottieni codifiche supportate

Utilizza le funzionalità di getAudioProfiles() (livello API 31 o superiore) oppure getEncodings() (livello API 23 o superiore) per determinare i formati audio disponibili sulla dispositivo audio predefinito.

Controllare i profili e i formati audio supportati

Utilizza AudioProfile (livello API 31 o superiore) oppure isDirectPlaybackSupported() (livello API 29 e successivi) per verificare le combinazioni di formato supportate, numero di canali e frequenza di campionamento.

Alcuni dispositivi Android sono in grado di supportare codifiche diverse da quelle supportati dal dispositivo audio di output. Questi formati aggiuntivi dovrebbero essere rilevato tramite isDirectPlaybackSupported(). In questi casi, i dati audio viene ricodificato in un formato supportato dal dispositivo audio di output. Utilizza le funzionalità di isDirectPlaybackSupported() per verificare correttamente il supporto del formato desiderato anche se non è presente nell'elenco restituito da getEncodings().

Percorso audio anticipato

Android 13 (livello API 33) ha introdotto percorsi audio anticipatamente. Puoi prevedere il supporto degli attributi audio del dispositivo e preparare le tracce per i contenuti dispositivo audio. Puoi utilizzare la modalità getDirectPlaybackSupport() per verificare se la riproduzione diretta è supportata sull'audio attualmente instradato dispositivo per un determinato formato e attributi:

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
}

In alternativa, puoi eseguire una query sui profili supportati per i contenuti multimediali diretti tramite il dispositivo audio attualmente instradato. Sono esclusi eventuali profili non supportati o che, ad esempio, sarebbero transcodificati dal :

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 questo esempio, preferredFormats è un elenco AudioFormat. Viene ordinato con il più preferito nell'elenco e il meno preferito per ultimo. getDirectProfilesForAttributes() restituisce un elenco di AudioProfile per l'oggetto dispositivo audio attualmente instradato con AudioAttributes L'elenco di elementi AudioFormat preferiti vengono iterati fino a quando non viene supportata una corrispondenza AudioProfile trovato. Questo AudioProfile è memorizzato come bestAudioProfile. Le frequenze di campionamento e le maschere del canale ottimali sono stabilite a partire dal giorno bestAudioProfile. Infine, un'istanza AudioFormat appropriata viene creata un'istanza.

Crea traccia audio

Le app devono usare queste informazioni per creare un AudioTrack per AudioFormat di qualità più alta supportata dal dispositivo audio predefinito (e disponibili per i contenuti selezionati).

Intercetta modifiche dispositivo audio

Per intercettare e reagire ai cambiamenti del dispositivo audio, le app devono:

  • Per i livelli API uguali o superiori a 24, aggiungi un parametro OnRoutingChangedListener per monitorare le modifiche del dispositivo audio (HDMI, Bluetooth e così via).
  • Per il livello API 23, registra un AudioDeviceCallback per ricevere modifiche nell'elenco di dispositivi audio disponibili.
  • Per i livelli API 21 e 22, monitora Eventi relativi alle prese HDMI e utilizzare i dati aggiuntivi delle trasmissioni.
  • Registra anche un BroadcastReceiver da monitorare Modifiche di stato in BluetoothDevice per i dispositivi con versioni precedenti all'API 23, AudioDeviceCallback non è ma supportati.

Quando viene rilevata una modifica del dispositivo audio per AudioTrack, l'app verificare le funzionalità audio aggiornate e, se necessario, ricrearle AudioTrack con un altro AudioFormat. Esegui questa operazione se un video di qualità superiore la codifica è ora supportata oppure la codifica utilizzata in precedenza non più supportato.

Codice di esempio

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);