I dispositivi Android TV possono avere più uscite audio collegate contemporaneamente: altoparlanti TV, home cinema collegato tramite HDMI, cuffie Bluetooth e così via. Questi dispositivi di uscita audio possono supportare diverse funzionalità audio, come codifiche (Dolby Digital+, DTS e PCM), frequenza di campionamento e canali. Ad esempio, le TV collegate tramite HDMI supportano una moltitudine di codifiche, mentre le cuffie Bluetooth collegate di solito supportano solo PCM.
L'elenco dei dispositivi audio disponibili e il dispositivo audio con routing possono anche cambiare quando si collegano a caldo dispositivi HDMI, si collegano o si scollegano le cuffie Bluetooth o l'utente modifica le impostazioni audio. Poiché le funzionalità di uscita audio possono cambiare anche durante la riproduzione di contenuti multimediali da parte delle app, queste devono adattarsi gradualmente a queste modifiche e continuare la riproduzione sul nuovo dispositivo audio con routing e sulle relative funzionalità. L'output del formato audio errato può causare errori o la mancata riproduzione dell'audio.
Le app hanno la possibilità di generare lo stesso contenuto in più codifiche per offrire all'utente la migliore esperienza audio a seconda delle funzionalità del dispositivo audio. Ad esempio, viene riprodotto uno stream audio codificato Dolby Digital se la TV lo supporta, mentre viene scelto uno stream audio PCM più ampiamente supportato quando non è disponibile il supporto per Dolby Digital. L'elenco dei decoder Android integrati utilizzati per trasformare uno stream audio in PCM è disponibile in Formati multimediali supportati.
Al momento della riproduzione, l'app di streaming deve creare un
AudioTrack con il miglior
AudioFormat supportato dal dispositivo audio di output.
Creare una traccia con il formato corretto
Le app devono creare un AudioTrack, avviare la riproduzione e chiamare
getRoutedDevice()
per determinare il dispositivo audio predefinito da cui riprodurre l'audio.
Ad esempio, può trattarsi di una traccia codificata PCM con un breve silenzio sicuro utilizzata solo per determinare il dispositivo con routing e le relative funzionalità audio.
Ottenere le codifiche supportate
Utilizza
getAudioProfiles()
(livello API 31 e versioni successive) o
getEncodings()
(livello API 23 e versioni successive) per determinare i formati audio disponibili sul
dispositivo audio predefinito.
Controllare i profili e i formati audio supportati
Utilizza AudioProfile
(livello API 31 e versioni successive) o
isDirectPlaybackSupported()
(livello API 29 e versioni successive) per controllare le combinazioni supportate di formato,
numero di canali e frequenza di campionamento.
Alcuni dispositivi Android sono in grado di supportare codifiche diverse da quelle supportate dal dispositivo audio di output. Questi formati aggiuntivi devono 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 per il formato desiderato, anche se non è presente nell'elenco restituito da getEncodings().
Percorso audio anticipatorio
Android 13 (livello API 33) ha introdotto i percorsi audio anticipatori. Puoi prevedere 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 con routing corrente
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 per scoprire quali profili sono supportati per la riproduzione diretta dei contenuti multimediali tramite il dispositivo audio con routing corrente. In questo modo vengono esclusi tutti i profili non supportati o che, ad esempio, verrebbero 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
AudioFormat istanze. L'elenco è ordinato in modo che il formato preferito sia il primo e quello meno preferito l'ultimo.
getDirectProfilesForAttributes()
restituisce un elenco di oggetti supportati
AudioProfile per il dispositivo audio con routing corrente con i
AudioAttributes forniti. L'elenco degli elementi AudioFormat preferiti viene iterato fino a quando non viene trovato un AudioProfile supportato corrispondente. Questo AudioProfile viene memorizzato come bestAudioProfile.
Le frequenze di campionamento e le maschere di canale ottimali vengono determinate da bestAudioProfile.
Infine, viene creata un'istanza AudioFormat
appropriata.
Creare una traccia audio
Le app devono utilizzare queste informazioni per creare un AudioTrack per AudioFormat di massima qualità supportato dal dispositivo audio predefinito (e disponibile per i contenuti selezionati).
Intercettare le modifiche del dispositivo audio
Per intercettare le modifiche del dispositivo audio e reagire a queste, le app devono:
- Per i livelli API pari o superiori a 24, aggiungi un
OnRoutingChangedListenerper monitorare le modifiche del dispositivo audio (HDMI, Bluetooth e così via). - Per il livello API 23, registra un
AudioDeviceCallbackper ricevere le modifiche nell'elenco dei dispositivi audio disponibili. - Per i livelli API 21 e 22, monitora gli eventi di collegamento HDMI e utilizza i dati extra delle trasmissioni.
- Registra anche un
BroadcastReceiverper monitorareBluetoothDevicemodifiche dello stato per i dispositivi con livello API inferiore a 23, poichéAudioDeviceCallbacknon è ancora supportato.
Quando viene rilevata una modifica del dispositivo audio per AudioTrack, l'app deve controllare le funzionalità audio aggiornate e, se necessario, ricreare AudioTrack con un AudioFormat diverso. Esegui questa operazione se ora è supportata una codifica di qualità superiore o se la codifica utilizzata in precedenza non è più supportata.
Codice campione
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);