Android TV, aynı anda birden fazla ses çıkışını destekler: TV hoparlörleri, HDMI ev sineması, Bluetooth ve daha fazlası. Bu cihazlar farklı kodlamaları (ör. Dolby Digital+, DTS, PCM), örnekleme 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 cihazları 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 hataya 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.
Çalma 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ı, bunu çalmaya başlamalı ve sesi hangi varsayılan ses sisteminden çalacağını belirlemek için getRoutedDevice() işlevini çağırmalıdır. Örneğin, bu, yalnızca yönlendirilen cihazı ve ses özelliklerini belirlemek için kullanılan güvenli, kısa ve sessiz bir PCM kodlu parça 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 örnekleme 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 cihazı tarafından desteklenenlerin ötesinde 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çimde yeniden kodlanır. getEncodings() tarafından döndürülen listede olmasa bile
seçilen biçimin desteğini düzgün şekilde kontrol etmek için isDirectPlaybackSupported()
kullanın.
Beklentiye dayalı sesli rota
Android 13 (API düzeyi 33) ile birlikte öngörülü ses rotaları kullanıma sunuldu. Cihazın cihazın sesi özelliği desteğini önceden tahmin edebilir ve etkin ses sistemi için parçalar hazırlayabilirsiniz. Belirli bir biçim ve özellik için şu anda yönlendirilen ses sisteminde doğrudan oynatmanın desteklenip desteklenmediğini kontrol etmek için getDirectPlaybackSupport() aracını 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 örneğin Android çerçevesi tarafından 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 listesidir.
Liste, en çok tercih edilenden en az tercih edilene doğru sıralanır. getDirectProfilesForAttributes(), şu anda yönlendirilen ses sistemi için desteklenen AudioProfile nesnelerin listesini, sağlanan AudioAttributes ile birlikte döndürür. Tercih edilen AudioFormat öğelerinin listesi, eşleşen desteklenen bir AudioProfile bulunana kadar yinelenir. Bu AudioProfile, bestAudioProfile olarak saklanır. Optimum örnekleme hızları ve kanal maskeleri bestAudioProfile ile 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 yakalama
Uygulamaların ses cihazı değişikliklerini yakalayıp bunlara tepki verebilmesi için:
- API düzeyi 24 veya daha yüksek olan uygulamalarda ses sistemi değişikliklerini (HDMI, Bluetooth vb.) izlemek için
OnRoutingChangedListenerekleyin. - API düzeyi 23 için, kullanılabilir ses sistemi listesindeki değişiklikleri almak üzere
AudioDeviceCallbackkaydettirin. - API düzeyleri 21 ve 22 için HDMI takma etkinliklerini izleyin ve yayınlardaki ek verileri kullanın.
- Ayrıca,
AudioDeviceCallbackhenüz desteklenmediğinden API 23'ten eski cihazlardaBluetoothDevicedurum değişikliklerini izlemek için birBroadcastReceiverkaydedin.
AudioTrack için bir ses sistemi değişikliği algılandığında uygulama, güncellenen ses özelliklerini kontrol etmeli ve gerekirse farklı bir AudioFormat ile AudioTrack öğesini yeniden oluşturmalıdır. Daha yüksek kaliteli 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);