Gerekli izinleri isteyip aldıktan sonra uygulamanız ses gözlüklerindeki veya ekran gözlüklerindeki donanıma erişebilir. Telefonun donanımı yerine gözlüğün donanımına erişmenin anahtarı, yansıtılan bağlamı kullanmaktır.
Kodunuzun yürütüldüğü yere bağlı olarak, tahmini bağlam elde etmenin iki temel yolu vardır:
Kodunuz yansıtılan bir etkinlikte çalışıyorsa yansıtılan bağlamı alma
Uygulamanızın kodu yansıtılan etkinliğinizden çalışıyorsa kendi etkinlik bağlamı zaten bir yansıtılan bağlamdır. Bu senaryoda, etkinlik içinde yapılan aramalar gözlüğün donanımına erişebilir.
Telefon uygulaması bileşeninde çalışan kod için tahmini bağlam alma
Uygulamanızın, planlanan etkinliğinizin dışında kalan bir bölümünün (ör. telefon etkinliği veya bir hizmet) gözlüğün donanımına erişmesi gerekiyorsa açıkça planlanan bir bağlam elde etmesi gerekir. Bunun için createProjectedDeviceContext yöntemini kullanın:
@RequiresApi(Build.VERSION_CODES.BAKLAVA) @OptIn(ExperimentalProjectedApi::class, ExperimentalCoroutinesApi::class) private fun monitorProjectedConnectivity(activity: ComponentActivity) { activity.lifecycleScope.launch { // Before creating a projected context, check to see if the projected device is connected. // While this method returns true, the projected context remains valid. ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext) .collectLatest { isConnected -> if (isConnected) { // From a phone Activity or Service, get a context for the audio and display glasses. // Re-initialize on reconnect: Obtain another context instance. val projectedContext = try { ProjectedContext.createProjectedDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to create projected context", e) return@collectLatest } // Use the projectedContext to initialize system services (e.g., CameraManager). Log.i(TAG, "Projected device connected. Initializing hardware...") } else { // The projected context is destroyed when the device disconnects. // Clean up on disconnect: Listen for 'false' and release resources. Log.i(TAG, "Projected device disconnected. Cleaning up hardware resources...") } } } }
Geçerliliği kontrol etme
createProjectedDeviceContext çağrısını ProjectedContext.isProjectedDeviceConnected içine alın. Bu yöntem true değerini döndürse de yansıtılan bağlam, bağlı cihaz için geçerli olmaya devam eder ve telefon uygulamanızın etkinliği veya hizmeti (ör. CameraManager) yapay zeka gözlüğünün donanımına erişebilir.
Bağlantı kesildiğinde temizleme
Yansıtılan bağlam, bağlı cihazın yaşam döngüsüne bağlı olduğundan cihazın bağlantısı kesildiğinde yok edilir. Cihazın bağlantısı kesildiğinde,
ProjectedContext.isProjectedDeviceConnected, false değerini döndürür. Uygulamanız bu değişikliği dinlemeli ve sistem hizmetlerini (ör. CameraManager) veya uygulamanızın bu öngörülen bağlamı kullanarak oluşturduğu kaynakları temizlemelidir.
Yeniden bağlandığında yeniden başlat
Gözlük yeniden bağlandığında uygulamanız createProjectedDeviceContext kullanarak başka bir yansıtılan bağlam örneği alabilir ve ardından yeni yansıtılan bağlamı kullanarak tüm sistem hizmetlerini veya kaynaklarını yeniden başlatabilir.
Gözlüğün mikrofonuyla ses kaydetme
Gözlükten ses kaydetmek için iki farklı yöntem kullanabilirsiniz:
- Öngörülen bağlamı kullanın.
- Bluetooth Eller Serbest Profilini (HFP) kullanın.
Kayıt yöntemi seçme
Seçeceğiniz yöntem, yüksek kaliteli, XR'a özel ses işleme veya standart Bluetooth ses girişi gerekip gerekmediğine bağlıdır.
| Kayıt yöntemi | Mikrofon erişimi | Yaygın kullanım alanı |
|---|---|---|
Öngörülen Bağlam |
Birden fazla mikrofon |
Yansıtılan bağlam kullanılarak yapılan kayıt, uygulamanızın gözlükteki birden fazla mikrofona ve özel donanım özelliklerine (ör. aşağıdakiler) erişmesine olanak tanır:
|
Bluetooth HFP |
Tek mikrofon |
Hemen kullanmaya başlamak için Bluetooth Eller Serbest Profili'ni (HFP) kullanır. Bu modda gözlük, standart mikrofonlu kulaklık ve gelişmiş ses dağıtım profili (A2DP) profillerini kullanarak telefona bağlanır ve normal bir Bluetooth çevre birimi gibi çalışır. Uygulamanız standart Bluetooth kaydı için tasarlanmışsa XR'a özgü özellikleri entegre etmeden gözlüklerden ses kaydetmek için bu yöntemi kullanabilirsiniz. |
Yansıtılan bağlamı kullanarak ses kaydetme
Yansıtılan bir bağlam kullanarak ses kaydetmek için önce gerekli çalışma zamanı izinlerini isteyin, ardından aşağıdaki bölümlerde açıklandığı gibi AudioRecord API'sini kullanarak sesi kaydedin.
Çalışma zamanı izinleri isteme
Gözlükteki birden fazla mikrofona erişmek için ses izinlerini özellikle yansıtılan cihaz için istemeniz gerekir. Kullanıcının mobil cihazında uygulamanız için verdiği standart, telefon kapsamlı
RECORD_AUDIO izin yeterli değil.
İzin istemek için aşağıdaki adımları uygulayın:
- Uygulamanızın manifest dosyasında
RECORD_AUDIOiznini beyan edin. Kodunuzun nerede yürütüldüğüne bağlı olarak, aşağıdaki yöntemlerden birini kullanarak projeksiyon cihazı kapsamlı izinleri isteyin:
- Yansıtılan bir etkinlikten kod yürütme:
ActivityResultLauncherileProjectedPermissionsResultContractöğesini kullanın. Bu yöntemi kullanma hakkında daha fazla bilgi için izin başlatıcıyı kaydetme bölümüne ve donanım izinleri isteme kılavuzundaki sonraki bölümlere bakın. - Ana telefon etkinliğinden kod yürütme:
Activity#requestPermissions(permissions, requestCode, deviceId)kullanın ve donanım izinleri isteme kılavuzunun izin isteği kullanıcı akışını anlama bölümünde açıklandığı gibiprojectedDeviceContextcihazınızdan alınan cihaz kimliğini sağlayın.
- Yansıtılan bir etkinlikten kod yürütme:
AudioRecord'u tahmini bir bağlamla başlatma
Sesin, barındıran telefondan değil gözlükten kaydedildiğinden emin olmak için AudioRecord nesnesini yansıtılan cihaz bağlamıyla ilişkilendirmeniz gerekir.
Aşağıdaki kodda AudioRecord.Builder kullanılır ve projectedDeviceContext, setContext yöntemine iletilir:
// 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()
Kodla ilgili önemli noktalar
Ses işlemeyi belirli kullanım alanınıza göre uyarlamak için ses kaynağını
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATIONveyaUNPROCESSEDolarak ayarlayabilirsiniz.Örneğin, kullanım alanınızda otomatik gürültü azaltma veya kullanıcının sesini izole etme özelliği gerekiyorsa
VOICE_COMMUNICATIONsimgesini kullanın.VOICE_RECOGNITION, akustik yankı giderme (AEC) ile işlenir. Bu özellik, kayıt sırasında eşzamanlı ses oynatma olduğunda faydalı olabilir. Ham ve değiştirilmemiş ses dosyasına ihtiyacınız varsaUNPROCESSEDveyaCAMCORDERsimgesini seçin.Gözlüklerle uyumluluğu sağlamak için
audioFormatnesnesi 16 kHz örnekleme hızı ve mono ya da stereo kanal yapılandırması (CHANNEL_IN_MONOveyaCHANNEL_IN_STEREOkullanılarak) tanımlamalıdır.AudioRecordnesnesini oluşturmak için minimum arabellek boyutunu belirlemek üzereAudioRecord.getMinBufferSize()öğesini kullanın. Ancak gözlükten ses düşmelerini önlemek için arabelleğin tamamının dolmasını beklemek yerine bu arabelleği kısa ve sık aralıklarla okumanız (ideal olarak 20 ms'lik dilimler halinde) gerekir.
Kullandıktan sonra temizleme
Uygulamanızın artık mikrofona ihtiyacı kalmadığında veya etkinlik durdurulduğunda AudioRecord nesnesinde stop ve release yöntemlerini çağırın.
Kayda başlamadan önce çalışma zamanı izinlerini kontrol etme
startRecording uygulamasını aramadan önce, kullanıcının yansıtılan bağlamı kullanarak gözlük için mikrofon izni verdiğini doğrulayın.
Bluetooth HFP kullanarak ses kaydetme
Bluetooth HFP kullanarak ses kaydetmek için önce gerekli çalışma zamanı izinlerini isteyin, ardından aşağıdaki bölümlerde açıklandığı gibi AudioManager API'yi kullanarak sesi kaydedin.
İzin iste
Standart Bluetooth ses cihazlarında olduğu gibi, RECORD_AUDIO,
BLUETOOTH_CONNECT ve diğer ilgili izinler, telefondan kontrol edilir. Bağlı cihazdan (ör. ses gözlüğü veya ekran gözlüğü) kontrol edilmez.
İzin istemek için aşağıdaki adımları uygulayın:
Uygulamanızın manifest dosyasında aşağıdaki izinleri beyan edin:
Standart Android izin akışını kullanarak çalışma zamanında hem
RECORD_AUDIOhem deBLUETOOTH_CONNECTizinlerini isteyin.
Sesi yönlendirmek için AudioManager'ı kullanma
Kullanıcı, uygulamanıza gerekli çalışma zamanı izinlerini verdikten sonra, sesi Bluetooth HFP üzerinden yönlendirmek için iletişim cihazını TYPE_BLUETOOTH_SCO olarak ayarlamak üzere AudioManager API'sini kullanın. Bu, sistemin sesi Bluetooth çevre biriminden almasını sağlar.
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()
Gözlüğün kamerasıyla görüntü yakalama
Gözlüğün kamerasıyla görüntü yakalamak için CameraX'in ImageCapture kullanım alanını uygulamanız için doğru bağlamı kullanarak gözlüğün kamerasına ayarlayın ve bağlayın:
private fun startCameraOnGlasses(activity: ComponentActivity) { activity.lifecycleScope.launch { // Before creating a projected context, check to see if the projected device is connected. ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext) .collectLatest { isConnected -> if (isConnected) { // 1. Get the CameraProvider using the projected context. // When using the projected context, DEFAULT_BACK_CAMERA maps to the audio and display glasses' camera. val projectedContext = try { ProjectedContext.createProjectedDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "Projected context could not be created", e) return@collectLatest } 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)) } } } }
Kodla ilgili önemli noktalar
- Öngörülen cihaz bağlamını kullanarak
ProcessCameraProviderörneğini alır. - Gözlüğün birincil, dışa dönük kamerası, yansıtılan bağlamın kapsamı içinde bir kamera seçilirken
DEFAULT_BACK_CAMERAile eşlenir. - Ön bağlama kontrolü, devam etmeden önce seçilen kameranın cihazda kullanılabilir olduğunu doğrulamak için
cameraProvider.hasCamera(cameraSelector)kullanır. - Desteklenen çözünürlüklerle ilgili gelişmiş kontroller için yararlı olabilecek temel
CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPdeğerini okumak üzereCamera2CameraInfoile Camera2 Interop'u kullanır. ResolutionSelector,ImageCaptureiçin çıkış görüntüsü çözünürlüğünü hassas bir şekilde kontrol etmek üzere özel olarak oluşturulur.ImageCaptureile yapılandırılmış bir kullanım alanı oluştururResolutionSelector.ImageCapturekullanım alanını etkinliğin yaşam döngüsüne bağlar. Bu, etkinliğin durumuna göre kameranın açılmasını ve kapanmasını otomatik olarak yönetir (örneğin, etkinlik duraklatıldığında kamerayı durdurur).- Tek akışlı donanım için yapılandırma: Gözlük kamera işlem hattı, aynı anda tek bir etkin akışla sınırlıdır.
ImageCapturetek başına bağlandığında CameraX, varsayılan olarak odaklanma ve ölçüm için dahili bir tekrarlayan akış (MeteringRepeating) ekler. Bu da tek akışlı ardışık düzenin durmasına neden olur.ImageCaptureöğesini önizleme yayını olmadan tek başına bağlamak içinsetRepeatingStreamForced(false)değeriniCameraXConfig.Builderolarak ayarlayarak (örneğin,ApplicationsınıfınızdaCameraXConfig.Provideröğesini uygulayarak veya başlatmadan önceProcessCameraProvideröğesini yapılandırarak) zorunlu tekrarlayan yayını devre dışı bırakın.
Gözlüğün kamerası ayarlandıktan sonra CameraX'in ImageCapture sınıfıyla görüntü yakalayabilirsiniz. takePicture kullanarak görüntü yakalama hakkında bilgi edinmek için CameraX'in belgelerine bakın.
Gözlüğün kamerasıyla video çekme
Gözlüğün kamerasıyla resim yerine video çekmek için ImageCapture bileşenlerini ilgili VideoCapture bileşenleriyle değiştirin ve çekim yürütme mantığını değiştirin.
Başlıca değişiklikler arasında farklı bir kullanım alanı kullanma, farklı bir çıkış dosyası oluşturma ve uygun video kaydı yöntemini kullanarak yakalama işlemini başlatma yer alır.
VideoCapture API ve nasıl kullanılacağı hakkında daha fazla bilgi için CameraX'in video yakalama belgelerine bakın.
Aşağıdaki tabloda, uygulamanızın kullanım alanına bağlı olarak önerilen çözünürlük ve kare hızı gösterilmektedir:
| Kullanım alanı | Çözünürlük | Kare hızı |
|---|---|---|
| Video İletişimi | 1280 x 720 | 15 FPS |
| Bilgisayar Görüşü | 640 x 480 | 10 FPS |
| Yapay Zeka Video Akışı | 640 x 480 | 1 FPS |
Yansıtılan bir etkinlikten telefonun donanımına erişme
Yansıtılan etkinlikler, ana makine cihazının (telefon) bağlamını almak için createHostDeviceContext(context) kullanarak telefonun donanımına (ör. kamera veya mikrofon) da erişebilir:
@OptIn(ExperimentalProjectedApi::class) private fun getPhoneContext(activity: ComponentActivity): Context? { return try { // From a projected Activity, get a context for the phone. ProjectedContext.createHostDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to create host device context", e) null } }
Karma bir uygulamada (hem mobil hem de gözlük deneyimlerini içeren bir uygulama) ana cihaza (telefon) özgü donanıma veya kaynaklara erişirken uygulamanızın doğru donanıma erişebilmesi için doğru bağlamı açıkça seçmeniz gerekir:
- Telefonun bağlamını almak için telefondaki
ActivitybağlamınıActivityveyaProjectedContext.createHostDeviceContextbağlamını kullanın. - Yansıtılan bir etkinlik en son başlatılan bileşen olduysa uygulama bağlamı, gözlüğün bağlamını yanlış döndürebileceğinden
getApplicationContextkullanmayın.