Android TV ডিভাইসে একই সময়ে একাধিক অডিও আউটপুট সংযুক্ত থাকতে পারে: টিভি স্পিকার, HDMI-সংযুক্ত হোম সিনেমা, ব্লুটুথ হেডফোন ইত্যাদি। এই অডিও আউটপুট ডিভাইসগুলি বিভিন্ন অডিও ক্ষমতা সমর্থন করতে পারে, যেমন এনকোডিং (ডলবি ডিজিটাল+, ডিটিএস, এবং পিসিএম), নমুনা হার এবং চ্যানেল। উদাহরণস্বরূপ, HDMI-সংযুক্ত টিভিগুলিতে প্রচুর এনকোডিংয়ের জন্য সমর্থন থাকে যখন সংযুক্ত ব্লুটুথ হেডফোনগুলি সাধারণত কেবল PCM সমর্থন করে।
উপলব্ধ অডিও ডিভাইসের তালিকা এবং রাউট করা অডিও ডিভাইস হট-প্লাগিং HDMI ডিভাইস, ব্লুটুথ হেডফোন সংযোগ বা সংযোগ বিচ্ছিন্ন করে, অথবা ব্যবহারকারী অডিও সেটিংস পরিবর্তন করে পরিবর্তন করতে পারে। যেহেতু অ্যাপগুলি মিডিয়া চালাচ্ছে তখনও অডিও আউটপুট ক্ষমতাগুলি পরিবর্তিত হতে পারে, তাই অ্যাপগুলিকে এই পরিবর্তনগুলির সাথে সুন্দরভাবে মানিয়ে নিতে হবে এবং নতুন রাউট করা অডিও ডিভাইস এবং এর ক্ষমতাগুলিতে প্লেব্যাক চালিয়ে যেতে হবে। ভুল অডিও ফর্ম্যাট আউটপুট করার ফলে ত্রুটি হতে পারে বা কোন শব্দ বাজতে পারে না।
অডিও ডিভাইসের ক্ষমতার উপর নির্ভর করে ব্যবহারকারীকে সর্বোত্তম অডিও অভিজ্ঞতা দেওয়ার জন্য অ্যাপগুলি একাধিক এনকোডিং-এ একই সামগ্রী আউটপুট করার ক্ষমতা রাখে। উদাহরণস্বরূপ, একটি ডলবি ডিজিটাল এনকোডেড অডিও স্ট্রীম চালানো হয় যদি টিভি এটি সমর্থন করে, যখন ডলবি ডিজিটালের জন্য কোনও সমর্থন না থাকলে একটি আরও ব্যাপকভাবে-সমর্থিত PCM অডিও স্ট্রিম বেছে নেওয়া হয়। একটি অডিও স্ট্রীমকে PCM-এ রূপান্তর করতে ব্যবহৃত অন্তর্নির্মিত Android ডিকোডারগুলির তালিকা সমর্থিত মিডিয়া ফর্ম্যাটে পাওয়া যাবে।
প্লেব্যাকের সময়ে, স্ট্রিমিং অ্যাপটিকে আউটপুট অডিও ডিভাইস দ্বারা সমর্থিত সেরা AudioFormat
সহ একটি AudioTrack
তৈরি করা উচিত।
সঠিক বিন্যাস সহ একটি ট্র্যাক তৈরি করুন
অ্যাপ্লিকেশানগুলির একটি AudioTrack
তৈরি করা উচিত, এটি চালানো শুরু করা উচিত, এবং ডিফল্ট অডিও ডিভাইস যা থেকে শব্দ চালানো হবে তা নির্ধারণ করতে getRoutedDevice()
কল করুন৷ এটি হতে পারে, উদাহরণস্বরূপ, একটি নিরাপদ সংক্ষিপ্ত নীরবতা PCM এনকোডেড ট্র্যাক যা শুধুমাত্র রাউট করা ডিভাইস এবং এর অডিও ক্ষমতা নির্ধারণ করতে ব্যবহৃত হয়।
সমর্থিত এনকোডিং পান
ডিফল্ট অডিও ডিভাইসে উপলব্ধ অডিও ফর্ম্যাটগুলি নির্ধারণ করতে getAudioProfiles()
(API স্তর 31 এবং উচ্চতর) বা getEncodings()
(API স্তর 23 এবং উচ্চতর) ব্যবহার করুন৷
সমর্থিত অডিও প্রোফাইল এবং ফরম্যাট চেক করুন
বিন্যাস, চ্যানেল গণনা এবং নমুনা হারের সমর্থিত সমন্বয় পরীক্ষা করতে AudioProfile
(API স্তর 31 এবং উচ্চতর) বা isDirectPlaybackSupported()
(API স্তর 29 এবং উচ্চতর) ব্যবহার করুন।
কিছু অ্যান্ড্রয়েড ডিভাইস আউটপুট অডিও ডিভাইস দ্বারা সমর্থিত এনকোডিংগুলিকে সমর্থন করতে সক্ষম। এই অতিরিক্ত ফরম্যাটগুলি isDirectPlaybackSupported()
এর মাধ্যমে সনাক্ত করা উচিত। এই ক্ষেত্রে অডিও ডেটা আউটপুট অডিও ডিভাইস দ্বারা সমর্থিত একটি বিন্যাসে পুনরায় এনকোড করা হয়। getEncodings()
দ্বারা প্রত্যাবর্তিত তালিকায় উপস্থিত না থাকলেও পছন্দসই বিন্যাসের জন্য সমর্থন সঠিকভাবে পরীক্ষা করতে isDirectPlaybackSupported()
ব্যবহার করুন।
প্রত্যাশিত অডিও রুট
Android 13 (API লেভেল 33) প্রত্যাশিত অডিও রুট চালু করেছে। আপনি ডিভাইস অডিও বৈশিষ্ট্য সমর্থন পূর্বাভাস এবং সক্রিয় অডিও ডিভাইসের জন্য ট্র্যাক প্রস্তুত করতে পারেন. প্রদত্ত ফর্ম্যাট এবং বৈশিষ্ট্যগুলির জন্য বর্তমানে রাউট করা অডিও ডিভাইসে সরাসরি প্লেব্যাক সমর্থিত কিনা তা পরীক্ষা করতে আপনি getDirectPlaybackSupport()
ব্যবহার করতে পারেন:
কোটলিন
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 }
জাভা
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 ফ্রেমওয়ার্ক দ্বারা ট্রান্সকোড করা হবে:
কোটলিন
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() }
জাভা
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
অবজেক্টের একটি তালিকা প্রদান করে। পছন্দের AudioFormat
আইটেমগুলির তালিকাটি একটি সমর্থিত AudioProfile
পাওয়া না যাওয়া পর্যন্ত পুনরাবৃত্তি করা হয়। এই AudioProfile
bestAudioProfile
হিসাবে সংরক্ষণ করা হয়েছে। সর্বোত্তম নমুনা হার এবং চ্যানেল মাস্ক bestAudioProfile
থেকে নির্ধারিত হয়। অবশেষে, একটি উপযুক্ত AudioFormat
উদাহরণ তৈরি করা হয়।
অডিও ট্র্যাক তৈরি করুন
ডিফল্ট অডিও ডিভাইস (এবং নির্বাচিত সামগ্রীর জন্য উপলব্ধ) দ্বারা সমর্থিত সর্বোচ্চ-মানের AudioFormat
জন্য একটি AudioTrack
তৈরি করতে অ্যাপগুলিকে এই তথ্য ব্যবহার করা উচিত।
অডিও ডিভাইস পরিবর্তন বাধা
অডিও ডিভাইসের পরিবর্তনগুলিকে আটকাতে এবং প্রতিক্রিয়া জানাতে, অ্যাপগুলির উচিত:
- 24 এর সমান বা তার বেশি এপিআই স্তরের জন্য, অডিও ডিভাইসের পরিবর্তনগুলি (HDMI, ব্লুটুথ এবং আরও) নিরীক্ষণ করতে একটি
OnRoutingChangedListener
যোগ করুন। - API স্তর 23-এর জন্য, উপলব্ধ অডিও ডিভাইস তালিকায় পরিবর্তনগুলি পেতে একটি
AudioDeviceCallback
নিবন্ধন করুন৷ - API স্তর 21 এবং 22 এর জন্য, HDMI প্লাগ ইভেন্টগুলির জন্য মনিটর করুন এবং সম্প্রচার থেকে অতিরিক্ত ডেটা ব্যবহার করুন৷
- এছাড়াও API 23-এর চেয়ে কম ডিভাইসগুলির জন্য
BluetoothDevice
অবস্থার পরিবর্তনগুলি নিরীক্ষণ করতে একটিBroadcastReceiver
নিবন্ধন করুন, যেহেতুAudioDeviceCallback
এখনও সমর্থিত নয়৷
যখন AudioTrack
এর জন্য একটি অডিও ডিভাইস পরিবর্তন শনাক্ত করা হয়, তখন অ্যাপটিকে আপডেট করা অডিও ক্ষমতা পরীক্ষা করা উচিত এবং প্রয়োজন হলে, একটি ভিন্ন AudioFormat
দিয়ে AudioTrack
পুনরায় তৈরি করা উচিত। যদি একটি উচ্চ-মানের এনকোডিং এখন সমর্থিত হয় বা পূর্বে-ব্যবহৃত এনকোডিং আর-সমর্থিত না হয় তবে এটি করুন।
নমুনা কোড
কোটলিন
// 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)
জাভা
// 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);