Android TV mendukung beberapa output audio serentak: speaker TV, home cinema HDMI, Bluetooth, dan lainnya. Perangkat ini mendukung berbagai encoding (seperti Dolby Digital+, DTS, PCM), kecepatan sampel, dan saluran. TV yang terhubung melalui HDMI mendukung banyak format, tetapi Bluetooth hanya mendukung PCM.
Perangkat audio dan perutean yang tersedia dapat berubah selama pemutaran: pengguna dapat mencolokkan HDMI saat perangkat dalam kondisi panas, menghubungkan Bluetooth, atau mengubah setelan. Aplikasi harus beradaptasi dengan baik untuk memastikan pemutaran berkelanjutan menggunakan kemampuan perangkat baru. Format yang tidak didukung dapat menyebabkan error atau tidak ada suara.
Menawarkan beberapa encoding untuk pengalaman terbaik berdasarkan kemampuan perangkat. Misalnya, Dolby Digital digunakan jika didukung; jika tidak, aplikasi akan kembali ke PCM. Lihat Format media yang didukung untuk dekoder Android yang mengubah streaming menjadi PCM.
Pada waktu pemutaran, aplikasi streaming Anda 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 akan
memutar suara. Hal ini dapat berupa, misalnya, trek yang dienkode PCM dengan keheningan singkat yang aman
yang hanya digunakan untuk menentukan perangkat yang dirutekan dan kemampuan audionya.
Mendapatkan encoding yang didukung
Gunakan getAudioProfiles() (level API 31 dan yang lebih tinggi) atau getEncodings() (level API 23 dan yang lebih tinggi) untuk menentukan format audio yang tersedia di perangkat audio default.
Memeriksa format dan profil audio yang didukung
Gunakan AudioProfile (API level 31 dan yang lebih tinggi) atau
isDirectPlaybackSupported() (API level 29 dan yang lebih tinggi) untuk memeriksa kombinasi format, jumlah saluran, dan kecepatan sampel yang didukung.
Beberapa perangkat Android dapat mendukung encoding di luar yang didukung oleh perangkat audio output. Format tambahan ini harus
dideteksi melalui isDirectPlaybackSupported(). Dalam kasus ini, data audio dienkode ulang ke format yang didukung oleh perangkat audio output. Gunakan
isDirectPlaybackSupported() untuk memeriksa dukungan dengan benar untuk format yang dipilih
meskipun tidak ada dalam daftar yang ditampilkan oleh getEncodings().
Rute audio antisipatif
Android 13 (level API 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 di 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 membuat kueri profil mana yang didukung untuk pemutaran media langsung melalui perangkat audio yang saat ini dirutekan. Hal ini mengecualikan profil apa pun yang tidak didukung atau, misalnya, akan ditranskode 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.
Bahasa ini diurutkan dengan bahasa yang paling disukai di urutan pertama dalam daftar, dan bahasa yang paling tidak disukai di urutan terakhir. getDirectProfilesForAttributes() menampilkan daftar objek
AudioProfile yang didukung untuk perangkat audio yang saat ini dirutekan dengan
AudioAttributes yang diberikan. Daftar item AudioFormat pilihan diiterasi hingga AudioProfile yang cocok dan didukung ditemukan. AudioProfile ini disimpan sebagai bestAudioProfile. Frekuensi sampling dan mask channel optimal ditentukan dari bestAudioProfile. Terakhir, instance
AudioFormat yang sesuai dibuat.
Membuat trek audio
Aplikasi harus menggunakan informasi ini untuk membuat AudioTrack untuk
AudioFormat berkualitas tertinggi yang didukung oleh perangkat audio default (dan
tersedia untuk konten yang dipilih).
Menyadap 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
OnRoutingChangedListeneruntuk memantau perubahan perangkat audio (HDMI, Bluetooth, dan sebagainya). - Untuk level API 23, daftarkan
AudioDeviceCallbackuntuk menerima perubahan dalam daftar perangkat audio yang tersedia. - Untuk level API 21 dan 22, pantau peristiwa pencabutan HDMI dan gunakan data tambahan dari siaran.
- Daftarkan juga
BroadcastReceiveruntuk memantau perubahan statusBluetoothDeviceuntuk perangkat yang lebih rendah dari API 23, karenaAudioDeviceCallbackbelum didukung.
Jika perubahan perangkat audio telah terdeteksi untuk AudioTrack, aplikasi
harus memeriksa kemampuan audio yang diperbarui dan, jika perlu, membuat ulang
AudioTrack dengan AudioFormat yang berbeda. Lakukan hal ini jika encoding berkualitas lebih tinggi kini didukung atau encoding yang digunakan sebelumnya 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);