Perangkat Android TV dapat memiliki beberapa output audio yang terhubung secara bersamaan: speaker TV, bioskop rumah yang terhubung ke HDMI, headphone Bluetooth, dan sebagainya. Perangkat output audio ini dapat mendukung berbagai kemampuan audio, seperti encoding (Dolby Digital+, DTS, dan PCM), frekuensi sampel, dan saluran. Misalnya, TV yang terhubung ke HDMI memiliki dukungan untuk banyak encoding, sedangkan headphone Bluetooth yang terhubung biasanya hanya mendukung PCM.
Daftar perangkat audio yang tersedia dan perangkat audio yang dirutekan juga dapat berubah dengan melakukan hot pluging pada perangkat HDMI, menghubungkan atau memutuskan sambungan headphone Bluetooth, atau pengguna mengubah setelan audio. Karena kemampuan output audio dapat berubah bahkan saat aplikasi memutar media, aplikasi harus beradaptasi dengan baik dengan perubahan ini dan melanjutkan pemutaran pada perangkat audio yang dirutekan baru dan kemampuannya. Menghasilkan format audio yang salah dapat menyebabkan error atau tidak ada suara yang diputar.
Aplikasi memiliki kemampuan untuk menghasilkan konten yang sama dalam beberapa encoding untuk menawarkan pengalaman audio terbaik kepada pengguna, bergantung pada kemampuan perangkat audio. Misalnya, streaming audio yang dienkode ke Dolby Digital akan diputar jika TV mendukungnya, sedangkan streaming audio PCM yang lebih banyak didukung akan dipilih saat tidak ada dukungan untuk Dolby Digital. Daftar dekoder Android bawaan yang digunakan untuk mengubah streaming audio menjadi PCM dapat ditemukan di Format media yang didukung.
Pada waktu pemutaran, aplikasi streaming harus membuat
AudioTrack
dengan
AudioFormat
terbaik yang didukung oleh perangkat audio
output.
Buat trek dengan format yang tepat
Aplikasi harus membuat AudioTrack
, mulai memutarnya, dan memanggil
getRoutedDevice()
untuk menentukan perangkat audio default yang digunakan untuk memutar suara.
Ini dapat berupa, misalnya, trek berenkode PCM sunyi singkat yang aman dan hanya digunakan untuk menentukan perangkat yang dirutekan dan kemampuan audionya.
Mendapatkan encoding yang didukung
Gunakan
getAudioProfiles()
(API level 31 dan yang lebih tinggi) atau
getEncodings()
(API level 23 dan yang lebih tinggi) untuk menentukan format audio yang tersedia di
perangkat audio default.
Memeriksa profil dan format audio yang didukung
Gunakan AudioProfile
(level API 31 dan yang lebih tinggi) atau
isDirectPlaybackSupported()
(level API 29 dan yang lebih tinggi) untuk memeriksa kombinasi format,
jumlah saluran, dan frekuensi sampel yang didukung.
Beberapa perangkat Android mampu mendukung encoding selain
yang didukung oleh perangkat audio output. Format tambahan ini akan
terdeteksi melalui isDirectPlaybackSupported()
. Dalam hal ini, data audio
dienkode ulang ke format yang didukung oleh perangkat audio output. Gunakan
isDirectPlaybackSupported()
untuk memeriksa dukungan dengan benar untuk format yang diinginkan
meskipun tidak ada dalam daftar yang ditampilkan oleh getEncodings()
.
Rute audio antisipatif
Android 13 (API level 33) memperkenalkan rute audio antisipatif. Anda dapat
mengantisipasi dukungan atribut audio perangkat dan menyiapkan trek untuk perangkat audio
aktif. Anda dapat menggunakan
getDirectPlaybackSupport()
untuk memeriksa apakah pemutaran langsung didukung pada perangkat audio yang saat ini dirutekan untuk format dan atribut tertentu:
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 }
Atau, Anda dapat mengkueri profil mana yang didukung untuk pemutaran media langsung melalui perangkat audio yang saat ini diarahkan. Tindakan ini tidak mencakup profil yang tidak didukung atau yang, misalnya, akan di-transcoding oleh 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(); }
Dalam contoh ini, preferredFormats
adalah daftar
instance AudioFormat
. Hal ini diurutkan
dari yang paling disukai terlebih dahulu dalam daftar, dan yang paling tidak disukai terakhir.
getDirectProfilesForAttributes()
menampilkan daftar objek
AudioProfile
yang didukung untuk
perangkat audio yang saat ini dirutekan dengan AudioAttributes
yang disediakan. Daftar
item AudioFormat
pilihan akan diiterasi sampai ditemukan AudioProfile
yang didukung
dan cocok. AudioProfile
ini disimpan sebagai bestAudioProfile
.
Frekuensi sampel optimal dan mask saluran ditentukan dari bestAudioProfile
.
Terakhir, instance AudioFormat
yang sesuai akan dibuat.
Buat trek audio
Aplikasi harus menggunakan informasi ini guna membuat AudioTrack
untuk
AudioFormat
berkualitas tertinggi yang didukung oleh perangkat audio default
(dan tersedia untuk konten yang dipilih).
Mencegah perubahan perangkat audio
Untuk mencegat dan bereaksi terhadap perubahan perangkat audio, aplikasi harus:
- Untuk level API yang sama dengan atau lebih besar dari 24, tambahkan
OnRoutingChangedListener
untuk memantau perubahan perangkat audio (HDMI, Bluetooth, dan sebagainya). - Untuk level API 23, daftarkan
AudioDeviceCallback
untuk menerima perubahan dalam daftar perangkat audio yang tersedia. - Untuk level API 21 dan 22, pantau peristiwa steker HDMI dan gunakan data tambahan dari siaran.
- Daftarkan juga
BroadcastReceiver
untuk memantau perubahan statusBluetoothDevice
untuk perangkat yang lebih rendah dari API 23, karenaAudioDeviceCallback
belum didukung.
Saat perubahan perangkat audio terdeteksi untuk AudioTrack
, aplikasi
harus memeriksa kemampuan audio yang telah diperbarui dan, jika diperlukan, membuat ulang
AudioTrack
dengan AudioFormat
yang berbeda. Lakukan hal ini jika encoding dengan kualitas lebih tinggi
sekarang didukung atau encoding yang sebelumnya digunakan
tidak lagi didukung.
Kode contoh
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);