Android TV में एक साथ कई ऑडियो आउटपुट इस्तेमाल किए जा सकते हैं: टीवी स्पीकर, एचडीएमआई होम सिनेमा, ब्लूटूथ वगैरह. इन डिवाइसों पर अलग-अलग तरह की एन्कोडिंग (जैसे, Dolby Digital+, DTS, PCM), सैंपल रेट, और चैनल काम करते हैं. एचडीएमआई से कनेक्ट किए गए टीवी पर कई फ़ॉर्मैट काम करते हैं. हालांकि, ब्लूटूथ पर सिर्फ़ पीसीएम फ़ॉर्मैट काम करता है.
प्लेबैक के दौरान, उपलब्ध ऑडियो डिवाइस और राउटिंग में बदलाव हो सकता है: उपयोगकर्ता एचडीएमआई को हॉट-प्लग कर सकते हैं, ब्लूटूथ कनेक्ट कर सकते हैं या सेटिंग में बदलाव कर सकते हैं. ऐप्लिकेशन को नए डिवाइस की सुविधाओं के हिसाब से काम करना चाहिए, ताकि वीडियो लगातार चलता रहे. ऐसे फ़ॉर्मैट इस्तेमाल करने पर गड़बड़ियां हो सकती हैं या आवाज़ नहीं आ सकती है.
डिवाइस की क्षमताओं के आधार पर, बेहतरीन अनुभव देने के लिए कई एन्कोडिंग ऑफ़र करें. उदाहरण के लिए, अगर Dolby Digital काम करता है, तो इसका इस्तेमाल किया जाता है. अगर यह काम नहीं करता है, तो ऐप्लिकेशन PCM पर वापस आ जाता है. Android डिकोडर के लिए, इस्तेमाल किए जा सकने वाले मीडिया फ़ॉर्मैट देखें. ये डिकोडर, स्ट्रीम को पीसीएम में बदलते हैं.
प्लेबैक के समय, आपके स्ट्रीमिंग ऐप्लिकेशन को AudioTrack बनाना चाहिए. इसमें आउटपुट ऑडियो डिवाइस के साथ काम करने वाला सबसे अच्छा AudioFormat होना चाहिए.
सही फ़ॉर्मैट में ट्रैक बनाना
ऐप्लिकेशन को एक AudioTrack बनाना चाहिए, उसे चलाना चाहिए, और getRoutedDevice() को कॉल करना चाहिए, ताकि यह पता चल सके कि आवाज़ चलाने के लिए डिफ़ॉल्ट ऑडियो डिवाइस कौन-सा है. उदाहरण के लिए, यह एक सुरक्षित और कम समय का साइलेंस पीसीएम एन्कोड किया गया ट्रैक हो सकता है. इसका इस्तेमाल सिर्फ़ रूट किए गए डिवाइस और उसकी ऑडियो क्षमताओं का पता लगाने के लिए किया जाता है.
इस्तेमाल किए जा सकने वाले एन्कोडिंग के बारे में जानकारी पाना
डिफ़ॉल्ट ऑडियो डिवाइस पर उपलब्ध ऑडियो फ़ॉर्मैट का पता लगाने के लिए, getAudioProfiles() (एपीआई लेवल 31 और इसके बाद के लेवल) या getEncodings() (एपीआई लेवल 23 और इसके बाद के लेवल) का इस्तेमाल करें.
देखें कि ऑडियो प्रोफ़ाइलें और फ़ॉर्मैट काम करते हैं या नहीं
फ़ॉर्मैट, चैनल की संख्या, और सैंपल रेट के साथ काम करने वाले कॉम्बिनेशन की जांच करने के लिए, AudioProfile (एपीआई लेवल 31 और इसके बाद के वर्शन) या isDirectPlaybackSupported() (एपीआई लेवल 29 और इसके बाद के वर्शन) का इस्तेमाल करें.
कुछ Android डिवाइसों में, आउटपुट ऑडियो डिवाइस के साथ काम करने वाली एन्कोडिंग के अलावा अन्य एन्कोडिंग का इस्तेमाल किया जा सकता है. इन अतिरिक्त फ़ॉर्मैट का पता isDirectPlaybackSupported() के ज़रिए लगाया जाना चाहिए. ऐसे मामलों में, ऑडियो डेटा को उस फ़ॉर्मैट में फिर से कोड किया जाता है जो आउटपुट ऑडियो डिवाइस पर काम करता है. isDirectPlaybackSupported() का इस्तेमाल करके, चुने गए फ़ॉर्मैट के साथ काम करने की सुविधा की सही तरीके से जांच करें. भले ही, यह getEncodings() से मिली सूची में मौजूद न हो.
ऑडियो रूट का अनुमान लगाना
Android 13 (एपीआई लेवल 33) में, ऑडियो के अनुमानित रास्तों की सुविधा जोड़ी गई है. डिवाइस के ऑडियो एट्रिब्यूट के साथ काम करने की सुविधा का अनुमान लगाया जा सकता है. साथ ही, चालू ऑडियो डिवाइस के लिए ट्रैक तैयार किए जा सकते हैं. getDirectPlaybackSupport() का इस्तेमाल करके यह पता लगाया जा सकता है कि किसी फ़ॉर्मैट और एट्रिब्यूट के लिए, मौजूदा ऑडियो डिवाइस पर डायरेक्ट प्लेबैक की सुविधा काम करती है या नहीं:
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
}
इसके अलावा, यह क्वेरी की जा सकती है कि ऑडियो डिवाइस से सीधे मीडिया चलाने के लिए, कौनसी प्रोफ़ाइलें काम करती हैं. इसमें ऐसी प्रोफ़ाइलें शामिल नहीं हैं जो काम नहीं करती हैं. उदाहरण के लिए, 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();
}
इस उदाहरण में, preferredFormats, AudioFormat इंस्टेंस की सूची है.
इस सूची में, सबसे ज़्यादा पसंद की जाने वाली भाषा को सबसे पहले और सबसे कम पसंद की जाने वाली भाषा को सबसे आखिर में रखा जाता है. getDirectProfilesForAttributes(), दिए गए AudioAttributes के साथ, ऑडियो को रूट करने वाले मौजूदा ऑडियो डिवाइस के लिए, काम करने वाले AudioProfile ऑब्जेक्ट की सूची दिखाता है. जब तक मेल खाने वाला AudioProfile नहीं मिल जाता, तब तक पसंदीदा AudioFormat आइटम की सूची को दोहराया जाता है. इस AudioProfile को bestAudioProfile के तौर पर सेव किया जाता है. सैंपल रेट और चैनल मास्क की सबसे सही जानकारी bestAudioProfile से मिलती है. आखिर में, AudioFormat का सही इंस्टेंस बनाया जाता है.
ऑडियो ट्रैक बनाना
ऐप्लिकेशन को इस जानकारी का इस्तेमाल करके, डिफ़ॉल्ट ऑडियो डिवाइस के साथ काम करने वाले और चुने गए कॉन्टेंट के लिए उपलब्ध सबसे अच्छी क्वालिटी वाला AudioTrackAudioFormat बनाना चाहिए.
ऑडियो डिवाइस में किए गए बदलावों को इंटरसेप्ट करना
ऑडियो डिवाइस में होने वाले बदलावों को रोकने और उन पर प्रतिक्रिया देने के लिए, ऐप्लिकेशन को यह काम करना चाहिए:
- एपीआई लेवल 24 या इससे ज़्यादा के लिए, ऑडियो डिवाइस में होने वाले बदलावों (एचडीएमआई, ब्लूटूथ वगैरह) को मॉनिटर करने के लिए,
OnRoutingChangedListenerजोड़ें. - एपीआई लेवल 23 के लिए, उपलब्ध ऑडियो डिवाइस की सूची में बदलावों की सूचना पाने के लिए,
AudioDeviceCallbackरजिस्टर करें. - एपीआई लेवल 21 और 22 के लिए, एचडीएमआई प्लग इवेंट को मॉनिटर करें और ब्रॉडकास्ट से मिले अतिरिक्त डेटा का इस्तेमाल करें.
- साथ ही, एपीआई लेवल 23 से पहले के लेवल वाले डिवाइसों के लिए,
BluetoothDeviceकी स्थिति में होने वाले बदलावों को मॉनिटर करने के लिए,BroadcastReceiverरजिस्टर करें. ऐसा इसलिए, क्योंकिAudioDeviceCallbackकी सुविधा अभी उपलब्ध नहीं है.
जब AudioTrack के लिए ऑडियो डिवाइस में बदलाव का पता चलता है, तो ऐप्लिकेशन को ऑडियो की अपडेट की गई क्षमताओं की जांच करनी चाहिए. अगर ज़रूरी हो, तो उसे किसी दूसरे AudioFormat के साथ AudioTrack को फिर से बनाना चाहिए. ऐसा तब करें, जब अब बेहतर क्वालिटी वाली एन्कोडिंग काम करती हो या पहले इस्तेमाल की गई एन्कोडिंग अब काम नहीं करती हो.
सैंपल कोड
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);