После того, как вы запросили и получили необходимые разрешения , ваше приложение сможет получить доступ к аппаратному обеспечению аудио- или дисплейных очков. Ключ к доступу к аппаратному обеспечению очков (а не телефона) — использование проецируемого контекста .
Существует два основных способа получения контекста проекции, в зависимости от того, где выполняется ваш код:
Получите контекст проекции, если ваш код выполняется в проецируемой активности.
Если код вашего приложения выполняется внутри вашей проецируемой активности , то контекст её собственной активности уже является проецируемым контекстом . В этом сценарии вызовы, совершаемые внутри этой активности, уже могут получить доступ к аппаратному обеспечению очков.
Получите контекст выполнения кода в компоненте мобильного приложения.
Если части вашего приложения, находящейся за пределами проектируемой активности (например, активности телефона или сервиса), необходимо получить доступ к аппаратному обеспечению очков, она должна явно получить проектируемый контекст. Для этого используйте метод 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 , а затем повторно инициализировать любые системные службы или ресурсы, используя новый проецируемый контекст.
Записывайте звук с помощью микрофона очков.
Запись звука с очков возможна двумя различными способами:
- Используйте проецируемый контекст .
- Используйте профиль громкой связи Bluetooth (HFP).
Выберите способ записи
Выбор метода зависит от того, требуется ли вам высококачественная обработка звука, специально разработанная для XR, или стандартный аудиовход Bluetooth.
| Метод записи | доступ к микрофону | Типичный сценарий использования |
|---|---|---|
Прогнозируемый контекст | Несколько микрофонов | Запись с использованием проецируемого контекста позволяет вашему приложению получать доступ к нескольким микрофонам очков и их специализированным аппаратным функциям, таким как:
|
Bluetooth HFP | Один микрофон | Для обеспечения мгновенной совместимости «из коробки» используются профили Bluetooth Hands-Free Profile (HFP). В этом режиме очки подключаются к телефону, используя стандартные профили Headset и Advanced Audio Distribution Profile (A2DP), функционируя как обычное Bluetooth-устройство. Если ваше приложение уже разработано для стандартной записи по Bluetooth, вы можете использовать этот метод для записи звука с очков без интеграции каких-либо специфических функций XR. |
Запись звука с использованием проецируемого контекста.
Для записи звука с использованием проецируемого контекста сначала запросите необходимые разрешения во время выполнения, а затем запишите звук с помощью API AudioRecord , как описано в следующих разделах.
Запросить разрешения во время выполнения
Для доступа к нескольким микрофонам на очках необходимо запросить разрешения на воспроизведение звука специально для проецируемого устройства. Стандартного разрешения RECORD_AUDIO , предоставленного пользователем вашему приложению на своем мобильном устройстве и ограничивающего его использование на телефоне, недостаточно.
Для запроса разрешений выполните следующие действия:
- Укажите разрешение
RECORD_AUDIOв файле манифеста вашего приложения. Запросить разрешения, ограниченные областью действия проектируемого устройства, можно одним из следующих способов, в зависимости от места выполнения вашего кода:
- Для выполнения кода из проецируемой активности используйте
ActivityResultLauncherсProjectedPermissionsResultContract. Дополнительную информацию об использовании этого метода см. в разделе «Регистрация средства запуска разрешений» и последующих разделах руководства по запросу разрешений на оборудование. - Код, выполняемый из активности хост-телефона : используйте
Activity#requestPermissions(permissions, requestCode, deviceId)и укажите идентификатор устройства, полученный из вашегоprojectedDeviceContext, как описано в разделе «Понимание потока запроса разрешений пользователем» руководства по запросу разрешений на оборудование.
- Для выполнения кода из проецируемой активности используйте
Инициализируйте AudioRecord с помощью проецируемого контекста.
Чтобы гарантировать запись звука с очков, а не с основного телефона, необходимо связать объект AudioRecord с контекстом проецируемого устройства.
В следующем коде используется объект AudioRecord.Builder , и в метод setContext передается объект projectedDeviceContext :
// 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должен определять частоту дискретизации 16 кГц и конфигурацию каналов либо моно, либо стерео (используяCHANNEL_IN_MONOилиCHANNEL_IN_STEREO).Хотя строгих требований к размеру буфера нет, выберите минимальный размер буфера , чтобы минимизировать воспринимаемую задержку.
После использования необходимо убрать за собой.
Когда вашему приложению больше не нужен микрофон или когда активность остановлена, вызовите методы stop и release для объекта AudioRecord .
Перед началом записи проверьте права доступа во время выполнения.
Перед вызовом startRecording убедитесь, что пользователь предоставил очкам разрешение на использование микрофона, используя контекст проекции.
Запись звука с использованием Bluetooth HFP
Для записи звука с использованием Bluetooth HFP сначала запросите необходимые разрешения во время выполнения, а затем запишите звук с помощью API AudioManager , как описано в следующих разделах.
Запросить разрешения
Как и в случае с любым стандартным аудиоустройством Bluetooth, права доступа RECORD_AUDIO , BLUETOOTH_CONNECT и другие связанные с ними разрешения контролируются телефоном, а не подключенным устройством (например, аудиоочками или очками с дисплеем).
Для запроса разрешений выполните следующие действия:
В файле манифеста вашего приложения укажите следующие разрешения :
Запросите разрешения
RECORD_AUDIOиBLUETOOTH_CONNECTво время выполнения, используя стандартный алгоритм получения разрешений Android .
Используйте AudioManager для маршрутизации звука.
После того, как пользователь предоставит вашему приложению необходимые разрешения во время выполнения, используйте API AudioManager , чтобы установить устройство связи в TYPE_BLUETOOTH_SCO для маршрутизации звука через Bluetooth HFP. Это укажет системе получать звук с периферийного устройства 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()
Сделайте снимок с помощью камеры очков.
Чтобы сделать снимок с помощью камеры очков, настройте и свяжите сценарий использования ImageCapture объекта CameraX с камерой очков, используя правильный контекст для вашего приложения:
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)подтверждает наличие выбранной камеры на устройстве перед продолжением. - Использует Camera2 Interop с
Camera2CameraInfoдля чтения базового объектаCameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP, что может быть полезно для расширенной проверки поддерживаемых разрешений. - Для точного управления разрешением выходного изображения в
ImageCaptureсоздан специальныйResolutionSelector. - Создает сценарий использования
ImageCapture, настроенный с использованием пользовательскогоResolutionSelector. - Привязывает сценарий использования
ImageCaptureк жизненному циклу активности. Это автоматически управляет открытием и закрытием камеры в зависимости от состояния активности (например, останавливает камеру, когда активность приостановлена).
После настройки камеры очков вы можете сделать снимок с помощью класса ImageCapture из библиотеки CameraX. Чтобы узнать, как использовать takePicture для захвата изображения , обратитесь к документации CameraX.
Снимите видео с помощью камеры очков.
Чтобы с помощью камеры очков захватывать видео, а не изображение, замените компоненты ImageCapture соответствующими компонентами VideoCapture и измените логику выполнения захвата.
Основные изменения касаются использования другого сценария применения, создания другого выходного файла и запуска захвата с помощью соответствующего метода видеозаписи. Для получения дополнительной информации об API VideoCapture и способах его использования см. документацию по захвату видео CameraX .
В таблице ниже приведены рекомендуемые разрешение и частота кадров в зависимости от сценария использования вашего приложения:
| Вариант использования | Разрешение | Частота кадров |
|---|---|---|
| Видеосвязь | 1280 x 720 | 15 кадров в секунду |
| Компьютерное зрение | 640 x 480 | 10 кадров в секунду |
| Потоковое видео с использованием ИИ | 640 x 480 | 1 кадр в секунду |
Получить доступ к аппаратному обеспечению телефона из проецируемой активности
Проектируемое действие также может получить доступ к аппаратному обеспечению телефона (например, к камере или микрофону), используя 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поскольку контекст приложения может некорректно возвращать контекст очков, если проецируемая активность была последним запущенным компонентом.