Une fois que vous avez demandé et obtenu les autorisations nécessaires, votre application peut accéder au matériel des lunettes audio ou d'affichage. Pour accéder au matériel des lunettes (au lieu de celui du téléphone), vous devez utiliser un contexte projeté.
Il existe deux façons principales d'obtenir un contexte projeté, selon l'endroit où votre code s'exécute :
Obtenir un contexte projeté si votre code s'exécute dans une activité projetée
Si le code de votre application s'exécute à partir de votre activité projetée, son propre contexte d'activité est déjà un contexte projeté. Dans ce cas, les appels effectués dans cette activité peuvent déjà accéder au matériel des lunettes.
Obtenir un contexte projeté pour le code s'exécutant dans un composant d'application pour téléphone
Si une partie de votre application en dehors de votre activité projetée (comme une activité téléphonique ou un service) doit accéder au matériel des lunettes, elle doit obtenir explicitement un contexte projeté. Pour ce faire, utilisez la
createProjectedDeviceContext méthode :
@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 } }
Vérifier la validité
Encapsulez l'appel createProjectedDeviceContext dans le
ProjectedContext.isProjectedDeviceConnected. Tant que cette méthode renvoie true, le contexte projeté reste valide pour l'appareil connecté, et l'activité ou le service de votre application pour téléphone (comme un CameraManager) peut accéder au matériel des lunettes d'IA.
Nettoyer lors de la déconnexion
Le contexte projeté est lié au cycle de vie de l'appareil connecté. Il est donc détruit lorsque l'appareil se déconnecte. Lorsque l'appareil se déconnecte,
ProjectedContext.isProjectedDeviceConnected renvoie false. Votre application doit écouter ce changement et nettoyer tous les services système (comme un CameraManager) ou les ressources qu'elle a créées à l'aide de ce contexte projeté.
Réinitialiser lors de la reconnexion
Lorsque les lunettes se reconnectent, votre application peut obtenir une autre instance de contexte projeté
à l'aide de createProjectedDeviceContext, puis
réinitialiser tous les services ou ressources système à l'aide du nouveau contexte projeté.
Enregistrer du contenu audio avec le micro des lunettes
Vous pouvez enregistrer du contenu audio à partir des lunettes à l'aide de deux méthodes distinctes :
- Utiliser un contexte projeté.
- Utiliser le profil mains libres Bluetooth
Choisir une méthode d'enregistrement
La méthode que vous choisissez dépend de si vous avez besoin d'un traitement audio haute fidélité et spécifique à la XR ou d'une entrée audio Bluetooth standard.
| Méthode d'enregistrement | Accès au micro | Cas d'utilisation courant |
|---|---|---|
Contexte projeté |
Plusieurs micros |
L'enregistrement à l'aide d'un contexte projeté permet à votre application d'accéder à plusieurs micros des lunettes et à leurs fonctionnalités matérielles spécialisées, telles que :
|
Profil mains libres Bluetooth |
Micro simple |
S'appuie sur le profil mains libres Bluetooth pour une compatibilité immédiate et prête à l'emploi. Dans ce mode, les lunettes se connectent au téléphone à l'aide des profils standard Headset et Advanced Audio Distribution Profile (A2DP) profiles, et fonctionnent comme un périphérique Bluetooth classique. Si votre application est déjà conçue pour l'enregistrement Bluetooth standard, vous pouvez utiliser cette méthode pour enregistrer du contenu audio à partir des lunettes sans intégrer de fonctionnalités spécifiques à la XR. |
Enregistrer du contenu audio à l'aide d'un contexte projeté
Pour enregistrer du contenu audio à l'aide d'un contexte projeté, demandez d'abord les autorisations d'exécution requises, puis enregistrez le contenu audio à l'aide de l'API AudioRecord, comme décrit dans les sections suivantes.
Demander des autorisations d'exécution
Pour accéder à plusieurs micros sur les lunettes, vous devez demander des autorisations audio spécifiquement pour l'appareil projeté. L'autorisation RECORD_AUDIO standard et limitée au téléphone qu'un utilisateur a accordée à votre application sur son appareil mobile ne suffit pas.
Pour demander les autorisations, procédez comme suit :
- Déclarez l'autorisation
RECORD_AUDIOdans le fichier manifeste de votre application. Demandez les autorisations limitées à l'appareil projeté de l'une des manières suivantes, selon l'endroit où votre code s'exécute :
- Code s'exécutant à partir d'une activité projetée : utilisez
ActivityResultLauncheravecProjectedPermissionsResultContract. Pour en savoir plus sur l'utilisation de cette méthode, consultez la section Enregistrer le lanceur d'autorisations et les sections suivantes du guide pour demander des autorisations matérielles. - Code s'exécutant à partir d'une activité téléphonique hôte : utilisez
Activity#requestPermissions(permissions, requestCode, deviceId)et fournissez l'ID d'appareil obtenu à partir de votreprojectedDeviceContext, comme décrit dans la section Comprendre le flux utilisateur de la demande d'autorisation du guide pour demander des autorisations matérielles.
- Code s'exécutant à partir d'une activité projetée : utilisez
Initialiser AudioRecord avec un contexte projeté
Pour vous assurer que le contenu audio est enregistré à partir des lunettes plutôt que du téléphone hôte, vous devez associer l'objet AudioRecord au contexte de l'appareil projeté.
Le code suivant utilise les AudioRecord.Builder et transmet les
projectedDeviceContext à la méthode 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()
Points clés concernant le code
Vous pouvez définir la source audio sur
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATION, ouUNPROCESSEDpour adapter le traitement audio à votre cas d'utilisation spécifique.Par exemple, utilisez
VOICE_COMMUNICATIONsi votre cas d'utilisation nécessite une réduction automatique du bruit.VOICE_RECOGNITIONest traité avec l'annulation de l'écho acoustique. Si vous avez besoin d'un contenu audio brut et non modifié, sélectionnezUNPROCESSEDouCAMCORDER.Pour assurer la compatibilité avec les lunettes, l'objet
audioFormatdoit définir une fréquence d'échantillonnage de 16 kHz et une configuration de canal mono ou stéréo (à l'aide deCHANNEL_IN_MONOouCHANNEL_IN_STEREO).Bien qu'il n'y ait pas d'exigence fixe concernant la taille de la mémoire tampon, obtenez la taille minimale de la mémoire tampon pour réduire la latence perçue.
Nettoyer après utilisation
Lorsque votre application n'a plus besoin du micro ou que l'activité est arrêtée,
appelez stop et release sur l'objet AudioRecord.
Vérifier les autorisations d'exécution avant l'enregistrement
Avant d'appeler startRecording, vérifiez que l'utilisateur a accordé l'
autorisation d'accès au micro pour les lunettes à l'aide du contexte projeté.
Enregistrer du contenu audio à l'aide du profil mains libres Bluetooth
Pour enregistrer du contenu audio à l'aide du profil mains libres Bluetooth, demandez d'abord les autorisations d'exécution requises, puis enregistrez le contenu audio à l'aide de l'API AudioManager, comme
décrit dans les sections suivantes.
Demander des autorisations
Comme pour tout appareil audio Bluetooth standard, les autorisations RECORD_AUDIO,
BLUETOOTH_CONNECT et autres autorisations associées sont contrôlées par le
téléphone et non par l'appareil connecté (comme des lunettes audio ou d'affichage).
Pour demander les autorisations, procédez comme suit :
Déclarez les autorisations suivantes dans le fichier manifeste de votre application :
Demandez les autorisations
RECORD_AUDIOetBLUETOOTH_CONNECTau moment de l'exécution à l'aide du flux d'autorisations Android standard.
Utiliser AudioManager pour acheminer le contenu audio
Une fois que l'utilisateur a accordé à votre application les autorisations d'exécution nécessaires, utilisez l'
AudioManager API pour définir l'appareil de communication sur
TYPE_BLUETOOTH_SCO afin d'acheminer le contenu audio via le profil mains libres Bluetooth. Cela indique au système de récupérer le contenu audio à partir du périphérique Bluetooth.
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()
Capturer une image avec l'appareil photo des lunettes
Pour capturer une image avec l'appareil photo des lunettes, configurez et liez le cas d'utilisation de CameraX
ImageCapture à l'appareil photo des lunettes à l'aide du contexte approprié
pour votre application :
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)) }
Points clés concernant le code
- Obtient une instance de
ProcessCameraProviderà l'aide du contexte de l'appareil projeté. - Dans le champ d'application du contexte projeté, l'appareil photo principal des lunettes, orienté vers l'extérieur, correspond à
DEFAULT_BACK_CAMERAlors de la sélection d'un appareil photo. - Une vérification préalable à la liaison utilise
cameraProvider.hasCamera(cameraSelector)pour vérifier que l'appareil photo sélectionné est disponible sur l'appareil avant de continuer. - Utilise Camera2 Interop avec
Camera2CameraInfopour lire leCameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPsous-jacent, ce qui peut être utile pour les vérifications avancées des résolutions compatibles. - Un
ResolutionSelectorpersonnalisé est créé pour contrôler précisément la résolution de l'image de sortie pourImageCapture. - Crée un cas d'utilisation
ImageCaptureconfiguré avec unResolutionSelectorpersonnalisé. - Lie le cas d'utilisation
ImageCaptureau cycle de vie de l'activité. Cela gère automatiquement l'ouverture et la fermeture de l'appareil photo en fonction de l'état de l'activité (par exemple, en arrêtant l'appareil photo lorsque l'activité est mise en pause).
Une fois l'appareil photo des lunettes configuré, vous pouvez capturer une image avec la classe ImageCapture de CameraX. Consultez la documentation de CameraX pour savoir
comment utiliser takePicture afin de capturer une image.
Capturer une vidéo avec l'appareil photo des lunettes
Pour capturer une vidéo au lieu d'une image avec l'appareil photo des lunettes, remplacez les
ImageCapture composants par les composants VideoCapture correspondants
et modifiez la logique d'exécution de la capture.
Les principales modifications impliquent l'utilisation d'un cas d'utilisation différent, la création d'un fichier de sortie différent et le lancement de la capture à l'aide de la méthode d'enregistrement vidéo appropriée.
Pour en savoir plus sur l'API VideoCapture et son utilisation, consultez la
documentation sur la capture vidéo de CameraX.
Le tableau suivant indique la résolution et la fréquence d'images recommandées en fonction du cas d'utilisation de votre application :
| Cas d'utilisation | Solution | Fréquence d'images |
|---|---|---|
| Communication vidéo | 1280 x 720 | 15 FPS |
| Vision par ordinateur | 640 x 480 | 10 FPS |
| Streaming vidéo IA | 640 x 480 | 1 FPS |
Accéder au matériel d'un téléphone à partir d'une activité projetée
Une activité projetée peut également accéder au matériel du téléphone (comme l'
appareil photo ou le micro) en utilisant createHostDeviceContext(context) pour obtenir
le contexte de l'appareil hôte (téléphone) :
@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 } }
Lorsque vous accédez à du matériel ou à des ressources spécifiques à l'appareil hôte (téléphone) dans une application hybride (une application contenant des expériences mobiles et des lunettes), vous devez sélectionner explicitement le contexte approprié pour vous assurer que votre application peut accéder au matériel approprié :
- Utilisez le
Activitycontexte du téléphoneActivityou leProjectedContext.createHostDeviceContextpour obtenir le contexte du téléphone. - N'utilisez pas
getApplicationContext, car le contexte de l'application peut renvoyer de manière incorrecte le contexte des lunettes si une activité projetée était le composant lancé le plus récemment.