প্রয়োজনীয় অনুমতি চেয়ে তা পেয়ে গেলে, আপনার অ্যাপটি অডিও গ্লাস বা ডিসপ্লে গ্লাসের হার্ডওয়্যার অ্যাক্সেস করতে পারবে। ফোনের হার্ডওয়্যারের পরিবর্তে গ্লাসের হার্ডওয়্যার অ্যাক্সেস করার মূল উপায় হলো একটি প্রোজেক্টেড কনটেক্সট ব্যবহার করা।
আপনার কোড কোথায় এক্সিকিউট হচ্ছে তার উপর নির্ভর করে, প্রজেক্টেড কনটেক্সট পাওয়ার দুটি প্রধান উপায় রয়েছে:
আপনার কোড যদি কোনো প্রজেক্টেড অ্যাক্টিভিটিতে চলে, তাহলে একটি প্রজেক্টেড কনটেক্সট নিন।
যদি আপনার অ্যাপের কোড আপনার প্রোজেক্টেড অ্যাক্টিভিটির ভেতর থেকে রান করে, তাহলে এর নিজস্ব অ্যাক্টিভিটি কনটেক্সট ইতিমধ্যেই একটি প্রোজেক্টেড কনটেক্সট হয়ে যায়। এই পরিস্থিতিতে, সেই অ্যাক্টিভিটির ভেতর থেকে করা কলগুলো আগে থেকেই চশমার হার্ডওয়্যার অ্যাক্সেস করতে পারে।
ফোন অ্যাপ কম্পোনেন্টে চলমান কোডের জন্য একটি প্রজেক্টেড কনটেক্সট পান।
আপনার অ্যাপের কোনো অংশ যদি আপনার প্রোজেক্টেড অ্যাক্টিভিটির বাইরে থাকে (যেমন একটি ফোন অ্যাক্টিভিটি বা একটি সার্ভিস), তবে সেটিকে অবশ্যই সুস্পষ্টভাবে একটি প্রোজেক্টেড কনটেক্সট সংগ্রহ করতে হবে। এটি করার জন্য, ` createProjectedDeviceContext মেথডটি ব্যবহার করুন:
@OptIn(ExperimentalProjectedApi::class) private fun getGlassesContext(context: Context): Context? { return try { // From a phone Activity or Service, get a context for the AI glasses. ProjectedContext.createProjectedDeviceContext(context) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to create projected device context", e) null } }
বৈধতা যাচাই করুন
createProjectedDeviceContext কলটিকে ProjectedContext.isProjectedDeviceConnected মেথডের মধ্যে রাখুন। যতক্ষণ এই মেথডটি true রিটার্ন করে, ততক্ষণ প্রজেক্টেড কনটেক্সটটি সংযুক্ত ডিভাইসটির জন্য বৈধ থাকে এবং আপনার ফোন অ্যাপের অ্যাক্টিভিটি বা সার্ভিস (যেমন CameraManager ) এআই গ্লাসের হার্ডওয়্যার অ্যাক্সেস করতে পারে।
সংযোগ বিচ্ছিন্ন হলে পরিষ্কার করুন
প্রজেক্টেড কনটেক্সটটি সংযুক্ত ডিভাইসের লাইফসাইকেলের সাথে যুক্ত থাকে, তাই ডিভাইসটি সংযোগ বিচ্ছিন্ন হলে এটিও নষ্ট হয়ে যায়। ডিভাইসটি সংযোগ বিচ্ছিন্ন হলে, ProjectedContext.isProjectedDeviceConnected ফলস false ) রিটার্ন করে। আপনার অ্যাপের উচিত এই পরিবর্তনটি পর্যবেক্ষণ করা এবং সেই প্রজেক্টেড কনটেক্সট ব্যবহার করে তৈরি করা যেকোনো সিস্টেম সার্ভিস (যেমন CameraManager ) বা রিসোর্স পরিষ্কার করে ফেলা।
পুনরায় সংযোগ করার সময় পুনরায় শুরু করুন
যখন গ্লাসটি পুনরায় সংযোগ স্থাপন করে, তখন আপনার অ্যাপ createProjectedDeviceContext ব্যবহার করে আরেকটি প্রজেক্টেড কনটেক্সট ইনস্ট্যান্স পেতে পারে এবং তারপর নতুন প্রজেক্টেড কনটেক্সট ব্যবহার করে যেকোনো সিস্টেম পরিষেবা বা রিসোর্স পুনরায় চালু করতে পারে।
চশমার মাইক্রোফোন দিয়ে অডিও রেকর্ড করুন।
আপনি দুটি ভিন্ন পদ্ধতি ব্যবহার করে চশমা থেকে অডিও রেকর্ড করতে পারেন:
- একটি প্রক্ষেপিত প্রেক্ষাপট ব্যবহার করুন।
- ব্লুটুথ হ্যান্ডস-ফ্রি প্রোফাইল (HFP) ব্যবহার করুন।
রেকর্ডিং পদ্ধতি নির্বাচন করুন
আপনার উচ্চ-মানের, এক্সআর-নির্দিষ্ট অডিও প্রসেসিং, নাকি সাধারণ ব্লুটুথ অডিও ইনপুট প্রয়োজন, তার উপর নির্ভর করে আপনি কোন পদ্ধতিটি বেছে নেবেন।
| রেকর্ডিং পদ্ধতি | মাইক্রোফোন অ্যাক্সেস | সাধারণ ব্যবহারের ক্ষেত্রে |
|---|---|---|
প্রত্যাশিত প্রেক্ষাপট | একাধিক মাইক্রোফোন | প্রজেক্টেড কনটেক্সট ব্যবহার করে রেকর্ডিং করলে আপনার অ্যাপটি চশমার একাধিক মাইক্রোফোন এবং এর বিশেষায়িত হার্ডওয়্যার ফিচারগুলো অ্যাক্সেস করতে পারে, যেমন:
|
ব্লুটুথ এইচএফপি | একক মাইক্রোফোন | তাৎক্ষণিক ও সরাসরি ব্যবহারের উপযোগী সামঞ্জস্যের জন্য এটি ব্লুটুথ হ্যান্ডস-ফ্রি প্রোফাইল (HFP)-এর উপর নির্ভর করে। এই মোডে, চশমাটি সাধারণ হেডসেট এবং অ্যাডভান্সড অডিও ডিস্ট্রিবিউশন প্রোফাইল (A2DP) ব্যবহার করে ফোনের সাথে সংযুক্ত হয় এবং একটি সাধারণ ব্লুটুথ পেরিফেরালের মতো কাজ করে। আপনার অ্যাপটি যদি আগে থেকেই সাধারণ ব্লুটুথ রেকর্ডিংয়ের জন্য ডিজাইন করা থাকে, তাহলে আপনি কোনো এক্সআর-নির্দিষ্ট সক্ষমতা যুক্ত না করেই চশমা থেকে অডিও রেকর্ড করতে এই পদ্ধতিটি ব্যবহার করতে পারেন। |
প্রজেক্টেড কনটেক্সট ব্যবহার করে অডিও রেকর্ড করুন
প্রজেক্টেড কনটেক্সট ব্যবহার করে অডিও রেকর্ড করতে, প্রথমে প্রয়োজনীয় রানটাইম পারমিশনের জন্য অনুরোধ করুন এবং তারপরে নিম্নলিখিত বিভাগগুলিতে বর্ণিত পদ্ধতি অনুযায়ী AudioRecord API ব্যবহার করে অডিও রেকর্ড করুন।
রানটাইম অনুমতির জন্য অনুরোধ করুন
চশমার একাধিক মাইক্রোফোন ব্যবহার করতে হলে, আপনাকে অবশ্যই প্রজেক্টেড ডিভাইসটির জন্য নির্দিষ্টভাবে অডিও পারমিশনের অনুরোধ করতে হবে। ব্যবহারকারী তার মোবাইল ডিভাইসে আপনার অ্যাপের জন্য যে সাধারণ, ফোন-কেন্দ্রিক RECORD_AUDIO পারমিশনটি দিয়েছেন, তা যথেষ্ট নয়।
অনুমতিগুলো অনুরোধ করতে এই ধাপগুলো অনুসরণ করুন:
- আপনার অ্যাপের ম্যানিফেস্ট ফাইলে
RECORD_AUDIOপারমিশনটি ঘোষণা করুন । আপনার কোডটি কোথায় এক্সিকিউট হচ্ছে তার উপর নির্ভর করে, নিম্নলিখিত উপায়গুলির মধ্যে যেকোনো একটিতে প্রজেক্টেড-ডিভাইস-স্কোপড পারমিশনগুলির জন্য অনুরোধ করুন:
- প্রজেক্টেড অ্যাক্টিভিটি থেকে কোড এক্সিকিউশনের জন্য ,
ProjectedPermissionsResultContractএর সাথেActivityResultLauncherব্যবহার করুন। এই পদ্ধতিটি ব্যবহারের বিষয়ে আরও তথ্যের জন্য, হার্ডওয়্যার পারমিশন অনুরোধ করার গাইডের 'পারমিশন লঞ্চার রেজিস্টার করুন' অংশ এবং পরবর্তী বিভাগগুলি দেখুন। - হোস্ট ফোন অ্যাক্টিভিটি থেকে কোড এক্সিকিউট করার জন্য : হার্ডওয়্যার পারমিশন অনুরোধ করার গাইডের 'অনুমতি অনুরোধ ইউজার ফ্লো বুঝুন ' বিভাগে বর্ণিত পদ্ধতি অনুযায়ী
Activity#requestPermissions(permissions, requestCode, deviceId)ব্যবহার করুন এবং আপনারprojectedDeviceContextথেকে প্রাপ্ত ডিভাইস আইডি প্রদান করুন।
- প্রজেক্টেড অ্যাক্টিভিটি থেকে কোড এক্সিকিউশনের জন্য ,
একটি প্রজেক্টেড কনটেক্সট দিয়ে AudioRecord শুরু করুন
হোস্ট ফোনের পরিবর্তে চশমা থেকে অডিও রেকর্ড করা নিশ্চিত করতে, আপনাকে অবশ্যই AudioRecord অবজেক্টটিকে প্রজেক্টেড ডিভাইস কনটেক্সটের সাথে যুক্ত করতে হবে।
নিম্নলিখিত কোডটি AudioRecord.Builder ব্যবহার করে এবং projectedDeviceContext কে setContext মেথডে পাস করে:
// Initialize AudioRecord with projected device context val audioRecord = AudioRecord.Builder() .setAudioSource(MediaRecorder.AudioSource.CAMCORDER) .setAudioFormat(audioFormat) .setBufferSizeInBytes(bufferSize) // pass in the projected device context .setContext(projectedDeviceContext) .build() audioRecord.startRecording()
কোড সম্পর্কে মূল বিষয়গুলো
আপনার নির্দিষ্ট ব্যবহারের প্রয়োজন অনুযায়ী অডিও প্রসেসিং সাজিয়ে নিতে, আপনি অডিও সোর্সকে
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATIONবাUNPROCESSEDএ সেট করতে পারেন।উদাহরণস্বরূপ, আপনার ব্যবহারের ক্ষেত্রে স্বয়ংক্রিয় নয়েজ রিডাকশনের প্রয়োজন হলে
VOICE_COMMUNICATIONব্যবহার করুন।VOICE_RECOGNITIONঅ্যাকোস্টিক ইকো ক্যান্সেলেশন (AEC) দ্বারা প্রসেস করা হয়। আর যদি আপনার র, অপরিবর্তিত অডিওর প্রয়োজন হয়, তাহলেUNPROCESSEDবাCAMCORDERনির্বাচন করুন।চশমার সাথে সামঞ্জস্য নিশ্চিত করতে,
audioFormatঅবজেক্টে অবশ্যই 16kHz-এর একটি স্যাম্পল রেট এবং মোনো অথবা স্টেরিও-র একটি চ্যানেল কনফিগারেশন (CHANNEL_IN_MONOবাCHANNEL_IN_STEREOব্যবহার করে) নির্ধারণ করতে হবে।যদিও বাফার সাইজের কোনো নির্দিষ্ট আবশ্যকতা নেই, অনুভূত ল্যাটেন্সি কমানোর জন্য সর্বনিম্ন বাফার সাইজ ব্যবহার করুন ।
ব্যবহারের পর পরিষ্কার করুন
যখন আপনার অ্যাপের আর মাইক্রোফোনের প্রয়োজন হয় না, অথবা যখন অ্যাক্টিভিটি বন্ধ করা হয়, তখন AudioRecord অবজেক্টে stop এবং release কল করুন।
রেকর্ডিং করার আগে রানটাইম অনুমতি যাচাই করুন।
startRecording কল করার আগে, প্রজেক্টেড কনটেক্সট ব্যবহার করে যাচাই করুন যে ব্যবহারকারী চশমার জন্য মাইক্রোফোনের অনুমতি দিয়েছেন ।
ব্লুটুথ এইচএফপি ব্যবহার করে অডিও রেকর্ড করুন
ব্লুটুথ HFP ব্যবহার করে অডিও রেকর্ড করতে, প্রথমে প্রয়োজনীয় রানটাইম পারমিশনের জন্য অনুরোধ করুন এবং তারপরে নিম্নলিখিত বিভাগগুলিতে বর্ণিত পদ্ধতি অনুসারে AudioManager API ব্যবহার করে অডিও রেকর্ড করুন।
অনুমতির জন্য অনুরোধ করুন
যেকোনো সাধারণ ব্লুটুথ অডিও ডিভাইসের মতোই, RECORD_AUDIO , BLUETOOTH_CONNECT এবং অন্যান্য সম্পর্কিত অনুমতিগুলো ফোন দ্বারা নিয়ন্ত্রিত হয়, সংযুক্ত ডিভাইস (যেমন অডিও গ্লাস বা ডিসপ্লে গ্লাস) দ্বারা নয়।
অনুমতিগুলো অনুরোধ করতে এই ধাপগুলো অনুসরণ করুন:
আপনার অ্যাপের ম্যানিফেস্ট ফাইলে নিম্নলিখিত অনুমতিগুলি ঘোষণা করুন :
স্ট্যান্ডার্ড অ্যান্ড্রয়েড পারমিশন ফ্লো ব্যবহার করে রানটাইমে
RECORD_AUDIOএবংBLUETOOTH_CONNECTউভয় পারমিশনের জন্য অনুরোধ করুন।
অডিও রাউট করতে AudioManager ব্যবহার করুন
ব্যবহারকারী আপনার অ্যাপকে প্রয়োজনীয় রানটাইম পারমিশন দেওয়ার পর, AudioManager এপিআই (AudioManager API) ব্যবহার করে কমিউনিকেশন ডিভাইসটিকে TYPE_BLUETOOTH_SCO তে সেট করুন, যাতে অডিও ব্লুটুথ এইচএফপি (Bluetooth HFP)-এর মাধ্যমে রাউট করা যায়। এটি সিস্টেমকে ব্লুটুথ পেরিফেরাল থেকে অডিও গ্রহণ করার নির্দেশ দেয়।
val audioManager = context.getSystemService(AudioManager::class.java) ?: return val devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS) val hfpDevice = devices.find { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO } hfpDevice?.let { device -> val audioRecord = AudioRecord.Builder() .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) .setAudioFormat(audioFormat) .setBufferSizeInBytes(bufferSize) .build() // Route recording to the Bluetooth device audioRecord.setPreferredDevice(device) audioManager.setCommunicationDevice(device) audioRecord.startRecording()
চশমার ক্যামেরা দিয়ে একটি ছবি তুলুন
চশমার ক্যামেরা দিয়ে ছবি তোলার জন্য, আপনার অ্যাপের জন্য সঠিক কনটেক্সট ব্যবহার করে CameraX-এর ImageCapture ইউজ কেসটি চশমার ক্যামেরার সাথে সেট আপ ও বাইন্ড করুন:
private fun startCameraOnGlasses(activity: ComponentActivity) { // 1. Get the CameraProvider using the projected context. // When using the projected context, DEFAULT_BACK_CAMERA maps to the AI glasses' camera. val projectedContext = try { ProjectedContext.createProjectedDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "AI Glasses context could not be created", e) return } val cameraProviderFuture = ProcessCameraProvider.getInstance(projectedContext) cameraProviderFuture.addListener({ val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA // 2. Check for the presence of a camera. if (!cameraProvider.hasCamera(cameraSelector)) { Log.w(TAG, "The selected camera is not available.") return@addListener } // 3. Query supported streaming resolutions using Camera2 Interop. val cameraInfo = cameraProvider.getCameraInfo(cameraSelector) val camera2CameraInfo = Camera2CameraInfo.from(cameraInfo) val cameraCharacteristics = camera2CameraInfo.getCameraCharacteristic( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP ) // 4. Define the resolution strategy. val targetResolution = Size(1920, 1080) val resolutionStrategy = ResolutionStrategy( targetResolution, ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER ) val resolutionSelector = ResolutionSelector.Builder() .setResolutionStrategy(resolutionStrategy) .build() // 5. If you have other continuous use cases bound, such as Preview or ImageAnalysis, // you can use Camera2 Interop's CaptureRequestOptions to set the FPS val fpsRange = Range(30, 60) val captureRequestOptions = CaptureRequestOptions.Builder() .setCaptureRequestOption(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange) .build() // 6. Initialize the ImageCapture use case with options. val imageCapture = ImageCapture.Builder() // Optional: Configure resolution, format, etc. .setResolutionSelector(resolutionSelector) .build() try { // Unbind use cases before rebinding. cameraProvider.unbindAll() // Bind use cases to camera using the Activity as the LifecycleOwner. cameraProvider.bindToLifecycle( activity, cameraSelector, imageCapture ) } catch (exc: Exception) { Log.e(TAG, "Use case binding failed", exc) } }, ContextCompat.getMainExecutor(activity)) }
কোড সম্পর্কে মূল বিষয়গুলো
- প্রজেক্টেড ডিভাইস কনটেক্সট ব্যবহার করে
ProcessCameraProviderএর একটি ইনস্ট্যান্স সংগ্রহ করে। - প্রজেক্টেড কনটেক্সটের পরিধির মধ্যে, ক্যামেরা নির্বাচন করার সময় চশমার প্রাথমিক, বাইরের দিকে মুখ করা ক্যামেরাটি
DEFAULT_BACK_CAMERAতে ম্যাপ করা হয়। - অগ্রসর হওয়ার আগে, নির্বাচিত ক্যামেরাটি ডিভাইসে উপলব্ধ আছে কিনা তা যাচাই করার জন্য
cameraProvider.hasCamera(cameraSelector)ব্যবহার করে একটি প্রি-বাইন্ডিং চেক করা হয়। - অন্তর্নিহিত
CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPপড়ার জন্যCamera2CameraInfoএর সাথে Camera2 Interop ব্যবহার করে, যা সমর্থিত রেজোলিউশনগুলির উপর উন্নত যাচাইয়ের জন্য উপযোগী হতে পারে। -
ImageCaptureএর আউটপুট ছবির রেজোলিউশন নিখুঁতভাবে নিয়ন্ত্রণ করার জন্য একটি কাস্টমResolutionSelectorতৈরি করা হয়েছে। - একটি
ImageCaptureব্যবহারের ক্ষেত্র তৈরি করে যা একটি কাস্টমResolutionSelectorদিয়ে কনফিগার করা থাকে। - এটি
ImageCaptureইউজ কেসটিকে অ্যাক্টিভিটির লাইফসাইকেলের সাথে সংযুক্ত করে। এটি অ্যাক্টিভিটির অবস্থার উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে ক্যামেরা খোলা এবং বন্ধ করা পরিচালনা করে (উদাহরণস্বরূপ, অ্যাক্টিভিটি পজ করা হলে ক্যামেরা বন্ধ করে দেওয়া)।
চশমার ক্যামেরা সেট আপ করার পরে, আপনি CameraX-এর ImageCapture ক্লাস ব্যবহার করে একটি ছবি তুলতে পারবেন। takePicture ব্যবহার করে কীভাবে ছবি তুলতে হয় , তা জানতে CameraX-এর ডকুমেন্টেশন দেখুন।
চশমার ক্যামেরা দিয়ে একটি ভিডিও ধারণ করুন।
চশমার ক্যামেরা দিয়ে ছবির পরিবর্তে ভিডিও ধারণ করতে, ImageCapture কম্পোনেন্টগুলোকে সংশ্লিষ্ট VideoCapture কম্পোনেন্ট দিয়ে প্রতিস্থাপন করুন এবং ক্যাপচার এক্সিকিউশন লজিক পরিবর্তন করুন।
প্রধান পরিবর্তনগুলোর মধ্যে রয়েছে একটি ভিন্ন ইউজ কেস ব্যবহার করা, একটি ভিন্ন আউটপুট ফাইল তৈরি করা এবং উপযুক্ত ভিডিও রেকর্ডিং পদ্ধতি ব্যবহার করে ক্যাপচার শুরু করা। VideoCapture API এবং এটি কীভাবে ব্যবহার করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, CameraX-এর ভিডিও ক্যাপচার ডকুমেন্টেশন দেখুন।
নিম্নলিখিত সারণিতে আপনার অ্যাপের ব্যবহারের ধরনের উপর নির্ভর করে প্রস্তাবিত রেজোলিউশন এবং ফ্রেম রেট দেখানো হলো:
| ব্যবহারের ক্ষেত্র | সমাধান | ফ্রেম রেট |
|---|---|---|
| ভিডিও যোগাযোগ | ১২৮০ x ৭২০ | ১৫ এফপিএস |
| কম্পিউটার ভিশন | ৬৪০ x ৪৮০ | ১০ এফপিএস |
| এআই ভিডিও স্ট্রিমিং | ৬৪০ x ৪৮০ | ১ এফপিএস |
প্রজেক্টেড অ্যাক্টিভিটি থেকে ফোনের হার্ডওয়্যার অ্যাক্সেস করুন
একটি প্রজেক্টেড অ্যাক্টিভিটি createHostDeviceContext(context) ব্যবহার করে হোস্ট ডিভাইসের (ফোনের) কনটেক্সট পাওয়ার মাধ্যমে ফোনের হার্ডওয়্যার (যেমন ক্যামেরা বা মাইক্রোফোন) অ্যাক্সেস করতে পারে:
@OptIn(ExperimentalProjectedApi::class) private fun getPhoneContext(activity: ComponentActivity): Context? { return try { // From an AI glasses Activity, get a context for the phone. ProjectedContext.createHostDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to create host device context", e) null } }
একটি হাইব্রিড অ্যাপে (যে অ্যাপে মোবাইল এবং গ্লাস উভয় অভিজ্ঞতাই থাকে) হোস্ট ডিভাইসের (ফোন) জন্য নির্দিষ্ট হার্ডওয়্যার বা রিসোর্স অ্যাক্সেস করার সময়, আপনার অ্যাপটি যাতে সঠিক হার্ডওয়্যার অ্যাক্সেস করতে পারে তা নিশ্চিত করার জন্য আপনাকে অবশ্যই সঠিক কনটেক্সট স্পষ্টভাবে নির্বাচন করতে হবে:
- ফোনের কনটেক্সট পেতে ফোন
ActivityথেকেActivityকনটেক্সট অথবাProjectedContext.createHostDeviceContextব্যবহার করুন। -
getApplicationContextব্যবহার করবেন না, কারণ যদি কোনো প্রজেক্টেড অ্যাক্টিভিটি সর্বশেষ চালু হওয়া কম্পোনেন্ট হয়ে থাকে, তাহলে অ্যাপ্লিকেশন কনটেক্সট ভুলভাবে চশমার কনটেক্সট রিটার্ন করতে পারে।