Android TV, birden fazla eşzamanlı ses çıkışını destekler: TV hoparlörleri, HDMI ev sineması, Bluetooth ve daha fazlası. Bu cihazlar farklı kodlamaları (ör. Dolby Digital+, DTS, PCM), örnek hızlarını ve kanalları destekler. HDMI ile bağlı TV'ler birçok biçimi destekler ancak Bluetooth yalnızca PCM'yi destekler.
Kullanılabilir ses sistemleri ve yönlendirme, oynatma sırasında değişebilir: Kullanıcılar HDMI'yı çalışırken takabilir, Bluetooth'u bağlayabilir veya ayarları değiştirebilir. Uygulamalar, yeni cihazın özelliklerini kullanarak kesintisiz oynatma sağlamak için sorunsuz bir şekilde uyum sağlamalıdır. Desteklenmeyen biçimler, hatalara veya sessizliğe neden olabilir.
Cihaz özelliklerine göre en iyi deneyimi sunmak için birden fazla kodlama sunun. Örneğin, destekleniyorsa Dolby Digital kullanılır, aksi takdirde uygulama PCM'ye geri döner. Akışları PCM'ye dönüştüren Android kod çözücüler için Desteklenen medya biçimleri bölümüne bakın.
Oynatma sırasında yayın uygulamanız, çıkış ses sistemi tarafından desteklenen en iyi AudioFormat ile bir AudioTrack oluşturmalıdır.
Doğru biçimde parça oluşturma
Uygulamalar bir AudioTrack oluşturmalı, oynatmaya başlamalı ve sesin çalınacağı varsayılan ses sistemini belirlemek için getRoutedDevice() yöntemini çağırmalıdır. Bu, yalnızca yönlendirilen cihazı ve ses özelliklerini belirlemek için kullanılan, PCM ile kodlanmış güvenli ve kısa bir sessizlik parçası olabilir.
Desteklenen kodlamaları alma
Varsayılan ses sisteminde kullanılabilen ses biçimlerini belirlemek için getAudioProfiles() (API düzeyi 31 ve sonraki sürümler) veya getEncodings() (API düzeyi 23 ve sonraki sürümler) kullanın.
Desteklenen ses profillerini ve biçimlerini kontrol etme
Desteklenen biçim, kanal sayısı ve örnek hızı kombinasyonlarını kontrol etmek için AudioProfile (API düzeyi 31 ve üzeri) veya isDirectPlaybackSupported() (API düzeyi 29 ve üzeri) kullanın.
Bazı Android cihazlar, çıkış ses sistemi tarafından desteklenenlerin dışındaki kodlamaları destekleyebilir. Bu ek biçimler isDirectPlaybackSupported() aracılığıyla algılanmalıdır. Bu durumlarda ses verileri, çıkış ses sistemi tarafından desteklenen bir biçime yeniden kodlanır. getEncodings() tarafından döndürülen listede yer almasa bile seçilen biçimin desteklenip desteklenmediğini doğru şekilde kontrol etmek için isDirectPlaybackSupported() kullanın.
Öngörülebilir ses rotası
Android 13 (API düzeyi 33), tahmini ses rotalarını kullanıma sundu. Cihazın ses özelliği desteğini tahmin edebilir ve etkin ses sistemi için parçalar hazırlayabilirsiniz. Belirli bir biçim ve özellikler için, yönlendirilen ses sisteminde doğrudan oynatmanın desteklenip desteklenmediğini kontrol etmek üzere getDirectPlaybackSupport() yöntemini kullanabilirsiniz:
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
}
Alternatif olarak, şu anda yönlendirilen ses sistemi üzerinden doğrudan medya oynatma için hangi profillerin desteklendiğini sorgulayabilirsiniz. Bu, desteklenmeyen veya Android çerçevesi tarafından kodu dönüştürülecek profilleri kapsamaz:
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();
}
Bu örnekte preferredFormats, AudioFormat örneklerinin bir listesidir.
En çok tercih edilen ilk sırada, en az tercih edilen ise son sırada yer alır. getDirectProfilesForAttributes(), sağlanan AudioAttributes ile o anda yönlendirilen ses sistemi için desteklenen AudioProfile nesnelerinin listesini döndürür. Tercih edilen AudioFormat öğelerinin listesi, eşleşen ve desteklenen bir AudioProfile bulunana kadar tekrarlanır. Bu AudioProfile, bestAudioProfile olarak saklanır. Optimum örnek hızları ve kanal maskeleri bestAudioProfile üzerinden belirlenir. Son olarak, uygun bir AudioFormat örneği oluşturulur.
Ses parçası oluşturma
Uygulamalar, varsayılan ses cihazı tarafından desteklenen (ve seçilen içerik için kullanılabilen) en yüksek kaliteli AudioFormat için AudioTrack oluşturmak üzere bu bilgileri kullanmalıdır.
Ses sistemi değişikliklerini engelleme
Uygulamalar, ses sistemi değişikliklerini yakalamak ve bunlara tepki vermek için şunları yapmalıdır:
- 24'e eşit veya daha yüksek API düzeyleri için ses sistemi değişikliklerini (HDMI, Bluetooth vb.) izlemek üzere bir
OnRoutingChangedListenerekleyin. - API düzeyi 23 için, kullanılabilir ses sistemi listesindeki değişiklikleri almak üzere bir
AudioDeviceCallbackkaydedin. - API düzeyi 21 ve 22 için HDMI fiş etkinliklerini izleyin ve yayınlardan gelen ek verileri kullanın.
AudioDeviceCallbackhenüz desteklenmediğinden, API 23'ten düşük cihazlardaBluetoothDevicedurum değişikliklerini izlemek için birBroadcastReceiverde kaydedin.
AudioTrack için bir ses sistemi değişikliği algılandığında uygulama, güncellenen ses özelliklerini kontrol etmeli ve gerekirse AudioTrack öğesini farklı bir AudioFormat ile yeniden oluşturmalıdır. Daha yüksek kaliteli bir kodlama artık destekleniyorsa veya daha önce kullanılan kodlama artık desteklenmiyorsa bu işlemi yapın.
Örnek kod
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);