หลังจากที่คุณขอและได้รับสิทธิ์ที่จำเป็นแล้ว แอป จะเข้าถึงฮาร์ดแวร์ในแว่นตาสำหรับเสียงหรือแว่นตาสำหรับการแสดงผลได้ เคล็ดลับในการ เข้าถึงฮาร์ดแวร์ของแว่นตา (แทนที่จะเป็นฮาร์ดแวร์ของโทรศัพท์) คือการใช้ บริบทที่คาดการณ์ไว้
มี 2 วิธีหลักๆ ในการรับบริบทที่คาดการณ์ไว้ โดยขึ้นอยู่กับตำแหน่งที่โค้ดของคุณทำงาน
รับบริบทที่คาดการณ์ไว้หากโค้ดของคุณทำงานในกิจกรรมที่คาดการณ์ไว้
หากโค้ดของแอปทำงานจากภายในกิจกรรมที่คาดการณ์ไว้ บริบทกิจกรรม ของโค้ดเองจะเป็นบริบทที่คาดการณ์ไว้แล้ว ในสถานการณ์นี้ การเรียกใช้ภายในกิจกรรมนั้นจะเข้าถึงฮาร์ดแวร์ของแว่นตาได้อยู่แล้ว
รับบริบทที่คาดการณ์ไว้สำหรับโค้ดที่ทำงานในคอมโพเนนต์แอปโทรศัพท์
หากส่วนหนึ่งของแอปที่อยู่นอกกิจกรรมที่คาดการณ์ไว้ (เช่น กิจกรรมโทรศัพท์หรือบริการ) จำเป็นต้องเข้าถึงฮาร์ดแวร์ของแว่นตา ส่วนนั้นจะต้องรับบริบทที่คาดการณ์ไว้อย่างชัดเจน โดยใช้เมธอด
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) จะเข้าถึงฮาร์ดแวร์ของแว่นตา AI ได้
ล้างข้อมูลเมื่อยกเลิกการเชื่อมต่อ
บริบทที่คาดการณ์ไว้จะเชื่อมโยงกับวงจรชีวิตของอุปกรณ์ที่เชื่อมต่อ ดังนั้นระบบจะทำลายบริบทนี้เมื่ออุปกรณ์ยกเลิกการเชื่อมต่อ เมื่ออุปกรณ์ยกเลิกการเชื่อมต่อ
ProjectedContext.isProjectedDeviceConnected จะแสดงผลเป็น false แอปควรตรวจสอบการเปลี่ยนแปลงนี้และล้างบริการระบบ (เช่น CameraManager) หรือทรัพยากรที่แอปสร้างขึ้นโดยใช้บริบทที่คาดการณ์ไว้นั้น
เริ่มต้นใหม่เมื่อเชื่อมต่ออีกครั้ง
เมื่อแว่นตาเชื่อมต่ออีกครั้ง แอปจะรับอินสแตนซ์บริบทที่คาดการณ์ไว้ได้อีกรายการโดยใช้ createProjectedDeviceContext จากนั้น
เริ่มต้นบริการระบบหรือทรัพยากรใหม่โดยใช้บริบทที่คาดการณ์ไว้ใหม่
บันทึกเสียงด้วยไมโครโฟนของแว่นตา
คุณบันทึกเสียงจากแว่นตาได้ 2 วิธีที่แตกต่างกันดังนี้
- ใช้บริบทที่คาดการณ์ไว้
- ใช้โปรไฟล์แฮนด์ฟรี (HFP) ของบลูทูธ
เลือกวิธีการบันทึก
วิธีการที่คุณเลือกจะขึ้นอยู่กับว่าคุณต้องการการประมวลผลเสียงที่มีความเที่ยงตรงสูงและเฉพาะเจาะจงสำหรับ XR หรืออินพุตเสียงบลูทูธมาตรฐาน
| วิธีการบันทึก | สิทธิ์เข้าถึงไมโครโฟน | กรณีการใช้งานทั่วไป |
|---|---|---|
บริบทที่คาดการณ์ไว้ |
ไมโครโฟนหลายตัว |
การบันทึกโดยใช้บริบทที่คาดการณ์ไว้จะช่วยให้แอปเข้าถึงไมโครโฟนหลายตัวจากแว่นตาและฟีเจอร์ฮาร์ดแวร์เฉพาะทางของแว่นตาได้ เช่น
|
HFP ของบลูทูธ |
ไมโครโฟนตัวเดียว |
ใช้โปรไฟล์แฮนด์ฟรี (HFP) ของบลูทูธเพื่อความเข้ากันได้ทันทีโดยไม่ต้องตั้งค่า ในโหมดนี้ แว่นตาจะเชื่อมต่อกับโทรศัพท์โดยใช้โปรไฟล์ชุดหูฟังมาตรฐานและโปรไฟล์การกระจายเสียงขั้นสูง (A2DP) โปรไฟล์ ซึ่งทำงานเหมือนอุปกรณ์ต่อพ่วงบลูทูธทั่วไป หากแอปได้รับการออกแบบมาสำหรับการบันทึกบลูทูธมาตรฐานอยู่แล้ว คุณสามารถใช้วิธีนี้เพื่อบันทึกเสียงจากแว่นตาโดยไม่ต้องผสานรวมความสามารถเฉพาะสำหรับ XR |
บันทึกเสียงโดยใช้บริบทที่คาดการณ์ไว้
หากต้องการบันทึกเสียงโดยใช้บริบทที่คาดการณ์ไว้ ให้ขอสิทธิ์รันไทม์ที่จำเป็น
ก่อน จากนั้นบันทึกเสียงโดยใช้ AudioRecord API ตามที่
อธิบายไว้ในส่วนต่อไปนี้
ขอสิทธิ์รันไทม์
หากต้องการเข้าถึงไมโครโฟนหลายตัวในแว่นตา คุณต้องขอสิทธิ์เสียงสำหรับอุปกรณ์ที่คาดการณ์ไว้โดยเฉพาะ สิทธิ์ RECORD_AUDIO มาตรฐานที่มีขอบเขตเป็นโทรศัพท์ซึ่งผู้ใช้ให้ไว้สำหรับแอปในอุปกรณ์เคลื่อนที่นั้นไม่เพียงพอ
ทำตามขั้นตอนต่อไปนี้เพื่อขอสิทธิ์
- ประกาศสิทธิ์
RECORD_AUDIOpermission ในไฟล์ Manifest ของแอป ขอสิทธิ์ที่มีขอบเขตเป็นอุปกรณ์ที่คาดการณ์ไว้ด้วยวิธีใดวิธีหนึ่งต่อไปนี้ โดยขึ้นอยู่กับตำแหน่งที่โค้ดของคุณทำงาน
- โค้ดที่ทำงานจากกิจกรรมที่คาดการณ์ไว้: ใช้
ActivityResultLauncherกับProjectedPermissionsResultContract. ดูข้อมูลเพิ่มเติมเกี่ยวกับ การใช้วิธีนี้ได้ที่ส่วน ลงทะเบียนตัวเปิดใช้สิทธิ์ และส่วนต่อๆ ไปในคำแนะนำสำหรับการขอสิทธิ์ฮาร์ดแวร์ - โค้ดที่ทำงานจากกิจกรรมโทรศัพท์โฮสต์: ใช้
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) เพื่อให้มั่นใจว่าเข้ากันได้กับแว่นตาแม้ว่าจะไม่มีข้อกำหนดที่ตายตัวสำหรับขนาดบัฟเฟอร์ แต่ให้ รับขนาดบัฟเฟอร์ขั้นต่ำ เพื่อลดเวลาในการตอบสนองที่รับรู้ได้
ล้างข้อมูลหลังการใช้งาน
เมื่อแอปไม่ต้องการไมโครโฟนอีกต่อไปหรือเมื่อกิจกรรมหยุดลง
ให้เรียกใช้ stop และ release ในออบเจ็กต์ AudioRecord
ตรวจสอบสิทธิ์รันไทม์ก่อนบันทึก
ก่อนเรียกใช้ startRecording, ตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์เข้าถึง
ไมโครโฟนสำหรับแว่นตาโดยใช้บริบทที่คาดการณ์ไว้แล้ว
บันทึกเสียงโดยใช้ HFP ของบลูทูธ
หากต้องการบันทึกเสียงโดยใช้ HFP ของบลูทูธ ให้ขอสิทธิ์รันไทม์ที่จำเป็น
ก่อน จากนั้นบันทึกเสียงโดยใช้ AudioManager API ตามที่
อธิบายไว้ในส่วนต่อไปนี้
ขอสิทธิ์
เช่นเดียวกับอุปกรณ์เสียงบลูทูธมาตรฐาน สิทธิ์ RECORD_AUDIO,
BLUETOOTH_CONNECT และสิทธิ์อื่นๆ ที่เกี่ยวข้องจะควบคุมโดย
โทรศัพท์ ไม่ใช่อุปกรณ์ที่เชื่อมต่อ (เช่น แว่นตาสำหรับเสียงหรือแว่นตาสำหรับการแสดงผล)
ทำตามขั้นตอนต่อไปนี้เพื่อขอสิทธิ์
ประกาศสิทธิ์ต่อไปนี้ในไฟล์ Manifest ของแอป
ใช้ AudioManager เพื่อกำหนดเส้นทางเสียง
หลังจากที่ผู้ใช้ให้สิทธิ์รันไทม์ที่จำเป็นแก่แอปแล้ว ให้ใช้
AudioManager API เพื่อตั้งค่าอุปกรณ์สื่อสารเป็น
TYPE_BLUETOOTH_SCO เพื่อกำหนดเส้นทางเสียงผ่าน 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()
ถ่ายภาพด้วยกล้องของแว่นตา
หากต้องการถ่ายภาพด้วยกล้องของแว่นตา ให้ตั้งค่าและผูกกรณีการใช้งาน
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ที่อยู่เบื้องหลัง ซึ่งอาจมีประโยชน์สำหรับการตรวจสอบขั้นสูงเกี่ยวกับความละเอียดที่รองรับ - สร้าง
ResolutionSelectorที่กำหนดเองเพื่อควบคุมความละเอียดของรูปภาพเอาต์พุต สำหรับImageCaptureได้อย่างแม่นยำ - สร้างกรณีการใช้งาน
ImageCaptureที่กำหนดค่าด้วยResolutionSelectorที่กำหนดเอง - ผูกกรณีการใช้งาน
ImageCaptureกับวงจรชีวิตของกิจกรรม ซึ่งจะจัดการการเปิดและปิดกล้องโดยอัตโนมัติตามสถานะของกิจกรรม (เช่น หยุดกล้องเมื่อกิจกรรมหยุดชั่วคราว)
หลังจากตั้งค่ากล้องของแว่นตาแล้ว คุณจะถ่ายภาพด้วยคลาส ImageCapture ของ CameraX ได้ โปรดดูเอกสารประกอบของ CameraX เพื่อเรียนรู้
เกี่ยวกับวิธีใช้ takePicture เพื่อถ่ายภาพ
บันทึกวิดีโอด้วยกล้องของแว่นตา
หากต้องการบันทึกวิดีโอแทนที่จะเป็นรูปภาพด้วยกล้องของแว่นตา ให้แทนที่
ImageCapture คอมโพเนนต์ด้วยคอมโพเนนต์ VideoCapture ที่เกี่ยวข้อง
และแก้ไขตรรกะการดำเนินการบันทึก
การเปลี่ยนแปลงหลักๆ ได้แก่ การใช้กรณีการใช้งานที่แตกต่างกัน การสร้างไฟล์เอาต์พุตที่แตกต่างกัน และการเริ่มต้นการบันทึกโดยใช้วิธีการบันทึกวิดีโอที่เหมาะสม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ VideoCapture API และวิธีใช้ได้ใน
เอกสารประกอบการบันทึกวิดีโอของ CameraX
ตารางต่อไปนี้แสดงความละเอียดและอัตราเฟรมที่แนะนำโดยขึ้นอยู่กับกรณีการใช้งานของแอป
| กรณีการใช้งาน | ความละเอียด | อัตราเฟรม |
|---|---|---|
| การสื่อสารผ่านวิดีโอ | 1280 x 720 | 15 FPS |
| คอมพิวเตอร์วิทัศน์ | 640 x 480 | 10 FPS |
| การสตรีมวิดีโอ AI | 640 x 480 | 1 FPS |
เข้าถึงฮาร์ดแวร์ของโทรศัพท์จากกิจกรรมที่คาดการณ์ไว้
กิจกรรมที่คาดการณ์ไว้ยังเข้าถึงฮาร์ดแวร์ของโทรศัพท์ (เช่น
กล้องหรือไมโครโฟน) ได้ด้วยการใช้ 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เนื่องจากบริบทของแอปพลิเคชัน อาจแสดงผลบริบทของแว่นตาอย่างไม่ถูกต้องหากกิจกรรมที่คาดการณ์ไว้เป็น คอมโพเนนต์ที่เปิดตัวล่าสุด