Ses özellikleri

Android TV cihazlara aynı anda birden fazla ses çıkışı bağlanabilir: TV hoparlörleri, HDMI ile bağlı ev sineması, Bluetooth kulaklıklar vb. Bu ses çıkışı cihazları, kodlamalar (Dolby Digital+, DTS ve PCM), örnekleme hızı ve kanallar gibi farklı ses özelliklerini destekleyebilir. Örneğin, HDMI ile bağlı TV'ler çok sayıda kodlamayı desteklerken Bluetooth ile bağlı kulaklıklar genellikle yalnızca PCM'yi destekler.

Kullanılabilir ses cihazlarının listesi ve yönlendirilen ses cihazı, HDMI cihazların çalışırken takılması, Bluetooth kulaklıkların bağlanması veya bağlantısının kesilmesi ya da kullanıcının ses ayarlarını değiştirmesiyle de değişebilir. Ses çıkışı özellikleri, uygulamalar medya oynatırken bile değişebileceğinden uygulamaların bu değişikliklere sorunsuz bir şekilde uyum sağlaması ve yeni yönlendirilen ses cihazında ve özelliklerinde oynatmaya devam etmesi gerekir. Yanlış ses biçiminin çıkışı, hatalara veya sesin çalınmamasına neden olabilir.

Uygulamalar, ses cihazı özelliklerine bağlı olarak kullanıcıya en iyi ses deneyimini sunmak için aynı içeriği birden fazla kodlamada çıkışa aktarabilir. Örneğin, TV destekliyorsa Dolby Digital ile kodlanmış bir ses akışı oynatılır. Dolby Digital desteklenmiyorsa daha yaygın olarak desteklenen bir PCM ses akışı seçilir. Ses akışını PCM'ye dönüştürmek için kullanılan yerleşik Android kod çözücülerin listesini Desteklenen medya biçimleri bölümünde bulabilirsiniz.

Oynatma sırasında akış uygulaması, çıkış ses cihazı 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 çalmak için varsayılan ses cihazını belirlemek üzere 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 cihazında kullanılabilen ses biçimlerini belirlemek için getAudioProfiles() (API düzeyi 31 ve üstü) veya getEncodings() (API düzeyi 23 ve üstü) 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 üstü) veya isDirectPlaybackSupported() (API düzeyi 29 ve üstü) 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 cihazı tarafından desteklenen bir biçimde yeniden kodlanır. İstenen biçim getEncodings() tarafından döndürülen listede yer almasa bile bu biçimin desteklenip desteklenmediğini doğru ş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 ses özelliği desteğini önceden tahmin edebilir ve etkin ses cihazı için parçalar hazırlayabilirsiniz. Belirli bir biçim ve özellikler için şu anda yönlendirilen ses cihazında doğrudan oynatmanın desteklenip desteklenmediğini kontrol etmek üzere getDirectPlaybackSupport() 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 cihazı ü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ülen 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 edilen ilk sırada, en az tercih edilen ise son sırada olacak şekilde sıralanır. getDirectProfilesForAttributes() sağlanan AudioAttributes ile şu anda yönlendirilen ses cihazı için desteklenen AudioProfile nesnelerinin listesini döndürür. Tercih edilen AudioFormat öğelerin listesi, eşleşen desteklenen bir AudioProfile bulunana kadar yinelenir. Bu AudioProfile, bestAudioProfile olarak saklanır. Optimum örnekleme hızları ve kanal maskeleri bestAudioProfile'dan 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) AudioTrack en yüksek kalitede AudioFormat oluşturmak için bu bilgileri kullanmalıdır.

Ses cihazı değişikliklerini yakalama

Uygulamaların ses cihazı değişikliklerini yakalayıp bunlara tepki verebilmesi için:

  • API seviyesi 24 veya daha yüksek olan cihazlarda ses cihazı değişikliklerini (HDMI, Bluetooth vb.) izlemek için OnRoutingChangedListener ekleyin.
  • API düzeyi 23 için, kullanılabilir ses cihazı listesindeki değişiklikleri almak üzere bir AudioDeviceCallback kaydedin.
  • API düzeyleri 21 ve 22 için HDMI takma etkinliklerini izleyin ve yayınlardaki ek verileri kullanın.
  • Ayrıca, BroadcastReceiver kaydederek API 23'ten düşük cihazlarda BluetoothDevice durum değişikliklerini izleyin. Çünkü AudioDeviceCallback henüz desteklenmemektedir.

AudioTrack için bir ses cihazı değişikliği algılandığında uygulama, güncellenen ses özelliklerini kontrol etmeli ve gerekirse AudioTrack'yı farklı bir AudioFormat ile 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);