Los dispositivos Android TV pueden tener varias salidas de audio conectadas al mismo tiempo: bocinas para TV, cine en casa conectado a HDMI, auriculares Bluetooth, etcétera. Los dispositivos de salida de audio admiten distintas capacidades de audio, como las codificaciones (Dolby Digital+, DTS y PCM), la tasa de muestreo y los canales. Por ejemplo, los televisores conectados a HDMI admiten una gran variedad de codificaciones. mientras que los auriculares Bluetooth conectados generalmente admiten solo PCM.
La lista de dispositivos de audio disponibles y el dispositivo de audio enrutado también pueden cambiar conectando dispositivos HDMI en caliente, conectando o desconectando auriculares Bluetooth o que el usuario cambie la configuración de audio. Dado que las capacidades de salida de audio pueden incluso cuando las apps reproducen contenido multimedia, necesitan adaptarse correctamente a estos y continúa la reproducción en el nuevo dispositivo de audio enrutado y su capacidades de integración. Si se emite un formato de audio incorrecto, se pueden producir errores No se reproduce sonido.
Las apps pueden mostrar el mismo contenido en varias codificaciones para ofrecer al usuario la mejor experiencia de audio según el dispositivo de audio capacidades de integración. Por ejemplo, se reproduce una transmisión de audio codificada en Dolby Digital. si la TV lo admite, mientras que una transmisión de audio PCM compatible cuando no hay compatibilidad con Dolby Digital. La lista de aplicaciones de Android de codificadores que se usan para transformar una transmisión de audio en PCM, Formatos multimedia compatibles.
En el momento de la reproducción, la app de streaming debe crear una
AudioTrack
con los mejores
AudioFormat
compatible con la salida
dispositivo de audio.
Crea una pista con el formato adecuado
Las apps deben crear un elemento AudioTrack
, comenzar a reproducirlo y realizar la llamada
getRoutedDevice()
para determinar el dispositivo de audio predeterminado desde el que se reproduce el sonido.
Puede ser, por ejemplo, una pista segura codificada en PCM de silencio corto que se usa solo para
determinar el dispositivo enrutado y sus capacidades de audio.
Obtén codificaciones compatibles
Usa
getAudioProfiles()
(nivel de API 31 y versiones posteriores) o
getEncodings()
(nivel de API 23 y versiones posteriores) para determinar los formatos de audio disponibles en
dispositivo de audio predeterminado.
Verifica los formatos y perfiles de audio compatibles
Usa AudioProfile
.
(nivel de API 31 y versiones posteriores) o
isDirectPlaybackSupported()
(nivel de API 29 y versiones posteriores) para verificar las combinaciones de formato admitidas.
el recuento de canales
y la tasa de muestreo.
Algunos dispositivos Android son capaces de admitir codificaciones más allá de las que
compatible con el dispositivo de audio de salida. Estos formatos adicionales deben ser
detectado a través de isDirectPlaybackSupported()
. En estos casos, los datos de audio
se vuelve a codificar a un formato compatible con el dispositivo de audio de salida. Usa
isDirectPlaybackSupported()
para verificar correctamente la compatibilidad con el formato deseado
incluso si no está presente en la lista que muestra getEncodings()
.
Vía de audio anticipada
Android 13 (nivel de API 33) introdujo rutas de audio anticipadas. Puedes
anticipar la compatibilidad con atributos de audio del dispositivo y preparar pistas para la
dispositivo de audio. Puedes usar
getDirectPlaybackSupport()
para comprobar si la reproducción directa es compatible con el audio enrutado actualmente
dispositivo para un formato y atributos determinados:
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 }
Como alternativa, puedes consultar qué perfiles son compatibles con contenido multimedia directo la reproducción a través del dispositivo de audio enrutado actualmente. Esto excluye todos los perfiles que no son compatibles o que se transcodificarán, por ejemplo, Android en la nube:
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(); }
En este ejemplo, preferredFormats
es una lista de
AudioFormat
instancias. Se ordena
con los más preferidos primero en la lista y los menos preferidos en último.
getDirectProfilesForAttributes()
devuelve una lista de las solicitudes
AudioProfile
para el
en el dispositivo de audio enrutado actualmente con el
AudioAttributes
La lista de
de elementos AudioFormat
preferidos se itera hasta que se admita
Se encontró AudioProfile
. Este AudioProfile
se almacenó como bestAudioProfile
.
Las tasas de muestreo óptimas y las máscaras de canal se determinan a partir del bestAudioProfile
.
Por último, una AudioFormat
adecuada
se crea una instancia de VM.
Crear pista de audio
Las apps deben usar esta información para crear un AudioTrack
para el
el AudioFormat
de mayor calidad compatible con el dispositivo de audio predeterminado
(y disponible para el contenido seleccionado).
Interceptar cambios en el dispositivo de audio
Para interceptar los cambios en el dispositivo de audio y reaccionar a ellos, las apps deben hacer lo siguiente:
- Para los niveles de API iguales o superiores a 24, agrega un
OnRoutingChangedListener
para supervisar cambios en el dispositivo de audio (HDMI, Bluetooth, etc.). - En el caso del nivel de API 23, registra un
AudioDeviceCallback
para recibir cambios en la lista de dispositivos de audio disponibles. - Para los niveles de API 21 y 22, supervisa Eventos de tomacorrientes HDMI y usar los datos adicionales de las transmisiones.
- También registra un
BroadcastReceiver
para supervisarlo Cambios de estado deBluetoothDevice
para dispositivos con un nivel de API inferior al 23, ya queAudioDeviceCallback
no es todavía es compatible.
Cuando se detecta un cambio en el dispositivo de audio para AudioTrack
, la app
debe comprobar las capacidades de audio actualizadas y, si es necesario, recrear
el AudioTrack
con un AudioFormat
diferente. Haz esto si una imagen de mayor
ahora se admite la codificación o la que se usaba anteriormente
ya no es compatible.
Código de muestra
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);