I dispositivi Android TV possono avere più uscite audio collegate contemporaneamente: speaker TV, home cinema collegato tramite HDMI, cuffie Bluetooth e così via. Questi dispositivi di output audio possono supportare funzionalità audio diverse, come codifiche (Dolby Digital+, DTS e PCM), frequenza di campionamento e canali. Ad esempio, le TV connesse a HDMI supportano una moltitudine di codifiche, mentre le cuffie Bluetooth collegate supportano in genere solo PCM.
L'elenco dei dispositivi audio disponibili e il dispositivo audio indirizzato possono variare anche collegando i dispositivi HDMI a caldo, collegando o scollegando le cuffie Bluetooth oppure tramite l'utente che modifica le impostazioni audio. Poiché le funzionalità di output audio possono cambiare anche quando le app riproducono contenuti multimediali, le app devono adattarsi agevolmente a questi cambiamenti e continuare la riproduzione sul nuovo dispositivo audio con routing e sulle relative funzionalità. L'output del formato audio errato potrebbe causare errori o non riprodurre l'audio.
Le app sono in grado di produrre gli stessi contenuti in più codifiche per offrire all'utente la migliore esperienza audio in base alle funzionalità del dispositivo audio. Ad esempio, uno stream audio codificato in Dolby Digital viene riprodotto se la TV lo supporta, mentre uno stream audio PCM più supportato viene scelto quando il formato Dolby Digital non è supportato. L'elenco dei decodificatori Android integrati utilizzati per trasformare uno stream audio in PCM è disponibile nell'articolo Formati multimediali supportati.
Al momento della riproduzione, l'app di streaming dovrebbe creare un elemento AudioTrack
con il migliore AudioFormat
supportato dal dispositivo audio di output.
Crea una traccia con il formato corretto
Le app dovrebbero creare un AudioTrack
, iniziare a riprodurlo e chiamare
getRoutedDevice()
per stabilire il dispositivo audio predefinito da cui riprodurre l'audio.
Potrebbe trattarsi, ad esempio, di una traccia codificata PCM di sicurezza breve silenzioso utilizzata solo per
determinare il dispositivo instradato e le sue funzionalità audio.
Ottieni codifiche supportate
Utilizza getAudioProfiles()
(livello API 31 e successivi) o getEncodings()
(livello API 23 e successivi) per determinare i formati audio disponibili sul dispositivo audio predefinito.
Controllare i formati e i profili audio supportati
Utilizza AudioProfile
(livello API 31 e successivi) o
isDirectPlaybackSupported()
(livello API 29 e successivi) per verificare le combinazioni supportate di formato,
conteggio canali e frequenza di campionamento.
Alcuni dispositivi Android sono in grado di supportare codifiche oltre a quelle supportate dal dispositivo audio di output. Questi formati aggiuntivi dovrebbero essere rilevati tramite isDirectPlaybackSupported()
. In questi casi, i dati audio vengono ricodificati in un formato supportato dal dispositivo audio di output. Utilizza isDirectPlaybackSupported()
per verificare correttamente il supporto del formato desiderato, anche se non è presente nell'elenco restituito da getEncodings()
.
Percorso audio previsto
Android 13 (livello API 33) ha introdotto i percorsi audio anticipato. Puoi
anticipare il supporto degli attributi audio del dispositivo e preparare le tracce per il
dispositivo audio attivo. Puoi utilizzare getDirectPlaybackSupport()
per verificare se la riproduzione diretta è supportata sul dispositivo audio attualmente indirizzato 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 chiedere quali profili sono supportati per la riproduzione diretta di contenuti multimediali tramite il dispositivo audio attualmente indirizzato. Sono esclusi eventuali profili che non sono supportati o che sarebbero, ad esempio, transcodificati dal framework Android:
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 di
istanze AudioFormat
. È ordinato con il più preferito per primo nell'elenco e il meno preferito per ultimo.
getDirectProfilesForAttributes()
restituisce un elenco di oggetti
AudioProfile
supportati per
il dispositivo audio attualmente instradato con il
AudioAttributes
fornito. L'elenco di
elementi AudioFormat
preferiti viene ripetuto fino a trovare un elemento AudioProfile
supportato corrispondente. Questo AudioProfile
è archiviato come bestAudioProfile
.
Le frequenze di campionamento e le maschere del canale ottimali vengono determinate a partire dal giorno bestAudioProfile
.
Infine, viene creata un'istanza AudioFormat
appropriata.
Crea traccia audio
Le app devono usare queste informazioni per creare un AudioTrack
per la
qualità massima supportata dal dispositivo audio (AudioFormat
) supportata dal dispositivo audio predefinito
(e disponibile per i contenuti selezionati).
Intercetta modifiche al dispositivo audio
Per intercettare e reagire alle modifiche audio del dispositivo, le app devono:
- Per livelli API pari o superiori a 24, aggiungi un elemento
OnRoutingChangedListener
per monitorare le modifiche del dispositivo audio (HDMI, Bluetooth e così via). - Per il livello API 23, registra un
AudioDeviceCallback
per ricevere le modifiche nell'elenco di dispositivi audio disponibili. - Per i livelli API 21 e 22, monitora gli eventi del connettore HDMI e utilizza i dati aggiuntivi delle trasmissioni.
- Registra anche un
BroadcastReceiver
per monitorare le modifiche dello stato diBluetoothDevice
per i dispositivi precedenti all'API 23, poichéAudioDeviceCallback
non è ancora supportato.
Quando viene rilevato un cambiamento del dispositivo audio per AudioTrack
, l'app deve controllare le funzionalità audio aggiornate e, se necessario, ricreare AudioTrack
con un AudioFormat
diverso. Scegli questa opzione se ora è supportata una codifica di qualità superiore o se la codifica utilizzata in precedenza non è più supportata.
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);